Skip to content

fix(mcp): v0.0.95 compat shims for gradual rollout#1099

Draft
mattzcarey wants to merge 6 commits intomainfrom
fix/mcp-v0095-compat
Draft

fix(mcp): v0.0.95 compat shims for gradual rollout#1099
mattzcarey wants to merge 6 commits intomainfrom
fix/mcp-v0095-compat

Conversation

@mattzcarey
Copy link
Copy Markdown
Contributor

@mattzcarey mattzcarey commented Mar 12, 2026

Summary

Adds backwards/forwards compatibility between v0.0.95 Workers and v0.7.5+ DOs for the streamable-http transport, enabling gradual rollouts where both versions run simultaneously.

  • DO-side compat shims: Re-adds _init(), isInitialized(), setInitialized() methods that v0.0.95 Workers call via RPC; dual storage so both old and new formats work
  • Worker-side resilience: readInitializedState/writeInitializedState helpers that try new RPC methods first, fall back to old on method-missing errors; ws.send() after connect for old DOs that ignore headers; handles both CF_MCP_AGENT_EVENT and raw JSONRPC response formats
  • Transport compat: writeSSEEvent sends raw JSONRPC for old Workers (detected via _mcpNewStyle connection tag), wrapped CF_MCP_AGENT_EVENT for new
  • Deferred transport init: onStart tolerates transport init failure; fetch() and onMessage() lazily initialize when transport type becomes known
  • 8 compat tests: Simulate old Worker control flow (plain name, _init, /streamable-http WS upgrade, ws.send() messages, raw JSONRPC responses)

Context

A customer upgrading from v0.0.95 → v0.7.5 uses gradual rollouts. During deployment, old Workers call new DOs (and vice versa). The RPC interface changed completely between versions — this PR bridges both directions for streamable-http transport.

Test plan

  • npm run build — compiles clean
  • npm run check — format/lint/types pass
  • npm test — all 994 tests pass (52 files, 8 skipped pre-existing)
  • 8 new compat tests verify old Worker → new DO flow
  • Manual verification with customer's deployment

During gradual rollouts, old Workers (v0.0.95) and new DOs (v0.7.5+)
must interoperate. The RPC interface changed completely between versions:
- _init/isInitialized/setInitialized → updateProps/getInitializeRequest/setInitializeRequest
- WS upgrade to /streamable-http → cf-mcp-method/cf-mcp-message headers
- Raw JSONRPC responses → CF_MCP_AGENT_EVENT wrappers

DO-side (index.ts):
- Add _init(), isInitialized(), setInitialized() compat shims
- Dual storage: setInitializeRequest also writes "initialized" boolean
- fetch() override detects /streamable-http path, caches transport type
- Deferred onStart: tolerate transport init failure for old Workers
- onMessage handler for legacy ws.send() frames
- shouldSendProtocolMessages suppresses for old-style connections
- getTransportType/getSessionId fall back for names without prefix
- Tag new-style connections with _mcpNewStyle in onConnect

Worker-side (utils.ts):
- readInitializedState/writeInitializedState with isRpcMethodMissing fallback
- ws.send() messages after connect for old DOs that ignore headers
- Handle both CF_MCP_AGENT_EVENT and raw JSONRPC response formats

Transport (transport.ts):
- writeSSEEvent sends raw JSONRPC for old Workers (no _mcpNewStyle)
- Preserve connection state when setting requestIds/_standaloneSse

Tests:
- 8 compat tests simulating old Worker control flow against new DO
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 12, 2026

⚠️ No Changeset found

Latest commit: 2203b30

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

mattzcarey and others added 5 commits March 12, 2026 11:08
oxlint in CI rejects `any` — use `as unknown as OldWorkerStub` instead.
Old Worker uses `idFromName("streamable-http:${sessionId}")`, not a
plain session ID. Updated test to match actual v0.0.95 behavior.
Old Workers use idFromName("streamable-http:${sessionId}"), so the name
always has the transport prefix. Remove _cachedTransportType, deferred
onStart, lazy transport init, transportType storage, and plain-name
fallbacks that were added for a case that doesn't occur.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 12, 2026

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@1099

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@1099

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@1099

hono-agents

npm i https://pkg.pr.new/hono-agents@1099

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@1099

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@1099

commit: 20cd787

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.

1 participant