You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Prove that the gateway can migrate to MCP Python SDK 2.0 — the vehicle for spec 2026-07-28 — while existing 2024/2025-era MCP servers and MCP clients keep working through the same gateway. Timeboxed spike: 3–5 days, producing a go/no-go answer and an effort estimate for the full migration.
Why a spike is needed
One SDK per process. SDK 1.x and 2.x are the same mcp distribution and cannot coexist in one venv. Backward compatibility must be achieved at the wire-protocol level: a v2-SDK gateway negotiating older protocol versions per connection.
The spec defines the downgrade path (2026-07-28 clients probe server/discover, fall back to legacy initialize; deprecated methods keep working ≥1 year), and the SDK splits HANDSHAKE_PROTOCOL_VERSIONS (2024-11-05 → 2025-11-25) from MODERN_PROTOCOL_VERSIONS (2026-era) — but there is no explicit guarantee that v2's stricter inbound schema validation tolerates old servers that deviate slightly from spec. This is the main empirical risk and must be measured, not assumed.
The SDK bump is only part of "support 2026-07-28". The SDK covers outbound clients and the streamable-HTTP ingress. The hand-rolled SSE/WebSocket/stdio ingress transports, the gateway's own initialize handling (mcpgateway/cache/session_registry.py), and the local wire-type layer (mcpgateway/common/models.py) gain nothing from the upgrade and need separate spec work — this spike sizes that work but does not build it.
Current surface (from code analysis)
Only 11 of 386 production files import the SDK; common/models.py (~1,400-line SDK-independent wire-type layer) insulates the rest. Four hard spots concentrate the risk:
Spot
Location
Issue under SDK 2.0
Private-internals probe
services/upstream_session_registry.py:207
Reaches into session._write_stream etc.; self-declared compat marker >=1.27.0,<2.0.0. Must be re-derived or replaced.
Validator-behavior coupling
services/tool_service.py:1592-1652
Relies on undocumented output-schema short-circuit and ClientSession._validate_tool_result semantics to preserve tool-result payloads. v2's stricter validation may break both.
Inherited version policy
middleware/protocol_version.py:25-27
Accepted/default versions inherited from SDK constants. v2 deprecates SUPPORTED_PROTOCOL_VERSIONS and moves LATEST_PROTOCOL_VERSION to 2026-07-28 — silent behavior change. Needs a gateway-owned floor.
Back-channel passthrough
services/notification_service.py
v2 changes message_handler semantics; 2026-era sessions raise NoBackChannelError for server-initiated sampling/elicitation. Needs a design check, not a mechanical port.
Also affected: decorator-based low-level Server handlers in transports/streamablehttp_transport.py (v2 moves registration to constructor on_* params), McpError → MCPError, streamablehttp_client → streamable_http_client, httpx → httpx2, camelCase → snake_case fields (by_alias=True on serialization), and the version-pinned tests/compliance/mcp_2025_11_25/ tree.
Plan
Phase 1 — Environments, test servers, focused interop check
Migrate the client path first (gateway_service, tool_service, resource_service, prompt_service, upstream_session_registry): renames, streamable_http_client, httpx2 objects, float timeouts, snake_case; disable the private-stream probe (documented owner-task-liveness fallback)
Replace inherited version constants in middleware/protocol_version.py with a gateway-owned supported-versions list (union of handshake + modern eras, config-driven default)
Migrate the streamable-HTTP ingress (streamablehttp_transport.py): decorator → on_* handler registration, (ctx, params) signatures, snake_case at the _to_mcp_* adapters
Timebox fallback: if the ingress rewrite overruns, park it and demo tool invocation through the gateway's REST API instead — that still proves the v2 client path + federation, and the ingress effort gets sized in the findings
Phase 3 — Demo + findings
On the branch: legacy server (SSE) + modern server (streamable HTTP) registered, one virtual server composed from both, happy-path and error-path tool call from each origin server (via MCP ingress if migrated, else REST fallback)
Assert per-connection negotiated protocol versions from gateway logs: legacy session on a 2025-era version, modern session on 2026-07-28, coexisting in one process — this assertion is the feasibility claim
Findings comment on this issue: interop results, verdict per hard spot (portable / needs redesign / upstream gap), recommended compatibility strategy, sized backlog of deferred work (hand-rolled ingress spec support, MRTR-based elicitation redesign, session-pool simplification, compliance-tree re-key, test migration), full-migration effort estimate
Success criteria
Interop check passes in every v2-client → 1.x-server cell for at least one client mode
Gateway boots and serves on mcp>=2 with the client path migrated
Logged evidence of a 2025-era and a 2026-era session coexisting in one gateway process
Written verdict + forward plan for each of the 4 hard spots
Full-migration effort estimate posted as findings
Risks
v2 strict validation rejects real-world old servers
mode='auto' discover probe confuses old servers
Tool-result payload preservation breaks
Ingress handler rewrite exceeds timebox
Session-pool liveness probe has no v2 equivalent
httpx2 conflicts with gateway's own httpx usage
Out of scope for the spike
2026-07-28 support on hand-rolled SSE/WS/stdio ingress · MRTR-based elicitation redesign · session-pool simplification for the stateless era · compliance-tree re-key · resources/prompts full coverage · performance and production hardening.
Goal
Prove that the gateway can migrate to MCP Python SDK 2.0 — the vehicle for spec 2026-07-28 — while existing 2024/2025-era MCP servers and MCP clients keep working through the same gateway. Timeboxed spike: 3–5 days, producing a go/no-go answer and an effort estimate for the full migration.
Why a spike is needed
mcpdistribution and cannot coexist in one venv. Backward compatibility must be achieved at the wire-protocol level: a v2-SDK gateway negotiating older protocol versions per connection.server/discover, fall back to legacyinitialize; deprecated methods keep working ≥1 year), and the SDK splitsHANDSHAKE_PROTOCOL_VERSIONS(2024-11-05 → 2025-11-25) fromMODERN_PROTOCOL_VERSIONS(2026-era) — but there is no explicit guarantee that v2's stricter inbound schema validation tolerates old servers that deviate slightly from spec. This is the main empirical risk and must be measured, not assumed.initializehandling (mcpgateway/cache/session_registry.py), and the local wire-type layer (mcpgateway/common/models.py) gain nothing from the upgrade and need separate spec work — this spike sizes that work but does not build it.Current surface (from code analysis)
Only 11 of 386 production files import the SDK;
common/models.py(~1,400-line SDK-independent wire-type layer) insulates the rest. Four hard spots concentrate the risk:services/upstream_session_registry.py:207session._write_streametc.; self-declared compat marker>=1.27.0,<2.0.0. Must be re-derived or replaced.services/tool_service.py:1592-1652ClientSession._validate_tool_resultsemantics to preserve tool-result payloads. v2's stricter validation may break both.middleware/protocol_version.py:25-27SUPPORTED_PROTOCOL_VERSIONSand movesLATEST_PROTOCOL_VERSIONto 2026-07-28 — silent behavior change. Needs a gateway-owned floor.services/notification_service.pymessage_handlersemantics; 2026-era sessions raiseNoBackChannelErrorfor server-initiated sampling/elicitation. Needs a design check, not a mechanical port.Also affected: decorator-based low-level
Serverhandlers intransports/streamablehttp_transport.py(v2 moves registration to constructoron_*params),McpError → MCPError,streamablehttp_client → streamable_http_client,httpx → httpx2, camelCase → snake_case fields (by_alias=Trueon serialization), and the version-pinnedtests/compliance/mcp_2025_11_25/tree.Plan
Phase 1 — Environments, test servers, focused interop check
MCPServerover streamable HTTP; verify it actually negotiates 2026-07-28 with a v2 client (not a silent downgrade)mode='auto'andmode='legacy'), v2 client → modern server, v1 client → minimal v2 low-level server. Operations: initialize/discover, list_tools, call_tool happy + error. Record negotiated protocol version and any validation rejections per cell.Phase 2 — Gateway branch spike
Branch
poc/mcp-sdk-2. Goal: gateway boots and federates — POC quality only, tests skipped except smoke.mcp>=2,<3; addmcp-types,httpx2,opentelemetry-api; raise floors (anyio ≥4.9, pydantic ≥2.12, sse-starlette ≥3.0)gateway_service,tool_service,resource_service,prompt_service,upstream_session_registry): renames,streamable_http_client, httpx2 objects, float timeouts, snake_case; disable the private-stream probe (documented owner-task-liveness fallback)middleware/protocol_version.pywith a gateway-owned supported-versions list (union of handshake + modern eras, config-driven default)streamablehttp_transport.py): decorator →on_*handler registration,(ctx, params)signatures, snake_case at the_to_mcp_*adaptersPhase 3 — Demo + findings
Success criteria
mcp>=2with the client path migratedRisks
mode='auto'discover probe confuses old serversOut of scope for the spike
2026-07-28 support on hand-rolled SSE/WS/stdio ingress · MRTR-based elicitation redesign · session-pool simplification for the stateless era · compliance-tree re-key · resources/prompts full coverage · performance and production hardening.
References