Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

MCP SDK v2 へ移行する

最終更新 Markdown で表示Agent セットアップ

このガイドは、Agents SDK v0.20.0 での MCP SDK v2 アップグレードを扱います。サーバーを @modelcontextprotocol/server へ移す方法、セッション依存の機能が必要なときだけ一時的なレガシーレーンを使う方法、MCP クライアントの更新方法を説明します。

サーバーの移行経路を選ぶ

次の表で移行経路を選びます。

現在のサーバー 移行経路
セッション依存のない SDK v1 サーバー サーバー定義を SDK v2 ファクトリへ移し、そのファクトリを createMcpHandler に渡します。
セッション依存のある SDK v1 サーバー SDK v2 ルートを追加します。依存を置き換えるあいだだけ、一時的なレガシーレーンに createLegacyMcpHandler を残します。
レガシーのステートフル機能を使わない McpAgent SDK v2 ファクトリと createMcpHandler へ直接移行します。
レガシーのステートフル機能を使う McpAgent ステートレス相当を設計し、ステートレスレーンとレガシーレーンを並行提供し、レガシーレーンを排出します。

Agents SDK v0.20.0 は、次の API を非推奨にします。

  • SDK v1 サーバーを createMcpHandler に渡すこと。サーバーを SDK v2 ファクトリへ移します。セッション依存の動作には、一時的な橋渡しとしてだけ createLegacyMcpHandler を使います。このオーバーロードは次のメジャーバージョンで削除予定です。
  • McpAgent。非推奨で、機能は凍結されています。できるだけ早く移行してください。削除バージョンは未発表です。
  • MCPClientManager.callTool(params, resultSchema, options)withX402Client(...).callTool(confirm, params, resultSchema, options)。代わりに callTool(params, options) または callTool(confirm, params, options) を使います。削除バージョンは未発表です。

experimental_createMcpHandler はすでに非推奨で、次のメジャーバージョンで削除予定のままです。その SDK v1 サーバーを SDK v2 ファクトリへ移します。移行中は、一時的なセッション依存レーンにだけ createLegacyMcpHandler を使います。

MCP パッケージをインストールする

アプリケーションがインポートする世代の MCP パッケージだけをインストールします。v2 のバージョンは固定してください。

ステートレスサーバーの場合:

npm i agents @modelcontextprotocol/server@2.0.0 zod

一時的なレガシーレーンの場合:

npm i agents @modelcontextprotocol/sdk@1.30.0 zod

MCP サーバーへ接続する Agent の場合:

npm i agents @modelcontextprotocol/client@2.0.0

パッケージマネージャーのピア依存関係の指示に従ってください。正確な MCP バージョンは、それらをサポートする Agents リリースと合わせて更新します。

一時的なレガシーレーンが必要か判断する

サーバーがいま SDK v1 をインポートしている、という理由だけで v1 を残さないでください。エンドポイントが次のセッション依存機能のいずれかに依存する場合を除き、SDK v2 ファクトリへ直接移します。

  • プロトコルセッション、または渡された WorkerTransport
  • トランスポートストレージ、またはイベント再生
  • スタンドアロンの GET ストリーム
  • プッシュされる elicitation、sampling、roots リクエスト
  • HTTP DELETE によるセッション削除

エンドポイントがこれらの機能を使う場合は、先にステートレスルートをデプロイします。セッション依存を置き換えるあいだだけ SDK v1 ルートを残します。リクエストの振り分けは isLegacyRequest() で行います。手順は ステートレスレーンとレガシーレーンを並行運用する を参照してください。

McpAgent を使わない SDK v1 エンドポイントでは、その一時的なレガシー分岐にだけ createLegacyMcpHandler を使います。クライアントが移行し、既存セッションが排出されたら削除します。

ステートレスサーバーを SDK v2 へ移す

