Commit Graph

839 Commits

Author SHA1 Message Date
Max Isbey c54fe3b36a feat(mrtr): linear continuation-based handler — Option H
The Option B footgun was: await elicit() looks like a suspension point but
is actually a re-entry point, so everything above it runs twice. Option H
fixes that by making it a REAL suspension point — the coroutine frame is
held in a ContinuationStore across MRTR rounds, keyed by request_state.

Handler code stays exactly as it was in the SSE era:

    async def my_tool(ctx: LinearCtx, location: str) -> str:
        audit_log(location)      # runs exactly once
        units = await ctx.elicit("Which units?", UnitsSchema)
        return f"{location}: 22°{units.u}"

The wrapper linear_mrtr(my_tool, store=...) translates this into a standard
MRTR on_call_tool handler. Round 1 starts the coroutine; elicit() sends
IncompleteResult back through the wrapper and parks on a stream. Round 2's
retry wakes it with the answer. The coroutine continues from where it
stopped — no re-entry, no double-execution.

Trade-off: server holds the frame in memory between rounds. Client sees
pure MRTR (no SSE, independent requests), but server is stateful within
a single tool call. Horizontally-scaled deployments need sticky routing on
the request_state token. Same operational shape as Option A's SSE hold,
without the long-lived connection.

SDK pieces (src/mcp/server/experimental/mrtr/linear.py):
- LinearCtx with async elicit(message, PydanticSchema) -> instance
- ContinuationStore — owns the task group, TTL-based frame expiry
- linear_mrtr(handler, store=...) — the wrapper
- ElicitDeclined raised when user declines/cancels

7 E2E tests including the key assertion: side-effects above await fire
exactly once (the test measures audit_log count).
2026-03-20 17:06:58 +00:00
Max Isbey 1acd0ce4fc refactor(mrtr): split experimental module into package
mrtr.py → mrtr/
├── __init__.py  — package docstring + re-exports
├── _state.py    — encode_state/decode_state + input_response helper
├── context.py   — MrtrCtx (Option F, ship target)
├── builder.py   — ToolBuilder (Option G, ship target)
└── compat.py    — sse_retry_shim + dispatch_by_version (comparison artifacts)

Ship targets (F/G) now live separately from the dual-path compat shims.
All imports from mcp.server.experimental.mrtr unchanged.
2026-03-20 16:50:01 +00:00
Max Isbey 4facab7115 examples(mrtr): add basic and multi-round lowlevel reference examples
Two standalone reference examples before the comparison deck:

- basic.py: the simple-tool equivalent for MRTR. One IncompleteResult,
  one retry. Comments walk through the two moves every MRTR handler
  makes: check input_responses, return IncompleteResult if missing.
  Runnable end-to-end against the in-memory Client.

- basic_multiround.py: the ADO-rules SEP example translated. Two
  cascading elicitation rounds with request_state carrying accumulated
  context so any server instance can handle any round. Shows the key
  gotcha: input_responses carries only the latest round's answers, not
  accumulated — anything that must survive goes in request_state.
2026-03-20 16:19:18 +00:00
Max Isbey 29cb1ba837 examples(mrtr): handler-shape comparison deck (SEP-2322)
Python-SDK counterpart to typescript-sdk#1701. Seven ways to write the
same weather-lookup tool so the diff between files is the argument.

SDK primitives (src/mcp/server/experimental/mrtr.py):
- MrtrCtx.once() — idempotency guard tracked in request_state (Option F)
- ToolBuilder — structural step decomposition; end_step runs exactly once
  regardless of round count (Option G)
- input_response() — sugar for the guard-first pattern
- sse_retry_shim() — Option A comparison artifact (pragma no-cover until
  LATEST_PROTOCOL_VERSION bumps past the MRTR gate)
- dispatch_by_version() — Option D comparison artifact

Option examples (examples/servers/mrtr-options/):
- E (degrade-only): the SDK default. MRTR-native; pre-MRTR gets a default
  or error. Both quadrant rows collapse here.
- A (SSE shim): SDK emulates retry over SSE. Safe re-entry, hidden loop.
- B (await shim): exception-based. UNSAFE — hidden double-execution above
  await. Not a ship target; for contrast.
- C (version branch): explicit if/else in handler body.
- D (dual handler): two functions, SDK picks by version.
- F (ctx.once): idempotency guard, opt-in per side-effect.
- G (ToolBuilder): no above-the-guard zone; end_step structurally
  unreachable until all elicitations complete.

