Migrate Coding Agent MCP Servers Without Losing Tool State
Last reviewed: 2026-08-08
The 2026-07-28 MCP release changes the transport contract for remote tools: requests are self-describing, the initialization handshake and protocol-level session identifier are gone, and servers can be reached through ordinary request routing. That is a transport change, not a promise that your application has no state. A coding agent can still need a workspace, an approval, a long-running task, or a conversation to survive from one call to the next.
The safe migration is therefore to make application state explicit, make every request independently understandable, and test the MCP gateway and model gateway as separate boundaries. The official MCP specification announcement describes the stateless core, Multi Round-Trip Requests (MRTR), routing headers, cache hints, and authorization changes. The MCP changelog is the normative checklist for fields and retry behavior. Use those documents to pin the protocol contract, then use the runbook below to move a coding-agent deployment in controlled steps.
Direct answer
Start by inventorying every place that assumes Mcp-Session-Id, initialize, connection affinity, a held-open stream, or a server-initiated request. Classify each dependency as one of three things:
- Explicit handle: return a short, opaque application handle from the first tool call and require it as an ordinary argument on later calls.
- Durable shared state: store larger or longer-lived state in a database, distributed cache, object store, or task system that every worker can reach.
- Protected request state: for an MRTR pause, return an expiring, authenticated state value that the client echoes on the retry. Bind it to the principal, operation, important parameters, expiry, and replay controls; never trust a client-returned value merely because it is unchanged.
Next, emit the protocol version, client identity, and capabilities in request metadata. Add the required Mcp-Method and Mcp-Name headers, but have the gateway compare those values with the JSON-RPC body before routing or authorizing. Headers are a routing aid, not an authority supplied by the caller.
For a tool that needs confirmation or another input, replace a server-initiated request with MRTR: return resultType: "input_required", collect the answer, and retry the original operation with a new request ID, inputResponses, and the protected state. Make side effects idempotent and stage them after approval. A broken response stream is a failed in-flight request; re-issue it as a new request rather than relying on redelivery.
Finally, canary the new client, MCP server, gateway policy, OAuth validation, and CometAPI-backed model route together. Keep the model-provider adapter’s tests separate from MCP transport tests so a model outage cannot be mistaken for a protocol regression. The CometAPI MCP migration guide recommends the same staged approach: find hidden session dependencies, update routing and authorization behavior, and canary before retiring legacy paths.
Who this is for
This guide is for platform engineers, developer-tool owners, and security operators running coding agents that call remote MCP servers over Streamable HTTP. It is especially useful when:
- a tool call can move between server instances;
- a gateway, WAF, or rate limiter currently parses request bodies or relies on sticky sessions;
- an agent can ask for approval, missing input, or a long-running task;
- OAuth credentials, tool catalogs, or workspace state are shared across workers; or
- the agent uses CometAPI as a model backend and you need to distinguish model-routing failures from tool-transport failures.
A local, one-shot stdio server with no cross-call state is a lower-risk case. It still needs SDK and compatibility tests, but it may not need a data-store redesign. A remote server using session IDs for business state, OAuth, streaming, or server-initiated interactions is a higher-risk migration. Use the risk categories in CometAPI’s migration guide to choose the size of your canary.
Key takeaways
- Stateless transport does not mean stateless business logic. Put workspace, task, and approval continuity in explicit handles or shared durable storage.
- Treat every request as a contract. Include protocol metadata and identify the client on each request; implement
server/discoverso version and capability checks happen before a risky call. - Use MRTR for pauses. An
input_requiredresult is an interim result, not permission to perform the side effect. Retry with a new JSON-RPC ID and validate the answer again. - Check two views of the request. Compare
Mcp-MethodandMcp-Namewith the body before applying routing, rate, or authorization policy. - Bind authorization to its issuer. Persist credentials by authorization-server issuer and reject a retry or token intended for another issuer. Prepare for Client ID Metadata Documents rather than adding new Dynamic Client Registration dependencies.
- Cache only what is safe to share. List responses can carry
ttlMsandcacheScope; do not turn a per-principal catalog into a public cache entry. - Make retries observable and harmless. New request IDs, idempotency checks, round limits, expiry, and sanitized logs matter more after stream resumability is removed.
- Keep the CometAPI boundary explicit. Record whether a failure occurred in MCP validation, tool execution, or model inference, and test each boundary independently. For a related contract-testing pattern, see Test Coding Agent Tool Calls Before Switching Models Through CometAPI .
Sources checked
These are the public sources refetched for this article:
- The 2026-07-28 MCP specification announcement
explains the stateless core, removal of the handshake and
Mcp-Session-Id, MRTR, routing headers, cache hints, authorization hardening, and SDK updates. - The MCP 2026-07-28 key changes
defines the required metadata,
server/discover, explicit handles,input_requiredretries, new request IDs after a broken stream, cache fields, issuer validation, and deprecations. - CometAPI’s MCP 2026-07-28 Migration Guide supplies an operator-oriented dependency audit, state patterns, MRTR safeguards, gateway checks, OAuth considerations, and staged rollout advice.
- Docker’s MCP gateway documentation describes host-managed server registration, OAuth-backed remote servers, static and dynamic loading, and the boundary between a sandboxed agent and the gateway.
- Microsoft’s MCP Security Gateway tutorial documents tool-call interception, denied and sensitive tools, approval routing, rate limits, response scanning, fail-closed behavior, and redacted audit records.
Contract details to verify
Make the migration testable by writing down the contract before changing code. A minimal Streamable HTTP request should have the following shape; authentication material is intentionally omitted from the fixture and must be injected by the runtime secret store, never copied into a test log.
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: update_workspace
Content-Type: application/json
{
"jsonrpc": "2.0",
"id": "req-41",
"method": "tools/call",
"params": {
"name": "update_workspace",
"arguments": {
"workspaceHandle": "ws-demo",
"status": "approved"
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {
"name": "agent-cli",
"version": "2.0.0"
},
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}
Verify these details in a contract test:
- Version and discovery: a modern server answers
server/discoverwith supported versions, capabilities, and identity. A version mismatch returns the protocol’s unsupported-version error, and the client chooses a deliberate legacy fallback or stops. - State ownership: a handle is opaque, scoped to the authenticated principal, expires when appropriate, and is accepted by any worker. If the workflow needs durable state, test a worker restart and a load-balancer hop.
- MRTR semantics: an interim response has
resultType: "input_required"; the retry has a new ID, validatedinputResponses, a bounded round count, and an expiring protected state. Validate authorization on every round. - Routing consistency: reject a request when a header names one tool but the body invokes another. Log the mismatch without retaining the body or sensitive arguments.
- Authorization: bind a stored credential reference to its issuer and client identity. Check the issuer before redeeming an authorization response, and do not reuse credentials after an issuer change.
- Caching and notifications: verify deterministic ordering and
ttlMs/cacheScopehandling for catalogs. Move change subscriptions to the new subscription stream and measure traffic still using deprecated HTTP+SSE, Roots, Sampling, or Logging features. - Model boundary: keep a fixture that reaches the MCP gateway with a stubbed model result, and a separate health test for the CometAPI model route. A model response failure should not cause a tool side effect to run twice.
A happy-path operator workflow
- Deploy the new client and server behind a small feature flag.
- Call
server/discover, thentools/list; confirm the catalog order and cache scope. - Run a read-only tool through the gateway and verify header/body agreement.
- Create a workspace and return
ws-demo; send the next call to a different worker to prove the handle is sufficient. - Run a side-effecting tool that requires confirmation. Expect
input_required, collect the approval, retry with a new ID, and commit the side effect once. - Send the resulting model task through the existing CometAPI adapter, recording only route, status, latency, and usage counters approved by your data policy.
- Compare success rate, duplicate-side-effect count, authorization denials, and latency with the legacy canary before increasing traffic.
The error-path operator workflow
If discovery reports an unsupported version, stop the canary and use the tested legacy path; do not silently downgrade a high-risk tool. If headers and body disagree, reject before execution and fix the client or gateway. If MRTR returns an expired or replayed state, return a safe error and ask the agent to start a new operation. If the response stream breaks, issue a fresh request ID and rely on idempotency rather than redelivery. If issuer validation fails, deny the authorization exchange and rotate the affected reference through the normal secret store. If the CometAPI model route times out, mark the model leg failed, preserve the tool operation as uncommitted, and retry only under the model adapter’s bounded retry policy. Roll back the feature flag when the canary crosses a predeclared error, duplicate, or denial threshold.
Sanitized logging fields
Log enough to reconstruct a decision without retaining prompts, tool arguments, authorization material, or opaque state. A useful event schema is:
{
"timestamp": "2026-08-08T12:00:00Z",
"request_id": "req-42",
"protocol_version": "2026-07-28",
"client_name": "agent-cli",
"server_instance": "mcp-02",
"mcp_method": "tools/call",
"mcp_name": "update_workspace",
"principal_ref": "[REDACTED]",
"state_ref": "[REDACTED]",
"request_state_present": true,
"round": 1,
"result_type": "complete",
"policy_decision": "allowed",
"model_route": "cometapi",
"http_status": 200,
"latency_ms": 182,
"error_code": null,
"tool_arguments": "[OMITTED]"
}
Use a short-lived correlation ID, a redacted principal reference, and a hash or redacted reference for state. Keep raw prompts, tool parameters, response content, and credential values out of ordinary logs. Microsoft’s gateway example explicitly describes redacted persisted audit payloads and response policies that block or sanitize credential and PII leaks; apply the same principle to your own gateway and model adapter.
Failure modes
Removing the session header while keeping local memory. Development succeeds on one worker, then a load-balancer hop loses the workspace. Detect this with a restart and cross-instance test. Move the state to an explicit handle plus shared storage, or keep the legacy path until that test passes.
Trusting routing headers. A caller can label a request as a harmless tool while the JSON body invokes a sensitive one. Compare both representations and fail closed on mismatch. Rate limits, approvals, and audit records should use the verified tool name.
Performing a side effect before MRTR approval. A delete, deployment, purchase, or credit change can happen before the user answers, then happen again on retry. Stage the operation, use an idempotency record, and commit only after the final accepted round.
Reusing a request ID after a broken stream. The new protocol removes SSE resumability and message redelivery. A retry with the same ID can be ambiguous to a downstream service. Generate a new ID, carry only protected state, and make the operation deduplicate safely.
Treating requestState as trusted input. A client can alter or replay an opaque value. Authenticate it, bind it to the operation and principal, enforce expiry, and reject reuse. Do not place secrets in it.
Caching a private tool catalog publicly. The new cache fields make efficient catalog caching possible, but a per-user list can reveal tools or resources to another user. Test cacheScope, authorization, and invalidation with two principals.
Ignoring authorization-server changes. Reusing a credential with a different issuer creates an authorization mix-up risk. Persist issuer identity alongside the reference, validate iss, and plan the move from Dynamic Client Registration toward Client ID Metadata Documents.
Assuming the sandbox contains every MCP process. Docker notes that host-launched local stdio servers run outside the sandbox and can see host files, network resources, and available credentials. Treat host registrations as a separate trust boundary, load only named servers, and test static versus dynamic gateway mode.
Blaming MCP for a model-backend outage. A CometAPI timeout, model refusal, or rate response is a different failure class from a malformed MCP request. Tag the failing leg, stop uncommitted side effects, and use separate retry and alert policies. The CometAPI response checks guide can complement the transport fixture.
FAQ
Does stateless MCP mean my agent cannot keep a workspace?
No. It means the transport no longer hides continuity in a protocol session. Return a server-minted handle or use shared durable state, then require that reference in the next tool call. The official changelog recommends explicit handles for cross-call state.
Do I need to rewrite a local stdio server first?
Not necessarily. A one-shot local server with no cross-call state is lower risk, but upgrade the SDK and test version discovery or compatibility behavior. Do not use the low-risk classification as a reason to skip gateway and authorization tests when the same tool is also exposed remotely.
What replaces a server-initiated confirmation request?
MRTR. Return input_required with the requested interaction, then retry the original call with validated responses and a new request ID. Keep the operation staged until the final round is accepted.
Can I authorize a request from the headers alone?
No. Headers help a gateway route and meter traffic, but the client supplies them. Compare them with the JSON-RPC method and tool name, then apply policy to the verified request. Also validate the authorization issuer and principal on each MRTR retry.
How should CometAPI fit into this migration?
Keep CometAPI model routing as an explicit downstream boundary. Maintain a transport fixture that proves MCP validation and tool idempotency without a live model, plus a separate model-route health test. When both are enabled in a canary, log which leg failed and never retry a side effect merely because model inference was retried.
Can I keep the old protocol indefinitely?
Use a time-bounded compatibility path. The 2026-07-28 release describes a formal deprecation policy with at least a twelve-month window, while the changelog marks HTTP+SSE and several older features deprecated. Measure legacy traffic, publish a removal date, and migrate callers deliberately.
Reader next step
Create one migration branch and write three fixtures before changing production traffic: a cross-worker handle test, an MRTR approval-and-retry test, and a header/body mismatch test. Search the client, server, gateway, and deployment files for Mcp-Session-Id, initialize, notifications/initialized, sticky-session settings, elicitation/create, sampling/createMessage, Last-Event-ID, and legacy HTTP+SSE. Then run a read-only canary through the MCP gateway and the existing CometAPI model route, using the sanitized fields above to compare outcomes.
For the surrounding operations, pair this runbook with Keep Retried CometAPI Calls From Running Agent Tools Twice and the contract-testing guide linked above. When your fixtures pass and your rollback threshold is written down, Start with CometAPI to evaluate the model route behind the same observable, explicit tool boundary.