You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The pages were written for a linear read-through, so many refer to the
reader's history on ANOTHER page -- "In Tools you returned a str and the
result came back twice", "the input schema you met in Tools", "the same
one you use in Testing", "So far every request has gone one way", "you
already know". Most people arrive at reference docs from a search engine
and read one page; for them those sentences are false and read as steps
in a walkthrough they are not on.
An audit of all 42 pages found 24 such sentences on 16 pages (26 pages
were already clean). Each is rewritten to carry the SAME facts and,
almost always, the SAME cross-link, with only the false claim about the
reader's history removed:
In [Tools] you returned a str and the result came back twice ...
-> A tool that returns a plain str produces the result twice ...
the TextContent you met in [Tools]
-> the TextContent a plain str result becomes ([Tools])
You saw this in [Tools] with Field(le=50).
-> [Tools] shows the same rejection with a Field(le=50) constraint.
Cross-REFERENCES are deliberately untouched: routing the reader
elsewhere for MORE ("the full addressing syntax is on [URI templates]")
is what good reference docs do. Only a sentence that DEPENDS on another
page to make sense is the bug. Every rewritten factual claim was
re-verified against the SDK source, not against the docs.
Also:
- The word "chapter" becomes "page" everywhere (27 sites): a book has
chapters, a reference has pages.
- The landing page's "Where to go next" gains the two audience routes it
was missing: someone building a CLIENT, and someone adding MCP to an
app they already run. The README routes both; the docs did not.
- migration.md's Tasks note is corrected. It said Tasks "are expected to
return as a separate MCP extension in a future release"; the
2026-07-28 revision reintroduces them as SEP-2663
(io.modelcontextprotocol/tasks), redesigned around polling. This SDK
does not implement the extension yet, and the note now says so.
Copy file name to clipboardExpand all lines: docs/advanced/low-level-server.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,7 @@ For everything else, stay on `MCPServer`.
12
12
13
13
## The same tool, by hand
14
14
15
-
This is `search_books`from **[Tools](../servers/tools.md)**(the nine-line `@mcp.tool()` file) with the sugar removed:
15
+
This is the `search_books`tool that **[Tools](../servers/tools.md)**writes in nine lines of `@mcp.tool()`, with the sugar removed:
16
16
17
17
```python title="server.py" hl_lines="23 27 33"
18
18
--8<--"docs_src/lowlevel/tutorial001.py"
@@ -56,12 +56,12 @@ asyncio.run(main())
56
56
57
57
The same text the `@mcp.tool()` version produced. Two honest differences:
58
58
59
-
*`result.structured_content` is `None`. The high-level server wrapped your`-> str` into `{"result": ...}`; here nobody builds what you didn't build.
59
+
*`result.structured_content` is `None`. The high-level server wraps a`-> str` into `{"result": ...}` for you; here nobody builds what you didn't build.
60
60
*`list_tools` returns the schema **you** typed, character for character. The high-level version had `"title": "Query"` on every property and a `"title": "search_booksArguments"` at the root: Pydantic artifacts. Down here, if it's on the wire, you put it there.
61
61
62
62
## Nothing is checked for you
63
63
64
-
In **[Tools](../servers/tools.md)** you saw a bad argument get rejected before your function ran. That was `MCPServer`validating the call against the schema it generated.
64
+
`MCPServer` rejects a bad argument before your function ever runs, validating the call against the schema it generated (**[Tools](../servers/tools.md)**).
65
65
66
66
`Server` does not do that. Your `input_schema` is *advertised* to the client; it is never *applied* to `params.arguments`.
67
67
@@ -179,7 +179,7 @@ The handshake belongs to the runner. `server/discover`, `ping`, and every other
179
179
180
180
## The other handlers
181
181
182
-
Each of these is one idea you now have the vocabulary for; each has its own chapter.
182
+
Each of these is one idea you now have the vocabulary for; each has its own page.
183
183
184
184
*`on_call_tool`, `on_get_prompt`, and `on_read_resource` may return an `InputRequiredResult` instead of their normal result to pause the call and ask the client for input; see **[Multi-round-trip requests](../handlers/multi-round-trip.md)**. True to this tier, nothing is installed for you: where `MCPServer` seals `requestState` by default, here the `request_state` you set crosses the wire exactly as written until you opt in with `server.middleware.append(RequestStateBoundary(RequestStateSecurity(keys=[...]), default_audience=server.name))`: one line (both names import from `mcp.server.request_state`) for the identical sealing and verification `MCPServer` performs (**[Protecting `requestState`](../handlers/multi-round-trip.md#protecting-requeststate)**).
185
185
*`on_list_resources`, `on_read_resource`, `on_list_prompts`, `on_get_prompt`, `on_completion` are the same `(ctx, params) -> result` shape for the other primitives.
Copy file name to clipboardExpand all lines: docs/advanced/pagination.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -48,7 +48,7 @@ Every `list_*` method on `Client` (`list_tools`, `list_resources`, `list_resourc
48
48
49
49
Run its `main()` and it prints `100 resources`: ten pages of ten, stitched together by a loop that never knew there were ten pages.
50
50
51
-
This is the same loop **[The Client](../client/index.md)**chapter showed you, and it costs nothing against a server that doesn't page: `next_cursor` is `None` on the first response and the loop runs once.
51
+
This is the same loop **[The Client](../client/index.md)**shows for every `list_*` verb, and it costs nothing against a server that doesn't page: `next_cursor` is `None` on the first response and the loop runs once.
Copy file name to clipboardExpand all lines: docs/client/callbacks.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# Client callbacks
2
2
3
-
So far every request has gone one way: client to server.
3
+
Nearly every request in MCP goes one way: client to server.
4
4
5
5
A server can also ask the **client** for things: to put a question to the user, to sample the user's model, to list the user's workspace folders. You answer those requests by passing **callbacks** to `Client(...)`.
6
6
@@ -15,7 +15,7 @@ Here is a server whose tool can't finish on its own:
15
15
*`ctx.elicit(...)` sends an `elicitation/create` request **to the client** and waits.
16
16
* The tool doesn't return until somebody (a person in a form, or your code) supplies a `name`.
17
17
18
-
That is the server half, and the **[Elicitation](../handlers/elicitation.md)**chapter owns it. This chapter is the other end of the wire.
18
+
That is the server half, and the **[Elicitation](../handlers/elicitation.md)**page owns it. This page is the other end of the wire.
Copy file name to clipboardExpand all lines: docs/client/identity-assertion.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,14 +1,14 @@
1
1
# Identity assertion
2
2
3
-
Every provider in **[OAuth clients](oauth-clients.md)** starts by asking the MCP server a question: *which authorization server do you trust?* It follows the answer wherever it points, and then either a person signs in or a pre-shared secret stands in for one.
3
+
An ordinary OAuth provider (**[OAuth clients](oauth-clients.md)**) starts by asking the MCP server a question: *which authorization server do you trust?* It follows the answer wherever it points, and then either a person signs in or a pre-shared secret stands in for one.
4
4
5
5
An enterprise wants neither decided per server. It already runs an identity provider (Okta, Microsoft Entra ID, your own); the user already signed in to it this morning; and it is the one place the security team wants to decide who may reach what. [SEP-990](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/990), the **Enterprise-Managed Authorization** extension, moves the decision there. The IdP signs a short-lived JWT, an **Identity Assertion JWT Authorization Grant**, the **ID-JAG**: a statement that *this user*, through *this client*, may reach *this MCP server*. The client trades it for an ordinary access token. No browser, no consent screen, no dynamic registration.
6
6
7
-
This chapter is both ends of that trade. The MCP server itself never changes: it is still the resource server from **[Authorization](../run/authorization.md)**, checking whatever token shows up.
7
+
This page is both ends of that trade. The MCP server itself never changes: it is still the resource server from **[Authorization](../run/authorization.md)**, checking whatever token shows up.
8
8
9
9
## Two token requests
10
10
11
-
Two different authorities are in play, and naming them apart is most of understanding this page. The **enterprise IdP** is your organization's identity provider: it knows who the employee is, it is where policy lives, and it issues the ID-JAG. The SDK never talks to it. The **MCP authorization server** is the same party it was in **[Authorization](../run/authorization.md)**: the issuer named in the MCP server's metadata, the thing that mints the tokens that MCP server accepts. In the flows you already know, those two roles are usually one box. Here they are two, and the whole grant is the second agreeing to trust the first.
11
+
Two different authorities are in play, and naming them apart is most of understanding this page. The **enterprise IdP** is your organization's identity provider: it knows who the employee is, it is where policy lives, and it issues the ID-JAG. The SDK never talks to it. The **MCP authorization server** is the same party it was in **[Authorization](../run/authorization.md)**: the issuer named in the MCP server's metadata, the thing that mints the tokens that MCP server accepts. In an ordinary OAuth flow, those two roles are usually one box. Here they are two, and the whole grant is the second agreeing to trust the first.
12
12
13
13
The client makes one token request to each.
14
14
@@ -27,7 +27,7 @@ Everything below is the second request: the client that sends it and the authori
27
27
28
28
Read it from the bottom.
29
29
30
-
*`main()` is the `main()`from **[OAuth clients](oauth-clients.md)**, line for line. That is the point: once the provider exists, nothing downstream knows which grant produced the token.
30
+
*`main()` is the standard OAuth-client `main()`(**[OAuth clients](oauth-clients.md)**), unchanged line for line. That is the point: once the provider exists, nothing downstream knows which grant produced the token.
31
31
* The provider takes what the other providers cannot discover: a `client_id` and `client_secret` somebody **pre-registered** with the authorization server, that authorization server's `issuer`, and `assertion_provider`, an async callback that returns a fresh ID-JAG on demand.
32
32
*`storage` is the same `TokenStorage` protocol. Only the two token methods are ever called; there is no dynamic registration here, so there is no `client_info` to remember.
Copy file name to clipboardExpand all lines: docs/client/index.md
+5-5Lines changed: 5 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,7 +12,7 @@ It is one object with one lifecycle: construct it, enter `async with`, call meth
12
12
13
13
The server at the top is only there so you have something to connect to. The client is the five highlighted lines.
14
14
15
-
*`Client(mcp)` is given the **server object itself**. That is the in-memory transport: no subprocess, no port, no HTTP. It is how every example in this chapter, and every test you write, connects.
15
+
*`Client(mcp)` is given the **server object itself**. That is the in-memory transport: no subprocess, no port, no HTTP. It is how every example on this page, and every test you write, connects.
16
16
*`async with` is the **lifecycle**. Entering it connects and negotiates; leaving it disconnects. There is no `connect()` / `close()` pair, and a `Client` cannot be reused after the block ends.
17
17
* Inside the block the connection facts are already there as plain properties.
18
18
@@ -24,7 +24,7 @@ The server at the top is only there so you have something to connect to. The cli
24
24
* A URL string (`Client("http://localhost:8000/mcp")`): Streamable HTTP, the production path.
25
25
* A **transport**: anything you can `async with ... as (read, write)`, such as `stdio_client(...)` wrapping a subprocess.
26
26
27
-
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)**.
27
+
Everything else on this page is identical across all three. Headers, subprocesses, timeouts, and the `Transport` protocol get their own page: **[Client transports](transports.md)**.
28
28
29
29
### What's on a connected client
30
30
@@ -104,7 +104,7 @@ That is why `main` narrows with `isinstance(block, TextContent)` before touching
104
104
105
105
`structured_content` is the tool's return value as JSON, matching the tool's declared `output_schema`. No string parsing, no guessing.
106
106
107
-
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](../servers/structured-output.md)**chapter.
107
+
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](../servers/structured-output.md)**page.
108
108
109
109
### `is_error`: whether the tool failed
110
110
@@ -181,7 +181,7 @@ A server with a completion handler can autocomplete prompt and resource-template
181
181
*`ref` says *which* prompt or template you're filling in: a `PromptReference` or a `ResourceTemplateReference`.
182
182
*`argument` is `{"name": ..., "value": ...}`: the argument and what the user has typed so far.
183
183
184
-
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](../servers/completions.md)**chapter.
184
+
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](../servers/completions.md)**page.
185
185
186
186
## Pagination
187
187
@@ -197,7 +197,7 @@ This loop is correct against every server. `MCPServer` returns everything in one
197
197
198
198
`Client(mcp)` with no process and no port is already a test harness for your server.
199
199
200
-
There is one constructor flag built for that: `Client(mcp, raise_exceptions=True)`. It only has an effect on in-memory connections, and **[Testing](../get-started/testing.md)** is the chapter that explains it and builds the whole pattern around it.
200
+
There is one constructor flag built for that: `Client(mcp, raise_exceptions=True)`. It only has an effect on in-memory connections, and **[Testing](../get-started/testing.md)** is the page that explains it and builds the whole pattern around it.
Copy file name to clipboardExpand all lines: docs/client/oauth-clients.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ Some MCP servers are protected. Send them a request without a token and they ans
4
4
5
5
**`OAuthClientProvider`** is how you get the token. It is not an MCP object at all. It is an `httpx.Auth`, the standard httpx hook for "do something to every request". You attach it to an `httpx.AsyncClient`, hand that client to the Streamable HTTP transport, and stop thinking about it.
6
6
7
-
This chapter is the client side. Making your own server demand a token is **[Authorization](../run/authorization.md)**.
7
+
This page is the client side. Making your own server demand a token is **[Authorization](../run/authorization.md)**.
8
8
9
9
## The provider
10
10
@@ -87,9 +87,9 @@ You wrote none of it. Three keyword arguments remain (`timeout`, `client_metadat
87
87
88
88
### Try it
89
89
90
-
Everything else in these docs you have checked with an in-memory `Client(server)`. Not this: the whole point of the flow is an HTTP `401`, and there is no HTTP between an in-memory client and its server.
90
+
Every other example in these docs you can check with an in-memory `Client(server)`. Not this: the whole point of the flow is an HTTP `401`, and there is no HTTP between an in-memory client and its server.
91
91
92
-
The repository ships the live version. `examples/servers/simple-auth/` runs a standalone authorization server and a protected MCP server; `examples/clients/simple-auth-client/` is this chapter's client grown into a small CLI. Its README has the two commands: start the servers, run the client against them, and you watch the four steps go by.
92
+
The repository ships the live version. `examples/servers/simple-auth/` runs a standalone authorization server and a protected MCP server; `examples/clients/simple-auth-client/` is this page's client grown into a small CLI. Its README has the two commands: start the servers, run the client against them, and you watch the four steps go by.
93
93
94
94
## Machine to machine
95
95
@@ -119,7 +119,7 @@ By default the secret travels as HTTP Basic auth on the token request (`client_s
119
119
the same pattern: construct one, put it on `auth=`. The same module ships
120
120
`SignedJWTParameters` and `static_assertion_provider`, two helpers that build its assertion.
121
121
122
-
There is one more no-human situation: the client belongs to an enterprise whose identity provider, not the user, decides which MCP servers it may reach. That is a different grant with its own trust model and its own chapter, **[Identity assertion](identity-assertion.md)**.
122
+
There is one more no-human situation: the client belongs to an enterprise whose identity provider, not the user, decides which MCP servers it may reach. That is a different grant with its own trust model and its own page, **[Identity assertion](identity-assertion.md)**.
Copy file name to clipboardExpand all lines: docs/client/transports.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,7 +18,7 @@ No subprocess, no port, no bytes on a wire. The client and the server are two ob
18
18
19
19
That makes it two things at once:
20
20
21
-
***A test harness.** Every example in this documentation is exercised this way, and the **[Testing](../get-started/testing.md)**chapter builds the whole pattern around it.
21
+
***A test harness.** Every example in this documentation is exercised this way, and the **[Testing](../get-started/testing.md)**page builds the whole pattern around it.
22
22
***An embedding API.** An application that constructs the server doesn't need a network hop to call its tools.
Copy file name to clipboardExpand all lines: docs/get-started/first-steps.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,8 @@
1
1
# First steps
2
2
3
-
On the landing page you wrote a server, ran it, and called a tool.
3
+
The **[landing page](../index.md)** moves fast: write a server, run it, call a tool.
4
4
5
-
Now do it again, slowly, with all three things a server can expose, and the names for everything you just saw.
5
+
This page takes it slowly, with all three things a server can expose, and a name for everything along the way.
6
6
7
7
## Host, client, and server
8
8
@@ -12,7 +12,7 @@ Three words you'll see on every page from here on:
12
12
* A **client** lives inside the host and speaks MCP. The host runs one client per server it's connected to.
13
13
* A **server** is what you build with this SDK. It exposes things to clients. It never talks to the model directly.
14
14
15
-
You write the server. Hosts are someone else's product. The SDK also gives you a `Client`. You'll use it to test your servers, and it shows up later in this chapter.
15
+
You write the server. Hosts are someone else's product. The SDK also gives you a `Client`. You'll use it to test your servers, and it shows up later on this page.
16
16
17
17
## The three primitives
18
18
@@ -70,7 +70,7 @@ Hello, World!
70
70
71
71
**Prompts.** One entry: `summarize`, with a single required `text` argument. Get it with some text and you receive one message with `role: user` and your rendered string as the content. That's all a prompt is: a function that builds messages.
72
72
73
-
The Inspector ran your server over **stdio**, one of the transports an MCP server can speak. You don't pick one yet; **[Running your server](../run/index.md)** is the chapter for that.
73
+
The Inspector ran your server over **stdio**, one of the transports an MCP server can speak. You don't pick one yet; **[Running your server](../run/index.md)** is the page for that.
Copy file name to clipboardExpand all lines: docs/handlers/context.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -66,7 +66,7 @@ The injected object is small. Besides `request_id`:
66
66
*`ctx.headers`: the request headers the transport carried, or `None` on stdio. Read a custom header with `(ctx.headers or {}).get("x-...")`. Headers are client-supplied input - fine for a locale or a feature flag, never an identity.
67
67
*`ctx.request_context`: the raw per-request record. The field you'll reach for is `lifespan_context`, the object your startup code yielded (see **[Lifespan](lifespan.md)**).
68
68
69
-
Logging is deliberately not on that list. A server logs with Python's `logging` module, like any other Python program. **[Logging](logging.md)** is the short chapter on why.
69
+
Logging is deliberately not on that list. A server logs with Python's `logging` module, like any other Python program. **[Logging](logging.md)** is the short page on why.
70
70
71
71
!!! tip
72
72
Injection only happens for the function you registered. A helper that your tool calls doesn't get
@@ -124,6 +124,6 @@ On a 2026-07-28 connection, clients receive change notifications only on a `subs
124
124
*`ctx.request_id` identifies the request; `ctx.request_context.lifespan_context` is what your startup yielded.
125
125
*`await ctx.read_resource(uri)` lets a tool read the server's own resources.
126
126
*`ctx.session` is the channel back to the client: `send_tool_list_changed()` and its siblings tell it to re-fetch a list you changed.
127
-
* Progress reporting and elicitation also start at `Context`; each has its own chapter.
127
+
* Progress reporting and elicitation also start at `Context`; each has its own page.
128
128
129
129
Next: parameters the model never sees, filled by your own functions, in **[Dependencies](dependencies.md)**.
0 commit comments