The invariant test (tests/experimental/test_mrtr.py) parametrises E/F/G
against the same Client + callback to prove identical wire behaviour —
the server's internal choice doesn't leak. The footgun test measures
audit_log count to prove F and G actually hold the guard (naive handler
fires twice; F and G fire once).

Both F and G depend on request_state integrity. The demos use plain
base64-JSON; a production SDK MUST HMAC-sign the blob.
2026-03-20 16:09:46 +00:00
Max Isbey 25fb05f416 feat(mrtr): add IncompleteResult types and client retry loop (SEP-2322)
Lowlevel plumbing for Multi Round-Trip Requests:

Types:
- IncompleteResult with result_type discriminator, input_requests, request_state
- InputRequest/InputResponse unions (elicitation, sampling, roots)
- input_responses + request_state fields on RequestParams

Server (lowlevel):
- on_call_tool return widened to include IncompleteResult

Session:
- send_request accepts TypeAdapter (overload) for union result parsing
- call_tool_mrtr() returns CallToolResult | IncompleteResult
- call_tool() stays narrow, raises on IncompleteResult with migration hint

Client:
- call_tool() drives MRTR retry loop internally — dispatches embedded
  input requests to elicitation/sampling/list_roots callbacks, retries
  with collected responses + echoed request_state
- max_mrtr_rounds bound (default 8)

