-
Notifications
You must be signed in to change notification settings - Fork 3
plat 225
| Coordination | Value |
|---|---|
| Assigned agent | Claude Code |
| Ticket state | implemented; runtime reverify |
| Last synchronized | 2026-08-29 |
-
Priority: P1 — Sales Outreach
PUL-AAC278EF(per the 2026-08-29 triage audit). -
Owner:
github.com/manishiitg/mcpagent,cmd/mcpbridge/main.go— a sibling repository (replace github.com/manishiitg/mcpagent => ../../mcpagentinagent_go/go.mod), same cross-repo pattern as PLAT-193. -
Related: PLAT-193 (
toolerrmisclassifyingexecute_shell_commandsuccess as failure). This ticket is the opposite direction: a genuine failure correctly detected, but its actual output thrown away in the process.
execute_shell_command on a chained script (e.g. ls | grep ... && curl ...) whose trailing command returned nonzero surfaced only a bare
ERROR: tool execution failed: exit_code=1 / permission denial in stderr
/ http_status=404 — with no captured stdout at all, even though earlier
commands in the chain had run successfully and produced real, useful
output. The agent (and, reproduced first-hand by this Pulse reviewer's own
calls in the same session) had to retry blind to see what had actually
happened, wasting tool calls, tokens, and turns, and raising the risk of a
repair attempt aimed at the wrong cause.
agent_go/pkg/workspace/tools.go's execute_shell_command executor
already forwards the full result (including Stdout/Stderr/ExitCode)
regardless of exit code — it never itself returns a Go error for a nonzero
exit. The actual discard happened one layer further out, in mcpbridge's
stdio tool-call handler (cmd/mcpbridge/main.go), which receives the
custom-tool HTTP response as {success, result, error}:
if !result.Success {
errorMsg := result.Error
...
return mcp.NewToolResultText(fmt.Sprintf("ERROR: %s", ...)), nil
}result.Result — the same tool output the caller would have received on
success — was never included when Success == false. It is not an
alternate success-only payload; for execute_shell_command it is the
captured stdout/stderr/exit_code JSON regardless of outcome. Discarding it
here is what produced the bare, contentless error text.
Append result.Result (bounded and offloaded to tool_output_folder the
same way a successful result is, via the existing prepareBridgeToolResult)
after the error text whenever it is non-empty. No behavior change when it is
empty. The error text stays regular content (not MCP IsError), matching
this file's existing rationale: some CLI providers (Claude Code among them)
hide content behind a generic message when IsError=true.
go test ./cmd/mcpbridge/... ./toolerr/... ./executor/... (mcpagent)
go build ./...; go test ./cmd/server/... (agent_go, via the local replace directive)
New TestMCPBridgePreservesShellOutputOnToolFailureE2E exercises the
complete production transport — a real mcpbridge subprocess over stdio,
forwarding to a real HTTP server, no helper called directly — with a
fixture shaped exactly like the finding's own reproduction (a chain whose
earlier commands produced real stdout before a trailing nonzero exit), and
asserts the returned text carries both the error signal and the actual
captured output. Full existing suites pass unchanged in both repositories.
No live agent turn has hit this corrected path through the deployed server
yet. Reverify by reproducing a chained execute_shell_command script with a
failing trailing command and confirming the captured stdout from the
earlier commands is now visible alongside the error.
Auto-synced from docs/ on main. Edit there, not here.