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
docs: ctx.elicit() at 2026-07-28 is refused server-side since #3040
Rebasing onto main picked up #3040, which hardened the era semantics:
a server-initiated request on a 2026-07-28 connection is now refused by
the SERVER, as
MCPError: Cannot send 'elicitation/create': this transport context
has no back-channel for server-initiated requests.
instead of reaching the client and being rejected there as "Method not
found". Two of this branch's docs tests failed on the rebase -- which
is the point of testing every example -- and the troubleshooting page
keyed its biggest elicitation entry on the old string.
So the two entries merge into the one that owns the surviving string.
"Method not found" is now short and generic (an era mismatch: a method
one protocol revision has and the other does not), and says explicitly
that ctx.elicit() at 2026-07-28 no longer produces it. The "Cannot
send 'elicitation/create' ..." entry becomes the single home for "your
handler reached back and nothing can carry it", with its two real
triggers -- any 2026-07-28 connection, and a legacy connection on a
stateless_http=True server -- both shown from tested examples, and the
one fix (a resolver). The tests pin the new behaviour, the same way
#3040 itself updated tests/docs_src/test_client_callbacks.py.
Copy file name to clipboardExpand all lines: docs/troubleshooting.md
+34-34Lines changed: 34 additions & 34 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -260,37 +260,9 @@ For the server operator, the matching log line is `Rejected request with unknown
260
260
261
261
## `MCPError: Method not found`
262
262
263
-
One side sent a JSON-RPC request the other side has no handler for, and `e.error.data` names the method. The one everybody hits has `data` equal to `elicitation/create`:
263
+
One side sent a JSON-RPC request the other has no handler for, and `e.error.data` names the method. The usual cause is an **era mismatch**: a method that exists in one protocol revision and not in the other, sent to a peer on the wrong one — a `2025`-era `resources/subscribe` arriving at a `2026-07-28` connection, a `2026`-only `subscriptions/listen` sent by a client pinned to `mode="legacy"`. **[Protocol versions](protocol-versions.md)** is the map of which side speaks what, and the other honest cause — an optional capability you never registered a handler for — is on **[Completions](servers/completions.md)**.
`ctx.elicit()` (and `ctx.elicit_url()`) is a request from the *server* to the *client*, and the **2026-07-28** protocol has no server-initiated requests. The in-memory `Client(server)` negotiates `2026-07-28` without being asked, so this fails on the very first test, and passing `elicitation_callback=` changes nothing: the method itself is what's missing, not the handler.
280
-
281
-
The fix is to move the question out of the tool body and into a **resolver**, which works on every protocol version:
282
-
283
-
```python title="server.py" hl_lines="15-17 21"
284
-
--8<--"docs_src/troubleshooting/tutorial007.py"
285
-
```
286
-
287
-
Same question, same `elicitation_callback` on the client. The difference is under the hood: a resolver lets the server *return* the question from the call instead of pushing it, so nothing ever flows server-to-client. **[Elicitation](handlers/elicitation.md)** covers resolvers; **[Multi-round-trip requests](handlers/multi-round-trip.md)** covers what actually happens on the wire.
288
-
289
-
!!! check
290
-
The tool with `ctx.elicit()` is not wrong, it is *pre-2026*. Connect with `mode="legacy"`
291
-
(the classic `initialize` handshake, spec `2025-11-25` and earlier) and it works, because the
292
-
server-to-client channel exists there. **[Protocol versions](protocol-versions.md)** is the
293
-
page on what each version has.
265
+
One thing does **not** produce this error, despite being a request the modern protocol removed: a tool calling `ctx.elicit()` on a `2026-07-28` connection. The server refuses to *send* that request at all, so what you get instead is `Cannot send 'elicitation/create': ...`, further down this page.
294
266
295
267
## `MCPError: Client did not declare the form elicitation capability required by resolver '<name>'`
296
268
@@ -333,17 +305,45 @@ You see this one from `ctx.elicit()` on a legacy connection, and — on any conn
333
305
334
306
## `MCPError: Cannot send 'elicitation/create': this transport context has no back-channel for server-initiated requests.`
335
307
336
-
Your handler tried to reach the client mid-request, on a transport where nothing can.
308
+
Your handler tried to reach the client mid-request, on a connection where nothing can carry a request from the server. There are exactly two ways to be on one.
337
309
338
-
Stateless HTTP is the usual trigger. `stateless_http=True` means every request is its own world: no session, no server-to-client stream, and so nowhere to send an `elicitation/create` (or `sampling/createMessage`, or `roots/list`):
310
+
**A `2026-07-28` connection — any transport, always.** The modern protocol has no server-initiated requests at all, so the server refuses before anything is sent. `ctx.elicit()` inside a tool is the classic way to meet this — on the very first in-memory test, since `Client(server)` negotiates `2026-07-28` without being asked — and passing `elicitation_callback=` changes nothing, because no request ever reaches the client for it to answer:
mcp.shared.exceptions.MCPError: Cannot send 'elicitation/create': this transport context has no back-channel for server-initiated requests.
324
+
```
325
+
326
+
**A legacy connection on a `stateless_http=True` server.** Statelessness means every request is its own world: no session, no server-to-client stream, and so nowhere to send an `elicitation/create` (or `sampling/createMessage`, or `roots/list`) even for the era that has them:
339
327
340
328
```python title="server.py" hl_lines="16 23"
341
329
--8<--"docs_src/troubleshooting/tutorial008.py"
342
330
```
343
331
344
332
The message names the method it could not send. `NoBackChannelError` is the class the server raises, but the wire carries only the base `MCPError`, so the sentence above is your traceback's last line, not the class name.
345
333
346
-
The fix is the same as for `Method not found`: don't reach back mid-call. A **resolver** (or a returned `InputRequiredResult`) turns the question into part of the *response*, which every transport can carry, stateless or not. **[Multi-round-trip requests](handlers/multi-round-trip.md)** is that mechanism.
334
+
The fix is the same for both: don't reach back mid-call. Move the question into a **resolver** (or return an `InputRequiredResult` yourself) and it becomes part of the *response*, which every connection can carry:
335
+
336
+
```python title="server.py" hl_lines="15-17 21"
337
+
--8<--"docs_src/troubleshooting/tutorial007.py"
338
+
```
339
+
340
+
Same question, same `elicitation_callback` on the client. The difference is under the hood: a resolver lets the server *return* the question from the call instead of pushing it, so nothing ever flows server-to-client. **[Elicitation](handlers/elicitation.md)** covers resolvers; **[Multi-round-trip requests](handlers/multi-round-trip.md)** covers what happens on the wire.
341
+
342
+
!!! check
343
+
The tool with `ctx.elicit()` is not wrong, it is *pre-2026*. Connect with `mode="legacy"`
344
+
(the classic `initialize` handshake, spec `2025-11-25` and earlier) to a server that is not
345
+
`stateless_http=True`, and it works, because the server-to-client channel exists there.
346
+
**[Protocol versions](protocol-versions.md)** is the page on what each version has.
* One 421, three spellings: `Server returned an error response` (the python `Client`), `421 Misdirected Request` / `Invalid Host header` (everything else), `Invalid Host header: <host>` (the server log). Fix: `transport_security=TransportSecuritySettings(allowed_hosts=[...])`.
408
408
*`Task group is not initialized` -> a mounted app whose host lifespan never entered `mcp.session_manager.run()`.
409
409
*`Session not found` -> the server restarted; reconnect.
410
-
*`Method not found` on `elicitation/create` -> `ctx.elicit()` needs a server-to-client channel and`2026-07-28` has none. Use a resolver.
410
+
*`Cannot send 'elicitation/create': ... no back-channel ...` -> `ctx.elicit()` needs a server-to-client channel: a`2026-07-28`connection never has one, and `stateless_http=True` takes away the legacy one. Use a resolver. Its neighbour `Method not found` is a request for a method the other side's protocol revision doesn't have.
411
411
*`Client did not declare the form elicitation capability ...` and `Elicitation not supported` -> the client is missing `elicitation_callback=`.
412
412
*`Invalid or expired requestState` never says why on the wire. The server log does; `unknown key` means share `RequestStateSecurity(keys=[...])` across workers.
0 commit comments