Run the Claude Code harness on AWS Bedrock AgentCore Runtime.
AgentCore gives you managed microVMs, per-session storage, and a CloudWatch observability dashboard, but its on-ramp assumes you are writing an agent from scratch. Writing a good one is hard. Claude Code already exists, and it ships skills, hooks, subagents, plan mode, MCP support, permissions, and session resume that took years to get right.
This is a container that runs the real Claude Code CLI as an AgentCore agent. Not the SDK, not a reimplementation — the harness itself, unmodified.
Status: working, not yet finished. It runs on a real AgentCore runtime: a turn streams, calls tools, and answers with Bedrock behind the proxy, and the spans arrive in CloudWatch —
invoke_agent claude-code
chat claude-sonnet-4-6 tokens=82
execute_tool Write
chat claude-sonnet-4-6 tokens=6
— including what each tool was called with and what it returned. Bedrock is reached through the runtime's own IAM role, so no credential is ever inside the microVM.
Missing: infrastructure as code (the deployment described in docs/deploying.md was done by hand), images or other media in a turn, and any harness other than Claude Code. See ROADMAP.md, and SECURITY.md for what the container assumes and what it does not defend against.
AgentCore invokes a container over HTTP. This one serves three endpoints:
| Endpoint | Who calls it | Purpose |
|---|---|---|
POST /invocations |
AgentCore | A turn. Streams the response back as SSE. |
GET /ping |
AgentCore | Health check. |
POST /v1/messages |
the harness, on localhost | Inference. |
POST /v1/messages/count_tokens |
the harness, on localhost | Token preflight. |
The last two are the interesting part. Rather than letting the harness call a
model provider directly, the container serves the Anthropic Messages API itself
and points the harness at 127.0.0.1. Everything follows from that:
- Credentials stay out of the container. The harness gets a localhost URL and a per-turn token that is worthless anywhere else and dies with the microVM. The provider key is never in the microVM at all, so a prompt-injected agent has nothing to find — not in its environment, and not in the server's.
- The response streams from where it arrives first. Model deltas reach the proxy before the harness has finished composing a message, so user-facing SSE is fed from there rather than from harness stdout. The harness still gets its own copy — it needs the tokens to run tools — but its stdout is treated as control output: what ran, what it cost, when it finished.
- Providers are a mapping, not a rewrite. The harness only ever speaks the Anthropic Messages API to localhost. Which provider that becomes, in which wire format, with which credentials, is decided by the proxy from per-request headers. The harness cannot tell one from another.
That last point is also what makes room for other harnesses later. Codex, OpenCode, and the rest each have their own CLI flags and their own stdout format, but all of them speak HTTP to a model endpoint. Put the streaming path in the proxy and it gets written once instead of once per harness.
Today there is one adapter (Claude Code) and one provider (Bedrock). The interfaces are public so that adding either is a contribution rather than a fork.
- An AWS account with Bedrock model access
- Docker with buildx (AgentCore requires
linux/arm64images) - Python 3.11+ and uv for local development
uv sync
uv run ruff check app/ tests/
uv run pytestThe tests need neither AWS credentials nor the Claude Code CLI.
Run them on Linux if you can. Ending a turn means killing a process group, and process groups do not exist on Windows — the suite still passes there, but the code it exercises is not the code that ships. CI runs on Linux for that reason.
Apache 2.0. See LICENSE.