Skip to main content
Glama
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.

![MCP Spec](https://img.shields.io/badge/MCP%20Spec-2026--07--28-blueviolet) ![SDK](https://img.shields.io/badge/SDK-official%20v2-orange) ![Language](https://img.shields.io/badge/language-Python-blue) ![License](https://img.shields.io/badge/license-research-lightgrey)

---

## 🔌 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.