Skip to content

feat(mcp): cut over to the SDK envelope and declare the two missing conformance fields - #9681

Merged
JSONbored merged 1 commit into
mainfrom
feat/mcp-cutover-and-conformance
Aug 6, 2026
Merged

feat(mcp): cut over to the SDK envelope and declare the two missing conformance fields#9681
JSONbored merged 1 commit into
mainfrom
feat/mcp-cutover-and-conformance

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Three things on the MCP surface, all found while wiring #9677.

1. Cut over to the SDK envelope — no flag, no second path

The SDK envelope has served all production traffic since 2026-08-06 12:15 UTC, with parity verified live across 12 protocol cases and 95/98 zero-argument tools. The flag has done its job; keeping it means keeping a second envelope nobody exercises, which is how the untested path becomes the one that breaks.

mcpSdkEnvelopeEnabled and its branch are gone. Every well-formed request is served by the SDK.

The malformed-input branch stays, permanently — it is not a rollback. The SDK's transport answers 400 -32700 Parse error for the whole request, which is wrong three ways:

ours SDK
code -32600 Invalid Request -32700 Parse error — reserved for JSON that didn't parse
status 200 — a JSON-RPC error is a successful HTTP exchange 400, which also starts firing the #9639 refusal event
mixed batch valid members still execute whole batch rejected

The third is data loss, so malformed input is answered by dispatchMessage directly and always will be.

2. Tool.execution.taskSupport was never emitted

MCP 2025-11-25 defines it as optional/required/forbidden. This server registers no task store, so a task-augmented call cannot be honoured — and absent the declaration a client discovers that by attempting one and failing. Declared "forbidden" on every tool, emitted once at listToolDefinitions so the next tool added carries it without anyone remembering.

3. Implementation.websiteUrl and Implementation.icons were never emitted

Both are serverInfo fields a client renders in a server picker. Now declared, pointing at real published assets (favicon.svg, android-chrome-192x192.png, android-chrome-512x512.png) on our own origin — verified reachable, and asserted same-origin so a third-party host can't creep in.

Server-level only, deliberately. The spec allows icons per tool, but all 224 would carry the identical set — roughly 45 KB added to a tools/list every client holds in context, for nothing the handshake didn't already say. Same reasoning that refused tools/list pagination in #9648. Asserted, so it reads as a decision rather than an omission someone later "fixes".

Adding websiteUrl turned up three copies of the site origin (src/feeds.ts, src/subnet-news.ts, and the icons wanted a fourth). Collapsed into SITE_ORIGIN in src/contracts.ts beside PRIMARY_DOMAIN.

The parity harness becomes a contract test

With one implementation left there is nothing to diff against, so tests/mcp-sdk-parity.test.tstests/mcp-envelope-contract.test.ts, and every expectation is now the literal response a caller receives. That's the stronger form regardless: a comparison passes happily when both sides regress together — which initialize was caught doing during the migration.

The malformed-input cases still exercise the non-SDK branch. That's not legacy coverage; the branch is permanent.

Verification

npx tsc --noEmit                 clean
npm run lint / format:check      clean
npm run validate                 129 subnets, 3442 surfaces, 136 providers
npx vitest run tests/            704 files, 17043 tests, all passing
patch coverage (diff ∩ v8)       100% statements, 100% branches

A pre-existing allowlist in tests/mcp-server.test.ts caught execution as an unpublished key on first run — working exactly as designed; added there with the reason.

Closes #9680
Closes #9647

…onformance fields

The SDK envelope has served all production traffic since 2026-08-06 12:15 UTC,
with parity verified live across 12 protocol cases and 95/98 zero-argument
tools. The flag has done its job. Keeping it means keeping a second envelope
nobody exercises, which is how the untested path becomes the one that breaks,
so mcpSdkEnvelopeEnabled and its branch are removed.

The malformed-input branch stays, and is not a rollback. The SDK's transport
answers 400 -32700 Parse error for the whole request, which is wrong three
ways: -32700 is reserved for JSON that did not parse (-32600 is Invalid
Request), a JSON-RPC error belongs inside a 200, and for a mixed batch the SDK
drops the valid members outright. That last one is data loss, so malformed
input is answered by dispatchMessage directly and always will be.

Two MCP 2025-11-25 fields were never emitted:

  Tool.execution.taskSupport  declared "forbidden" on every tool -- this
                              server registers no task store, and absent the
                              declaration a client learns that by attempting a
                              task-augmented call and failing.
  Implementation.websiteUrl   both on serverInfo, both what a client renders in
  Implementation.icons        a server picker. Icons point at real published
                              assets on our own origin.

Icons are server-level only. The spec allows them per tool, but all 224 would
carry the identical set -- roughly 45 KB added to a tools/list every client
holds in context, for nothing the handshake did not already say. Same
reasoning that refused tools/list pagination in #9648, and asserted so the
decision is explicit rather than an omission someone later "fixes".

Adding websiteUrl turned up three copies of the site origin (src/feeds.ts,
src/subnet-news.ts, and the icons wanted a fourth); collapsed into SITE_ORIGIN
in src/contracts.ts beside PRIMARY_DOMAIN.

The parity harness becomes a contract test: with one implementation left there
is nothing to diff against, so every expectation is now the literal response a
caller receives. That is the stronger form regardless -- a comparison passes
happily when both sides regress together, which initialize was caught doing
during the migration.

Closes #9680
Closes #9647
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 6, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
metagraphed-registry-sync-api 27b79d5 Aug 06 2026, 12:39 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 6, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
metagraphed-data-api 27b79d5 Aug 06 2026, 12:38 PM

@superagent-security

Copy link
Copy Markdown

Superagent didn't find any vulnerabilities or security issues in this PR.

@JSONbored
JSONbored merged commit 67fa1c2 into main Aug 6, 2026
7 checks passed
@JSONbored
JSONbored deleted the feat/mcp-cutover-and-conformance branch August 6, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant