anthropics / anthropics/claude-agent-sdk-python
[FEATURE] Native Bedrock Guardrail body wrapping for InvokeModel / InvokeModelWithResponseStream
- Dominant language
- Python
- Stars
- 8.1k
- Forks
- 1.3k
- Avg merge
- 2d 31m
- Merged PRs (30d)
- 1
Description
## Summary
When using `claude-agent-sdk` with Amazon Bedrock and a Bedrock Guardrail
attached via the `X-Amzn-Bedrock-GuardrailIdentifier` /
`X-Amzn-Bedrock-GuardrailVersion` headers (e.g. injected through
`ANTHROPIC_CUSTOM_HEADERS`), every `InvokeModel` /
`InvokeModelWithResponseStream` call fails closed with:
HTTP 400 Guardrail was enabled but input is in incorrect format.
The model is never invoked, the guardrail never evaluates, no usage is
billed beyond the round-trip, and no `guardrailTrace` event is produced —
so guardrails can be neither **enforced** nor **observed** while routing
through this SDK against Bedrock.
Reproduced on `claude-agent-sdk==0.2.87` + `us.anthropic.claude-opus-4-7`
on Bedrock `us-east-1`, both single-agent and multi-agent (sub-agent /
A2A) configurations.
## Why it fails
Bedrock Guardrail enforcement on the InvokeModel path requires **two
coordinated changes** per request:
1. **Headers** — `X-Amzn-Bedrock-GuardrailIdentifier`,
`X-Amzn-Bedrock-GuardrailVersion`, optional
`X-Amzn-Bedrock-Trace`. The SDK forwards these correctly.
2. **Body** — when the headers are present, Bedrock requires:
- a top-level `amazon-bedrock-guardrailConfig` object
(e.g. `{\"streamProcessingMode\": \"sync\"}`), and
- natural-language text segments in `messages[]` and the system
prompt wrapped in
`>...`
markers so the engine knows which spans to evaluate.
Tool-use / tool-result blocks must NOT be wrapped.
The SDK currently emits Anthropic Messages-shaped bodies on the
Bedrock path with neither of these, so Bedrock fails closed.
Reference: AWS Bedrock Guardrails — *Use a guardrail with the
InvokeModel API* (the `guardContent` and `guardrailConfig` sections).
## Requested behavior
When \`ClaudeAgentOptions\` is configured for Bedrock and the runtime
detects guardrail headers (or via an explicit option, e.g.
\`bedrock_guardrail={\"id\": \"...\", \"version\": \"DRAFT\"}\`), the SDK should:
- Inject \`amazon-bedrock-guardrailConfig\` into the outgoing body.
- Wrap text content blocks in \`messages[]\` and the system prompt with
\`guardContent\` markers; leave \`tool_use\` / \`tool_result\` blocks
untouched.
- Pass through \`guardrailTrace\` / intervention metadata on the
response side so callers can attribute guardrail actions to a turn.
Equivalent support is already needed on
\`InvokeModelWithResponseStream\` for the streaming code path.
## Why this matters
Without native support, integrators have two choices, both bad:
1. **Local botocore hook** that monkey-patches the outgoing body —
fragile, will silently double-wrap once the SDK adds real support
unless a feature flag is plumbed.
2. **\`ApplyGuardrail\` sidecar calls** — possible for *observability*
but does not enforce; adds 1–2 extra round-trips per turn.
Native support lets every \`claude-agent-sdk\` user pick up Bedrock
Guardrail enforcement and the corresponding \`guardrailTrace\`
observability with a single config option, the same way Bedrock
Guardrail headers already work today.
## Related
- Bedrock Converse API handles this natively; the InvokeModel path
does not. Migrating the SDK's Bedrock transport to Converse would
also resolve this (and prompt-caching #626) but is a larger change.
## Environment
- \`claude-agent-sdk==0.2.87\`
- AWS Bedrock \`us-east-1\`
- \`us.anthropic.claude-opus-4-7\`
- Reproduced on AWS Bedrock AgentCore Runtime; same body-shape issue
applies to any caller that adds guardrail headers.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.