Claude Media
MCPでコード実行する設計 — Anthropicが示す「ツール呼び出し」から「コードAPI」への移行

MCPでコード実行する設計 — Anthropicが示す「ツール呼び出し」から「コードAPI」への移行

MCPツールをコードAPIとして呼ぶ設計で、15万トークンが2,000トークンに。Claude Codeのツール検索、APIのProgrammatic Tool Callingとの違いも解説します。

2025年11月4日にAnthropicのエンジニアリングブログが公開した「Code Execution with MCP: Building More Efficient Agents」は、エージェントからMCPを使う実装パターンを示した記事です。MCP(Model Context Protocol)は、モデルとツールを繋ぐ標準プロトコルです。

主張は1つです。MCPツールをモデルに直接呼ばせず、コードとして呼び出させると、消費トークンが大きく減ります。Google DriveからSalesforceへ会議メモを転記する例では、150,000トークンが2,000トークンになり、削減率は98.7%と書かれています。

数字

記事に出てくる数字

  • 転記タスクの消費

    150,000 → 2,000

    トークン。削減率98.7%

  • 2時間の会議の追加分

    約50,000

    全文が2回モデルを通る場合のトークン

  • スプレッドシート

    10,000行 → 5行

    モデルに見せる行数

いずれも原文の例示で、実測値の一般化ではありません

MCPをすでに使っているチームにとって、この記事の価値は2つあります。Claude Codeの「ツール検索」が実装している部分と、まだ実装されていない部分を切り分けられること。そしてAPI側のProgrammatic Tool Callingとは対象が違うと分かることです。MCPの基礎はMCP実用ガイドにあります。エージェント実装側はAgent SDK入門が入口です。

なぜMCPのツールがcontext windowを圧迫するのか

原文は、エージェントのコストとレイテンシを押し上げる要因を2つ挙げています。

1つ目はツール定義の前ロードです。多くのMCPクライアントは、接続した全ツールの定義を最初にコンテキストへ入れます。原文は、数千のツールにつながるエージェントでは、ユーザーの依頼を読む前に数十万トークンを処理することになると書いています。ツールが数個なら問題になりません。数十のサーバーを束ねる構成で効いてきます。

2つ目は中間結果の通り抜けです。「Google Driveの会議トランスクリプトをSalesforceのリードに添付して」という依頼を考えます。直接呼び出しでは、getDocument の戻り値として全文がモデルに流れ込みます。続く updateRecord の引数として、同じ全文をモデルがもう一度書き出します。2時間の商談なら、追加で約50,000トークンです。

さらに、文書が大きければコンテキストの上限を超えてワークフローが止まります。原文は、長いデータをモデルが転記する過程でミスが入りやすくなる点にも触れています。

ツールをファイルツリーに置き、コードから呼ぶ

原文の解決策は、MCPサーバーをコードAPIとして見せることです。接続中のサーバーのツールをTypeScriptのファイルツリーに展開します。

servers
├── google-drive
│   ├── getDocument.ts
│   └── index.ts
├── salesforce
│   ├── updateRecord.ts
│   └── index.ts

各ファイルは、MCPツールを呼ぶ薄い型付きラッパーです。中身は callMCPTool('google_drive__get_document', input) のように、元のMCPツール名を指定して呼び出すだけです。この構造を使うと、転記の依頼はモデルが書く次のコードになります。

import * as gdrive from './servers/google-drive';
import * as salesforce from './servers/salesforce';
 
const transcript = (await gdrive.getDocument({ documentId: 'abc123' })).content;
await salesforce.updateRecord({
  objectType: 'SalesMeeting',
  recordId: '00Q5f000001abcXYZ',
  data: { Notes: transcript }
});

トランスクリプトは変数 transcript に入るだけで、モデルのコンテキストを通りません。モデルが書くのは数行のコードです。

手順

エージェントがツールを見つけて使うまで

  1. 1

    サーバーの一覧を見る

    ./servers/ を一覧し、google-drive と salesforce があると知ります。ツールの中身はまだ読みません。

  2. 2

    必要なファイルだけ開く

    getDocument.ts と updateRecord.ts を読み、引数と戻り値の型を把握します。使わない数百のツール定義はコンテキストに入りません。

  3. 3

    コードを書いて実行環境に渡す

    2つのツールをつなぐコードを書きます。実行はサンドボックス側で、データは変数として実行環境の中に留まります。

  4. 4

    見せたい結果だけ返す

    console.log や戻り値で出した分だけがモデルに返ります。

4つの効き方

必要なツールだけを後から読む(progressive disclosure)

モデルはファイルシステムの探索が得意で、定義を必要になった時点で読めます。原文はもう1つの形として、サーバーに search_tools を足す案も挙げています。「salesforce」で検索して必要な定義だけを取り込む形です。詳細度の引数を持たせ、名前だけ・名前と説明・スキーマ込みの全定義から選べるようにすると、さらにコンテキストを節約できると書かれています。

Claude CodeのSkillsも同じ作りです。Skillの説明文は既定では常にコンテキストにあり、本文は呼び出されたときに読み込まれます。MCPのツール定義にも同じ段階的な読み込みを持ち込む提案、と位置づけると分かりやすくなります。

