Skip to main content
Glama
weijianzhg

mCP 2.0

by weijianzhg

Zustandsloses MCP, zustandsbehaftete Anwendung

Ein minimales JavaScript-Projekt, das die wichtigsten Anfrage-/Antwortmuster von MCP 2026-07-28 mit dem MCP TypeScript SDK v2 demonstriert.

Die Demo umfasst:

  • Zustandslose HTTP-Anfragen ohne Mcp-Session-Id.

  • Von der Anwendung verwalteter Zustand, der über explizite Handles adressiert wird.

  • Anfragen mit mehreren Round-Trips zur Benutzerbestätigung.

  • Fortschrittsaktualisierungen, die auf eine einzelne Operation begrenzt sind.

  • Cache-fähige Tool-Erkennung mit TTL und Freigabebereich.

Ausführen

Erfordert Node.js 20 oder neuer.

npm install
npm run demo

Der Befehl startet einen Server auf einem verfügbaren lokalen Port, führt jedes Szenario aus, gibt die Ergebnisse aus und fährt den Server herunter. Das Löschbeispiel verwendet einen virtuellen Dateisatz im Speicher und berührt niemals Dateien auf der Festplatte.

Führen Sie die Integrationstests wie folgt aus:

npm test

Um den Server für einen anderen MCP-Client weiterlaufen zu lassen:

npm run server

Der Endpunkt ist http://127.0.0.1:3000/mcp. Setzen Sie PORT, um den Port zu überschreiben.

Related MCP server: MCP RC Long-Running Task Prototype

1. Zustandslose Anfragen

MCP 2026-07-28 entfernt HTTP-Sitzungen auf Protokollebene. Dieses Projekt erstellt für jede Anfrage einen neuen McpServer, sodass jede Anfrage jede Serverinstanz erreichen kann:

const mcpHandler = createMcpHandler(
  () => createStateServer(demoFiles),
  {
    legacy: "reject",
    onerror: (error) => console.error("MCP error:", error),
  },
);

Der Client wählt explizit die moderne Protokollrevision aus:

const client = new Client(
  { name: "state-demo-client", version: "0.1.0" },
  {
    capabilities: { elicitation: { form: {} } },
    versionNegotiation: {
      mode: { pin: "2026-07-28" },
    },
  },
);

Der in einer McpServer-Instanz gespeicherte Zustand verschwindet daher nach dieser Anfrage. Wenn Sie den ephemeren Zähler der Demo zweimal aufrufen, erhalten Sie von zwei verschiedenen Serverinstanzen jeweils 1.

2. Zustandsbehaftete Anwendungsdaten

Zustandsloses MCP erfordert keine zustandslose Anwendung. Dauerhafter Zustand befindet sich außerhalb des pro Anfrage erstellten MCP-Servers und wird über ein explizites Handle ausgewählt:

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,
    };
  },
);

Der Client führt das Handle zwischen ansonsten unabhängigen Aufrufen mit:

const created = await client.callTool({ name: "create-counter" });
const { counterId } = created.structuredContent;

await client.callTool({
  name: "increment-counter",
  arguments: { counterId },
});

Die In-Memory-Map ist nur ein Platzhalter. Ein Produktionsserver sollte eine Datenbank oder einen gemeinsamen Speicher verwenden, Handles an den authentifizierten Principal binden und bei jedem Abruf Autorisierung und Ablauf erzwingen.

3. Bestätigung über mehrere Round-Trips

Ein Tool, das weitere Eingaben benötigt, gibt input_required zurück. Der Client erhält die Antwort und wiederholt die ursprüngliche Anfrage mit inputResponses, sodass der Server keine zweite JSON-RPC-Anfrage auf dem Operations-Stream initiiert.

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 },
    };
  },
);

Das SDK kann die Anfrage über den normalen Elicitation-Handler erfüllen und automatisch wiederholen:

client.setRequestHandler("elicitation/create", async (request) => {
  const confirm = await askUser(request.params.message);
  return {
    action: "accept",
    content: { confirm },
  };
});

Die Bestätigung ist eine Sache der Benutzererfahrung, nicht der Autorisierung. Der Server muss den Aufrufer weiterhin authentifizieren und die Berechtigung zum Löschen von Dateien unabhängig durchsetzen.

4. Anfragespezifischer Fortschritt

Der Fortschritt bleibt an die Anfrage gebunden, die die Arbeit gestartet hat. Parallele Operationen erhalten nur ihre eigenen Aktualisierungen, anstatt sich einen globalen Ereignisstream zu teilen.

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" },
    };
  },
);

Jeder Client-Aufruf liefert seinen eigenen Fortschritts-Callback:

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. Cache-Fähigkeit

Cache-fähige Antworten enthalten eine Frische-Lebensdauer und eine Freigaberichtlinie. Dies reduziert den wiederholten Discovery-Verkehr, wenn sich ein Agent mit vielen MCP-Servern verbindet.

Dieser Server kennzeichnet seinen Tool-Katalog als fünf Minuten lang wiederverwendbar:

const server = new McpServer(
  { name: "mcp-state-demo", version: "0.1.0" },
  {
    cacheHints: {
      "tools/list": {
        ttlMs: 300_000,
        cacheScope: "public",
      },
    },
  },
);

Der Client verwendet frische Einträge automatisch:

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

Die Felder gelten für tools/list-, prompts/list-, resources/list-, resources/templates/list- und resources/read-Ergebnisse.

  • public erlaubt Clients und gemeinsamen Zwischeninstanzen, das Ergebnis über Benutzer hinweg wiederzuverwenden.

  • private beschränkt die Wiederverwendung auf den anfragenden Autorisierungskontext. Wenn ein Cache-Speicher gemeinsam genutzt wird, setzen Sie die cachePartition des Clients auf eine stabile Principal-Kennung.

Eine TTL ist eine Frische-Schätzung. Listenänderungs-Benachrichtigungen können zwischengespeicherte Kataloge ungültig machen, bevor ihre TTL abläuft.

Projektstruktur

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

Die MCP-Pakete sind auf 2.0.0 festgelegt, einschließlich der hier verwendeten inputRequired-, acceptedContent-, Cache- und anfragespezifischen Fortschritts-APIs.

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