Bedrock AgentCore on AWS
Giving a Bedrock agent internet access built the smallest useful agent on AgentCore and spent its words on the trust boundary a search tool opens. This page is the platform underneath: what each AgentCore component owns, how a user's identity travels from the front door to a third-party API, and what the two most common open-source proxies do when they sit in front of it.
The guides disagree with each other often enough that this page takes its facts from the AWS SDK's API model first — botocore 1.43.107, the contract the service actually enforces — and uses the guides, two proxies' source code and four hands-on blog posts for behaviour the model cannot show.
What AgentCore is now
The data-plane API has 67 operations and the control plane 171. Grouped by what they create, AgentCore is no longer the seven services it launched as:
| component | owns | the one fact that matters |
|---|---|---|
| Runtime | your agent's code, one microVM per session | sessions are the unit of isolation and of cost |
| Gateway | an MCP endpoint in front of tools | MCP is its only protocol; every tool is reached as target___tool |
| Identity | inbound authorizers, workload identities, a token vault for outbound OAuth and API keys | the agent never holds a refresh token or client secret |
| Memory | short-term events and long-term extracted records | records are listable — the agent can show what it kept |
| Browser | managed Chrome sessions over CDP | idle sessions are most of the bill |
| Code Interpreter | sandboxed code execution sessions | same session model as Browser, 8-hour ceiling |
| Observability | traces and metrics from the above | W3C traceparent and baggage are first-class invoke headers |
| Policy | Cedar policy engines attached to a Gateway | a deterministic allow or deny on each tool call, outside the model |
| Evaluations | LLM-as-judge and code evaluators, online and batch | — |
| Registry | a catalogue of MCP servers, A2A agents and skills | — |
The API also carries payments, A/B tests, recommendations and "harness" resources that no guide here covers; they are not discussed below.
Runtime: one session, one microVM
The contract your container implements:
| protocol | port | endpoint your code serves |
|---|---|---|
| HTTP | 8080 | POST /invocations, GET /ping |
| MCP | 8000 | POST /mcp (Streamable HTTP) |
| A2A | 9000 | POST / (JSON-RPC), agent card at /.well-known/agent-card.json |
| AG-UI | 8080 | POST /invocations, server-sent events |
A runtime serves one protocol. Callers never see those ports: every protocol is invoked the same way,
POST https://bedrock-agentcore.<region>.amazonaws.com/runtimes/<url-encoded ARN>/invocations?qualifier=DEFAULT. The artifact is a container image, or a code bundle on Python 3.10–3.14 or Node 22 with no image to build.The limits, from the API model:
| limit | value |
|---|---|
| X-Amzn-Bedrock-AgentCore-Runtime-Session-Id | 33 to 256 characters — a bare UUID is 36, a short random string fails |
| payload | 100 MB |
| idle session timeout | default 900 s, configurable 60 s to 14 days |
| maximum session lifetime | default 8 hours, configurable 60 s to 14 days |
| network | PUBLIC or VPC |
Requests with the same session id reach the same microVM; when the session ends — idle, lifetime, or
StopRuntimeSession — the microVM and its memory are destroyed, and the next request with that id gets a fresh one with no state. A /ping that answers HealthyBusy holds the idle timer off for long-running work. A capacity provider can now place runtimes on EC2 instances you define, for workloads that need a specific instance type.Three consequences people meet:
- The session id is the conversation. If a client generates a new id per request, every turn lands in a new microVM with nothing in memory. See the LiteLLM section below.
- Idle is billed. A session that is never stopped lives until its idle timeout. The Browser measurements below show what that costs.
runtimeUserIdis an impersonation lever. TheX-Amzn-Bedrock-AgentCore-Runtime-User-Idheader tells the runtime — and Identity's token vault — whose tokens to use. Under SigV4 it requires a separate permission,InvokeAgentRuntimeForUser; grant it only to the front-end that authenticated the user, never to a client that could name someone else.
Identity: three hops from a user to a third-party API
1
2
user ──JWT──▶ inbound authorizer ──▶ agent code ──workload token──▶ token vault ──▶ Slack / GitHub / Salesforce
(Runtime or Gateway) (GetWorkloadAccessTokenForJWT) (GetResourceOauth2Token)
Hop 1, inbound. A Runtime accepts either SigV4 or a JWT, not both — a JWT-authorized runtime cannot be called with the SDK's
InvokeAgentRuntime. The JWT authorizer takes an OIDC discoveryUrl and checks any of allowedAudience, allowedClients, allowedScopes and custom claims. Set allowedClients even when it feels redundant: without it, a new app client added to the same Cognito pool next year can call your agent. JWT claims such as sub are written to CloudTrail, so the subject should be an opaque id, not an email address.Hop 2, workload identity. The agent exchanges the user's JWT (or a user id, under SigV4) for a workload access token — the key to that user's entries in the token vault for that agent.
Hop 3, outbound.
GetResourceOauth2Token returns a token for a named credential provider, in one of three flows: USER_FEDERATION (authorization code, per user, with a consent URL the first time), M2M (client credentials, shared), or ON_BEHALF_OF_TOKEN_EXCHANGE. Providers are created per vendor — 25 named ones, from Google, GitHub, Slack and Salesforce to Okta, Auth0 and Cognito, plus CustomOauth2 — and API keys live in the same vault. In the Python SDK a @requires_access_token decorator wraps all of it, so the function receives an access token and nothing else.That is the property worth paying for: the agent's context never contains a refresh token or a client secret, so a prompt injection that exfiltrates the context gets a short-lived access token at most.
Gateway: MCP in front, anything behind
| options | |
|---|---|
| protocol | MCP only |
| inbound authorizer | CUSTOM_JWT, AWS_IAM (SigV4, caller needs InvokeGateway), AUTHENTICATE_ONLY (SigV4 checked, authorization left to a policy engine or interceptor), NONE |
| target kinds | Lambda, OpenAPI schema, Smithy model, API Gateway stage, another MCP server, and AWS-built connectors (web search, managed knowledge bases) |
| outbound credentials, per target | GATEWAY_IAM_ROLE, OAUTH, API_KEY, CALLER_IAM_CREDENTIALS (the caller's own SigV4), JWT_PASSTHROUGH |
| in the call path | interceptor Lambdas; a policy engine |
| debugging | exceptionLevel: DEBUG returns granular errors to the caller — useful in development, an information leak in production |
The policy engine is the important addition. A Cedar policy engine attached to a Gateway evaluates every tool call and allows or denies it before the target sees it. That is the control Multi-agent operations argues for — a guardrail that sits outside the model, where the model cannot argue with it — and it can see the tool and its arguments, which a Bedrock Guardrail on prose cannot (wish 5 ).
Two connectors, both us-east-1 only as of mid-2026:
| connector | what it is | price, per the hands-on posts |
|---|---|---|
| Web search (web-search) | an AWS-run web index; queries do not go to a third-party engine; reachable only through a Gateway | $7 per 1,000 calls, plus $0.005 per 1,000 Gateway calls |
| Managed knowledge base (bedrock-knowledge-bases) | the June 2026 managed KB; exposes Retrieve and AgenticRetrieveStream; the KB id stays behind the Gateway | $1 per 1,000 Retrieve, $4 per 1,000 agentic retrievals plus their retrieves, $5 per GB-month |
Two Gateway behaviours from the posts: Gateway application logs record the full search results, which is an audit and privacy decision before it is a debugging aid; and
AgenticRetrieveStream cannot be scoped to a resource in IAM, so the Gateway role gets it on *.Memory: what it keeps, and whether you can see it
Short-term memory is a log of immutable events per actor and session, expiring after 3 to 365 days. Long-term memory is what asynchronous strategies extract from those events:
semantic (facts), summary, userPreference, episodic, and custom with your own extraction prompt. Records live under namespaces, so one user's preferences are not another's.Wish 7 asked for a way to show a user what the memory kept. The data-plane API now has
ListMemoryRecords and GetMemoryRecord alongside the semantic RetrieveMemoryRecords, and batch create, update and delete — so "what do you remember about me" is answerable, and "forget that" is an API call. What the API still does not return is which events a record was extracted from; "why do you believe X" remains the agent's job to record.Browser and Code Interpreter: sessions, billed while idle
Both are session APIs with the Runtime's model: start, use, stop, with a session timeout up to 28,800 s (8 hours). The Browser exposes a CDP WebSocket for Playwright, Strands or Nova Act, with Live View; the managed
aws.browser.v1 runs in PUBLIC mode only, and session recording to S3 or VPC mode needs a custom browser — Security Hub's BedrockAgentCore.5 control flags custom browsers left public.One post measured the bill: browser compute at $0.0895 per vCPU-hour and $0.00945 per GB-hour, per second. A session leaked after its task cost about fifteen times one stopped immediately, and about 80% of browser spend was idle waiting. The model tokens for the same task cost over 300 times the browser. The billing metrics arrive 15 to 25 minutes late, and a leaked session also holds a slot in the concurrent-session quota. Stop sessions in a
finally block, and alert on sessions older than the longest task you run.Putting a proxy in front of it
Two open-source proxies have AgentCore support, and both are worth reading before you route production traffic through them.
| LiteLLM (9915df6875, Sept 2026) | agentgateway (94986070, Sept 2026) | |
|---|---|---|
| model | bedrock/agentcore/<runtime ARN>, plus an A2A provider and a Gateway web-search tool | an aws.agentCore backend with agentRuntimeArn and qualifier |
| signing | SigV4 as bedrock-agentcore, or a bearer JWT when api_key is set | SigV4 from the default chain; STS AssumeRole with session tags from JWT claims, for per-user CloudTrail attribution |
| session id | chat: a new litellm-session-<uuid> per request unless you pass runtimeSessionId; A2A: the contextId, prefixed per caller and checked for 33–256 | passed through from the client, unchecked |
| what is sent | chat: only the last message, as {"prompt": …} — no history, no temperature, no tools | the request body, buffered whole for signing |
| A2A | JSON-RPC envelope forwarded; caller headers that could spoof x-amzn-bedrock-agentcore-runtime-* identity are stripped | opaque HTTP |
| sharp edges | debug logging prints the first 50 characters of the bearer JWT; MCP SigV4 credentials resolved once, so they can expire in a long-lived client | the client's path and query are replaced, so the qualifier is fixed per route and is not URL-encoded |
The LiteLLM chat path is stateless by construction. Every turn arrives as a lone prompt in a brand-new microVM. For a conversation, either pass a stable
runtimeSessionId per conversation (33 characters or more), or use the A2A provider, which pins sessions to the A2A context and scopes them to the caller so two API keys cannot read each other's agent memory.agentgateway's useful idea is the STS session tag. Assuming the AgentCore caller role with a
sessionName or tag taken from the user's JWT puts the end user into CloudTrail and Cost Explorer, which plain SigV4 from a shared proxy role cannot. Its example sets the runtime user id with a static header modifier; derive it from the authenticated user instead, for the reason in the Runtime section.What it costs
| line | driver | the lever |
|---|---|---|
| model tokens | usually the largest, by two orders of magnitude over Browser in the measured case | context size and tool-call count |
| Runtime, Browser, Code Interpreter | per-second CPU and memory while a session exists | stop sessions; shorten idle timeouts from the 15-minute default |
| Gateway | per call | — |
| web search, knowledge base | per call, plus KB storage | cache, and do not search when the context already has the answer |
| logs and traces | CloudWatch ingestion | Gateway logs include full tool results — size them |
Region availability
Wish 4 recorded AgentCore in 20 regions against Bedrock's 36 as of September 2026, and the two connectors above were us-east-1 only in July. An agent whose model and AgentCore resources must share a region is constrained to the intersection; a cross-region inference profile lets the model live elsewhere. Check the current list before choosing a region — the SDK no longer ships it offline.
What the guides get wrong
| claim | where | per the API model or the code |
|---|---|---|
| IAM actions GetTokenVaultToken, PutTokenVaultToken, RefreshTokenVaultToken | a runtime OAuth guide | no such operations; the vault is reached through GetWorkloadAccessToken*, GetResourceOauth2Token and GetResourceApiKey |
| a SERVICE_ACCOUNT OAuth flow | same | the flows are USER_FEDERATION, M2M and ON_BEHALF_OF_TOKEN_EXCHANGE |
| create_credential_provider(credentialProviderType="OAUTH", …) | same | CreateOauth2CredentialProvider and CreateApiKeyCredentialProvider, with a vendor enum |
| the JWT authorizer has allowedClients only, matching aud or client_id | same | separate allowedAudience, allowedClients and allowedScopes |
| /ping returns {"status": "healthy"} | the guides' FastAPI examples | the contract is Healthy or HealthyBusy |
| three memory strategies plus custom | memory guide | five: semantic, summary, user preference, episodic, custom |
| memory retention unspecified | memory guide | events expire after 3–365 days |
| the Gateway service role needs InvokeGateway | one web-search post | InvokeGateway is the caller's permission under AWS_IAM; the service role does not need it |
| Kiro's native OAuth config for a JWT Gateway | the knowledge-base post | a later post shows it collides with Kiro CLI on callback port 38123; use mcp-remote with --use-id-token |
| "three" inbound modes | one post | four: CUSTOM_JWT, AWS_IAM, AUTHENTICATE_ONLY, NONE |
Verified, and not
From the API model (botocore 1.43.107, installed for this page): every operation, enum, field and limit stated as a number — session id length, payload size, lifecycle defaults and ranges, session timeouts, authorizer types, target kinds, outbound credential types, OAuth flows and vendors, memory strategies and expiry, code runtimes, and the descriptions of Policy, capacity providers and Registry. The model gives no region list; botocore no longer ships one for these services.
From source code: LiteLLM at
9915df6875 and agentgateway at 94986070 — the session-id generation, last-message payload, header stripping, JWT debug logging, credential caching, path replacement and STS session tags. Part of LiteLLM's chat transformation (its later streaming and async code) was not read in full; its non-200 responses are raised as BedrockError.From hands-on posts, not reproduced: all prices, the Browser cost measurements, the connectors' region availability, Gateway log contents, and the Kiro and Cognito gotchas. Prices and regions are from July and August 2026.
Not run. No AgentCore resource was created for this page; there is no AWS account behind it. The container ports, the
/ping contract and a Runtime's inbound SigV4-or-JWT exclusivity are from the runtime guides, consistent across them, but not exercised.Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article