ステートレスの createMcpHandler はファクトリを受け取ります。ファクトリは @modelcontextprotocol/serverMcpServer または Server を返します。

  1. サーバー登録の変更は、上流の TypeScript SDK v2 移行ガイド に従います。

  2. サーバーを @modelcontextprotocol/server からインポートします。

  3. サーバーの構築と登録をファクトリへ移します。

  4. Worker オブジェクトのデフォルトエクスポートは残します。その fetch() 内でファクトリを createMcpHandler に渡し、返ってきた呼び出し可能オブジェクトをこれまでどおり呼び出します。より低レベルのリクエスト統合にだけ、ハンドラーの fetch(request, options?) メソッドを使います。

  5. Agents ハンドラーが返す呼び出し可能オブジェクトをデフォルトエクスポートしないでください。Wrangler は関数のデフォルトエクスポートを WorkerEntrypoint クラスとして解釈します。

  6. SDK v1 のトランスポートとセッションオプションを削除します。

  7. ステートレスクライアントとレガシークライアントの両方でエンドポイントをテストします。

src/index.jsjs
import { McpServer } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
import { z } from "zod";

function createServer() {
	const server = new McpServer({
		name: "example-server",
		version: "1.0.0",
	});

	server.registerTool(
		"hello",
		{
			description: "Return a greeting",
			inputSchema: { name: z.string().optional() },
		},
		async ({ name }) => ({
			content: [{ type: "text", text: `Hello, ${name ?? "World"}!` }],
		}),
	);

	return server;
}

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer)(request, env, ctx);
	},
};
src/index.tsts
import { McpServer } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
import { z } from "zod";

function createServer() {
	const server = new McpServer({
		name: "example-server",
		version: "1.0.0",
	});

	server.registerTool(
		"hello",
		{
			description: "Return a greeting",
			inputSchema: { name: z.string().optional() },
		},
		async ({ name }) => ({
			content: [{ type: "text", text: `Hello, ${name ?? "World"}!` }],
		}),
	);

	return server;
}

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer)(request, env, ctx);
	},
} satisfies ExportedHandler;

Worker のエントリポイントはオブジェクトのままです。変わるのはハンドラーの呼び出しだけです。

// SDK v1: pass a fresh constructed server.
return createMcpHandler(createServer())(request, env, ctx);

// SDK v2: pass the factory itself.
return createMcpHandler(createServer)(request, env, ctx);

これを export default createMcpHandler(createServer) に簡略化しないでください。Agents ハンドラーは、別のハンドラー内で合成するために呼び出し可能ですが、Wrangler は関数のデフォルトエクスポートをすべて WorkerEntrypoint クラスとして扱います。

SDK v2 ハンドラーは、MCP リクエストごとにサーバーを 1 つ作成します。並行する Worker リクエストが、接続済みのサーバーインスタンスを共有することはありません。

ステートレスサーバーのハンドラーオプション

Agents ラッパーは routecorsOptionsallowedHostnamesallowedOriginHostnamesauthContext を追加します。対応する SDK v2 オプションは、上流ハンドラーへそのまま渡します。

よく使うオプションは次のとおりです。

オプション 動作
route 正確なリクエストパスを設定します。デフォルトは /mcp です。
legacy デフォルトではレガシー互換を使います。ステートレス専用エンドポイントでは "reject" にします。
responseMode 自動、JSON、SSE のいずれかの応答処理を選びます。
allowedHostnames Host ヘッダーを特定のホスト名に制限します。
allowedOriginHostnames ブラウザーの Origin を制限します。信頼できるミドルウェアが検証する場合は "*" も受け付けます。
corsOptions CORS 応答ヘッダーを制御します。削除するには false を設定します。
onerror 応答を変えずにハンドラーエラーを報告します。
maxSubscriptionskeepAliveMs subscriptions/listen の配信を設定します。

ステートレスハンドラーは、次の SDK v1 オプションを拒否します。

  • transport
  • storage
  • sessionIdGenerator
  • onsessioninitializedonsessionclosed
  • enableJsonResponse
  • eventStore
  • allowedHostsallowedOrigins
  • enableDnsRebindingProtection
  • retryInterval

enableJsonResponse: true の代わりに responseMode: "json" を使います。JSON モードは、最終結果より前に出た通知を落とします。

Workers 上の Origin 検証

Workers ラッパーは、存在する Origin をすべて検証します。不正、不透明、非 HTTP の Origin は 403 で拒否します。

デフォルトの許可リストには、localhost 系の Origin と、エンドポイントの workers.dev ホスト名が含まれます。具体的な corsOptions.origin も、そのホスト名を自動で追加します。ハンドラーは localhost と workers.dev エンドポイントに、対応する Host チェックを適用します。

ワイルドカード CORS のカスタムドメインでは、Host と Origin の制限を両方とも明示します。

