|
31 | 31 | from mcp.server import MCPServer |
32 | 32 | from mcp.server.auth.settings import AuthSettings |
33 | 33 | from mcp.server.mcpserver import Context |
34 | | -from mcp.server.mcpserver.exceptions import ResourceNotFoundError |
| 34 | +from mcp.server.mcpserver.exceptions import ResourceNotFoundError, ToolError |
35 | 35 | from mcp.types import ( |
36 | 36 | CallToolResult, |
37 | 37 | Completion, |
@@ -551,7 +551,7 @@ def _redact(text: str) -> str: |
551 | 551 | def _classify_exception(exc: Exception, *, transport: Literal["stdio", "http"]) -> str: |
552 | 552 | """The one place an exception becomes a stable, transport-redacted |
553 | 553 | string — shared by ``_call_and_wrap_errors`` (below, which wraps it in |
554 | | - ``RuntimeError`` for a single-item tool call) and phase 4's |
| 554 | + ``ToolError`` for a single-item tool call) and phase 4's |
555 | 555 | ``_run_batch_item`` (which stores it directly as |
556 | 556 | ``BatchItemOutput.error``, never raising it at all — per-item |
557 | 557 | isolation, ``docs/mcp-server/mcp-server-phase4-batch-progress/ |
@@ -582,27 +582,37 @@ async def _call_and_wrap_errors( |
582 | 582 | """The one place every single-item tool call's exceptions are |
583 | 583 | classified and formatted — not a decorator (see |
584 | 584 | ``_run_convert_tool``'s docstring). Refigure's typed exceptions become |
585 | | - ``RuntimeError("ClassName: message")`` so a calling agent can branch |
586 | | - on the class name, mirroring ``cli.py``'s ``_exit_code_for`` — |
587 | | - ``MCPServer``'s own ``@mcp.tool()`` wrapper catches a plain exception |
588 | | - raised here and reports it as ``isError=True`` with the exception text |
589 | | - as content (confirmed against this ``mcp`` version's docs), so raising |
590 | | - is sufficient, no manual ``CallToolResult`` construction needed. |
| 585 | + ``ToolError("ClassName: message")`` so a calling agent can branch on |
| 586 | + the class name, mirroring ``cli.py``'s ``_exit_code_for``. |
| 587 | +
|
| 588 | + Raised as the SDK's own ``mcp.server.mcpserver.exceptions.ToolError``, |
| 589 | + not a bare ``RuntimeError``/``ValueError`` — as of ``mcp`` 2.1 (this |
| 590 | + project's own floor is ``mcp>=2.0,<3``, so both must work), |
| 591 | + ``Tool.run()`` only keeps an exception's message visible to the |
| 592 | + caller for ``ToolError``/``ResourceError``; anything else is treated |
| 593 | + as a crash and collapses to a bare ``"Error executing tool <name>"`` |
| 594 | + with no detail (``UnexpectedToolError`` — confirmed live: this is |
| 595 | + exactly what broke every test in this module on the mcp 2.0.0->2.1.1 |
| 596 | + bump). ``ToolError`` existed already in 2.0.0 too, where it changes |
| 597 | + nothing — that version's ``Tool.run()`` wrapped every exception |
| 598 | + uniformly regardless of type. Same discipline this project's own |
| 599 | + resource-template handler already follows via ``ResourceNotFoundError`` |
| 600 | + (see this module's other exceptions import) — now extended to tools. |
591 | 601 | ``transport`` is threaded through already, unused on the only |
592 | 602 | transport phase 1 had (``"stdio"``) — phase 3 extended this for HTTP, |
593 | 603 | phase 4 extends it a third time for ``convert_batch`` (via |
594 | 604 | ``_classify_exception``, shared below), never rewriting it, per |
595 | 605 | architecture doc §8.""" |
596 | 606 | try: |
597 | 607 | return await coro |
598 | | - except ValueError: |
| 608 | + except ValueError as exc: |
599 | 609 | # Input-validation failures from _run_convert_tool itself — already |
600 | 610 | # a clean, stable message (see _classify_exception's own |
601 | 611 | # docstring for why this case is excluded from it here), nothing |
602 | 612 | # further to classify. |
603 | | - raise |
| 613 | + raise ToolError(str(exc)) from exc |
604 | 614 | except Exception as exc: # noqa: BLE001 - unexpected exceptions are the real-bug signal, see _classify_exception |
605 | | - raise RuntimeError(_classify_exception(exc, transport=transport)) from exc |
| 615 | + raise ToolError(_classify_exception(exc, transport=transport)) from exc |
606 | 616 |
|
607 | 617 |
|
608 | 618 | def _register_convert_docx( |
|
0 commit comments