Skip to content

feat(claude-agent-sdk): opt-in multi-block tool-result concatenation - #2637

Open
boyuan-fh wants to merge 1 commit into
ag-ui-protocol:mainfrom
boyuan-fh:fh-10254/pr3-toolresult-multiblock
Open

feat(claude-agent-sdk): opt-in multi-block tool-result concatenation#2637
boyuan-fh wants to merge 1 commit into
ag-ui-protocol:mainfrom
boyuan-fh:fh-10254/pr3-toolresult-multiblock

Conversation

@boyuan-fh

Copy link
Copy Markdown

Fixes #2632

Summary

buildAguiToolMessage reads only content[0], silently dropping everything
after the first block for tools returning multiple content blocks. Rather than
changing the default behavior, this adds a concatenateToolResultBlocks config
option (default false) so callers can opt in.

  • concatenateToolResultBlocks: false (default): exactly the current
    content[0]-only behavior. No behavioral change for existing consumers.
  • concatenateToolResultBlocks: true: iterates all content blocks. Text
    blocks are joined with \n; mixed content (text + non-text blocks) falls
    back to JSON.stringify(content) to avoid silently dropping non-text blocks
    like images.

The JSON round-trip (JSON.stringify(JSON.parse(text))) is preserved in both
paths for parity with the Python sibling adapter. Whether to remove it is an
open question (happy to drop the round-trip in favor of verbatim pass-through if you prefer).

Config option

  • Name: concatenateToolResultBlocks
  • Type: boolean
  • Default: false
  • Location: ClaudeAgentAdapterConfig in src/types.ts
  • Threading: adapter.ts reads this.config.concatenateToolResultBlocks
    and passes it as the third argument to buildAguiToolMessage(toolUseId, resultContent, concatenate).

Test plan

  • New src/utils.buildAguiToolMessage.test.ts with 16 cases in two groups:

    Shared edge cases (independent of flag):

    • Empty content array serialized as "[]"
    • Null content returns empty string
    • Non-array content serialized via JSON.stringify
    • Correct id format (${toolUseId}-result)

    Default (concatenate disabled):

    • Multi-block input yields only block 0
    • Single text block passes through unchanged
    • Non-JSON text passes through without modification
    • Non-text first block falls back to JSON.stringify(content)
    • JSON text is re-serialized (round-trip behavior)

    Opt-in (concatenate enabled):

    • Multi-block text result concatenates all blocks with \n
    • Single text block passes through unchanged
    • Non-JSON text passes through without modification
    • Non-text blocks fall back to JSON.stringify(content)
    • Mixed text + non-text blocks: lossless JSON.stringify(content)
    • Non-text-first mixed content ([image, text]): image survives
    • JSON text is re-serialized (round-trip behavior)

buildAguiToolMessage reads only content[0], dropping later blocks for multi-block tool results. Add a concatenateToolResultBlocks config option (default false) that opts into iterating all blocks; the default preserves the existing content[0] behavior exactly. When enabled, text blocks join with \n and mixed text+non-text content falls back to JSON.stringify(content) so non-text blocks (e.g. images) are not dropped.

Fixes #<issue>
@boyuan-fh
boyuan-fh requested a review from a team as a code owner September 4, 2026 10:24
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.

[Bug]: @ag-ui/claude-agent-sdk: tool results with multiple content blocks are truncated to the first block

1 participant