export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer, {
			allowedHostnames: ["mcp.example.com"],
			allowedOriginHostnames: ["app.example.com"],
			corsOptions: { origin: "https://app.example.com" },
		})(request, env, ctx);
	},
};
export default {
	fetch(request, env, ctx) {
		return createMcpHandler(createServer, {
			allowedHostnames: ["mcp.example.com"],
			allowedOriginHostnames: ["app.example.com"],
			corsOptions: { origin: "https://app.example.com" },
		})(request, env, ctx);
	},
} satisfies ExportedHandler;

allowedOriginHostnames: "*" は、信頼できるミドルウェアがハンドラー呼び出し前に Origin を検証する場合にだけ設定します。この値はハンドラーの Origin チェックをオフにします。MCP HTTP サーバーは、ブラウザーの Origin を検証する必要があります。

CORS ヘッダーはリクエストを認証しません。エンドポイントは OAuth または別の認証層で保護します。

ハンドラーは request.url から信頼できる Host 許可リストを推論しません。デプロイが任意の Host 値を受け付ける場合は、ハンドラーを呼ぶ前に検証します。Cloudflare Workers 外のローカルサーバーでは、上流 SDK の Host と Origin 検証の案内に従ってください。

レガシークライアントとの互換性を理解する

デフォルトの legacy: "stateless" 設定は、通常のレガシーツール、リソース、プロンプトに対応します。このレーンは SDK v2 のウェブ標準トランスポートを使います。WorkerTransport はインポートせず、完全なセッション依存トランスポートでもありません。

フォールバックには次の制限があります。

  • 各 POST は新しいサーバーとトランスポートを受け取ります。
  • HTTP GET と DELETE は 405 を返します。
  • MCP セッション ID もプロトコルセッション状態も永続化しません。
  • プッシュされる sampling、elicitation、roots リクエストは即座に失敗します。
  • スタンドアロンストリーム、イベント再生、セッション削除は使えません。
  • 公開済みの実験的タスクは、このフォールバックではサポートされません。

これらの機能を移行しているあいだは、影響を受けるレガシークライアントを、一時的な createLegacyMcpHandler または McpAgent レーンへ振り分けます。

McpAgent サーバーを移行する

移行中、McpAgent は SDK v1 サーバーのままです。レガシールート内のサーバーインポートを @modelcontextprotocol/server に変えないでください。

レガシーのステートフル機能なしで直接移行する

サーバーが MCP セッション状態、RPC、プッシュされるサーバーからクライアントへのリクエスト、スタンドアロンストリーム、イベント再生に依存しない場合は、ツールを SDK v2 ファクトリへ移し、createMcpHandler で提供します。

ステートフル機能のステートレス相当を計画する

サーバーがレガシーのステートフル機能を使う場合は、ステートレス相当を設計してデプロイするあいだ、既存の McpAgent ルートを残します。

レガシー経路上のステートフル機能 ステートレス経路での設計
MCP セッションをキーにしたアプリケーションデータ Durable Object、D1、KV、R2 など、明示的なアプリケーション境界の裏にデータを置きます。アドレス指定は MCP セッション ID ではなく、認証済みのサーバー発行ハンドルで行います。
複数ステップの対話状態 完全性保護された requestStateinput_required と一緒に返します。認証済みユーザー、元のメソッドとパラメーター、有効期限に結び付けます。
プッシュされる elicitation、sampling、roots inputRequired(...) を返します。クライアントが埋め込みリクエストを満たし、元の操作を再試行します。
スタンドアロンの list-change ストリーム 変更は subscriptions/listen で公開します。ストリームが終わったら、クライアントはサブスクリプションを開き直します。
セッション再生またはトランスポート復旧 各ステートレスリクエストを独立して復旧できるようにします。業務進捗は MCP トランスポートではなく、アプリケーションストレージに永続化します。
Agent から McpAgent への RPC プロトコルセッション依存を、明示的なアプリケーション RPC または HTTP 境界に置き換え、ステートレス MCP ツールは別に公開します。

ステートレス実装ができても、すぐレガシールートを削除しないでください。クライアントが移行し、既存セッションが排出されるまで、両方のレーンを提供します。

ステートレスレーンとレガシーレーンを並行運用する

