Skip to main content
Glama

🛡️ MCPResilience

A Resilient, Spec-Compliant MCP Server Built on the Official SDK v2

Speak MCP however your client speaks it. MCPResilience auto-detects legacy and modern protocol eras on the very first request — and survives the difference.

MCP Spec SDK Language License


🔌 Client Modes

MCPResilience auto-detects which protocol era a connecting client speaks — no configuration needed:

  1. ⚡ Modern Stateless Clients — clients whose first request carries the _meta envelope (io.modelcontextprotocol/protocolVersion + clientInfo) skip the handshake entirely. tools/call can be their very first message.

  2. 🤝 Legacy Handshake Clients — clients without that envelope are routed through the traditional initialize flow, with -32600 Invalid request parameters enforced on anything sent before initialize completes.

See Protocol Support for the full breakdown of both eras.


Related MCP server: mcp-uni

🧠 What This Is

MCPResilience exists because swapping a hand-rolled MCP server for the official SDK isn't a drop-in change — the wire format shifts in ways that break naive migrations. This project tackles that in two stages:

  1. SDK Migration — replace a hand-rolled MCP server core with the official MCP SDK v2, targeting the 2026-07-28 spec, to gain a stateless core and type-safe Pydantic serialization.

  2. Compatibility Hardening — make sure the migration doesn't silently drop support for clients still using the legacy handshake, lose data to upstream schema gaps, or break the experimental Tasks extension mid-flight.

Both stages are documented honestly below, including the one upstream SDK bug that surfaced along the way.


📊 Key Results

All Phase 5 compatibility tests and Phase 6 benchmarks pass, end-to-end, on the official MCP SDK v2 — with full support for both protocol eras and the experimental Tasks extension, plus one upstream SDK bug identified and patched (see Known SDK Quirk).

Tasks extension: what changed under the SDK migration

Aspect

Legacy behavior

SDK v2 behavior

Declaring task support

Boolean longRunning: true flag

execution object, e.g. execution: {"taskSupport": "required"}

Task handle location

Top-level taskHandle in result

Moved to metadata envelope: result._meta.taskHandle

Terminal success state

"succeeded"

"completed"

Task content delivery

Returned via tasks/get polling

Delivered only via the tools/call response stream — tasks/get returns status metadata only (statusMessage, createdAt, etc.)

Re-cancelling a finished task

{cancelled: true} or -32602 error

Idempotent — returns CancelTaskResult with status: "cancelled"


🏗️ How It Works

Incoming connection
        │
        ▼
  First request received
        │
        ▼
  Does it carry the _meta envelope?
  (protocolVersion + clientInfo)
        │
   ┌────┴────┐
  Yes         No
   │           │
   ▼           ▼
Modern Era   Legacy Era
(stateless)  (handshake required)
   │           │
   ▼           ▼
tools/call   initialize → any request
runs          (initialize enforced,
immediately    notifications/initialized
               not blocked)
   │           │
   └─────┬─────┘
         ▼
  Era locked for the
  life of the connection

📡 Protocol Support

Stateless Era (2026-07-28)

Under the modern spec, the traditional initialize → notifications/initialized handshake is obsolete. The server runs a serve_dual_era_loop:

  • If the first request includes the _meta envelope with io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientInfo, the server locks into the modern stateless era.

  • Clients can send tools/call as their very first request — no initialize call needed.

Legacy Era

If the first request lacks the modern _meta envelope, the server locks into the legacy era:

  • Any request sent before initialize (e.g. tools/call) is rejected with -32600 Invalid request parameters.

  • Once initialize has been answered, the server does not wait for notifications/initialized before processing further requests.

Version Mismatch Handling

Modern requests specifying an unsupported protocol version in the _meta envelope are rejected cleanly with -32022 Unsupported protocol version — the connection itself is preserved rather than dropped.


🧩 Tasks Extension Deep Dive

The experimental Tasks extension underwent the most wire-format churn of anything in the migration (see the comparison table in Key Results). Two behaviors are worth calling out specifically:

  • tasks/get is metadata-only now. Task content is delivered exclusively through the tools/call response stream; polling tasks/get will only ever return status fields like statusMessage and createdAt — never the payload itself.

  • Cancellation is idempotent by design. Re-cancelling a task that's already completed or cancelled returns a successful CancelTaskResult rather than an error, unlike the legacy server's -32602 on repeat cancellation.

Known SDK Quirk

SDK Issue #2156 — execution field stripped from tools/list. The current Pydantic schema for v2026_07_28.Tool doesn't define the experimental execution field, so serialize_server_result silently strips it from tools/list responses.

Workaround: a targeted monkeypatch on mcp_types.methods.serialize_server_result intercepts the validated output and restores the execution dictionary from the original handler data. This is a stopgap — remove it once the upstream schema ships the field natively.


🔧 Technical Notes (the parts that weren't trivial)

  1. Era detection happens exactly once, on the first request. There's no mid-connection upgrade path — a client that opens without the _meta envelope stays in the legacy era for the life of that connection, even if it starts sending modern-shaped requests later.

  2. The task handle didn't just move, its contract changed. Relocating taskHandle from the top-level result to result._meta also freed up the top-level result object to be reserved purely for immediate content output and the isError flag — a cleaner separation than the legacy shape allowed.

  3. The monkeypatch is scoped narrowly on purpose. It only intercepts serialize_server_result to restore one missing field, rather than forking or wrapping the SDK's schema wholesale — keeping the patch easy to delete the moment upstream ships a fix.


🛠️ Tech Stack

  • Protocol: JSON-RPC 2.0 over the Model Context Protocol, spec 2026-07-28

  • SDK: Official MCP SDK v2 — Pydantic-based schema validation & serialization

  • Server core: Python, stateless-first request handling (serve_dual_era_loop)

  • Testing: Phase 5 compatibility suite + Phase 6 benchmark run


🚀 Getting Started

git clone https://github.com/HoorShumail/MCPResilience.git
cd MCPResilience
pip install -r requirements.txt

Adjust the commands above to match your actual package layout and entry point.

Run the compatibility suite and benchmarks with:

pytest

⚠️ Honest Limitations

  • The Tasks extension is still experimental upstream. It isn't finalized in the core MCP spec, so its wire format could shift again in a future SDK release — this server tracks the SDK's current experimental implementation, not a stable target.

  • The execution-field fix is a monkeypatch, not a permanent solution. It patches serialize_server_result at runtime rather than fixing the underlying schema — it needs to be removed once SDK Issue #2156 ships an upstream fix.

  • Era detection is first-request-only. A client locked into the legacy era at connection start has no path to "upgrade" to the stateless era mid-connection, even if its later requests look modern.


🙏 Acknowledgments

  • Official MCP SDK v2 — Model Context Protocol maintainers

  • Model Context Protocol specification (2026-07-28)

🧑‍💻 Author

Hoor Shumail AI | Machine Learning | Agentic AI | Multi-Agent Systems | Career Intelligence

📜 License

This project is developed for educational, research, and portfolio purposes.

It builds upon the official Model Context Protocol SDK — refer to that SDK's own license and the Model Context Protocol specification for terms governing those components.

Related MCP Connectors

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    A dual-protocol MCP server that supports both modern Streamable HTTP and legacy HTTP+SSE protocols, providing backward compatibility for clients while offering advanced features like session resumability.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A universal MCP server that acts as a unified gateway for dynamically connecting and managing multiple MCP servers via a single HTTP endpoint.
    3 npm
    6
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables multiple MCP clients to securely access various third-party MCP backends through a single HTTP gateway, with modern MCP handshake compatibility and session lifecycle management.
    38 npm
    MIT