What
Field-testing the cc-explorer tools (observed across several live agent runs) shows a recurring, self-correcting failure at the entry to almost every tool: agents call with the wrong parameter name/shape, eat a pydantic validation error, and retry 1–3 times before the call sticks. Nothing breaks — the verbose errors do teach the right key — but every interrogation/search/forensics call tends to start with one or more wasted round-trips (tokens, latency, and context burned on guess-and-retry).
The guesses are not random. They cluster into a few systemic mismatches between the model's prior and our actual signatures, and several are made worse by the tool surface using different names for the same concept across tools.
Observed failure families (synthetic repros)
-
Singular string where the signature wants a list. Most frequent.
search_projects({patterns: "rsync delta"})-style call written as grep_session({pattern: "a|b|c"}) — singular pattern, plus pipe-OR that the description explicitly discourages.
get_agent_detail({agent_id: "aXXXX"}) instead of agent_ids: ["aXXXX"].
The model's prior is "search takes a string." Descriptions already say "use a list" and the guess still happens — signal that the type, not more prose, is the lever.
-
No canonical id-param name across the surface. The same concept is spelled three ways:
grep_session/browse_session → session
list_session_agents → also session, but agents repeatedly guess session_id
convert_session → src_id
The on-disk JSONL field is sessionId, so session_id is the model's natural guess — and it is rejected everywhere. Observed: list_session_agents({session_id: ...}) and convert_session({session_id: ...}) / convert_session({session: ..., project: ...}).
-
Vocabulary bleed from ToolSearch. Agents who just called ToolSearch({query, max_results}) carry that exact vocabulary into search_projects:
search_projects({query: "..."}) → should be patterns
search_projects({patterns: [...], max_results: 8}) → max_results is not a param here (the cap lives on grep_session.limit, not on search_projects).
-
Affordance gaps surfaced by introspection guesses.
convert_session({src_id: ..., direction: "help"}) — an agent literally passed "help" to probe the enum; the two valid values and "which direction do I want" aren't legible at call time. (Worth noting: in observed usage session_to_subagent is effectively the only direction used at the entry point.)
get_activity_timeline({window: "48h"}) / ({hours: "48"}) — there is no relative-time param, but "last 48h" is the natural ask; took several guesses before falling back to absolute after/before.
Why this is one issue, not six
These are facets of one product-UX problem: parameter naming/typing across the cc-explorer tool surface isn't consistent with each other or with the model's strong priors (and with the adjacent ToolSearch surface). Worth treating as a single surface-wide consistency pass rather than patching each call site.
Breadcrumbs
Not prescribing the API here — capturing the problem + repros so the fix can be designed against the full set.
What
Field-testing the cc-explorer tools (observed across several live agent runs) shows a recurring, self-correcting failure at the entry to almost every tool: agents call with the wrong parameter name/shape, eat a pydantic validation error, and retry 1–3 times before the call sticks. Nothing breaks — the verbose errors do teach the right key — but every interrogation/search/forensics call tends to start with one or more wasted round-trips (tokens, latency, and context burned on guess-and-retry).
The guesses are not random. They cluster into a few systemic mismatches between the model's prior and our actual signatures, and several are made worse by the tool surface using different names for the same concept across tools.
Observed failure families (synthetic repros)
Singular string where the signature wants a list. Most frequent.
search_projects({patterns: "rsync delta"})-style call written asgrep_session({pattern: "a|b|c"})— singularpattern, plus pipe-OR that the description explicitly discourages.get_agent_detail({agent_id: "aXXXX"})instead ofagent_ids: ["aXXXX"].The model's prior is "search takes a string." Descriptions already say "use a list" and the guess still happens — signal that the type, not more prose, is the lever.
No canonical id-param name across the surface. The same concept is spelled three ways:
grep_session/browse_session→sessionlist_session_agents→ alsosession, but agents repeatedly guesssession_idconvert_session→src_idThe on-disk JSONL field is
sessionId, sosession_idis the model's natural guess — and it is rejected everywhere. Observed:list_session_agents({session_id: ...})andconvert_session({session_id: ...})/convert_session({session: ..., project: ...}).Vocabulary bleed from ToolSearch. Agents who just called
ToolSearch({query, max_results})carry that exact vocabulary intosearch_projects:search_projects({query: "..."})→ should bepatternssearch_projects({patterns: [...], max_results: 8})→max_resultsis not a param here (the cap lives ongrep_session.limit, not onsearch_projects).Affordance gaps surfaced by introspection guesses.
convert_session({src_id: ..., direction: "help"})— an agent literally passed"help"to probe the enum; the two valid values and "which direction do I want" aren't legible at call time. (Worth noting: in observed usagesession_to_subagentis effectively the only direction used at the entry point.)get_activity_timeline({window: "48h"})/({hours: "48"})— there is no relative-time param, but "last 48h" is the natural ask; took several guesses before falling back to absoluteafter/before.Why this is one issue, not six
These are facets of one product-UX problem: parameter naming/typing across the cc-explorer tool surface isn't consistent with each other or with the model's strong priors (and with the adjacent ToolSearch surface). Worth treating as a single surface-wide consistency pass rather than patching each call site.
Breadcrumbs
project-mining/src/cc_explorer/mcp_server.pyconversion.py) — note this is not yet onmain(main/this repo tip is 2.43.0); the fix should target wherever 2.44.0 lands.Not prescribing the API here — capturing the problem + repros so the fix can be designed against the full set.