1 つの URL で、ステートレスリクエストを SDK v2 へ、レガシーリクエストを既存のセッション依存サーバーへ振り分けられます。

import { isLegacyRequest } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";

const stateless = createMcpHandler(createStatelessServer, {
	route: "/mcp",
	legacy: "reject",
});

const legacy = MyMcpAgent.serve("/mcp");

export default {
	async fetch(request, env, ctx) {
		if (await isLegacyRequest(request)) {
			return legacy.fetch(request, env, ctx);
		}
		return stateless(request, env, ctx);
	},
};
import { isLegacyRequest } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";

const stateless = createMcpHandler(createStatelessServer, {
	route: "/mcp",
	legacy: "reject",
});

const legacy = MyMcpAgent.serve("/mcp");

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext) {
		if (await isLegacyRequest(request)) {
			return legacy.fetch(request, env, ctx);
		}
		return stateless(request, env, ctx);
	},
} satisfies ExportedHandler<Env>;

ステートレスハンドラーでは legacy: "reject" を維持します。そうしないと、レガシー互換レーンが先にリクエストを消費し、セッション依存ルートへ届きません。

クライアントを移す前に、両方のルートをデプロイします。レガシーレーンを監視し、既存セッションを排出させます。どのクライアントも依存しなくなってから、レガシールートとそのプロトコル専用 Durable Object バインディングを削除します。Durable Object の移行設定は、別のデプロイ手順として扱います。

MCP クライアントを更新する

Agents は内部で @modelcontextprotocol/client を使うようになりました。既存の addMcpServer 呼び出しは、プロトコル世代を自動で交渉します。

ステートレス経路のサーバーは server/discover を使います。レガシーの Streamable HTTP、SSE、RPC サーバーでは、Agents は initialize にフォールバックします。

クライアント API の変更は次のとおりです。

  • 推奨シグネチャは callTool(params, options) です。
  • callTool(params, resultSchema, options) は残っていますが、非推奨です。
  • MCP クライアントの型は @modelcontextprotocol/client から来ます。
  • 必須のステートレス HTTP ヘッダーは SDK が処理します。
  • リスト変更は、交渉したレーンに応じて、ステートレスサブスクリプションまたはレガシー通知を使います。

ステートレスリクエスト向けに elicitation を設定する

ステートレス経路のツール、プロンプト、リソースは、複数往復リクエスト(MRTR)で input_required を返せます。SDK は設定済みの elicitation ハンドラーを呼び、元の操作を再試行します。元の callToolgetPromptreadResource の Promise は pending のままです。

各再試行に含まれるのは、直前の入力ラウンドへの応答だけです。それより前の応答はすべて含まれません。クライアントは最新の不透明な requestState もエコーします。後続ラウンドが必要とする信頼できる中間値は、完全性保護された requestState に封入します。inputResponses がラウンドをまたいで蓄積することは期待しないでください。

2 ラウンドのツールフローは ステートレス elicitation の例 を参照してください。

export class MyAgent extends Agent {
	onStart() {
		this.mcp.configureElicitationHandlers({
			form: async (request, serverId, signal) => {
				return collectInput(request, serverId, signal);
			},
			url: async (request, serverId, signal) => {
				return openExternalFlow(request, serverId, signal);
			},
		});
	}
}
export class MyAgent extends Agent<Env> {
	onStart() {
		this.mcp.configureElicitationHandlers({
			form: async (request, serverId, signal) => {
				return collectInput(request, serverId, signal);
			},
			url: async (request, serverId, signal) => {
				return openExternalFlow(request, serverId, signal);
			},
		});
	}
}

ハンドラーと進行中の呼び出しはメモリに残ります。ハイバネーション、isolate の再起動、トランスポート喪失、接続の再構築は、稼働中の対話呼び出しを拒否します。接続が復旧してから操作を再試行します。

手動で扱う requestState は、信頼できない入力として扱います。認証済みユーザーと操作に結び付け、完全性を保護し、短い有効期限を設定します。

カスタム OAuth プロバイダーを更新する

カスタムの AgentMcpOAuthProvider は、v2 の OAuthClientProvider 契約を実装する必要があります。

  • OAuth 型は @modelcontextprotocol/client からインポートします。
  • StoredOAuthClientInformationStoredOAuthTokens を保存します。
  • 資格情報上の SDK issuer スタンプを維持します。
  • ブラウザーリダイレクトをまたいで OAuthDiscoveryState を永続化します。
  • invalidateCredentials"discovery" を受け付けます。
  • 認可 issuer が異なる場合は、資格情報を分けて保持します。

