- server.py: `mcp.server.mcpserver.MCPServer`; StrictArgsFastMCP becomes
StrictArgsMCPServer, whose call_tool takes and forwards v2's `context`
and raises ToolError (the SDK logs anything else as an unexpected crash;
the message reaches the caller either way).
- stateless_http and transport_security moved from the constructor to
`streamable_http_app(...)` in mount_mcp, with their reasons.
- Per-request user identity is unchanged: the contextvar set around the
ASGI call reaches the handler on both v2 paths (legacy stateless spawns
from the request task; the 2026-07-28 modern path opens its task group
inside the request).
- pyproject: mcp[cli]>=2.2, no ceiling (installs are --locked). uv.lock
regenerated with --upgrade-package mcp in the ci-python image: only mcp
and its own dependencies moved (106 → 110 packages).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FastMCP validates tool arguments with a pydantic model whose extra-field
policy is 'ignore', so create_note(content=...) — a plausible near-miss
for body=, primed by add_task_log's content — ran successfully, stored
body: '', and left a record embedding/search cannot see. Two real notes
were persisted body-less before the pattern was noticed; create_task
only 'worked' because those calls happened to use the right name.
StrictArgsFastMCP rejects any tool call carrying arguments the tool does
not declare, before dispatch, with a did-you-mean hint when one is close
and the declared list when none is. Applied at the dispatch seam so
every tool gets the guarantee — an error the caller sees once beats data
half-written forever.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>