Skip to content

feat: implement MCP 2026-07-28 stateless protocol, task management, CIMD/OAuth 2.1 hardening, and MRTR support - #20

Merged
manish-wekan merged 59 commits into
nitrocloudofficial:statelessfrom
pallavt93:feature/stateless_2.0
Sep 9, 2026
Merged

feat: implement MCP 2026-07-28 stateless protocol, task management, CIMD/OAuth 2.1 hardening, and MRTR support#20
manish-wekan merged 59 commits into
nitrocloudofficial:statelessfrom
pallavt93:feature/stateless_2.0

Conversation

@pallavt93

@pallavt93 pallavt93 commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Description

This PR implements the official Model Context Protocol (MCP) 2026-07-28 specification across the NitroStack Python SDK while maintaining backward compatibility for existing 2025-era sessionful clients.

It introduces protocol-era selection, official mcp 2.x integration for stateless HTTP and stdio, a task orchestration engine (TaskManager / TaskStore), multi-channel task status notification routing, hardened OAuth 2.1 and Client ID Metadata Document (CIMD) resolution, Multi-Round-Trip Requests (MRTR) for interactive elicitation, JSON Schema 2020-12 validation, W3C trace context propagation, cache hints, and a dedicated MCP 2.0 acceptance test suite.

Scope: 86 files changed · +15,105 / −650 lines · 58 commits


System Architecture Diagram

flowchart TD
    subgraph Clients["MCP Clients"]
        C1["Modern MCP Client (2026-07-28)\n(NitroStudio / Claude / Cursor)"]
        C2["Legacy MCP Client (2025-06-18)\n(Sessionful SSE / HTTP)"]
    end

    subgraph Transport["Transport Layer (HTTP / STDIO)"]
        TH["Streamable HTTP (/mcp)"]
        TS["StdIO Transport"]
        SEL{"Protocol Era Selector\n(MCP_STATELESS / NITRO_MCP_PROTOCOL_VERSION)"}
    end

    subgraph Engines["HTTP / Wire Engines"]
        MV2["Official mcp 2.x\n(sessionless / dual-era)"]
        LV1["Legacy 1.x Session Manager\n(era: legacy only)"]
    end

    subgraph Security["Auth & Interception Layer"]
        JWT["JWT / Bearer Verification\n(Authorization / envelope auth)"]
        CIMD["CIMD & SSRF Resolver\n(HTTPS, RFC 6890, 5 KiB cap, RFC 9207 iss)"]
        SAN["Argument Sanitizer\n(strips _meta from tool arguments)"]
    end

    subgraph Tasks["Task Orchestration (SEP-2663)"]
        TINT["Task Wire Interceptor\n(tools/call task param, tasks/get, tasks/cancel)"]
        TM["TaskManager\n(lifecycle, access control, cancellation safety)"]
        TS_STORE[("Pluggable TaskStore\n(InMemoryTaskStore / custom)")]
        NR["TaskStatusRouter\n(/subscriptions/listen / legacy SSE / stdio)"]
    end

    subgraph Features["Protocol Features"]
        MRTR["MRTR Elicitation\n(input_required / requestState)"]
        SCH["JSON Schema 2020-12\n(depth-bounded, registration gate)"]
        OBS["Observability & Caching\n(W3C Trace Context / cache hints)"]
    end

    subgraph Registry["NitroStack Application Registry"]
        TOOLS["@tool handlers"]
        RES["@resource handlers"]
        PROMPTS["@prompt handlers"]
        WIDGETS["Widget / UI templates"]
    end

    C1 -->|"stateless HTTP (_meta / server/discover)"| TH
    C2 -->|"legacy JSON-RPC (initialize / Mcp-Session-Id)"| TH
    C1 -.->|"stdio stream"| TS
    C2 -.->|"stdio stream"| TS

    TH --> SEL
    TS --> SEL

    SEL -->|"era: auto (default) | modern"| MV2
    SEL -->|"era: legacy (2025-06-18)"| LV1

    MV2 --> JWT
    JWT --> CIMD
    MV2 --> TINT
    TINT --> TM
    TM <--> TS_STORE
    TM --> NR

    MV2 --> SAN
    MV2 --> SCH
    MV2 --> MRTR
    MV2 --> OBS

    SAN --> TOOLS
    OBS --> TOOLS
    OBS --> RES
    SCH --> PROMPTS
    TOOLS --> WIDGETS
    RES --> WIDGETS
Loading

Protocol Era Support & Default Behavior

NitroStack Python features runtime protocol era resolution with auto as the default when unset:

  • Unset / NITRO_MCP_PROTOCOL_VERSION=auto (default):
    One official mcp 2.x sessionless engine serves both modern 2026-07-28 clients (per-request _meta, server/discover, SEP-2243 headers) and legacy 2025 clients (sessionless initialize — no Mcp-Session-Id).
  • NITRO_MCP_PROTOCOL_VERSION=2026-07-28 (or modern, latest, 2026):
    Pure modern stateless MCP 2.0; legacy 2025 wire is rejected.
  • NITRO_MCP_PROTOCOL_VERSION=2025-06-18 (or legacy, 2025, 2025-11-25):
    Sessionful 2025-era wire via the legacy HTTP engine only.
  • Precedence order:
    MCP_STATELESS > NITRO_MCP_PROTOCOL_VERSION > ServerConfig.protocol_era > default auto.

Era resolution is logged at HTTP/stdio startup with source: mcp_stateless | env | config | default.


Key Changes & Enhancements

1. Stateless Core & Protocol Versioning (SEP-2575, SEP-2567, SEP-2243)

  • Official mcp 2.x integration: Bumped dependency to mcp>=2,<3; modern and auto eras mount the official v2 HTTP handler — not a second session manager on /mcp.
  • Protocol era selector: nitrostack/protocol/version.py — dynamic resolution with documented era aliases; unknown tokens → auto.
  • Discovery endpoint: server/discover on the HTTP engine advertising metadata, capabilities, and extensions.
  • Stateless HTTP transport: No Mcp-Session-Id on sessionless engines; per-request _meta envelope support.
  • Header validation & CORS: SEP-2243 method contract table for MCP-Protocol-Version, Mcp-Method, Mcp-Name, Mcp-Param-*; CORS preflight for 2026 headers; echo headers on responses.
  • Health & ping: /mcp/health advertises resolved era; header-only ping fast path without a JSON-RPC body.
  • Concurrent RPC isolation: Identical JSON-RPC ids on concurrent requests stay isolated.

2. Task Orchestration & Storage Abstraction (SEP-2663)

  • Pluggable task storage: TaskStore interface and InMemoryTaskStore with terminal-only TTL eviction from lastUpdatedAt.
  • Task lifecycle & safety: TaskManager — create, progress, input_required, resume, complete, fail, cancel; pagination; cancellation safety.
  • Multi-tenant authorization: TaskAccessContext with anti-enumeration (TaskNotFoundError on mismatch).
  • Wire interception: Task-augmented tools/call returns immediate CreateTaskResult; rejects deprecated tasks/result and tasks/list on modern wire.
  • Multi-channel notifications: TaskStatusRouter in nitrostack/tasks/notify.py fans out notifications/tasks/status to /subscriptions/listen, legacy /sse, and stdio sessions; failed notify does not fail the task; honors task access context.