SDK v2 はデフォルトで OAuth メタデータの issuer を検証します。メタデータの不一致が既知の、信頼できるレガシーサーバーでは skipIssuerMetadataValidation: true を使えます。これは OAuth の mix-up 保護を弱めるため、一般的なフォールバックにはしないでください。

プロトコルの違いを確認する

MCP のステートレスモデルは、トランスポートとライフサイクルを変えます。

領域 以前の動作 新しい動作
起動 initialize ハンドシェイク ハンドシェイクなし。クライアントにとって server/discover は任意
リクエストメタデータ 接続スコープの交渉 各リクエストにバージョン、クライアント能力、識別メタデータ
セッション 任意の Mcp-Session-Id プロトコルセッションなし
サーバー入力リクエスト サーバーが JSON-RPC リクエストを送る サーバーは input_required を返す。クライアントが元の操作を再試行する
変更通知 スタンドアロン GET ストリームと list-change 通知 SSE 応答付きの subscriptions/listen POST
ストリーム復旧 Last-Event-ID で設定済みストリームを再開できる Listen ストリームは失敗後に開き直す。Last-Event-ID 再生はない

カスタムトランスポート、プロキシ、ゲートウェイは、ドラフトのリクエストヘッダーを保持する必要があります。

  • MCP-Protocol-Version
  • Mcp-Method
  • ツール、プロンプト、リソース操作向けの Mcp-Name
  • 宣言された Mcp-Param-* ツールヘッダー

Agents が使う正確な SDK バージョンは、MCP 2026-07-28 改訂を実装します。clientInfo を任意にし、サーバー識別を結果の _meta に置きます。高レベルの SDK API を使い、各プロトコル改訂をサポートする Agents リリースと合わせて MCP パッケージを更新します。生のステートレス結果には resultType が必要です。

ドラフトは Roots、Sampling、Logging、旧 HTTP+SSE トランスポート、Dynamic Client Registration を非推奨にします。非推奨期間中、レガシー互換のため型は残ります。公開済みの実験的タスクメソッドは io.modelcontextprotocol/tasks 拡張になります。Agents SDK v0.20.0 はこの拡張を追加しません。

統合の互換性

次の統合は、このリリースでも SDK v1 サーバー出力を維持します。

  • 現行 Code Mode の codeMcpServeropenApiMcpServer ヘルパー
  • サーバー側の withX402 ヘルパー
  • SDK v1 の McpServer をインポートする既存の OpenAI Apps 例

これらの統合が SDK v2 サーバーを出すまでは、出力を一時的な createLegacyMcpHandler ルートの後ろに隔離します。Code Mode の MCP コネクターと withX402Client は、どちらのクライアント世代も受け付けます。

ロールアウトを計画する

  1. 各エンドポイントを、セッション依存の有無で分類します。

  2. Agents リリースが要求する MCP SDK バージョンを固定します。

  3. ステートレスにできるサーバー定義を、それぞれ SDK v2 ファクトリへ移します。

  4. 一時的なレガシーレーンがまだ必要なエンドポイントの横に、ステートレスハンドラーを追加します。

  5. ステートレスとレガシーのリクエストを isLegacyRequest() で振り分けます。

  6. ステートレスクライアントとレガシークライアントを、それぞれ独立してテストします。

  7. 必須 HTTP ヘッダーを、すべてのプロキシとゲートウェイ経由でテストします。

  8. クリーンなログイン後と、Durable Object ハイバネーション後の OAuth をテストします。

  9. キャンセル、複数の入力ラウンド、トランスポート喪失をテストします。

  10. 有効な Origin を確認し、無効な Origin は 403 で拒否します。

  11. 既存セッションが排出されたあとでのみ、レガシールートを削除します。

v0.20.0 より前の Agents リリースが保存した HTTP セッション ID には、交渉済みプロトコルバージョンが含まれません。アップグレード後のクライアントは、それらの ID を捨て、危険な再開リクエストを送らずに再接続します。古いリモートセッションに紐づく進行中の作業は再開しません。

API の詳細は createMcpHandlerMcpClient を参照してください。

役に立ちましたか?