Skip to content

Commit 5b1275b

Browse files
committed
Merge branch 'main' into shttp-native-dispatch
Brings in "Stop answering cancelled requests" and reapplies its policy onto the rebuilt seam instead of the code it targeted: - The cancelled-request answer policy now lives at the correlator's single site: a peer-cancelled request is never answered by the dispatch layer, its late result or error is dropped, and it settles through a transport-supplied hook. The dispatcher forwards the hook it receives in message metadata; the streamable HTTP transport supplies its own, which ends the request's stream with the REQUEST_CANCELLED terminal error written through the request's ordered channel (so a resuming client's replay terminates too). - Hook containment happens once, in the correlator.
2 parents 47e156d + b61ce38 commit 5b1275b

205 files changed

Lines changed: 2134 additions & 890 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/shared.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
- name: mcp-types installs and imports standalone
5252
run: |
5353
uv run --isolated --no-project --with ./src/mcp-types python -c \
54-
"import mcp_types, mcp_types.jsonrpc, mcp_types.methods, mcp_types.version, mcp_types.v2025_11_25, mcp_types.v2026_07_28"
54+
"import mcp_types, mcp_types.jsonrpc, mcp_types.methods, mcp_types.version, mcp_types._v2025_11_25, mcp_types._v2026_07_28"
5555
5656
test:
5757
name: test (${{ matrix.python-version }}, ${{ matrix.dep-resolution.name }}, ${{ matrix.os }})

docs/advanced/apps.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ then come back.
2020

2121
## A clock with a face
2222

23-
```python title="server.py" hl_lines="18 21 29 31"
23+
```python title="server.py" hl_lines="19 22 30 32"
2424
--8<-- "docs_src/apps/tutorial001.py"
2525
```
2626

@@ -51,7 +51,7 @@ The model reads `content`; the iframe is for humans. A UI-capable host still fee
5151
the text result to the model, and a text-only client gets *only* that. So the
5252
canonical pattern is one tool, two answers. Look at `get_time` again:
5353

54-
```python title="server.py" hl_lines="22-26"
54+
```python title="server.py" hl_lines="23-27"
5555
--8<-- "docs_src/apps/tutorial001.py"
5656
```
5757

docs/advanced/extensions.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ The same file's `main()` is the whole client story, both halves of it:
128128
The one interceptive hook. Override `intercept_tool_call` to observe, short-circuit,
129129
or veto a tool call:
130130

131-
```python title="server.py" hl_lines="18-25"
131+
```python title="server.py" hl_lines="17-24"
132132
--8<-- "docs_src/extensions/tutorial005.py"
133133
```
134134

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

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

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

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

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

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

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

docs/advanced/low-level-server.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ For everything else, stay on `MCPServer`.
1414

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

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

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

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

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

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

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

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

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

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

120-
```python title="server.py" hl_lines="38"
120+
```python title="server.py" hl_lines="37"
121121
--8<-- "docs_src/lowlevel/tutorial004.py"
122122
```
123123

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

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

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

docs/advanced/middleware.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ If `Server(name, on_call_tool=...)` is new to you, read **[The low-level Server]
1616

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

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

docs/advanced/pagination.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Pagination is for the server whose resource list is really a database: thousands
1010

1111
## A server that pages
1212

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

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

3939
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`:
4040

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

docs/client/caching.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Out of the box every result says `ttlMs: 0, cacheScope: "private"`: immediately
2525

2626
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:
2727

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

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

4040
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.
4141

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

docs/client/callbacks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ That is the server half, and the **[Elicitation](../handlers/elicitation.md)** p
1919

2020
## The elicitation callback
2121

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

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

5757
!!! info
58-
`mode="legacy"` on line 17 is doing real work. By default `Client(...)` negotiates the modern
58+
`mode="legacy"` on the `Client(...)` call is doing real work. By default `Client(...)` negotiates the modern
5959
protocol path, and that path has no back-channel for server-to-client requests: `ctx.elicit`
6060
fails before your callback ever runs. The transport doesn't decide that; the negotiated
6161
protocol does, in-memory and over a URL alike. Pin `mode="legacy"` whenever your client has

docs/client/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ A tool that raises does **not** raise in your client. It comes back as an ordina
135135

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

138-
```python title="client.py" hl_lines="23-32"
138+
```python title="client.py" hl_lines="22-31"
139139
--8<-- "docs_src/client/tutorial004.py"
140140
```
141141

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

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

177-
```python title="client.py" hl_lines="28-32"
177+
```python title="client.py" hl_lines="27-31"
178178
--8<-- "docs_src/client/tutorial006.py"
179179
```
180180

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

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

190-
```python title="client.py" hl_lines="23-31"
190+
```python title="client.py" hl_lines="22-30"
191191
--8<-- "docs_src/client/tutorial007.py"
192192
```
193193

docs/client/oauth-clients.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ There is one more no-human situation: the client belongs to an enterprise whose
131131

132132
## When it fails
133133

134-
When the OAuth flow goes wrong, the provider raises an `OAuthFlowError` from `mcp.client.auth`. It has two subclasses. `OAuthRegistrationError` means the authorization server refused to register you. `OAuthTokenError` means the token endpoint said no. One `except OAuthFlowError:` covers discovery, registration, authorization, and exchange.
134+
When the OAuth flow goes wrong, the provider raises an `OAuthFlowError` from `mcp.client.auth`. It has two subclasses. `OAuthRegistrationError` means registration did not yield a client you can use: the authorization server refused to register you, or it did register you but with credentials this flow cannot use (for instance an authentication method it does not implement). `OAuthTokenError` means a token could not be obtained: the token endpoint said no, or a stored client record carries an authentication method this client cannot apply, which is reported while building the token request rather than sent. One `except OAuthFlowError:` covers discovery, registration, authorization, and exchange.
135135

136136
Not everything is a flow error. The network can still fail; those are ordinary `httpx2` exceptions and pass through untouched.
137137

0 commit comments

Comments
 (0)