forthling について
forthlingは、サンドボックス内でForthコードを実行するMCPサーバーです。AIエージェントなどのMCPクライアント向けで、呼び出し間で定義とスタックを保持し、エラーやトレースを含む構造化結果も返します。
主な機能
- 01継続するセッションでForthを実行
- 02データスタックと浮動小数点スタックを確認
- 03セッション内のワード一覧とヘルプ
- 04メモリ内ファイルの読み込みと保持
- 05構造化エラー、トレース、テスト結果の返却
- 06呼び出しごとの時間とリソース制限
活用シーン
- 01エージェントからForthコードを実行して確認
- 02呼び出しをまたいでForth定義を再利用
- 03トレースとソース位置からスタックエラーを診断
MCP 対応
stdio経由でMCPクライアントにForth実行とセッション確認のツールを提供します。各クライアントセッションはサンドボックス内で動作し、任意のHTTPアクセスは明示的に許可したホストに限定されます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
forthling-mcp
AI エージェント(Claude Code、Claude Desktop、その他の MCP クライアント)が、常にサンドボックス内で forthling の Forth を実行できる MCP サーバーです。エージェントは 1 つのセッションを維持し、定義したワードやスタックは呼び出しの間も保持されます。
インストール
Rust を使わずに MCP バンドルとしてインストールする場合は、最新リリースから forthling.mcpb をダウンロードし、Claude Desktop(または .mcpb ファイルをインストールできる別のクライアント)で開きます。バンドルには、Windows(x64)、macOS(Apple Silicon および Intel)、Linux(x64)用のサーバーが含まれています。
Rust 1.85 以降を使い、crates.ioからインストールする場合:
cargo install forthling-mcp
SQLite も合わせてビルドするため、C コンパイラーが必要です(Windows では Visual Studio の C コンパイラーが必要です。Rust も同じものを使います)。cargo install forthling-mcp --no-default-features を実行すると SQLite は含まれません。リポジトリをクローンした場合は、cargo install --path mcp または cargo build --release --manifest-path mcp/Cargo.toml を実行すると、プログラムは mcp/target/release/forthling-mcp に生成されます。Claude Code では次のようにします。
claude mcp add forthling -- forthling-mcp
その他のクライアントも同様に起動します。コマンドは forthling-mcp で、stdio を介して通信します。
クライアントが接続すると、サーバーはモデルに基本事項を指示します。モデルがつまずきやすい慣用表現、ROT、PICK、ROLL の代わりに locals を使うこと、JSON ワード、そして制限事項です。llms.txtには、事前に読むモデル向けに同じ内容がより詳しく記載されています。テストによって、両方の制限事項が一致していることを確認しています。
ツール
| ツール | 引数 | |
|---|---|---|
forth_eval | code, data, files, trace | Forth コードを実行し、出力、データスタックと浮動小数点スタック、およびエラーが発生した場合はその内容を返します |
forth_stack | 何も実行せず、スタックを返します | |
forth_words | このセッションで定義されたワードを、新しいものから順に返します | |
forth_help | word | ワードのスタック効果、動作、例、および属するワードリストを返します。未定義の名前の場合は、名前が近いワードを返します。セッションのワードでは、名前の後に ( ) コメントが表示されます |
forth_reset | 新しいセッションを開始します。すべての定義、スタック値、データ、ファイルが消去されます |
コードの1行、および S" 内の文字列は、最大1024文字です。JSON ドキュメントなどの大きな入力は data に渡します。コードは DATA ( -- c-addr u ) で読み取り、ALLOCATE されたメモリにコピーを取得します(FREE してください)。データは、別のデータを渡す呼び出しが行われるまで後続の呼び出しでも利用でき、Forth のメモリと同じサイズまで渡せます。
{"name": "forth_eval", "arguments": {"code": "ALSO JSON DATA JSON-LENGTH .", "data": "[[59],[73,41]]"}}
各セッションには、メモリ上だけに存在し、ディスクには保存されない独自のファイルがあります。INCLUDE、REQUIRE、FILE ワード、ブロックワードはこれらのファイルを使用し、forth_reset が呼び出されるまで保持されます。files を指定すると、コードの実行前に名前とテキストのオブジェクトとしてファイルが追加され、同じ名前のファイルは置き換えられます。そのため、エージェントはライブラリを一度渡して INCLUDE し、コードが書き込んだ内容を後の呼び出しで読み取ることができます。ファイルの合計サイズは最大4 MiBです。上限を超える書き込みはワードの ior を返して失敗し、収まらない files は拒否されます。
{"name": "forth_eval", "arguments": {"code": "INCLUDE square.fs 7 SQUARE .", "files": {"square.fs": ": SQUARE DUP * ;"}}}
各結果は、REPL に表示される形式のテキストです。
2401 ok
stack: 1 2
また、構造化 JSON(outputSchema を含む structuredContent)も同じ内容です。
{"output": "2401 ", "stack": [1, 2], "fstack": [], "compiling": false, "error": null, "steps": …, "max_depth": …}。steps と max_depth(forth_eval のみ)は、呼び出しで実行されたステップ数とデータスタックの最大深度を示します。実行されたシステムのワードもカウントされます(. は Forth で記述されているため、これもカウントされます)。
エラーが発生するとスタックは空になります。そのため、結果にはエラー発生時のスタックの内容、発生箇所、実行中だったワードも含まれ、モデルは一度で修正できます。
error -10: division by zero
at input, line 2, column 3: MAIN
in: MAIN > STEP > /
stack at the error: 5 1
stack: empty
JSON では、"error": {"code": -10, "message": "division by zero", "stack": [5, 1], "fstack": [], "words": ["MAIN", "STEP", "/"], "source": "input", "line": 2, "column": 3, "token": "MAIN", …} のようになり、isError は true になります。ここでの / のように、失敗する前に引数を取り出したワードは、その引数を消費済みです。検索順序の外にあるワードリストに未定義ワードがある場合は hint が付与され、テキストではワードの後に追加されます。
error -13: undefined word: JSON@ (it is in the word list JSON; ALSO JSON finds it)。
暴走したプログラムでは、スタックに10万個のセルが残ったり、実行中のワードが大量に残ったりすることがあります(無限再帰など)。この場合、結果にはスタックの先頭32個のセルと、最も内側の32個のワードが保持されます。テキストには ... (n more) と表示され、JSON では stack_depth、fstack_depth、words_depth に完全な深度が記録されます。未完了の定義(compiling が true)の場合、テキストにその旨が表示されます。compiled (a definition is still open; ; ends it)。
コードが T{ ... -> ... }T テスト(ALSO TESTING)を実行した場合、結果にはその呼び出しのテスト件数も含まれ、テキストは tests: 1 passed, 1 failed で始まります。失敗行(FAIL input:3 (sums): incorrect result: expected <1> 3, got <1> 2)は出力に含まれます。JSON では次のようになります。
"tests": {"passed": 1, "failed": 1, "failures": [{"source": "input", "line": 3, "group": "sums", "expected": [3], "expected_depth": 1, "actual": [2], "actual_depth": 1, "stack": [], "expected_floats": [], "expected_floats_depth": 0, "actual_floats": [], "actual_floats_depth": 0}], "failures_dropped": 0}。最初の32件の失敗が保持され、各リストには先頭の32個のセルと浮動小数点数が保持されます(深度には元の個数が示されます)。files で渡したファイルは、その名前が source として表示されます。
エージェントがよくある間違いで目にする内容
それぞれのケースは forth_eval (#47) で実行しました。回答にはテキストの先頭部分が含まれます。
コードは新しいセッションで実行しました。
| 間違い | コード | 回答 |
|---|---|---|
| 未定義のワード | 1 2 FOO . | error -13: undefined word: FOO、その列番号、エラー発生時のスタック 1 2 |
| 検索順に含まれていないワードリスト内のワード | S" :memory:" DB-OPEN | error -13: undefined word: DB-OPEN (it is in the word list SQLITE; ALSO SQLITE finds it) |
| 定義内の未定義のワード | : sq DUPP * ; | error -13: undefined word: DUPP、その列番号 |
| スタックアンダーフロー | : add2 + ; 1 add2 | error -4: stack underflow、in: ADD2 > +、エラー発生時のスタック |
| 引数を取りすぎるワード | : f ( n -- ) DROP ; f | error -4、in: F > DROP |
定義外の IF、DO、BEGIN | 1 IF 2 THEN | error -14: interpreting a compile-only word: IF |
閉じられていない IF | : foo 1 IF 2 ; | error -22: control structure mismatch、; の位置 |
DO のない LOOP | : f LOOP ; | error -22: control structure mismatch |
| 閉じられていない定義 | : foo 1 2 + | compiled (a definition is still open; ; ends it) |
| ゼロ除算 | 1 0 / | error -10: division by zero、in: /、エラー発生時のスタックは空(/ が引数を消費しています) |
| 不正なアドレス | 12345678 @ | error -9: invalid memory address: 12345678 |
| ステップ上限 | : spin BEGIN 1 DROP AGAIN ; spin | error -258: limit exceeded: steps、in: SPIN > DROP |
| 出力上限 | : p BEGIN 65 EMIT AGAIN ; p | 上限に達するまでの出力、その後に error -258: limit exceeded: output |
| スタックの無限増加 | : grow BEGIN 1 AGAIN ; grow | error -3: stack overflow、スタックの先頭32セルと ... (n more) |
| 無限再帰 | : r RECURSE ; r | error -5: return stack overflow、最も内側にある32個のワード |
| ファイルが見つからない | S" nofile.fth" INCLUDED | error -38: non-existent file: nofile.fth: no such file |
EVALUATE 内のエラー | S" 1 2 FOO" EVALUATE | at evaluate, line 1, column 5: FOO、スタック 1 2 |
| 1024文字を超える行 | S" xxxx…" TYPE | error -18: parsed string overflow: input line too long |
ABORT" | : chk 0= ABORT" zero" ; 0 chk | error -2: aborted: zero |
CATCH | : t 1 0 / ; : u ['] t CATCH . ; u | -10 を出力し、エラー結果はありません |
エラーが発生せず、メッセージも出ないケース:閉じられていない S" または ( は、標準の規定どおり行末までを取り込みます。: f >R ; はリターンスタックのバランスを崩しますが、1 f はワードを返さずに終了します。負のサイズを指定した ALLOCATE と、2回目の FREE は、スタックにゼロ以外の ior(-59、-60)を返します。DB-EXEC が失敗した場合も同様です(-261)。存在しないJSONパスでは 0 0 0 が返されます。これらは標準またはワードのスタック効果に従っています。エージェントはスタックを読み取る必要があります。
"trace": true を指定すると、forth_eval はトレースも返します。実行された各ワード、そのネストの深さ、実行開始時のデータスタック(先頭8セルまで)が記録されます(最大1000ワード)。残りの数は trace_dropped に記録されます。システム独自のワードの内部は省略されます。テキストは TRACE-ON の出力と同じインデントで始まります。
trace:
F <0>
SQ <1> 3
DUP <1> 3
* <2> 3 3
1+ <1> 9
. <1> 10
end of trace
10 ok
stack: empty
また、JSONには "trace": [{"word": "F", "depth": 0, "stack": [], "stack_depth": 0}, {"word": "SQ", "depth": 1, "stack": [3], "stack_depth": 1}, …], "trace_dropped": 0 が含まれます。失敗した呼び出しのトレースには、エラーに至るまでの過程が表示されます。コード内で ALSO DEBUG TRACE-ON … TRACE-OFF を実行すると、同じ行が出力されます。
BREAK(および ALSO DEBUG)は、確認する位置を示します。呼び出しの途中で回答することはできないため、プログラムは停止しません。代わりに結果に breaks が含まれ、各 BREAK でのプログラムの状態が記録されます(最大100件。残りの数は breaks_dropped に記録されます)。エラーの場合と同じフィールド、つまり stack、fstack、words、source、line、column、token が含まれます。テキストは、それぞれ次の行で始まります。
break 1: in F > BREAK, at input, line 1, column 3: stack <2> 7 9
ALSO TUTORIAL はチュートリアルを提供します。Forth の 20 レッスンで、初心者向けが 10 レッスン、その先の内容が 10 レッスンです。エージェントが人を順に案内できます(LESSONS、n LESSON、回答をテストする CHECK、HINT、NEXT)。
JSON のワードはインストールされています(ALSO JSON)。また、メモリ内データベース用の SQLite ワード(ALSO SQLITE。:memory: のみで、時間切れになると SQL が停止し、Forth のメモリより大きくなることはありません)、線形時間でマッチする正規表現ワード(ALSO REGEX)、ベクトル、行列、線形代数、特殊関数のワード(ALSO SCIENCE)も利用できます。Cargo の機能 sqlite、regex、science(--no-default-features)を使わずにビルドすると、これらは含まれません。
HTTP ワード(ALSO HTTP。 http/README.md を参照)は 3 つ目の機能である http に含まれています。ネットワークに接続するため、デフォルトでは無効になっています。この機能を有効にしてサーバーをビルドし(cargo install forthling-mcp --features http)、Forth コードからリクエストを送信できるホストを指定して起動します。ホストごとに 1 回指定してください。例:forthling-mcp --allow-host api.example.com(*.example.com と * も使用できます)。--allow-host を指定しない場合、ネットワークは閉じられたままで、モデルにワードも通知されません。サンドボックスの外部に接続する唯一のオプションですが、指定したホストに限られます。Forth ワードからホストを追加することはできません。
サンドボックス
各セッションは forthling のサンドボックス内で実行されます。ファイルはメモリ内に限られ、各呼び出しは最大 1,000 万ステップ、5 秒、出力 1 MiB までです。セッションの定義などは、すべて合わせて最大 4 MiB です。オプションでこれらの上限を変更できますが、それぞれプログラムで定められた上限を超えることはできません。
-m, --memory SIZE Forth memory (default 1M) [64M]
--max-steps N steps per call (default 10000000) [1000000000]
--max-time T time per call, such as 5s or 500ms (default 5s) [60s]
--max-output SIZE output per call (default 1M) [16M]
--max-space SIZE definitions and the like in all (default 4M) [64M]
--allow-host HOST a host that Forth code may send HTTP requests to (repeat)
サンドボックスを無効にしたり、ファイルを許可したりするオプションはありません。そのため、サーバーの起動方法を変えても、ファイルにアクセスできるようにはなりません。ネットワークを開くのは --allow-host のみで、指定したホストに限られます(上記を参照)。
強制の仕組み。 サンドボックスを強制するのはサーバーであり、モデルへの説明やモデルの約束ではありません。ツールが受け取るのは Forth コードのみで(その他の引数は拒否されます)、Forth ワードから上限や権限を変更することはできません。AI にサンドボックスを回避するよう頼んでも、forthling を通じてそれを実行することはできません。ただし、シェルやファイル編集など、ほかのツールも使えるエージェントであれば、MCP の設定を変更したり、別のプログラムを起動したりして回避できる可能性があります。forthling ではそれを防げないため、エージェントの権限を使って対処してください。Claude Code では、シェルを拒否または制限し、MCP の設定(.mcp.json、~/.claude.json)の編集を拒否し、管理設定を通じて必要な MCP サーバーのみを許可してください。forthling のバグに対する安全性をさらに高めるには、コンテナー内、または権限の少ないユーザーとしてサーバーを実行してください。
テスト
cargo test --manifest-path mcp/Cargo.toml
cargo test --manifest-path mcp/Cargo.toml --no-default-features
cargo test --manifest-path mcp/Cargo.toml --all-features
リンク
- ソースと forthling の詳細:https://github.com/DeSchaker/forthling
- MCP Registry では、このサーバーは
io.github.DeSchaker/forthlingとして登録されています(以下の行で、crate が当社のものであることを確認できます)。 - mcp-name: io.github.DeSchaker/forthling
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。