fix(mcp)!: carry tool problems only as structured content - #2651
Conversation
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
A refusing tool result carried its typed problem record in two places: the canonical renderer wrote a top-level `problem` member, and the rmcp server and the daemon's projectless route then moved it into MCP `structuredContent.problem`. The project-open refusal and the CLI's locally rendered `tracedecay tool --json` output kept the top-level copy, so the CLI retry check and the test helpers had to read both. The renderer and the project-open refusal now write the record only at `structuredContent.problem` (the one member MCP clients can see), the rmcp and projectless post-processing that moved it is deleted, and the CLI exit status and mounting retry read it through one reader. BREAKING CHANGE: a refusing MCP tool result, including `tracedecay tool --json` output, carries its typed problem record only at `structuredContent.problem`; the top-level `problem` member is removed.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b6020dfa17
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "structuredContent".to_string(), | ||
| problem_structured_content(&problem.problem)?, |
There was a problem hiding this comment.
Migrate the remaining raw CLI refusal readers
When an application tool such as diagnostics returns a semantic refusal under --json, this writer now exposes the record only at structuredContent.problem, but the Unix core_cli_suite still has SurfaceOutcome::problem_code reading payload.problem and the tests at tool_surface_transport_test.rs:584 and :642 doing the same. Those deterministic refusal journeys consequently receive null instead of the expected code/detail, leaving the advertised full core CLI suite red; update these consumers to read the new location as part of this cutover.
AGENTS.md reference: AGENTS.md:L178-L180
Useful? React with 👍 / 👎.
b6020df to
976af8b
Compare
* test(daemon): wait on readiness in load-sensitive lib tests Replace test-invented wall-clock bounds and races in daemon and MCP server lib tests with the readiness signal each one is waiting for: armed idle notifications, the production store shutdown before an in-process restart, a fake daemon that stays live, explicit socket shutdown, paused clocks, and the cancellation signal itself. Also read the routed refusal from structuredContent after #2651. * test(daemon): connect TLS egress peers before pausing time Connect the 128 non-reading peers one at a time so none waits out the 5 s request-read deadlines behind the others' handshakes, join the handler barrier on its own readiness, and pause time at the release so only the test's sleeps age the write-idle deadlines. * test(daemon): await the reindexed generation without a wall clock * test(daemon): keep the saturated fake daemon live for the client
Cause
A refusing tool result carried its typed
ApplicationProblemRecordin two places. The canonical renderer (render_application_result) and the project-open reset refusal wrote a top-levelproblemmember. The rmcp server and the daemon's projectlesstools/callroute then moved it into MCPstructuredContent.problemthroughstructure_tool_problem. The CLI's locally renderedtracedecay tool --jsonoutput and the project-open refusal kept the top-level copy. As a result, #2632 had to teach the CLI mounting retry to read both locations,tool_result_process_outcomeread both, and the mcp_suite helpers read both.Change
tracedecay-mcp::tool_errorsnow has one writer,problem_structured_content, and one reader,tool_result_problem. The only location isstructuredContent.problem, the one memberCallToolResultlets MCP clients see.render_application_resultandtool_call_open_refusal_responsewrite only that location.structure_tool_problemand its calls in the rmcpcall_tooland projectlesstools/callroutes are deleted.tool_result_process_outcome) and the one-shot mounting retry (tool_result_retry_after_delay) no longer read two locations; both go throughtool_result_problem.contracts:generatehas nothing to regenerate. The problem record's own schema is unchanged.Consumers migrated
The CLI (
tool_command.rs,core_client.rs) and these tests: mcp_suitesupport.rs(the dual-read helpers), plus feedback_list, type_hierarchy, hook_runtime_request, info_health_request, unsealed_graph, source_edit_reconcile, ast_grep_rewrite, affected_tests_behavior, the daemon lib tests (replay, feedback_impact, socket, project_open_handshake, core_client), configuration_set_behavior, retained_timeout_dispatch, application_surface, edit, core_cli_suite tool_daemon and tool_first_touch, and runtime_acceptance tool_client_transport. The dashboard, the SDK, hooks (runtime_portsreads the text envelope) and the Python sweep (walks every object) read no tool-result placement.Fails on master
daemon::tests::socket::projectless_and_project_route_refusals_place_the_problem_identicallydrives a real projectless refusal over the daemon socket and a real project-route refusal (tool_call_open_refusal_response), then compares where each carries its problem. Ona301a2c1db, with only the test and thepub(super)visibility applied:That run printed the placements as byte vectors:
left: [91, 34, 47, 115, 116, ...]decodes to["/structuredContent/problem"]andright: [91, 34, 47, 112, ...]decodes to["/problem"]. The test now prints strings. With this change it passes: both routes emit["/structuredContent/problem"].daemon::core_client::tests::projectless_mounting_refusal_is_re_sent_after_its_delayrenders the mounting refusal with the production renderer. It assertsstructuredContent.problemcarries diagnosticapplication.runtime.mounting,retry: after_delayandretry_after_millis: 250, with no top-levelproblem, and thatproject_open_retry_waitre-sends after 250ms. A completedauthority-unavailableanswer is not re-sent.Runtime journey
Debug
tracedecaybuilt from this branch, isolatedHOME, one daemon in aMemoryMax=6Gscope:Local verification (after rebasing on 8d4fd5e)
cargo test -p tracedecay --features test-transport,test-helpers --test mcp_suite: 609 passed, 0 failedcargo test -p tracedecay-cli --features test-transport --test core_cli_suite: 169 passed, 0 failedcargo test -p tracedecay-application(before the rebase; no application changes since): lib 482, application_suite 66, pr_tracking 7cargo test -p tracedecay-mcp: lib 388 plus integration targets, all passingtracedecaylib (core_client, project_open_handshake, socket, replay, feedback_impact, retained_timeout_dispatch, application_surface, production_harness, projectless): 64 passedruntime_acceptance_suite -- tool_client_transport: 8 passedcargo clippy -p tracedecay-mcp -p tracedecay -p tracedecay-cli --all-targets --features ... -- -D warnings: clean.cargo fmt --all -- --check: clean.Load flakes seen before the rebase and not caused by this change:
session_search_test::scheduled_session_import_makes_the_final_codex_source_searchableandworkflow_query_test::workflows_query_surface_end_to_endfailed at load average around 150 and passed in isolation on this branch.daemon_sigterm_exits_while_authenticated_project_client_is_connectedis tracked as #2430 and passed on rerun. The post-rebase full runs above are fully green.BREAKING CHANGE: a refusing MCP tool result, including
tracedecay tool --jsonoutput, carries its typed problem record only atstructuredContent.problem; the top-levelproblemmember is removed.