3. Hardened OAuth 2.1 & CIMD Authentication (RFC 9207, RFC 7517)

  • CIMD resolver: HTTPS-only, no redirects, 5 KiB payload cap, timeout support, strict client-id URL validation.
  • SSRF protection: RFC 6890 special-use and private IP blocking during CIMD fetch.
  • Token extraction: Bearer from Authorization, spec envelope auth slot, and vetted _meta; verified JWT before unsigned envelope claims.
  • Argument sanitization: Strips _meta and io.modelcontextprotocol/* keys from tool arguments before handlers run.
  • RFC 9207 anti-mixup: Issuer validation on verified tokens.

4. Multi-Round-Trip Requests (MRTR) (SEP-2322)

  • InputRequiredResult and MRTR helpers for multi-turn elicitation with input_required, inputRequests, and opaque requestState.

5. JSON Schema 2020-12 & Schema Normalization (SEP-2106)

  • Registration gate rejects non-2020-12 schemas at tool registration.
  • Depth-bounded schema processing (max 64); resource URI templates and resolution.

6. Cache Hints, Trace Context, Subscriptions & Stdio (SEP-2549, SEP-414)

  • Cache hints (ttlMs, cacheScope) on list/read/discover responses.
  • W3C Trace Context (traceparent, tracestate, baggage) on ExecutionContext.trace.
  • GET/POST /subscriptions/listen: SSE attach on the official v2 subscription bus with JWT-gated listen when OAuth/JWT is required.
  • Modern stdio: auto → official dual-era loop; modern → rejects initialize; legacy → handshake loop.

Environment Configuration

# =============================================================================
# NitroStack Python — Stateless MCP Server
# =============================================================================
NITROSTACK_LOG_LEVEL=info

# Transport: stdio | http | dual
MCP_TRANSPORT_TYPE=http
PORT=3000
HOST=127.0.0.1
ENABLE_CORS=true

# Protocol era (case-insensitive; unset = auto)
#   auto / both / dual / dual-spec  → dual-spec sessionless (default)
#   modern / latest / 2026 / 2026-07-28 → pure 2026 stateless
#   legacy / 2025 / 2025-06-18 / 2025-11-25 → sessionful 2025 wire
NITRO_MCP_PROTOCOL_VERSION=auto

# Explicit boolean override (wins over NITRO_MCP_PROTOCOL_VERSION)
# MCP_STATELESS=true  → forces modern
# MCP_STATELESS=false → forces legacy

# OAuth 2.1 (optional — set OAUTH_REQUIRED=true to enforce)
OAUTH_REQUIRED=false
RESOURCE_URI=http://localhost:3000/mcp
TOKEN_AUDIENCE=http://localhost:3000/mcp
AUTH_SERVER_URL=https://your-tenant.auth0.com
JWKS_URI=https://your-tenant.auth0.com/.well-known/jwks.json
TOKEN_ISSUER=https://your-tenant.auth0.com/

# Session limits (legacy era only)
MCP_MAX_SESSIONS=100
MCP_SESSION_TIMEOUT_MS=1800000

Type of Change

  • feat: New feature
  • fix: Bug fix
  • refactor: Internal code change
  • test: Test-only changes
  • docs: Documentation update
  • chore: Build, CI, or tooling changes

Verification & Testing

  • pytest tests/test_mcp20_*.py17 modules, 455 tests passed
  • Stateless HTTP ingress, discover, ping, CORS (test_mcp20_stateless_http.py)
  • Protocol era resolution and blueprint conformance (test_mcp20_blueprint.py, test_mcp20_foundation.py)
  • Task lifecycle, store TTL, authorization (test_mcp20_tasks.py, test_mcp20_task_store.py, test_mcp20_task_authorization.py)
  • OAuth 2.1, CIMD, SSRF defenses (test_mcp20_oauth_cimd.py)
  • JSON-RPC wire, contracts, deprecated methods, tool args (test_mcp20_jsonrpc_wire.py, test_mcp20_contracts.py, test_mcp20_deprecated.py, test_mcp20_tool_args.py)
  • Subscriptions listen, concurrency, stdio era routing (test_mcp20_subscriptions.py, test_mcp20_concurrency.py, test_mcp20_stdio.py)
  • MRTR, extensions, cache hints, observability (test_mcp20_mrtr.py, test_mcp20_extensions_cache_observability.py)
  • Acceptance registry covers all seven protocol areas (test_mcp20_acceptance.py)

Checklist

  • I have kept this PR focused and scoped
  • I have used conventional commits
  • I have added/updated tests where appropriate
  • I have updated docs and starter templates where appropriate
  • I have read and followed CONTRIBUTING.md

Implementation notes

Area Python SDK
Wire engine Official Python mcp 2.x package
Query-string tokens Not implemented — Bearer / envelope auth / JWT only
Default HTTP bind 127.0.0.1
Merge target develop

Bring in PR 17 (Duffel API v2) and PR 18 (agent skills on init/upgrade).
@pallavt-hub pallavt-hub changed the title Feature/stateless 2.0 feat: implement MCP 2026-07-28 stateless protocol, task management, CIMD/OAuth 2.1 hardening, and MRTR support Sep 9, 2026
@manish-wekan
manish-wekan changed the base branch from develop to stateless September 9, 2026 14:11
@manish-wekan
manish-wekan merged commit d2ad3a7 into nitrocloudofficial:stateless Sep 9, 2026
0 of 3 checks passed
@manish-wekan manish-wekan mentioned this pull request Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants