Title: Add the todos-server reference example by maxisbey · Pull Request #3048 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: Add the todos-server reference example by maxisbey · Pull Request #3048 · modelcontextprotocol/python-sdk
X Title: Add the todos-server reference example by maxisbey · Pull Request #3048 · modelcontextprotocol/python-sdk
Description: Adds examples/servers/todos-server/ — a Python port of the TypeScript SDK's reference server, examples/todos-server: a small project todo board where every server-side MCP feature has a real job, built on the high-level MCPServer. Motivation and Context The TypeScript SDK ships a reference host/server pair (cli-client + todos-server) that exercises tools, resources, prompts, sampling, elicitation, multi-round input_required flows, progress, logging, and subscriptions in one small, readable app, serving both protocol revisions (2026-07-28 and 2025-11-25) over stdio and Streamable HTTP. This PR brings the server half to the Python SDK so the two SDKs have a directly comparable reference workload — the TS cli-client connects to this server out of the box over HTTP. Notable porting decisions (details in the example README's "Fidelity" section): The three interactive tools (clear_done, prioritize, brainstorm_tasks) are written once as resolver dependencies (Resolve + Elicit/Sample, the latter from #3049): the framework carries the questions as InputRequiredResult rounds on 2026-07-28 connections and as push-style requests on pre-2026 ones, so no handler branches on the era and the example does no round bookkeeping. brainstorm_tasks shows the multi-round shape as a resolver chain — count/theme form → conditional custom-amount form → sampling round derived from the recorded answers. resources/subscribe/unsubscribe, logging/setLevel, and a dynamic resources/list (one entry per task, like the TS ResourceTemplate list callback) are registered on the low-level server via mcp._lowlevel_server.add_request_handler — the same pattern (and TODO(felix) gap) as the everything-server. Logging honours logging/setLevel on 2025 sessions and the per-request io.modelcontextprotocol/logLevel _meta opt-in on 2026-07-28 sessions, matching the TS server's semantics. The multi-round state is minted by the resolver framework and sealed by the server; REQUEST_STATE_SECRET maps to RequestStateSecurity(keys=[...]) like the TS example's HMAC env key. How Has This Been Tested? Drove this server and the TS reference server through an identical scripted scenario (~38 steps: all eight tools including the three-round brainstorm flow and its decline/cancel branches, resources, templates, prompts, completions, progress, logging thresholds, subscriptions) with the same client and scripted elicitation/sampling callbacks: stdio × both eras: every tool result text, structured output, elicitation form, sampling request, progress sequence, and log line matches the TS server — the resolver-generated elicitation form schemas are byte-identical to the TS literals. After normalizing pydantic-vs-zod JSON Schema cosmetics on tool schemas, the only remaining differences are SDK-level (Python includes the prompt description in prompts/get results and advertises experimental: {} on legacy initialize). Streamable HTTP × both eras: modern-era results identical, including subscriptions/listen streams (ack + per-mutation board updates on both servers). On the legacy HTTP leg the Python server's default stateful sessions serve push-style elicitation/sampling, where the TS server's stateless posture refuses (its documented caveat). The TS entry has since moved to its protected HTTP wiring (createMcpHonoApp, loopback bind + localhost Host/Origin validation); the Python entry gets the equivalent from the SDK's streamable-HTTP defaults, noted in server.py. Adversarial probes: schema-violating arguments and whitespace-only sampling replies match the TS semantics; out-of-range elicited answers fail the resolver framework's schema validation. A client without the sampling/elicitation capability now gets a structured -32021 error from both servers on 2026-07-28 connections (the TS SDK's capability gate); on pre-2026 connections the TS server reports an isError tool result where this server still answers -32021 — README fidelity section updated accordingly. Cancelling work_through_tasks mid-flight stops the loop on both eras (after #3046); the README documents the remaining granularity difference vs TS. uv run --frozen ruff format --check, ruff check, pyright (strict, root config), and the full pre-commit suite pass. Breaking Changes None — example only; no src/ changes. 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 Two SDK gaps surfaced while verifying parity, documented in the example README rather than worked around: subscriptions/listen is not served over stdio (2026-era stdio clients get METHOD_NOT_FOUND, so board-change notifications over stdio reach 2025-era subscribers only), and there is no public seam to advertise listChanged capabilities to pre-2026 HTTP clients (serve_stdio patches the stdio path via the low-level server). A third gap noted in the first revision — no courtesy notifications/cancelled from the client on 2026-era transports — was fixed by #3046. Re-checked against current main after the latest rebase: both remaining gaps still stand, and nothing that landed since (the client-side Client.listen() driver from #3047, the httpx2 swap, Streamable HTTP body limits) changes the example's server-side surface. The README now points 2026-era watchers at client.listen(...) and its docs page. AI Disclaimer
Open Graph Description: Adds examples/servers/todos-server/ — a Python port of the TypeScript SDK's reference server, examples/todos-server: a small project todo board where every server-side MCP feature has a real jo...
X Description: Adds examples/servers/todos-server/ — a Python port of the TypeScript SDK's reference server, examples/todos-server: a small project todo board where every server-side MCP feature has a rea...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/pull/3048
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:e18cf272-bc2f-2520-28bf-0a41bb622ba0 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | 82E6:1E1ACC:231A466:2EFDB1B:6A5BDB74 |
| html-safe-nonce | dd648cafbb7e788e5b6171d98ba2d5bbadcadc683d82fa7fdea50e2d2f5bbf2d |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI4MkU2OjFFMUFDQzoyMzFBNDY2OjJFRkRCMUI6NkE1QkRCNzQiLCJ2aXNpdG9yX2lkIjoiMjQzNDM2ODkxNTE5MzQ1MzQyOCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 88a0804f9b1e6090b15d97fd9a6220831b6ed40b90bed7b7985ca8ca765bef6b |
| hovercard-subject-tag | pull_request:3973307418 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/modelcontextprotocol/python-sdk/pull/3048/files |
| twitter:image | https://avatars.githubusercontent.com/u/224885523?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/224885523?s=400&v=4 |
| og:image:alt | Adds examples/servers/todos-server/ — a Python port of the TypeScript SDK's reference server, examples/todos-server: a small project todo board where every server-side MCP feature has a real jo... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5290d7e14309ad1e76106a9c4237bd1041517e83ea182c8ab756752cb0c6940b |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c975978430e9ad293956f2bbdaf153b1bd84a99 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width