Skip to main content
Glama
weijianzhg

mCP 2.0

by weijianzhg

无状态 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/listprompts/listresources/listresources/templates/listresources/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 scenario

MCP 包被固定为 2.0.0,包括此处使用的 inputRequiredacceptedContent、缓存和请求范围进度 API。

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    A 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
  • F
    license
    Not graded
    quality
    C
    maintenance
    Demonstrates MCP 2026-07-28 behavior for long-running tool calls, task lifecycle (get, update, cancel), and elicitation clarification during async tasks.
  • A
    license
    Not graded
    quality
    C
    maintenance
    Educational MCP server demonstrating the 2026-07-28 stateless protocol with raw Starlette, no SDK, featuring tools, request state handles, MRTR elicitation, and subscriptions.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Demo 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.
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/weijianzhg/mcp-2-0'

If you have feedback or need assistance with the MCP directory API, please join our Discord server