docs: convert bold cross-references into links, link SEP and RFC mentions (#3017)

This commit is contained in:
Max 2026-06-29 13:07:26 +02:00 • committed by GitHub
commit 24fdd909ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 167 additions and 167 deletions

View file

@ -15,7 +15,7 @@ Here is a server whose tool can't finish on its own:
* `ctx.elicit(...)` sends an `elicitation/create` request **to the client** and waits.
* The tool doesn't return until somebody (a person in a form, or your code) supplies a `name`.
That is the server half, and the **Elicitation** chapter owns it. This chapter is the other end of the wire.
That is the server half, and the **[Elicitation](../tutorial/elicitation.md)** chapter owns it. This chapter is the other end of the wire.
## The elicitation callback
@ -31,7 +31,7 @@ That is the server half, and the **Elicitation** chapter owns it. This chapter i
!!! tip
`params` is a union of the two elicitation modes. Here `params.mode` is `"form"`; a `"url"` request
carries `params.url` instead of a schema. One callback handles both; branch on `params.mode`.
**Elicitation** shows the full pattern.
**[Elicitation](../tutorial/elicitation.md)** shows the full pattern.
### Try it
@ -59,11 +59,11 @@ One `tools/call` from you, one `elicitation/create` back from the server, answer
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
to answer one; every test behind this page does. **Protocol versions** has the whole story.
to answer one; every test behind this page does. **[Protocol versions](protocol-versions.md)** has the whole story.
On a 2026-07-28 session the callback isn't dead, it's fed differently: when a tool returns an
`InputRequiredResult` carrying an `ElicitRequest`, `Client` dispatches that entry to the same
`elicitation_callback` and retries the call for you. That flow is **Multi-round-trip requests**.
`elicitation_callback` and retries the call for you. That flow is **[Multi-round-trip requests](../advanced/multi-round-trip.md)**.
## A callback is a capability
@ -113,7 +113,7 @@ Pass all three callbacks and you get `['elicitation', 'sampling', 'roots']`. Pas
`sampling_callback` answers `sampling/createMessage`: the server asking *your* model to complete something. `list_roots_callback` answers `roots/list`: the server asking which directories it may work in.
Both work. Both follow the rule above. And both serve RPCs the **2026-07-28 spec removes**: a modern server doesn't call back into your client mid-request, it hands the request back to you as part of the tool result (**Multi-round-trip requests**). The callbacks themselves are not dead. When an `InputRequiredResult` carries a `CreateMessageRequest` or a `ListRootsRequest`, `Client`'s auto-loop dispatches it to the same `sampling_callback` or `list_roots_callback` you registered here. The whole list is in **Deprecated features**.
Both work. Both follow the rule above. And both serve RPCs the **2026-07-28 spec removes**: a modern server doesn't call back into your client mid-request, it hands the request back to you as part of the tool result (**[Multi-round-trip requests](../advanced/multi-round-trip.md)**). The callbacks themselves are not dead. When an `InputRequiredResult` carries a `CreateMessageRequest` or a `ListRootsRequest`, `Client`'s auto-loop dispatches it to the same `sampling_callback` or `list_roots_callback` you registered here. The whole list is in **[Deprecated features](../advanced/deprecated.md)**.
You still need the callbacks to talk to servers that haven't moved. The signatures:
@ -131,7 +131,7 @@ Pass them to `Client(...)` exactly like `elicitation_callback`.
Two more. Neither declares anything.
`logging_callback` receives every `notifications/message` a server sends, as `LoggingMessageNotificationParams` (`level`, `logger`, `data`). Protocol logging is itself deprecated by the 2026-07-28 spec (**Logging** has what to do instead), so this callback exists for the servers that still emit it.
`logging_callback` receives every `notifications/message` a server sends, as `LoggingMessageNotificationParams` (`level`, `logger`, `data`). Protocol logging is itself deprecated by the 2026-07-28 spec (**[Logging](../tutorial/logging.md)** has what to do instead), so this callback exists for the servers that still emit it.
`message_handler` is the catch-all: every server notification reaches it (as well as its specific callback), and on a stream-backed transport so does every transport-level `Exception`. The one pattern worth knowing is `if isinstance(message, Exception): raise message`, so a broken connection fails loudly instead of vanishing.
@ -144,4 +144,4 @@ Two more. Neither declares anything.
* `sampling_callback` and `list_roots_callback` work the same way but serve deprecated features; modern servers use multi-round-trip requests instead.
* `logging_callback` and `message_handler` receive notifications. They declare nothing.
Next: the first argument you've been passing to `Client(...)` all along, **Client transports**.
Next: the first argument you've been passing to `Client(...)` all along, **[Client transports](transports.md)**.

View file

@ -24,7 +24,7 @@ The server at the top is only there so you have something to connect to. The cli
* A URL string (`Client("http://localhost:8000/mcp")`): Streamable HTTP, the production path.
* A **transport**: anything you can `async with ... as (read, write)`, such as `stdio_client(...)` wrapping a subprocess.
Everything else on this page is identical across all three. Headers, subprocesses, timeouts, and the `Transport` protocol get their own chapter: **Client transports**.
Everything else on this page is identical across all three. Headers, subprocesses, timeouts, and the `Transport` protocol get their own chapter: **[Client transports](transports.md)**.
### What's on a connected client
@ -35,7 +35,7 @@ Four read-only properties, populated the moment you enter the block:
* `client.protocol_version`: the protocol version the two sides agreed on. Here it is `"2026-07-28"`.
* `client.instructions`: the server's `instructions=` string, or `None` if it didn't set one.
You never picked a protocol version. By default the `Client` probes the server and falls back to the classic handshake on older ones, so one client works against any era of server. When you need to control that, **Protocol versions** has the whole story.
You never picked a protocol version. By default the `Client` probes the server and falls back to the classic handshake on older ones, so one client works against any era of server. When you need to control that, **[Protocol versions](protocol-versions.md)** has the whole story.
!!! tip
`client.session` is the underlying `ClientSession`, the low-level escape hatch.
@ -104,7 +104,7 @@ That is why `main` narrows with `isinstance(block, TextContent)` before touching
`structured_content` is the tool's return value as JSON, matching the tool's declared `output_schema`. No string parsing, no guessing.
When both are present they say the same thing twice on purpose: `content` is for a model, `structured_content` is for code. Where the structured half comes from, and how to control it, is the **Structured Output** chapter.
When both are present they say the same thing twice on purpose: `content` is for a model, `structured_content` is for code. Where the structured half comes from, and how to control it, is the **[Structured Output](../tutorial/structured-output.md)** chapter.
### `is_error`: whether the tool failed
@ -129,7 +129,7 @@ A tool that raises does **not** raise in your client. It comes back as an ordina
(`call_tool("does_not_exist", {})`) and nothing raises. You get the same shape back,
`is_error=True` with `Unknown tool: does_not_exist` in `content`. A `Client` method raises
`MCPError` only when the server answers with a JSON-RPC **error** instead of a result, and
**Handling errors** covers when a server produces which.
**[Handling errors](../tutorial/handling-errors.md)** covers when a server produces which.
## Resources
@ -145,7 +145,7 @@ The resource verbs come in pairs: two ways to list, one way to read.
`read_resource` returns `contents`, a list of `TextResourceContents` or `BlobResourceContents`. Same idea as tool content: narrow with `isinstance`, then read `.text` (or `.blob`).
A client can also **subscribe** to a resource and be told when it changes: `subscribe_resource(uri)` and `unsubscribe_resource(uri)`, same shape as everything else here. `MCPServer` doesn't implement that half. It says so up front (`server_capabilities.resources.subscribe` is `False`) and answers the request with an `MCPError`: `-32601`, *Method not found*. A server that does support subscriptions is built on the low-level `Server` (**The low-level Server**).
A client can also **subscribe** to a resource and be told when it changes: `subscribe_resource(uri)` and `unsubscribe_resource(uri)`, same shape as everything else here. `MCPServer` doesn't implement that half. It says so up front (`server_capabilities.resources.subscribe` is `False`) and answers the request with an `MCPError`: `-32601`, *Method not found*. A server that does support subscriptions is built on the low-level `Server` (**[The low-level Server](../advanced/low-level-server.md)**).
## Prompts
@ -181,7 +181,7 @@ A server with a completion handler can autocomplete prompt and resource-template
* `ref` says *which* prompt or template you're filling in: a `PromptReference` or a `ResourceTemplateReference`.
* `argument` is `{"name": ..., "value": ...}`: the argument and what the user has typed so far.
The answer is in `result.completion.values`. Type `"p"` and the server comes back with `['poetry']`. The server side, and how a handler uses the *other* already-filled arguments to narrow its suggestions, is the **Completions** chapter.
The answer is in `result.completion.values`. Type `"p"` and the server comes back with `['poetry']`. The server side, and how a handler uses the *other* already-filled arguments to narrow its suggestions, is the **[Completions](../tutorial/completions.md)** chapter.
## Pagination
@ -191,13 +191,13 @@ Every `list_*` method takes a `cursor=` keyword and every result carries a `next
--8<-- "docs_src/client/tutorial007.py"
```
This loop is correct against every server. `MCPServer` returns everything in one page, so `next_cursor` is `None` and the loop runs once, which is why most code never writes it. Servers that genuinely page, and the rules cursors obey, are in **Pagination**.
This loop is correct against every server. `MCPServer` returns everything in one page, so `next_cursor` is `None` and the loop runs once, which is why most code never writes it. Servers that genuinely page, and the rules cursors obey, are in **[Pagination](../advanced/pagination.md)**.
## In tests
`Client(mcp)` with no process and no port is already a test harness for your server.
There is one constructor flag built for that: `Client(mcp, raise_exceptions=True)`. It only has an effect on in-memory connections, and **Testing** is the chapter that explains it and builds the whole pattern around it.
There is one constructor flag built for that: `Client(mcp, raise_exceptions=True)`. It only has an effect on in-memory connections, and **[Testing](../tutorial/testing.md)** is the chapter that explains it and builds the whole pattern around it.
## Recap
@ -209,4 +209,4 @@ There is one constructor flag built for that: `Client(mcp, raise_exceptions=True
* `list_resources` / `list_resource_templates` / `read_resource`, `list_prompts` / `get_prompt`, and `complete` round out the verbs.
* Every `list_*` takes `cursor=`; loop until `next_cursor` is `None`.
Next: the things a server can ask the *client* for, and how you answer, in **Client callbacks**.
Next: the things a server can ask the *client* for, and how you answer, in **[Client callbacks](callbacks.md)**.

View file

@ -48,9 +48,9 @@ You want this for the **push-style** features.
A server-initiated request is the server calling *you*: `ctx.elicit(...)` putting a form in front of your user, sampling asking your model for a completion mid-tool-call. That channel only exists on a handshake-era session.
At 2026-07-28 it is gone. The server *returns* its questions and you retry the call with the answers (**Multi-round-trip requests**).
At 2026-07-28 it is gone. The server *returns* its questions and you retry the call with the answers (**[Multi-round-trip requests](../advanced/multi-round-trip.md)**).
`mode="auto"` only gives you a handshake when the server is too old for anything else. `mode="legacy"` guarantees one. Reach for it whenever you hand `Client(...)` a `sampling_callback`, an `elicitation_callback` you want driven as a request, or a `message_handler`. **Client callbacks** goes through each.
`mode="auto"` only gives you a handshake when the server is too old for anything else. `mode="legacy"` guarantees one. Reach for it whenever you hand `Client(...)` a `sampling_callback`, an `elicitation_callback` you want driven as a request, or a `message_handler`. **[Client callbacks](callbacks.md)** goes through each.
## Pinning a version
@ -124,4 +124,4 @@ The second connection made **zero** negotiation round trips and still knows exac
* A version pin (`mode="2026-07-28"`) sends no negotiation traffic at all, at the cost of a blank `server_info`.
* `prior_discover=` pays that cost back: save `client.session.discover_result`, reconnect with it, get both.
A modern connection has no push channel, so how does a 2026 server ask you a question mid-call? It returns it: **Multi-round-trip requests**.
A modern connection has no push channel, so how does a 2026 server ask you a question mid-call? It returns it: **[Multi-round-trip requests](../advanced/multi-round-trip.md)**.

View file

@ -4,7 +4,7 @@ Every `Client` talks to its server over a **transport**: the thing that actually
You never configure one separately. `Client` takes a single positional argument and works the transport out from its type.
The *server* side of each (what `mcp.run()` does and what you deploy) is **Running your server**.
The *server* side of each (what `mcp.run()` does and what you deploy) is **[Running your server](../run/index.md)**.
## In memory
@ -18,7 +18,7 @@ No subprocess, no port, no bytes on a wire. The client and the server are two ob
That makes it two things at once:
* **A test harness.** Every example in this documentation is exercised this way, and the **Testing** chapter builds the whole pattern around it.
* **A test harness.** Every example in this documentation is exercised this way, and the **[Testing](../tutorial/testing.md)** chapter builds the whole pattern around it.
* **An embedding API.** An application that constructs the server doesn't need a network hop to call its tools.
## Streamable HTTP
@ -68,7 +68,7 @@ Two things to notice:
!!! info
If you know `httpx`, you already know how to do auth, proxies, event hooks, retries and connection
limits here. The SDK adds nothing on top and takes nothing away. It is also where OAuth plugs in:
`httpx.AsyncClient(auth=OAuthClientProvider(...))`. That whole flow is **OAuth clients**.
`httpx.AsyncClient(auth=OAuthClientProvider(...))`. That whole flow is **[OAuth clients](../advanced/oauth-clients.md)**.
## stdio
@ -112,4 +112,4 @@ A **transport** is any async context manager that yields a `(read, write)` pair
* A transport is anything you can `async with x as (read, write)`. `Client` hands anything that isn't a server object or a URL straight to that protocol.
* Constructing a `Client` picks the transport. `async with` opens it.
Once the transport is open the two sides have to agree on a protocol version. You normally never think about it; when you do, **Protocol versions** is the page.
Once the transport is open the two sides have to agree on a protocol version. You normally never think about it; when you do, **[Protocol versions](protocol-versions.md)** is the page.