Skip to content

fix(agent): drop rawOutput for non-MCP tool results#2626

Open
K3N4Y wants to merge 3 commits into
PostHog:mainfrom
K3N4Y:posthog-code/truncate-large-tool-output
Open

fix(agent): drop rawOutput for non-MCP tool results#2626
K3N4Y wants to merge 3 commits into
PostHog:mainfrom
K3N4Y:posthog-code/truncate-large-tool-output

Conversation

@K3N4Y

@K3N4Y K3N4Y commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Standard tool results (Read, Bash, ...) render from content, so also copying the full result into rawOutput duplicated large payloads on their way to the renderer. Only MCP tools consume rawOutput (App bridge replay), so emit it solely when an MCP tool result is present.

Problem

When PostHog Code runs standard tools like Read or Bash, the tool result payload was being duplicated: once in content (where the renderer actually reads it) and again in rawOutput. For large file reads or substantial bash output, this doubled the serialized payload sent through the renderer pipeline for no reason. Only MCP tools consume rawOutput — the App bridge replays it to display structured MCP results.

Changes

sdk-to-acp.ts: Conditionally include rawOutput in tool_call_update only when an MCP tool result is present (ctx.mcpToolUseResult). Standard tools now emit status and metadata without the redundant rawOutput payload.
sdk-to-acp.test.ts: Added two tests covering both branches:

  • Standard tools (e.g. Read) omit rawOutput entirely
  • MCP tools (e.g. mcp__srv__do) forward rawOutput with content, structuredContent, and isError
    This is a pure optimization — no behavioral change for consumers.

How did you test this?

Ran pnpm --filter @posthog/agent test — all 690 tests pass, including the 2 new test cases
Ran pnpm biome lint on both changed files — 0 issues
Manually verified the MCP path: rawOutput still carries content, structuredContent, and isError for App bridge replay

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Standard tool results (Read, Bash, ...) render from `content`, so also
copying the full result into `rawOutput` duplicated large payloads on
their way to the renderer. Only MCP tools consume rawOutput (App bridge
replay), so emit it solely when an MCP tool result is present.
@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
packages/agent/src/adapters/claude/conversion/sdk-to-acp.test.ts:310-341
**Prefer parameterised tests here**

The two `it(...)` blocks share identical scaffolding — `registerToolUse`, clearing `updates`, calling `handleUserAssistantMessage`, and calling `toolUpdate` — differing only in the tool name, the presence of `tool_use_result`, and the expected `rawOutput` shape. The team's coding standard asks for parameterised tests wherever the same logical flow is exercised with varied inputs. An `it.each` table that passes `toolName`, `toolUseResult`, and `expectedRawOutput` (nullable) would express this once, making future cases (e.g. a third tool type) a single table row rather than a copied block.

Reviews (1): Last reviewed commit: "fix(agent): drop rawOutput for non-MCP t..." | Re-trigger Greptile

Comment on lines +310 to +341
await registerToolUse(context, "tool_read", "Read", {
file_path: "/test/a.ts",
});
updates.length = 0;
await handleUserAssistantMessage(
userToolResult("tool_read", "line one\nline two"),
context,
);
const update = toolUpdate(updates);
expect(update?.status).toBe("completed");
expect(update && "rawOutput" in update).toBe(false);
});

it("forwards rawOutput for MCP tool results consumed by the App bridge", async () => {
const { context, updates } = createHandlerContext();
await registerToolUse(context, "tool_mcp", "mcp__srv__do");
updates.length = 0;
await handleUserAssistantMessage(
userToolResult("tool_mcp", "ignored", {
content: [{ type: "text", text: "mcp payload" }],
structuredContent: { ok: true },
}),
context,
);
const update = toolUpdate(updates);
expect(update?.status).toBe("completed");
expect(update?.rawOutput).toEqual({
content: [{ type: "text", text: "mcp payload" }],
structuredContent: { ok: true },
isError: false,
});
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Prefer parameterised tests here

The two it(...) blocks share identical scaffolding — registerToolUse, clearing updates, calling handleUserAssistantMessage, and calling toolUpdate — differing only in the tool name, the presence of tool_use_result, and the expected rawOutput shape. The team's coding standard asks for parameterised tests wherever the same logical flow is exercised with varied inputs. An it.each table that passes toolName, toolUseResult, and expectedRawOutput (nullable) would express this once, making future cases (e.g. a third tool type) a single table row rather than a copied block.

Context Used: Do not attempt to comment on incorrect alphabetica... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agent/src/adapters/claude/conversion/sdk-to-acp.test.ts
Line: 310-341

Comment:
**Prefer parameterised tests here**

The two `it(...)` blocks share identical scaffolding — `registerToolUse`, clearing `updates`, calling `handleUserAssistantMessage`, and calling `toolUpdate` — differing only in the tool name, the presence of `tool_use_result`, and the expected `rawOutput` shape. The team's coding standard asks for parameterised tests wherever the same logical flow is exercised with varied inputs. An `it.each` table that passes `toolName`, `toolUseResult`, and `expectedRawOutput` (nullable) would express this once, making future cases (e.g. a third tool type) a single table row rather than a copied block.

**Context Used:** Do not attempt to comment on incorrect alphabetica... ([source](https://app.greptile.com/review/custom-context?memory=instruction-0))

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant