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: clarify when Client(raise_exceptions=True) actually raises.
Document that the flag only unsanitises unexpected in-memory handler
crashes (still MCPError, with message/__cause__), leaves tool is_error
results alone, and is ignored for URL/transport clients. Fixes#3287.
Copy file name to clipboardExpand all lines: docs/advanced/low-level-server.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
@@ -72,7 +72,7 @@ The same text the `@mcp.tool()` version produced. Two honest differences:
72
72
MCPError: Internal server error
73
73
```
74
74
75
-
A JSON-RPC error, code `-32603`, with a deliberately generic message: the SDK won't leak your traceback to a remote caller. The model never finds out what it did wrong, so it can't retry. (In a test, `raise_exceptions=True` surfaces the real exception instead; see **[Testing](../get-started/testing.md)**.)
75
+
A JSON-RPC error, code `-32603`, with a deliberately generic message: the SDK won't leak your traceback to a remote caller. The model never finds out what it did wrong, so it can't retry. (In a test, `Client(server, raise_exceptions=True)` keeps the `MCPError` but puts the real message on it and chains the original as `__cause__`; see **[Testing](../get-started/testing.md)**.)
76
76
77
77
That generalises. An exception raised from a low-level handler is **always** a protocol error, never an `is_error=True` tool result. If you want the model to read the failure and recover, validate `params.arguments` yourself and return `CallToolResult(content=[TextContent(...)], is_error=True)`. The two kinds of failure are the subject of **[Handling errors](../servers/handling-errors.md)**.
Copy file name to clipboardExpand all lines: docs/client/index.md
+6-1Lines changed: 6 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -197,7 +197,12 @@ 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 page 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
201
+
effect on in-memory connections (ignored for URL strings and transports). On the modern
202
+
in-process path it does **not** make the original exception raise in place of `MCPError` — it
203
+
unsanitises an unexpected handler crash so the `MCPError` message is `str(original)` and
204
+
`__cause__` is the original. Tool `is_error=True` results are unchanged. **[Testing](../get-started/testing.md)**
*`ExceptionGroup: unhandled errors in a TaskGroup` is never the error. Read the **last line**; catching `MCPError`*inside* the `async with Client(...)` block skips the wrapping entirely.
415
+
* In-memory `MCPError: Internal server error` is a sanitised handler crash; `raise_exceptions=True` unsanitises the message and `__cause__` (see **[Testing](get-started/testing.md)**).
407
416
*`call_tool` does not raise for a failing tool. `Error executing tool ...` and `Unknown tool: ...` are results: check `result.is_error`.
408
417
*`Client must be used within an async context manager` -> use `async with`. `Use @tool() instead of @tool` -> add the parentheses.
409
418
*`Tool already exists:` in the server log is the only sign that two same-named tools collapsed into one.
0 commit comments