MCP client: let tool errors reach agent error handling, and keep structured-only results
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 25/100
- Issue type
- Feature
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- python
- Domain
- ai-infra-agents
Research direction
Start with the mcp_client and per_user_mcp_client function groups, MCPToolClient.acall(), and convert_to_mcp_error, which the issue says turns tool error text into MCPAuthenticationError. Reproduce with a stdio server whose tool returns isError: true, and check how tool_call_max_retries and pass_tool_call_errors_to_agent respond. Done means an opt-in raise_on_tool_error path, a MCPToolError type, and the default behaviour unchanged; first settle the three open questions with maintainers.
Written by the indexing model from the issue text.
Description
Is this a new feature, an improvement, or a change to existing functionality?
Improvement
How would you describe the priority of this feature request
Medium
Please provide a clear description of problem this feature solves
When an MCP tool fails, the mcp_client function group returns the error message as normal tool output. This includes results the server marks with isError: true. Code downstream of the tool can't tell a failed call from a successful one, so:
- ReAct
tool_call_max_retriesnever retries a failed MCP tool call. - ReAct
pass_tool_call_errors_to_agent: falsecannot stop the run.
I reproduced this with a local stdio FastMCP server and a react_agent workflow that has pass_tool_call_errors_to_agent: false. The tool raised Order ORD-404 does not exist:
- With
nvidia/nemotron-3-super-120b-a12b, the run did not stop. The model read the error text and answered correctly, but the agent's error setting was ignored. - With a scripted LLM that ignores tool output, the run also did not stop, and the final answer was "Your order ORD-404 has shipped!". Nothing in the agent or the trace marked the tool call as failed.
In both cases, the function's FUNCTION_END step records the error text as a normal output.
A second issue: if a tool returns only structuredContent with no text block, MCPToolClient.acall() returns "" and the result is lost. The spec allows this, because mirroring structured content in a text block is a SHOULD. The ReAct agent then reports "The tool provided an empty response."
I understand that returning errors as text was intentional (#840): the LLM gets useful information instead of a hang. So I'm proposing an opt-in setting rather than a change to the default.
Describe your ideal solution
- A
raise_on_tool_error: bool = Falseoption onmcp_clientandper_user_mcp_client. When it's true, failed tool calls raise instead of returning the error text. That coversisErrorresults, timeouts, and connection errors. The default keeps returning the same text as today.langchain-mcp-adaptershas a similar option,handle_tool_errors. - A dedicated
MCPToolError(MCPError)forisErrorresults. Today the tool's own error text goes throughconvert_to_mcp_error, which matches on the message. So a tool error such as "GitHub API returned 403 Forbidden" becomes anMCPAuthenticationErrorsaying authentication with the MCP server failed. - When a result has no text content, return
structuredContentserialized as JSON instead of"". - Keep the server URL out of the exception message, because agents pass the message to the LLM and a URL can contain credentials. The URL stays on the exception as
.url.
function_groups:
orders:
_type: mcp_client
server: {transport: streamable-http, url: "http://localhost:9901/mcp"}
raise_on_tool_error: true
I have a working implementation with unit tests and docs, and I'll open a draft PR linked to this issue so the design can be discussed alongside the code.
Additional context
- Related: #2138 (closed as stale), which reported that the client exposes only text and drops
structuredContentandisError. This proposal covers only the client-side result handling from that issue. - The CLI path already treats
isErroras an error:nat mcp client tool callraises (cli/commands.py). - Retrying a failed tool call runs the tool again with the same arguments. That may be unwanted for tools with side effects, so I documented it next to the option. Telling retryable failures (timeouts, connection errors) apart from tool errors could be a follow-up.
- The MCP SDK already validates
structuredContentagainst the tool'soutputSchemainClientSession.call_tool, so the client doesn't need to validate it again.
Questions for maintainers:
- Should
raise_on_tool_errordefault tofalse(no behavior change) ortrue? - Should the session wrapper's own failure returns ("Tool temporarily unavailable. Try again.", "User not authorized to call the tool") also raise when the option is on? My implementation raises them as
MCPErrorwith theCONNECTIONandAUTHENTICATIONcategories. - Is returning typed output based on
outputSchemaworth a follow-up, or should the client stay text-only?
Code of Conduct
- I agree to follow this project's Code of Conduct
- I have searched the open feature requests and have found no duplicates for this feature request
- Dominant language
- Python
- Stars
- 2.7k
- Forks
- 814
- Avg merge
- 1d 7h
- Merged PRs (30d)
- 131
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from NVIDIA/NeMo-Agent-Toolkit
-
Alert triage agent: the HTTP server (run.py) starts every triage with a config file that does not existPossibly taken @trackoor claimed this 1 day ago. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
NVIDIA/NeMo-Agent-Toolkit#2536 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
NVIDIA/NeMo-Agent-Toolkit#2534 · 1 comment ·
Maintainers usually reply within 1 day
-
[BUG]: HuggingFace embedder remote mode fails to initialize (endpoint_url rejected as model and timeout dropped)Possibly taken @Bhumit267 claimed this today. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 25/100
NVIDIA/NeMo-Agent-Toolkit#2541 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 45/100
NVIDIA/NeMo-Agent-Toolkit#2539 ·
Maintainers usually reply within 1 day
-
[FEA] Add a Future AGI OTLP telemetry exporter (futureagi)Possibly taken @nik13 claimed this 4 days ago. Open
NVIDIA/NeMo-Agent-Toolkit#2489 · 1 comment · 1 assignee ·
Maintainers usually reply within 1 day
All issues in NVIDIA/NeMo-Agent-Toolkit
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
enhancement good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
elviskahoro/sdk-python-attio#144 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Maintainers usually reply within 1 day
-
RenyiELBO raises TypeError when model has no sample sitesPossibly taken @alibb007 claimed this today. Open
Difficulty 1/5 Under an hour Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
[Bug]: Four evaluation scripts unpack init_memmachine_params's five return values into three namesOpen
Difficulty 1/5 Under an hour Newbie friendliness 88/100
MemMachine/MemMachine#1911 ·
Maintainers usually reply within 3 days