Skip to content

Commit a05dd3d

Browse files
committed
Polish the todos-server example after rebasing onto latest main
Re-verified against the TypeScript reference at its current main: the missing-capability shape is now a -32021 protocol error on both servers on 2026-07-28 connections (README fidelity note updated), the TS entry's protected HTTP wiring is matched by the SDK's streamable HTTP defaults (noted in server.py), and 2026-era watchers are pointed at the client's listen() driver. Also renames the module-level task store so the add_tasks parameter no longer shadows it, folds the brainstorm outcome union into one alias, types the log threshold as LoggingLevel, and makes comments and docstrings ASCII-only.
1 parent 1ec54ed commit a05dd3d

3 files changed

Lines changed: 51 additions & 50 deletions

File tree

examples/servers/todos-server/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pnpm --filter @mcp-examples/cli-client start -- --server http://127.0.0.1:3000/m
4848
| Progress | `work_through_tasks`, `add_tasks` | paced per-task progress notifications via `ctx.report_progress` |
4949
| Logging | every mutating tool, via `log_info` | honours `logging/setLevel` on 2025 connections and the per-request log-level `_meta` opt-in on 2026-07-28 |
5050
| Resources | `todos://board`, `todos://tasks/{id}` | one concrete resource + a URI template; every task also appears in `resources/list` |
51-
| Subscriptions | the board | `resources/subscribe`/`unsubscribe` handlers for 2025-era clients; `subscriptions/listen` streams (over HTTP) for 2026-07-28; every mutation notifies |
51+
| Subscriptions | the board | `resources/subscribe`/`unsubscribe` handlers for 2025-era clients; `subscriptions/listen` streams (over HTTP) for 2026-07-28, consumed client-side with [`client.listen(...)`](https://py.sdk.modelcontextprotocol.io/v2/client/subscriptions/); every mutation notifies |
5252
| list_changed | every mutation | resource list + resource updated notifications on both eras |
5353
| Prompts + completions | `plan-my-day`, `seed-board` | argument completion (project names, themes, task ids) wired to `completion/complete` via `@mcp.completion()` |
5454

@@ -77,6 +77,6 @@ This port is verified against the TypeScript `todos-server` by driving both over
7777
- **`resources/list` composition.** The TypeScript `ResourceTemplate` has a `list` callback; `MCPServer` doesn't, so this example overrides the low-level `resources/list` handler to append one entry per task (the same private-API pattern the everything-server uses for `resources/subscribe` and `logging/setLevel`).
7878
- **`subscriptions/listen` over stdio.** The Python SDK serves 2026-era listen streams on streamable HTTP only; over stdio a listen request is rejected. Board-change notifications over stdio therefore reach 2025-era subscribers only.
7979
- **Legacy HTTP interactivity.** The TypeScript server's per-request HTTP posture refuses push-style sampling/elicitation for 2025-era HTTP clients; the Python server's default Streamable HTTP mode is stateful, so those tools work on that leg here.
80-
- **Legacy HTTP fan-out.** Pre-2026 board-change notifications go to the session that made the mutating call. Over stdio that is every subscriber; with several concurrent 2025-era HTTP sessions, the others don't hear about it (the TypeScript entry broadcasts via its handler notifier). Pre-2026 HTTP handshakes also advertise `listChanged: false` — the SDK exposes no seam to change that on the HTTP path (stdio is patched, see `serve_stdio`).
80+
- **Legacy HTTP fan-out.** Pre-2026 board-change notifications go to the session that made the mutating call. Over stdio that is every subscriber; with several concurrent 2025-era HTTP sessions, the others don't hear about it. The subscription set is also process-wide, so a mutating session that never subscribed is notified once any other session has subscribed (the TypeScript entry scopes subscriptions per session and broadcasts via its handler notifier). Pre-2026 HTTP handshakes also advertise `listChanged: false` — the SDK exposes no seam to change that on the HTTP path (stdio is patched, see `serve_stdio`).
8181
- **Cancellation granularity.** When a client cancels `work_through_tasks`, this SDK interrupts the handler at its next `await` (the in-flight pretend task stays open); the TypeScript server checks between tasks and finishes the in-flight one.
82-
- **Elicited answers are schema-validated.** The resolver framework validates every accepted form answer against the requested schema, so a non-conforming answer (an out-of-range custom count, a value outside the count enum) fails the call with a validation error; the TypeScript handlers parse manually and report those as polite "Nothing added" declines. Similarly, a client that never declared the sampling/elicitation capability gets a structured `-32021` protocol error naming the missing capability, where the TypeScript server returns an `isError` tool result.
82+
- **Elicited answers are schema-validated.** The resolver framework validates every accepted form answer against the requested schema, so a non-conforming answer (an out-of-range custom count, a value outside the count enum) fails the call with a validation error; the TypeScript handlers parse manually and report those as polite "Nothing added" declines. On 2026-07-28 connections, both servers answer a client that never declared the sampling/elicitation capability with a structured `-32021` protocol error naming the missing capability; on pre-2026 connections this server still answers `-32021`, where the TypeScript server reports an `isError` tool result.

examples/servers/todos-server/mcp_todos_server/server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,7 @@ def main(transport: str, port: int | None) -> int:
2929
else:
3030
resolved_port = port if port is not None else int(os.environ.get("PORT", "3000"))
3131
print(f"[todos] listening on http://127.0.0.1:{resolved_port}/mcp", file=sys.stderr)
32+
# The SDK binds 127.0.0.1 and validates Host/Origin against localhost by default,
33+
# matching the TypeScript reference's protected HTTP wiring.
3234
mcp.run(transport="streamable-http", port=resolved_port)
3335
return 0

0 commit comments

Comments
 (0)