Skip to content

fix(providers): pass through a client tool named tool_call on kiro - #18823

Merged
diegosouzapw merged 7 commits into
cheaper-inference:release/v3.8.53from
gaurav0107:fix/18675-kiro-client-tool-named-tool-call
Nov 4, 2026
Merged

diegosouzapw merged 7 commits into
cheaper-inference:release/v3.8.53from
gaurav0107:fix/18675-kiro-client-tool-named-tool-call

Conversation

@gaurav0107

@gaurav0107 gaurav0107 commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • The Kiro executor treated every toolUseEvent named tool_call as its internal MCP wrapper and required input.name / input.arguments. Agent runtimes that declare their own deferred-tool bridge under that name (OpenClaw tool_call {id, args}, Hermes Agent tool_call {calls: [...]}) had the model's call rejected with Invalid Kiro tool_call payload: missing nested MCP tool name at input.name, or kiro_tool_call_repair_retry_failed with the repair flag on.
  • execute() now checks the translated request for a client tool named tool_call (kiroRequestDeclaresToolCallWrapperName, which reads the current message's userInputMessageContext.tools, where openai-to-kiro always puts the tool list). When it is declared, transformEventStreamToSSE sends those events down the normal client-tool path, so the call reaches the client with its arguments unchanged. This skips both validation sites the second reporter pointed out: the buffering branch and flushPendingWrapperToolCalls.
  • Requests that do not declare tool_call are unchanged: wrapper validation and the one-shot repair still apply. The exported validateKiroToolUse gets the same optional clientDeclaresWrapperName switch so it gives the same answer as the stream path.

Related Issues

Target Branch

  • Base is the active release/v3.8.x branch (release/v3.8.53), not main
  • New feature? Not applicable: this is a bug fix

Validation

  • Change type: provider executor
  • Focused tests: the new test file plus every tests/unit/kiro-*.test.ts, executor-kiro, translator-openai-to-kiro and kiroThinking suite
  • eslint (with the repo suppressions file) and prettier --check on the changed files, plus npm run check:open-sse-typecheck
  • Reconciled with the current release/v3.8.53 tip
  • Production-code change includes a new automated test

Without the fix, the new test fails everywhere except the case that confirms the wrapper path still works:

✖ #18675 a client-declared tool_call streams through with the client's arguments
✖ #18675 string-fragment input for a client tool_call is concatenated unchanged
✔ #18675 without a client tool_call the wrapper validation still applies
✖ #18675 execute() detects a declared tool_call in the translated request
✖ #18675 kiroRequestDeclaresToolCallWrapperName reads the current message tool list
✖ #18675 validateKiroToolUse skips wrapper checks only when the client declared tool_call
ℹ tests 6
ℹ pass 1
ℹ fail 5

With the fix:

$ node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit tests/unit/kiro-client-tool-call-name-18675.test.ts
ℹ tests 6
ℹ pass 6
ℹ fail 0

# 53 files: tests/unit/kiro-*.test.ts, executor-kiro, translator-openai-to-kiro, kiroThinking
ℹ tests 391
ℹ pass 391
ℹ fail 0

$ npm run check:open-sse-typecheck
openSseTypecheckErrors=0

$ npm run check:file-size && npm run check:complexity && npm run check:test-discovery
OK / OK / OK

Tests Added Or Updated

  • Changelog fragment: changelog.d/fixes/18823-kiro-client-tool-call-name.md.

  • tests/unit/kiro-client-tool-call-name-18675.test.ts (new):

    • a client-declared tool_call with object input (the OpenClaw {id, args} shape) is emitted as a single tool_call tool call with the client's arguments and finish_reason: "tool_calls";
    • string-fragment input (the Hermes {calls: [...]} shape) is concatenated unchanged;
    • without the declaration, the same payload still fails with invalid_kiro_tool_call;
    • execute() with the repair flag on picks up the declaration from the translated body, sends no repair retry, and streams the call through;
    • unit cases for kiroRequestDeclaresToolCallWrapperName (declared, not declared, no tools, malformed) and validateKiroToolUse with and without the option.

Coverage Notes

  • Every changed line in open-sse/executors/kiro.ts and open-sse/executors/kiroToolCallValidation.ts is exercised by the new test file. The existing wrapper and repair suites (kiro-tool-call-validation, kiro-tool-call-repair) cover the unchanged default path.

Reviewer Notes

  • The fix decides once per request, from the declared tools, and does not guess from each event's input shape. The workaround in the issue comment (skip validation when the input has neither name nor arguments) would still break a client schema that happens to contain a name key, and it would also let genuinely malformed wrappers through on requests that never declared tool_call.

  • When a client declares tool_call, Kiro wrapper validation is off for that request. The name is ambiguous at that point anyway, and the client is the one that declared the tool, so its schema wins.

  • Edge case: when a request carries tool-call history but no tools, openai-to-kiro synthesizes a minimal schema from the tool names in history. If one of those names is tool_call, the request counts as declaring it and wrapper validation is skipped for that turn. A wrapper call that arrives malformed would then reach the client as-is instead of ending the stream with invalid_kiro_tool_call. I left this alone because the synthesized schema can't be told apart from a real client declaration, and passing the call through does no harm. Happy to narrow it if you'd prefer.

The Kiro executor treated every toolUseEvent named "tool_call" as its MCP
wrapper and required input.name / input.arguments. Agent runtimes that
declare their own deferred-tool bridge under that name (OpenClaw, Hermes
Agent) had every such call rejected with "missing nested MCP tool name".

execute() now checks whether the translated request declares a client tool
named tool_call and, if so, the stream transform handles those events as
ordinary tool calls with their arguments unchanged. Requests that do not
declare it keep the wrapper validation and the one-shot repair.

Closes cheaper-inference#18675
@gaurav0107
gaurav0107 marked this pull request as ready for review November 3, 2026 06:32
gaurav0107 and others added 3 commits November 3, 2026 04:13
…he complexity ratchet

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
Summary

Summary by CodeRabbit

  • Bug Fixes
    • Kiro now correctly handles client-declared tool_call tools, passing their arguments through unchanged instead of rejecting them as malformed.
    • Wrapper validation continues to apply when the client has not declared a tool with that name.

Kiro now checks whether the request declares a client tool named tool_call. When it does, Kiro passes matching tool-use events through as client tool calls rather than treating them as MCP wrapper calls.

Changes: Kiro tool-call handling

Layer / File(s) Summary
Detect declared tool names
open-sse/executors/kiroToolCallValidation.ts, tests/unit/kiro-client-tool-call-name-18675.test.ts
A request helper checks the current message’s tool list for a trimmed tool_call name. Wrapper input validation skips validation only when the client declares that name. Tests cover declared, absent, and malformed tool lists.
Route declared tool calls
open-sse/executors/kiro.ts, tests/unit/kiro-client-tool-call-name-18675.test.ts, changelog.d/fixes/18823-kiro-client-tool-call-name.md
KiroExecutor.execute passes the declaration flag to both SSE transforms. The transform uses the direct tool-call path for a declared tool_call. Tests cover streamed arguments, repair retries, and undeclared wrapper validation. The changelog records the change.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: Low · previous review 26fcc

Requests with no declared tools but a historical tool_call can pass a malformed wrapper to the client. This bounded edge case warrants a fix or explicit acceptance before merge.

Security architecture risk: Low · up to 26fcc

The change is narrowly scoped to tool-call interpretation. However, a tool name recovered from conversation history can disable wrapper checks even without an explicit current tool declaration. No new server-side tool execution authority was identified; consequences for client-side execution remain unverified.

Retained concerns

  • Low · architecture · observed: History-derived tool specifications also disable wrapper validation. When tools are omitted or empty, existing translation synthesizes specifications from historical assistant tool names. A historical tool_call therefore satisfies the new declaration predicate, allowing subsequent tool_call events to bypass wrapper validation and repair without an explicit current tool declaration. This changes the client handoff contract beyond the intended name-collision exception. Privileged downstream execution is not established.
Security review details

Security Blast Radius

  • inferred — The demonstrated effect is request-local interpretation and forwarding of Kiro tool events. A requester able to supply assistant history can satisfy the new predicate through a historical tool_call name. Broader asset access depends on downstream tool execution and authorization, which were not established.

Security Findings and Attack Paths

  • inferred — The supported control-change path is historical assistant tool name to synthesized current specification to wrapper-validation exemption to client-visible provider arguments. This establishes a provenance and contract gap, not a verified privileged-execution attack.

Trust Boundaries and Controls

  • observed — History synthesis predates this PR, but its use to disable wrapper validation is new. The detector checks only the resulting tool name and retains no distinction between an explicit client declaration and a specification reconstructed from assistant history.

Resilience and Maintainability Implications

  • observed — Repair does not replay a response after meaningful output. A second malformed pre-output attempt terminates with a repair error rather than entering another retry, limiting replay-related failure propagation.

Hardening Proposals

  • proposed — Preserve explicit-versus-synthesized declaration provenance and use explicit client declarations to select the wrapper exception, while retaining history synthesis for upstream compatibility.
Pre-merge checks: Passed 5 · Failed 1 · Inconclusive 1

Failed Failed checks: 1 warning · 1 inconclusive

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
No Ai Attribution Inconclusive The reviewed commits contain no AI or bot attribution. The only Co-authored-by trailer names the human contributor diegosouzapw. The supplied PR body is explicitly truncated, and the full body is no… Provide the complete PR body or event payload, then check it for AI/bot Co-Authored-By trailers and Generated with <AI tool> footers.
Passed Passed checks: 5 passed
Check name Status Explanation
Title check Passed The title uses the required Conventional Commits format: fix(providers): subject. It clearly describes the Kiro client tool_call pass-through change.
Description check Passed The description includes the required summary, related issues, target branch, validation results, tests, coverage notes, and reviewer notes. It provides specific implementation details and documents t…
Linked Issues check Passed The PR satisfies the coding requirement in #18675. kiroRequestDeclaresToolCallWrapperName detects tool_call in the translated request's current-message tool list. The stream path skips MCP-wrapper…
Out of Scope Changes check Passed The reported changes stay within #18675. They modify Kiro request detection, tool-call validation, stream transformation, repair-path behavior, and focused automated tests. The changelog entry documen…
Tests For Production Code Passed The PR changes production code under open-sse/ in kiro.ts and kiroToolCallValidation.ts. It adds tests/unit/kiro-client-tool-call-name-18675.test.ts with six focused tests covering the new beh…
Full details: Description check

Explanation

The description includes the required summary, related issues, target branch, validation results, tests, coverage notes, and reviewer notes. It provides specific implementation details and documents the known edge case.

Full details: Linked Issues check

Explanation

The PR satisfies the coding requirement in #18675. kiroRequestDeclaresToolCallWrapperName detects tool_call in the translated request's current-message tool list. The stream path skips MCP-wrapper validation when that client tool is declared. It preserves client arguments for object input and string fragments. Requests without the declaration retain wrapper validation. execute() passes the declaration state into the stream and repair paths. validateKiroToolUse exposes the matching option. The added tests cover both paths and request detection. #18303 is a related issue, not a directly linked target, and the PR does not need to implement its separate behavior.

Full details: Out of Scope Changes check

Explanation

The reported changes stay within #18675. They modify Kiro request detection, tool-call validation, stream transformation, repair-path behavior, and focused automated tests. The changelog entry documents the fix. No unrelated product behavior is identified. The noted synthesized-tool-list edge case remains part of the same declaration-detection behavior.

Full details: Tests For Production Code

Explanation

The PR changes production code under open-sse/ in kiro.ts and kiroToolCallValidation.ts. It adds tests/unit/kiro-client-tool-call-name-18675.test.ts with six focused tests covering the new behavior and retained validation paths.

Full details: Docstring Coverage

Explanation

Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 3 functions across 3 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Full details: No Ai Attribution

Explanation

The reviewed commits contain no AI or bot attribution. The only Co-authored-by trailer names the human contributor diegosouzapw. The supplied PR body is explicitly truncated, and the full body is not available in the checkout, so a prohibited footer in the omitted text cannot be ruled out.

Resolution

Provide the complete PR body or event payload, then check it for AI/bot Co-Authored-By trailers and Generated with &lt;AI tool&gt; footers.

Finishing Touches
Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 090f889 and 26fccb0.

Files selected for processing: 4
  • changelog.d/fixes/18823-kiro-client-tool-call-name.md
  • open-sse/executors/kiro.ts
  • open-sse/executors/kiroToolCallValidation.ts
  • tests/unit/kiro-client-tool-call-name-18675.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 4 remain after this review.

@coderabbitai coderabbitai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

  • Fix comments: Fix CodeRabbit comments on this PR

gaurav0107 and others added 2 commits November 3, 2026 18:25
… declaration

When a request omits `tools`, buildKiroPayload synthesizes tool specs from
history. A synthesized `tool_call` entry was read as a client declaration,
which skipped the MCP wrapper validation and the one-shot repair stream.

buildKiroPayload now records synthesized names in an in-memory
`_kiroSynthesizedToolNames` Map (dropped by the executor allowlist), and
kiroRequestDeclaresToolCallWrapperName ignores them. The executor reads the
pre-allowlist body so the marker is visible. Adds regression tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@diegosouzapw
diegosouzapw merged commit 4bf2a97 into cheaper-inference:release/v3.8.53 Nov 4, 2026
3 of 37 checks passed
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.

fix(providers): Kiro executor treats any client tool named "tool_call" as its MCP wrapper and fails the request

2 participants