Skip to content

fix(mcp): send Kiro client-identity headers on web_search MCP call#213

Open
severity1 wants to merge 1 commit into
jwadow:mainfrom
severity1:fix/mcp-websearch-client-identity-headers
Open

fix(mcp): send Kiro client-identity headers on web_search MCP call#213
severity1 wants to merge 1 commit into
jwadow:mainfrom
severity1:fix/mcp-websearch-client-identity-headers

Conversation

@severity1

@severity1 severity1 commented Jun 11, 2026

Copy link
Copy Markdown

Problem

Claude Code's native web_search server-side tool, routed through the gateway to runtime.kiro.dev/mcp, fails with 403 for IAM Identity Center (AWS SSO OIDC / kiro-cli) accounts, even when MCP is enabled on the profile:

{"message":"User is not authorized to make this call.","reason":null}

This 403 is distinct from the profileArn 400 issue (#173, addressed by #180/#189/#175). It surfaces only once profileArn is present: the request is then authenticated, but the endpoint still declines it.

Root Cause

call_kiro_mcp_api() in mcp_tools.py sent only three headers (Authorization, x-amzn-codewhisperer-optout, Content-Type). The completion path (/generateAssistantResponse, via get_kiro_headers in utils.py) sends a full Kiro client-identity set: User-Agent with the KiroIDE-<version>-<fingerprint> signature, x-amz-user-agent, x-amzn-kiro-agent-mode, and the amz-sdk-* pair.

The /mcp endpoint gates authorization on these client-identity signals. A bare Authorization header is rejected with 403 even when the account is entitled. Completions work because they send the full identity; MCP did not.

Fix

Build the MCP headers from the canonical get_kiro_headers (single source of truth, shared with the completion path) and override only the three fields that differ for /mcp:

  • Content-Type: application/json/mcp is JSON-RPC, not the x-amz-json-1.0 completion API
  • drop x-amz-target — completion-operation-specific, meaningless to /mcp
  • x-amzn-codewhisperer-optout: false — preserves the original MCP value

Reusing get_kiro_headers (rather than duplicating the literals) means future KiroIDE version bumps flow to the MCP call automatically instead of silently drifting.

Scope — depends on profileArn contributions

This PR is deliberately surgical: it contains only the novel client-identity-headers fix.

A complete web_search fix also requires profileArn in the MCP request body — without it the endpoint returns 400, before the 403 this PR addresses. That profileArn work is not included here; it is contributed separately by #180 / #189 / #175 (and tracked in #173). This change stacks on top of those: once a profileArn PR merges, this header fix carries the call the rest of the way past the 403.

Credit to those contributors for the profileArn half of the puzzle.

Testing

  • Verified end-to-end: the native web_search tool returns live results through the gateway on an enterprise SSO OIDC (kiro-cli) account, where it previously 403'd (with profileArn applied locally).
  • Added a regression test asserting the MCP request sends the Kiro client-identity headers.
  • tests/unit/test_mcp_tools.py passes (22 tests).

The header change is additive (sends more identity signal, matching the completion path), so it should not regress auth types that already worked.

@cla-bot cla-bot Bot added the cla-signed Contributor License Agreement has been signed label Jun 11, 2026
@severity1 severity1 force-pushed the fix/mcp-websearch-client-identity-headers branch from 5b092c8 to 9f28b7a Compare June 11, 2026 04:41
@severity1 severity1 force-pushed the fix/mcp-websearch-client-identity-headers branch from 9f28b7a to fcfd884 Compare June 11, 2026 04:56
web_search via runtime.kiro.dev/mcp fails with 403 "User is not
authorized to make this call" for IAM Identity Center (AWS SSO OIDC /
kiro-cli) accounts, even when MCP is enabled on the profile.

call_kiro_mcp_api sent only Authorization, x-amzn-codewhisperer-optout,
and Content-Type. The completion path (get_kiro_headers in utils.py)
sends a full Kiro client-identity set: User-Agent with the
KiroIDE-<version>-<fingerprint> signature, x-amz-user-agent,
x-amzn-kiro-agent-mode, and amz-sdk-*. The /mcp endpoint gates
authorization on these signals, so a bare Authorization header is
rejected with 403 even when the account is entitled.

Build the MCP headers from the canonical get_kiro_headers (single source
of truth, shared with the completion path) and override the three fields
that differ for /mcp: Content-Type application/json (JSON-RPC, not
x-amz-json), drop x-amz-target (completion-operation-specific), and set
optout false.

NOTE: a complete web_search fix also requires profileArn in the MCP
request body (else 400, before the 403 this addresses). That is handled
by separate contributions (jwadow#180/jwadow#189/jwadow#175); this PR is scoped to only
the novel client-identity-headers fix and stacks on top of them.

Add a regression test asserting the MCP request sends the Kiro
client-identity headers.
@severity1 severity1 force-pushed the fix/mcp-websearch-client-identity-headers branch from fcfd884 to adc1cb2 Compare June 11, 2026 05:02
@LazarusX

Copy link
Copy Markdown

Hey, I ran into the same issue and combined this PR's header fix with the profileArn fix from #175/#180. One thing I found that neither PR addresses: if auth_manager.profile_arn is empty at call time (e.g. token loaded from cache, no refresh triggered yet), you need to force a refresh to get it from the response. Something like:

profile_arn = auth_manager.profile_arn or PROFILE_ARN or ""
if not profile_arn:
    token = await auth_manager.force_refresh()
    profile_arn = auth_manager.profile_arn or PROFILE_ARN or ""

Without this, accounts that haven't done a token refresh since startup will still hit the 400 even with profileArn code in place. Might be worth adding to this PR or #175 since they stack together anyway.

@severity1

Copy link
Copy Markdown
Author

@LazarusX i think it makes sense to stack it against #175 since it deals with the same parts of the code.

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

Labels

cla-signed Contributor License Agreement has been signed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants