mCP 2.0
无状态 MCP,有状态应用
一个最小的 JavaScript 项目,演示 MCP 2026-07-28 的主要请求/响应模式,使用 MCP TypeScript SDK v2。
该演示涵盖:
无
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" },
},
},
);因此,存储在单个 McpServer 实例内的状态会在该请求结束后消失。调用演示的临时计数器两次,会从两个不同的服务器实例各产生 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 重试原始请求,因此服务器不会在操作流上发起第二个 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. 请求范围的进度
进度仍然附着在发起工作的请求上。并发操作只接收各自的更新,而不是共享一个全局事件流。
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. 可缓存性
可缓存的响应包含新鲜度生命周期和共享策略。当一个代理连接到许多 MCP 服务器时,这减少了重复的发现流量。
此服务器将其工具目录标记为可复用五分钟:
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