Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/advanced/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ The same file's `main()` is the whole client story, both halves of it:
The one interceptive hook. Override `intercept_tool_call` to observe, short-circuit,
or veto a tool call:

```python title="server.py" hl_lines="18-25"
```python title="server.py" hl_lines="17-24"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 Pre-existing (not introduced by this PR): docs/advanced/apps.md:23 has hl_lines="18 21 29 31", but all four numbers point at blank lines in docs_src/apps/tutorial001.py — the four "moves" the page's prose enumerates sit exactly one line below each. Since this PR's hl_lines sweep fixed every other page in this class (apps.md was skipped only because its tutorial had no mcp_types import to reorder), it's a natural one-line rider: change the spec to hl_lines="19 22 30 32".

Extended reasoning...

What the bug is. docs/advanced/apps.md line 23 reads:

```python title="server.py" hl_lines="18 21 29 31"
--8<-- "docs_src/apps/tutorial001.py"

All four highlighted line numbers land on blank lines in docs_src/apps/tutorial001.py. Verified against the working tree with cat -n:

Highlighted (blank) Intended (one line below)
18 19: apps = Apps()
21 22: @apps.tool(resource_uri="ui://clock/app.html", ...)
29 30: apps.add_html_resource("ui://clock/app.html", CLOCK_HTML, title="Clock")
31 32: mcp = MCPServer("clock", extensions=[apps])

The intended targets are unambiguous: they are exactly the four "moves" the page's own prose enumerates immediately below the code block (Apps(), @apps.tool(resource_uri=...), add_html_resource, extensions=[apps]). The spec is uniformly off by one low; the fix is hl_lines="19 22 30 32".

How it manifests. mkdocs-material renders hl_lines as highlighted rows in the included snippet. As shipped, the page highlights four empty rows and none of the four lines the surrounding prose tells the reader to look at — a visible rendering defect on the published Apps page, though nothing functional breaks.

Why this PR didn't fix it (and why it's pre-existing). This PR reorders mcp_typesmcp.types imports across ~145 docs/example files and mechanically remaps every hl_lines spec that the import reshuffling shifted. docs_src/apps/tutorial001.py imports nothing from mcp_types, so the sweep never touched it — neither docs/advanced/apps.md nor its tutorial appears in this PR's diff. The off-by-one predates the PR (introduced in commit 48ef569). It's surfaced here only because it is the one remaining hl_lines defect in the docs tree after this PR's sweep: every remap the PR did make was verified content-identical (old spec on old source vs. new spec on new source highlight the same lines, the only difference being the two swapped-but-still-highlighted import lines in the handling_errors tutorial).

Step-by-step proof. (1) Open docs/advanced/apps.md:23 — the fence carries hl_lines="18 21 29 31". (2) Run cat -n docs_src/apps/tutorial001.py: lines 18, 21, 29, and 31 are empty; lines 19, 22, 30, and 32 hold apps = Apps(), the @apps.tool(resource_uri=...) decorator, apps.add_html_resource(...), and mcp = MCPServer("clock", extensions=[apps]) respectively. (3) Read the prose under the block — it walks through exactly those four constructs as the page's "four moves". (4) Render the page: four blank highlighted rows, zero of the four moves highlighted.

Fix. One-line change in docs/advanced/apps.md:23: hl_lines="18 21 29 31"hl_lines="19 22 30 32". It fits naturally into this PR (whose third commit is precisely an hl_lines-remap sweep), or as a trivial follow-up.

Severity. Pre-existing and cosmetic — a docs rendering issue in a file this PR does not touch. It must not block merge; it's noted here only because this PR fixed every other instance of the same defect class. (Anchored to the nearest in-diff hl_lines hunk in docs/advanced/extensions.md because the actual defect's file is not in the diff.)

--8<-- "docs_src/extensions/tutorial005.py"
```

Expand Down Expand Up @@ -158,7 +158,7 @@ A **client extension** is the same contract from the consuming side: a bundle of
client-side behaviour behind one identifier. Pass instances to
`Client(extensions=[...])` and call tools normally:

