新しいModel Context Protocol(MCP)サーバーは、クライアントコンテキストにツールを追加します。50ツールのサーバーが10個あれば、モデルが最初のユーザーメッセージを読む前に、500のツール定義がロードされます。

エンタープライズ規模——内部API、SaaS統合、データプラットフォーム——では、ツールの肥大化が支配的なコストになります。エージェントが決して呼ばないスキーマにトークンが使われます。ルーティングは遅くなります。ツール選択は混乱します。

解決策は、統合を減らすことではありません。ツールへの段階的開示です。巨大なダンプではなく、層で能力を明かします。

CloudflareのCode Modeパターンは、すべての上流操作を宣伝する代わりに、二つのツール——searchとexecute——でMCPにそれを実装します。設計の根拠とコンテキスト節約は、Code Mode: give agents an entire API in 1,000 tokensにあります。

このパターンは、発見と実行を分離します。searchは、OpenAPIドキュメント、あるいはツールレジストリに対して、隔離されたWorkerサンドボックスでモデル記述のJavaScriptを実行します。コードが返すサブセットだけが、モデルコンテキストに入ります。フィルタされたパス、パラメータ名、スキーマスライスです。

executeは、ホスト提供の認証済みリクエスト関数でサンドボックスコードを実行します。モデルはAPI呼び出しを組み立て、レスポンスをマップし、焦点を絞った結果を返します。認証情報はホストWorkerに留まります。サンドボックスはトークンを見ません。

search と execute が直接ツールに勝るとき

直接のMCPツールは、表面が小さく安定しているとき——名前が明確な十数の操作——に機能します。

search と execute が勝つのは、上流APIに数百または数千のOpenAPI操作があるときです。複数のMCPサーバーを一つのポータル背後に集約するとき、ツール説明がコンテキスト予算の大きな割合を消費するとき、APIの進化に合わせて操作ごとのMCPスキーマを保守する代わりに、拡張可能な基盤が欲しいときにも勝ちます。

ツール数が少ない場合でも、一度投資するエンジニアリング能力があれば、この型は価値があります。二つの安定したMCPツールを出荷し、新しいMCPツール定義ではなく、OpenAPIの更新でAPI操作を追加します。新入りとエージェントは、ツールリストをスクロールするのではなく、searchコードで能力を発見します。

前提条件

Cloudflare Workersプロジェクト、APIのOpenAPI 3.xドキュメント(またはプログラムで公開できるツールカタログ)、ホスト側の認証方法が必要です。@cloudflare/codemode、agents、@modelcontextprotocol/sdk、zodをインストールします。wrangler.jsoncにWorker Loaderバインディングとnodejs_compat互換フラグを追加します。Code Modeは実験的です。本番の硬化の前に評価してください。

例:wrangler.jsonc

DynamicWorkerExecutorサンドボックスに必要なWorker Loaderバインディング

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "openapi-codemode-mcp",
  "main": "src/server.ts",
  "compatibility_date": "2026-08-15",
  "compatibility_flags": ["nodejs_compat"],
  "worker_loaders": [{ "binding": "LOADER" }]
}

例:openApiMcpServer()を使うホストWorker

src/server.ts — 認証情報はホスト、search と execute はサンドボックス

import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { openApiMcpServer } from "@cloudflare/codemode/mcp";
import { createLegacyMcpHandler } from "agents/mcp";

const SPEC_URL = "https://api.example.com/openapi.json";
const API_ORIGIN = "https://api.example.com";

export default {
  async fetch(request, env, ctx) {
    const authorization = request.headers.get("Authorization");
    if (!authorization?.startsWith("Bearer ")) {
      return new Response("Bearer token required", { status: 401 });
    }
    const spec = await (await fetch(SPEC_URL)).json();
    const server = openApiMcpServer({
      spec,
      executor: new DynamicWorkerExecutor({ loader: env.LOADER }),
      name: "example-api",
      request: async (options) => {
        const url = new URL(`${API_ORIGIN}${options.path}`);
        for (const [k, v] of Object.entries(options.query ?? {})) {
          if (v !== undefined) url.searchParams.set(k, String(v));
        }
        const response = await fetch(url, {
          method: options.method,
          headers: { Authorization: authorization, "Content-Type": "application/json" },
          body: options.body === undefined ? undefined : JSON.stringify(options.body),
        });
        if (!response.ok) throw new Error(`API request failed: ${response.status}`);
        return response.headers.get("Content-Type")?.includes("json")
          ? await response.json()
          : await response.text();
      },
    });
    return createLegacyMcpHandler(server, { route: "/mcp" })(request, env, ctx);
  },
};