中間結果をモデルに見せない

10,000行のスプレッドシートから未処理の注文だけを取り出す場合を考えます。直接呼び出しでは10,000行がモデルを通ります。コード実行なら、フィルタはサンドボックス内の filter で終わります。

const allRows = await gdrive.getSheet({ sheetId: 'abc123' });
const pendingOrders = allRows.filter(row => row["Status"] === 'pending');
console.log(`Found ${pendingOrders.length} pending orders`);
console.log(pendingOrders.slice(0, 5)); // 確認用に先頭5行だけ

モデルが見るのは件数と5行です。集計・複数データソースの結合・特定フィールドの抽出も同じ型で書けます。

ループと条件分岐をコードに任せる

原文の例は、Slackに「デプロイ完了」の投稿が出るまで待つ処理です。コードなら while と5秒の待機で書けます。ツール呼び出しとsleepをエージェントのループで交互に繰り返すより効率的で、条件分岐の評価もモデルを待たずに済みます。原文はこれを最初のトークンが出るまでの待ち時間の短縮にも結びつけています。

個人情報をモデルに通さない

サンドボックス内の中間結果は、ログに出すか返すかしない限りモデルに見えません。さらに、ハーネス側で個人情報を自動でトークン化できます。スプレッドシートの行をモデルがログに出すと、メールアドレスは [EMAIL_1]、電話番号は [PHONE_1] のように見えます。別のMCPツールへ渡す段階で、MCPクライアントが元の値に戻します。

実際のメールアドレスはGoogle SheetsからSalesforceへ流れますが、モデルは一度も見ません。データの流入元と流出先を決定的なルールで決められる点も、原文は利点に挙げています。

直接呼び出しとコード実行の違い

くらべる

同じ転記タスクの2つの経路

従来

ツールを直接呼ぶ

全ツールの定義が最初からコンテキストに入ります。getDocument の戻り値と updateRecord の引数で、全文が2回モデルを通ります。1ステップごとにモデルが次の呼び出しを判断します。

提案

コードとして呼ぶ

必要な定義だけを後から読みます。全文は実行環境の変数に留まり、モデルには要約や件数が返ります。ループや分岐はコードが処理します。

削減幅は、2回通るデータの重さにほぼ比例します。短いテキスト1件のコピーなら差はわずかです。長文ドキュメント、大きなスプレッドシート、会議の書き起こしのような重い中間データで効きます。

現在の天気を1回問い合わせるだけのような軽い処理では、サンドボックスを用意するコストのほうが上回りえます。

保存した手順がSkillになる

原文の最後の論点は、状態の保持です。ファイルシステムにアクセスできれば、途中結果を ./workspace/leads.csv のようなファイルに書き出し、後続の実行で続きから再開できます。

エージェントが書いたコードを再利用可能な関数として保存する運用も紹介されています。たとえば、スプレッドシートをCSVに保存する saveSheetAsCsv を ./skills/ に置けば、次回以降は同じ処理をモデルに書かせずに呼び出せます。SKILL.md を添えると、Skillとして体裁が整うと原文は述べています。CLAUDE.mdの書き方と同じく、エージェントの知識をファイルとして育てる発想です。

Skill、MCP、サブエージェントはいずれも、全部を最初から渡さず必要な部分だけを取り出す方向で設計されています。この記事は、MCPをその系列に寄せる提案です。

Claude Codeではどこまで実現しているか

ここからは、原文にはない実装側の話です。Claude Codeの公式ドキュメントによると、MCPのツール検索は既定で有効です。起動時に読み込まれるのはツール名とサーバーの指示文だけで、ツール定義は必要になるまで遅延されます。サーバーを増やしてもコンテキストへの影響は小さく、サーバーごとのツール数に固定の上限もありません。

原文の「定義を後から読む」部分は、Claude Codeでは製品の既定動作になっています。一方、ファイルツリーに展開してコードで呼ぶ部分は、この仕組みの範囲外です。

環境変数 ENABLE_TOOL_SEARCH で挙動を切り替えられます。

値挙動
未設定挙動全MCPツールを遅延。一部の環境では起動時に全読み込みへ戻る
true挙動全MCPツールを遅延
auto挙動ツール定義の合計がコンテキストの10%未満なら先に読み込み、10%に達したら全て遅延
auto:N挙動閾値をN%(0〜100)に変更
false挙動全ツールを起動時に読み込み。遅延なし
# 閾値を5%にして起動する
ENABLE_TOOL_SEARCH=auto:5 claude

毎ターン使うツールが少数ある場合は、そのサーバーの設定に alwaysLoad: true を書くと、遅延の対象から外せます。この設定を入れたサーバーは、全ツールが起動時にコンテキストへ入ります。大量に接続したサーバーに付けると、遅延の効果が消える点に注意が必要です。