```python title="client.py" hl_lines="67-69"
```python title="client.py" hl_lines="66-68"
--8<-- "docs_src/extensions/tutorial006.py"
```

Expand Down Expand Up @@ -188,7 +188,7 @@ client = Client(mcp, extensions=[advertise("com.example/search")])
Subclass `ClientExtension` and override only what you need. Three contribution
kinds, each with a default: `settings()`, `claims()`, and `notifications()`.

```python title="client.py" hl_lines="18-19 44-45 47-48"
```python title="client.py" hl_lines="17-18 43-44 46-47"
--8<-- "docs_src/extensions/tutorial006.py"
```

Expand Down Expand Up @@ -226,12 +226,12 @@ claimed shape reaching a session-tier caller raises `UnexpectedClaimedResult`.
### Extension verbs

An extension's own request methods need no client-side registration. A vendor request
type subclasses `mcp_types.Request` and goes through `client.session.send_request`,
type subclasses `mcp.types.Request` and goes through `client.session.send_request`,
as in [Serving your own methods](#serving-your-own-methods). One addition: when a
params key must ride the `Mcp-Name` header (extension specs such as tasks require
this for their verbs), the request type declares `name_param`:

```python title="client.py" hl_lines="23-26 47-48"
```python title="client.py" hl_lines="22-25 46-47"
--8<-- "docs_src/extensions/tutorial007.py"
```

Expand Down
10 changes: 5 additions & 5 deletions docs/advanced/low-level-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ For everything else, stay on `MCPServer`.

This is the `search_books` tool that **[Tools](../servers/tools.md)** writes in nine lines of `@mcp.tool()`, with the sugar removed:

```python title="server.py" hl_lines="23 27 33"
```python title="server.py" hl_lines="22 26 32"
--8<-- "docs_src/lowlevel/tutorial001.py"
```

Expand Down Expand Up @@ -80,7 +80,7 @@ That generalises. An exception raised from a low-level handler is **always** a p

`on_call_tool` is the single entry point for every tool on the server. You route on `params.name`:

```python title="server.py" hl_lines="39-44"
```python title="server.py" hl_lines="38-43"
--8<-- "docs_src/lowlevel/tutorial002.py"
```

Expand All @@ -91,7 +91,7 @@ That generalises. An exception raised from a low-level handler is **always** a p

Declare `output_schema` on the `Tool` and put `structured_content` on the result. Both are yours:

```python title="server.py" hl_lines="20-24 37"
```python title="server.py" hl_lines="19-23 36"
--8<-- "docs_src/lowlevel/tutorial003.py"
```

Expand All @@ -117,7 +117,7 @@ The server never compares the two fields. This SDK's `Client` does: return `stru

Use it for record IDs, trace IDs, anything your UI needs and your prompt doesn't:

```python title="server.py" hl_lines="38"
```python title="server.py" hl_lines="37"
--8<-- "docs_src/lowlevel/tutorial004.py"
```

Expand All @@ -144,7 +144,7 @@ No `resources`, no `prompts`: there is nothing to back them. Pass `on_list_promp

`Server` is generic in the type its lifespan yields. Annotate it once and the object is typed everywhere it surfaces:

```python title="server.py" hl_lines="25-27 45-46 51"
```python title="server.py" hl_lines="24-26 44-45 50"
--8<-- "docs_src/lowlevel/tutorial005.py"
```

Expand Down
2 changes: 1 addition & 1 deletion docs/advanced/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ If `Server(name, on_call_tool=...)` is new to you, read **[The low-level Server]

One server, one tool, one middleware that logs how long each message took:

```python title="server.py" hl_lines="40-46 50"
```python title="server.py" hl_lines="39-45 49"
--8<-- "docs_src/middleware/tutorial001.py"
```

Expand Down
4 changes: 2 additions & 2 deletions docs/advanced/pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Pagination is for the server whose resource list is really a database: thousands

## A server that pages

```python title="server.py" hl_lines="13 16-17"
```python title="server.py" hl_lines="12 15-16"
--8<-- "docs_src/pagination/tutorial001.py"
```

Expand Down Expand Up @@ -38,7 +38,7 @@ The tenth page comes back with `next_cursor` set to `None`. Done.

Every `list_*` method on `Client` (`list_tools`, `list_resources`, `list_resource_templates`, `list_prompts`) takes a `cursor=` keyword. Draining a paged list is one `while True`:

```python title="client.py" hl_lines="27-33"
```python title="client.py" hl_lines="26-32"
--8<-- "docs_src/pagination/tutorial002.py"
```

Expand Down
4 changes: 2 additions & 2 deletions docs/client/caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Out of the box every result says `ttlMs: 0, cacheScope: "private"`: immediately

On the low-level `Server`, handlers build their results by hand, and `ttl_ms` / `cache_scope` are just fields on the result models. A handler that sets them explicitly always wins over the constructor map, field by field:

```python title="server.py" hl_lines="11 17"
```python title="server.py" hl_lines="10 16"
--8<-- "docs_src/caching/tutorial002.py"
```

Expand All @@ -39,7 +39,7 @@ One caveat on paginated lists: the protocol requires the **same `cacheScope` on

On a 2026-07-28 session, `Client` honors the hints for you: it has a built-in response cache, on by default. A result that arrives carrying a `ttlMs` is stored, and an identical call within that TTL is served from the cache with no round trip. A result that carries *no* hint is not cached: hint-less results get `CacheConfig.default_ttl_ms`, which defaults to `0` (immediately stale), so a server that declares nothing sees exactly the call-for-call traffic it always did.

```python title="client.py" hl_lines="34 36 39"
```python title="client.py" hl_lines="33 35 38"
--8<-- "docs_src/caching/tutorial003.py"
```

Expand Down
4 changes: 2 additions & 2 deletions docs/client/callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ That is the server half, and the **[Elicitation](../handlers/elicitation.md)** p

## The elicitation callback

```python title="client.py" hl_lines="7-11 17-18"
```python title="client.py" hl_lines="6-10 16-17"
--8<-- "docs_src/client_callbacks/tutorial002.py"
```

Expand Down Expand Up @@ -55,7 +55,7 @@ result.content # [TextContent(type='text', text='Card issued to Ada Lovelace.')
One `tools/call` from you, one `elicitation/create` back from the server, answered by your function, all inside a single tool call.

!!! info
`mode="legacy"` on line 17 is doing real work. By default `Client(...)` negotiates the modern
`mode="legacy"` on the `Client(...)` call is doing real work. By default `Client(...)` negotiates the modern
protocol path, and that path has no back-channel for server-to-client requests: `ctx.elicit`
fails before your callback ever runs. The transport doesn't decide that; the negotiated
protocol does, in-memory and over a URL alike. Pin `mode="legacy"` whenever your client has
Expand Down
6 changes: 3 additions & 3 deletions docs/client/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ A tool that raises does **not** raise in your client. It comes back as an ordina

The resource verbs come in pairs: two ways to list, one way to read.

```python title="client.py" hl_lines="23-32"
```python title="client.py" hl_lines="22-31"
--8<-- "docs_src/client/tutorial004.py"
```

Expand Down Expand Up @@ -174,7 +174,7 @@ A host hands those messages straight to the model. That is the whole feature.

A server with a completion handler can autocomplete prompt and resource-template arguments as the user types.

```python title="client.py" hl_lines="28-32"
```python title="client.py" hl_lines="27-31"
--8<-- "docs_src/client/tutorial006.py"
```

Expand All @@ -187,7 +187,7 @@ The answer is in `result.completion.values`. Type `"p"` and the server comes bac

Every `list_*` method takes a `cursor=` keyword and every result carries a `next_cursor`. When `next_cursor` is `None`, you have everything.

```python title="client.py" hl_lines="23-31"
```python title="client.py" hl_lines="22-30"
--8<-- "docs_src/client/tutorial007.py"
```

Expand Down
2 changes: 1 addition & 1 deletion docs/client/session-groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Create a `ClientSessionGroup` and call **`connect_to_server`** once per server:

You fix this at the group, not at the servers. Pass a function of `(name, server_info)` and the group runs it on every name it registers:

```python title="client.py" hl_lines="8-9 16"
```python title="client.py" hl_lines="7-8 15"
--8<-- "docs_src/session_groups/tutorial004.py"
```

Expand Down
2 changes: 1 addition & 1 deletion docs/client/subscriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This page is the client end: opening the stream, watching it beside your main fl

A subscription is one context manager. Entering it sends the request, with your keyword arguments as the subscription filter, and waits for the server's acknowledgment, so the stream is live by the time the block starts.

```python title="client.py" hl_lines="16 19 29"
```python title="client.py" hl_lines="15 18 28"
--8<-- "docs_src/subscriptions/tutorial003.py"
```

Expand Down
4 changes: 2 additions & 2 deletions docs/get-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ These docs describe **v2**, currently a release candidate, so the version pin is

You don't need to know any of this to use the SDK, but if you're wondering what each dependency is for:

* `mcp-types`: every protocol type (requests, results, content blocks) as its own package, versioned in lockstep with the SDK. Every `from mcp_types import ...` in these docs is this package.
* `mcp-types`: every protocol type (requests, results, content blocks) as its own package, versioned in lockstep with the SDK. Code that depends on `mcp` imports it through the `mcp.types` alias (every `from mcp.types import ...` in these docs); import `mcp_types` directly only in a project that installs `mcp-types` without the SDK.
* [`anyio`](https://anyio.readthedocs.io/): the async runtime. The whole SDK is written against anyio, so it runs on either `asyncio` or `trio`.
* [`pydantic`](https://docs.pydantic.dev/): what every `mcp_types` model is built on, plus all schema generation and validation.
* [`pydantic`](https://docs.pydantic.dev/): what every `mcp.types` model is built on, plus all schema generation and validation.
* [`httpx2`](https://pypi.org/project/httpx2/): the HTTP client behind the Streamable HTTP and SSE *client* transports, with server-sent events support built in.
* [`starlette`](https://www.starlette.io/), [`uvicorn`](https://www.uvicorn.org/), [`sse-starlette`](https://pypi.org/project/sse-starlette/), and [`python-multipart`](https://pypi.org/project/python-multipart/): the HTTP *server* transports.
* [`jsonschema`](https://pypi.org/project/jsonschema/): validates a tool's structured output against its declared output schema.
Expand Down
2 changes: 1 addition & 1 deletion docs/get-started/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Now the test:
import pytest
from inline_snapshot import snapshot
from mcp import Client
from mcp_types import CallToolResult, TextContent
from mcp.types import CallToolResult, TextContent

from server import mcp

Expand Down
2 changes: 1 addition & 1 deletion docs/handlers/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ That's the right default for a precondition: no answer, no order. When declining

Elicitation is one of the three questions a resolver can ask, and the multi-round-trip flow allows no others. The other two go to the **client** rather than the user: return `Sample(...)` to run an LLM call through the client (a `sampling/createMessage` request), or `ListRoots()` to fetch the client's current roots. Neither has an accept/decline outcome; the consumer annotates the result type directly, `CreateMessageResult` (`CreateMessageResultWithTools` when the request carries `tools` or `tool_choice`) or `ListRootsResult`:

```python title="server.py" hl_lines="11-16 22"
```python title="server.py" hl_lines="10-15 21"
--8<-- "docs_src/dependencies/tutorial004.py"
```

Expand Down
2 changes: 1 addition & 1 deletion docs/handlers/elicitation.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Look at the second tool. When your server learns the out-of-band flow finished (

Servers ask. Clients answer by passing an **`elicitation_callback`** to `Client(...)`:

```python title="client.py" hl_lines="7-8 19"
```python title="client.py" hl_lines="6-7 18"
--8<-- "docs_src/elicitation/tutorial003.py"
```

Expand Down
8 changes: 4 additions & 4 deletions docs/handlers/multi-round-trip.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ That's the whole protocol. Every leg is an ordinary request from the client to t

On `@mcp.tool()` you rarely build this by hand: declare a dependency that asks the user (`Elicit`), samples the client's LLM (`Sample`), or lists its roots (`ListRoots`) and the SDK returns the `InputRequiredResult` for you; that form is the **[Dependencies](dependencies.md)** page. The two forms don't mix: a call has one `input_responses`/`request_state` channel, so a tool that uses `Resolve(...)` parameters cannot also return `InputRequiredResult` from its body. A declared `InputRequiredResult` return is rejected at registration (`InvalidSignature`), and an undeclared one fails the call at runtime. The manual form is the **low-level** `Server`, whose `on_call_tool` handler is allowed to return either result type:

```python title="server.py" hl_lines="44-47"
```python title="server.py" hl_lines="43-46"
--8<-- "docs_src/mrtr/tutorial001.py"
```

Expand All @@ -35,7 +35,7 @@ Everything else in that file (the explicit `input_schema`, the hand-built `CallT

`tools/call` is not special: at 2026-07-28 a server may answer `prompts/get` and `resources/read` the same way. On `MCPServer`, an `@mcp.prompt()` function — or an `@mcp.resource()` **template** function — returns the `InputRequiredResult` itself and reads the retry's answers off the context:

```python title="server.py" hl_lines="21 23 25"
```python title="server.py" hl_lines="20 22 24"
--8<-- "docs_src/mrtr/tutorial004.py"
```

Expand All @@ -51,7 +51,7 @@ Everything else in that file (the explicit `input_schema`, the hand-built `CallT

Register the callbacks the server might ask for (`elicitation_callback`, `sampling_callback`, `list_roots_callback`) and call the tool. When an `InputRequiredResult` arrives, `Client` dispatches each entry in `input_requests` to the matching callback, retries with the answers and the echoed `request_state`, and keeps going until a `CallToolResult` comes back:

```python title="client.py" hl_lines="12 13"
```python title="client.py" hl_lines="11 12"
--8<-- "docs_src/mrtr/tutorial003.py"
```

Expand All @@ -76,7 +76,7 @@ The auto-loop is enough for a single-process client. Own the loop instead when:

Drop to the underlying session, where `allow_input_required=True` hands you the union directly:

```python title="client.py" hl_lines="13 14 20"
```python title="client.py" hl_lines="12 13 19"
--8<-- "docs_src/mrtr/tutorial002.py"
```

Expand Down
4 changes: 2 additions & 2 deletions docs/handlers/sampling-and-roots.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Both still work, on every protocol version the SDK speaks. But read the warning

A resolver returns `Sample(...)` and the tool receives the completion, through the same dependency mechanism that runs `Elicit` in **[Dependencies](dependencies.md)**:

```python title="server.py" hl_lines="11-16 20"
```python title="server.py" hl_lines="10-15 19"
--8<-- "docs_src/sampling_and_roots/tutorial001.py"
```

Expand All @@ -24,7 +24,7 @@ A resolver returns `Sample(...)` and the tool receives the completion, through t

Roots are the folders the client says the server may operate on. They are informational guidance, not an access-control mechanism. A resolver returns `ListRoots()`:

```python title="server.py" hl_lines="11-12 16"
```python title="server.py" hl_lines="10-11 15"
--8<-- "docs_src/sampling_and_roots/tutorial002.py"
```

Expand Down
4 changes: 2 additions & 2 deletions docs/handlers/subscriptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Two things the stream is *not*:

Here is a client on the other side of that stream, following the board:

```python title="client.py" hl_lines="16"
```python title="client.py" hl_lines="15"
--8<-- "docs_src/subscriptions/tutorial003.py"
```

Expand Down Expand Up @@ -119,7 +119,7 @@ async def tools_reloaded() -> None:

Down on the low-level `Server` there is no pre-wired anything, and the same parts assemble in three lines:

```python title="server.py" hl_lines="9-10 48"
```python title="server.py" hl_lines="8-9 47"
--8<-- "docs_src/subscriptions/tutorial002.py"
```

Expand Down
Loading
Loading