Skip to content

Comments

refactor(mcp): use SDK transport directly, drop custom transport layer#936

Draft
mattzcarey wants to merge 7 commits intomainfrom
feat/mcp-server-refactor
Draft

refactor(mcp): use SDK transport directly, drop custom transport layer#936
mattzcarey wants to merge 7 commits intomainfrom
feat/mcp-server-refactor

Conversation

@mattzcarey
Copy link
Contributor

@mattzcarey mattzcarey commented Feb 18, 2026

Summary

Rewrites the MCP server implementation to use the SDK's WebStandardStreamableHTTPServerTransport directly, removing the custom WorkerTransport and WebSocket bridging layer.

  • Kill custom transport layer — deletes WorkerTransport, McpSSETransport, StreamableHTTPServerTransport, and all WS bridging code
  • McpAgent handles HTTP directly in DO — each DO creates a WebStandardStreamableHTTPServerTransport, handles HTTP via onRequest() using createMcpHandler with the transport option
  • createMcpHandler requires a factory function — prevents CVE-2026-25536 (cross-client data leak from reusing server/transport instances)
  • createMcpHandler accepts a transport option — for stateful mode (McpAgent), pass a pre-connected transport; for stateless mode, fresh server+transport per request
  • CfWorkerJsonSchemaValidator injected automatically — both McpAgent and createMcpHandler inject the Workers-compatible JSON schema validator so elicitation works on the edge (Ajv's new Function() is blocked)
  • Remove custom elicitInput — use the SDK's built-in server.server.elicitInput() with { relatedRequestId: extra.requestId }
  • Drop legacy SSE transport — only Streamable HTTP going forward
  • WorkerTransport re-exported as deprecated alias for easier migration
  • Migration guide at packages/agents/MCP_MIGRATION_GUIDE.md

Architecture

Worker (McpAgent.serve())
  └── Routes by mcp-session-id header → Durable Object
        └── McpAgent (DO)
              ├── init() — register tools, resources, prompts
              ├── _setupMcp() — create SDK transport, connect server
              └── onRequest() → createMcpHandler(transport) → transport.handleRequest()
                    └── WebStandardStreamableHTTPServerTransport (from SDK)

CVE-2026-25536 Mitigation

  • McpAgent: one DO per session = one server + one transport per client, no cross-client sharing
  • createMcpHandler: factory-only signature makes it impossible to reuse a server across requests

CORS Handling

  • Stateless (createMcpHandler directly): handler owns full CORS (OPTIONS + response headers). Pass corsOptions to createMcpHandler.
  • Stateful (McpAgent.serve()): serve() owns full CORS (OPTIONS preflight + response headers). Pass corsOptions to serve().

Breaking Changes

See packages/agents/MCP_MIGRATION_GUIDE.md for full details:

  1. Legacy SSE transport removed (serveSSE(), mount())
  2. WorkerTransport removed (deprecated alias available)
  3. createMcpHandler options simplified
  4. Internal transport classes removed
  5. Internal headers removed
  6. createMcpHandler requires a factory function
  7. Session routing changes (DO-per-session)
  8. McpAgent.serve() options simplified

Test plan

  • npm run build passes
  • All tests pass (23 test files, 195 tests)
  • Formatting (oxfmt) and linting (oxlint) pass
  • Manual: elicitation example works end-to-end (init → tool call → elicitation → response)
  • Manual: deploy examples/mcp and test with MCP client
  • Manual: verify examples/mcp-worker stateless handler works

…eHTTPServerTransport

Kill the custom WorkerTransport, WS bridging, and legacy SSE transport.
McpAgent now handles HTTP directly in the DO using the SDK transport.
createMcpHandler requires a factory function to prevent CVE-2026-25536
(cross-client data leak from server/transport reuse). init() replaced
with onStart() to match the standard Agent pattern. Custom elicitInput
removed in favor of the SDK's server.server.elicitInput() with
relatedRequestId via extra.

Deletes ~5,400 lines of custom transport code.
@changeset-bot
Copy link

changeset-bot bot commented Feb 18, 2026

⚠️ No Changeset found

Latest commit: d27b0ff

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 18, 2026

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/agents@936

commit: d27b0ff

… API

Keeps the McpAgent API structurally similar to the previous version.
Also adds deprecated WorkerTransport alias for easier migration.
…kers

Ajv's default validator uses new Function() which is blocked in Workers.
Inject @cfworker/json-schema based validator in both McpAgent and
createMcpHandler so elicitation schema validation works on the edge.
createMcpHandler now accepts a pre-connected transport for stateful mode.
McpAgent._setupMcp() creates the handler once and onRequest() delegates to it.
CORS response headers are now always added by createMcpHandler in both
stateful and stateless modes. serve() only handles OPTIONS preflight
to avoid creating a DO for preflight requests.
@mattzcarey mattzcarey marked this pull request as draft February 18, 2026 17:48
Remove unused imports, fix oxfmt formatting.
@mattzcarey mattzcarey changed the title refactor(mcp): replace custom transport with SDK WebStandardStreamableHTTPServerTransport refactor(mcp): use SDK transport directly, drop custom transport layer Feb 18, 2026
@threepointone
Copy link
Contributor

Drop legacy SSE transport
This would make it a breaking change, wanna keep it for the next major?

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