mcp-v8: V8 JavaScript MCP Server について
mcp-v8は、AIエージェントが隔離されたV8アイソレートでJavaScriptとTypeScriptを実行できるRust製MCPサーバーです。単一のコード実行ツールに、永続ヒープスナップショットとポリシーによるホスト機能の制御を組み合わせています。
主な機能
- 01V8でのJavaScript・TypeScript実行
- 02ヒープスナップショットの保存と復元
- 03Regoポリシーによるホストアクセス制御
- 04JavaScriptから上流MCPサーバーを呼び出し
- 05stdio、HTTP、SSEトランスポート対応
- 06長時間の呼び出しをMCPタスクとして実行
活用シーン
- 01単一のツール呼び出しでJavaScriptによるデータ変換
- 02エージェントの複数ターンにわたる変数やオブジェクトの保持
- 03ポリシーによる特定のネットワーク・ファイル操作の許可
- 04接続済みMCPサーバーのツールの組み合わせ
MCP 対応
`run_js`を提供し、ステートフルモードでは実行、出力、セッション、ヒープスナップショットを管理するツールも公開します。JavaScriptから`mcp.callTool()`と`mcp.listTools()`を使い、接続済みの上流MCPサーバーも呼び出せます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
mcp-v8 — AI エージェント向け JavaScript/TypeScript ランタイム
mcp-v8 は、Rust で書かれた Model Context Protocol サーバーです。
AI エージェントが サンドボックス化された V8 アイソレート内で JavaScript と TypeScript を実行できるようにします。用途を絞った多数のツールを連携させる代わりに、エージェントに 1 つのツール、run_js を提供し、コードを記述させます。ループ処理や分岐、データ変換、他のツールの呼び出しなどを行い、同等のツール呼び出しの連鎖と比べて、多くの場合トークン数を大幅に減らせます。
デフォルトの ステートフル モードでは、V8 ヒープはコンテンツアドレス方式のスナップショットとして保存されるため、エージェントは複数のターンにわたって状態を積み上げられます。ホストの機能(ネットワーク、ファイルシステム、サブプロセス、WebAssembly、モジュールのインポート、他の MCP サーバーの呼び出し)はすべて デフォルトで無効であり、明示的な OPA/Rego ポリシーによってのみ有効になります。
mcp-v8 を選ぶ理由
- 1 つのツールで、無制限の機能。 エージェントは固定されたツールの一覧ではなく、プログラムを実行します。
- 永続的な状態。 ヒープのスナップショットにより、変数やオブジェクトが呼び出しをまたいで保持されます。
- デフォルトで安全。
fetch、ファイルシステム、サブプロセス、外部からのインポートは、ポリシーで許可するまで拒否されます。 - カーネルによる強制的な隔離。 オプトインの
--sandbox-manifestにより、nono のケイパビリティマニフェスト(Linux では Landlock、macOS では Seatbelt)を使ってプロセス全体を隔離し、ポリシーレイヤーの下で多層防御を実現します。 - 本番環境に対応。 stdio/Streamable HTTP/SSE トランスポート、REST サイドカー、ページネーション対応の非同期実行、JWKS 認証、Raft レプリケーションによるクラスタリングを備えています。
ドキュメント
メンテナーの方は、Docker ベースのレジストリマニフェストと自動リリースの手順について、MCP Registry への公開をご覧ください。
すべてのドキュメントは https://r33drichards.github.io/mcp-js/ にあります(site-docs/ から生成)。チュートリアル、操作ガイド、概念の解説、CLI フラグ、HTTP API、MCP ツールの完全なリファレンスを掲載しています。
コミュニティ
質問、使用例、プロジェクトについてのディスカッションは、mcp-js Discord コミュニティにご参加ください。
クイックスタート
インストール
# Server
curl -fsSL https://raw.githubusercontent.com/r33drichards/mcp-js/main/install.sh | sudo bash
# Optional CLI client
curl -fsSL https://raw.githubusercontent.com/r33drichards/mcp-js/main/install-cli.sh | sudo bash
/usr/local/bin にインストールされます。対応プラットフォーム:Linux x86_64/arm64、macOS Apple Silicon。nix run github:r33drichards/mcp-js の実行や Docker の利用(docker-compose.*.yml スタックを参照)、またはソースからのビルドも可能です。
ホスト型サーバーをお探しですか?Railway にデプロイしてください。リポジトリの railway.json でビルド、ヘルスチェック、再起動ポリシーが設定されており、RAILWAY.mdではボリューム、変数、ワンクリックテンプレートの設定手順を説明しています。
MCP クライアントを接続する
# Claude Code (stdio)
claude mcp add mcp-v8 -- mcp-v8 --directory-path /tmp/mcp-v8-heaps # stateful
claude mcp add mcp-v8 -- mcp-v8 --stateless # stateless
Claude Desktop/Cursor では、クライアントの mcpServers 設定に追加します。
{ "mcpServers": { "js": { "command": "mcp-v8", "args": ["--stateless"] } } }
次に、エージェントに *「この JavaScript を実行してください:console.log([1,2,3].map(x => x*2))」*と依頼します。
HTTP 経由で実行する
mcp-v8 --stateless --http-port 8080
# MCP endpoint: POST http://localhost:8080/mcp
# REST sidecar: POST http://localhost:8080/api/exec (JSON body, or a raw-body file upload)
/api/exec は、JSON ボディまたはリクエストボディにスクリプトを含めるファイルアップロードのどちらも受け付けます。JSON 以外の Content-Type を指定して、リクエストボディとしてスクリプトを送信してください(curl --data-binary @script.js -H 'Content-Type: application/javascript' .../api/exec)。
run_js MCP ツールでは、オプションの file パラメーターを使用してサーバー上のパスからスクリプトを読み込むこともできます。これはデフォルトでは無効で、--allow-run-js-file または run_js_file
ポリシーで有効にできます。
詳しくは、クイックスタートチュートリアルと トランスポートガイドをご覧ください。
単一ファイルで設定する
すべてのフラグは、--config(または
MCP_V8_CONFIG)で指定する 1 つの TOML または JSON ファイルに記述することもできます。これには、個別の WASM/MCP サーバー/フェッチヘッダー/ポリシーの JSON ファイルを置き換える構造化セクションも含まれます。
# server.toml — run with: mcp-v8 --config server.toml
http_port = 8080
heap_store = "dir"
heap_dir = "/var/lib/mcp-v8/heaps"
[policies.fetch]
policies = [{ url = "file:///etc/mcp-v8/fetch.rego" }]
優先順位は CLI フラグ > MCP_V8_* 環境変数 > 設定ファイル > デフォルトです。設定ファイルのリファレンスをご覧ください。
機能
- JavaScript と TypeScript を分離された V8 エンジンで実行します(
deno_core経由)。TypeScript の型は SWC で除去されます(型の除去であり、型チェックではありません)。 - Async/await とタイマー — Promise とイベントループに加え、
setTimeout/clearTimeoutを使用できます。 - コンソールのキャプチャ —
console.log/info/warn/error/debug/traceをストレージにストリーミングし、行単位またはバイト単位のページネーションで読み取れます。 - 非同期実行モデル —
run_jsは実行 ID を返します。ステータスをポーリングして出力をストリーミングし、実行中の処理をキャンセルできます。 - コンテンツアドレス指定方式のヒープスナップショット — V8 の状態を呼び出し間で永続化/復元できます(ローカル FS、S3、または S3 + ライトスルーキャッシュ)。または ステートレス で実行できます。
- WebAssembly — 標準の
WebAssemblyAPI に加え、事前読み込みされたモジュール(--wasm-module)をグローバルとして公開し、クライアントにはrunjs__wasm__<name>スタブツールとして通知します。 - ES モジュールのインポート —
npm:、jsr:、URL からのインポートをオプションで実行時に取得できます(ポリシーによる制御あり)。 - ポリシーで制御される機能 —
fetch、ファイルシステム(fs)、サブプロセスへのアクセスは、操作ごとに Rego ポリシーでチェックされます。さらに、fetch用のヘッダー/OAuth 注入にも対応しています。 - 他の MCP サーバーを連携 — 上流の MCP サーバーに接続し、JS から
mcp.callTool()/mcp.listTools()で呼び出せます。 - カスタマイズ可能なインターフェース — サーバーの
instructionsとrun_jsの説明を上書きできます(--instructions、--run-js-description)。 - 単一ファイル設定 — 1 つの TOML/JSON
--configファイルですべてのフラグを設定できます(優先順位:CLI フラグ > 環境変数 > 設定ファイル > デフォルト)。 - 認証とクラスタリング — JWKS ベースの JWT 検証と、セッションメタデータの複製および水平スケーリングに対応するオプションの Raft クラスタリングを利用できます。
- 複数のトランスポート — stdio、Streamable HTTP(MCP 2025-03-26 以降)、レガシーな HTTP+SSE トランスポート(
--sse-port。ベンダー提供の rmcp 0.1.5 が使用されます)に対応し、REST サイドカーと OpenAPI 仕様も備えています。 - タスク — Streamable HTTP/stdio 経由でネイティブ MCP タスク拡張(
io.modelcontextprotocol/tasks、SEP-2663)を利用できます。この拡張を宣言するクライアントはrun_jsをタスク(tasks/get、tasks/cancel)として実行でき、長時間かかる呼び出しに最適です。(レガシーな SSE トランスポートはタスクに対応していません。)
エージェントのコードでできること
run_js 内では、以下のグローバル変数を利用できます(機能に関するグローバル変数にはポリシーが必要です)。
| グローバル変数 | 用途 | 制御対象 |
|---|---|---|
console、setTimeout | 出力とタイマー | — |
fetch(url, opts?) | HTTP リクエスト(Fetch API) | fetch ポリシー |
fs.* | ファイル入出力(readFile、writeFile など) | filesystem ポリシー |
child_process / Deno.Command | サブプロセスの実行 | subprocess ポリシー |
import(npm: / jsr: / URL) | 外部 ES モジュール | --allow-external-modules + modules ポリシー |
WebAssembly、__wasm_<name> | WASM の実行/インスタンス化 | — |
mcp.callTool/listTools/servers | 上流の MCP サーバーの呼び出し | mcp_tools ポリシー |
ポリシーモデルについては、概念 → セキュリティポリシーを参照してください。
MCP ツール
| ツール | モード | 説明 |
|---|---|---|
run_js | 両方 | ステートフル:実行をキューに追加 → {execution_id}。ステートレス:実行して {output, error?} を返します。 |
get_execution | ステートフル | 実行のステータス/結果をポーリングします。 |
get_execution_output | ステートフル | ページ分割されたコンソール出力(行またはバイト単位)を読み取ります。 |
cancel_execution | ステートフル | 実行中の処理を終了します。 |
list_executions | ステートフル | 実行とそのステータスを一覧表示します。 |
list_sessions、list_session_snapshots | ステートフル | 名前付きセッションと履歴を確認します。 |
get_heap_tags、set_heap_tags、delete_heap_tags、query_heaps_by_tags | ステートフル | ヒープスナップショットにタグを付けて検索します。 |
全パラメーターについては、MCP ツールリファレンスを参照してください。
タスクとしての長時間実行呼び出し
このサーバーは、rmcp を介して Streamable HTTP と stdio の両トランスポートで、MCP の tasks 拡張機能
(io.modelcontextprotocol/tasks、SEP-2663)をネイティブに実装しています。initialize の結果では、
capabilities.extensions の下にこの拡張機能が提示されます。また、クライアント自身のケイパビリティで同じ拡張機能を宣言すると、
run_js がタスクとして実行されます。
// client initialize params
{ "capabilities": { "extensions": { "io.modelcontextprotocol/tasks": {} } } }
// → tools/call { name: "run_js", ... } returns immediately with a task:
// { "resultType": "task", "taskId": "…", "status": "working", … }
その後、クライアントは tasks/get をポーリングします(完了したタスクにはツールの結果がインラインで含まれ、これは呼び出しで返される結果とまったく同じです)。また、tasks/cancel で実行を停止できます。この拡張機能を宣言していないクライアントには、影響を受けることなく同期的な結果が返されます。
設定
mcp-v8 は、ストレージバックエンド、トランスポート、実行制限、ポリシー、フェッチヘッダーの挿入、WASM モジュール、クラスタリング、JWKS 認証、プロンプト/ツール説明のオーバーライドなど、すべて CLI フラグで設定します。常に最新の完全な一覧は、生成されたCLI フラグリファレンスを参照してください。
mcp-v8 --help # all flags
mcp-v8 --print-openapi # print the REST OpenAPI spec
アップストリーム MCP サーバー向けブラウザー OAuth
--mcp-config を使用して、OAuth 2.1 の認可コードフローで保護された Streamable HTTP MCP サーバーに接続します。この設定は JSON 専用であり、簡易な --mcp-server 構文では利用できません。
[
{
"name": "protected",
"transport": "http",
"url": "https://mcp.example.com/mcp",
"auth": {
"type": "oauth_browser",
"scope": ["tools.read", "tools.call"],
"client_id": "optional-registered-client-id",
"client_secret": "optional-client-secret",
"redirect_port": 48123,
"token_cache": "/home/alice/.cache/mcp-js/oauth-protected.json"
}
}
]
auth 内の type 以外のすべてのフィールドは省略可能です。client_id を指定しない場合、サーバーは OAuth 動的クライアント登録を使用します。指定した場合は、登録済みのクライアントを使用します。scope は要求するスコープの配列です。
保護リソース、認可、トークン、動的登録の各エンドポイントでは、ループバックエンドポイントを除き HTTPS が必要です。これにより、ローカルの OAuth テストサーバーは使用できますが、暗号化されていないリモートの認可インフラストラクチャは拒否されます。
初回使用時、または利用可能な認証情報がキャッシュにない場合、mcp-v8 はループバックコールバックリスナーを起動し、認可 URL を表示します。その後、その URL をローカルで開こうとします。ヘッドレスホストの場合は、表示された URL を別のマシンのブラウザーにコピーして認可を完了し、そのブラウザーからホストの http://localhost:<port>/callback URL にアクセスできることを確認してください(SSH ポートフォワードなどを利用できます)。認可の待機時間は最大 5 分です。OAuth の state が一致しないコールバックは無視されます。認可が拒否された場合、またはタイムアウトした場合は、接続に失敗します。
redirect_port を省略すると、リスナーは利用可能なローカルポートを選択します。ID プロバイダーで登録済みのコールバックポートが必要な場合にのみ設定してください。コールバックは常にループバックにバインドされ、結果の http://localhost:<port>/callback リダイレクト URI を使用します。
認証情報は token_cache にキャッシュされます。指定がない場合は、デフォルトで ${XDG_CACHE_HOME:-$HOME/.cache}/mcp-js/oauth-<server-name>.json に保存されます(どちらのキャッシュ場所も利用できない場合は、システムの一時ディレクトリに保存されます)。キャッシュは MCP URL、スコープ、クライアント設定に紐付けられます。OAuth 認証情報は、初回接続時または再接続時に毎回解決されます。有効なリフレッシュトークンがあれば、その解決時に期限切れのアクセストークンが更新され、ブラウザーは開きません。キャッシュされた認証情報でトークンを取得できない場合にのみ、対話型認可が行われます。確立済みのトランスポートは、再接続または無効になるまでベアラートークンを保持します。確立済みのトランスポートに対して、事前にトークンを更新することはありません。
キャッシュにはリフレッシュトークンが含まれることがあるため、秘密情報として扱ってください。Unix では、mcp-v8 はキャッシュをモード 0600 で書き込み、シンボリックリンク、通常ファイル以外のファイル、別のユーザーが所有するファイル、グループまたはその他のユーザーが読み取り可能なファイルを拒否します。安全でないファイル、所有者が異なるファイル、またはシンボリックリンクのキャッシュファイルは使用されません。これらは利用不可として扱われるため、再認可が開始され、認可に成功するとキャッシュが安全に置き換えられることがあります。コミット、共有、編集はしないでください。ローカルアクセスを取り消すには、認可サーバーで許可を取り消し、設定済みのキャッシュファイルを削除してください。次回の接続時に、認可が再度開始されます。
CLI クライアント(mcp-v8-cli)
progenitor を使用して OpenAPI 仕様から生成された、完全な型定義を備えた REST API クライアントです。
mcp-v8 --stateless --http-port 3000 &
mcp-v8-cli exec "console.log('hello'); 1 + 1"
mcp-v8-cli exec --file ./script.js # run a local file (uploaded as the code)
mcp-v8-cli executions get <execution_id>
mcp-v8-cli executions output <execution_id>
export MCP_V8_URL=https://my-server.example.com # point at a remote server
Rust クライアント(mcp-v8-client)
[dependencies]
mcp-v8-client = { git = "https://github.com/r33drichards/mcp-js" }
use mcp_v8_client::Client;
let client = Client::new("http://localhost:3000");
let body = mcp_v8_client::types::ExecRequest {
code: "1 + 1".to_string(),
heap: None, session: None,
heap_memory_max_mb: None, execution_timeout_secs: None, tags: None,
};
let resp = client.exec_handler(&body).await?;
println!("execution_id: {}", resp.into_inner().execution_id);
TypeScript クライアント(@mcp-v8/client)
OpenAPI 仕様から生成された、こちらも完全な型定義を備えた TypeScript クライアントです(openapi-typescript の型定義と openapi-fetch のランタイムを使用)。clients/typescript にあります。
import { createMcpV8Client } from "@mcp-v8/client";
const client = createMcpV8Client("http://localhost:3000");
const { status, output, heap } = await client.runJs("console.log('hi'); 1 + 1");
API の変更後は、npm run generate を実行して型定義を再生成してください(openapi.json を読み込みます)。
ソースからビルドする
このリポジトリは Nix flake です(事前取得した V8 アーカイブを組み込み、ビルドをオフラインでも行いやすくしています)。
nix build github:r33drichards/mcp-js # → ./result/bin/server
# or for development:
nix develop # then: cargo build -p server
deno_core/V8 をビルドできるツールチェーンがあれば、server/ 内で通常の cargo build --release を実行してもビルドできます。
制限事項
setIntervalは利用できません —setTimeoutを await するループを使用してください。- DOM やブラウザー API はありません —
window/documentはありません。 - TypeScript は型を除去するだけで、型チェックは行いません — 無効な型は報告されずに除去されます。JSX/TSX はサポートされていません(解析時に明確なエラーになります)。
ライセンス
MCP-V8 負荷テストベンチマークレポート v0.18.1
さまざまなリクエストレートでの単一ノードと3ノードクラスターの比較です。
結果
| トポロジー | 目標レート | 実際の Iter/s | HTTP Req/s | 実行平均 (ms) | 実行 p95 (ms) | 実行 p99 (ms) | 成功率 % | ドロップ数 | 最大 VU 数 |
|---|---|---|---|---|---|---|---|---|---|
| cluster-stateful | 100/s | 99.9 | 301.3 | 52.42 | 53.46 | 102.44 | 100% | 1 | 11 |
| cluster-stateful | 200/s | 134.5 | 3855.4 | 1359.67 | 4062.32 | 4119.07 | 100% | 3450 | 200 |
| cluster-stateless | 1000/s | 602.2 | 1609.8 | 1377.83 | 5443.24 | 8890.83 | 99.9% | 22267 | 1000 |
| cluster-stateless | 100/s | 99.9 | 299.8 | 51.49 | 52.6 | 53.34 | 100% | 0 | 10 |
| cluster-stateless | 200/s | 199.8 | 599.5 | 51.92 | 53.43 | 55.14 | 100% | 0 | 20 |
| cluster-stateless | 500/s | 499.6 | 1498.5 | 53.2 | 56.33 | 63.75 | 100% | 0 | 50 |
| single-stateful | 100/s | 51.3 | 1970.1 | 1849.35 | 2081.54 | 2103.18 | 100% | 2823 | 100 |
| single-stateful | 200/s | 46.6 | 3828.4 | 4074.71 | 4628.01 | 4673.8 | 100% | 9020 | 200 |
| single-stateless | 1000/s | 115.4 | 430.8 | 8174.65 | 13854.8 | 16506.74 | 90% | 52654 | 1000 |
| single-stateless | 100/s | 99.9 | 299.6 | 53.31 | 55.96 | 59.52 | 100% | 4 | 14 |
| single-stateless | 200/s | 198.2 | 589.1 | 68.52 | 126.81 | 337.57 | 100% | 50 | 69 |
| single-stateless | 500/s | 138.9 | 348.3 | 3460.41 | 7332.03 | 10000.8 | 99.7% | 21416 | 500 |
P95 レイテンシ
| トポロジー | レート | P95 (ms) | |
|---|---|---|---|
| cluster-stateful | 100/s | 53.46 | ████████████ |
| cluster-stateful | 200/s | 4062.32 | ██████████████████████████ |
| cluster-stateless | 100/s | 52.6 | ████████████ |
| cluster-stateless | 200/s | 53.43 | ████████████ |
| cluster-stateless | 500/s | 56.33 | █████████████ |
| cluster-stateless | 1000/s | 5443.24 | ███████████████████████████ |
| single-stateful | 100/s | 2081.54 | ████████████████████████ |
| single-stateful | 200/s | 4628.01 | ███████████████████████████ |
| single-stateless | 100/s | 55.96 | █████████████ |
| single-stateless | 200/s | 126.81 | ███████████████ |
| single-stateless | 500/s | 7332.03 | ████████████████████████████ |
| single-stateless | 1000/s | 13854.8 | ██████████████████████████████ |
注記
- 目標レート:設定された constant-arrival-rate(k6 が試行するリクエスト数/秒)
- 実際の Iter/s:達成した1秒あたりのイテレーション数(各イテレーション = 1 POST /api/exec)
- HTTP Req/s:1秒あたりの HTTP リクエスト総数(イテレーションごとに1件)
- ドロップ数:VU が不足し、k6 がスケジュールできなかったイテレーション数(サーバーの飽和を示します)
- トポロジー:
single= MCP-V8 ノード1台、cluster= Raft を使用する MCP-V8 ノード3台
動的 HTTP ハンドラーは再起動せずに登録できます。オプトインのセットアップ、条件付き更新、認証、リクエスト/レスポンスの仕様については、HTTP 関数を参照してください。
スケジューラーと HTTP のドキュメント
Diátaxis ドキュメントマップでは、実験的なスケジューラーと動的 HTTP 関数について、ガイド付きチュートリアル、運用手順、リファレンス、解説へのリンクを提供しています。#284 の堅牢化が #287、#293、#295 を通じて統合されたことを記録していますが、メインへのマージやデプロイを示すものではありません。
オプションの DynamoDB メタデータバックエンド
共有セッション履歴、ヒープタグ、ファイルシステムのラベル/reflog メタデータには、--dynamodb-table TABLE --dynamodb-namespace NAME を使用します。デフォルトは引き続き Sled です。スナップショットの内容は、別途選択した S3/ディレクトリバックエンドに保存されます。テーブルスキーマ、IAM、制限、LocalStack/Testcontainers のセットアップについては、メタデータバックエンドと統合テストを参照してください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。