npx wrangler deployでデプロイします。MCPクライアントを、bearerトークン付きでhttps://<worker>.<subdomain>.workers.dev/mcpに接続します。ツールをリストします。searchとexecuteが見えるはずで、操作ごとのツールが何百もあるはずではありません。完全なウォークスルーはBuild a search and execute MCP serverです。

フェーズ1:スキーマを検索する

executeの前にsearchを呼び出します。モデルは、サンドボックス内でOpenAPIドキュメントを検査するJavaScriptを送信します。

モデルが書いたsearchコード——返されたパスだけがコンテキストに入る

async () => {
  const spec = await codemode.spec();
  return Object.entries(spec.paths)
    .filter(([path]) => path.includes("/orders"))
    .map(([path, operations]) => ({ path, methods: Object.keys(operations) }));
};

ローカルの$refは、サンドボックス内で解決されます。完全なドキュメントは、searchコードが明示的に一部を返さないかぎり、モデルコンテキストの外に留まります。

フェーズ2:認証済みリクエストでexecuteする

操作を選んだあと、モデルはホスト提供のリクエスト関数を使うコードでexecuteを呼び出します。

モデルが書いたexecuteコード——返す前にマップとフィルタ

async () => {
  const response = await codemode.request({
    method: "GET",
    path: "/orders",
    query: { status: "processing", limit: 20 },
  });
  return response.items.map(({ id, status }) => ({ id, status }));
};

生成コードは、返す前に選択、マップ、集約、またはページネーションしてください。パブリッシャーは、およそ6,000推定トークンでレスポンスを制限し、--- TRUNCATED ---で切り詰めをマークします。中間のAPI作業は、それでも実行されます。返すものは、生のダンプではなく、判断サイズに設計します。

セキュリティ境界

MCPサーバーを作る前に、ホストWorkerでbearerトークンを読み、認可を強制します。トークンはサンドボックスに入りません。DynamicWorkerExecutorは、デフォルトで外部のfetch()とconnect()をブロックします。生成コードは、ホストのrequestコールバック経由でのみ、APIに到達します。

パスを検証します。任意のオリジンを受け入れないでください。openApiMcpServer()は、execute内の操作ごとの永続的承認を提供しません。副作用の前に、ホストコールバックで認可を強制してください。OpenAPIドキュメントやAPIレスポンスにシークレットを埋め込まないでください。どちらも、モデルが書いたコードから見えます。

多数のMCPサーバーを集約する

組織が単一APIだけを持つことは稀です。CloudflareのMCPポータルは、search_and_execute最適化モードを公開し、すべての上流ツールを隠し、portal_query_toolsとportal_execute経由でプロキシします。ポータル層でも、同じ二つのツール表面です。ポータルURLで?optimize_context=search_and_executeを付けて有効化します。

初期トークンコストは、背後にいくつの上流サーバーがあっても、小さな定数になります。Cloudflareは切り替えをMCPポータルのコンテキスト最適化で文書化しています。

代替:既存MCPサーバー向けの単一codeツール

すでに管理可能な、離散ツールのMCPサーバーを運用している場合、codeMcpServer()は二つではなく一つのcodeツールでラップします。上流ツールはサーバー側に登録されたままです。クライアントは、単一のコード実行表面を見ます。

上流の表面がOpenAPIドキュメント、あるいは無制限のツールカタログなら、search と execute を使います。既知のツールセットを持つ既存MCPサーバーをラップするなら、単一のcodeツールを使います。どちらの型も、中間結果をコンテキストの外に保ちます。ガイドはBuild a Code Mode MCP serverです。

ここから構築する

Workerをプラットフォーム層として扱います。新しいMCPツールなしで、OpenAPIバージョンを追加します。アイデンティティ、サーバートグル、WriteGuardの書き込み制御には、MCPポータルを重ねます。

そのプラットフォームを、AGENTS.mdのリポジトリレベルエージェントコンテキストと、エージェント向けナレッジの整理のようなプライベート知識ベースと組み合わせます。

Cloudflare API MCPサーバーは、この型で完全なCloudflare APIをsearch と execute 経由で公開しています。このアプローチが本番API表面にスケールすることの証明です。

Dylan Engelbrechtは、MCPとエージェントの道具が進化するにつれ、この知識ハブを頻繁に更新します。llms.txtを読むクローラーと、リポジトリのAGENTS.mdからリンクを辿るエージェントは、これらの記事を生きた参照として扱えます。Cloudflareが次のCode Modeリリースを出すたびに古くなる静的スナップショットではなく、現在のMCPアーキテクチャ実践です。