Start here. Register tools with @mcp.tool(); the SDK infers the JSON
input schema from type hints, the output schema from the return annotation, and
returns structuredContent alongside text. ToolAnnotations carries
behavioural hints (readOnlyHint, idempotentHint) the host can show to
users. The client lists tools, inspects schemas + annotations, calls both, and
asserts structured output.
# stdio (default — the client spawns the server as a subprocess)
uv run python -m stories.tools.client
# HTTP — the client self-hosts the server on a free port, runs, then tears it down
uv run python -m stories.tools.client --http
# same, against the lowlevel-API server variant
uv run python -m stories.tools.client --http --server server_lowlevelserver.pycalc—Literal[...]andBaseModelin the signature become the tool'sinputSchema/outputSchemawith zero hand-written JSON.server.pyecho—structured_output=Falseopts out of schema inference for a plain text-only tool.server_lowlevel.py— the same wire contract built by hand: this is whatMCPServergenerates for you.
schema_validators/ (every input-schema source: pydantic / TypedDict /
dataclass / dict), error_handling/ (is_error vs protocol error),
streaming/ (progress mid-call).