検索の対象になるのは定義です。ツールの戻り値は別の仕組みで扱われます。MCPツールの出力が10,000トークンを超えると警告が出て、既定の上限は25,000トークンです。テキスト結果が50,000文字を超えると、会話には置かれずファイルに保存され、Claudeはそのパスから必要なときに読みます。原文の「大きな中間結果をモデルに通さない」という狙いを、直接呼び出しの側から部分的に補う仕組みと言えます。

ただし、保存されたファイルをClaudeが読めば、その内容はやはりコンテキストに入ります。コードで絞り込んでから返す設計とは役割が異なります。

APIのProgrammatic Tool Callingとの違い

Anthropic APIには、似た発想のProgrammatic Tool Callingがあります。ツール定義に allowed_callers: ["code_execution_20260120"] を付けると、そのツールをコード実行コンテナの中から呼べます。使い分けはallowed_callersの3パターン使い分けにまとめています。

公式ドキュメントには、MCPコネクタ経由のツールはプログラム的に呼べないという制限が書かれています。つまり、MCPコネクタ経由のツールのままでは、このブログのパターンをAPIの標準機能だけで再現できません。自前でMCPクライアントとコード実行環境をつなぐ実装が前提です。

観点本記事の設計Programmatic Tool Calling
対象のツール本記事の設計MCPサーバーのツールProgrammatic Tool Callingallowed_callers を付けた自前のツール
MCPコネクタのツール本記事の設計コードAPIとして公開できるProgrammatic Tool Callingプログラム的には呼べない
実行環境本記事の設計自前のサンドボックスProgrammatic Tool CallingAPIが管理するコンテナ
状態の保持本記事の設計自前のファイルシステムProgrammatic Tool CallingコンテナのIDを渡して再利用

コンテナにも制約があります。ドキュメントによると、アイドル状態のコンテナはおよそ5分で回収され、作成から30日を超えると再利用できません。ツールの結果を待つ間、保留中の呼び出しは約4分でタイムアウトし、コード内で TimeoutError になります。長い人手の承認を挟む処理は、この時間に収まるかを先に見積もる必要があります。

Claude Haiku 4.5は、code_execution_20260120 以降のツールバージョンを受け付けますが、プログラム的な呼び出しには対応していません。

サンドボックスの運用コスト

原文は、利点だけを述べているわけではありません。

Running agent-generated code requires a secure execution environment with appropriate sandboxing, resource limits, and monitoring. These infrastructure requirements add operational overhead and security considerations that direct tool calls avoid.

エージェントが生成したコードを動かす以上、悪意のある指示が混ざってもコードが安全に動く環境が要ります。直接呼び出しでは、ホストが呼べるツールを限定すれば足ります。コード実行では、サンドボックスの中で何が走っても安全という前提をホストが担保します。

運用観点ツール直接呼び出しコード実行経由
攻撃面ツール直接呼び出しホストが許可するMCPツールコード実行経由サンドボックスで実行されるコード全般
必要なインフラツール直接呼び出しMCPホストのみコード実行経由サンドボックスとリソース制限、監視が加わる
監査ログツール直接呼び出しツール呼び出しの記録コード実行経由コード本体と実行ログ

Claude Codeを社内の基盤として使うチームでは、この負担がそのまま跳ね返ります。原文は、トークン削減・低レイテンシ・ツールの組み合わせやすさという利点と、この実装コストを比べるよう求めています。

Cloudflareの「Code Mode」

原文は、Cloudflareが同じ着眼点の知見をすでに公開していて、MCPのコード実行を「Code Mode」と呼んでいると書いています。核心は共通で、LLMはコードを書くのが得意なので、その強みを使ってMCPサーバーとのやり取りを効率化する、というものです。

別々の組織が同じ結論に至ったことで、この設計はAnthropic固有の工夫ではなく、LLMとツールをつなぐ共通のパターンとして見えてきます。

自作MCPサーバーのツール定義を検証する手段としては、InsomniaのMCPクライアント機能があります。コードAPIとして公開する前に、ツールの入出力を手で確かめられます。

導入前に決めておくこと

原文は設計パターンの提示にとどまり、汎用ライブラリの公開には触れていません。実装に進むなら、次の点を自分で決める必要があります。

  • サンドボックスの実行基盤と、CPU・メモリ・ネットワークの制限
  • 既存のMCPサーバーをコードAPIにする方法(ツール定義からの自動生成か、手書きのラッパーか)
  • 監査ログの粒度(コードの全文を残すか、ツール呼び出しだけを残すか)
  • 外部からの指示でモデルが危険なコードを書く場合の防御

先に試せる範囲もあります。ENABLE_TOOL_SEARCH=false と既定の状態で、同じ作業を試して比べると、前ロードの重さを自分の構成で測れます。

まとめ

数百のツールや大きな中間データを扱うエージェントで、効果が大きい設計です。軽い呼び出しが中心なら、サンドボックスの運用コストが上回りえます。まず、自分の構成でツール定義と中間結果のどちらが重いかを測ると、導入の要否が判断できます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. anthropic.com/engineering/code-execution-with-mcp
  2. code.claude.com/docs/en/mcp
  3. platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling
  4. code.claude.com/docs/en/skills
この記事を共有:XはてブLinkedIn