Add story-style examples suite (27 stories + harness + CI)
Merged
MA
4mo ago+6801+6801+0123456789012345678901234567890123456789-26-26-01234567890123456789
Adds a story-style examples suite: one self-contained folder per protocol feature, each with server.py (MCPServer), server_lowlevel.py (lowlevel Server), client.py, and README.md. Mirrors the typescript-sdk v2-2026-07-28 examples layout.
Motivation and Context
The existing examples are spread across four parallel layouts and only everything-server runs in CI. We want examples that:
- answer "how do I do X?" with one small, focused folder a user can read top to bottom — the duplication across folders is deliberate
- run as part of the test suite, so any public-API change is immediately visible against real usage
- cover both the 2025-handshake and 2026-stateless eras, with both
MCPServerand lowlevelServervariants wherever the feature allows
What's in this PR
- 27 runnable stories under
examples/stories/: tools, prompts, resources, lifespan, dual_era, streaming, legacy_elicitation, sampling, stickynotes, custom_methods, schema_validators, middleware, parallel_calls, roots, pagination, error_handling, serve_one, stateless_legacy, json_response, legacy_routing, starlette_mount, sse_polling, standalone_get, reconnect, bearer_auth, oauth, oauth_client_credentials — 23 of them with a lowlevelServersibling. - 7 README-only stubs for features not yet runnable here: caching, mrtr, subscriptions, tasks, apps, skills, events.
- Every
client.pyconstructs theClientinline. The body isasync def main(target, *, mode="auto"): async with Client(target, mode=mode) as client: ...— the one line a client example exists to teach is visible in every file, and the harness only decides whattargetis. An AST shape-check (tests/examples/test_story_shape.py) keeps it that way. - Test harness (
tests/examples/):manifest.tomlparametrizes each story across (server variant × transport × era) — 266 legs in ~1.5s, all in-memory or ASGI-bridged, no subprocesses or sleeps. Plus a 3-leg subprocess smoke test (gated onMCP_EXAMPLES_SMOKE=1) that runs the literal documented commands over real stdio/uvicorn. - Self-hosting HTTP runs.
python -m stories.<story>.client --httpwith no URL starts the sibling server on a port it owns, waits for it, runs the scenario, and tears it down — nothing to background, kill, or collide.--http <url>still targets a server you run yourself, and stays the documented form in the stories where hosting is the lesson. - Status markers. The manifest carries
status = current | legacy | deprecated, surfaced in the story index; legacy-mechanism stories (e.g.legacy_elicitation, the handshake-era push that MRTR replaces) and deprecated features (roots,sampling, the logging half ofstreaming) open their READMEs with a banner and a migration note. - Scaffolding:
_harness.py(client-side) and_hosting.py(server-side, isolating the HTTP entry calls still being reshaped so one edit propagates),_shared/auth.py(in-process AS for the auth stories). Singleexamples/pyproject.tomlworkspace member, sofrom stories.tools.server import build_serverworks in scripts,-m, and pytest. - CI: the in-memory matrix runs in the existing test job; the smoke leg runs on one ubuntu cell.
- The existing
examples/clients/,examples/servers/*,examples/mcpserver/, andexamples/snippets/directories are left in place for now.
How Has This Been Tested?
./scripts/test: 100% coverage,strict-no-coverclean (net −6# pragma: no coverinsrc/— the stories exercise previously-uncovered branches)pytest tests/examples/: 261 passed, 2 xfail (a documented progress-drop on the modern HTTP path), 3 skipped (smoke without the env var)MCP_EXAMPLES_SMOKE=1 pytest tests/examples/test_stories_smoke.py: 3 passed over real subprocess stdio + uvicorn- pyright, ruff, markdownlint, pre-commit: clean
Breaking Changes
None.
Types of changes
- Bug fix (non-breaking change which fixes an issue)
- New feature (non-breaking change which adds functionality)
- Breaking change (fix or feature that would cause existing functionality to change)
- Documentation update
Checklist
- I have read the MCP Documentation
- My code follows the repository's style guidelines
- New and existing tests pass locally
- I have added appropriate error handling
- I have added or updated documentation as needed
Additional context
Intentional behavior changes
src/mcp/server/{elicitation,lowlevel/server,mcpserver/server}.py,src/mcp/shared/exceptions.py: removed 6# pragma: no covermarkers on branches the new stories now exercise. No logic change.
Known caveats / follow-ups
mrtr/andsubscriptions/are stubs even though their lowlevel registration surface now exists onmain(#2967); implementing them is a deliberate follow-up to keep this PR reviewable. Same for the deferred extension stories.- Several SDK ergonomic gaps surfaced while writing these (e.g. no
Client(stdio_params)overload, noauth=passthrough onClient(url), no publicMCPServermiddleware accessor, no server-side supported-protocol-versions override). The stories work around them in the harness, never in the user-facing files; tracked separately. - Consolidating the old
examples/{clients,servers,mcpserver}/trees intostories/is a follow-up.