Loading
Log accurate tool_status for Orbit tools on the main MCP server
What does this MR do and why?
Orbit tools on the main MCP server logged tool_status: ok for a failed query_graph, and used coarse reasons for Orbit errors. A failed query now logs its real status, NOT_FOUND logs not_found, a rejected Rails JWT (UNAUTHENTICATED) logs error, and "no enabled namespaces" logs unauthorized. What the agent sees is unchanged.
Related Issues
Closes #634643 (closed)
How to verify
Call query_graph through /api/v4/mcp with a query Orbit rejects and check log/mcp.log: tool_status is no longer ok. The response body is identical to before.
UNAUTHENTICATED maps to error, not unauthorized: for this call it only means Orbit rejected, or never got, the JWT Rails signed, which is a Rails-to-Orbit trust problem rather than a user lacking access.
Agent context
Mcp::Tools::Orbit::CommandToolnow runs theQueryRunneritself and returnsOrbitQuerywith the runnerresponseattached, so the query finishes beforeCallToollogs.render_mcp_resultrenders that stored response through the newrender_orbit_response!; the standalone Orbit endpoints still userender_orbit_query!.QueryRunner::Responsegets anerror_reason, set byMcpRendererfrom the query error code or failure status (viaResponse.reason_for_http_status). The EEfailed_resultoverride turns a failed one into aResponse.error, and a newdiscard_resulthook closes the streamed body ifCallToolraises after the query ran.- For a failed
OrbitQuery,CallToollogs the failure but returns the tool's own result untouched. Hash error results behave as before. - gRPC mapping, checked against
orbit-serverorigin/main(24575c48d):InvokeAgentCommandreturnsNOT_FOUNDfor an unknown command orget_query_dslunder GQL,INVALID_ARGUMENTfor bad parameters,FAILED_PRECONDITIONfor intercepted commands (keptbad_request), andUNAUTHENTICATEDonly from JWT validation (kepterror, see above).PERMISSION_DENIEDis only raised byauthorize_traversal_path, which the traversal-path RPCs call, so there is no mapping for it and it falls toerror. Error text is unchanged. - Limits: a streamed query logs
okonce streaming starts, so a mid-stream failure (already logged byQueryRunner) is not reflected intool_status. A client disconnect (499 response, noerror_reason) also logsok. Fixing both needs deferring the log until the body closes. - The Workhorse
send_orbit_querypath was removed by !263077 (merged), so only theQueryRunnerpath exists. - Exceptional path: an exception from
params_fororQueryRunner.newnow happens inside the tool, so it becomes anisErrortool result ("Tool execution failed: ...") instead of a Grape 500. - The DB connection release now also runs at the start of the stream body, since
CallTool's tail work runs betweenexecuteand the stream. CallTool#failed_resultis overridden in EE forOrbitQuery; CE only treatsisErrorhashes as failures.- Specs run in a Caproni instance:
command_tool_spec, bothcall_tool_specs,orbit/mcp_handlers/call_tool_spec,query_runner_params_for_spec,orbit_tools_spec,orbit/mcp_spec,orbit/data_specand both MCPbase_specs pass.query_runner_specneeds mirrord limited to the DB and Redis ports, since mirrord otherwise breaks its in-process gRPC server; with that, all 53 examples pass. RuboCop is clean. - Labels copied from the issue, minus
automation:quick-win-judged. Milestone 19.6 matches the issue.
Edited by Dmitry Gruzd