Skip to content

Serve MCP 2026-07-28 statelessly from mcp-server-kit - #15

Merged
clbotdev merged 1 commit into
mainfrom
feat/mcp-2026-07-28
Oct 30, 2026
Merged

clbotdev merged 1 commit into
mainfrom
feat/mcp-2026-07-28

Conversation

@clbotdev

Copy link
Copy Markdown
Contributor

@codelitdev/mcp-server-kit: MCP 2026-07-28, stateless, on SDK 2

The kit was on MCP SDK 1, which tops out at protocol 2025-11-25, and it reshaped SDK results in two non-compliant ways:

  • It prepended { type: "json", json: … } to every tool result's content. MCP has no json content type. ChatGPT rejected every CourseLit tool result because of it and retried the call, so each "failed" products.create created another product (three courses for one request).
  • It turned tool failures into JSON-RPC errors with string codes ("forbidden"), which JSON-RPC does not allow.

SendLit already serves 2026-07-28 on SDK 2, and SendLit, FrontLit and MediaLit are to move onto this kit, so the kit now follows the same design (ADR 0016):

  • SDK 2, stateless. Every request goes through createMcpHandler with a fresh request-local server. Modern clients get 2026-07-28: server/discover, per-request envelopes, Mcp-Method/Mcp-Name validation, resultType, cache hints on tools/list and server/discover (five minutes, private; catalogCacheHint overrides).
  • 2025 clients keep working through the SDK's legacy: "stateless" mode. initialize no longer mints Mcp-Session-Id; GET/DELETE answer 405.
  • Same product API. createMcpServerKit, handle(), listTools() and shutdown() keep their shape. A legacy reply (a one-response SSE stream) is returned as application/json, which Streamable HTTP allows.
  • Spec-shaped results. A tool result is structuredContent plus one text copy. A tool failure, including a destructive call without confirm: true, is an isError result with structuredContent: { code, message, details } and HTTP 200. Authentication and context failures before dispatch return the platform error body with its HTTP status, as before.
  • Schemas and annotations. Zod 3 tool schemas are converted to JSON Schema once per process and advertised without $schema. Annotations are spec-only (readOnlyHint, destructiveHint); the risk moves to _meta.risk.
  • Removed exports: the session store and SDK 1 transport re-exports, and the kit's sessions property. Hence a minor bump (0.5.0 for the fixed group).

platform-conformance reads MCP tool failures from isError results. The reference product and template tests follow the new shapes.

Verification

  • bun run verify passes (lint, preset sync, typecheck, drift, tests, build, packed tests).
  • New kit tests drive the kit with the official SDK 2 client pinned to 2026-07-28 and in legacy mode, and cover header/envelope mismatch, tool failure codes, auth, CORS, 405s, abort and shutdown. The packed-consumer check now makes a real tool call.
  • Packed tarballs installed into CourseLit: all 210 API tests pass, and a live SDK 2 client over HTTP creates products in both eras with text-only content, a repeat create returning a readable conflict tool result.

Move the kit to the official MCP TypeScript SDK 2 and serve every request
through createMcpHandler with a fresh request-local server. Modern clients
get protocol 2026-07-28; 2025-era Streamable HTTP clients are served by the
SDK's stateless legacy mode, so there are no sessions, no Mcp-Session-Id,
and GET/DELETE answer 405.

Tool results are no longer reshaped: the invalid { type: "json" } content
item is gone (ChatGPT rejected results carrying it and retried, duplicating
writes), and tool failures are isError tool results with a platform code
instead of JSON-RPC errors with string codes. platform-conformance reads
failures from those results. Recorded as ADR 0016.
@clbotdev
clbotdev merged commit abfed2e into main Oct 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants