mCP 2.0
ステートレス MCP、ステートフルなアプリケーション
MCP TypeScript SDK v2 を使用して、MCP 2026-07-28 の主要なリクエスト/レスポンスパターンを示す最小限の JavaScript プロジェクトです。
このデモでは以下を扱います:
Mcp-Session-Idのないステートレスな HTTP リクエスト。明示的なハンドルで参照されるアプリケーション管理の状態。
ユーザー確認のための複数回のラウンドトリップリクエスト。
個々の操作にスコープされた進捗更新。
TTL と共有スコープを備えたキャッシュ可能なツールディスカバリ。
実行方法
Node.js 20 以降が必要です。
npm install
npm run demoこのコマンドは、利用可能なローカルポートでサーバーを起動し、すべてのシナリオを実行して結果を出力し、サーバーをシャットダウンします。削除の例では、インメモリの仮想ファイルセットを使用し、ディスク上のファイルには一切触れません。
統合テストを実行するには:
npm test別の MCP クライアントのためにサーバーを起動したままにするには:
npm run serverエンドポイントは http://127.0.0.1:3000/mcp です。PORT を設定するとポートを上書きできます。
Related MCP server: MCP RC Long-Running Task Prototype
1. ステートレスリクエスト
MCP 2026-07-28 はプロトコルレベルの HTTP セッションを廃止します。このプロジェクトでは、リクエストごとに新しい McpServer を作成するため、どのリクエストも任意のサーバーインスタンスに到達できます:
const mcpHandler = createMcpHandler(
() => createStateServer(demoFiles),
{
legacy: "reject",
onerror: (error) => console.error("MCP error:", error),
},
);クライアントは明示的に最新のプロトコルリビジョンを選択します:
const client = new Client(
{ name: "state-demo-client", version: "0.1.0" },
{
capabilities: { elicitation: { form: {} } },
versionNegotiation: {
mode: { pin: "2026-07-28" },
},
},
);したがって、1 つの McpServer インスタンス内に保存された状態は、そのリクエストの後に消えます。デモの一時的なカウンターを 2 回呼び出すと、2 つの異なるサーバーインスタンスから 1 が生成されます。
2. ステートフルなアプリケーションデータ
ステートレスな MCP は、ステートレスなアプリケーションを要求しません。永続的な状態は、リクエストごとの MCP サーバーの外部にあり、明示的なハンドルを使用して選択されます:
const countersById = new Map();
server.registerTool(
"create-counter",
{
outputSchema: z.object({ counterId: z.uuid(), value: z.number() }),
},
async () => {
const counterId = randomUUID();
countersById.set(counterId, 0);
const structuredContent = { counterId, value: 0 };
return {
content: [{ type: "text", text: JSON.stringify(structuredContent) }],
structuredContent,
};
},
);クライアントは、独立した呼び出しの間でハンドルを保持します:
const created = await client.callTool({ name: "create-counter" });
const { counterId } = created.structuredContent;
await client.callTool({
name: "increment-counter",
arguments: { counterId },
});インメモリの Map は単なる代用です。本番サーバーは、データベースまたは共有ストアを使用し、ハンドルを認証済みプリンシパルにバインドし、すべてのルックアップで認可と有効期限を強制する必要があります。
3. 複数回のラウンドトリップ確認
追加の入力が必要なツールは input_required を返します。クライアントは回答を取得し、inputResponses を使用して元のリクエストを再試行するため、サーバーは操作ストリーム上で 2 番目の JSON-RPC リクエストを開始しません。
server.registerTool(
"delete-files",
{
inputSchema: z.object({ files: z.array(z.string()).min(1) }),
annotations: { destructiveHint: true },
},
async ({ files }, ctx) => {
const confirmation = acceptedContent(
ctx.mcpReq.inputResponses,
"confirm",
confirmationSchema,
);
if (confirmation === undefined) {
return inputRequired({
inputRequests: {
confirm: inputRequired.elicit({
message: `Delete ${files.length} virtual files?`,
requestedSchema: confirmationSchema,
}),
},
});
}
if (!confirmation.confirm) {
return {
content: [{ type: "text", text: "Cancelled" }],
structuredContent: { status: "cancelled", deleted: [] },
};
}
const deleted = files.filter((file) => demoFiles.delete(file));
return {
content: [{ type: "text", text: `Deleted: ${deleted.join(", ")}` }],
structuredContent: { status: "deleted", deleted },
};
},
);SDK は、通常の引き出しハンドラーを使用してリクエストを満たし、自動的に再試行できます:
client.setRequestHandler("elicitation/create", async (request) => {
const confirm = await askUser(request.params.message);
return {
action: "accept",
content: { confirm },
};
});確認はユーザーエクスペリエンスであり、認可ではありません。サーバーは依然として呼び出し元を認証し、ファイルを削除する権限を独立して強制する必要があります。
4. リクエストスコープの進捗
進捗は、作業を開始したリクエストに付随したままです。並行操作は、1 つのグローバルイベントストリームを共有するのではなく、自分自身の更新のみを受け取ります。
server.registerTool(
"run-work",
{ inputSchema: z.object({ job: z.string() }) },
async ({ job }, ctx) => {
const progressToken = ctx.mcpReq._meta?.progressToken;
for (const progress of [10, 30, 70]) {
if (progressToken !== undefined) {
await ctx.mcpReq.notify({
method: "notifications/progress",
params: {
progressToken,
progress,
total: 100,
message: `${job}: ${progress}%`,
},
});
}
}
return {
content: [{ type: "text", text: `${job}: complete` }],
structuredContent: { job, status: "complete" },
};
},
);各クライアント呼び出しは、独自の進捗コールバックを提供します:
await Promise.all([
client.callTool(
{ name: "run-work", arguments: { job: "alpha" } },
{ onprogress: (update) => alphaProgress.push(update) },
),
client.callTool(
{ name: "run-work", arguments: { job: "beta" } },
{ onprogress: (update) => betaProgress.push(update) },
),
]);5. キャッシュ可能性
キャッシュ可能なレスポンスには、鮮度の有効期間と共有ポリシーが含まれます。これにより、1 つのエージェントが多くの MCP サーバーに接続する際の繰り返しのディスカバリトラフィックが削減されます。
このサーバーは、ツールカタログを 5 分間再利用可能としてマークします:
const server = new McpServer(
{ name: "mcp-state-demo", version: "0.1.0" },
{
cacheHints: {
"tools/list": {
ttlMs: 300_000,
cacheScope: "public",
},
},
},
);クライアントは新しいエントリを自動的に使用します:
await client.listTools(); // network request; stores the result
await client.listTools(); // cache hit; no network request
await client.listTools(undefined, {
cacheMode: "refresh",
}); // forces a network request and updates the cacheこれらのフィールドは、tools/list、prompts/list、resources/list、resources/templates/list、および resources/read の結果に適用されます。
publicは、クライアントと共有仲介者が結果をユーザー間で再利用することを許可します。privateは、再利用をリクエスト元の認可コンテキストに制限します。キャッシュストアが共有されている場合、クライアントのcachePartitionを安定したプリンシパル識別子に設定します。
TTL は鮮度の見積もりです。リスト変更通知は、TTL が期限切れになる前にキャッシュされたカタログを無効化できます。
プロジェクト構造
src/server.js MCP server and tool implementations
src/demo.js Client exercising all five scenarios
test/state.test.js Integration tests for every scenarioMCP パッケージは 2.0.0 に固定されており、ここで使用される inputRequired、acceptedContent、キャッシュ、およびリクエストスコープの進捗 API が含まれています。
This server cannot be deployed
Related MCP Connectors
Cross-tool persistent memory and context for AI assistants over MCP.
AI Reasoning Cache & Consensus Layer with 11 MCP tools via Streamable HTTP.
Agentic rails for complex workflows with receipts, fees, and MCP tool access.
- WauldoOAuthcom.wauldo
Stateless agentic tools over MCP: concept extraction, long-context, knowledge graph, planning.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA reference implementation demonstrating proper MCP server patterns with HTTP transport, featuring session management, progress notifications, and example tools for testing server functionality. Serves as a clean template for building MCP servers with streamable responses and comprehensive error handling.7-
- FlicenseNot gradedqualityDmaintenanceDemonstrates MCP 2026-07-28 behavior for long-running tool calls, task lifecycle (get, update, cancel), and elicitation clarification during async tasks.-
- AlicenseNot gradedqualityCmaintenanceEducational MCP server demonstrating the 2026-07-28 stateless protocol with raw Starlette, no SDK, featuring tools, request state handles, MRTR elicitation, and subscriptions.MIT
- AlicenseNot gradedqualityBmaintenanceDemo MCP server for ACEL, a runtime verification middleware that blocks a rule-violating tool call before it executes. 5 tools (authenticate, read/validate/delete records, send payment) showing ACEL enforcing call ordering and state preconditions live via the official MCP SDK's middleware hook.1MIT