MCPResilience
by HoorShumail
README.md
# 🛡️ 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.
   
---
## 🔌 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](#-protocol-support) for the full breakdown of both eras.
---
## 🧠 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](#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](#-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
```bash
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:
```bash
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
- GitHub: [https://github.com/HoorShumail](https://github.com/HoorShumail)
- LinkedIn: [https://www.linkedin.com/in/hoor-shumail-a3a076326/](https://www.linkedin.com/in/hoor-shumail-a3a076326/)
## 📜 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.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues