🚀 MCPコード実行サーバー: 100以上のMCPツールのゼロコンテキストディスカバリー

クエリごとに30,000トークンの支払いをやめましょう。 このブリッジは、ルートレスセキュリティでAnthropicのディスカバリーパターンを実装し、MCPコンテキストを30Kから200トークンに削減しながら、任意のstdioサーバーをプロキシします。

🚀 クイックスタート
1. 前提条件 (macOSまたはLinux)
- バージョンを確認します:
python3 --version
- 必要に応じて、パッケージマネージャまたはpython.orgを通じてPython 3.14をインストールします。
- macOSの場合:
brew install podman または brew install --cask docker
- Ubuntu/Debianの場合:
sudo apt-get install -y podman または curl -fsSL https://get.docker.com | sh
curl -LsSf https://astral.sh/uv/install.sh | sh
podman pull python:3.14-slim
docker pull python:3.14-slim
Pydantic互換性に関する注意 (Python 3.14):
- Python 3.14を使用する場合、最新のPydanticリリースがインストールされていることを確認してください (例:
pydantic >= 2.12.0)。一部の古いPydanticバージョンや、PyPIから個別の typing パッケージをインストールする環境では、以下のようなエラーが発生する場合があります。
TypeError: _eval_type() got an unexpected keyword argument 'prefer_fwd_module'
このエラーが表示された場合は、以下のコマンドを実行してください。
pip install -U pydantic
pip uninstall typing
そして、プロジェクトのセットアップを再度実行します (例: .venv/ を削除して uv sync を実行)。
2. 依存関係のインストール
uvを使用してプロジェクト環境を同期します。
uv sync
3. ブリッジの起動
uvx --from git+https://github.com/elusznik/mcp-server-code-execution-mode mcp-server-code-execution-mode run
ローカルチェックアウトから実行する場合は、同等のコマンドは以下の通りです。
uv run python mcp_server_code_execution_mode.py
4. エージェントへの登録
エージェントのMCP設定ファイル (例: mcp_config.json, claude_desktop_config.json など) に以下のサーバー設定を追加します。
{
"mcpServers": {
"mcp-server-code-execution-mode": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/elusznik/mcp-server-code-execution-mode",
"mcp-server-code-execution-mode",
"run"
],
"env": {
"MCP_BRIDGE_RUNTIME": "podman"
}
}
}
}
5. コードの実行
result = await mcp_filesystem.read_file(path='/tmp/test.txt')
data = await mcp_search.search(query="TODO")
await mcp_github.create_issue(repo='owner/repo', title=data.title)
サーバーを明示的に読み込む
run_python は、要求したMCPサーバーのみを読み込みます。ツールを呼び出すときに servers 配列を介して渡すことで、mcp_serena や mcp_filesystem などのプロキシがサンドボックス内で使用可能になります。
{
"code": "print(await mcp_serena.search(query='latest AI papers'))",
"servers": ["serena", "filesystem"]
}
このリストを省略した場合でも、ディスカバリーヘルパーはすべてのサーバーを列挙しますが、読み込まれていないサーバーを対象とするRPC呼び出しは Server '<name>' is not available を返します。
注: servers 配列は、サンドボックス呼び出し用に生成されるプロキシを制御するだけです。cwd などのサーバー設定フィールドは設定されません。cwd プロパティはホスト/サーバー設定の一部であり、LLMは runtime.describe_server(name) を呼び出すか、runtime.list_loaded_server_metadata() を調べて、サーバーの作業ディレクトリを想定する前に構成された cwd を確認する必要があります。
注: サーバー設定には、オプションの cwd プロパティを含めることができます。存在する場合、ブリッジはその作業ディレクトリでホストMCPサーバープロセスを起動します。エージェントは、想定する前に runtime.describe_server(name) をチェックして、サーバーの構成された cwd を確認する必要があります。
✨ 主な機能
🛡️ 堅牢性と信頼性
- 遅延ランタイム検出: Podman/Dockerが準備できていなくてもすぐに起動します。コード実行が要求されたときにのみランタイムをチェックします。
- 自己参照防止: ブリッジを再帰的に起動する構成を自動的に検出してスキップします。
- ノイズフィルタリング: 多くのメッセージを出力するMCPクライアントからの良性なJSONパースエラー (空白行など) を無視します。
- スマートボリューム共有: Podman VMを調査して、古いバージョンでもボリューム共有が機能することを確認します。
🔒 セキュリティ第一
- デフォルトでルートレス: Podman/Dockerコンテナは
--cap-drop=ALL、読み取り専用ルート、新しい特権なし、および明示的なメモリ/PID制限で実行されます。
- ネットワーク分離: ネットワークアクセスがないため、外部との通信が遮断されます。
- 読み取り専用ファイルシステム: ルートディレクトリが不変であり、誤った書き込みを防ぎます。
- 特権削除: システムへのアクセスが制限され、セキュリティリスクを低減します。
- 非特権ユーザー: UID 65534で実行され、不必要な特権を持たないため、セキュリティが強化されます。
- リソース制限: メモリ、PID、CPU、時間に制限が設定され、過剰なリソース消費を防ぎます。
- 自動クリーンアップ: 一時的なIPCディレクトリが自動的にクリーンアップされ、不要なファイルが残らないようにします。
⚡ パフォーマンス
- 永続的なセッション: 呼び出し間で変数と状態が保持されるため、連続した操作が効率的に行えます。
- 永続的なクライアント: MCPサーバーがアクティブなままであり、応答時間が短縮されます。
- コンテキスト効率: 従来のMCPと比較して95%以上のコンテキスト使用量を削減します。
- 非同期実行: 適切なリソース管理により、並列処理が可能です。
- 単一のツール: Claudeのコンテキストには
run_python のみが含まれるため、オーバーヘッドが最小限に抑えられます。
🔧 開発者体験
mcp_servers["server"]
mcp_server_name
from mcp.servers.server import *
- トップレベルのawait: 最新のPythonパターンをサポートし、非同期コードの記述が容易になります。
- 型安全: 適切なシグネチャとドキュメントが提供され、コードの信頼性が向上します。
- コンパクトな応答: デフォルトではプレーンテキスト出力が提供され、必要に応じてTOONブロックを使用できます。
応答形式
- デフォルト (コンパクト): 応答はプレーンテキストと最小限の
structuredContent ペイロードでレンダリングされ、空でないフィールドのみが含まれます。stdout/stderr の行はそのまま保持されるため、コンテンツを犠牲にすることなくプロンプトをシンプルに保つことができます。
- オプションのTOON:
MCP_BRIDGE_OUTPUT_MODE=toon を設定すると、Token-Oriented Object Notation ブロックを出力します。空のフィールドは削除され、structuredContent で同じ構造が反映されます。TOONは、下流のプロンプトで決定的なトークン化が必要な場合に便利です。
- フォールバックJSON: TOONエンコーダーが利用できない場合は、トリミングされたペイロードを保持しながら自動的に整形されたJSONブロックにフォールバックします。
ディスカバリーワークフロー
SANDBOX_HELPERS_SUMMARY は、ツールスキーマ内でディスカバリーヘルパー (discovered_servers(), list_servers(), query_tool_docs(), search_tool_docs() など) のみを宣伝します。個々のサーバーまたはツールのドキュメントは含まれません。
- 初回使用時、LLMは通常
discovered_servers() (またはキャッシュされたリストの場合は list_servers_sync()) を呼び出してMCPサーバーを列挙し、次に query_tool_docs(server) / query_tool_docs_sync(server) または search_tool_docs("keyword") / search_tool_docs_sync("keyword") を呼び出して関連するドキュメントのサブセットを取得します。
- ツールメタデータは必要に応じてストリーミングされ、インストールされているサーバーやツールの数に関係なく、システムプロンプトは約200トークンに保たれます。
- LLMが必要なドキュメントを取得したら、生成された
mcp_<alias> プロキシまたは mcp.runtime ヘルパーを使用してツールを呼び出すPythonコードを記述します。
ヘルパーを調べることなく短い説明が必要ですか? runtime.capability_summary() を呼び出して、「コード実行MCPは何ができますか?」などの質問に回答するのに適した1段落の概要を印刷してください。
📦 インストール
上記のクイックスタートセクションを参照してください。そこには前提条件の確認、依存関係のインストール、ブリッジの起動、エージェントへの登録など、必要な手順が詳しく記載されています。
💻 使用例
基本的な使用法
files = await mcp_filesystem.list_directory(path='/tmp')
for file in files:
content = await mcp_filesystem.read_file(path=file)
if 'TODO' in content:
print(f"TODO in {file}")
高度な使用法
transcript = await mcp_google_drive.get_document(documentId='abc123')
summary = transcript[:500] + "..."
await mcp_salesforce.update_record(
objectType='SalesMeeting',
recordId='00Q5f000001abcXYZ',
data={'Notes': summary}
)
その他の使用例
マルチシステムワークフロー
issues = await mcp_jira.search_issues(project='API', status='Open')
for issue in issues:
details = await mcp_jira.get_issue(id=issue.id)
if 'bug' in details.description.lower():
await mcp_github.create_issue(
repo='owner/repo',
title=f"Bug: {issue.title}",
body=details.description
)
利用可能なサーバーの調査
from mcp import runtime
print("Discovered:", runtime.discovered_servers())
print("Cached servers:", runtime.list_servers_sync())
print("Loaded metadata:", runtime.list_loaded_server_metadata())
print("Selectable via RPC:", await runtime.list_servers())
loaded = runtime.list_loaded_server_metadata()
if loaded:
first = runtime.describe_server(loaded[0]["name"])
for tool in first["tools"]:
print(tool["alias"], "→", tool.get("description", ""))
if loaded:
summaries = await runtime.query_tool_docs(loaded[0]["name"])
detailed = await runtime.query_tool_docs(
loaded[0]["name"],
tool=summaries[0]["toolAlias"],
detail="full",
)
print("Summaries:", summaries)
print("Cached tools:", runtime.list_tools_sync(loaded[0]["name"]))
print("Detailed doc:", detailed)
results = await runtime.search_tool_docs("calendar events", limit=3)
for result in results:
print(result["server"], result["tool"], result.get("description", ""))
print("Capability summary:", runtime.capability_summary())
print("Docs from cache:", runtime.query_tool_docs_sync(loaded[0]["name"]) if loaded else [])
print("Search from cache:", runtime.search_tool_docs_sync("calendar"))
上記のスニペットをスタブサーバーで実行したときにLLMが見る例の出力:
Discovered: ('stub',)
Loaded metadata: ({'name': 'stub', 'alias': 'stub', 'tools': [{'name': 'echo', 'alias': 'echo', 'description': 'Echo the provided message', 'input_schema': {...}}]},)
Selectable via RPC: ('stub',)
listMcpResources を好むクライアントは、ヘルパースニペットの実行をスキップして、代わりに resource://mcp-server-code-execution-mode/capabilities リソースを要求することができます。サーバーは resources/list を介してこれを宣伝し、読み取ると同じヘルパー概要とサーバーを明示的に読み込むための短いチェックリストが返されます。
📚 ドキュメント
- README.md - このファイルで、クイックスタートガイドが提供されています。
- GUIDE.md - 包括的なユーザーガイドです。
- ARCHITECTURE.md - 技術的な詳細を深く掘り下げたドキュメントです。
- HISTORY.md - 進化と教訓について記述されています。
- STATUS.md - 現在の状態とロードマップが記載されています。
🔧 技術詳細
アーキテクチャ
┌─────────────┐
│ MCP Client │ (Your Agent)
└──────┬──────┘
│ stdio
▼
┌──────────────┐
│ MCP Code Exec │ ← Discovers, proxies, manages
│ Bridge │
└──────┬──────┘
│ container
▼
┌─────────────┐
│ Container │ ← Executes with strict isolation
│ Sandbox │
└─────────────┘
ゼロコンテキストディスカバリー
従来のMCPサーバーがすべてのツール定義を事前に読み込む (場合によっては30kトークン以上) のとは異なり、このブリッジはシステムプロンプトを約200トークンに固定し、LLMに必要なものを必要に応じて発見するように訓練します。
- LLMが
discovered_servers() を呼び出す → スキーマを読み込むことなく、利用可能なブリッジを学習します。
- LLMが
query_tool_docs("serena") を呼び出す → そのサーバーのツールドキュメントを読み込み、必要に応じてツールごとにフィルタリングします。
- LLMがオーケストレーションコードを記述する →
mcp_serena.search() や mcp.runtime.call_tool() などのヘルパーを呼び出します。
結果: 構成するMCPサーバーの数に関係なく、コンテキストの使用量は事実上一定に保たれます。
プロセス:
- クライアントが
run_python(code, servers, timeout) を呼び出す。
- ブリッジが要求されたMCPサーバーを読み込む。
- サンドボックス呼び出しを準備する: MCPツールメタデータを収集し、共有
/ipc ボリュームにエントリポイントを書き込み、MCP_AVAILABLE_SERVERS をエクスポートする。
- 生成されたエントリポイントは、stdioをJSONフレームメッセージに再構成し、コンテナのstdin/stdoutパイプを介してMCP呼び出しをプロキシする。
- 永続的な実行: コンテナは一度起動されると (実行中でない場合) アクティブなままになります。
- 状態保持: 1回の呼び出しで定義された変数、インポート、関数は、後続の呼び出しで利用可能です。
- ホストストリームハンドラはJSONフレームを処理し、MCPトラフィックを転送し、タイムアウトを強制し、次のリクエストのためにコンテナをアクティブに保ちます。
設定
環境変数
| 変数 |
デフォルト |
説明 |
MCP_BRIDGE_RUNTIME |
auto |
コンテナランタイム (podman/docker) |
MCP_BRIDGE_IMAGE |
python:3.14-slim |
コンテナイメージ |
MCP_BRIDGE_TIMEOUT |
30s |
デフォルトのタイムアウト |
MCP_BRIDGE_MAX_TIMEOUT |
120s |
最大タイムアウト |
MCP_BRIDGE_MEMORY |
512m |
メモリ制限 |
MCP_BRIDGE_PIDS |
128 |
プロセス制限 |
MCP_BRIDGE_CPUS |
- |
CPU制限 |
MCP_BRIDGE_CONTAINER_USER |
65534:65534 |
UID:GIDとして実行 |
MCP_BRIDGE_RUNTIME_IDLE_TIMEOUT |
300s |
シャットダウン遅延 |
MCP_BRIDGE_STATE_DIR |
~/MCPs |
IPCソケットと一時状態のホストディレクトリ |
MCP_BRIDGE_OUTPUT_MODE |
compact |
応答テキスト形式 (compact または toon) |
MCP_BRIDGE_LOG_LEVEL |
INFO |
ブリッジのログ詳細度 |
サーバーディスカバリー
主要な場所:
注意: 個々のエージェント構成ファイル (例: .claude.json, .vscode/mcp.json) のスキャンサポートは現在 延期されています。すべてのMCPサーバー定義を .json ファイルに ~/MCPs ディレクトリに配置して、検出されるようにしてください。
サーバーの例 (~/MCPs/filesystem.json):
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
}
}
注意: 再帰的な起動を防ぐため、ブリッジは mcp-server-code-execution-mode を再度起動するように見える構成エントリ ( uvx … mcp-server-code-execution-mode run を含む) を自動的にスキップします。ブリッジをネストされたMCPサーバーとして意図的に公開する必要がある場合は、MCP_BRIDGE_ALLOW_SELF_SERVER=1 を設定してください。
Docker MCPゲートウェイ統合
docker mcp gateway run を使用してサードパーティのMCPサーバーを公開する場合、ブリッジは単にゲートウェイバイナリを実行します。ゲートウェイはツールイメージの取得とstdioトランスポートの設定を担当するため、ホスト環境が準備されていることを確認してください。
- ゲートウェイカタログで参照されるすべてのレジストリ (例: Docker Hubの
mcp/* イメージ、ghcr.io/github/github-mcp-server) に対して docker login を実行します。キャッシュされた資格情報がないと、ツールがオンラインになる前に取得ステップが失敗します。
- それらのサーバーに必要なシークレットを提供します。
github-official は github.personal_access_token を必要とし、他のサーバーはAPIキーまたは認証トークンを期待する場合があります。docker mcp secret set <name> (またはゲートウェイが構成されている任意のメカニズム) を使用して、起動時にコンテナが値を確認できるようにします。
- カタログが期待するすべてのボリュームマウントまたは環境変数 (ファイルシステムパス、ストレージボリュームなど) を反映します。不足しているマウントまたは資格情報は、stdioハンドシェイク中に
failed to connect: calling "initialize": EOF として一般的に表示されます。
list_tools が内部管理ヘルパー (mcp-add, code-mode, …) のみを返す場合、ゲートウェイは外部サーバーの初期化を完了していません。ゲートウェイのログを確認して、欠落しているシークレットまたはレジストリアクセスエラーを確認してください。
状態ディレクトリとボリューム共有
- ランタイムアーティファクト (生成された
/ipc/entrypoint.py および関連するハンドシェイクメタデータを含む) は、デフォルトで ~/MCPs/ の下に保存されます。MCP_BRIDGE_STATE_DIR を設定して、それらを移動することができます。
- 選択されたランタイムがPodmanの場合、ブリッジは自動的に
podman machine set --rootful --now --volume <state_dir>:<state_dir> を実行して、VMがディレクトリをマウントできるようにします。--volume をサポートしていない古い podman machine ビルドでは、ブリッジは podman machine ssh test -d <state_dir> でVMを調査し、共有がすでに利用可能な場合は続行します。
- Docker Desktopはファイル共有のCLIを公開していないため、ブリッジを実行する前に、選択した状態ディレクトリがDocker Desktop → 設定 → リソース → ファイル共有で共有としてマークされていることを確認してください。
- 共有を手動で確認するには、
docker run --rm -v ~/MCPs:/ipc alpine ls /ipc (またはPodmanの同等のコマンド) を実行し、ファイルが表示されることを確認してください。
📄 ライセンス
GPLv3ライセンス
サポート
問題や質問がある場合は、ドキュメントを参照するか、問題を報告してください。