The client-side delta from today's code is zero: elicitation_callback is the
same function whether it fires from SSE push or MRTR retry.
2026-03-20 15:54:11 +00:00
Max Isbey 92c693bb73 fix: cancel in-flight handlers when transport closes in server.run() (#2306) 2026-03-20 13:37:32 +00:00
Max Isbey 883d893097 test: rewrite cli.claude config tests to assert JSON output directly (#2311)
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Felix Weinberger <felixweinberger@users.noreply.github.com>
2026-03-19 15:16:34 +00:00
Jonathan Hefner 5388bea53a docs: generate hierarchical per-module API reference pages (#2103) 2026-03-18 18:15:17 +00:00
Max Isbey 20dd94632e feat(client): store InitializeResult as initialize_result (#2300) 2026-03-18 17:31:26 +00:00
Max Isbey 67201a9bbd test: fix WS test port race; narrow to single smoke test covering both transport ends (#2267) 2026-03-18 15:48:30 +00:00
Max Isbey 7826ade12b test: convert test_integration.py to in-memory transport (fix flaky) (#2277) 2026-03-18 15:25:11 +00:00
Max Isbey ff50351f9e ci: run strict-no-cover in scripts/test to catch stale pragmas locally (#2305) 2026-03-17 19:53:39 +00:00
Max Isbey 1a2244f402 fix: handle non-UTF-8 bytes in stdio server stdin (#2302) 2026-03-17 18:40:39 +00:00
Max Isbey 75a80b6f07 refactor: connect-first stream lifecycle for sse and streamable_http (#2292)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-03-16 23:30:20 +00:00
Max Isbey abfb482246 refactor(examples): migrate all HTTP examples to streamable_http_app() (#2291) 2026-03-16 11:37:01 +00:00
Max Isbey e1fd62e0f3 fix: close all memory stream ends in client transport cleanup (#2266) 2026-03-13 14:43:54 +00:00
dependabot[bot] 2c73a2a881 chore(deps): bump black from 25.1.0 to 26.3.1 in the uv group across 1 directory (#2290)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-13 10:39:56 +00:00
Max Isbey dd52713517 Rewrite TestChildProcessCleanup with socket-based deterministic liveness probe (#2265) 2026-03-12 12:52:32 +00:00
Max Isbey 62eb08e5b2 fix: don't send log notification on transport error (#2257) 2026-03-09 17:47:27 +00:00
Max Isbey 31a38b5078 fix: correct Context type parameters across examples and tests (#2256) 2026-03-09 16:52:56 +00:00
Shivam Aggarwal 51c53f2c18 fix: accept wildcard media types in Accept header per RFC 7231 (#2152)
Co-authored-by: Shivam <shivam@Shivams-MacBook-Air-2.local>
2026-03-09 16:30:02 +00:00
Max Isbey 7ba41dcfae fix: make local coverage runs reliable (#2236) 2026-03-06 17:24:18 +00:00
Ramesh Reddy Adutla eaf971cf25 Add warning log when rejecting request with unknown/expired session ID (#2212)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-03-06 16:55:37 +00:00
Max Isbey 92f1b1500d fix: remove MIME type validation from MCPServer Resource (#2235) 2026-03-06 14:50:58 +00:00
Giulio Leone b33c811675 perf: use deque for InMemoryTaskMessageQueue FIFO operations (#2165) 2026-03-05 15:44:33 +00:00
Giulio Leone 7c0224828b fix(oauth): include client_id in token request body for client_secret_post (#2185)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-05 14:57:33 +00:00
Max Isbey 528abfab86 tests: remove lax-no-cover pragmas by moving assertions before cancellation (#2206) 2026-03-04 16:11:34 +00:00
Varun6578 b3149d2f33 fix: clean up SSE session on client disconnect (#2200)
Co-authored-by: Varun Sharma <sharmava@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-03-04 14:45:11 +00:00
Max Isbey cc22bf5464 refactor: remove request_ctx ContextVar, thread Context explicitly (#2203)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-03-04 13:23:02 +00:00
Max Isbey 62575edabd ci: sign weekly lockfile commits as github-actions[bot] (#2148) 2026-02-26 15:36:46 +00:00
Jonathan Hefner 0fe16dd5fd fix: silence mkdocs social plugin warnings in strict mode (#2109) 2026-02-19 23:12:51 +01:00
Jonathan Hefner cb07adeca3 docs: add code fences to Example: docstring blocks (#2104) 2026-02-19 21:06:11 +01:00
Jonathan Hefner c0328540c9 docs: fix docstrings across public API surface (#2095) 2026-02-19 06:45:59 +01:00
Den Delimarsky 688c6e3ade Update SECURITY.md to use GitHub Security Advisories (#2092) 2026-02-18 21:19:25 -08:00
Max Isbey 43d709c976 ci: pin all GitHub Actions to commit SHAs (#2088) 2026-02-18 19:42:00 +00:00
Max Isbey 0e96aecd1d fix: use exact match for loopback hosts in issuer URL validation (#2089) 2026-02-18 19:40:52 +00:00
Max Isbey b9431d483f fix: prevent command injection in example URL opening (#2082) 2026-02-18 15:16:44 +00:00
Max Isbey e82203bfc4 refactor: remove unused mcp.shared.progress module (#2080) 2026-02-18 13:10:02 +00:00
Akshan Krithick fc57c2c4c5 test: fix progress notification assertions for related_request_id (#2038)
Co-authored-by: Lee Hubbard <hubbard.zlee@unknowncyber.com>
Co-authored-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
2026-02-18 11:53:03 +00:00
Felix Weinberger 92140e5086 Add idle session timeout to StreamableHTTPSessionManager (#2022) 2026-02-18 10:47:02 +00:00
Felix Weinberger be5bb7c4f2 fix: normalize trailing slashes before length check in check_resource_allowed (#2074) 2026-02-17 14:34:59 +00:00
Max Isbey 705497a593 fix: allow null id in JSONRPCError per JSON-RPC 2.0 spec (#2056) 2026-02-17 10:30:34 +00:00
BabyChrist666 3b53fb9a00 fix: add HTTP readiness check to wait_for_server and remove dead code in SSE tests (#2073) 2026-02-17 08:34:47 +01:00
Marcelo Trylesinski 2fe56e56de fix: handle HTTP error status codes in streamable HTTP client (#2047) 2026-02-14 09:49:42 +01:00
Max Isbey 8f669a77e3 fix: explicitly load required pytest plugins in addopts (#2055) 2026-02-13 18:25:10 +00:00
Marcelo Trylesinski a287a40184 docs: add coverage verification instruction to CLAUDE.md (#2050) 2026-02-13 13:10:00 +00:00
Max Isbey 29a14ab9e5 fix: skip readme-v1-frozen in CI and add diff-based README.md check (#2048) 2026-02-13 10:30:30 +00:00
Max Isbey 1e0b5c0479 fix: revert README.md to v1 documentation (#2045)
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
2026-02-12 16:22:07 +00:00
Max Isbey 0a22a9dc33 refactor: replace lowlevel Server decorators with on_* constructor kwargs (#1985) 2026-02-12 15:55:54 +00:00
dependabot[bot] d6d3ad9a7c chore(deps-dev): bump pillow from 12.1.0 to 12.1.1 in the uv group across 1 directory (#2036)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-11 14:57:37 +00:00