Surface MCP tool-discovery failures instead of running tool-less and silent - #434
Merged
Conversation
A failed tools/list was rescued to [] and logged as a warning, so a server that 401s and a server that legitimately serves no tools were indistinguishable. The agent then ran tool-less, the model fabricated an answer, and the run reported a plausible low score — #425 records that costing hours to diagnose, because the report recommended prompt changes for what was a transport failure. Contributing nothing still keeps the run alive; that part was deliberate and is unchanged. What was missing is the reason, so callers can tell the two cases apart: - discovery_errors returns why each declared server contributed no tools, naming the server, its url and the underlying error. - all_servers_failed? reports the case where nothing could be discovered at all, which is when a tool-backed scenario cannot produce a meaningful score and a caller should fail loudly rather than grade an invented answer. Errors reset per tool_definitions call, so a transient failure does not leak into a later run. This takes the approach from the unmerged #421 (MCPToolBinding's @discovery_errors), narrowed to the dispatcher on current main rather than rebasing that branch across 54 commits of divergence. Surfacing these in the evaluation report is the follow-up, and is what turns "improve the instructions" back into "sparkle-diagnostic returned 401". Filed as the scoring-side counterpart in #433. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01He34kWksjpqPPDpCCqJq2C
TonsOfFun
force-pushed
the
fix/mcp-discovery-errors
branch
from
September 11, 2026 02:30
bcdd6b0 to
321c314
Compare
…real tool roster Builds on the dispatcher change: recording why a server failed is only useful if something reads it. Two fixes to the scenario evaluation runner: **The run now says when discovery failed.** A run whose declared MCP servers could not be reached scored an agent with no tools to call — the model answered from its own weights, so the scores are real but meaningless. error_message now carries that, naming each server and its error, and distinguishes "every server failed" from "some failed". The column is already rendered by the evaluations API (:362), so it reaches the UI without new plumbing. **The diagnosis roster was understated.** tool_roster listed only AgentToolbox definitions, so an agent whose tools come from MCP servers was diagnosed against a roster those tools were missing from — Diagnosis would report "none of the available tools covers this task" while naming an incomplete list. It now includes the dispatcher's discovered tools, matching what AgentExecutionService#tool_schemas actually offers the provider. 347 runs, 0 failures. Both new tests verified to fail without the change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01He34kWksjpqPPDpCCqJq2C
This was referenced Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #425.
MCPToolDispatcher#tool_definitionsrescued a failedtools/listto[]and logged a warning, so a server that 401s and a server that legitimately serves no tools were indistinguishable. The agent then ran tool-less, the model fabricated an answer, and the run reported a plausible low score.#425 records that costing several hours to diagnose, because the report offered "improve the instructions" recommendations for what was a transport failure. The only evidence was two
logger.warnlines.What changed
Contributing nothing still keeps the run alive — that part was deliberate and is unchanged. What was missing is the reason:
discovery_errors— why each declared server contributed no tools, naming the server, its URL and the underlying error:all_servers_failed?— true when every declared HTTP server failed discovery. That is the case where a tool-backed scenario cannot produce a meaningful score, so a caller can fail loudly instead of grading an invented answer.Errors reset per
tool_definitionscall, so a transient failure does not leak into a later run. An agent declaring no servers is not "failed" — its tool-less execution is the configured behaviour.Relationship to #421
This takes the approach from the unmerged #421 (
MCPToolBinding's@discovery_errors), narrowed to the dispatcher on currentmain. #421 isCONFLICTING/DIRTYand 54 commits behind, and #420 already solved execution a different way — so rebasing it wholesale was not worth it, while this idea was.Tests
5 new tests, all verified to fail without the change (
NoMethodError: undefined method 'discovery_errors').actionagent/test: 345 runs, 0 failures (was 340)test/evals: 115 runs, 0 failuresFollow-up
Surfacing these in the evaluation report is what turns "improve the instructions" back into "records returned 401". The scoring-side counterpart — that a fabricated answer raises no fault at all, so the judge is never asked for a tool — is #433.
🤖 Generated with Claude Code
https://claude.ai/code/session_01He34kWksjpqPPDpCCqJq2C
Update — second commit
093f8b4bmakes something actually read the recorded errors, and fixes a related understatement:The run now says when discovery failed. A run whose declared MCP servers could not be reached scored an agent with no tools to call — the model answered from its own weights, so the scores are real but meaningless.
error_messagenow carries that, naming each server and its error, and distinguishes every server failing from some. The column is already rendered by the evaluations API (:362), so it reaches the UI without new plumbing.The diagnosis roster was understated.
tool_rosterlisted onlyAgentToolboxdefinitions, so an agent whose tools come from MCP servers was diagnosed against a roster those tools were missing from —Diagnosiswould report "none of the available tools covers this task" while naming an incomplete list. It now includes the dispatcher's discovered tools, matching whatAgentExecutionService#tool_schemasoffers the provider.Tests:
actionagent/test347 runs, 0 failures (main: 340). All new tests verified to fail without their implementation. RuboCop clean.