

# Host agent or tools with Amazon Bedrock AgentCore Runtime
AgentCore Runtime: Host agent or tools

Amazon Bedrock AgentCore Runtime provides a secure, serverless and purpose-built hosting environment for deploying and running AI agents or tools. It offers the following benefits:

Framework agnostic  
AgentCore Runtime lets you transform any local agent code to cloud-native deployments with a few lines of code no matter the underlying framework. Works seamlessly with popular frameworks like LangGraph, Strands, and CrewAI. You can also leverage it with custom agents that don’t use a specific framework.

Model flexibility  
AgentCore Runtime works with any Large Language Model, such as models offered by Amazon Bedrock, Anthropic Claude, Google Gemini, and OpenAI.

Protocol support  
AgentCore Runtime lets agents communicate with other agents and tools via Model Context Protocol (MCP) or Agent to Agent (A2A).

Session isolation  
In AgentCore Runtime, each user session runs in a dedicated microVM with isolated CPU, memory, and filesystem resources. This helps create complete separation between user sessions, safeguarding stateful agent reasoning processes and helps prevent cross-session data contamination. After session completion, the entire microVM is terminated and memory is sanitized, delivering deterministic security even when working with non-deterministic AI processes.

Extended execution time  
AgentCore Runtime supports both real-time interactions and long-running workloads up to 8 hours, enabling complex agent reasoning and asynchronous workloads that may involve multi-agent collaboration or extended problem-solving sessions.

Persistent filesystems  
Runtime supports persisting filesystem state across session stop/resume cycles. The agent’s files, installed packages, and build artifacts can survive session stops without external storage.

Consumption-based pricing model  
Runtime implements consumption-based pricing that charges only for resources actually consumed. Unlike allocation-based models that require pre-selecting resources, Runtime dynamically provisions what’s needed without requiring right-sizing. The service aligns CPU billing with actual active processing - typically eliminating charges during I/O wait periods when agents are primarily waiting for LLM responses - while continuously maintaining your session state.

Built-in authentication  
AgentCore Runtime, powered by AgentCore Identity, assigns distinct identities to AI agents and seamlessly integrates with your corporate identity provider such as Okta, Microsoft Entra ID, or Amazon Cognito, enabling your end users to authenticate into only the agents they have access to. In addition, Runtime lets outbound authentication flows to securely access third-party services like Slack, Zoom, and GitHub - whether operating on behalf of users or autonomously (using either OAuth or API keys).

Agent-specific observability  
AgentCore Runtime provides specialized built-in tracing that captures agent reasoning steps, tool invocations, and model interactions, providing clear visibility into agent decision-making processes, a critical capability for debugging and auditing AI agent behaviors.

Enhanced payload handling  
AgentCore Runtime can process 100MB payloads enabling seamless processing of multiple modalities (text, images, audio, video), with rich media content or large datasets.

Bidirectional streaming  
AgentCore Runtime supports both HTTP API calls and persistent WebSocket connections for real-time bidirectional streaming, enabling interactive applications with immediate response feedback and maintained conversation context.

Unified set of agent-specific capabilities  
AgentCore Runtime is delivered through a single, comprehensive SDK that provides streamlined access to the complete AgentCore capabilities including Memory, Tools, and Gateway. This integrated approach eliminates the integration work typically required when building equivalent agent infrastructure from disparate components.

**Topics**
+ [

# How it works
](runtime-how-it-works.md)
+ [

# Understand the AgentCore Runtime service contract
](runtime-service-contract.md)
+ [

# IAM Permissions for AgentCore Runtime
](runtime-permissions.md)
+ [

# Get started with AgentCore Runtime
](runtime-getting-started.md)
+ [

# Use any agent framework
](using-any-agent-framework.md)
+ [

# Use any foundation model
](using-any-model.md)
+ [

# Deploy MCP servers in AgentCore Runtime
](runtime-mcp.md)
+ [

# Stateful MCP server features
](mcp-stateful-features.md)
+ [

# Deploy A2A servers in AgentCore Runtime
](runtime-a2a.md)
+ [

# Deploy AGUI servers in AgentCore Runtime
](runtime-agui.md)
+ [

# Use isolated sessions for agents
](runtime-sessions.md)
+ [

# Persist session state across stop/resume with a filesystem configuration (Preview)
](runtime-persistent-filesystems.md)
+ [

# Handle asynchronous and long running agents with Amazon Bedrock AgentCore Runtime
](runtime-long-run.md)
+ [

# Stream agent responses
](response-streaming.md)
+ [

# Bidirectional streaming
](runtime-bidirectional-streaming.md)
+ [

# Pass custom headers to Amazon Bedrock AgentCore Runtime
](runtime-header-allowlist.md)
+ [

# Authenticate and authorize with Inbound Auth and Outbound Auth
](runtime-oauth.md)
+ [

# AgentCore Runtime versioning and endpoints
](agent-runtime-versioning.md)
+ [

# Invoke an AgentCore Runtime agent
](runtime-invoke-agent.md)
+ [

# Execute shell commands in AgentCore Runtime sessions
](runtime-execute-command.md)
+ [

# Observe agents in Amazon Bedrock AgentCore Runtime
](runtime-observability.md)
+ [

# Troubleshoot AgentCore Runtime
](runtime-troubleshooting.md)

# How it works
How it works

The Amazon Bedrock AgentCore Runtime handles scaling, session management, security isolation, and infrastructure management, allowing you to focus on building intelligent agent experiences rather than operational complexity. By leveraging the features and capabilities described here, you can build, deploy, and manage sophisticated AI agents that deliver value to your users while helping to maintain enterprise-grade security and reliability.

**Topics**
+ [

## Key components
](#runtime-key-components)
+ [

## Authentication and security
](#runtime-auth-security)
+ [

## Additional features
](#runtime-additional-features)
+ [

## Implementation overview
](#runtime-implementation-overview)

## Key components


### AgentCore Runtime


An AgentCore Runtime is the foundational component that hosts your AI agent or tool code. It represents a containerized application that processes user inputs, maintains context, and executes actions using AI capabilities. When you create an agent, you define its behavior, capabilities, and the tools it can access. For example, a customer support agent might answer product questions, process returns, and escalate complex issues to human representatives.

You can build and deploy agents to AgentCore Runtime using the [AgentCore CLI](develop-agents.md#agentcore-cli-configure-deploy) , the [AgentCore Python SDK](develop-agents.md#develop-agents-bedroock-agentcore-sdk) or directly through [AWS SDKs](develop-agents.md#develop-agents-bedrock-agentcore-aws-sdk) . With the AgentCore Python SDK, you can define your agent using [popular frameworks](using-any-agent-framework.md) like LangGraph, CrewAI, or Strands Agents. The SDK handles infrastructure complexities, allowing you to focus on the agent’s logic and capabilities.

Each AgentCore Runtime:
+ Has a unique identity
+ Is versioned to support controlled deployment and updates

### Versions


Each AgentCore Runtime maintains immutable [versions](agent-runtime-versioning.md) that capture a complete snapshot of the configuration at a specific point in time:
+ When you create an AgentCore Runtime, Version 1 (V1) is automatically created
+ Each update to configuration (container image, protocol settings, network settings) creates a new version
+ Each version contains all necessary configuration needed for execution

This versioning system provides reliable deployment history and rollback capabilities.

### Endpoints


 [Endpoints](agent-runtime-versioning.md) provide addressable access points to specific versions of your AgentCore Runtime. Each endpoint:
+ Has a unique ARN for invocation
+ References a specific version of your Agent Runtime
+ Provides stable access to your agent even as you update implementations

Key endpoint details:
+ The "DEFAULT" endpoint is automatically created when you call [CreateAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateAgentRuntime.html) and points to the latest version
+ When you update your AgentCore Runtime, a new version is created but the `DEFAULT` endpoint automatically updates to reference it
+ You can create custom endpoints with the [CreateAgentRuntimeEndpoint](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateAgentRuntimeEndpoint.html) operation for different environments (dev, test, prod)
+ When a user makes a request to an endpoint, the request is resolved to the specific agent version referenced by that endpoint

Endpoints have distinct lifecycle states:
+  `CREATING` - Initial state during endpoint creation
+  `CREATE_FAILED` - Indicates creation failure due to permissions or other issues
+  `READY` - Endpoint is operational and accepting requests
+  `UPDATING` - Endpoint is being modified to reference a new version
+  `UPDATE_FAILED` - Indicates update operation failure

You can update endpoints without downtime, allowing for seamless version transitions and rollbacks.

### Sessions


 [Sessions](runtime-sessions.md) represent individual interaction contexts between users and your AgentCore Runtime. Each session:
+ Is identified by a unique `runtimeSessionId` provided by your application, or by the Runtime itself in the first invocation if the `runtimeSessionId` is left empty
+ Runs in a dedicated microVM with completely isolated CPU, memory, and filesystem resources
+ Preserves context across multiple interactions within the same conversation
+ Can persist for up to 8 hours of total runtime

Session states include:
+  **Active** - Currently processing a request or executing background tasks
+  **Idle** - Not processing any requests but maintaining context while waiting for next interaction
+  **Terminated** - Session ended due to inactivity (15 minutes), reaching maximum lifetime (8 hours), or being deemed unhealthy

Important session characteristics:
+ After session termination, the entire microVM is terminated and memory is sanitized
+ A subsequent request with the same `runtimeSessionId` after termination will create a new execution environment
+ Session isolation prevents cross-session data contamination and ensures security
+ Session state is ephemeral and should not be used for long-term durability (use AgentCore Memory for context durability)

This complete isolation between sessions is crucial for enterprise security, particularly when dealing with non-deterministic AI processes.

## Authentication and security


Inbound authentication controls who can access your agents through AWS Identity and Access Management or OAuth 2.0, validating bearer tokens from identity providers before allowing requests to proceed. Outbound authentication enables your agents to securely access third-party services using OAuth or API keys, with AgentCore Identity managing credentials in either user-delegated or autonomous modes. For more information, see [Authenticate and authorize with Inbound Auth and Outbound Auth](runtime-oauth.md).

### Inbound authentication


Inbound Auth, powered by AgentCore Identity, controls who can access and invoke your agents or tools in AgentCore Runtime.

#### Authentication methods

+  ** AWS IAM** (SigV4): Uses AWS credentials for identity verification
+  **OAuth 2.0** : Integrates with external identity providers

#### OAuth configuration options

+  **Discovery URL** : Your identity provider’s OpenID Connect discovery endpoint
+  **Allowed Audiences** : List of valid audience values your tokens should contain
+  **Allowed Clients** : List of client identifiers that can access this agent

#### Authentication flow


1. End users authenticate with your identity provider (Amazon Cognito, Okta, Microsoft Entra ID)

1. Your client application receives a bearer token after successful authentication

1. The client passes this token in the authorization header when invoking the agent

1. AgentCore Runtime validates the token with the authorization server

1. If valid, the request is processed; if invalid, it’s rejected

This ensures only authenticated users with proper authorization can access your agents.

### Outbound authentication


Outbound Auth, powered by Amazon Bedrock AgentCore Identity, lets your agents hosted on AgentCore Runtime securely access third-party services:

#### Authentication methods

+  **OAuth** : For services supporting OAuth flows
+  **API Keys** : For services using key-based authentication

#### Authentication modes

+  **User-delegated** : Acting on behalf of the end user with their credentials
+  **Autonomous** : Acting independently with service-level credentials

#### Supported services

+ Enterprise systems such as Slack, Zoom, and GitHub
+  AWS services
+ Custom APIs and data sources

AgentCore Identity manages these credentials securely, preventing credential exposure in your agent code or logs.

## Additional features


### Asynchronous processing


AgentCore Runtime supports long-running workloads through:
+ Background task handling for operations that exceed request/response cycles
+ Automatic status tracking via the `/ping` endpoint
+ Support for operations up to 8 hours in duration

For more information, see [Handle asynchronous and long running agents with Amazon Bedrock AgentCore Runtime](runtime-long-run.md).

### Streaming responses


Agents can stream partial results as they become available rather than waiting for complete processing. This lets you provide a more responsive user experience, especially for operations that generate large amounts of content or take significant time to complete. For more information, see [Stream agent responses](response-streaming.md).

### WebSocket API


The AgentCore Runtime provides [WebSocket](runtime-http-protocol-contract.md#ws-endpoint) support for real-time bidirectional streaming connections for interactive agent communication. This enables more responsive and interactive agent experiences. For more information, see [Get started with bidirectional streaming using WebSocket](runtime-get-started-websocket.md).

### Protocol support


AgentCore Runtime supports multiple communication protocols:
+  [HTTP](runtime-http-protocol-contract.md) : Direct REST API endpoints for traditional request/response patterns. For more information, see [Get started with the AgentCore CLI](runtime-get-started-cli.md).
+  [MCP](runtime-mcp-protocol-contract.md) : Model Context Protocol for tools and agent servers. For more information, see [Deploy MCP servers in AgentCore Runtime](runtime-mcp.md).
+  [A2A](runtime-a2a-protocol-contract.md) : Agent-to-Agent protocol for multi-agent communication and discovery. For more information, see [Deploy A2A servers in AgentCore Runtime](runtime-a2a.md).

## Implementation overview


Here’s how to get started with the AgentCore Runtime. For the complete example, see [Get started with the AgentCore CLI](runtime-get-started-cli.md).

### Prepare your agent or tool code

+ Define your agent logic using any AI framework or custom code
+ Add the required HTTP endpoints using the AgentCore SDK or custom implementation
+ Package dependencies in a requirements.txt file

### Deploy your agent or tool

+ Build and push a container image to Amazon ECR directly or via the AgentCore SDK
+ Create an AgentCore Runtime using the container image
+ The initial version (V1) and DEFAULT endpoint are created automatically

### Invoke your agent or tool

+ Generate a unique session ID for each user conversation
+ Call the [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) or [InvokeAgentRuntimeWithWebSocketStream](runtime-get-started-websocket.md) operation with your agent’s ARN and session ID
+ Pass user input in the request payload

### Manage and observe sessions, and make updates

+ Use the same session ID for follow-up interactions to maintain context
+ Review logs, traces, and observability metrics
+ Deploy updates by modifying your AgentCore Runtime (creates new versions)
+ Control rollout by updating endpoints to point to new versions

# Understand the AgentCore Runtime service contract
Understand the AgentCore Runtime service contract

The AgentCore Runtime service contract defines the standardized communication protocol that your agent application must implement to integrate with the Amazon Bedrock agent hosting infrastructure. This contract ensures seamless communication between your custom agent code and AWS's managed hosting environment.

**Topics**
+ [

## Supported protocols
](#supported-protocols)
+ [

## Compare supported protocols
](#protocol-comparison)
+ [

# HTTP protocol contract
](runtime-http-protocol-contract.md)
+ [

# MCP protocol contract
](runtime-mcp-protocol-contract.md)
+ [

# A2A protocol contract
](runtime-a2a-protocol-contract.md)
+ [

# AGUI protocol contract
](runtime-agui-protocol-contract.md)

## Supported protocols


The AgentCore Runtime service contract supports the following communication protocols:
+  [HTTP](runtime-http-protocol-contract.md) : Direct REST API endpoints for traditional request/response patterns
+  [MCP](runtime-mcp-protocol-contract.md) : Model Context Protocol for tools and agent servers
+  [A2A](runtime-a2a-protocol-contract.md) : Agent-to-Agent protocol for multi-agent communication and discovery
+  [AGUI](runtime-agui-protocol-contract.md) : Agent-to-User Interface protocol for interactive agent experiences with UI rendering

## Compare supported protocols


Compare the HTTP, MCP, A2A, and AGUI protocols to understand the differences and use cases.


| Feature | HTTP Protocol | MCP Protocol | A2A Protocol | AGUI Protocol | 
| --- | --- | --- | --- | --- | 
|   **Port**   |  8080  |  8000  |  9000  |  8080  | 
|   **Mount Path**   |  /invocations (HTTP), /ws (WebSocket)  |  /mcp  |  / (root)  |  /invocations (SSE), /ws (WebSocket)  | 
|   **Message Format**   |  REST JSON/SSE, WebSocket (text/binary)  |  JSON-RPC  |  JSON-RPC 2.0  |  Event streams (SSE/WebSocket)  | 
|   **Discovery**   |  N/A  |  Tool listing  |  Agent Cards  |  N/A  | 
|   **Authentication**   |  SigV4, OAuth 2.0; WebSocket supports SigV4 by headers and query params  |  SigV4, OAuth 2.0  |  SigV4, OAuth 2.0  |  SigV4, OAuth 2.0  | 
|   **Use Case**   |  Direct API calls, real-time streaming  |  Tool servers  |  Agent-to-agent communication  |  Interactive UI experiences  | 

# HTTP protocol contract
HTTP protocol contract

Understand the requirements for implementing the HTTP protocol in your agent application. Use the HTTP protocol to create direct REST API endpoints for traditional request/response patterns and WebSocket endpoints for real-time bidirectional streaming connections.

**Note**  
Both HTTP ( `/invocations` ) and WebSocket ( `/ws` ) endpoints can be deployed on the same container using port 8080, allowing a single agent implementation to support both traditional API interactions and real-time bidirectional streaming.

For example code, see [Get started with the AgentCore CLI](runtime-get-started-cli.md).

**Topics**
+ [

## Container requirements
](#container-requirements-http)
+ [

## Path requirements
](#path-requirements-http)
+ [

## OAuth Authentication Responses
](#http-oauth-authentication-responses)

## Container requirements


Your agent must be deployed as a containerized application meeting these specifications:
+  **Host** : `0.0.0.0` 
+  **Port** : `8080` - Standard port for HTTP-based agent communication
+  **Platform** : ARM64 container - Required for compatibility with the AgentCore Runtime environment

## Path requirements


### /invocations - POST


This is the primary agent interaction endpoint with JSON input and JSON/SSE output.

 **Purpose** 

Receives incoming requests from users or applications and processes them through your agent’s business logic

 **Use cases** 

The `/invocations` endpoint serves several key purposes:
+ Direct user interactions and conversations
+ API integrations with external systems
+ Batch processing of multiple requests
+ Real-time streaming responses for long-running operations

 **Example Request format** 

```
Content-Type: application/json

{
  "prompt": "What's the weather today?"
}
```

 **Response formats** 

Your agent can respond using either of the following formats depending on the use case:

#### JSON response (non-streaming)


 **Purpose** 

Provides complete responses for requests that can be processed quickly

 **Use cases** 

JSON responses are ideal for:
+ Simple question-answering scenarios
+ Deterministic computations
+ Quick data lookups
+ Status confirmations

 **Example JSON response format** 

```
Content-Type: application/json

{
  "response": "Your agent's response here",
  "status": "success"
}
```

#### SSE response (streaming)


Server-sent events (SSE) let you deliver real-time streaming responses. For more information, see the [Server-sent events](https://html.spec.whatwg.org/multipage/server-sent-events.html#server-sent-events) specification.

 **Purpose** 

Enables incremental response delivery for long-running operations and improved user experience

 **Use cases** 

SSE responses are ideal for:
+ Real-time conversational experiences
+ Progressive content generation
+ Long-running computations with intermediate results
+ Live data feeds and updates

 **Example SSE response format** 

```
Content-Type: text/event-stream

data: {"event": "partial response 1"}
data: {"event": "partial response 2"}
data: {"event": "final response"}
```

### /ws - WebSocket (Optional)


This is the primary WebSocket connection endpoint for real-time bidirectional communication.

 **Purpose** 

Accepts WebSocket upgrade requests and maintains persistent connections for streaming agent interactions

 **Use cases** 

The `/ws` endpoint serves several key purposes:
+ Real-time conversational interfaces
+ Interactive agent sessions with immediate feedback
+ Streaming data processing with bidirectional communication

 **Connection establishment** 

WebSocket connections begin with an HTTP upgrade request:

 **Example HTTP Upgrade Request** 

```
GET /ws HTTP/1.1
Host: agent-endpoint
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: session-uuid
```

 **Example WebSocket Upgrade Response** 

```
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
```

 **Message handling requirements** 

Your WebSocket endpoint must handle:
+  **Connection acceptance** : Call `await websocket.accept()` to establish the connection
+  **Message reception** : Support text or binary message types based on your application requirement
+  **Message processing** : Handle incoming messages according to your agent’s business logic
+  **Response sending** : Send appropriate responses using `send_text()` or `send_bytes()` 
+  **Connection lifecycle** : Manage connection establishment, maintenance, and termination

#### Message formats


##### Text Messages


##### JSON Format (Recommended)


 **Purpose** 

Structured data exchange for agent interactions

 **Example message** 

```
{
  "prompt": "Hello, can you help me with this question?",
  "session_id": "session-uuid",
  "message_type": "user_message"
}
```

 **Example response** 

```
{
  "response": "I'd be happy to help you with your question!",
  "session_id": "session-uuid",
  "message_type": "agent_response"
}
```

##### Plain Text Format


 **Purpose** 

Simple text-based communication

 **Example** 

```
Hello, can you help me with this question?
```

##### Binary Messages


 **Purpose** 

Support for non-text data such as images, audio, or other binary formats

 **Use cases** 

Binary messages support several scenarios:
+ Multi-modal agent interactions
+ File uploads and downloads
+ Compressed data transmission
+ Binary protocol data

 **Handling requirements** 

Binary message handling requires:
+ Use `receive_bytes()` and `send_bytes()` methods
+ Implement appropriate binary data processing
+ Consider message size limitations

#### Connection lifecycle


##### Connection Establishment


1.  **HTTP Handshake** : Client sends WebSocket upgrade request

1.  **Upgrade Response** : Agent accepts and returns 101 Switching Protocols

1.  **WebSocket Active** : Bidirectional communication begins

1.  **Session Binding** : Associate connection with session identifier

##### Message Exchange


1.  **Continuous Loop** : Implement message listening loop

1.  **Message Processing** : Handle incoming messages asynchronously

1.  **Response Generation** : Send appropriate responses

1.  **Error Handling** : Manage exceptions and connection issues

### /ping - GET


 **Purpose** 

Verifies that your agent is operational and ready to handle requests

 **Use cases** 

The `/ping` endpoint serves several key purposes:
+ Service monitoring to detect and remediate issues
+ Automated recovery through AWS's managed infrastructure

 **Response format** 

Returns a status code indicating your agent’s health:
+  **Content-Type** : `application/json` 
+  **HTTP Status Code** : `200` for healthy, appropriate error codes for unhealthy states

If your agent needs to process background tasks, you can indicate it with the `/ping` status. If the ping status is `HealthyBusy` , the runtime session is considered active.

 **Example Ping response format** 

```
{
  "status": "<status_value>",
  "time_of_last_update": <unix_timestamp>
}
```

 **status**   
 `Healthy` - System is ready to accept new work  
 `HealthyBusy` - System is operational but currently busy with async tasks

 **time\$1of\$1last\$1update**   
Used to determine how long the system has been in its current state

## OAuth Authentication Responses


OAuth-configured agents follow [RFC 6749 (OAuth 2.0)](https://datatracker.ietf.org/doc/html/rfc6749) authentication standards. When authentication is missing, the service returns a 401 Unauthorized response with a WWW-Authenticate header (per [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235) ), enabling clients to discover the authorization server endpoints through the GetRuntimeProtectedResourceMetadata API.

### 401 Unauthorized


Returned when Authorization header is missing.

Includes WWW-Authenticate header:

```
WWW-Authenticate: Bearer resource_metadata="https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{ESCAPED_ARN}/invocations/.well-known/oauth-protected-resource?qualifier={QUALIFIER}"
```

**Note**  
SigV4-configured agents return HTTP 403 with an `ACCESS_DENIED` error and do not include `WWW-Authenticate` headers.

# MCP protocol contract
MCP protocol contract

Understand the requirements for implementing the Model Context Protocol (MCP) so that agents can call tools and agent servers.

For example code, see [Deploy MCP servers in AgentCore Runtime](runtime-mcp.md).

**Topics**
+ [

## Protocol implementation requirements
](#protocol-implementation-requirements)
+ [

## Container requirements
](#container-requirements-mcp)
+ [

## Path requirements
](#path-requirements-mcp)
+ [

## OAuth Authentication Responses
](#mcp-oauth-authentication-responses)

## Protocol implementation requirements


Your MCP server must implement these specific protocol requirements:
+  **Transport** : Streamable-http transport is required. By default, use stateless mode ( `stateless_http=True` ) for compatibility with AWS's session management and load balancing.
+  **Session Management** : Platform automatically adds `Mcp-Session-Id` header for session isolation. In stateless mode, servers must support stateless operation so as to not reject platform generated `Mcp-Session-Id` header.

**Tip**  
Amazon Bedrock AgentCore also supports stateful MCP servers ( `stateless_http=False` ) that enable capabilities such as elicitation (multi-turn user interactions) and sampling (LLM-generated content). Stateful mode is required when your MCP server needs to maintain session context across multiple requests within the same tool invocation. For more information and examples, see [Stateful MCP server features](mcp-stateful-features.md).

### MCP session management and microVM stickiness


The Model Context Protocol (MCP) uses the `Mcp-Session-Id` header to manage session state and route requests. For the MCP specification, see [MCP Streamable HTTP Transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).

 **MicroVM Stickiness** : Amazon Bedrock AgentCore uses the `Mcp-Session-Id` header to route requests to the same microVM instance. Clients must capture the `Mcp-Session-Id` returned in the response and include it in all subsequent requests to ensure session affinity. Without a consistent session ID, each request may be routed to a new microVM, which may result in additional latency due to cold starts.

 **Stateless MCP** ( `stateless_http=True` ):
+ Platform generates the `Mcp-Session-Id` and includes it in the request to your MCP server.
+ Your MCP server must accept the platform-provided session ID (do not reject it).
+ Platform returns the same `Mcp-Session-Id` to the client in the response.
+ Client must include this session ID in all subsequent requests for microVM affinity.

 **Stateful MCP** ( `stateless_http=False` ):
+ Client sends the initialize request without an `Mcp-Session-Id` header.
+ Platform returns `Mcp-Session-Id` in the response.
+ Client must include this `Mcp-Session-Id` in all subsequent requests for both session state and microVM affinity.

For more details on stateful MCP session management, see the [MCP session management specification](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#session-management).

**Note**  
In both modes, Amazon Bedrock AgentCore always returns an `Mcp-Session-Id` header to clients. Always capture and reuse this header for optimal performance.

## Container requirements


Your MCP server must be deployed as a containerized application meeting these specifications:
+  **Host** : `0.0.0.0` 
+  **Port** : `8000` - Standard port for MCP server communication (different from HTTP protocol)
+  **Platform** : ARM64 container - Required for compatibility with AWS Amazon Bedrock AgentCore runtime environment

## Path requirements


### /mcp - POST


 **Purpose** 

Receives MCP RPC messages and processes them through your agent’s tool capabilities, complete pass-through of [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) API payload with standard MCP RPC messages

 **Response format** 

JSON-RPC based request/response format, supporting both `application/json` and `text/event-stream` as response content-types

 **Use cases** 

The `/mcp` endpoint serves several key purposes:
+ Tool invocation and management
+ Agent capability discovery
+ Resource access and manipulation
+ Multi-step agent workflows

## OAuth Authentication Responses


OAuth-configured agents follow [RFC 6749 (OAuth 2.0)](https://datatracker.ietf.org/doc/html/rfc6749) authentication standards. When authentication is missing, the service returns a 401 Unauthorized response with a WWW-Authenticate header (per [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235) ), enabling clients to discover the authorization server endpoints through the GetRuntimeProtectedResourceMetadata API.

### 401 Unauthorized


Returned when the Authorization header is missing or empty.

Response includes WWW-Authenticate header:

```
WWW-Authenticate: Bearer resource_metadata="https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{ESCAPED_ARN}/invocations/.well-known/oauth-protected-resource?qualifier={QUALIFIER}"
```

**Note**  
SigV4-configured agents return HTTP 403 with an `ACCESS_DENIED` error and do not include `WWW-Authenticate` headers.

# A2A protocol contract
A2A protocol contract

The A2A protocol contract defines the requirements for implementing agent-to-agent communication in Amazon Bedrock AgentCore Runtime. This contract specifies the technical requirements, endpoints, and communication patterns that your A2A server must implement.

For example code, see [Deploy A2A servers in AgentCore Runtime](runtime-a2a.md).

**Topics**
+ [

## Protocol implementation requirements
](#protocol-implementation-requirements)
+ [

## Container requirements
](#container-requirements)
+ [

## Path requirements
](#path-requirements)
+ [

## Authentication requirements
](#authentication-requirements)
+ [

## Error handling
](#error-handling)
+ [

## OAuth Authentication Responses
](#a2a-oauth-authentication-responses)

## Protocol implementation requirements


Your A2A server must implement these specific protocol requirements:
+  **Transport** : [JSON-RPC 2.0](https://www.jsonrpc.org/specification) over HTTP - Enables standardized agent-to-agent communication
+  **Session Management** : Platform automatically adds `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header for session isolation
+  **Agent Discovery** : Must provide Agent Card at `/.well-known/agent-card.json` endpoint

## Container requirements


Your A2A server must be deployed as a containerized application meeting these specifications:
+  **Host** : `0.0.0.0` 
+  **Port** : `9000` - Standard port for A2A server communication (different from HTTP and MCP protocols)
+  **Platform** : ARM64 container - Required for compatibility with AWS Amazon Bedrock AgentCore runtime environment

## Path requirements


### / - POST


#### Purpose


Receives JSON-RPC 2.0 messages and processes them through your agent’s capabilities, complete pass-through of [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) API payload with A2A protocol messages

#### Use cases


The root endpoint serves several key purposes:
+ Agent-to-agent communication and collaboration
+ Multi-step agent workflows and task delegation
+ Real-time conversational experiences between agents
+ Tool invocation and capability sharing

#### Request format


A2A servers expect JSON-RPC 2.0 formatted requests:

```
Content-Type: application/json
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [
        {
          "kind": "text",
          "text": "Your message content here"
        }
      ],
      "messageId": "unique-message-id"
    }
  }
}
```

#### Response format


A2A servers respond with JSON-RPC 2.0 formatted responses containing tasks and artifacts:

```
Content-Type: application/json
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "result": {
    "artifacts": [
      {
        "artifactId": "unique-artifact-id",
        "name": "agent_response",
        "parts": [
          {
            "kind": "text",
            "text": "Agent response content"
          }
        ]
      }
    ]
  }
}
```

### /.well-known/agent-card.json - GET


#### Purpose


Provides Agent Card metadata for agent discovery and capability advertisement

#### Use cases


The Agent Card endpoint serves several key purposes:
+ Agent discovery in multi-agent systems
+ Capability and skill advertisement
+ Authentication requirement specification
+ Service endpoint configuration

#### Response format


Returns JSON metadata describing the agent’s identity and capabilities:

```
Content-Type: application/json
{
  "name": "Agent Name",
  "description": "Agent description and purpose",
  "version": "1.0.0",
  "url": "https://bedrock-agentcore.region.amazonaws.com/runtimes/agent-arn/invocations/",
  "protocolVersion": "0.3.0",
  "preferredTransport": "JSONRPC",
  "capabilities": {
    "streaming": true
  },
  "defaultInputModes": ["text"],
  "defaultOutputModes": ["text"],
  "skills": [
    {
      "id": "skill-id",
      "name": "Skill Name",
      "description": "Skill description and capabilities",
      "tags": []
    }
  ]
}
```

### /ping - GET


#### Purpose


Verifies that your A2A server is operational and ready to handle requests

#### Response format


Returns a status code indicating your agent’s health:
+  **Content-Type** : `application/json` 
+  **HTTP Status Code** : `200` for healthy, appropriate error codes for unhealthy states

```
{
  "status": "Healthy",
  "time_of_last_update": 1640995200
}
```

## Authentication requirements


A2A servers support multiple authentication mechanisms:

### OAuth 2.0 Bearer Tokens


For A2A client authentication, include the Bearer token in request headers:

```
Authorization: Bearer <oauth-token>
X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: <session-id>
```

### SigV4 Authentication


Standard AWS SigV4 authentication is also supported for programmatic access.

## Error handling


A2A servers return errors as standard JSON-RPC 2.0 error responses with HTTP 200 status codes to maintain protocol compliance:


| JSON-RPC Error Code | Runtime Exception | HTTP Error Code | JSON-RPC Error Message | 
| --- | --- | --- | --- | 
|  -32501  |  ResourceNotFoundException  |  404  |  Resource not found - Requested resource does not exist  | 
|  -32052  |  ValidationException  |  400  |  Validation error - Invalid request data  | 
|  -32053  |  ThrottlingException  |  429  |  Rate limit exceeded - Too many requests  | 
|  -32054  |  ResourceConflictException  |  409  |  Resource conflict - Resource already exists  | 
|  -32055  |  RuntimeClientError  |  424  |  Runtime client error - Please check your CloudWatch logs for more information  | 

Example error response:

```
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "error": {
    "code": -32052,
    "message": "Validation error - Invalid request data"
  }
}
```

## OAuth Authentication Responses


OAuth-configured agents follow [RFC 6749 (OAuth 2.0)](https://datatracker.ietf.org/doc/html/rfc6749) authentication standards. When authentication is missing, the service returns a 401 Unauthorized response with a WWW-Authenticate header (per [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235) ), enabling clients to discover the authorization server endpoints through the GetRuntimeProtectedResourceMetadata API.

### 401 Unauthorized - Missing Authentication


```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{ESCAPED_ARN}/invocations/.well-known/oauth-protected-resource?qualifier={QUALIFIER}"
```

**Note**  
SigV4-configured agents return HTTP 403 with an `ACCESS_DENIED` error and do not include `WWW-Authenticate` headers.

# AGUI protocol contract
AGUI protocol contract

The AGUI protocol contract defines the requirements for implementing agent-to-user interface communication in Amazon Bedrock AgentCore Runtime. This contract specifies the technical requirements, endpoints, and communication patterns that your AGUI agent must implement.

For example code, see [Deploy AGUI servers in AgentCore Runtime](runtime-agui.md).

**Topics**
+ [

## Protocol implementation requirements
](#agui-protocol-implementation-requirements)
+ [

## Container requirements
](#agui-container-requirements)
+ [

## Path requirements
](#agui-path-requirements)
+ [

## Authentication requirements
](#agui-authentication-requirements)
+ [

## Error handling
](#agui-error-handling)
+ [

## OAuth authentication responses
](#agui-oauth-authentication-responses)

## Protocol implementation requirements


Your AGUI agent must implement these specific protocol requirements:
+  **Transport** : Server-Sent Events (SSE) or WebSocket - SSE provides unidirectional streaming from server to client, while WebSocket enables bidirectional real-time communication
+  **Session Management** : Platform automatically adds `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header for session isolation

## Container requirements


Your AGUI agent must be deployed as a containerized application meeting these specifications:
+  **Host** : `0.0.0.0` 
+  **Port** : `8080` - Standard port for AGUI agent communication (same as HTTP protocol)
+  **Platform** : ARM64 container - Required for compatibility with AWS Amazon Bedrock AgentCore runtime environment

## Path requirements


### /invocations - POST


#### Purpose


Receives user requests and streams responses as Server-Sent Events (SSE)

#### Use cases


The invocations endpoint serves several key purposes:
+ Streaming chat responses
+ Agent status and thinking steps
+ Tool calls and results

#### Request format


Amazon Bedrock AgentCore passes request payloads directly to your container without validation. To be AGUI-compliant, your requests should follow the `RunAgentInput` format. Your container implementation determines which fields are required and how validation errors are handled.

AGUI-compliant agents expect a `RunAgentInput` JSON payload. Example:

```
{
  "threadId": "thread-123",
  "runId": "run-456",
  "messages": [{"id": "msg-1", "role": "user", "content": "Hello, agent!"}],
  "tools": [],
  "context": [],
  "state": {},
  "forwardedProps": {}
}
```

For the complete `RunAgentInput` schema and message format details, see [AGUI Types](https://docs.ag-ui.com/sdk/js/core/types).

#### Response format


AGUI agents respond with SSE-formatted event streams:

```
Content-Type: text/event-stream

data: {"type":"RUN_STARTED","threadId":"thread-123","runId":"run-456"}

data: {"type":"TEXT_MESSAGE_START","messageId":"msg-789","role":"assistant"}

data: {"type":"TEXT_MESSAGE_CONTENT","messageId":"msg-789","delta":"Processing your request"}

data: {"type":"TOOL_CALL_START","toolCallId":"tool-001","toolCallName":"search","parentMessageId":"msg-789"}

data: {"type":"TOOL_CALL_RESULT","messageId":"msg-789","toolCallId":"tool-001","content":"Search completed"}

data: {"type":"TEXT_MESSAGE_END","messageId":"msg-789"}

data: {"type":"RUN_FINISHED","threadId":"thread-123","runId":"run-456"}
```

### /ws - WebSocket


#### Purpose


Provides bidirectional real-time communication between clients and agents

#### Use cases


The WebSocket endpoint serves several key purposes:
+ Real-time conversational interfaces
+ Interactive agent sessions with user interrupts
+ Multi-turn conversations with persistent connections

### /ping - GET


#### Purpose


Verifies that your AGUI agent is operational and ready to handle requests

#### Response format


Returns a status code indicating your agent’s health:
+  **Content-Type** : `application/json` 
+  **HTTP Status Code** : `200` for healthy, appropriate error codes for unhealthy states

```
{
  "status": "Healthy",
  "time_of_last_update": 1640995200
}
```

## Authentication requirements


AGUI agents support multiple authentication mechanisms:

### OAuth 2.0 Bearer Tokens


For AGUI client authentication, include the Bearer token in request headers:

```
Authorization: Bearer <oauth-token>
X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: <session-id>
```

### SigV4 Authentication


Standard AWS SigV4 authentication is also supported for programmatic access.

## Error handling


Errors are classified into two categories based on when they occur:
+  **Connection-level errors** : Occur before the request reaches your container (authentication, validation, throttling). These return standard HTTP status codes.
+  **Runtime errors** : Occur during agent execution after the stream has started. These surface as `RUN_ERROR` events in the SSE stream rather than HTTP status codes.


| AGUI Error Code | HTTP Status | Description | 
| --- | --- | --- | 
|   `UNAUTHORIZED`   |  401  |  Authentication required or invalid credentials  | 
|   `ACCESS_DENIED`   |  403  |  Insufficient permissions for requested operation  | 
|   `VALIDATION_ERROR`   |  400  |  Invalid request data or parameters  | 
|   `RATE_LIMIT_EXCEEDED`   |  429  |  Too many requests from client  | 
|   `AGENT_ERROR`   |  200  |  Agent code failed during execution – check your CloudWatch logs  | 

Example runtime error (agent failure):

```
HTTP/1.1 200 OK
Content-Type: text/event-stream
x-amzn-requestid: 8bg30e9c-7e26-6bge-dc4b-75h368cc10cf

data: {"type":"RUN_ERROR","code":"AGENT_ERROR","message":"Agent execution failed"}
```

## OAuth authentication responses


OAuth-configured agents return authentication errors with standard HTTP status codes. The response includes a `WWW-Authenticate` header (per [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235) ) for OAuth discovery through the GetRuntimeProtectedResourceMetadata API.

Example OAuth authentication error:

```
HTTP/1.1 401 Unauthorized
Content-Type: text/event-stream
WWW-Authenticate: Bearer resource_metadata="https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{ESCAPED_ARN}/invocations/.well-known/oauth-protected-resource?qualifier={QUALIFIER}"
x-amzn-requestid: 8bg30e9c-7e26-6bge-dc4b-75h368cc10cf

data: {"type":"RUN_ERROR","code":"UNAUTHORIZED","message":"Authentication required"}
```

SigV4-configured agents return HTTP 403 with an `ACCESS_DENIED` error and do not include `WWW-Authenticate` headers.

# IAM Permissions for AgentCore Runtime
IAM Permissions for AgentCore Runtime

The following are IAM permissions you need to create an agent in an AgentCore Runtime and the execution role permissions that an agent needs to run in an AgentCore Runtime. You can also use resource-based policies to control access to your runtime resources.

For information about using resource-based policies to control access to your AgentCore Runtime resources, see [Resource-based policies for Amazon Bedrock AgentCore](security.md#resource-based-policies).

**Topics**
+ [

## Use Amazon Bedrock AgentCore
](#runtime-permissions-use-agentcore)
+ [

## Use the AgentCore CLI
](#runtime-permissions-cli)
+ [

## User permissions for Amazon Bedrock AgentCore Console
](#runtime-permissions-console)
+ [

## Execution role for running an agent in AgentCore Runtime
](#runtime-permissions-execution)

## Use Amazon Bedrock AgentCore


To use Amazon Bedrock AgentCore, you can attach the [BedrockAgentCoreFullAccess](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/BedrockAgentCoreFullAccess.html) AWS managed policy to your IAM user or IAM. role. This AWS managed policy grants broad permissions. We recommend creating a custom policy with only the permissions your application requires by copying the relevant statements and restricting the resources to your specific use case. To use the AgentCore CLI, you need [additional](#runtime-permissions-cli) permissions.

## Use the AgentCore CLI


To use the AgentCore CLI, attach the following IAM policy to your IAM user or role. To change IAM permissions, see [Change permissions for an IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_change-permissions.html).

**Important**  
The IAM policies created by the AgentCore CLI are designed for development and testing purposes. These permissions grant broad access to facilitate rapid prototyping and are not suitable for production environments. For production deployments, create custom IAM policies that follow the principle of least privilege and restrict permissions to only the specific resources and actions required by your Amazon Bedrock AgentCore application.

```
{
"Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "IAMRoleManagement",
            "Effect": "Allow",
            "Action": [
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:GetRole",
                "iam:PutRolePolicy",
                "iam:DeleteRolePolicy",
                "iam:AttachRolePolicy",
                "iam:DetachRolePolicy",
                "iam:TagRole",
                "iam:ListRolePolicies",
                "iam:ListAttachedRolePolicies"
            ],
            "Resource": [
                "arn:aws:iam::*:role/*BedrockAgentCore*",
                "arn:aws:iam::*:role/service-role/*BedrockAgentCore*"
            ]
        },
        {
            "Sid": "CodeBuildProjectAccess",
            "Effect": "Allow",
            "Action": [
                "codebuild:StartBuild",
                "codebuild:BatchGetBuilds",
                "codebuild:ListBuildsForProject",
                "codebuild:CreateProject",
                "codebuild:UpdateProject",
                "codebuild:BatchGetProjects"
            ],
            "Resource": [
                "arn:aws:codebuild:*:*:project/bedrock-agentcore-*",
                "arn:aws:codebuild:*:*:build/bedrock-agentcore-*"
            ]
        },
        {
            "Sid": "CodeBuildListAccess",
            "Effect": "Allow",
            "Action": [
                "codebuild:ListProjects"
            ],
            "Resource": "*"
        },
        {
            "Sid": "IAMPassRoleAccess",
            "Effect": "Allow",
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::*:role/AmazonBedrockAgentCore*",
                "arn:aws:iam::*:role/service-role/AmazonBedrockAgentCore*"
            ]
        },
        {
            "Sid": "CloudWatchLogsAccess",
            "Effect": "Allow",
            "Action": [
                "logs:GetLogEvents",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams"
            ],
            "Resource": [
                "arn:aws:logs:*:*:log-group:/aws/bedrock-agentcore/*",
                "arn:aws:logs:*:*:log-group:/aws/codebuild/*"
            ]
        },
        {
            "Sid": "S3Access",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:ListBucket",
                "s3:CreateBucket",
                "s3:PutLifecycleConfiguration"
            ],
            "Resource": [
                "arn:aws:s3:::bedrock-agentcore-*",
                "arn:aws:s3:::bedrock-agentcore-*/*"
            ]
        },
        {
            "Sid": "ECRRepositoryAccess",
            "Effect": "Allow",
            "Action": [
                "ecr:CreateRepository",
                "ecr:DescribeRepositories",
                "ecr:GetRepositoryPolicy",
                "ecr:InitiateLayerUpload",
                "ecr:CompleteLayerUpload",
                "ecr:PutImage",
                "ecr:UploadLayerPart",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:ListImages",
                "ecr:TagResource"
            ],
            "Resource": [
                "arn:aws:ecr:*:*:repository/bedrock-agentcore-*"
            ]
        },
        {
            "Sid": "ECRAuthorizationAccess",
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken"
            ],
            "Resource": "*"
        }
    ]
}
```

## User permissions for Amazon Bedrock AgentCore Console


Attach the [BedrockAgentCoreFullAccess](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/BedrockAgentCoreFullAccess.html) policy to the console role. Additionally, add the following permissions for IAM if you want service console to auto-create the execution role.

```
{
"Version": "2012-10-17",		 	 	 
  "Statement": [{
    "Sid": "IAMRoleAccess",
    "Effect": "Allow",
    "Action": ["iam:CreateRole"],
    "Resource": ["arn:aws:iam::*:role/service-role/AmazonBedrockAgentCoreRuntimeDefaultServiceRole-*"]
  }, {
    "Sid": "IAMPolicyAccess",
    "Effect": "Allow",
    "Action": ["iam:CreatePolicy"],
    "Resource": ["arn:aws:iam::*:policy/service-role/AmazonBedrockAgentCoreRuntimeExecutionPolicy_*"]
  }, {
    "Sid": "IAMRolePolicyAccess",
    "Effect": "Allow",
    "Action": ["iam:AttachRolePolicy"],
    "Resource": ["arn:aws:iam::*:role/service-role/AmazonBedrockAgentCoreRuntimeDefaultServiceRole-*"],
    "Condition": {
      "ArnLike": {
        "iam:PolicyARN": "arn:aws:iam::*:policy/service-role/AmazonBedrockAgentCoreRuntimeExecutionPolicy_*"
      }
    }
  }]
}
```

## Execution role for running an agent in AgentCore Runtime


To run agent or tool in AgentCore Runtime you need an AWS Identity and Access Management execution role. For information about creating an IAM role, see [IAM role creation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create.html).

### Amazon Bedrock AgentCore direct deploy execution role


The Amazon Bedrock AgentCore direct deploy execution role is an IAM role that Amazon Bedrock AgentCore assumes to run an agent. Replace the following:
+  `us-east-1` with the AWS Region that you are using
+  `123456789012` with your AWS account ID

```
{
"Version": "2012-10-17",		 	 	 
  "Statement": [{
    "Effect": "Allow",
    "Action": [
      "logs:DescribeLogStreams",
      "logs:CreateLogGroup"
    ],
    "Resource": [
      "arn:aws:logs:us-east-1:123456789012:log-group:/aws/bedrock-agentcore/runtimes/*"
    ]
  }, {
    "Effect": "Allow",
    "Action": ["logs:DescribeLogGroups"],
    "Resource": ["arn:aws:logs:us-east-1:123456789012:log-group:*"]
  }, {
    "Effect": "Allow",
    "Action": [
      "logs:CreateLogStream",
      "logs:PutLogEvents"
    ],
    "Resource": [
      "arn:aws:logs:us-east-1:123456789012:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*"
    ]
  }, {
    "Effect": "Allow",
    "Action": [
      "xray:PutTraceSegments",
      "xray:PutTelemetryRecords",
      "xray:GetSamplingRules",
      "xray:GetSamplingTargets"
    ],
    "Resource": ["*"]
  }, {
    "Effect": "Allow",
    "Resource": "*",
    "Action": "cloudwatch:PutMetricData",
    "Condition": {
      "StringEquals": {
        "cloudwatch:namespace": "bedrock-agentcore"
      }
    }
  }, {
    "Sid": "BedrockModelInvocation",
    "Effect": "Allow",
    "Action": [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream"
    ],
    "Resource": [
      "arn:aws:bedrock:*::foundation-model/*",
      "arn:aws:bedrock:us-east-1:123456789012:*"
    ]
  }]
}
```

### AgentCore Runtime execution role


The AgentCore Runtime execution role is an IAM role that AgentCore Runtime assumes to run an agent. Replace the following:
+  `us-east-1` with the AWS Region that you are using
+  `123456789012` with your AWS account ID
+  *agentName* with the name of your agent. You’ll need to decide the agent name before creating the role and AgentCore Runtime.

```
{
"Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "ECRImageAccess",
            "Effect": "Allow",
            "Action": [
                "ecr:BatchGetImage",
                "ecr:GetDownloadUrlForLayer"
            ],
            "Resource": [
                "arn:aws:ecr:us-east-1:123456789012:repository/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:DescribeLogStreams",
                "logs:CreateLogGroup"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:123456789012:log-group:/aws/bedrock-agentcore/runtimes/*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:DescribeLogGroups"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:123456789012:log-group:*"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:123456789012:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*"
            ]
        },
        {
            "Sid": "ECRTokenAccess",
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken"
            ],
            "Resource": "*"
        },
        {
        "Effect": "Allow",
        "Action": [
            "xray:PutTraceSegments",
            "xray:PutTelemetryRecords",
            "xray:GetSamplingRules",
            "xray:GetSamplingTargets"
            ],
         "Resource": [ "*" ]
         },
         {
            "Effect": "Allow",
            "Resource": "*",
            "Action": "cloudwatch:PutMetricData",
            "Condition": {
                "StringEquals": {
                    "cloudwatch:namespace": "bedrock-agentcore"
                }
            }
        },
        {
            "Sid": "GetAgentAccessToken",
            "Effect": "Allow",
            "Action": [
                "bedrock-agentcore:GetWorkloadAccessToken",
                "bedrock-agentcore:GetWorkloadAccessTokenForJWT",
                "bedrock-agentcore:GetWorkloadAccessTokenForUserId"
            ],
            "Resource": [
              "arn:aws:bedrock-agentcore:us-east-1:123456789012:workload-identity-directory/default",
              "arn:aws:bedrock-agentcore:us-east-1:123456789012:workload-identity-directory/default/workload-identity/agentName-*"
            ]
        },
         {"Sid": "BedrockModelInvocation",
         "Effect": "Allow",
         "Action": [
                "bedrock:InvokeModel",
                "bedrock:InvokeModelWithResponseStream"
              ],
        "Resource": [
            "arn:aws:bedrock:*::foundation-model/*",
            "arn:aws:bedrock:us-east-1:123456789012:*"
        ]
        }
    ]
}
```

### AgentCore Runtime trust policy


The AgentCore Runtime execution role must include the following trust policy which allows the AgentCore Runtime to assume the role.

In the policy, replace:
+  `us-east-1` with the AWS Region that you are using
+  `123456789012` with your AWS account ID

To add the trust policy to the AgentCore Runtime execution role, go to the AWS Management Console, navigate to the role, choose the **Trust relationships** tab, and choose **Edit trust policy**.

```
{
"Version":"2012-10-17",		 	 	 
  "Statement": [
    {
      "Sid": "AssumeRolePolicy",
      "Effect": "Allow",
      "Principal": {
        "Service": "bedrock-agentcore.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
            "StringEquals": {
                "aws:SourceAccount": "123456789012"
            },
            "ArnLike": {
                "aws:SourceArn": "arn:aws:bedrock-agentcore:us-east-1:123456789012:*"
            }
       }
    }
  ]
}
```

# Get started with AgentCore Runtime
Get started with AgentCore Runtime

You can use the following tutorials to get started with Amazon Bedrock AgentCore Runtime.

The [AgentCore CLI](https://github.com/aws/agentcore-cli) is a Command Line Interface (CLI) that simplifies the infrastructure setup for containerizing and deploying an agent to an AgentCore Runtime.

**Topics**
+ [

# Get started with the AgentCore CLI
](runtime-get-started-cli.md)
+ [

# Get started with the AgentCore CLI in TypeScript
](runtime-get-started-cli-typescript.md)
+ [

# Get started without the AgentCore CLI
](getting-started-custom.md)
+ [

# Get started with Amazon Bedrock AgentCore Runtime direct code deployment
](runtime-get-started-code-deploy.md)
+ [

# Get started with bidirectional streaming using WebSocket
](runtime-get-started-websocket.md)

# Get started with the AgentCore CLI
Get started with AgentCore CLI

This tutorial shows you how to use the [AgentCore CLI](https://github.com/aws/agentcore-cli) to create, deploy, and invoke a Python agent on Amazon Bedrock AgentCore Runtime.

The AgentCore CLI is a command-line tool that scaffolds agent projects, deploys them to Amazon Bedrock AgentCore Runtime, and invokes them. You can use the CLI with popular Python agent frameworks such as [Strands Agents](https://strandsagents.com/latest/documentation/docs/) , LangChain/LangGraph, Google ADK, and OpenAI Agents. This tutorial uses Strands Agents.

For information about the HTTP protocol that the agent uses, see [HTTP protocol contract](runtime-http-protocol-contract.md).

**Topics**
+ [

## Prerequisites
](#prerequisites)
+ [

## Step 1: Install the AgentCore CLI
](#setup-project)
+ [

## Step 2: Create your agent project
](#create-agent)
+ [

## Step 3: Test your agent locally
](#configure-agent)
+ [

## Step 4: Enable observability for your agent
](#enable-observability)
+ [

## Step 5: Deploy to Amazon Bedrock AgentCore Runtime
](#deploy-runtime)
+ [

## Step 6: Test your deployed agent
](#test-deployed-agent)
+ [

## Step 7: Invoke your deployed agent
](#invoke-programmatically)
+ [

## Step 8: Clean up
](#stop-session-or-clean-up)
+ [

## Find your resources
](#find-resources)
+ [

## Common issues and solutions
](#common-issues)
+ [

## Advanced options (Optional)
](#advanced-options)

## Prerequisites


Before you start, make sure you have:
+  ** AWS Account** with credentials configured. To configure your AWS credentials, see [Configuration and credential file settings in the AWS CLI.](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) 
+  **Node.js 20\$1** installed. The AgentCore CLI is distributed as an npm package.
+  **Python 3.10\$1** installed. The generated agent code is Python.
+  ** AWS CDK** installed. The CLI uses the AWS CDK to deploy resources. For information, see [Getting started with the AWS CDK](https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html).
+  ** AWS Permissions** : To create and deploy an agent with the AgentCore CLI, you must have appropriate permissions. For information, see [Use the AgentCore CLI](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html#runtime-permissions-cli).
+  **Model access** : Anthropic Claude Sonnet 4.0 [enabled](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access-modify.html) in the Amazon Bedrock console (if using Bedrock as the model provider). For information about using a different model with Strands Agents, see the *Model Providers* section in the [Strands Agents SDK](https://strandsagents.com/latest/documentation/docs/) documentation.

## Step 1: Install the AgentCore CLI


Install the AgentCore CLI globally:

```
npm install -g @aws/agentcore
```

Verify the installation:

```
agentcore --help
```

You should see output similar to the following:

```
Usage: agentcore [options] [command]

Build and deploy Agentic AI applications on AgentCore

Options:
  -V, --version                output the version number
  -h, --help                   Display help

Commands:
  add [subcommand]             Add resources (agent, evaluator, online-eval,
                               memory, identity, target)
  dev|d [options]              Launch local development server with hot-reload.
  deploy|p [options]           Deploy project infrastructure to AWS via CDK.
  create [options]             Create a new AgentCore project
  evals                        View past eval run results.
  fetch                        Fetch access info for deployed resources.
  help                         Display help topics
  invoke|i [options] [prompt]  Invoke a deployed agent endpoint.
  logs|l [options]             Stream or search agent runtime logs.
  package|pkg [options]        Package agent artifacts without deploying.
  pause                        Pause an online eval config.
  remove [subcommand]          Remove resources from project config.
  resume                       Resume a paused online eval config.
  run                          Run on-demand evaluation.
  status|s [options]           Show deployed resource details and status.
  traces|t                     View and download agent traces.
  update [options]             Check for and install CLI updates
  validate [options]           Validate agentcore/ config files.
```

## Step 2: Create your agent project


Use the `agentcore create` command to scaffold a new agent project:

**Example**  

1. Pass flags directly to create a project non-interactively:

   ```
   agentcore create --name MyAgent --framework Strands --protocol HTTP --model-provider Bedrock --memory none
   ```

   To accept all defaults (Python, Strands, Bedrock, no memory), use the `--defaults` flag:

   ```
   agentcore create --name MyAgent --defaults
   ```

1. Run `agentcore create` without flags to launch the interactive wizard:

   ```
   agentcore create
   ```

1. Enter your project name:  
![\[Create wizard: enter project name\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-create-name.png)

1. Choose your agent framework and model provider:  
![\[Create wizard: select framework\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-create-framework.png)

1. Review your configuration and confirm:  
![\[Create wizard: review and confirm\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-create-confirm.png)

The `agentcore create` command accepts the following flags:
+  `--name` – The project name (alphanumeric, starts with a letter, max 36 characters).
+  `--framework` – The agent framework. Supported values: `Strands` , `LangChain_LangGraph` , `GoogleADK` , `OpenAIAgents`.
+  `--protocol` – The protocol mode. Supported values: `HTTP` (default), `MCP` , `A2A`.
+  `--build` – The build type. Supported values: `CodeZip` (default), `Container`.
+  `--model-provider` – The model provider. Supported values: `Bedrock` , `Anthropic` , `OpenAI` , `Gemini`.
+  `--memory` – Memory configuration. Supported values: `none` , `shortTerm` , `longAndShortTerm`.

The command generates a project directory with the following structure:

```
MyAgent/
  agentcore/
    agentcore.json        # Project and agent configuration
    aws-targets.json      # AWS account and region targets
    .env.local            # Local environment variables (gitignored)
  app/
    MyAgent/
      main.py             # Agent entrypoint
      pyproject.toml      # Python dependencies
  README.md
```

The `agentcore/agentcore.json` file contains your project and agent configuration. The `app/MyAgent/main.py` file contains starter agent code using your selected framework.

## Step 3: Test your agent locally


Before deploying to AWS, test your agent locally using the development server. First, change into the project directory:

```
cd MyAgent
```

If you selected a model provider that requires an API key (OpenAI, Anthropic, or Gemini), make sure the key is configured in `agentcore/.env.local`.

Start the local development server:

**Example**  

1. 

   ```
   agentcore dev
   ```

1. Run `agentcore` to open the TUI home screen, then navigate to the dev server option:

   ```
   agentcore
   ```  
![\[Dev server TUI with inline chat prompt\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-dev-server.png)

The `agentcore dev` command:
+ Automatically creates a Python virtual environment and installs dependencies
+ Starts a local server that mimics the AgentCore Runtime environment
+ Runs on `http://localhost:8080` by default (use `-p` to change the port)

In a separate terminal, invoke your local agent:

```
agentcore dev "Hello, tell me a joke"
```

Passing a prompt sends it to the running local development server. Use `--stream` to see the response streamed in real time.

## Step 4: Enable observability for your agent


 [Amazon Bedrock AgentCore Observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability.html) helps you trace, debug, and monitor agents that you host in Amazon Bedrock AgentCore Runtime. First enable CloudWatch Transaction Search by following the instructions at [Enabling Amazon Bedrock AgentCore runtime observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-configure.html#observability-configure-builtin) . To observe your agent, see [View observability data for your Amazon Bedrock AgentCore agents](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-view.html).

After you deploy your agent, you can use the AgentCore CLI to stream logs and view traces:

```
# Stream agent logs
agentcore logs

# List recent traces
agentcore traces list
```

## Step 5: Deploy to Amazon Bedrock AgentCore Runtime


Deploy your agent to Amazon Bedrock AgentCore Runtime:

**Example**  

1. 

   ```
   agentcore deploy
   ```

1. Run `agentcore deploy` to start deployment. The CLI shows the deployment progress as it builds and deploys your project:

   ```
   agentcore deploy
   ```  
![\[Deploy progress: CloudFormation resource creation and deployment status\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-deploy-progress.png)

To preview the deployment without making changes, use the `--plan` flag:

```
agentcore deploy --plan
```

The `agentcore deploy` command:
+ Reads your `agentcore/agentcore.json` and `agentcore/aws-targets.json` configuration
+ Packages your agent code (as a CodeZip archive or Docker container, depending on your build type)
+ Uses the AWS CDK to synthesize and deploy CloudFormation resources
+ Creates the necessary AWS resources (IAM roles, Amazon Bedrock AgentCore Runtime, etc.)

Use `-v` for verbose output that shows resource-level deployment events. Use `-y` to auto-confirm the deployment without a prompt.

If the deployment fails, check for [common issues](#common-issues).

## Step 6: Test your deployed agent


After deployment completes, invoke your deployed agent:

**Example**  

1. 

   ```
   agentcore invoke "Tell me a joke"
   ```

   You can also pass the prompt with the `--prompt` flag, specify a runtime with `--runtime` , or stream the response in real time with `--stream` :

   ```
   agentcore invoke --prompt "Tell me a joke" --stream
   ```

   To maintain a conversation across multiple invocations, use the `--session-id` flag:

   ```
   agentcore invoke --session-id my-session "What else can you tell me?"
   ```

1. Run `agentcore` to open the TUI home screen, then select the invoke option to chat with your deployed agent:

   ```
   agentcore
   ```  
![\[Invoke TUI screen showing chat interface\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-invoke-chat.png)

If you see a joke in the response, your agent is running in Amazon Bedrock AgentCore Runtime and can be invoked. If not, check for [common issues](#common-issues).

## Step 7: Invoke your deployed agent


**Example**  

1. Invoke your deployed agent with a prompt:

   ```
   agentcore invoke --runtime MyAgent "Hello, what can you do?"
   ```

   Stream the response in real time:

   ```
   agentcore invoke --runtime MyAgent "Tell me a joke" --stream
   ```

   Run `agentcore invoke` without a prompt to open the interactive chat TUI, which streams responses by default and maintains your session automatically.

1. You can also invoke the agent using the AWS SDK [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) operation. To get the ARN of your deployed agent, use the `agentcore status` command:

   ```
   agentcore status
   ```

   Use the following boto3 (AWS SDK for Python) code to invoke your agent. Replace *Agent ARN* with the ARN of your agent. Make sure that you have `bedrock-agentcore:InvokeAgentRuntime` permissions. Create a file named `invoke_agent.py` and add the following code:

   ```
   import json
   import uuid
   import boto3
   
   agent_arn = "Agent ARN"
   prompt = "Tell me a joke"
   
   # Initialize the Amazon Bedrock AgentCore client
   agent_core_client = boto3.client('bedrock-agentcore')
   
   # Prepare the payload
   payload = json.dumps({"prompt": prompt}).encode()
   
   # Invoke the agent
   response = agent_core_client.invoke_agent_runtime(
       agentRuntimeArn=agent_arn,
       runtimeSessionId=str(uuid.uuid4()),
       payload=payload,
       qualifier="DEFAULT"
   )
   
   content = []
   for chunk in response.get("response", []):
       content.append(chunk.decode('utf-8'))
   print(json.loads(''.join(content)))
   ```

   Open a terminal window and run the code with the following command:

   ```
   python invoke_agent.py
   ```

   If successful, you should see a joke in the response. If the call fails, check the logs using `agentcore logs` or view them in Amazon CloudWatch.
**Note**  
If you plan on integrating your agent with OAuth, you can’t use the AWS SDK to call `InvokeAgentRuntime` . Instead, make a HTTPS request to `InvokeAgentRuntime` . For more information, see [Authenticate and authorize with Inbound Auth and Outbound Auth](runtime-oauth.md).

## Step 8: Clean up


If you no longer want to host the agent in Amazon Bedrock AgentCore Runtime, remove the deployed AWS resources. First, remove all resources from your local configuration:

**Example**  

1. 

   ```
   agentcore remove all
   ```

1. Run `agentcore` to open the TUI home screen, then select the remove option to choose which resources to remove:

   ```
   agentcore
   ```  
![\[Remove resource selection TUI\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-remove-resource.png)

Then deploy again to tear down the AWS resources:

**Example**  

1. 

   ```
   agentcore deploy
   ```

1. From the AgentCore CLI home screen, select `deploy` to apply the removal and tear down AWS resources:  
![\[Deploy progress: CloudFormation resource deletion and teardown status\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-deploy-teardown.png)

The `remove all` command resets the `agentcore/agentcore.json` configuration file while preserving `agentcore/aws-targets.json` and deployment state. The subsequent `deploy` detects the removed resources and tears down the corresponding AWS resources.

## Find your resources


After deployment, you can check the status of your resources by using the AgentCore CLI:

**Example**  

1. 

   ```
   agentcore status
   ```

1. Run `agentcore` and select `status` to view a live dashboard of all deployed resources:

   ```
   agentcore
   ```  
![\[AgentCore CLI TUI status dashboard\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-status-dashboard.png)

You can also view your resources in the AWS Console:


| Resource | Location | 
| --- | --- | 
|   **Agent Logs**   |  CloudWatch → Log groups → `/aws/bedrock-agentcore/runtimes/{agent-id}-DEFAULT`   | 
|   **CloudFormation Stack**   |  CloudFormation → Stacks → search for your project name  | 
|   **IAM Role**   |  IAM → Roles → Search for "BedrockAgentCore"  | 
|   **S3 Assets (CodeZip)**   |  S3 → Buckets → CDK staging bucket  | 

## Common issues and solutions


Common issues and solutions when getting started with the AgentCore CLI. For more troubleshooting information, see [Troubleshoot Amazon Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-troubleshooting.html).

 **Permission denied errors**   
Verify your AWS credentials and permissions:  
+ Verify AWS credentials: `aws sts get-caller-identity` 
+ Check you have the required policies attached
+ Review caller permissions policy for detailed requirements

 **Model access denied**   
Enable model access in the Bedrock console:  
+ Enable Anthropic Claude 4.0 in the Bedrock console
+ Make sure you’re in the correct AWS Region (us-west-2 by default)

 **CDK deployment errors**   
Check CDK setup and permissions:  
+ Make sure you have bootstrapped your AWS account for CDK: `cdk bootstrap` 
+ Verify your caller permissions include CloudFormation and CDK access
+ Use `agentcore deploy -v` for verbose output to identify the failing resource

 **Port 8080 in use (local only)**   
Find and stop processes that are using port 8080:  
Use `lsof -ti:8080` to get a list of processes using port 8080.  
Use `kill -9 PID` to stop the process. Replace *PID* with the process ID.  
Alternatively, start the dev server on a different port: `agentcore dev -p 3000` 

 **Region mismatch**   
Verify the AWS Region with `aws configure get region` and make sure the region in `agentcore/aws-targets.json` matches where your resources should be deployed.

 **Configuration validation errors**   
Validate your configuration files:  
Use `agentcore validate` to check for syntax or schema errors in `agentcore/agentcore.json` and related configuration files.

## Advanced options (Optional)


After creating your agent project with `agentcore create` , you can extend it by using the `agentcore add` commands. For the full CLI reference, see the [AgentCore CLI documentation](https://github.com/aws/agentcore-cli).

### Build types


When creating your project, choose a build type that fits your needs:

 **CodeZip (default)**   
Your agent code is packaged as a zip archive and uploaded to S3. This is the simplest option and does not require Docker:  

```
agentcore create --name MyAgent --framework Strands --model-provider Bedrock --memory none --build CodeZip
```

 **Container**   
Your agent code is packaged as a Docker container image. Use this option when you need custom system-level dependencies or a specific base image:  

```
agentcore create --name MyAgent --framework Strands --model-provider Bedrock --memory none --build Container
```

### Add resources to your project


You can add additional resources to your project after creation:

```
# Add another agent to the same project
agentcore add agent --name SecondAgent --language Python --framework Strands --model-provider Bedrock

# Add a memory store for conversational context
agentcore add memory --name MyMemory --strategies SEMANTIC

# Add an API key credential for external services
agentcore add credential --name MyApiKey --type api-key --api-key your-api-key
```

After adding resources, run `agentcore deploy` to provision the new resources in AWS.

### Why ARM64?


Amazon Bedrock AgentCore Runtime runs on ARM64 (AWS Graviton). The AgentCore CLI handles architecture compatibility automatically for both the CodeZip and Container build types. For Container builds, only images built for ARM64 will work when deployed to Amazon Bedrock AgentCore Runtime.

# Get started with the AgentCore CLI in TypeScript
Get started with AgentCore CLI (TypeScript)

This tutorial shows you how to use the AgentCore CLI ( [agentcore-cli](https://github.com/aws/agentcore-cli) ) to deploy a TypeScript agent to an Amazon Bedrock AgentCore Runtime.

The AgentCore CLI is a command-line tool that you can use to create, develop, and deploy AI agents to an Amazon Bedrock AgentCore Runtime. This tutorial shows you how to create and deploy a TypeScript agent using the [bedrock-agentcore](https://www.npmjs.com/package/bedrock-agentcore) SDK.

For information about the HTTP protocol that the agent uses, see [HTTP protocol contract](runtime-http-protocol-contract.md).

**Topics**
+ [

## Prerequisites
](#ts-prerequisites)
+ [

## Step 1: Install the AgentCore CLI and create a project
](#ts-setup-project)
+ [

## Step 2: Create your TypeScript agent
](#ts-create-agent)
+ [

## Step 3: Review your project configuration
](#ts-configure-agent)
+ [

## Step 4: Test your agent locally
](#ts-test-locally)
+ [

## Step 5: Enable observability for your agent
](#ts-enable-observability)
+ [

## Step 6: Deploy to Amazon Bedrock AgentCore Runtime
](#ts-deploy-runtime)
+ [

## Step 7: Test your deployed agent
](#ts-test-deployed-agent)
+ [

## Step 8: Invoke your agent programmatically
](#ts-invoke-programmatically)
+ [

## Step 9: Stop session or clean up
](#ts-stop-session-or-clean-up)
+ [

## Find your resources
](#ts-find-resources)
+ [

## Common issues and solutions
](#ts-common-issues)
+ [

## Advanced: Streaming responses
](#ts-streaming-responses)

## Prerequisites


Before you start, make sure you have:
+  ** AWS Account** with credentials configured. To configure your AWS credentials, see [Configuration and credential file settings in the AWS CLI.](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) 
+  **Node.js 20\$1** installed
+  **Docker** (or [Podman](https://podman.io) or [Finch](https://runfinch.com) ) installed. A container runtime is required for building and testing your TypeScript agent locally. A local container runtime is not required for `agentcore deploy` because AWS CodeBuild builds the image remotely.
+  ** AWS Permissions** : To create and deploy an agent with the AgentCore CLI, you must have appropriate permissions. For information, see [Use the AgentCore CLI](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html#runtime-permissions-cli).
+  **Model access** : If your agent uses Amazon Bedrock models, ensure the required models are [enabled](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access-modify.html) in the Amazon Bedrock console.

## Step 1: Install the AgentCore CLI and create a project


Install the AgentCore CLI globally:

```
npm install -g @aws/agentcore
```

Verify installation:

```
agentcore --help
```

To deploy a TypeScript agent, use container-based deployment. Create a project without a default agent. You will add a TypeScript container agent in the next step.

**Example**  

1. 

   ```
   agentcore create --name MyTsAgent --no-agent
   ```

1. Run `agentcore create` without flags to launch the interactive wizard. Select "No" when asked to add an agent — you will add a TypeScript container agent in the next step:

   ```
   agentcore create
   ```  
![\[Create wizard: enter project name\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-create-name.png)

This command creates a project directory with the following structure:

```
MyTsAgent/
  agentcore/
    agentcore.json
    aws-targets.json
```

Change into the project directory:

```
cd MyTsAgent
```

## Step 2: Create your TypeScript agent


This tutorial uses the [Strands Agents](https://strandsagents.com/latest/) framework, which is currently the supported framework for TypeScript agents deployed to AgentCore Runtime.

Add a TypeScript agent to your project using the bring-your-own-code (BYO) workflow with container-based deployment:

**Example**  

1. 

   ```
   agentcore add agent --name TsAgent --type byo --build Container --language TypeScript --framework Strands --model-provider Bedrock --code-location app/TsAgent
   ```

1. Run `agentcore add agent` to launch the interactive wizard. Select **Bring my own code** as the agent type:

   ```
   agentcore add agent
   ```

1. Select **Bring my own code** as the agent type:  
![\[Add agent wizard: select BYO type\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/runtime-ts-add-agent-byo.png)

1. Set the code location for your agent:  
![\[Add agent wizard: code location\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/runtime-ts-add-agent-code.png)

1. Review the configuration and confirm:  
![\[Add agent wizard: review and confirm\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/runtime-ts-add-agent-confirm.png)

This command registers a TypeScript agent in `agentcore/agentcore.json` and creates a code directory for your agent at `app/TsAgent/`.

Initialize the TypeScript project inside the agent code directory:

```
cd app/TsAgent
npm init -y
```

Install the required packages:
+  **bedrock-agentcore** - The Amazon Bedrock AgentCore SDK for building AI agents in TypeScript
+  **@strands-agents/sdk** - The [Strands Agents](https://strandsagents.com/latest/) SDK
+  **@opentelemetry/auto-instrumentations-node** - OpenTelemetry instrumentation for observability
+  **typescript** - TypeScript compiler

```
npm install bedrock-agentcore @strands-agents/sdk @opentelemetry/auto-instrumentations-node --legacy-peer-deps
npm install --save-dev typescript @types/node tsx
```

**Note**  
The `--legacy-peer-deps` flag resolves peer dependency conflicts between `@strands-agents/sdk` and `@opentelemetry/auto-instrumentations-node` . This is safe and does not affect runtime behavior.

Create a `tsconfig.json` file with the following content:

```
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": ".",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "declaration": true
  },
  "include": ["*.ts"],
  "exclude": ["node_modules", "dist"]
}
```

Update your `package.json` to add the required configuration:

```
{
  "name": "ts-agent",
  "version": "1.0.0",
  "type": "module",
  "main": "dist/agent.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/agent.js",
    "dev": "npx tsx --watch agent.ts"
  },
  "engines": {
    "node": ">=20.0.0"
  },
  "dependencies": {
    "bedrock-agentcore": "^0.2.0",
    "@strands-agents/sdk": "^0.1.5",
    "@opentelemetry/auto-instrumentations-node": "^0.56.0"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "typescript": "^5.5.0",
    "tsx": "^4.0.0"
  }
}
```

Create an `agent.ts` file with your agent code:

```
import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
import { Agent } from '@strands-agents/sdk';

// Create a Strands agent
const agent = new Agent();

// Create and start the server
const app = new BedrockAgentCoreApp({
  invocationHandler: {
    process: async (payload, context) => {
      const prompt = (payload as { prompt?: string }).prompt ?? 'Hello!';
      console.log(`Session ${context.sessionId} - Received prompt:`, prompt);

      const result = await agent.invoke(prompt);
      return result;
    }
  }
});

app.run();
```

Create a `Dockerfile` in the `app/TsAgent/` directory. Container-based deployment uses a multi-stage Docker build: the builder stage compiles TypeScript to JavaScript, and the production stage runs only the compiled output. The image runs as a non-root user for security, and exposes port 8080 (HTTP), port 8000 (MCP), and port 9000 (A2A). OpenTelemetry instrumentation is included automatically at startup.

```
FROM node:20-slim AS builder

WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY ..
RUN npm run build

FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./

RUN useradd -m -u 1000 bedrock_agentcore
USER bedrock_agentcore

EXPOSE 8080 8000 9000
CMD ["node", "--require", "@opentelemetry/auto-instrumentations-node/register", "dist/agent.js"]
```

Create a `.dockerignore` file to exclude unnecessary files from the Docker build context:

```
node_modules
dist
.git
*.log
```

Build locally to verify that your TypeScript code compiles. The container build will also run this step during deployment.

```
npm run build
```

Return to the project root directory:

```
cd ../..
```

## Step 3: Review your project configuration


The `agentcore create` and `agentcore add agent` commands generated the configuration files for your project. Open `agentcore/agentcore.json` to review the configuration:

```
{
  "name": "MyTsAgent",
  "version": 1,
  "agents": [
    {
      "type": "AgentCoreRuntime",
      "name": "TsAgent",
      "build": "Container",
      "entrypoint": "main.py",
      "codeLocation": "app/TsAgent/",
      "runtimeVersion": "PYTHON_3_12",
      "protocol": "HTTP",
      "networkMode": "PUBLIC"
    }
  ]
}
```

**Note**  
For container-based deployments, the `runtimeVersion` and `entrypoint` fields are present in the configuration but are not used at deployment time. The container image (via its `Dockerfile CMD` ) defines the actual runtime and entrypoint. The CLI sets these to Python defaults regardless of language.

You can also review your deployment target in `agentcore/aws-targets.json` :

```
[]
```

Before deploying, you must configure at least one deployment target. Open `agentcore/aws-targets.json` and add your AWS account and Region:

```
[
  {
    "name": "default",
    "account": "123456789012",
    "region": "us-west-2"
  }
]
```

Replace *123456789012* with your AWS account ID and update the Region as needed.

To validate that your configuration is correct, run:

```
agentcore validate
```

## Step 4: Test your agent locally


Before deploying to AWS, test your agent locally using the development server:

**Example**  

1. 

   ```
   agentcore dev
   ```

   If your project has multiple runtimes, use `--runtime` to start a specific runtime directly:

   ```
   agentcore dev --runtime TsAgent
   ```

   In a separate terminal, invoke your agent against the local dev server:

   ```
   agentcore dev "Hello, tell me a joke"
   ```

1. Run `agentcore` to open the TUI home screen, then navigate to the dev server option:

   ```
   agentcore
   ```  
![\[Dev server TUI with inline chat prompt\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-dev-server.png)
+ For container agents, this command builds the Docker image and runs the container locally with your source directory mounted for hot reload. Your AWS credentials are forwarded automatically to the container.
+ The server runs on `http://localhost:8080` by default. Use the `--port` flag to specify a different port.

**Note**  
If your project has multiple runtimes, `agentcore dev` opens an interactive selection menu. Use `agentcore dev --runtime TsAgent` to skip the menu and start a specific runtime directly.

## Step 5: Enable observability for your agent


 [Amazon Bedrock AgentCore Observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability.html) helps you trace, debug, and monitor agents that you host in Amazon Bedrock AgentCore Runtime. First enable CloudWatch Transaction Search by following the instructions at [Enabling Amazon Bedrock AgentCore runtime observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-configure.html#observability-configure-builtin) . To observe your agent, see [View observability data for your Amazon Bedrock AgentCore agents](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-view.html).

After deploying your agent, you can stream logs and view traces using the AgentCore CLI:

```
agentcore logs --runtime TsAgent --since 1h
```

```
agentcore traces list --runtime TsAgent
```

## Step 6: Deploy to Amazon Bedrock AgentCore Runtime


Deploy your agent to AgentCore Runtime:

**Example**  

1. 

   ```
   agentcore deploy
   ```

1. Run `agentcore deploy` to start deployment. The CLI shows the deployment progress as it builds and deploys your project:

   ```
   agentcore deploy
   ```  
![\[Deploy progress: CloudFormation resource creation and deployment status\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-deploy-progress.png)

You can preview the deployment before confirming by using the `--plan` flag:

```
agentcore deploy --plan
```

The `agentcore deploy` command:
+ Builds your container image using AWS CodeBuild and pushes it to Amazon ECR
+ Creates necessary AWS resources (IAM roles, ECR repository, etc.) using AWS CDK
+ Deploys your agent to Amazon Bedrock AgentCore Runtime
+ Configures CloudWatch logging

In the output from `agentcore deploy` note the following:
+ The Amazon Resource Name (ARN) of the agent. You need it to invoke the agent with the [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) operation.
+ The location of the logs in Amazon CloudWatch Logs

To check the status of your deployed resources, run:

**Example**  

1. 

   ```
   agentcore status
   ```

1. Run `agentcore` to open the TUI home screen, then select the status option to view your deployed resources dashboard:

   ```
   agentcore
   ```  
![\[Status dashboard TUI showing deployed resource details\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-status-dashboard.png)

If the deployment fails check for [common issues](#ts-common-issues).

## Step 7: Test your deployed agent


Test your deployed agent:

**Example**  

1. 

   ```
   agentcore invoke "tell me a joke"
   ```

   To stream the response in real time, use the `--stream` flag:

   ```
   agentcore invoke --stream "tell me a joke"
   ```

1. Run `agentcore` to open the TUI home screen, then select the invoke option to chat with your deployed agent:

   ```
   agentcore
   ```  
![\[Invoke TUI screen showing chat interface\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-invoke-chat.png)

If you see a response, your agent is now running in an Amazon Bedrock AgentCore Runtime and can be invoked. If not, check for [common issues](#ts-common-issues).

## Step 8: Invoke your agent programmatically


You can invoke the agent using the AWS SDK [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) operation. To call `InvokeAgentRuntime` , you need the ARN of the agent that you noted in Step 6. You can also find the ARN by running `agentcore status`.

Create a file named `invoke-agent.ts` and add the following code:

```
import {
  BedrockAgentCoreClient,
  InvokeAgentRuntimeCommand
} from '@aws-sdk/client-bedrock-agentcore';
import { randomUUID } from 'crypto';

const agentArn = 'Agent ARN';
const prompt = 'Tell me a joke';

// Initialize the Amazon Bedrock AgentCore client
const client = new BedrockAgentCoreClient({ region: 'us-west-2' });

// Invoke the agent
const command = new InvokeAgentRuntimeCommand({
  agentRuntimeArn: agentArn,
  runtimeSessionId: randomUUID(),
  payload: JSON.stringify({ prompt }),
  contentType: 'application/json',
  qualifier: 'DEFAULT',
});

const response = await client.send(command);
const textResponse = await response.response?.transformToString();

console.log('Response:', textResponse);
```

Run the code with the following command:

```
npx tsx invoke-agent.ts
```

If successful, you should see a response from your agent. If the call fails, check the logs that you noted in [Step 6: Deploy to Amazon Bedrock AgentCore Runtime](#ts-deploy-runtime).

**Note**  
If you plan on integrating your agent with OAuth, you can’t use the AWS SDK to call `InvokeAgentRuntime` . Instead, make a HTTPS request to `InvokeAgentRuntime` . For more information, see [Authenticate and authorize with Inbound Auth and Outbound Auth](runtime-oauth.md).

## Step 9: Stop session or clean up


To stop the running session before the configurable `IdleRuntimeSessionTimeout` (defaulted at 15 minutes) and save on any potential runaway costs, execute: `StopRuntimeSession` 

Create a file named `stop-runtime-session.ts` with the following content:

 **Example** 

```
import {
  BedrockAgentCoreClient,
  StopRuntimeSessionCommand
} from '@aws-sdk/client-bedrock-agentcore';
import { randomUUID } from 'crypto';

const client = new BedrockAgentCoreClient({ region: 'us-west-2' });
const agentArn = 'Agent ARN';
const command = new StopRuntimeSessionCommand({
  agentRuntimeArn: agentArn,
  runtimeSessionId: randomUUID(),
  qualifier: 'DEFAULT',
});

const response = await client.send(command);
console.log('Session stopped:', response);
```

Run the code with the following command:

```
npx tsx stop-runtime-session.ts
```

If you no longer want to host the agent in the AgentCore Runtime, remove all resources from your project configuration and then deploy to tear down the AWS resources:

**Example**  

1. 

   ```
   agentcore remove all --yes
   agentcore deploy
   ```

1. Run `agentcore` to open the TUI home screen, then select the remove option to choose which resources to remove:

   ```
   agentcore
   ```  
![\[Remove resource selection TUI\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/common-remove-resource.png)

## Find your resources


After deployment, view your resources in the AWS Console:


| Resource | Location | 
| --- | --- | 
|   **Agent Logs**   |  CloudWatch → Log groups → `/aws/bedrock-agentcore/runtimes/{agent-id}-DEFAULT`   | 
|   **Container Image**   |  Amazon ECR → Repositories → Search for your agent name  | 
|   **Deployed Status**   |  Run `agentcore status` to see all deployed resources  | 
|   **IAM Role**   |  IAM → Roles → Search for "BedrockAgentCore"  | 

## Common issues and solutions


Common issues and solutions when getting started with the AgentCore CLI for TypeScript. For more troubleshooting information, see [Troubleshoot Amazon Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-troubleshooting.html).

 **Container runtime not found**   
Install Docker, Podman, or Finch. For Docker, ensure Docker Desktop is running. Verify your container runtime is available by running `docker info` (or the equivalent command for your container runtime).

 **Dockerfile not found**   
Ensure a `Dockerfile` exists in your agent’s code directory (for example, `app/TsAgent/Dockerfile` ). The container build requires a Dockerfile in the directory specified by `codeLocation` in `agentcore/agentcore.json`.

 **Container image exceeds size limit**   
Use multi-stage builds to reduce the final image size, minimize installed packages, and review your `.dockerignore` file to exclude unnecessary files from the build context.

 **Permission denied errors**   
Verify your AWS credentials and permissions:  
+ Verify AWS credentials: `aws sts get-caller-identity` 
+ Check you have the required policies attached
+ Review caller permissions policy for detailed requirements

 **TypeScript compilation errors**   
Ensure your TypeScript configuration is correct:  
+ Verify `tsconfig.json` exists and has correct settings
+ Run `npm run build` locally to check for errors
+ Ensure `"type": "module"` is set in `package.json` 

 **Configuration validation errors**   
Check your project configuration:  
+ Run `agentcore validate` to check for configuration errors
+ Verify `agentcore/agentcore.json` has the correct agent entry
+ Ensure `codeLocation` points to the correct directory

 **Port 8080 in use (local only)**   
Find and stop processes that are using port 8080:  
Use `lsof -ti:8080` to get a list of process using port 8080.  
Use `kill -9 PID` to stop the process. Replace *PID* with the process ID.  
Alternatively, start the dev server on a different port: `agentcore dev --port 3000` 

 **Region mismatch**   
Verify the AWS Region with `aws configure get region` and make sure the region in `agentcore/aws-targets.json` matches

 **Module not found errors**   
Ensure your imports use the correct paths:  
+ Use `bedrock-agentcore/runtime` for the runtime module
+ Run `npm install` to ensure all dependencies are installed

## Advanced: Streaming responses


Your TypeScript agent can return streaming responses using async generators. This is useful for long-running operations or when you want to provide incremental updates to the client:

```
import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';

// Streaming handler using async generator
const streamingHandler = async function* (request: unknown, context: { sessionId: string }) {
  yield { event: 'start', sessionId: context.sessionId };

  // Simulate processing steps
  for (let i = 0; i < 5; i++) {
    yield {
      event: 'progress',
      step: i + 1,
      data: `Processing step ${i + 1}`,
    };
  }

  yield { event: 'complete', result: 'done' };
};

const app = new BedrockAgentCoreApp({
  invocationHandler: { process: streamingHandler },
});

app.run();
```

The server automatically handles streaming responses using Server-Sent Events (SSE).

# Get started without the AgentCore CLI
Get started without the AgentCore CLI

You can create a AgentCore Runtime agent without the AgentCore CLI. Instead you can use a combination of command line tools to configure and deploy your agent to an AgentCore Runtime.

This tutorial shows how to deploy a custom agent without using the AgentCore CLI. A custom agent is an agent built without using the AgentCore Python SDK. In this tutorial, the custom agent is built using FastAPI and Docker. The custom agent follows the [AgentCore Runtime requirements](runtime-service-contract.md) , meaning the agent must expose `/invocations` POST and `/ping` GET endpoints and be packaged in a Docker container. Amazon Bedrock AgentCore requires ARM64 architecture for all deployed agents.

**Note**  
You can also use this approach for agents that you build with the AgentCore Python SDK.

## Quick start setup


### Enable observability for your agent


 [Amazon Bedrock AgentCore Observability](observability.md) helps you trace, debug, and monitor agents that you host in AgentCore Runtime. To observe an agent, first enable CloudWatch Transaction Search by following the instructions at [Enabling AgentCore observability](observability-configure.md#observability-configure-builtin).

### Install uv


For this example, we’ll use the `uv` package manager, though you can use any Python utility or package manager. To install `uv` on macOS:

```
curl -LsSf https://astral.sh/uv/install.sh | sh
```

For installation instructions on other platforms, refer to the [uv documentation](https://docs.astral.sh/uv/getting-started/installation/).

### Create your agent project


<a name="create-agent-project"></a> **Setting up your project** 

1. Create and navigate to your project directory:

   ```
   mkdir my-custom-agent && cd my-custom-agent
   ```

1. Initialize the project with Python 3.11:

   ```
   uv init --python 3.11
   ```

1. Add the required dependencies (uv automatically creates a .venv):

   ```
   uv add fastapi 'uvicorn[standard]' pydantic httpx strands-agents
   ```

## Agent contract requirements


Your custom agent must fulfill these core requirements:
+  **/invocations Endpoint** : POST endpoint for agent interactions (REQUIRED)
+  **/ping Endpoint** : GET endpoint for health checks (REQUIRED)
+  **Docker Container** : ARM64 containerized deployment package

## Project structure


 **Note:** For convenience, the example below uses **FastAPI Server** as the Web server framework for handling requests.

Your project should have the following structure:

```
my-custom-agent/
├── agent.py                 # FastAPI application
├── Dockerfile               # ARM64 container configuration
├── pyproject.toml           # Created by uv init
└── uv.lock                  # Created automatically by uv
```

## Complete strands agent example


Create `agent.py` in your project root with the following content:

 **Example agent.py** 

```
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import Dict, Any
from datetime import datetime
from strands import Agent

app = FastAPI(title="Strands Agent Server", version="1.0.0")

# Initialize Strands agent
strands_agent = Agent()

class InvocationRequest(BaseModel):
    input: Dict[str, Any]

class InvocationResponse(BaseModel):
    output: Dict[str, Any]

@app.post("/invocations", response_model=InvocationResponse)
async def invoke_agent(request: InvocationRequest):
    try:
        user_message = request.input.get("prompt", "")
        if not user_message:
            raise HTTPException(
                status_code=400,
                detail="No prompt found in input. Please provide a 'prompt' key in the input."
            )

        result = strands_agent(user_message)
        response = {
            "message": result.message,
            "timestamp": datetime.utcnow().isoformat()
        }

        return InvocationResponse(output=response)

    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Agent processing failed: {str(e)}")

@app.get("/ping")
async def ping():
    return {"status": "healthy"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8080)
```

This implementation:
+ Creates a FastAPI application with the required endpoints
+ Initializes a Strands agent for processing user messages
+ Implements the `/invocations` POST endpoint for agent interactions
+ Implements the `/ping` GET endpoint for health checks
+ Configures the server to run on host `0.0.0.0` and port `8080` 

## Test locally


<a name="test-locally"></a> **Testing your agent** 

1. Run the application:

   ```
   uv run uvicorn agent:app --host 0.0.0.0 --port 8080
   ```

1. Test the `/ping` endpoint (in another terminal):

   ```
   curl http://localhost:8080/ping
   ```

1. Test the `/invocations` endpoint:

   ```
   curl -X POST http://localhost:8080/invocations \
     -H "Content-Type: application/json" \
     -d '{
       "input": {"prompt": "What is artificial intelligence?"}
     }'
   ```

## Create dockerfile


Create `Dockerfile` in your project root with the following content:

 **Example Dockerfile** 

```
# Use uv's ARM64 Python base image
FROM --platform=linux/arm64 ghcr.io/astral-sh/uv:python3.11-bookworm-slim

WORKDIR /app

# Copy uv files
COPY pyproject.toml uv.lock ./

# Install dependencies (including strands-agents)
RUN uv sync --frozen --no-cache

# Copy agent file
COPY agent.py ./

# Expose port
EXPOSE 8080

# Run application
CMD ["uv", "run", "uvicorn", "agent:app", "--host", "0.0.0.0", "--port", "8080"]
```

This Dockerfile:
+ Uses an ARM64 Python base image (required by Amazon Bedrock AgentCore)
+ Sets up the working directory
+ Copies the dependency files and installs dependencies
+ Copies the agent code
+ Exposes port 8080
+ Configures the command to run the application

## Build and deploy ARM64 image


### Setup docker buildx


Docker buildx lets you build images for different architectures. Set it up with:

```
docker buildx create --use
```

### Build for ARM64 and test locally


<a name="build-for-arm64-and-test-locally"></a> **Building and testing your image** 

1. Build the image locally for testing:

   ```
   docker buildx build --platform linux/arm64 -t my-agent:arm64 --load.
   ```

1. Test locally with credentials (Strands agents need AWS credentials):

   ```
   docker run --platform linux/arm64 -p 8080:8080 \
     -e AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \
     -e AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \
     -e AWS_SESSION_TOKEN="$AWS_SESSION_TOKEN" \
     -e AWS_REGION="$AWS_REGION" \
     my-agent:arm64
   ```

### Create ECR repository and deploy


<a name="create-ecr-repository-and-deploy"></a> **Deploying to ECR** 

1. Create an ECR repository:

   ```
   aws ecr create-repository --repository-name my-strands-agent --region us-west-2
   ```

1. Log in to ECR:

   ```
   aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin account-id.dkr.ecr.us-west-2.amazonaws.com
   ```

1. Build and push to ECR:

   ```
   docker buildx build --platform linux/arm64 -t account-id.dkr.ecr.us-west-2.amazonaws.com/my-strands-agent:latest --push.
   ```

1. Verify the image was pushed:

   ```
   aws ecr describe-images --repository-name my-strands-agent --region us-west-2
   ```

## Deploy agent runtime


Create a file named `deploy_agent.py` with the following content:

 **Example deploy\$1agent.py** 

```
import boto3

client = boto3.client('bedrock-agentcore-control', region_name='us-west-2')

response = client.create_agent_runtime(
    agentRuntimeName='strands_agent',
    agentRuntimeArtifact={
        'containerConfiguration': {
            'containerUri': 'account-id.dkr.ecr.us-west-2.amazonaws.com/my-strands-agent:latest'
        }
    },
    networkConfiguration={"networkMode": "PUBLIC"},
    roleArn='arn:aws:iam::account-id:role/AgentRuntimeRole',
    lifecycleConfiguration={
        'idleRuntimeSessionTimeout': 300,  # 5 min, configurable
        'maxLifetime': 1800                # 30 minutes, configurable
    },
)

print(f"Agent Runtime created successfully!")
print(f"Agent Runtime ARN: {response['agentRuntimeArn']}")
print(f"Status: {response['status']}")
```

Run the script to deploy your agent:

```
uv run deploy_agent.py
```

This script uses the `create_agent_runtime` operation to deploy your agent to Amazon Bedrock AgentCore. Make sure to replace *account-id* with your actual AWS account ID and ensure the IAM role has the necessary permissions. For more information, see [IAM Permissions for AgentCore Runtime](runtime-permissions.md).

## Invoke your agent


Create a file named `invoke_agent.py` with the following content:

 **Example invoke\$1agent.py** 

```
import boto3
import json

agent_core_client = boto3.client('bedrock-agentcore', region_name='us-west-2')
payload = json.dumps({
    "input": {"prompt": "Explain machine learning in simple terms"}
})

response = agent_core_client.invoke_agent_runtime(
    agentRuntimeArn='arn:aws:bedrock-agentcore:us-west-2:account-id:runtime/myStrandsAgent-suffix',
    runtimeSessionId='dfmeoagmreaklgmrkleafremoigrmtesogmtrskhmtkrlshmt',  # Must be 33+ chars
    payload=payload,
    qualifier="DEFAULT"
)

response_body = response['response'].read()
response_data = json.loads(response_body)
print("Agent Response:", response_data)
```

Run the script to invoke your agent:

```
uv run invoke_agent.py
```

This script uses the [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) AWS SDK operation to send a request to your deployed agent. Make sure to replace *account-id* and *agentArn* with your actual values.

If you plan on integrating your agent with OAuth, you can’t use the AWS SDK to call `InvokeAgentRuntime` . Instead, make a HTTPS request to InvokeAgentRuntime. For more information, see [Authenticate and authorize with Inbound Auth and Outbound Auth](runtime-oauth.md).

## Expected response format


When you invoke your agent, you’ll receive a response like this:

 **Example Sample response** 

```
{
  "output": {
    "message": {
      "role": "assistant",
      "content": [
        {
          "text": "# Artificial Intelligence in Simple Terms\n\nArtificial Intelligence (AI) is technology that allows computers to do tasks that normally need human intelligence. Think of it as teaching machines to:\n\n- Learn from information (like how you learn from experience)\n- Make decisions based on what they've learned\n- Recognize patterns (like identifying faces in photos)\n- Understand language (like when I respond to your questions)\n\nInstead of following specific step-by-step instructions for every situation, AI systems can adapt to new information and improve over time.\n\nExamples you might use every day include voice assistants like Siri, recommendation systems on streaming services, and email spam filters that learn which messages are unwanted."
        }
      ]
    },
    "timestamp": "2025-07-13T01:48:06.740668"
  }
}
```

## Stop runtime session


To stop the running session before the configurable `IdleRuntimeSessionTimeout` (defaulted at 15 minutes) and save on any potential runaway costs, execute: `stop_runtime_session` 

Create a file named `stop_runtime_session.py` with the following content:

 **Example stop\$1runtime\$1session.py** 

```
import boto3

agent_core_client = boto3.client('bedrock-agentcore', region_name='us-west-2')
response = agent_core_client.stop_runtime_session(
    agentRuntimeArn='arn:aws:bedrock-agentcore:us-west-2:account-id:runtime/myStrandsAgent-suffix',
    runtimeSessionId='dfmeoagmreaklgmrkleafremoigrmtesogmtrskhmtkrlshmt',
    qualifier="DEFAULT"
)
```

## Amazon Bedrock AgentCore requirements summary

+  **Platform** : Must be `linux/arm64` 
+  **Endpoints** : `/invocations` POST and `/ping` GET are mandatory
+  **ECR** : Images must be deployed to ECR
+  **Port** : Application runs on port 8080
+  **Strands Integration** : Uses Strands Agent for AI processing
+  **Credentials** : Strands agents require AWS credentials for operation

## Conclusion


In this guide, you’ve learned how to:
+ Set up a development environment for building custom agents
+ Create a FastAPI application that implements the required endpoints
+ Containerize your agent for ARM64 architecture
+ Test your agent locally
+ Deploy your agent to ECR
+ Create an agent runtime in Amazon Bedrock AgentCore
+ Invoke your deployed agent
+ Stop agent runtime session

By following these steps, you can create and deploy custom agents that leverage the power of Amazon Bedrock AgentCore while maintaining full control over your agent’s implementation.

# Get started with Amazon Bedrock AgentCore Runtime direct code deployment
Get started with direct code deployment

Direct code deployment enables you to bring your Python-based agent to Amazon Bedrock AgentCore Runtime simply by packaging agent code and its dependencies in a .zip file archive. Your agent still needs to follow [AgentCore Runtime requirements](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-service-contract.html) : have an entrypoint .py file that either uses the `@app.entrypoint` annotation from [Amazon Bedrock AgentCore Python SDK](https://github.com/aws/bedrock-agentcore-sdk-python) or implements `/invocations` POST and `/ping` GET server endpoints.

To create your deployment package as .zip file archive, you can use [AgentCore CLI](https://github.com/aws/agentcore-cli) or follow the steps in the *Custom zip \$1 boto3* tab below, or any other .zip file utility such as [7zip](https://www.7-zip.org/download.html) . The examples shown in the following sections assume you’re using a command-line `zip` tool in a Linux or MacOS environment. To use the same commands in Windows, you can [install the Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10) to get a Windows-integrated version of Ubuntu and Bash.

Note that AgentCore Runtime uses POSIX file permissions, so you may need to [set permissions for the deployment package folder](http://aws.amazon.com/premiumsupport/knowledge-center/lambda-deployment-package-errors/) before you create the .zip file archive.

**Topics**
+ [

## Prerequisites
](#prerequisites)
+ [

## Step 1: Set up project and install dependencies
](#step-1-setup)
+ [

## Step 2: Create your agent project
](#step-2-create-agent)
+ [

## Step 3: Test locally
](#step-3-test-locally)
+ [

## Step 4: Enable observability for your agent
](#step-4-enable-observability)
+ [

## Step 5: Deploy to AgentCore Runtime and invoke
](#step-5-deploy)
+ [

## Step 6: Stop session, update, or cleanup
](#step-6-update-cleanup)
+ [

## Direct code deployment concepts
](#runtime-code-deploy-concepts)
+ [

## Common Issues
](#common-issues-direct-code-deploy)

## Prerequisites


Before you start, make sure you have:
+  ** AWS Account** with credentials configured. To configure your AWS credentials, see [Configuration and credential file settings in the AWS CLI.](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) 
+  [Uv](https://docs.astral.sh/uv/getting-started/installation/) **installed** and [Python 3.10\$1](https://docs.astral.sh/uv/guides/install-python/) installed
+  ** AWS Permissions** : To create and deploy an agent with the AgentCore CLI, you must have appropriate permissions. For information, see [Use the AgentCore CLI](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html#runtime-permissions-cli).
+  **Model access** : Anthropic Claude Sonnet 4.0 [enabled](https://docs.aws.amazon.com/bedrock/latest/userguide/model-access-modify.html) in the Amazon Bedrock console. For information about using a different model with the Strands Agents see the *Model Providers* section in the [Strands Agents SDK](https://strandsagents.com/latest/documentation/docs/) documentation.

## Step 1: Set up project and install dependencies


Initialize your project with the following commands:

```
uv init agentcore_runtime_direct_deploy --python 3.13
cd agentcore_runtime_direct_deploy
```

Add core packages:

```
uv add bedrock-agentcore strands-agents
```

Install the AgentCore CLI (required for the steps that follow):

```
npm install -g @aws/agentcore
```

Package descriptions:
+  **bedrock-agentcore** - The Amazon Bedrock AgentCore SDK for building AI agents
+  **strands-agents** - The [Strands Agents](https://strandsagents.com/latest/) SDK
+  **@aws/agentcore** - The AgentCore CLI

Optionally, run `uv add aws-opentelemetry-distro` to enable [Amazon Bedrock AgentCore observability traces](https://docs.aws.amazon.com/xray/latest/devguide/xray-services-adot.html).

Uv will automatically create a `pyproject.toml` file with dependencies, `uv.lock` file with dependency closure and `.venv` directory.

## Step 2: Create your agent project


Use the `agentcore create` command to set up a skeleton agent project with the framework of your choice:

```
agentcore create
```

The command will prompt you to:
+ Choose a framework (choose Strands Agents for this tutorial)
+ Provide a project name
+ Choose a template (basic or production)
+ Choose model provider and other options

This command generates:
+ Agent code with your selected framework
+ A `pyproject.toml` file with necessary dependencies
+ An `agentcore/agentcore.json` configuration file
+ Infrastructure as Code (IaC) files if production template is selected

## Step 3: Test locally


Make sure port 8080 is free before starting. See *Port 8080 in use (local only)* in [Common issues and solutions](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-get-started-cli.html#common-issues).

Open a terminal window and start your agent with the following command:

```
agentcore dev
```

Test your agent by opening another terminal window and enter the following command:

```
curl -X POST http://localhost:8080/invocations \
  -H "Content-Type: application/json" \
  -d '{"prompt": "Hello!"}'
```

 **Success:** You should see a response like `{"result": "Hello! I’m here to help…​"}` . In the terminal window that’s running the agent, enter `Ctrl+C` to stop the agent.

## Step 4: Enable observability for your agent


 [Amazon Bedrock AgentCore Observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability.html) helps you trace, debug, and monitor agents that you host in AgentCore Runtime. First enable CloudWatch Transaction Search by following the instructions at [Enabling Amazon Bedrock AgentCore runtime observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-configure.html#observability-configure-builtin) . To observe your agent, see [View observability data for your Amazon Bedrock AgentCore agents](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-view.html).

## Step 5: Deploy to AgentCore Runtime and invoke


Deploy your agent using one of the following methods:

**Example**  

1. The following steps will be required to deploy an agent to AgentCore Runtime, refer to [Get started with the AgentCore CLI](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-get-started-cli.html) , for detailed steps. If Uv is available, the AgentCore CLI will recommend direct code deployment. Otherwise it will default to container deployment type.

   Once you have your agent set up using `agentcore create` , use the `deploy` command to create a zip deployment package, upload it to the specified bucket, and deploy the agent.

   ```
   agentcore deploy
   ```

   Let’s prompt the agent to tell a joke\$1

   ```
   agentcore invoke "Tell me a joke"
   ```

   The first deployment takes time to install dependencies but subsequent updates to the agent optimizes this by re-using zipped dependencies

    **Configuration management** 

   You can modify your agent configuration at any time using the `agentcore add` commands or by editing the `agentcore/agentcore.json` configuration file directly.

   The configuration file allows you to update deployment parameters such as your VPC configuration, execution roles, session timeouts, and OAuth authorizer settings.

1. Run `agentcore` to open the TUI, then select **deploy** . The deploy screen shows real-time progress as it validates your project, synthesizes CloudFormation, and provisions AWS resources:  
![\[AgentCore deploy progress showing CloudFormation stack updates\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/tui/code-deploy-progress.png)

   After deployment completes, use `agentcore invoke` to test your agent.

1. To download a wheel that’s compatible with AgentCore Runtime, you use the uv pip `--python-platform` option. AgentCore Runtime only supports **arm64** instruction set architecture, run the following command. Replace `--python 3.x` with the version of the Python runtime you are using.

   ```
   uv pip install \
   --python-platform aarch64-manylinux2014 \
   --python-version 3.13 \
   --target=deployment_package \
   --only-binary=:all: \
   -r pyproject.toml
   ```

   Create a .zip file with the installed libraries at the project root.

   ```
   cd deployment_package
   zip -r ../deployment_package.zip.
   ```

   Add the `main.py` file and other files in your package to the root of the .zip file.

   ```
   cd ..
   zip deployment_package.zip main.py
   ```

   After you have created your .zip deployment package, you can use it to create a new AgentCore Runtime or update an existing one. You can deploy your .zip package using AgentCore Runtime API, AgentCore Runtime console and AWS Command Line Interface. The AgentCore CLI will take care of above steps to create .zip.
**Note**  
. The maximum size for a .zip deployment package for AgentCore Runtime is 250 MB (zipped) and 750 MB (unzipped). Note that this limit applies to the combined size of all the files you upload. . The AgentCore Runtime needs permission to read the files in your deployment package. In Linux permissions octal notation, AgentCore Runtime needs 644 permissions for non-executable files (rw-r—r--) and 755 permissions (rwxr-xr-x) for directories and executable files. . In Linux and MacOS, use the `chmod` command to change file permissions on files and directories in your deployment package. For example, to give a non-executable file the correct permissions, run the following command, `chmod 644 <filepath>` . To change file permissions in Windows, see [Set, View, Change, or Remove Permissions on an Object](https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2008-R2-and-2008/cc731667(v=ws.10)) in the Microsoft Windows documentation. \$1 .. If you don’t grant AgentCore Runtime the permissions it needs to access directories in your deployment package, AgentCore Runtime sets the permissions for those directories to 755 (rwxr-xr-x).

   A ZIP archive containing Linux **arm64** dependencies needs to be uploaded to S3 as a pre-requisite to Create Agent Runtime. The below code requires the specified S3 bucket to already exist. Please follow the AWS documentation [here](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/create_bucket.html) to create an bucket using boto3. Following boto3 code will upload .zip file archive to a s3 and create Amazon Bedrock AgentCore runtime.

   ```
   import boto3
   
   account_id = "your aws account id"
   agent_name = "strands_10_23"
   
   s3_client = boto3.client('s3', region_name='us-west-2')
   print("Uploading deployment.zip to S3...")
   s3_client.upload_file(
       'deployment_package.zip', # archive on file system
       f"bedrock-agentcore-code-{account_id}-us-west-2", # bucket name
       f"{agent_name}/deployment_package.zip", # prefix
       ExtraArgs={'ExpectedBucketOwner': account_id} # ownership check
   )
   print("Upload completed successfully!")
   print(f"S3 Location: s3://bedrock-agentcore-code-{account_id}-us-west-2/deployment_package.zip")
   
   agentcore_client = boto3.client('bedrock-agentcore-control', region_name='us-west-2')
   response = agentcore_client.create_agent_runtime(
       agentRuntimeName=agent_name,
       agentRuntimeArtifact={
           'codeConfiguration': {
               'code': {
                   's3': {
                       'bucket': f"bedrock-agentcore-code-{account_id}-us-west-2",
                       'prefix': f"{agent_name}/deployment_package.zip"
                   }
               },
               'runtime': 'PYTHON_3_13',
               'entryPoint': ['opentelemetry-instrument', 'main.py']
           } # if not adding otel dependency, remove opentelemetry-instrument from entrypoint array
       },
       networkConfiguration={"networkMode": "PUBLIC"},
       roleArn=f"arn:aws:iam::{account_id}:role/AmazonBedrockAgentCoreSDKRuntime-us-west-2",
       lifecycleConfiguration={
           'idleRuntimeSessionTimeout': 300,  # 5 min, configurable
           'maxLifetime': 1800                # 30 minutes, configurable
       },
   )
   print(f"Agent Runtime created successfully!")
   print(f"Agent Runtime ARN: {response['agentRuntimeArn']}")
   print(f"Status: {response['status']}")
   ```

   To invoke an agent on Amazon Bedrock AgentCore runtime using boto3, refer: [https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-get-started-cli.html\$1invoke-programmatically](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-get-started-cli.html#invoke-programmatically) 

1. You can deploy your agent using the Amazon Bedrock AgentCore console with managed runtime support. The console provides an intuitive interface for uploading ZIP files and configuring agent settings.

   To deploy using the console, first create your deployment package following the steps in the *Custom zip \$1 boto3* tab above.

    **Create agent through console** 

   To create your agent:

1. From the Agents home page, click **Host Agent** 

1. Choose your source selection:
   +  **S3 Source** - Upload from S3 bucket
   +  **Local Upload** - Upload ZIP file from your computer
   +  **Templates** - Use pre-built agent templates

1. Configure your agent settings:
   + Agent name
   + Runtime version (Python 3.13 recommended)
   + Entry point (e.g., `main.py` )
   + Execution role (create new or use existing)

1. Click **Create Agent** to deploy

    **Create endpoint** 

   Click **Create Endpoint** to create a new endpoint for your agent. The endpoint name and associated version will be pre-filled.

    **Test endpoint** 

   Select an endpoint and click **Test Endpoint** to navigate to the Playground/Sandbox for testing.

    **Execution role requirements** 

   Your agent requires an execution role with appropriate permissions. For detailed information about execution roles and permissions, see [AgentCore Runtime permissions](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html) . The execution role must include basic Amazon Bedrock AgentCore runtime permissions, S3 access permissions for your deployment package, and CloudWatch Logs permissions for observability.

## Step 6: Stop session, update, or cleanup


Stop your runtime session, update, or cleanup your agent using one of the following methods:

**Example**  

1. To update previously deployed AgentCore Runtime, execute:

   ```
   agentcore deploy
   ```

   To stop a running session before the configurable `IdleRuntimeSessionTimeout` (defaulted at 15 minutes) and save on any potential runaway costs, use the [StopRuntimeSession](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_StopRuntimeSession.html) API operation. The AgentCore CLI does not currently support stopping sessions directly.

   To delete all resources related to a AgentCore Runtime, first remove all resources and then deploy to tear down AWS resources:

   ```
   agentcore remove all
   agentcore deploy
   ```

1. Following boto3 code will update an AgentCore Runtime.

   ```
   import boto3
   
   account_id = "your aws account id"
   agent_name = "strands_10_23"
   
   s3_client = boto3.client('s3', region_name='us-west-2')
   print("Uploading deployment.zip to S3...")
   s3_client.upload_file(
       'deployment_package.zip', # archive on file system
       f"bedrock-agentcore-code-{account_id}-us-west-2", # bucket name
       f"{agent_name}/deployment_package.zip", # prefix
       ExtraArgs={'ExpectedBucketOwner': account_id} # ownership check
   )
   print("Upload completed successfully!")
   print(f"S3 Location: s3://bedrock-agentcore-code-{account_id}-us-west-2/deployment_package.zip")
   
   bedrock_agentcore_client = boto3.client('bedrock-agentcore-control', region_name='us-west-2')
   response = bedrock_agentcore_client.update_agent_runtime(
       agentRuntimeId='<your-agent-id>',
       agentRuntimeArtifact={
           'codeConfiguration': {
               'code': {
                   's3': {
                       'bucket': f"bedrock-agentcore-code-{account_id}-us-west-2",
                       'prefix': 'strands/deployment_package.zip'
                   }
               },
               'runtime': 'PYTHON_3_12',
               'entryPoint': ['opentelemetry-instrument', 'main.py']
           } # if not adding otel dependency, remove opentelemetry-instrument from entrypoint array
       },
       networkConfiguration={"networkMode": "PUBLIC"},
       roleArn=f"arn:aws:iam::{account_id}:role/AmazonBedrockAgentCoreSDKRuntime-us-west-2"
   )
   
   print(f"Agent Runtime updated successfully!")
   print(f"Agent Runtime ARN: {response['agentRuntimeArn']}")
   print(f"Status: {response['status']}")
   ```

   To stop the running session before the configurable `IdleRuntimeSessionTimeout` (defaulted at 15 minutes) and save on any potential runaway costs, use the following boto3 code:

   ```
   import boto3
   
   agent_core_client = boto3.client('bedrock-agentcore', region_name='us-west-2')
   response = agent_core_client.stop_runtime_session(
       agentRuntimeArn='arn:aws:bedrock-agentcore:us-west-2:account-id:runtime/agent-name-suffix',
       runtimeSessionId='your-session-id',
       qualifier="DEFAULT"
   )
   ```

   Following boto3 code will delete Amazon Bedrock AgentCore runtime and .zip archive file in s3.

   ```
   import boto3
   
   account_id = "your aws account id"
   agent_name = "strands_10_23"
   
   bedrock_agentcore_client = boto3.client('bedrock-agentcore-control', region_name='us-west-2')
   print("Deleting Agent from Amazon Bedrock AgentCore Runtime!")
   response = bedrock_agentcore_client.delete_agent_runtime(
       agentRuntimeId='<agent-id>'
   )
   print(f"Agent Runtime delete successfully!")
   print(f"Status: {response['status']}")
   
   s3_client = boto3.client('s3', region_name='us-west-2')
   print("Deleting deployment archive from S3...")
   s3_client.delete_object(
       Bucket=f"bedrock-agentcore-code-{account_id}-us-west-2",
       Key=f"{agent_name}/deployment_package.zip",
       ExpectedBucketOwner=account_id
   )
   print("Archive deleted successfully from S3!")
   ```

1. ====== Update agent

   From the Agent details page, click **Update hosting** to create a new version with updated code or configuration.

    **Update endpoint** 

   Select an endpoint from the Endpoints table and click **Edit** to update the description or associated version.

    **Delete endpoint** 

   Select an endpoint and click **Delete** . You’ll need to type "delete" to confirm the deletion.

    **Delete agent** 

   From the agent list or details page, select your agent and click **Delete** to remove the agent and all associated resources.

## Direct code deployment concepts


Learn about key concepts when using direct code deployment with Amazon Bedrock AgentCore Runtime.

**Topics**

### Shared responsibility model for direct code deployment


Amazon Bedrock AgentCore Runtime with direct code deployment uses a shared responsibility model similar to AWS Lambda. AgentCore Runtime manages the Python runtime environment and applies security patches automatically, while you focus on your agent code and dependencies.

If you’re using [container images to deploy your agents](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html) , then AgentCore Runtime is responsible to patch only compute kernel. In this case, you’re responsible for rebuilding your agent’s container image from the latest secure image and redeploying the container image.

This is summarized in the following table:


| Deployment mode | AgentCore Runtime’s responsibility | Your responsibility | 
| --- | --- | --- | 
|  Direct deploy mode  |  Publish new language runtime version containing the latest patches for language runtime. Apply language runtime patches to existing AgentCore Runtime direct deploy.  |  Update your agent code, including dependencies, to address any security vulnerabilities.  | 
|  Container image  |  Automatically patch underlying compute OS kernel with latest version.  |  Update your agent code, including dependencies, to address any security vulnerabilities. Regularly re-build and re-deploy your container image using the latest base image.  | 

For more information about shared responsibility with AWS, see [Shared Responsibility Model](https://aws.amazon.com/compliance/shared-responsibility-model/).

AgentCore Runtime keeps each direct code deploy runtime up to date with security updates, bug fixes, new features, performance enhancements, and support for minor version releases. These runtime updates are published as *runtime versions* . AgentCore Runtime applies direct code deploy runtime updates to agents by migrating the agents from an earlier runtime version to a new runtime version.

For direct deploy runtimes, AgentCore Runtime applies runtime updates automatically. With automatic runtime updates, AgentCore Runtime takes on the operational burden of patching the runtime versions. For most customers, this should be a safe choice as only language runtime patches will be automatically applied and customers are responsible to bring and managed their code dependencies. Currently AgentCore Runtime doesn’t support changing this automated patching behavior.

AgentCore Runtime strives to provide runtime updates that are backward compatible with existing functions. However, as with software patching, there are rare cases in which a runtime update can negatively impact an existing function. For example, security patches can expose an underlying issue with an existing function that depends on the previous, insecure behavior. If in extremely rare cases this risk is not acceptable, please use [container images to deploy your agent](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html).

### Supported language runtimes and deprecation policy


The following table lists the supported AgentCore Runtime direct code deploy language runtimes and projected deprecation dates. After a language environment is deprecated, you’re still able to create and update functions for a limited period. The table provides the currently forecasted dates for runtime deprecation, based on our [Runtime deprecation policy](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtime-support-policy) . These dates are provided for planning purposes and are subject to change.


| \$1 | Name | Identifier | Operating system | Deprecation date | Block function update | 
| --- | --- | --- | --- | --- | --- | 
|  1  |  Python 3.14  |   `PYTHON_3_14`   |  Amazon Linux 2023  |  6/30/2030  |  8/31/2030  | 
|  2  |  Python 3.13  |   `PYTHON_3_13`   |  Amazon Linux 2023  |  6/30/2029  |  8/31/2029  | 
|  3  |  Python 3.12  |   `PYTHON_3_12`   |  Amazon Linux 2023  |  10/31/2028  |  1/10/2029  | 
|  4  |  Python 3.11  |   `PYTHON_3_11`   |  Amazon Linux 2023  |  6/30/2026  |  8/31/2026  | 
|  5  |  Python 3.10  |   `PYTHON_3_10`   |  Amazon Linux 2023  |  6/30/2026  |  8/31/2026  | 

<a name="deprecation-policy"></a>==== Language environment deprecation policy

AgentCore Runtime direct code deploy for .zip file archives are built around a combination of operating system, programming language, and software libraries that are subject to maintenance and security updates. AgentCore Runtime standard deprecation policy is to deprecate a language runtime when any major component of the runtime reaches the end of community long-term support (LTS) and security updates are no longer available. Most usually, this is the language runtime, though in some cases, a runtime can be deprecated because the operating system (OS) reaches end of LTS.

After a runtime is deprecated, AWS may no longer apply security patches or updates to that runtime, and functions using that runtime are no longer eligible for technical support. Such deprecated runtimes are provided 'as-is', without any warranties, and may contain bugs, errors, defects, or other vulnerabilities.

**Important**  
AgentCore Runtime occasionally delays deprecation of a AgentCore Runtime direct code deploy programming language runtime for a limited period beyond the end of support date of the language version that the runtime supports. During this period, AgentCore Runtime only applies security patches to the runtime OS. AgentCore Runtime doesn’t apply security patches to programming language runtimes after they reach their end of support date.

### Container vs direct code deployment comparison


Some of the dimensions of comparison to see how one option differs from the other, so it helps to choose the right option
+  **Deployment Process** : Direct code deployment deploys agents using ZIP files instead of containers, lending itself to faster development iterations.
+  **Deployment time** : Although there is not much difference during first deployment of an agent, subsequent updates to the agent are significantly faster with direct code deployment.
+  **Customization** : Direct code supports for custom dependencies through ZIP-based packaging while maintaining deployment simplicity while container based depends on a Docker file.
+  **Package size** : Direct code deployment limits the package size to 250MB whereas container based packages can be upto 1GB in size.
+  **Session creation rate** : The direct code deployment allows for higher session creation of 25 new sessions/second compared to 0.16 new sessions/second with container based deployments.

Our general guidance is
+ If the size of the deployment package exceeds 250MB, and you have existing container CI/CD pipelines, and you need highly specialized dependencies and packaging, then container based deployments is a good option to choose.
+ If the size of the deployment package is small, the code and package is not complex to build and uses common frameworks and languages, and you need rapid prototyping and iteration, then direct code deployment would be the option.

There can also be a hybrid option where developers use the direct code deployment to quickly experiment and prototype agents and then switch to container based deployments (for the above reasons) to develop, test and deploy to production.

### Dependency search path and runtime-included libraries


When you use an `import` statement in your code, the Python runtime searches the directories in its search path until it finds the module or package. By default, the first location the runtime searches is the directory into which your .zip deployment package is decompressed and mounted ( `/var/task` ).

You can see the full search path for your AgentCore Runtime agent by adding the following code snippet.

```
import sys
search_path = sys.path
print(search_path)
```

You can also add dependencies in a separate folder inside your .zip package. For example, you might add a version of the Boto3 SDK to a folder in your .zip package called `common` . When your .zip package is decompressed and mounted, this folder is placed inside the `/var/task` directory. To use a dependency from a folder in your .zip deployment package in your code, use an `import from` statement. For example, to use a version of Boto3 from a folder named `common` in your .zip package, use the following statement.

```
from common import boto3
```

### Python bytecode and *pycache* folders


We recommend that you don’t include ` pycache ` folders in your agent’s deployment package. Python bytecode that’s compiled on a build machine with a different architecture or operating system might not be compatible with the AgentCore Runtime execution environment.

### Deployment packages with native libraries


If your function uses only pure Python packages and modules, you can use the `uv pip install` command to install your dependencies on any local build machine and create your .zip file. Many popular Python libraries, including NumPy and Pandas, are not pure Python and contain code written in C or C. When you add libraries containing C/C code to your deployment package, you must build your package correctly to make it compatible with the AgentCore Runtime execution environment.

Most packages available on the Python Package Index ( [PyPI](https://pypi.org/) ) are available as "wheels" (.whl files). A .whl file is a type of ZIP file which contains a built distribution with pre-compiled binaries for a particular operating system and instruction set architecture. To make your deployment package compatible with Amazon Bedrock AgentCore AgentCore Runtime, you install the wheel for Linux operating systems and **arm64** instruction set architecture.

Some packages may only be available as source distributions. For these packages, you need to compile and build the C/C\$1\$1 components yourself.

To see what distributions are available for your required package, do the following:

1. Search for the name of the package on the [Python Package Index main page](https://pypi.org/).

1. Choose the version of the package you want to use.

1. Choose **Download files**.

### Working with source distributions


If your package is only available as a source distribution, you need to build the C/C\$1\$1 libraries yourself. To make your package compatible with the Amazon Bedrock AgentCore AgentCore Runtime execution environment, you need to build it in an environment that uses the same Amazon Linux operating system with **arm64** instruction set. You can do this by building your package in an Amazon Elastic Compute Cloud (Amazon EC2) Linux instance.

To learn how to launch and connect to an Amazon EC2 Linux instance, see [Get started with Amazon EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EC2_GetStarted.html) in the *Amazon EC2 User Guide*.

## Common Issues


Common issues and solutions when getting started with Amazon Bedrock AgentCore direct code deployment. For more troubleshooting information, see [Troubleshoot Amazon Bedrock AgentCore AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-troubleshooting.html).

### Permission Issues - Insufficient S3 permissions


 **When this occurs:** During agent creation or update with zipped artifact in S3 via console, SDK, or CLI

 **Why this happens:** The role used to call Create/UpdateAgentRuntime does not have s3:GetObject permissions on S3 uri passed in the API input.

 **Solution:** Add s3:GetObject permission in role used to call the Agentcore Runtime create/update API.

```
{
"Version": "2012-10-17",		 	 	 
  "Statement": [
    {
        "Effect": "Allow",
        "Action": "s3:GetObject",
        "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}
```

### Permission Issues - CMK Encrypted S3 Object Access


 **When this occurs:** During agent creation with CMK-encrypted S3 objects when the execution role lacks KMS decrypt permissions.

 **Why this happens:** The role used to call Create/UpdateAgentRuntime does not have kms:Decrypt permissions on the CMK used to encrypt the S3 object containing the agent code.

 **Solution:** Add kms:Decrypt permission to the role for the specific CMK:

```
{
"Version": "2012-10-17",		 	 	 
  "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "s3:GetObject",
            "kms:Decrypt"
        ],
        "Resource": [
            "arn:aws:s3:::your-bucket-name/*",
            "arn:aws:kms:us-west-2:your-account:key/your-cmk-key-id"
        ]
    }
  ]
}
```

### Code Package Compatibility Issues


 **When this occurs:** During agent creation or update with incompatible code packages (wrong architecture, Python version, or package format).

 **Why this happens:** The uploaded ZIP file contains binaries compiled for wrong architecture (ARM64 vs x86\$164), incompatible Python version, or incorrect package structure that doesn’t match AgentCore Runtime requirements.

 **Solution:** 
+ Ensure code is compiled for arm64
+ Use compatible Python version (check AgentCore Runtime supported versions)
+ Verify ZIP structure contains proper entry points and dependencies
+ Rebuild packages on compatible runtime environment

 **Error indicators:** 
+ Agent status: CREATE\$1FAILED or runtime errors
+ Import errors or "cannot execute binary file" messages in cloudwatch logs
+ Architecture mismatch errors when you do getAgentRuntimeEndpoint.

# Get started with bidirectional streaming using WebSocket
Get started with bidirectional streaming using WebSocket

Amazon Bedrock AgentCore Runtime lets you deploy agents that support WebSocket streaming for real-time bidirectional communication. This guide walks you through creating, testing, and deploying your first bidirectional streaming agent using WebSocket.

In this section, you learn:
+ How AgentCore Runtime supports WebSocket connections
+ How to create an agent application with bidirectional streaming capabilities
+ How to test your agent locally
+ How to deploy your agent to AWS 
+ How to invoke your deployed agent
+ How to use sessions with WebSocket connections

For more information about the WebSocket protocol, see [WebSocket RFC 6455](https://tools.ietf.org/html/rfc6455).

**Topics**
+ [

## How AgentCore Runtime supports WebSocket connections
](#websocket-support-overview)
+ [

## Using WebSocket with AgentCore Runtime
](#using-websocket-with-runtime)
+ [

## Session management
](#websocket-session-management)
+ [

## Observability
](#websocket-observability)
+ [

## Custom Headers
](#websocket-custom-headers)
+ [

## Appendix
](#websocket-appendix)

## How AgentCore Runtime supports WebSocket connections


AgentCore Runtime’s WebSocket support enables persistent, bidirectional streaming connections between clients and agents. AgentCore Runtime expects containers to implement WebSocket endpoints on port `8080` at the `/ws` path, which aligns with standard WebSocket server practices.

AgentCore Runtime’s WebSocket support provides the same serverless, session isolation, identity, and observability capabilities as `InvokeAgentRuntime` . Additionally, it enables low-latency, real-time bidirectional streaming of messages through WebSocket connections using SigV4 or OAuth 2.0 authentication, making it ideal for applications such as real-time conversational voice agents.

### Supported WebSocket libraries


Bidirectional streaming using WebSockets on AgentCore Runtime supports applications using any WebSocket language library. The only requirements are that clients connect to the service endpoint with a WebSocket protocol connection:

```
wss://bedrock-agentcore.<region>.amazonaws.com/runtimes/<agentRuntimeArn>/ws
```

using one of the supported authentication methods (SigV4 headers, SigV4 pre-signed URL, or OAuth 2.0) and that the agent application implements the WebSocket service contract as specified in [HTTP protocol contract](runtime-http-protocol-contract.md).

This flexibility allows you to use your preferred WebSocket implementation across different programming languages and frameworks, ensuring compatibility with existing codebases and development workflows.

## Using WebSocket with AgentCore Runtime


In this getting started tutorial you will create, test, and deploy an agent application that supports bidirectional streaming using the **bedrock-agentcore Python SDK** and the **AgentCore CLI** for deployment.

**Topics**
+ [

### Prerequisites
](#websocket-prerequisites)
+ [

### Step 1: Set up project and install dependencies
](#setup-websocket-project)
+ [

### Step 2: Create your bidirectional streaming agent
](#create-websocket-agent)
+ [

### Step 3: Test your bidirectional streaming agent locally
](#step-2-test-websocket-locally)
+ [

### Step 4: Deploy your bidirectional streaming agent to AgentCore Runtime
](#step-3-deploy-websocket-to-aws)
+ [

### Step 5: Invoke your deployed bidirectional streaming agent
](#step-4-invoke-deployed-websocket)

### Prerequisites


Before you start, make sure you have:
+  ** AWS Account** with credentials configured. To configure your AWS credentials, see [Configuration and credential file settings in the AWS CLI.](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) 
+  **Python 3.10\$1** installed
+  ** AWS Permissions** : To create and deploy an agent with the AgentCore CLI, you must have appropriate permissions. For more information, see [Use the AgentCore CLI](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html#runtime-permissions-cli).

### Step 1: Set up project and install dependencies


Create a project folder and install the required packages:

```
mkdir agentcore-runtime-quickstart-websocket
cd agentcore-runtime-quickstart-websocket
python3 -m venv .venv
source .venv/bin/activate
```

Upgrade pip to the latest version:

```
pip install --upgrade pip
```

Install the following required packages:
+  **bedrock-agentcore** - The Amazon Bedrock AgentCore SDK for building AI agents, the python `websockets` library dependency is included

```
pip install bedrock-agentcore
```

### Step 2: Create your bidirectional streaming agent


Create a source file for your bidirectional streaming agent code named `websocket_echo_agent.py` . Add the following code:

#### Bidirectional streaming agent implementation (websocket\$1echo\$1agent.py)


```
from bedrock_agentcore import BedrockAgentCoreApp

app = BedrockAgentCoreApp()

@app.websocket
async def websocket_handler(websocket, context):
    """Simple echo WebSocket handler."""
    await websocket.accept()

    try:
        data = await websocket.receive_json()
        # Echo back
        await websocket.send_json({"echo": data})
    except Exception as e:
        print(f"Error: {e}")
    finally:
        await websocket.close()

if __name__ == "__main__":
    app.run(log_level="info")
```

Create `requirements.txt` and add the following:

```
bedrock-agentcore
```

The python `websockets` library dependency is included

#### Understanding the code

+  **BedrockAgentCoreApp** : Creates an agent application that extends Starlette for AI agent deployment, providing WebSocket support, HTTP routing, middleware, and exception handling capabilities
+  **WebSocket Decorator** : The `@app.websocket` decorator automatically handles connections at the `/ws` path on port 8080
+  **Echo Logic** : Sends back received data using `{"echo": data}` 
+  **Error Handling** : Uses try/except/finally structure to ensure proper error logging and graceful connection closure.

### Step 3: Test your bidirectional streaming agent locally


#### Start your bidirectional streaming agent


Open a terminal window and start your bidirectional streaming agent with the following command:

```
python websocket_echo_agent.py
```

You should see output indicating the server is running on port 8080.

#### Test WebSocket connection


Create a local WebSocket client named `websocket_agent_client.py` :

##### Local WebSocket client (websocket\$1agent\$1client.py)


```
import asyncio
import websockets
import json

async def local_websocket():
    uri = "ws://localhost:8080/ws"

    try:
        async with websockets.connect(uri) as websocket:
            # Send a message
            await websocket.send(json.dumps({"inputText": "Hello WebSocket!"}))

            # Receive the echo response
            response = await websocket.recv()
            print(f"Received: {response}")
    except Exception as e:
        print(f"Connection failed: {e}")

if __name__ == "__main__":
    asyncio.run(local_websocket())
```

Test your bidirectional streaming agent locally by opening another terminal window and running the client:

```
python websocket_agent_client.py
```

 **Success:** You should see a response like `Received: {"echo":{"inputText":"Hello WebSocket!"}}` . In the terminal window that’s running the agent, enter `Ctrl+C` to stop the agent.

### Step 4: Deploy your bidirectional streaming agent to AgentCore Runtime


#### Install deployment tools


Install the AgentCore CLI:

```
npm install -g @aws/agentcore
```

Verify installation:

```
agentcore --help
```

#### Create project and deploy to AWS


Create a new project for your bidirectional streaming agent:

```
agentcore create
```

Deploy your agent:

```
agentcore deploy
```

**Note**  
Run these commands from your project directory ( `agentcore-runtime-quickstart-websocket` ) where your agent files are located.

After deployment, you’ll receive an agent runtime ARN that looks like:

```
arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/websocket_echo_agent-xyz123
```

Save this ARN as you’ll need it to invoke your deployed agent.

### Step 5: Invoke your deployed bidirectional streaming agent


#### Set up environment variables


Set up the required environment variables:

1. Export your agent ARN:

   ```
   export AGENT_ARN="arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/websocket_echo_agent-xyz123"
   ```

1. If using OAuth, export your bearer token:

   ```
   export BEARER_TOKEN="your_oauth_token_here"
   ```

#### Authentication methods


The `InvokeAgentRuntimeWithWebSocketStream` API action establishes a WebSocket connection that supports bidirectional streaming between the client and agent. You can authenticate WebSocket connections using the following methods:
+  ** AWS Signature Version 4 headers** : Sign the WebSocket handshake request headers using your AWS credentials
+  ** AWS Signature Version 4 Pre-signed URL** : Create a presigned WebSocket URL with SigV4 signature provided as query parameters
+  **OAuth Bearer token** : Pass an OAuth token in the Authorization header for external identity provider integration

**Tip**  
Make sure that you have `bedrock-agentcore:InvokeAgentRuntimeWithWebSocketStream` permissions.

#### Connect using SigV4 signed headers


The following example shows how to establish a WebSocket connection and communicate with an agent runtime using SigV4 signed headers:

##### WebSocket client with SigV4 headers (websocket\$1agent\$1client\$1sigv4\$1headers.py)


```
from bedrock_agentcore.runtime import AgentCoreRuntimeClient
import websockets
import asyncio
import json
import os

async def main():
    # Get runtime ARN from environment variable
    runtime_arn = os.getenv('AGENT_ARN')
    if not runtime_arn:
        raise ValueError("AGENT_ARN environment variable is required")

    # Initialize client
    client = AgentCoreRuntimeClient(region="us-west-2")

    # Generate WebSocket connection with authentication
    ws_url, headers = client.generate_ws_connection(
        runtime_arn=runtime_arn
    )

    try:
        async with websockets.connect(ws_url, additional_headers=headers) as ws:
            # Send message
            await ws.send(json.dumps({"inputText": "Hello!"}))

            # Receive response
            response = await ws.recv()
            print(f"Received: {response}")
    except websockets.exceptions.InvalidStatus as e:
        print(f"WebSocket handshake failed with status code: {e.response.status_code}")
        print(f"Response headers: {e.response.headers}")
        print(f"Response body: {e.response.body.decode()}")
    except Exception as e:
        print(f"Connection failed: {e}")

if __name__ == "__main__":
    asyncio.run(main())
```

Run the client to test your deployed agent:

```
python websocket_agent_client_sigv4_headers.py
```

 **Success:** You should see a response like:

```
Received: {"echo":{"inputText":"Hello!"}}
```

#### Connect using pre-signed URL (SigV4 via query parameters)


The following example shows how to create a WebSocket URL with SigV4 query parameters and establish a connection:

##### WebSocket client with pre-signed URL (websocket\$1agent\$1client\$1sigv4\$1presigned\$1url.py)


```
from bedrock_agentcore.runtime import AgentCoreRuntimeClient
import websockets
import asyncio
import json
import os

async def main():
    runtime_arn = os.getenv('AGENT_ARN')
    if not runtime_arn:
        raise ValueError("AGENT_ARN environment variable is required")

    client = AgentCoreRuntimeClient(region="us-west-2")

    # Generate WebSocket pre-signed URL (with SigV4 via query parameters)
    # wss://...amazonaws.com/runtimes/.../ws?X-Amz-Algorithm=AWS4-HMAC-SHA256
    #   &X-Amz-Credential=...&X-Amz-Date=...&X-Amz-Expires=300
    #   &X-Amz-SignedHeaders=...&X-Amz-Signature=...
    sigv4_url = client.generate_presigned_url(
        runtime_arn=runtime_arn,
        expires=300  # 5 minutes
    )

    try:
        async with websockets.connect(sigv4_url) as ws:
            await ws.send(json.dumps({"inputText": "Hello!"}))
            response = await ws.recv()
            print(f"Received: {response}")
    except websockets.exceptions.InvalidStatus as e:
        print(f"WebSocket handshake failed with status code: {e.response.status_code}")
        print(f"Response headers: {e.response.headers}")
        print(f"Response body: {e.response.body.decode()}")
    except Exception as e:
        print(f"Connection failed: {e}")

if __name__ == "__main__":
    asyncio.run(main())
```

Run the client to test your deployed agent:

```
python websocket_agent_client_sigv4_query_parameters.py
```

 **Success:** You should see a response like:

```
Received: {"echo":{"inputText":"Hello!"}}
```

#### Connect using OAuth


AgentCore Runtime supports OAuth Bearer token authentication for WebSocket connections. To use OAuth authentication, you need to configure your agent runtime with JWT authorization as described in the [JWT inbound authorization and OAuth outbound access sample](runtime-oauth.md#oauth-sample-overview) section of [Authenticate and authorize with Inbound Auth and Outbound Auth](runtime-oauth.md).

Once you have completed the OAuth setup and obtained a bearer token following [Step 4: Use bearer token to invoke your agent](runtime-oauth.md#oauth-invoke-agent) in the OAuth guide, you can use that token to establish WebSocket connections.

##### Python client with OAuth


The following example shows how to establish a WebSocket connection from Python using OAuth:

##### WebSocket client with OAuth authentication (websocket\$1agent\$1client\$1oauth.py)


```
from bedrock_agentcore.runtime import AgentCoreRuntimeClient
import websockets
import asyncio
import json
import os

async def main():
    # Get runtime ARN from environment variable
    runtime_arn = os.getenv('AGENT_ARN')
    if not runtime_arn:
        raise ValueError("AGENT_ARN environment variable is required")

    # Get OAuth bearer token from environment variable
    bearer_token = os.getenv('BEARER_TOKEN')
    if not bearer_token:
        raise ValueError("BEARER_TOKEN environment variable required for OAuth")

    # Initialize client
    client = AgentCoreRuntimeClient(region="us-west-2")

    # Generate WebSocket connection with OAuth
    ws_url, headers = client.generate_ws_connection_oauth(
        runtime_arn=runtime_arn,
        bearer_token=bearer_token
    )

    try:
        async with websockets.connect(ws_url, additional_headers=headers) as ws:
            # Send message
            await ws.send(json.dumps({"inputText": "Hello!"}))

            # Receive response
            response = await ws.recv()
            print(f"Received: {response}")
    except websockets.exceptions.InvalidStatus as e:
        print(f"WebSocket handshake failed with status code: {e.response.status_code}")
        print(f"Response headers: {e.response.headers}")
        print(f"Response body: {e.response.body.decode()}")
    except Exception as e:
        print(f"Connection failed: {e}")

if __name__ == "__main__":
    asyncio.run(main())
```

Run the client to test your deployed agent:

```
python websocket_agent_client_oauth.py
```

 **Success:** You should see a response like:

```
Received: {"echo":{"inputText":"Hello!"}}
```

##### Browser JavaScript client with OAuth


The browser’s native WebSocket API does not provide a method to set custom headers during the handshake. To support OAuth authentication from browsers, AgentCore Runtime accepts the bearer token embedded in the `Sec-WebSocket-Protocol` header during the WebSocket handshake.

The token must be base64url-encoded and prefixed with `base64UrlBearerAuthorization.` , followed by the sentinel subprotocol `base64UrlBearerAuthorization`.

The following example shows how to establish a WebSocket connection from browser JavaScript using OAuth:

##### Browser WebSocket client with OAuth (index.html)


```
<!DOCTYPE html>
<html>
<body>
    <button onclick="connect()">Connect</button>
    <div id="output"></div>

    <script>
        function connect() {
            const bearerToken = "your_oauth_token_here";
            const runtimeArn = "arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/agent-xyz123";

            // Base64url encode token
            const base64url = btoa(bearerToken)
                .replace(/\+/g, '-')
                .replace(/\//g, '_')
                .replace(/=/g, '');

            const ws = new WebSocket(
                `wss://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/${runtimeArn}/ws`,
                [`base64UrlBearerAuthorization.${base64url}`, "base64UrlBearerAuthorization"]
            );

            ws.onopen = () => ws.send(JSON.stringify({ inputText: "Hello!" }));
            ws.onmessage = (e) => document.getElementById("output").innerText = e.data;
        }
    </script>
</body>
</html>
```

**Note**  
This authentication method is for browser-based clients where setting custom headers is not possible. For non-browser clients (Python, Node.js servers, etc.), use OAuth header authentication shown in [Python client with OAuth](#websocket-oauth-python).

**Note**  
Subprotocols other than `base64UrlBearerAuthorization` are not yet supported.

**Important**  
This is a reference example. It is not recommended to hardcode tokens in production code.

## Session management


Providing a `session_id` ( `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` ) on the WebSocket connection (as either a URL query parameter or request header) routes the connection to an isolated runtime session. The agent can access conversation context stored within that session, to implement continuity for a conversation by referencing previous interactions. Different session IDs access separate isolated contexts, ensuring complete isolation between users or conversations.

For comprehensive session lifecycle management including tracking, cleanup, and error handling, see [Use isolated sessions for agents](runtime-sessions.md).

### Using sessions with WebSocket connections


To use sessions with WebSocket connections, generate a unique session ID for each user or conversation and pass it when establishing the connection:

**Example**  

1. 

   ```
   from bedrock_agentcore.runtime import AgentCoreRuntimeClient
   import websockets
   import asyncio
   import json
   import os
   
   async def websocket_with_session():
       client = AgentCoreRuntimeClient(region="us-west-2")
       session_id = "user-123-conversation-456"
       runtime_arn = os.getenv('AGENT_ARN')
   
       ws_url, headers = client.generate_ws_connection(
           runtime_arn=runtime_arn,
           session_id=session_id
       )
   
       try:
           async with websockets.connect(ws_url, additional_headers=headers) as ws:
               await ws.send(json.dumps({"inputText": "Hello!"}))
               response = await ws.recv()
               print(f"Response: {response}")
       except websockets.exceptions.InvalidStatus as e:
           print(f"WebSocket handshake failed with status code: {e.response.status_code}")
           print(f"Response headers: {e.response.headers}")
           print(f"Response body: {e.response.body.decode()}")
       except Exception as e:
           print(f"Connection failed: {e}")
   
   asyncio.run(websocket_with_session())
   ```

1. 

   ```
   from bedrock_agentcore.runtime import AgentCoreRuntimeClient
   import websockets
   import asyncio
   import json
   import os
   
   async def websocket_with_session():
       client = AgentCoreRuntimeClient(region="us-west-2")
       session_id = "user-123-conversation-456"
       runtime_arn = os.getenv('AGENT_ARN')
   
       presigned_url = client.generate_presigned_url(
           runtime_arn=runtime_arn,
           session_id=session_id,
           expires=300
       )
   
       try:
           async with websockets.connect(presigned_url) as ws:
               await ws.send(json.dumps({"inputText": "Hello!"}))
               response = await ws.recv()
               print(f"Response: {response}")
       except websockets.exceptions.InvalidStatus as e:
           print(f"WebSocket handshake failed with status code: {e.response.status_code}")
           print(f"Response headers: {e.response.headers}")
           print(f"Response body: {e.response.body.decode()}")
       except Exception as e:
           print(f"Connection failed: {e}")
   
   asyncio.run(websocket_with_session())
   ```

1. 

   ```
   from bedrock_agentcore.runtime import AgentCoreRuntimeClient
   import websockets
   import asyncio
   import json
   import os
   
   async def websocket_with_session():
       client = AgentCoreRuntimeClient(region="us-west-2")
       session_id = "user-123-conversation-456"
       runtime_arn = os.getenv('AGENT_ARN')
       bearer_token = os.getenv('BEARER_TOKEN')
   
       ws_url, headers = client.generate_ws_connection_oauth(
           runtime_arn=runtime_arn,
           session_id=session_id,
           bearer_token=bearer_token
       )
   
       try:
           async with websockets.connect(ws_url, additional_headers=headers) as ws:
               await ws.send(json.dumps({"inputText": "Hello!"}))
               response = await ws.recv()
               print(f"Response: {response}")
       except websockets.exceptions.InvalidStatus as e:
           print(f"WebSocket handshake failed with status code: {e.response.status_code}")
           print(f"Response headers: {e.response.headers}")
           print(f"Response body: {e.response.body.decode()}")
       except Exception as e:
           print(f"Connection failed: {e}")
   
   asyncio.run(websocket_with_session())
   ```

**Tip**  
For best results, use a UUID or other unique identifier for your session IDs to avoid collisions between different users or conversations.

By using the same session ID for related WebSocket connections, you ensure that context is maintained across the same conversation, allowing your agent to provide coherent responses that build on previous interactions.

### Session lifecycle with WebSocket connections


For WebSocket connections, the session’s idle timeout is reset each time there is message activity between the client and agent. This includes any WebSocket message exchange such as sending data from client to agent, receiving responses from agent to client, or WebSocket ping/pong frames. This means that active WebSocket conversations will keep the session alive as long as messages continue to flow, preventing premature session termination during ongoing interactions.

For more information about configuring lifecycle settings, see [Configure Amazon Bedrock AgentCore lifecycle settings](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-lifecycle-settings.html) . For more direct control of session lifecycle through agent health status, see [Runtime session lifecycle management](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-long-run.html#runtime-long-run-session-lifecycle).

### Stop runtime session


To stop a running session before the configurable `IdleRuntimeSessionTimeout` (defaulted at 15 minutes), see [Stop a running session](runtime-stop-session.md).

## Observability


 [Amazon Bedrock AgentCore Observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability.html) helps you trace, debug, and monitor agents that you host in Amazon Bedrock AgentCore Runtime. First enable CloudWatch Transaction Search by following the instructions at [Enabling Amazon Bedrock AgentCore runtime observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-configure.html#observability-configure-builtin) . To observe your agent, see [View observability data for your Amazon Bedrock AgentCore agents](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-view.html).

For WebSocket connections, a trace represents the complete connection session rather than individual message exchanges.

## Custom Headers


Custom headers let you pass contextual information from your application directly to your agent code on the initial WebSocket connection. For complete information about custom header support, configuration, and limitations, see [Pass custom headers to Amazon Bedrock AgentCore Runtime](runtime-header-allowlist.md).

Additionally, headers prefixed with `X-Amzn-Bedrock-AgentCore-Runtime-Custom-` can be passed as URL query parameters in WebSocket connections.

For example, you can pass custom headers as query parameters in the WebSocket URL:

```
wss://bedrock-agentcore.<region>.amazonaws.com/runtimes/<agentRuntimeArn>/ws?X-Amzn-Bedrock-AgentCore-Runtime-Custom-TestHeader=query-param-test-value
```

The agent application container will receive these as headers:

```
"headers": {
    "x-amzn-bedrock-agentcore-runtime-custom-testheader": "query-param-test-value"
  }
```

## Appendix


**Topics**
+ [

### Security considerations
](#security-considerations)
+ [

### Troubleshooting
](#websocket-troubleshooting)
+ [

### WebSocket vs other protocols
](#websocket-vs-other-protocols)
+ [

### Additional getting started examples
](#websocket-additional-examples)

### Security considerations


Authentication  
All WebSocket connections require proper AWS authentication through SigV4 or OAuth 2.0

Session Isolation  
Each session runs in isolated execution environments with dedicated resources

Transport Security  
All connections use WSS (WebSocket Secure) over HTTPS for encrypted communication

Access Control  
IAM policies control WebSocket connection permissions and access to specific agents

### Troubleshooting


#### Common WebSocket-specific issues


The following are common issues you might encounter:

Connection failures  
Verify that your agent application processes connection requests at `/ws` 

Authentication method mismatch  
Ensure your client uses the same authentication method (OAuth or SigV4) that the agent was configured with

Connection closed due to limit exceeded  
Connections are automatically closed if limits are exceeded, such as message frame rate or message frame size limits. For complete limit information, see [Quotas for Amazon Bedrock AgentCore](bedrock-agentcore-limits.md) 

Message frame size exceeded  
Configure message frame fragmentation or implement chunking to stay below the 32KB frame size limit. Split large messages into smaller chunks before sending

Health check failures  
Ensure your agent container implements the `/ping` endpoint as specified in [HTTP protocol contract](runtime-http-protocol-contract.md) . This endpoint verifies that your agent is operational and ready to handle requests, enabling service monitoring and automated recovery

#### Error handling


WebSocket connections use standard close codes for error communication. Common close codes include:
+  `1000` - Normal closure
+  `1001` - Going away
+  `1008` - Policy violated (limit exceeded)
+  `1009` - Message too big (message frame size limit exceeded)
+  `1011` - Server error

### WebSocket vs other protocols


 **When to use WebSocket** :
+ Real-time voice conversations with immediate audio streaming for natural conversation flow
+ Bidirectional audio/text/binary data flow (streaming data chunks from client to agent and vice versa)
+ Interrupt handling (user can interrupt agent mid-conversation)

 **When to use HTTP** :
+ HTTP for request-response patterns without bidirectional streaming needs

### Additional getting started examples


For additional examples using WebSocket bidirectional streaming with AgentCore Runtime, see the [WebSocket bidirectional streaming GitHub samples](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming) :
+  **Sonic implementation (Python)** : Native Amazon Nova Sonic WebSocket implementation with real-time audio conversations, voice selection, and interruption support
+  **Strands implementation (Python)** : Framework-based implementation using the Strands BidiAgent for simplified real-time audio conversations with automatic session management and tool integration
+  **Echo implementation (Python)** : Simple echo server for testing WebSocket connectivity and authentication

# Use any agent framework
Use any agent framework

You can use open source AI frameworks to create an agent or tool. This topic shows examples for a variety of frameworks, including Strands Agents, LangGraph, and Google ADK.

**Topics**
+ [

## Strands Agents
](#agent-runtime-frameworks-strands)
+ [

## LangGraph
](#agent-runtime-frameworks-langgraph)
+ [

## Google Agent Development Kit (ADK)
](#agent-runtime-frameworks-google-adk)
+ [

## OpenAI Agents SDK
](#agent-runtime-frameworks-open-ai-agents-sdk)

## Strands Agents


For the full example, see [https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/03-integrations/agentic-frameworks/strands-agents](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/03-integrations/agentic-frameworks/strands-agents).

```
import os
from strands import Agent
from strands_tools import file_read, file_write, editor

agent = Agent(tools=[file_read, file_write, editor])

from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
@app.entrypoint
def agent_invocation(payload, context):
    """Handler for agent invocation"""
    user_message = payload.get("prompt", "No prompt found in input, please guide customer to create a json payload with prompt key")
    result = agent(user_message)
    print("context:\n-------\n", context)
    print("result:\n*******\n", result)
    return {"result": result.message}
app.run()
```

## LangGraph


For the full example, see [https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/03-integrations/agentic-frameworks/langgraph](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/03-integrations/agentic-frameworks/langgraph).

```
from langchain.chat_models import init_chat_model
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
#------------------------------------------------
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()
#------------------------------------------------

llm = init_chat_model(
    "us.anthropic.claude-3-5-haiku-20241022-v1:0",
    model_provider="bedrock_converse",
)

# Create graph
graph_builder = StateGraph(State)
...
# Add nodes and edges
...
graph = graph_builder.compile()

# Finally write your entrypoint
@app.entrypoint
def agent_invocation(payload, context):

    print("received payload")
    print(payload)

    tmp_msg = {"messages": [{"role": "user", "content": payload.get("prompt", "No prompt found in input, please guide customer as to what tools can be used")}]}
    tmp_output = graph.invoke(tmp_msg)
    print(tmp_output)

    return {"result": tmp_output['messages'][-1].content}

app.run()
```

## Google Agent Development Kit (ADK)


For the full example, see [https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/03-integrations/agentic-frameworks/adk](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/03-integrations/agentic-frameworks/adk).

```
from google.adk.agents import Agent
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
from google.adk.tools import google_search
from google.genai import types
import asyncio
import os

# adapted form https://google.github.io/adk-docs/tools/built-in-tools/#google-search

APP_NAME="google_search_agent"
USER_ID="user1234"

# Agent Definition
# Add your GEMINI_API_KEY
root_agent = Agent(
    model="gemini-2.0-flash",
    name="openai_agent",
    description="Agent to answer questions using Google Search.",
    instruction="I can answer your questions by searching the internet. Just ask me anything!",
    # google_search is a pre-built tool which allows the agent to perform Google searches.
    tools=[google_search]
)

# Session and Runner
async def setup_session_and_runner(user_id, session_id):
    session_service = InMemorySessionService()
    session = await session_service.create_session(app_name=APP_NAME, user_id=user_id, session_id=session_id)
    runner = Runner(agent=root_agent, app_name=APP_NAME, session_service=session_service)
    return session, runner

# Agent Interaction
async def call_agent_async(query, user_id, session_id):
    content = types.Content(role='user', parts=[types.Part(text=query)])
    session, runner = await setup_session_and_runner(user_id, session_id)
    events = runner.run_async(user_id=user_id, session_id=session_id, new_message=content)

    async for event in events:
        if event.is_final_response():
            final_response = event.content.parts[0].text
            print("Agent Response: ", final_response)

    return final_response

from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()

@app.entrypoint
def agent_invocation(payload, context):
    return asyncio.run(call_agent_async(payload.get("prompt", "what is Bedrock Agentcore Runtime?"), payload.get("user_id",USER_ID), context.session_id))

app.run()
```

## OpenAI Agents SDK


For the full example, see [https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/03-integrations/agentic-frameworks/openai-agents](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/03-integrations/agentic-frameworks/openai-agents).

```
from agents import Agent, Runner, WebSearchTool
import logging
import asyncio
import sys

# Set up logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger("openai_agents")

# Configure OpenAI library logging
logging.getLogger("openai").setLevel(logging.DEBUG)

logger.debug("Initializing OpenAI agent with tools")
agent = Agent(
    name="Assistant",
    tools=[
        WebSearchTool(),
    ],
)

async def main(query=None):
    if query is None:
        query = "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?"

    logger.debug(f"Running agent with query: {query}")

    try:
        logger.debug("Starting agent execution")
        result = await Runner.run(agent, query)
        logger.debug(f"Agent execution completed with result type: {type(result)}")
        return result
    except Exception as e:
        logger.error(f"Error during agent execution: {e}", exc_info=True)
        raise

# Integration with Bedrock AgentCore
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()

@app.entrypoint
async def agent_invocation(payload, context):
    logger.debug(f"Received payload: {payload}")
    query = payload.get("prompt", "How can I help you today?")

    try:
        result = await main(query)
        logger.debug("Agent execution completed successfully")
        return {"result": result.final_output}
    except Exception as e:
        logger.error(f"Error during agent execution: {e}", exc_info=True)
        return {"result": f"Error: {str(e)}"}

# Run the app when imported
if __name__== "__main__":
    app.run()
```

# Use any foundation model
Use any foundation model

You can use any foundation model with AgentCore Runtime The following are examples for Amazon Bedrock, Open AI, Gemini and Fireworks AI:

**Topics**
+ [

## Amazon Bedrock
](#agent-runtime-model-bedrock)
+ [

## Open AI
](#agent-runtime-model-open-ai)
+ [

## Gemini
](#agent-runtime-model-gemini)
+ [

## Fireworks AI
](#agent-runtime-model-fireworksai)

## Amazon Bedrock


```
import boto3
from strands.models import BedrockModel

# Create a Bedrock model with the custom session
bedrock_model = BedrockModel(
    model_id="us.anthropic.claude-3-7-sonnet-20250219-v1:0",
    boto_session=session
)
```

## Open AI


```
from strands.models.openai import OpenAIModel
model = OpenAIModel(
    client_args={
        "api_key": "<our_OPENAI_API_KEY>",
    # **model_config
    model_id="gpt-4o",
    params={
        "max_tokens": 1000,
        "temperature": 0.7,
}
)

from strands import Agent
from strands_tools import python_repl
agent = Agent(model=model,tools=[python_repl])
```

## Gemini


```
import os
from langchain.chat_models import init_chat_model

# Use your Google API key to initialize the chat model
os.environ["GOOGLE_API_KEY"] = "..."

llm = init_chat_model("google_genai:gemini-2.0-flash")
```

## Fireworks AI


```
from strands import Agent, tool
from strands_tools import file_read, file_write
from strands.models.openai import OpenAIModel
import os
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()

@tool
def code_python(user_prompt: str):
    """Generate Python code based on user requirements."""
    return f"Generate clean Python code for: {user_prompt}"

model = OpenAIModel(
    client_args={
        "api_key": os.getenv("FIREWORKS_API_KEY"),
        "base_url": "https://api.fireworks.ai/inference/v1",
    },
    model_id="accounts/fireworks/models/kimi-k2-instruct-0905",
    params={"max_tokens": 5000, "temperature": 0.0}
)

agent = Agent(
    model=model,
    tools=[file_read, file_write, code_python],
    system_prompt="You are a software engineer. You can read files, write files and generate python code."
)

@app.entrypoint
def strands_agent_fireworks_ai(payload):
    user_input = payload.get("prompt")
    response = agent(user_input)
    return response.message['content'][0]['text']

if __name__ == "__main__":
    app.run()
```

# Deploy MCP servers in AgentCore Runtime
Deploy MCP servers

Amazon Bedrock AgentCore Runtime lets you deploy and run Model Context Protocol (MCP) servers in the AgentCore Runtime. This guide walks you through creating, testing, and deploying your first MCP server.

For an example, see [https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/01-tutorials/01-AgentCore-runtime/02-hosting-MCP-server).

In this section, you learn:
+ How to create an MCP server with tools
+ How to test your server locally
+ How to deploy your server to AWS 
+ How to invoke your deployed server

For more information about MCP, see [MCP protocol contract](runtime-mcp-protocol-contract.md).

**Topics**
+ [

## How Amazon Bedrock AgentCore supports MCP
](#runtime-mcp-how-it-works)
+ [

## Prerequisites
](#runtime-mcp-prerequisites)
+ [

## Step 1: Create your MCP server
](#runtime-mcp-create-server)
+ [

## Step 2: Test your MCP server locally
](#runtime-mcp-test-locally)
+ [

## Step 3: Deploy your MCP server to AWS
](#runtime-mcp-deploy-aws)
+ [

## Step 4: Invoke your deployed MCP server
](#runtime-mcp-invoke-server)
+ [

## Authentication Error Responses for OAuth-Configured Agents
](#runtime-mcp-auth-error-responses)
+ [

## End to End Flow with Auth0
](#runtime-mcp-auth0-flow)
+ [

## Appendix
](#runtime-mcp-appendix)

## How Amazon Bedrock AgentCore supports MCP


When you configure a Amazon Bedrock AgentCore Runtime with the MCP protocol, the service expects MCP server containers to be available at the path `0.0.0.0:8000/mcp` , which is the default path supported by most official MCP server SDKs.

Amazon Bedrock AgentCore supports both stateless and stateful streamable-HTTP MCP servers. By default, stateless mode ( `stateless_http=True` ) is recommended for basic MCP servers. The platform automatically adds an `Mcp-Session-Id` header for any request without one, so MCP clients can maintain connection continuity to the same Amazon Bedrock AgentCore Runtime session.

For MCP servers that require multi-turn interactions (elicitation), LLM-generated content (sampling), or progress notifications, stateful mode ( `stateless_http=False` ) enables these capabilities. In stateful mode, the runtime preserves MCP session state across requests within the same invocation. For more information, see [Stateful MCP server features](mcp-stateful-features.md).

The payload of the [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) API is passed through directly, allowing RPC messages of protocols like MCP to be easily proxied.

## Prerequisites

+ Python 3.10 or higher installed and basic understanding of Python
+ An AWS account with appropriate permissions and local credentials configured

## Step 1: Create your MCP server


### Install required packages


First, install the MCP package:

```
pip install mcp
```

### Create your first MCP server


Create a new file called `my_mcp_server.py` :

```
# my_mcp_server.py

from mcp.server.fastmcp import FastMCP
from starlette.responses import JSONResponse

mcp = FastMCP(host="0.0.0.0", stateless_http=True)

@mcp.tool()
def add_numbers(a: int, b: int) -> int:
    """Add two numbers together"""
    return a + b

@mcp.tool()
def multiply_numbers(a: int, b: int) -> int:
    """Multiply two numbers together"""
    return a * b

@mcp.tool()
def greet_user(name: str) -> str:
    """Greet a user by name"""
    return f"Hello, {name}! Nice to meet you."

if __name__ == "__main__":
    mcp.run(transport="streamable-http")
```

### Understanding the code

+  **FastMCP** : Creates an MCP server that can host your tools
+  **@mcp.tool()** : Decorator that turns your Python functions into MCP tools
+  **Tools** : Three simple tools that demonstrate different types of operations
+  **stateless\$1http=True** : Configures the server in stateless mode, which is the default for basic MCP servers

**Tip**  
For MCP servers that require multi-turn interactions (elicitation) or LLM-generated content (sampling), use `stateless_http=False` to enable stateful mode. Stateful MCP servers maintain session context across multiple requests within the same tool invocation. For more information, see [Stateful MCP server features](mcp-stateful-features.md).

## Step 2: Test your MCP server locally


### Start your MCP server


Run your MCP server locally:

```
python my_mcp_server.py
```

You should see output indicating the server is running on port `8000`.

### Test with MCP client


From a new terminal, create a new file `my_mcp_client.py` and execute it using `python my_mcp_client.py` 

```
# my_mcp_client.py

import asyncio

from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def main():
    mcp_url = "http://localhost:8000/mcp"
    headers = {}

    async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as (
        read_stream,
        write_stream,
        _,
    ):
        async with ClientSession(read_stream, write_stream) as session:
            await session.initialize()
            tool_result = await session.list_tools()
            print(tool_result)

asyncio.run(main())
```

You can also test your server using the MCP Inspector as described in [Local testing with MCP inspector](#runtime-mcp-appendix-b).

## Step 3: Deploy your MCP server to AWS


### Install deployment tools


Install the AgentCore CLI:

```
npm install -g @aws/agentcore
```

You use the AgentCore CLI to deploy your agent to AgentCore Runtime.

Create a project folder with the following structure:

```
## Project Folder Structure

your_project_directory/
├── mcp_server.py # Your main agent code
├── requirements.txt # Dependencies for your agent
└── __init__.py # Makes the directory a Python package
```

Create a new file called `requirements.txt` , add the following to it:

```
mcp
```

 `requirements.txt` specifies the requirements that the agent needs for deployment to AgentCore Runtime.

### Create your project for deployment


Before creating your project, you need to set up a Cognito user pool for authentication as described in [Set up Cognito user pool for authentication](#runtime-mcp-appendix-a) . This provides the OAuth tokens required for secure access to your deployed server.

**Note**  
Starting **October 7, 2025** , Amazon Bedrock AgentCore uses a Service-Linked Role for workload identity permissions when using OAuth authentication. For detailed information about this change, see [Identity service-linked role](service-linked-roles.md#identity-service-linked-role).

After setting up authentication, scaffold a new project with MCP protocol:

```
agentcore create --protocol MCP
```

Follow the interactive prompts to provide a project name. The CLI scaffolds the project structure including an `agentcore/agentcore.json` configuration file. Copy your `my_mcp_server.py` file into the generated project’s agent code directory, and ensure the entrypoint in `agentcore/agentcore.json` points to your server file.

### Deploy to AWS


Deploy your agent:

```
agentcore deploy
```

This command will:

1. Package your agent code and dependencies

1. Upload the deployment artifact to Amazon S3

1. Create a Amazon Bedrock AgentCore runtime

1. Deploy your agent to AWS 

After deployment, you’ll receive an agent runtime ARN that looks like:

```
arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/my_mcp_server-xyz123
```

## Step 4: Invoke your deployed MCP server


### Test with MCP client (remote)


Before testing, set the following environment variables:
+ Export agent ARN as an environment variable: `export AGENT_ARN="agent_arn"` 
+ Export bearer token as an environment variable: `export BEARER_TOKEN="bearer_token"` 

if you pass in an `Accept` header, it must follow the [MCP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#sending-messages-to-the-server) standard. Acceptable media types are `application/json` and `text/event-stream`.

Create a new file `my_mcp_client_remote.py` and execute it using `python my_mcp_client_remote.py` 

```
import asyncio
import os
import sys

from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def main():
    agent_arn = os.getenv('AGENT_ARN')
    bearer_token = os.getenv('BEARER_TOKEN')
    if not agent_arn or not bearer_token:
        print("Error: AGENT_ARN or BEARER_TOKEN environment variable is not set")
        sys.exit(1)

    encoded_arn = agent_arn.replace(':', '%3A').replace('/', '%2F')
    mcp_url = f"https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT"
    headers = {"authorization": f"Bearer {bearer_token}","Content-Type":"application/json"}
    print(f"Invoking: {mcp_url}, \nwith headers: {headers}\n")

    async with streamablehttp_client(mcp_url, headers, timeout=120, terminate_on_close=False) as (
        read_stream,
        write_stream,
        _,
    ):
        async with ClientSession(read_stream, write_stream) as session:
            await session.initialize()
            tool_result = await session.list_tools()
            print(tool_result)

asyncio.run(main())
```

You can also test your deployed server using the MCP Inspector as described in [Remote testing with MCP inspector](#runtime-mcp-appendix-c).

## Authentication Error Responses for OAuth-Configured Agents


OAuth-configured agents follow [RFC 6749 (OAuth 2.0)](https://datatracker.ietf.org/doc/html/rfc6749) authentication standards. When authentication is missing, the service returns a 401 Unauthorized response with a WWW-Authenticate header (per [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235) ), enabling clients to discover the authorization server endpoints through the GetRuntimeProtectedResourceMetadata API.

### 401 Unauthorized - Missing Authentication


When no Bearer token is provided in the Authorization header, the response is:

```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{ESCAPED_ARN}/invocations/.well-known/oauth-protected-resource?qualifier={QUALIFIER}"
```

## End to End Flow with Auth0


This section demonstrates OAuth authentication using Auth0 as the identity provider. We use Auth0 for this example because it supports **Dynamic Client Registration (DCR)** , which simplifies the client setup process by allowing clients to register themselves programmatically at runtime.

### Step 1 - Step 3: Create and test your MCP server


Follow Steps 1-3 from [Step 1: Create your MCP server](#runtime-mcp-create-server) through [Step 3: Deploy your MCP server to AWS](#runtime-mcp-deploy-aws) to create and test your MCP server.

### Step 4: Create Auth0 application


Follow the Auth0 setup instructions at [Auth0 by Okta](identity-idp-auth0.md).

 **Enable Dynamic Client Registration:** 

1. Dashboard → Settings → Advanced

1. Toggle "OIDC Dynamic Application Registration" → ON

1. Save changes

For more information, see [Auth0 Dynamic Client Registration documentation](https://auth0.com/docs/get-started/applications/dynamic-client-registration).

### Step 5: Create your project for deployment


After setting up authentication, scaffold a new project with MCP protocol:

```
agentcore create --protocol MCP
```

Follow the interactive prompts to provide a project name. The CLI scaffolds the project structure including an `agentcore/agentcore.json` configuration file. Copy your `my_mcp_server.py` file into the generated project’s agent code directory, and ensure the entrypoint in `agentcore/agentcore.json` points to your server file.

### Step 6: Deploy to AWS


Deploy your agent:

```
agentcore deploy
```

This command will:
+ Package your agent code and dependencies
+ Upload the deployment artifact to Amazon S3
+ Create a Amazon Bedrock AgentCore runtime
+ Deploy your agent to AWS 

After deployment, you’ll receive an agent runtime ARN that looks like:

```
arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/my_mcp_server-xyz123
```

### Step 7: Invoke your deployed Agent


This client is based on the [official MCP SDK simple-auth-client example](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/clients/simple-auth-client/mcp_simple_auth_client/main.py) with Auth0-specific modifications.

**Note**  
When using Auth0 with Dynamic Client Registration, you must include the `audience` parameter in authorization requests to receive JWT tokens. Without this parameter, Auth0 returns opaque tokens or JWE (encrypted) tokens instead of standard JWT tokens. The MCP SDK sends OAuth 2.0’s `resource` parameter (RFC 8707), but Auth0 requires the OIDC `audience` parameter for JWT tokens. Both parameters serve similar purposes but Auth0 prioritizes `audience` . For more information, see [Auth0 Community - JWT tokens with Dynamic Application Registration](https://community.auth0.com/t/jwt-tokens-with-dynamic-application-registration/189741).

Create a file named `mcp_auth0_client.py` with the following code. This client handles Auth0-specific requirements including the audience parameter:

**Note**  
The code includes httpx patching to inject User-Agent headers into all HTTP requests. This is necessary because the MCP Python SDK currently does not include User-Agent headers in its HTTP requests, which can cause issues with AWS WAF rules that require User-Agent headers. For more information, see [MCP Python SDK Issue \$11664](https://github.com/modelcontextprotocol/python-sdk/issues/1664) and [AWS WAF managed rule groups](https://docs.aws.amazon.com/waf/latest/developerguide/aws-managed-rule-groups-baseline.html).

#### MCP Python Auth0 Client Code


```
#!/usr/bin/env python3
"""
MCP client with OAuth authentication support for Auth0.

Based on the official MCP SDK simple-auth-client example with Auth0 compatibility.
Adds support for Auth0's 'audience' parameter requirement.

Usage:
    # Required
    export AGENT_ARN="arn:aws:bedrock:us-west-2:123456789012:agent/ABCD1234"

    # Required for Auth0
    export AUTH0_API_IDENTIFIER="your-api-identifier"

    # Optional - custom endpoint for beta/dev environments
    export CUSTOM_ENDPOINT="https://beta.example.com"

    python mcp_auth0_client.py

The client will automatically:
- Encode the Agent ARN for use in the URL
- Construct the MCP invocation endpoint URL
- Add Auth0 'audience' parameter to authorization requests (when using Auth0)
- Work with any OAuth 2.0 compliant identity provider
"""

import asyncio
import httpx
import os
import threading
import time
import webbrowser
from datetime import timedelta
from http.server import BaseHTTPRequestHandler, HTTPServer
from typing import Any
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse

# Patch httpx at the request level to inject User-Agent header
# This ensures ALL HTTP requests have the User-Agent header, including OAuth discovery calls
_original_httpx_request = httpx.Request.__init__

def _patched_httpx_request_init(self, method, url, *args, **kwargs):
    """Patched Request.__init__ that injects User-Agent header into all HTTP requests."""
    # Get or create headers
    headers = kwargs.get('headers')
    if headers is None:
        headers = {}
        kwargs['headers'] = headers

    # Convert to mutable dict if needed
    if not isinstance(headers, dict):
        headers = dict(headers)
        kwargs['headers'] = headers

    # Inject User-Agent if not present (case-insensitive check)
    if 'User-Agent' not in headers and 'user-agent' not in headers:
        headers['User-Agent'] = 'python-mcp-sdk/1.0 (BedrockAgentCore-Runtime)'

    # Call original __init__
    _original_httpx_request(self, method, url, *args, **kwargs)

# Apply the patch globally before importing MCP modules
httpx.Request.__init__ = _patched_httpx_request_init

# Now import MCP modules - they will use patched httpx
from mcp.client.auth import OAuthClientProvider, TokenStorage
from mcp.client.session import ClientSession
from mcp.client.sse import sse_client
from mcp.client.streamable_http import streamablehttp_client
from mcp.shared.auth import OAuthClientInformationFull, OAuthClientMetadata, OAuthToken

class InMemoryTokenStorage(TokenStorage):
    """Simple in-memory token storage implementation."""

    def __init__(self):
        self._tokens: OAuthToken | None = None
        self._client_info: OAuthClientInformationFull | None = None

    async def get_tokens(self) -> OAuthToken | None:
        return self._tokens

    async def set_tokens(self, tokens: OAuthToken) -> None:
        self._tokens = tokens

    async def get_client_info(self) -> OAuthClientInformationFull | None:
        return self._client_info

    async def set_client_info(self, client_info: OAuthClientInformationFull) -> None:
        self._client_info = client_info

class CallbackHandler(BaseHTTPRequestHandler):
    """Simple HTTP handler to capture OAuth callback."""

    def __init__(self, request, client_address, server, callback_data):
        """Initialize with callback data storage."""
        self.callback_data = callback_data
        super().__init__(request, client_address, server)

    def do_GET(self):
        """Handle GET request from OAuth redirect."""
        parsed = urlparse(self.path)
        query_params = parse_qs(parsed.query)

        if "code" in query_params:
            self.callback_data["authorization_code"] = query_params["code"][0]
            self.callback_data["state"] = query_params.get("state", [None])[0]
            self.send_response(200)
            self.send_header("Content-type", "text/html")
            self.end_headers()
            self.wfile.write(b"""
            <html>
            <body>
                <h1>Authorization Successful!</h1>
                <p>You can close this window and return to the terminal.</p>
                <script>setTimeout(() => window.close(), 2000);</script>
            </body>
            </html>
            """)
        elif "error" in query_params:
            self.callback_data["error"] = query_params["error"][0]
            self.send_response(400)
            self.send_header("Content-type", "text/html")
            self.end_headers()
            self.wfile.write(
                f"""
            <html>
            <body>
                <h1>Authorization Failed</h1>
                <p>Error: {query_params["error"][0]}</p>
                <p>You can close this window and return to the terminal.</p>
            </body>
            </html>
            """.encode()
            )
        else:
            self.send_response(404)
            self.end_headers()

    def log_message(self, format, *args):
        """Suppress default logging."""
        pass

class CallbackServer:
    """Simple server to handle OAuth callbacks."""

    def __init__(self, port=3030):
        self.port = port
        self.server = None
        self.thread = None
        self.callback_data = {"authorization_code": None, "state": None, "error": None}

    def _create_handler_with_data(self):
        """Create a handler class with access to callback data."""
        callback_data = self.callback_data

        class DataCallbackHandler(CallbackHandler):
            def __init__(self, request, client_address, server):
                super().__init__(request, client_address, server, callback_data)

        return DataCallbackHandler

    def start(self):
        """Start the callback server in a background thread."""
        handler_class = self._create_handler_with_data()
        self.server = HTTPServer(("localhost", self.port), handler_class)
        self.thread = threading.Thread(target=self.server.serve_forever, daemon=True)
        self.thread.start()
        print(f"🖥️  Started callback server on http://localhost:{self.port}")

    def stop(self):
        """Stop the callback server."""
        if self.server:
            self.server.shutdown()
            self.server.server_close()
        if self.thread:
            self.thread.join(timeout=1)

    def wait_for_callback(self, timeout=300):
        """Wait for OAuth callback with timeout."""
        start_time = time.time()
        while time.time() - start_time < timeout:
            if self.callback_data["authorization_code"]:
                return self.callback_data["authorization_code"]
            elif self.callback_data["error"]:
                raise Exception(f"OAuth error: {self.callback_data['error']}")
            time.sleep(0.1)
        raise Exception("Timeout waiting for OAuth callback")

    def get_state(self):
        """Get the received state parameter."""
        return self.callback_data["state"]

def add_auth0_audience_parameter(authorization_url: str, audience: str) -> str:
    """
    Add Auth0 'audience' parameter to authorization URL.

    Auth0 requires the 'audience' parameter to identify which API's token settings
    to use. Without it, Auth0 returns opaque tokens or JWE instead of JWT.

    This function properly adds the audience parameter while preserving all existing
    query parameters (including the OAuth 'resource' parameter).

    Args:
        authorization_url: The authorization URL from the OAuth flow
        audience: The Auth0 API identifier (e.g., "runtime-api")

    Returns:
        Modified URL with audience parameter added

    Reference:
        https://auth0.com/docs/secure/tokens/access-tokens/get-access-tokens
    """
    # Only apply to Auth0 URLs that don't already have audience
    if 'auth0.com' not in authorization_url or 'audience=' in authorization_url:
        return authorization_url

    # Parse URL and query parameters
    parsed = urlparse(authorization_url)
    query_params = parse_qs(parsed.query, keep_blank_values=True)

    # Add audience parameter
    query_params['audience'] = [audience]

    # Rebuild URL with new parameter
    new_query = urlencode(query_params, doseq=True)
    return urlunparse((
        parsed.scheme,
        parsed.netloc,
        parsed.path,
        parsed.params,
        new_query,
        parsed.fragment
    ))

class SimpleAuthClient:
    """Simple MCP client with Auth0 OAuth support."""

    def __init__(
        self,
        server_url: str,
        transport_type: str = "streamable-http",
        auth0_audience: str | None = None,
    ):
        self.server_url = server_url
        self.transport_type = transport_type
        self.auth0_audience = auth0_audience
        self.session: ClientSession | None = None

    async def connect(self):
        """Connect to the MCP server."""
        print(f"🔗 Attempting to connect to {self.server_url}...")

        try:
            callback_server = CallbackServer(port=3030)
            callback_server.start()

            async def callback_handler() -> tuple[str, str | None]:
                """Wait for OAuth callback and return auth code and state."""
                print("⏳ Waiting for authorization callback...")
                try:
                    auth_code = callback_server.wait_for_callback(timeout=300)
                    return auth_code, callback_server.get_state()
                finally:
                    callback_server.stop()

            client_metadata_dict = {
                "client_name": "MCP Auth0 Client",
                "redirect_uris": ["http://localhost:3030/callback"],
                "grant_types": ["authorization_code", "refresh_token"],
                "response_types": ["code"],
            }

            async def redirect_handler(authorization_url: str) -> None:
                """Redirect handler that opens the URL in a browser with Auth0 audience parameter."""
                # Add Auth0 audience parameter if configured
                if self.auth0_audience:
                    authorization_url = add_auth0_audience_parameter(
                        authorization_url,
                        self.auth0_audience
                    )

                webbrowser.open(authorization_url)

            print("\n🔧 Creating OAuth client provider...")
            # Create OAuth authentication handler
            # Note: httpx.AsyncClient is globally patched to inject User-Agent header
            oauth_auth = OAuthClientProvider(
                server_url=self.server_url,
                client_metadata=OAuthClientMetadata.model_validate(client_metadata_dict),
                storage=InMemoryTokenStorage(),
                redirect_handler=redirect_handler,
                callback_handler=callback_handler,
            )
            print("🔧 OAuth client provider created successfully")

            # Create transport with auth handler based on transport type
            if self.transport_type == "sse":
                print("📡 Opening SSE transport connection with auth...")
                async with sse_client(
                    url=self.server_url,
                    auth=oauth_auth,
                    timeout=60,
                ) as (read_stream, write_stream):
                    await self._run_session(read_stream, write_stream, None)
            else:
                print("📡 Opening StreamableHTTP transport connection with auth...")
                async with streamablehttp_client(
                    url=self.server_url,
                    auth=oauth_auth,
                    timeout=timedelta(seconds=60),
                ) as (read_stream, write_stream, get_session_id):
                    await self._run_session(read_stream, write_stream, get_session_id)

        except Exception as e:
            print(f"❌ Failed to connect: {e}")
            import traceback
            traceback.print_exc()

    async def _run_session(self, read_stream, write_stream, get_session_id):
        """Run the MCP session with the given streams."""
        print("🤝 Initializing MCP session...")
        async with ClientSession(read_stream, write_stream) as session:
            self.session = session
            print("⚡ Starting session initialization...")
            await session.initialize()
            print("✨ Session initialization complete!")

            print(f"\n✅ Connected to MCP server at {self.server_url}")
            if get_session_id:
                session_id = get_session_id()
                if session_id:
                    print(f"Session ID: {session_id}")

            # Run interactive loop
            await self.interactive_loop()

    async def list_tools(self):
        """List available tools from the server."""
        if not self.session:
            print("❌ Not connected to server")
            return

        try:
            result = await self.session.list_tools()
            if hasattr(result, "tools") and result.tools:
                print("\n📋 Available tools:")
                for i, tool in enumerate(result.tools, 1):
                    print(f"{i}. {tool.name}")
                    if tool.description:
                        print(f"   Description: {tool.description}")
                    print()
            else:
                print("No tools available")
        except Exception as e:
            print(f"❌ Failed to list tools: {e}")

    async def call_tool(self, tool_name: str, arguments: dict[str, Any] | None = None):
        """Call a specific tool."""
        if not self.session:
            print("❌ Not connected to server")
            return

        try:
            result = await self.session.call_tool(tool_name, arguments or {})
            print(f"\n🔧 Tool '{tool_name}' result:")
            if hasattr(result, "content"):
                for content in result.content:
                    if content.type == "text":
                        print(content.text)
                    else:
                        print(content)
            else:
                print(result)
        except Exception as e:
            print(f"❌ Failed to call tool '{tool_name}': {e}")

    async def interactive_loop(self):
        """Run interactive command loop."""
        print("\n🎯 Interactive MCP Client")
        print("Commands:")
        print("  list - List available tools")
        print("  call <tool_name> [args] - Call a tool")
        print("  quit - Exit the client")
        print()

        while True:
            try:
                command = input("mcp> ").strip()

                if not command:
                    continue

                if command == "quit":
                    break

                elif command == "list":
                    await self.list_tools()

                elif command.startswith("call "):
                    parts = command.split(maxsplit=2)
                    tool_name = parts[1] if len(parts) > 1 else ""

                    if not tool_name:
                        print("❌ Please specify a tool name")
                        continue

                    # Parse arguments (simple JSON-like format)
                    arguments = {}
                    if len(parts) > 2:
                        import json
                        try:
                            arguments = json.loads(parts[2])
                        except json.JSONDecodeError:
                            print("❌ Invalid arguments format (expected JSON)")
                            continue

                    await self.call_tool(tool_name, arguments)

                else:
                    print("❌ Unknown command. Try 'list', 'call <tool_name>', or 'quit'")

            except KeyboardInterrupt:
                print("\n\n👋 Goodbye!")
                break
            except EOFError:
                break

async def main():
    """Main entry point."""
    # Get Agent ARN from environment
    agent_arn = os.getenv("AGENT_ARN")

    if not agent_arn:
        print("❌ Please set AGENT_ARN environment variable")
        print("Example: export AGENT_ARN='arn:aws:bedrock:us-west-2:123456789012:agent/ABCD1234'")
        return

    # Encode the ARN for use in URL
    encoded_arn = agent_arn.replace(':', '%3A').replace('/', '%2F')

    # Get base URL - use custom endpoint or default to production
    base_endpoint = os.getenv("CUSTOM_ENDPOINT", "https://bedrock-agentcore.us-west-2.amazonaws.com")

    # Construct MCP URL from encoded ARN (no qualifier - SDK discovers it from PRM API)
    server_url = f"{base_endpoint}/runtimes/{encoded_arn}/invocations"

    # Get Auth0 configuration (required only for Auth0)
    auth0_audience = os.getenv("AUTH0_API_IDENTIFIER")

    # Get optional transport type
    transport_type = os.getenv("MCP_TRANSPORT_TYPE", "streamable-http")

    print("🚀 MCP Auth0 Client")
    print(f"Agent ARN: {agent_arn}")
    print(f"Endpoint: {base_endpoint}")
    print(f"Connecting to: {server_url}")
    print(f"Transport type: {transport_type}")
    if auth0_audience:
        print(f"Auth0 audience: {auth0_audience}")

    # Start connection flow - OAuth will be handled automatically
    client = SimpleAuthClient(
        server_url,
        transport_type,
        auth0_audience,
    )
    await client.connect()

def cli():
    """CLI entry point for uv script."""
    asyncio.run(main())

if __name__ == "__main__":
    cli()
```

To use the client:

1. Set required environment variables:

   ```
   export AGENT_ARN="arn:aws:bedrock:us-west-2:123456789012:agent/ABCD1234"
   ```

1. Set Auth0-specific environment variable (required only for Auth0):

   ```
   export AUTH0_API_IDENTIFIER="your-api-identifier"
   ```

1. Run the client:

   ```
   python mcp_auth0_client.py
   ```

The client will automatically:
+ Encode the Agent ARN for use in the URL
+ Construct the MCP invocation endpoint URL
+ Add Auth0 `audience` parameter to authorization requests (when using Auth0)
+ Work with any OAuth 2.0 compliant identity provider

## Appendix


### Set up Cognito user pool for authentication


Create a new file `setup_cognito.sh` and add the following content.

```
#!/bin/bash

# Create User Pool and capture Pool ID directly
export POOL_ID=$(aws cognito-idp create-user-pool \
  --pool-name "MyUserPool" \
  --policies '{"PasswordPolicy":{"MinimumLength":8}}' \
  --region $REGION | jq -r '.UserPool.Id')

# Create App Client and capture Client ID directly
export CLIENT_ID=$(aws cognito-idp create-user-pool-client \
  --user-pool-id $POOL_ID \
  --client-name "MyClient" \
  --no-generate-secret \
  --explicit-auth-flows "ALLOW_USER_PASSWORD_AUTH" "ALLOW_REFRESH_TOKEN_AUTH" \
  --region $REGION | jq -r '.UserPoolClient.ClientId')

# Create User
aws cognito-idp admin-create-user \
  --user-pool-id $POOL_ID \
  --username $USERNAME \
  --region $REGION \
  --message-action SUPPRESS > /dev/null

# Set Permanent Password
aws cognito-idp admin-set-user-password \
  --user-pool-id $POOL_ID \
  --username $USERNAME \
  --password $PASSWORD \
  --region $REGION \
  --permanent > /dev/null

# Authenticate User and capture Access Token
export BEARER_TOKEN=$(aws cognito-idp initiate-auth \
  --client-id "$CLIENT_ID" \
  --auth-flow USER_PASSWORD_AUTH \
  --auth-parameters USERNAME=$USERNAME,PASSWORD=$PASSWORD \
  --region $REGION | jq -r '.AuthenticationResult.AccessToken')

# Output the required values
echo "Pool id: $POOL_ID"
echo "Discovery URL: https://cognito-idp.$REGION.amazonaws.com/$POOL_ID/.well-known/openid-configuration"
echo "Client ID: $CLIENT_ID"
echo "Bearer Token: $BEARER_TOKEN"
```

Open a terminal window and set the following environment variables:
+  *REGION* – the AWS Region that you want to use
+  *USERNAME* – the user name for the new user
+  *PASSWORD* – the password for the new user

```
export REGION=us-east-1 // set your desired Region
export USERNAME=USER NAME
export PASSWORD=PASSWORD
```

Run the script using the command `source setup_cognito.sh`.

**Note**  
For detailed OAuth authentication setup and Service-Linked Role information, see [Authenticate and authorize with Inbound Auth and Outbound Auth](runtime-oauth.md).

After running this script, note the following values for use in the deployment configuration:
+ Discovery URL: Used during the `agentcore create` step
+ Client ID: Used during the `agentcore create` step
+ Bearer Token: Used when invoking your deployed server

### Local testing with MCP inspector


The MCP Inspector is a visual tool for testing MCP servers. To use it, you need:
+ Node.js and npm installed

Install and run the MCP Inspector:

```
npx @modelcontextprotocol/inspector
```

This will:
+ Start the MCP Inspector server
+ Display a URL in your terminal (typically `http://localhost:6274` )

To use the Inspector:

1. Navigate to `http://localhost:6274` in your browser

1. Paste the MCP server URL ( `http://localhost:8000/mcp` ) into the MCP Inspector connection field

1. You’ll see your tools listed in the sidebar

1. Click on any tool to test it

1. Fill in the parameters (e.g., for `add_numbers` , enter values for `a` and `b` )

1. Click "Call Tool" to see the result

### Remote testing with MCP inspector


You can also test your deployed server using the MCP Inspector. First, URL-encode your agent ARN:

```
export AGENT_ARN="arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my_mcp_server-xyz123"
echo -n $AGENT_ARN | jq -sRr '@uri'
```

This outputs the URL-encoded ARN:

```
arn%3Aaws%3Abedrock-agentcore%3Aus-west-2%3A123456789012%3Aruntime%2Fmy_mcp_server-xyz123
```

Then connect with the MCP Inspector:

1. Start the MCP Inspector:

   ```
   npx @modelcontextprotocol/inspector
   ```

1. In the web interface:
   + Select "Streamable HTTP" as the transport
   + Enter your agent’s endpoint URL using the encoded ARN. Make sure to use the same region as your agent’s ARN:

     ```
     https://bedrock-agentcore.REGION.amazonaws.com/runtimes/ENCODED_ARN/invocations?qualifier=DEFAULT
     ```

     Example for us-west-2:

     ```
     https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/arn%3Aaws%3Abedrock-agentcore%3Aus-west-2%3A123456789012%3Aruntime%2Fmy_mcp_server-xyz123/invocations?qualifier=DEFAULT
     ```
   + Add your Bearer token in the Authentication section with header name `Authorization` and value `Bearer YOUR_TOKEN` 
   + Click "Connect"

1. Test your tools just like you did locally

# Stateful MCP server features
Stateful MCP features

The Model Context Protocol (MCP) provides a standardized way for AI applications to interact with external data and capabilities. This guide demonstrates how to build a comprehensive MCP server that showcases all major protocol features, and how to test it both locally and when deployed to Amazon Bedrock AgentCore.

For complete protocol details, see the [MCP Specification](https://modelcontextprotocol.io/specification/2025-11-25).

## MCP features overview


MCP servers can expose capabilities to clients through several feature types. The following features are demonstrated in this guide:

 **Resources**   
Resources expose data and content from your server to MCP clients. Use resources to share configuration, reference data, or any contextual information that clients or AI models can read. Resources are identified by URIs (for example, `travel://destinations` ).

 **Prompts**   
Prompts are reusable templates that generate structured messages for AI models. Use prompts to standardize common interactions, such as generating packing lists or learning local phrases for a destination.

 **Tools**   
Tools are functions that AI models can invoke to perform actions or retrieve information. Tools can range from simple data lookups to complex multi-step workflows that combine other MCP features.

 **Elicitation**   
Elicitation enables server-initiated requests for user input during tool execution. Use elicitation when your tool needs to collect information interactively, such as gathering travel preferences through a multi-turn conversation.

 **Sampling**   
Sampling allows servers to request LLM-generated content from the client. Use sampling when your tool needs AI-powered text generation, such as personalized travel recommendations based on user preferences.

 **Progress notifications**   
Progress notifications keep clients informed about long-running operations. Use progress reporting to provide real-time feedback during tasks like searching for flights or processing bookings.

**Note**  
Features like elicitation, sampling, and progress notifications require stateful MCP sessions. Enable stateful mode by setting `stateless_http=False` when running your server.

 **Session management** 

In stateful mode, the server returns an `Mcp-Session-Id` header during the initialize call. Clients must include this session ID in subsequent requests to maintain session context. If the server terminates or the session expires, requests may return a 404 error, and clients must re-initialize to obtain a new session ID. For more details, see [Session Management](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#session-management) in the MCP specification.

## Create an MCP server with all features


 **To set up the project** 

1. Create a `requirements.txt` file with the required dependencies:

   ```
   fastmcp>=2.10.0
   mcp
   ```

1. Install the dependencies:

   ```
   pip install -r requirements.txt
   ```

Create a file called `travel_server.py` with the following code. This travel booking agent demonstrates all MCP features in a realistic workflow:

### travel\$1server.py - Complete MCP Server


```
"""
Travel Booking Agent - Stateful MCP Server
Demonstrates all MCP features in a real-world travel booking workflow:
- Elicitation: Collect trip preferences interactively
- Progress: Show search progress for flights and hotels
- Sampling: AI-generated personalized recommendations
- Resources: Expose destination data and pricing
- Prompts: Templates for packing lists and local phrases
"""
import asyncio
import json
from fastmcp import FastMCP, Context
from enum import Enum

mcp = FastMCP("Travel-Booking-Agent")

# ============================================================
# DATA
# ============================================================

class TripType(str, Enum):
    BUSINESS = "business"
    LEISURE = "leisure"
    FAMILY = "family"

DESTINATIONS = {
    "paris": {"name": "Paris, France", "flight": 450, "hotel": 180,
              "highlights": ["Eiffel Tower", "Louvre", "Notre-Dame"],
              "phrases": ["Bonjour", "Merci", "S'il vous plait"]},
    "tokyo": {"name": "Tokyo, Japan", "flight": 900, "hotel": 150,
              "highlights": ["Shibuya", "Senso-ji Temple", "Mt. Fuji day trip"],
              "phrases": ["Konnichiwa", "Arigato", "Sumimasen"]},
    "new york": {"name": "New York, USA", "flight": 350, "hotel": 250,
                 "highlights": ["Central Park", "Broadway", "Statue of Liberty"],
                 "phrases": ["Hey!", "Thanks", "Excuse me"]},
    "bali": {"name": "Bali, Indonesia", "flight": 800, "hotel": 100,
             "highlights": ["Ubud Rice Terraces", "Tanah Lot", "Beach clubs"],
             "phrases": ["Selamat pagi", "Terima kasih", "Sama-sama"]}
}

# ============================================================
# RESOURCES - Expose data to MCP clients
# ============================================================

@mcp.resource("travel://destinations")
def list_destinations() -> str:
    """All available destinations with pricing."""
    return json.dumps({k: {"name": v["name"], "flight": v["flight"], "hotel": v["hotel"]}
                       for k, v in DESTINATIONS.items()}, indent=2)

@mcp.resource("travel://destination/{city}")
def get_destination(city: str) -> str:
    """Detailed info for a specific destination."""
    dest = DESTINATIONS.get(city.lower())
    return json.dumps(dest, indent=2) if dest else f"Unknown: {city}"

# ============================================================
# PROMPTS - Reusable templates for AI generation
# ============================================================

@mcp.prompt()
def packing_list(destination: str, days: int, trip_type: str) -> str:
    """Generate packing list prompt."""
    return f"Create a {days}-day packing list for a {trip_type} trip to {destination}. Be practical and concise."

@mcp.prompt()
def local_phrases(destination: str) -> str:
    """Generate local phrases prompt."""
    dest = DESTINATIONS.get(destination.lower(), {})
    phrases = dest.get("phrases", [])
    return f"Teach me essential phrases for {destination}. Start with: {', '.join(phrases)}"

# ============================================================
# MAIN TOOL - Complete booking with all MCP features
# ============================================================

@mcp.tool()
async def plan_trip(ctx: Context) -> str:
    """
    Plan a complete trip using all MCP features:
    1. Elicitation - Collect preferences
    2. Progress - Search flights and hotels
    3. Sampling - AI recommendations
    """

    # -------- PHASE 1: ELICITATION --------
    # Collect trip details through multi-turn conversation

    dest_result = await ctx.elicit(
        message="Where would you like to go?\nOptions: Paris, Tokyo, New York, Bali",
        response_type=str
    )
    if dest_result.action != "accept":
        return "Trip planning cancelled."

    dest_key = dest_result.data.lower().strip()
    dest = DESTINATIONS.get(dest_key, DESTINATIONS["paris"])

    type_result = await ctx.elicit(
        message="What type of trip?\n1. business\n2. leisure\n3. family",
        response_type=TripType
    )
    if type_result.action != "accept":
        return "Trip planning cancelled."
    trip_type = type_result.data

    days_result = await ctx.elicit(
        message="How many days? (3-14)",
        response_type=int
    )
    if days_result.action != "accept":
        return "Trip planning cancelled."
    days = max(3, min(14, days_result.data))

    travelers_result = await ctx.elicit(
        message="Number of travelers?",
        response_type=int
    )
    if travelers_result.action != "accept":
        return "Trip planning cancelled."
    travelers = travelers_result.data

    # -------- PHASE 2: PROGRESS NOTIFICATIONS --------
    # Search for flights and hotels with progress updates

    total_steps = 5

    await ctx.report_progress(progress=1, total=total_steps)  # Searching flights
    await asyncio.sleep(0.4)

    await ctx.report_progress(progress=2, total=total_steps)  # Comparing airlines
    await asyncio.sleep(0.4)

    await ctx.report_progress(progress=3, total=total_steps)  # Searching hotels
    await asyncio.sleep(0.4)

    await ctx.report_progress(progress=4, total=total_steps)  # Checking availability
    await asyncio.sleep(0.4)

    await ctx.report_progress(progress=5, total=total_steps)  # Finalizing
    await asyncio.sleep(0.2)

    # Calculate costs
    flight_cost = dest["flight"] * travelers
    hotel_cost = dest["hotel"] * days * ((travelers + 1) // 2)  # Rooms needed
    total_cost = flight_cost + hotel_cost

    # -------- PHASE 3: SAMPLING --------
    # Get AI-generated personalized recommendations

    ai_tips = f"Enjoy {dest['name']}!"
    try:
        response = await ctx.sample(
            messages=f"Give 3 brief tips for a {trip_type} trip to {dest['name']} for {travelers} travelers, {days} days. Max 60 words.",
            max_tokens=150
        )
        if hasattr(response, 'text') and response.text:
            ai_tips = response.text
    except Exception:
        ai_tips = f"Visit {dest['highlights'][0]}, try local food, learn basic phrases!"

    # -------- FINAL CONFIRMATION --------

    confirm = await ctx.elicit(
        message=f"""
========== TRIP SUMMARY ==========
Destination: {dest['name']}
Trip Type: {trip_type}
Duration: {days} days
Travelers: {travelers}

COSTS:
  Flights: ${flight_cost}
  Hotels: ${hotel_cost} ({(travelers + 1) // 2} room(s) x {days} nights)
  TOTAL: ${total_cost}

Confirm booking? (Yes/No)""",
        response_type=["Yes", "No"]
    )

    if confirm.action != "accept" or confirm.data == "No":
        return "Booking cancelled. Your search results are saved for 24 hours."

    # -------- FINAL RESULT --------

    highlights_str = '\n'.join(f'  * {h}' for h in dest['highlights'])
    phrases_str = '\n'.join(f'  * {p}' for p in dest['phrases'])

    return f"""
{'=' * 50}
  BOOKING CONFIRMED!
{'=' * 50}

Booking Reference: TRV-{ctx.session_id[:8].upper()}

TRIP DETAILS:
  {dest['name']}
  {days} days | {travelers} traveler(s)
  Trip type: {trip_type}

FLIGHTS: ${flight_cost}
  Outbound: Day 1, Morning departure
  Return: Day {days}, Evening departure

ACCOMMODATION: ${hotel_cost}
  {(travelers + 1) // 2} room(s) for {days} nights

TOTAL PAID: ${total_cost}

HIGHLIGHTS TO EXPLORE:
{highlights_str}

USEFUL PHRASES:
{phrases_str}

AI RECOMMENDATIONS:
{ai_tips}

{'=' * 50}
Thank you for booking with Travel Agent!
"""

if __name__ == "__main__":
    print("=" * 60)
    print("  Travel Booking Agent - Stateful MCP Server")
    print("=" * 60)
    print("\n MCP FEATURES DEMONSTRATED:")
    print("   * Elicitation - Multi-turn trip preference collection")
    print("   * Progress    - Real-time search progress updates")
    print("   * Sampling    - AI-powered travel recommendations")
    print("   * Resources   - Destination data and pricing")
    print("   * Prompts     - Packing list and phrase templates")
    print("\n TOOLS:")
    print("   plan_trip - Complete booking flow with all features")
    print("\n RESOURCES:")
    print("   travel://destinations - All destinations")
    print("   travel://destination/{city} - City details")
    print("\n PROMPTS:")
    print("   packing_list - Generate packing suggestions")
    print("   local_phrases - Learn useful phrases")
    print("\n" + "=" * 60)
    print(f" Server: http://0.0.0.0:8000/mcp")
    print("=" * 60)

    mcp.run(
        transport="streamable-http",
        host="0.0.0.0",
        port=8000,
        stateless_http=False
    )
```

## Test locally


 **To start the server** 
+ Run the MCP server:

  ```
  python travel_server.py
  ```

  You should see output indicating the server is running on port 8000.

Create a file called `test_client.py` with the following code. This client tests all MCP features including resources, prompts, and the main tool:

### test\$1client.py - Complete Test Client


```
"""
Travel Booking Agent - Test Client
Tests all MCP features: Elicitation, Sampling, Progress, Resources, Prompts
"""
import asyncio
import os
import sys
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
from fastmcp.client.elicitation import ElicitResult
from mcp.types import CreateMessageResult, TextContent

async def elicit_handler(message: str, response_type, params, ctx):
    """Handle elicitation - interactive input."""
    print(f"\n>>> Server asks: {message}")

    if isinstance(response_type, list):
        for i, opt in enumerate(response_type, 1):
            print(f"    {i}. {opt}")
        choice = input("    Your choice (number): ").strip()
        response = response_type[int(choice) - 1]
    else:
        hint = " (number)" if response_type == int else ""
        response = input(f"    Your answer{hint}: ").strip()
        if response_type == int:
            response = int(response)

    print(f"<<< Responding: {response}")
    return ElicitResult(action="accept", content={"value": response})

async def sampling_handler(messages, params, ctx):
    """Handle sampling - provide LLM response."""
    print(f"\n>>> AI Sampling Request")
    prompt = messages if isinstance(messages, str) else str(messages)
    print(f"    Prompt: {prompt[:80]}...")

    user_input = input("    Enter AI response (or Enter for auto): ").strip()
    if not user_input:
        user_input = "1. Book popular attractions early. 2. Try local street food. 3. Learn basic greetings!"

    print(f"<<< AI Response: {user_input}")
    return CreateMessageResult(
        role="assistant",
        content=TextContent(type="text", text=user_input),
        model="test-model",
        stopReason="endTurn"
    )

async def progress_handler(progress: float, total: float | None, message: str | None):
    """Handle progress notifications."""
    pct = int((progress / total) * 100) if total else 0
    bar = "#" * (pct // 5) + "-" * (20 - pct // 5)
    print(f"\r    Progress: [{bar}] {pct}% ({int(progress)}/{int(total or 0)})", end="", flush=True)
    if progress == total:
        print(" Done!")

async def main():
    local_test = os.getenv('LOCAL_TEST', 'true').lower() == 'true'

    if local_test:
        url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8000/mcp"
        token = None
    else:
        agent_arn = os.getenv('AGENT_ARN')
        if not agent_arn:
            print("ERROR: Missing AGENT_ARN environment variable")
            sys.exit(1)

        encoded_arn = agent_arn.replace(':', '%3A').replace('/', '%2F')
        endpoint = os.getenv('MCP_ENDPOINT', 'https://bedrock-agentcore.us-west-2.amazonaws.com')
        url = f"{endpoint}/runtimes/{encoded_arn}/invocations?qualifier=DEFAULT"
        token = os.getenv('BEARER_TOKEN')

        if not token:
            print("ERROR: Missing BEARER_TOKEN for remote testing")
            sys.exit(1)

        print(f"  Agent ARN: {agent_arn}")
        print(f"  Endpoint: {endpoint}")

    print("=" * 60)
    print("  Travel Agent - MCP Feature Test Client")
    print("=" * 60)

    headers = {}
    if token:
        headers["Authorization"] = f"Bearer {token}"
        print(f"  Using auth token (len={len(token)})")

    transport = StreamableHttpTransport(url=url, headers=headers)
    client = Client(
        transport,
        elicitation_handler=elicit_handler,
        sampling_handler=sampling_handler,
        progress_handler=progress_handler
    )

    try:
        await client.__aenter__()

        # Test Resources
        print("\n[1] Testing RESOURCES...")
        resources = await client.list_resources()
        print(f"    Found {len(resources)} resource(s)")

        # Test Prompts
        print("\n[2] Testing PROMPTS...")
        prompts = await client.list_prompts()
        print(f"    Found {len(prompts)} prompt(s)")

        # Test Main Tool (Elicitation + Progress + Sampling)
        print("\n[3] Testing PLAN_TRIP tool...")
        print("    (This tests Elicitation, Progress, and Sampling)\n")

        result = await client.call_tool("plan_trip", {})

        print("\n" + "=" * 60)
        print("RESULT:")
        print("=" * 60)
        print(result.content[0].text)

        print("=" * 60)
        print("  ALL TESTS COMPLETED!")
        print("=" * 60)

    except Exception as e:
        print(f"\nERROR: {e}")
        return False
    finally:
        await client.__aexit__(None, None, None)

    return True

if __name__ == "__main__":
    success = asyncio.run(main())
    sys.exit(0 if success else 1)
```

 **To run the local test** 

1. With the server running in one terminal, open a new terminal and run the test client:

   ```
   python test_client.py
   ```

1. The client tests resources and prompts, then runs the `plan_trip` tool which demonstrates elicitation, progress notifications, and sampling in a complete workflow.

## Deploy to Amazon Bedrock AgentCore


 **To configure and deploy** 

1. Install the AgentCore CLI if you haven’t already:

   ```
   npm install -g @aws/agentcore
   ```

1. Create a project for deployment:

   ```
   agentcore create --name TravelAgentDemo --protocol MCP
   ```

1. Deploy the agent:

   ```
   agentcore deploy
   ```

   After deployment completes, note the agent ARN provided in the output.

## Test your deployed agent


 **To test the deployed agent** 

1. Set the required environment variables:

   ```
   export AGENT_ARN='arn:aws:bedrock-agentcore:us-west-2:YOUR_ACCOUNT:runtime/YOUR_AGENT_NAME'
   export BEARER_TOKEN='your_bearer_token'
   ```

   Replace the placeholders with your actual agent ARN and bearer token.

1. Run the test client in remote mode:

   ```
   LOCAL_TEST=false python test_client.py
   ```

1. The client will test resources, prompts, and the `plan_trip` tool. Follow the interactive prompts to complete a booking, which demonstrates elicitation, progress notifications, and sampling on your deployed agent.

### Expected output


```
  Agent ARN: arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/TravelAgentDemo
  Endpoint: https://bedrock-agentcore.us-west-2.amazonaws.com
============================================================
  Travel Agent - MCP Feature Test Client
============================================================
  Using auth token (len=1034)

[1] Testing RESOURCES...
    Found 1 resource(s)

[2] Testing PROMPTS...
    Found 2 prompt(s)

[3] Testing PLAN_TRIP tool...
    (This tests Elicitation, Progress, and Sampling)

>>> Server asks: Where would you like to go?
Options: Paris, Tokyo, New York, Bali
    Your answer: Paris
<<< Responding: Paris

>>> Server asks: What type of trip?
1. business
2. leisure
3. family
    Your answer: leisure
<<< Responding: leisure

>>> Server asks: How many days? (3-14)
    Your answer (number): 5
<<< Responding: 5

>>> Server asks: Number of travelers?
    Your answer (number): 2
<<< Responding: 2

    Progress: [####################] 100% (5/5) Done!

>>> AI Sampling Request
    Prompt: Give 3 brief tips for a leisure trip to Paris, France for 2 travelers...
    Enter AI response (or Enter for auto):
<<< AI Response: 1. Book popular attractions early. 2. Try local street food. 3. Learn basic greetings!

>>> Server asks:
========== TRIP SUMMARY ==========
Destination: Paris, France
Trip Type: leisure
Duration: 5 days
Travelers: 2

COSTS:
  Flights: $900
  Hotels: $900 (1 room(s) x 5 nights)
  TOTAL: $1800

Confirm booking? (Yes/No)
    1. Yes
    2. No
    Your choice (number): 1
<<< Responding: Yes

============================================================
RESULT:
============================================================

==================================================
  BOOKING CONFIRMED!
==================================================

Booking Reference: TRV-A1B2C3D4

TRIP DETAILS:
  Paris, France
  5 days | 2 traveler(s)
  Trip type: leisure

FLIGHTS: $900
  Outbound: Day 1, Morning departure
  Return: Day 5, Evening departure

ACCOMMODATION: $900
  1 room(s) for 5 nights

TOTAL PAID: $1800

HIGHLIGHTS TO EXPLORE:
  * Eiffel Tower
  * Louvre
  * Notre-Dame

USEFUL PHRASES:
  * Bonjour
  * Merci
  * S'il vous plait

AI RECOMMENDATIONS:
1. Book popular attractions early. 2. Try local street food. 3. Learn basic greetings!

==================================================
Thank you for booking with Travel Agent!

============================================================
  ALL TESTS COMPLETED!
============================================================
```

**Tip**  
You can also test your MCP server using the MCP Inspector, a visual tool for testing MCP servers. For local testing instructions, see [Local testing with MCP inspector](runtime-mcp.md#runtime-mcp-appendix-b) . For remote testing instructions, see [Remote testing with MCP inspector](runtime-mcp.md#runtime-mcp-appendix-c).

# Deploy A2A servers in AgentCore Runtime
Deploy A2A servers

Amazon Bedrock AgentCore AgentCore Runtime lets you deploy and run Agent-to-Agent (A2A) servers in the AgentCore Runtime. This guide walks you through creating, testing, and deploying your first A2A server.

In this section, you learn:
+ How Amazon Bedrock AgentCore supports A2A
+ How to create an A2A server with agent capabilities
+ How to test your server locally
+ How to deploy your server to AWS 
+ How to invoke your deployed server
+ How to retrieve agent cards for discovery

For more information about A2A, see [A2A protocol contract](runtime-a2a-protocol-contract.md).

**Topics**
+ [

## How Amazon Bedrock AgentCore supports A2A
](#runtime-a2a-how-agentcore-supports)
+ [

## Using A2A with AgentCore Runtime
](#runtime-a2a-steps)
+ [

## Appendix
](#runtime-a2a-appendix)

## How Amazon Bedrock AgentCore supports A2A


Amazon Bedrock AgentCore’s A2A protocol support enables seamless integration with A2A servers by acting as a transparent proxy layer. When configured for A2A, Amazon Bedrock AgentCore expects containers to run stateless, streamable HTTP servers on port `9000` at the root path ( `0.0.0.0:9000/` ), which aligns with the default A2A server configuration.

The service provides enterprise-grade session isolation while maintaining protocol transparency - JSON-RPC payloads from the [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) API are passed through directly to the A2A container without modification. This architecture preserves the standard A2A protocol features like built-in agent discovery through Agent Cards at `/.well-known/agent-card.json` and JSON-RPC communication, while adding enterprise authentication (SigV4/OAuth 2.0) and scalability.

The key differentiators from other protocols are the port (9000 vs 8080 for HTTP), mount path ( `/` vs `/invocations` ), and the standardized agent discovery mechanism, making Amazon Bedrock AgentCore an ideal deployment platform for A2A agents in production environments.

Key differences from other protocols:

 **Port**   
A2A servers run on port 9000 (vs 8080 for HTTP, 8000 for MCP)

 **Path**   
A2A servers are mounted at `/` (vs `/invocations` for HTTP, `/mcp` for MCP)

 **Agent Cards**   
A2A provides built-in agent discovery through Agent Cards at `/.well-known/agent-card.json` 

 **Protocol**   
Uses JSON-RPC for agent-to-agent communication

 **Authentication**   
Supports both SigV4 and OAuth 2.0 authentication schemes

For more information, see [https://a2a-protocol.org/](https://a2a-protocol.org/).

## Using A2A with AgentCore Runtime


In this tutorial you create, test, and deploy an A2A server.

**Topics**
+ [

### Prerequisites
](#runtime-a2a-prerequisites)
+ [

### Step 1: Create your A2A project
](#runtime-a2a-create-server)
+ [

### Step 2: Test your A2A server locally
](#runtime-a2a-test-locally)
+ [

### Step 3: Deploy your A2A server to Bedrock AgentCore Runtime
](#runtime-a2a-deploy)
+ [

### Step 4: Get the agent card
](#runtime-a2a-step-4)
+ [

### Step 5: Invoke your deployed A2A server
](#runtime-a2a-step-5)

### Prerequisites

+ Python 3.10 or higher installed and basic understanding of Python
+ Node.js 18 or higher installed (required for the AgentCore CLI)
+ The AgentCore CLI installed: `npm install -g @aws/agentcore` 
+ An AWS account with appropriate permissions and local credentials configured
+ Understanding of the A2A protocol and agent-to-agent communication concepts

### Step 1: Create your A2A project


This example uses Strands Agents, but the AgentCore CLI also supports A2A projects with LangChain/LangGraph and Google ADK.

#### Scaffold the project


Run the following command and select *Strands* as your framework when prompted:

```
agentcore create --protocol A2A
```

The CLI scaffolds a complete project with all required dependencies and configuration. The generated `main.py` contains your A2A server:

```
from strands import Agent, tool
from strands.multiagent.a2a.executor import StrandsA2AExecutor
from bedrock_agentcore.runtime import serve_a2a
from model.load import load_model

@tool
def add_numbers(a: int, b: int) -> int:
    """Return the sum of two numbers."""
    return a + b

tools = [add_numbers]

agent = Agent(
    model=load_model(),
    system_prompt="You are a helpful assistant. Use tools when appropriate.",
    tools=tools,
)

if __name__ == "__main__":
    serve_a2a(StrandsA2AExecutor(agent))
```

#### Understanding the code


 **Strands Agent**   
Creates an agent with specific tools and capabilities

 **StrandsA2AExecutor**   
Wraps the Strands agent to provide A2A protocol compatibility

 **serve\$1a2a**   
The Amazon Bedrock AgentCore SDK helper that starts a Bedrock-compatible A2A server. It handles the `/ping` health endpoint, Agent Card serving, `AGENTCORE_RUNTIME_URL` environment variable, Bedrock header propagation, and runs on port 9000 by default.

 **Port 9000**   
A2A servers run on port 9000 by default in AgentCore Runtime

To customize this agent, replace the `add_numbers` tool with your own tools and update the system prompt.

### Step 2: Test your A2A server locally


Run and test your A2A server in a local development environment.

#### Start your A2A server


Start your A2A server locally using the AgentCore CLI:

```
agentcore dev
```

Alternatively, you can run the server directly:

```
python main.py
```

You should see output indicating the server is running on port `9000`.

#### Invoke agent


```
curl -X POST http://localhost:9000/ \
-H "Content-Type: application/json" \
-d '{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "message/send",
  "params": {
    "message": {
      "role": "user",
      "parts": [
        {
          "kind": "text",
          "text": "what is 101 * 11?"
        }
      ],
      "messageId": "12345678-1234-1234-1234-123456789012"
    }
  }
}' | jq .
```

#### Test agent card retrieval


You can test the agent card endpoint locally:

```
curl http://localhost:9000/.well-known/agent-card.json | jq.
```

You can also test your deployed server using the A2A Inspector as described in [Remote testing with A2A inspector](https://github.com/a2aproject/a2a-inspector).

### Step 3: Deploy your A2A server to Bedrock AgentCore Runtime


#### Set up Cognito user pool for authentication


Before deploying, configure authentication for secure access to your deployed server. For detailed Cognito setup instructions, see [Set up Cognito user pool for authentication](runtime-mcp.md#runtime-mcp-appendix-a) . This provides the OAuth tokens required for secure access to your deployed server.

#### Deploy to AWS


Deploy your agent:

```
agentcore deploy
```

This command will:

1. Package your agent code and dependencies

1. Upload the deployment artifact to Amazon S3

1. Create a Amazon Bedrock AgentCore runtime

1. Deploy your agent to AWS 

After deployment, you’ll receive an agent runtime ARN that looks like:

```
arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/my_a2a_server-xyz123
```

### Step 4: Get the agent card


Agent Cards are JSON metadata documents that describe an A2A server’s identity, capabilities, skills, service endpoint, and authentication requirements. They enable automatic agent discovery in the A2A ecosystem.

#### Set up environment variables


Set up environment variables

1. Export bearer token as an environment variable. For bearer token setup, see [Bearer token setup](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-mcp.html#runtime-mcp-appendix).

   ```
   export BEARER_TOKEN="<BEARER_TOKEN>"
   ```

1. Export the agent ARN.

   ```
   export AGENT_ARN="arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/my_a2a_server-xyz123"
   ```

#### Retrieve agent card


```
import os
import json
import requests
from uuid import uuid4
from urllib.parse import quote

def fetch_agent_card():
    # Get environment variables
    agent_arn = os.environ.get('AGENT_ARN')
    bearer_token = os.environ.get('BEARER_TOKEN')

    if not agent_arn:
        print("Error: AGENT_ARN environment variable not set")
        return

    if not bearer_token:
        print("Error: BEARER_TOKEN environment variable not set")
        return

    # URL encode the agent ARN
    escaped_agent_arn = quote(agent_arn, safe='')

    # Construct the URL
    url = f"https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/{escaped_agent_arn}/invocations/.well-known/agent-card.json"

    # Generate a unique session ID
    session_id = str(uuid4())
    print(f"Generated session ID: {session_id}")

    # Set headers
    headers = {
        'Accept': '*/*',
        'Authorization': f'Bearer {bearer_token}',
        'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id': session_id
    }

    try:
        # Make the request
        response = requests.get(url, headers=headers)
        response.raise_for_status()

        # Parse and pretty print JSON
        agent_card = response.json()
        print(json.dumps(agent_card, indent=2))

        return agent_card

    except requests.exceptions.RequestException as e:
        print(f"Error fetching agent card: {e}")
        return None

if __name__ == "__main__":
    fetch_agent_card()
```

After you get the URL from the Agent Card, export `AGENTCORE_RUNTIME_URL` as an environment variable:

```
export AGENTCORE_RUNTIME_URL="https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/<ARN>/invocations/"
```

### Step 5: Invoke your deployed A2A server


Create client code to invoke your deployed Amazon Bedrock AgentCore A2A server and send messages to test the functionality.

Create a new file `my_a2a_client_remote.py` to invoke your deployed A2A server:

```
import asyncio
import logging
import os
from uuid import uuid4

import httpx
from a2a.client import A2ACardResolver, ClientConfig, ClientFactory
from a2a.types import Message, Part, Role, TextPart

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

DEFAULT_TIMEOUT = 300  # set request timeout to 5 minutes

def create_message(*, role: Role = Role.user, text: str) -> Message:
    return Message(
        kind="message",
        role=role,
        parts=[Part(TextPart(kind="text", text=text))],
        message_id=uuid4().hex,
    )

async def send_sync_message(message: str):
    # Get runtime URL from environment variable
    runtime_url = os.environ.get('AGENTCORE_RUNTIME_URL')

    # Generate a unique session ID
    session_id = str(uuid4())
    print(f"Generated session ID: {session_id}")

    # Add authentication headers for Amazon Bedrock AgentCore
    headers = {"Authorization": f"Bearer {os.environ.get('BEARER_TOKEN')}",
        'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id': session_id}

    async with httpx.AsyncClient(timeout=DEFAULT_TIMEOUT, headers=headers) as httpx_client:
        # Get agent card from the runtime URL
        resolver = A2ACardResolver(httpx_client=httpx_client, base_url=runtime_url)
        agent_card = await resolver.get_agent_card()

        # Agent card contains the correct URL (same as runtime_url in this case)
        # No manual override needed - this is the path-based mounting pattern

        # Create client using factory
        config = ClientConfig(
            httpx_client=httpx_client,
            streaming=False,  # Use non-streaming mode for sync response
        )
        factory = ClientFactory(config)
        client = factory.create(agent_card)

        # Create and send message
        msg = create_message(text=message)

        # With streaming=False, this will yield exactly one result
        async for event in client.send_message(msg):
            if isinstance(event, Message):
                logger.info(event.model_dump_json(exclude_none=True, indent=2))
                return event
            elif isinstance(event, tuple) and len(event) == 2:
                # (Task, UpdateEvent) tuple
                task, update_event = event
                logger.info(f"Task: {task.model_dump_json(exclude_none=True, indent=2)}")
                if update_event:
                    logger.info(f"Update: {update_event.model_dump_json(exclude_none=True, indent=2)}")
                return task
            else:
                # Fallback for other response types
                logger.info(f"Response: {str(event)}")
                return event

# Usage - Uses AGENTCORE_RUNTIME_URL environment variable
asyncio.run(send_sync_message("what is 101 * 11"))
```

## Appendix


**Topics**
+ [

### Set up Cognito user pool for authentication
](#runtime-a2a-setup-cognito-appendix)
+ [

### Remote testing with A2A inspector
](#runtime-a2a-remote-testing)
+ [

### Troubleshooting
](#runtime-a2a-troubleshooting)

### Set up Cognito user pool for authentication


For detailed Cognito setup instructions, see Set up [Cognito user pool for authentication](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-mcp.html#set-up-cognito-user-pool-for-authentication) in the MCP documentation.

### Remote testing with A2A inspector


See [https://github.com/a2aproject/a2a-inspector](https://github.com/a2aproject/a2a-inspector).

### Troubleshooting


 **Common A2A-specific issues** 

The following are common issues you might encounter:

Port conflicts  
A2A servers must run on port 9000 in the AgentCore Runtime environment

JSON-RPC errors  
Check that your client is sending properly formatted JSON-RPC 2.0 messages

Authorization method mismatch  
Make sure your request uses the same authentication method (OAuth or SigV4) that the agent was configured with

 **Exception handling** 

A2A specifications for Error handling: [https://a2a-protocol.org/latest/specification/#81-standard-json-rpc-errors](https://a2a-protocol.org/latest/specification/#81-standard-json-rpc-errors) 

A2A servers return errors as standard JSON-RPC error responses with HTTP 200 status codes. Internal Runtime errors are automatically translated to JSON-RPC internal errors to maintain protocol compliance.

The service now provides proper A2A-compliant error responses with standardized JSON-RPC error codes:


| JSON-RPC Error Code | Runtime Exception | HTTP Error Code | JSON-RPC Error Message | 
| --- | --- | --- | --- | 
|  N/A  |   `AccessDeniedException`   |  403  |  N/A  | 
|  -32501  |   `ResourceNotFoundException`   |  404  |  Resource not found – Requested resource does not exist  | 
|  -32502  |   `ValidationException`   |  400  |  Validation error – Invalid request data  | 
|  -32503  |   `ThrottlingException`   |  429  |  Rate limit exceeded – Too many requests  | 
|  -32503  |   `ServiceQuotaExceededException`   |  429  |  Rate limit exceeded – Too many requests  | 
|  -32504  |   `ResourceConflictException`   |  409  |  Resource conflict – Resource already exists  | 
|  -32505  |   `RuntimeClientError`   |  424  |  Runtime client error – Check your CloudWatch logs for more information.  | 

# Deploy AGUI servers in AgentCore Runtime
Deploy AGUI servers

Amazon Bedrock AgentCore AgentCore Runtime lets you deploy and run Agent User Interface (AGUI) servers in the AgentCore Runtime. This guide walks you through creating, testing, and deploying your first AGUI server.

In this section, you learn:
+ How Amazon Bedrock AgentCore supports AGUI
+ How to create an AGUI server
+ How to test your server locally
+ How to deploy your server to AWS 
+ How to invoke your deployed server

For more information about AGUI, see [AGUI protocol contract](runtime-agui-protocol-contract.md).

**Topics**
+ [

## How Amazon Bedrock AgentCore supports AGUI
](#runtime-agui-how-agentcore-supports)
+ [

## Using AGUI with AgentCore Runtime
](#runtime-agui-steps)
+ [

## Appendix
](#runtime-agui-appendix)

## How Amazon Bedrock AgentCore supports AGUI


Amazon Bedrock AgentCore’s AGUI protocol support enables integration with agent user interface servers by acting as a proxy layer. When configured for AGUI, Amazon Bedrock AgentCore expects containers to run servers on port `8080` at the `/invocations` path for HTTP/SSE or `/ws` for WebSocket connections. Although AGUI uses the same port and paths as the HTTP protocol, the runtime distinguishes between them based on the `--protocol` flag specified during deployment configuration.

Amazon Bedrock AgentCore acts as a proxy between clients and your AGUI container. Requests from the [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) API are passed through to your container without modification. Amazon Bedrock AgentCore handles authentication (SigV4/OAuth 2.0), session isolation, and scaling.

Key differences from other protocols:

 **Port**   
AGUI servers run on port 8080 (same as HTTP, vs 8000 for MCP, 9000 for A2A)

 **Path**   
AGUI servers use `/invocations` for HTTP/SSE and `/ws` for WebSocket (same as HTTP protocol)

 **Message Format**   
Uses event streams via Server-Sent Events (SSE) for streaming, or WebSocket for bidirectional communication

 **Protocol Focus**   
Agent-to-User interaction (vs MCP for tools, A2A for agent-to-agent)

 **Authentication**   
Supports both SigV4 and OAuth 2.0 authentication schemes

For more information, see [https://docs.ag-ui.com/introduction](https://docs.ag-ui.com/introduction).

## Using AGUI with AgentCore Runtime


In this tutorial you create, test, and deploy an AGUI server.

For complete examples and framework-specific implementations, see [AGUI Quickstart Documentation](https://docs.ag-ui.com/quickstart/introduction) and [AGUI Dojo](https://dojo.ag-ui.com/).

**Topics**
+ [

### Prerequisites
](#runtime-agui-prerequisites)
+ [

### Step 1: Create your AGUI server
](#runtime-agui-create-server)
+ [

### Step 2: Test your AGUI server locally
](#runtime-agui-test-locally)
+ [

### Step 3: Deploy your AGUI server to Bedrock AgentCore Runtime
](#runtime-agui-deploy)
+ [

### Step 4: Invoke your deployed AGUI server
](#runtime-agui-step-4)

### Prerequisites

+ Python 3.12 or higher (or TypeScript/Node.js) installed and basic understanding of your chosen language
+ An AWS account with appropriate permissions and local credentials configured
+ Understanding of the AGUI protocol and event-based agent-to-user communication concepts

### Step 1: Create your AGUI server


AGUI is supported by multiple agent frameworks. Choose the framework that best fits your needs. For this example, we’ll show the general pattern.

#### Install required packages


Install packages for AWS Strands with AGUI support:

```
pip install fastapi
pip install uvicorn
pip install ag-ui-strands
```

For other frameworks, see the [AGUI framework integrations](https://docs.ag-ui.com/introduction#supported-integrations).

#### Create your first AGUI server


Create a new file called `my_agui_server.py` . This example uses AWS Strands with AGUI:

```
# my_agui_server.py
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse, JSONResponse
from ag_ui_strands import StrandsAgent
from ag_ui.core import RunAgentInput
from ag_ui.encoder import EventEncoder
from strands import Agent
from strands.models.bedrock import BedrockModel

# Create a simple Strands agent
model = BedrockModel(
    model_id="us.anthropic.claude-3-5-sonnet-20241022-v2:0",
    region_name="us-west-2",
)

strands_agent = Agent(
    model=model,
    system_prompt="You are a helpful assistant.",
)

# Wrap with AGUI protocol support
agui_agent = StrandsAgent(
    agent=strands_agent,
    name="my_agent",
    description="A helpful assistant",
)

# FastAPI server
app = FastAPI()

@app.post("/invocations")
async def invocations(input_data: dict, request: Request):
    """Main AGUI endpoint that returns event streams."""
    accept_header = request.headers.get("accept")
    encoder = EventEncoder(accept=accept_header)

    async def event_generator():
        run_input = RunAgentInput(**input_data)
        async for event in agui_agent.run(run_input):
            yield encoder.encode(event)

    return StreamingResponse(
        event_generator(),
        media_type=encoder.get_content_type()
    )

@app.get("/ping")
async def ping():
    return JSONResponse({"status": "Healthy"})

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8080)
```

For complete, framework-specific examples, see:
+  [LangGraph \$1 AGUI](https://docs.copilotkit.ai/langgraph/) 
+  [CrewAI \$1 AGUI](https://docs.copilotkit.ai/crewai-flows) 
+  [AWS Strands \$1 AGUI](https://docs.copilotkit.ai/aws-strands) 

#### Understanding the code


 **Event Streams**   
AGUI uses Server-Sent Events (SSE) to stream typed events to the client

 **/invocations Endpoint**   
Primary endpoint for HTTP/SSE communication (same as HTTP protocol)

 **Port 8080**   
AGUI servers run on port 8080 by default in AgentCore Runtime

### Step 2: Test your AGUI server locally


Run and test your AGUI server in a local development environment.

#### Start your AGUI server


Run your AGUI server locally:

```
python my_agui_server.py
```

You should see output indicating the server is running on port `8080`.

#### Test the endpoint


Test the SSE endpoint with a properly formatted AGUI request:

```
curl -N -X POST http://localhost:8080/invocations \
-H "Content-Type: application/json" \
-d '{
  "threadId": "test-123",
  "runId": "run-456",
  "state": {},
  "messages": [{"role": "user", "content": "Hello, agent!", "id": "msg-1"}],
  "tools": [],
  "context": [],
  "forwardedProps": {}
}'
```

You should see AGUI event streams returned in SSE format, including `RUN_STARTED` , `TEXT_MESSAGE_CONTENT` , and `RUN_FINISHED` events.

### Step 3: Deploy your AGUI server to Bedrock AgentCore Runtime


Deploy your AGUI server to AWS using the Amazon Bedrock AgentCore starter toolkit.

#### Install deployment tools


Install the Amazon Bedrock AgentCore starter toolkit:

```
pip install bedrock-agentcore-starter-toolkit
```

Start by creating a project folder with the following structure:

```
## Project Folder Structure
your_project_directory/
├── my_agui_server.py          # Your main agent code
├── requirements.txt           # Dependencies for your agent
```

Create a new file called `requirements.txt` with your dependencies:

```
fastapi
uvicorn
ag-ui-strands
```

#### Set up Cognito user pool for authentication


Configure authentication for secure access to your deployed server. For detailed Cognito setup instructions, see [Set up Cognito user pool for authentication](#runtime-agui-appendix-a) . This provides the OAuth tokens required for secure access to your deployed server.

#### Configure your AGUI server for deployment


After setting up authentication, create the deployment configuration:

```
agentcore configure -e my_agui_server.py --protocol AGUI
```
+ Select protocol as AGUI
+ Configure with OAuth configuration as setup in the previous step

#### Deploy to AWS


Deploy your agent:

```
agentcore deploy
```

After deployment, you’ll receive an agent runtime ARN that looks like:

```
arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/my_agui_server-xyz123
```

### Step 4: Invoke your deployed AGUI server


Invoke your deployed Amazon Bedrock AgentCore AGUI server and interact with the event streams.

#### Set up environment variables


Set up environment variables

1. Export bearer token as an environment variable. For bearer token setup, see [Set up Cognito user pool for authentication](#runtime-agui-appendix-a).

   ```
   export BEARER_TOKEN="<BEARER_TOKEN>"
   ```

1. Export the agent ARN.

   ```
   export AGENT_ARN="arn:aws:bedrock-agentcore:us-west-2:accountId:runtime/my_agui_server-xyz123"
   ```

#### Invoke the AGUI server


To invoke the AGUI server programmatically with Python, install the required packages:

```
pip install httpx httpx-sse
```

Then use the following client code:

```
import asyncio
import json
import os
from urllib.parse import quote
from uuid import uuid4

import httpx
from httpx_sse import aconnect_sse

async def invoke_agui_agent(message: str):
    agent_arn = os.environ.get('AGENT_ARN')
    bearer_token = os.environ.get('BEARER_TOKEN')
    escaped_arn = quote(agent_arn, safe='')

    url = f"https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/{escaped_arn}/invocations?qualifier=DEFAULT"
    headers = {
        "Authorization": f"Bearer {bearer_token}",
        "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": str(uuid4()),
    }
    payload = {
        "threadId": str(uuid4()),
        "runId": str(uuid4()),
        "messages": [{"id": str(uuid4()), "role": "user", "content": message}],
        "state": {},
        "tools": [],
        "context": [],
        "forwardedProps": {},
    }

    async with httpx.AsyncClient(timeout=300) as client:
        async with aconnect_sse(client, "POST", url, headers=headers, json=payload) as sse:
            async for event in sse.aiter_sse():
                data = json.loads(event.data)
                event_type = data.get("type")
                if event_type == "TEXT_MESSAGE_CONTENT":
                    print(data.get("delta", ""), end="", flush=True)
                elif event_type == "RUN_ERROR":
                    print(f"Error: {data.get('code')} - {data.get('message')}")

asyncio.run(invoke_agui_agent("Hello!"))
```

For building full UI applications, see [CopilotKit](https://docs.copilotkit.ai/) or the [AGUI TypeScript client SDK](https://docs.ag-ui.com/sdk/js/client/overview).

## Appendix


**Topics**
+ [

### Set up Cognito user pool for authentication
](#runtime-agui-appendix-a)
+ [

### Troubleshooting
](#runtime-agui-troubleshooting)

### Set up Cognito user pool for authentication


For detailed Cognito setup instructions, see Set up [Cognito user pool for authentication](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-mcp.html#set-up-cognito-user-pool-for-authentication) in the MCP documentation. The setup process is identical for AGUI servers.

### Troubleshooting


 **Common AGUI-specific issues** 

The following are common issues you might encounter:

Port conflicts  
AGUI servers must run on port 8080 in the AgentCore Runtime environment

Authorization method mismatch  
Make sure your request uses the same authentication method (OAuth or SigV4) that the agent was configured with

Event format errors  
Ensure your events follow the AGUI protocol specification. See [AGUI Events Documentation](https://docs.ag-ui.com/concepts/events) 

# Use isolated sessions for agents
Use isolated sessions for agents

Amazon Bedrock AgentCore Runtime lets you isolate each user session and safely reuse context across multiple invocations in a user session. Session isolation is critical for AI agent workloads due to their unique operational characteristics:
+  **Complete execution environment separation** : Each user session in AgentCore Runtime receives its own dedicated microVM with isolated Compute, memory, and filesystem resources. This prevents one user’s agent from accessing another user’s data. After session completion, the entire microVM is terminated and memory is sanitized to remove all session data, eliminating cross-session contamination risks.
+  **Stateful reasoning processes** : Unlike stateless functions, AI agents maintain complex contextual state throughout their execution cycle, beyond simple message history for multi-turn conversations. AgentCore Runtime preserves this state securely within a session while ensuring complete isolation between different users, enabling personalized agent experiences without compromising data boundaries.
+  **Privileged tool operations** : AI agents perform privileged operations on users' behalf through integrated tools accessing various resources. AgentCore Runtime’s isolation model ensures these tool operations maintain proper security contexts and prevents credential sharing or permission escalation between different user sessions.
+  **Deterministic security for non-deterministic processes** : AI agent behavior can be non-deterministic due to the probabilistic nature of foundation models. AgentCore Runtime provides consistent, deterministic isolation boundaries regardless of agent execution patterns, delivering the predictable security properties required for enterprise deployments.

**Note**  
AgentCore does not enforce session-to-user mappings - your client backend should maintain the relationship between users and their session IDs. Additionally, your client backend should implement logic for user to session lifecycle management like maximum number of sessions per user.

**Topics**
+ [

## Understanding ephemeral context
](#ephemeral-context)
+ [

## Extended conversations and multi-step workflows
](#extended-conversations)
+ [

## AgentCore Runtime session lifecycle
](#session-lifecycle)
+ [

## How to use sessions
](#using-sessions)
+ [

## Session headers by protocol
](#session-headers-by-protocol)
+ [

# Configure Amazon Bedrock AgentCore lifecycle settings
](runtime-lifecycle-settings.md)
+ [

# Stop a running session
](runtime-stop-session.md)

## Understanding ephemeral context


By default, the compute (microVM) associated with a session is ephemeral. Any data stored in memory or written to disk persists only for the compute lifecycle. This includes conversation history, user preferences, intermediate calculation results, and any other state information your agent maintains.

To persist filesystem data across session stop/resume cycles, configure **session storage** — a persistent directory that survives compute termination. See [Persist session state across stop/resume with a filesystem configuration (Preview)](runtime-persistent-filesystems.md).

For structured data that needs to be retained beyond the session lifetime (such as user conversation history, learned preferences, or important insights), use AgentCore Memory. This service provides purpose-built persistent storage designed specifically for agent workloads, with both short-term and long-term memory capabilities.

## Extended conversations and multi-step workflows


Unlike traditional serverless functions that terminate after each request, AgentCore supports isolated sessions backed by ephemeral computes lasting up to 8 hours per lifecycle. This simplifies building multi-step agentic workflows as you can make multiple calls to the same environment, with each invocation building upon the context established by previous interactions. You can use both `InvokeAgentRuntime` for agent reasoning and `InvokeAgentRuntimeCommand` for deterministic shell command execution within the same session.

## AgentCore Runtime session lifecycle


 **Session creation** 

A new session is created on the first invoke with a unique runtimeSessionId provided by your application. AgentCore Runtime provisions a dedicated execution environment (microVM) for each session. Context is preserved between invocations to the same session. Both `InvokeAgentRuntime` and `InvokeAgentRuntimeCommand` operate on the same session — a command sees the same container, filesystem, and environment as the agent.

 **Session states** 

Session state is determined by the compute lifecycle and can be one of the following:
+  **Active** : Either processing a sync request, executing a command, or doing background tasks. Sync invocation and command execution activity is automatically tracked based on invocations to a runtime session. Background tasks are communicated by the agent code by responding with "HealthyBusy" status in pings.
+  **Idle** : When not processing any requests or background tasks. The session has completed processing but remains available for future invocations.
+  **Stopped** : The compute (microVM) provisioned for the session has been terminated and the session is stopped. This can occur due to inactivity (default 15 minutes), reaching max compute lifetime (default 8 hours), an explicit stop by invoking the [StopRuntimeSession](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_StopRuntimeSession.html) API, or if the compute is deemed unhealthy based on health checks. The session transitions back to Active on the next invocation and a new compute is provisioned, with the same lifecycle configuration (i.e. idleRuntimeSessionTimeout and maxLifetime that can be up to another 8 hours). The session itself remains valid until the AgentCore Runtime ARN is deleted. If the runtime is configured with session storage, filesystem data at the configured mount path persists across stop/resume cycles. See [Persist session state across stop/resume with a filesystem configuration (Preview)](runtime-persistent-filesystems.md).

## How to use sessions


To use sessions effectively:
+ Generate a unique session ID for each user or conversation with at least 33 characters
+ Pass the same session ID for all related invocations
+ Use different session IDs for different users or conversations

 **Example Using sessions for a conversation** 

```
# First message in a conversation

response1 = agent_core_client.InvokeAgentRuntime(
   agentRuntimeArn=agent_arn,
   runtimeSessionId="user-123456-conversation-12345678", # or uuid.uuid4()
   payload=json.dumps({"prompt": "Tell me about AWS"}).encode()
)

# Follow-up message in the same conversation reuses the runtimeSessionId.

response2 = agent_core_client.InvokeAgentRuntime(
   agentRuntimeArn=agent_arn,
   runtimeSessionId="user-123456-conversation-12345678", # or uuid.uuid4()
   payload=json.dumps({"prompt": "How does it compare to other cloud providers"}).encode()
)
```

By using the same runtimeSessionId for related invocations, you ensure that context is maintained across the conversation, allowing your agent to provide coherent responses that build on previous interactions.

## Session headers by protocol


When invoking agents, include the appropriate session header to ensure requests are routed to the same microVM. The header depends on your agent’s configured protocol:


| Protocol | Session Header | 
| --- | --- | 
|  MCP  |   `Mcp-Session-Id`   | 
|  HTTP  |   `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`   | 
|  A2A  |   `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`   | 
|  AGUI  |   `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id`   | 

 **MicroVM stickiness** : Amazon Bedrock AgentCore uses the session header to route requests to the same microVM instance. Clients must capture the session ID returned in the response and include it in all subsequent requests to ensure session affinity. Without a consistent session ID, each request may be routed to a new microVM, which may result in additional latency due to cold starts.

For MCP protocol specifics including stateless and stateful modes, see [MCP session management and microVM stickiness](runtime-mcp-protocol-contract.md#mcp-session-management).

# Configure Amazon Bedrock AgentCore lifecycle settings
Configure lifecycle settings

The `LifecycleConfiguration` input parameter to [CreateAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateAgentRuntime.html) lets you manage the lifecycle of runtime sessions and resources in Amazon Bedrock AgentCore Runtime. This configuration helps optimize resource utilization by automatically cleaning up idle sessions and preventing long-running instances from consuming resources indefinitely.

You can also configure lifecycle settings for an existing AgentCore Runtime with the [UpdateAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_UpdateAgentRuntime.html) operation.

**Topics**
+ [

## Configuration attributes
](#configuration-attributes)
+ [

## Default behavior
](#default-behavior)
+ [

## Create an AgentCore Runtime with lifecycle configuration
](#create-agent-runtime-with-lifecycle)
+ [

## Update the lifecycle configuration for an AgentCore Runtime
](#update-lifecycle-configuration)
+ [

## Get the lifecycle configuration for an AgentCore Runtime
](#update-lifecycle-configuration)
+ [

## Validation and constraints
](#validation-and-constraints)
+ [

## Lifecycle settings and runtime sessions
](#lifecycle-and-session-relationship)
+ [

## Best practices
](#best-practices)

## Configuration attributes



| Attribute | Type | Range (seconds) | Required | Description | 
| --- | --- | --- | --- | --- | 
|   `idleRuntimeSessionTimeout`   |  Integer  |  60-28800  |  No  |  Timeout in seconds for idle runtime sessions. When a session remains idle for this duration, it will trigger termination. Termination can last up to 15 seconds due to logging and other process completion. Default: 900 seconds (15 minutes)  | 
|   `maxLifetime`   |  Integer  |  60-28800  |  No  |  Maximum lifetime for the instance in seconds. Once reached, instances will initialize termination. Termination can last up to 15 seconds due to logging and other process completion. Default: 28800 seconds (8 hours). The session itself can persist beyond this with a new instance provisioned.  | 

### Constraints

+  `idleRuntimeSessionTimeout` must be less than or equal to `maxLifetime` 
+ Both values are measured in seconds
+ Valid range: 60 to 28800 seconds (up to 8 hours)

## Default behavior


When `LifecycleConfiguration` is not provided or contains null values, the platform applies the following logic:


| Customer Input | idleRuntimeSessionTimeout | maxLifetime | Result | 
| --- | --- | --- | --- | 
|   **No configuration**   |  900 sec  |  28800s  |  Uses defaults: 900s and 28800s  | 
|   **Only maxLifetime provided**   |  900 sec  |  Customer value  |  If maxLifetime ≤ 900s: uses maxLifetime for both If maxLifetime > 900s: uses 900s for idle, customer value for max  | 
|   **Only idleTimeout provided**   |  Customer value  |  28800s  |  Uses customer value for idle, 28800s for max  | 
|   **Both values provided**   |  Customer value  |  Customer value  |  Uses customer values as-is  | 

### Default values

+  `idleRuntimeSessionTimeout` : **900 seconds** (15 minutes)
+  `maxLifetime` : **28800 seconds** (8 hours)

## Create an AgentCore Runtime with lifecycle configuration


You can specify a lifecycle configuration when you create an AgentCore Runtime.

```
import boto3

client = boto3.client('bedrock-agentcore-control', region_name='us-west-2')

try:
    response = client.create_agent_runtime(
        agentRuntimeName='my_agent_runtime',
        agentRuntimeArtifact={
            'containerConfiguration': {
                'containerUri': '123456789012.dkr.ecr.us-west-2.amazonaws.com/my-agent:latest'
            }
        },
        lifecycleConfiguration={
            'idleRuntimeSessionTimeout': 1800,  # 30 minutes, configurable
            'maxLifetime': 14400  # 4 hours
        },
        networkConfiguration={'networkMode': 'PUBLIC'},
        roleArn='arn:aws:iam::123456789012:role/AgentRuntimeRole'
    )

    print(f"Agent runtime created: {response['agentRuntimeArn']}")
except client.exceptions.ValidationException as e:
    print(f"Validation error: {e}")
except Exception as e:
    print(f"Error creating agent runtime: {e}")
```

## Update the lifecycle configuration for an AgentCore Runtime


You can update lifecycle configuration for an existing AgentCore Runtime.

```
import boto3

client = boto3.client('bedrock-agentcore-control', region_name='us-west-2')

agent_runtime_id = 'my_agent_runtime'

try:
    response = client.update_agent_runtime(
        agentRuntimeId=agent_runtime_id,
        agentRuntimeArtifact={
            'containerConfiguration': {
                'containerUri': '123456789012.dkr.ecr.us-west-2.amazonaws.com/my-agent:latest'
            }
        },
        networkConfiguration={'networkMode': 'PUBLIC'},
        roleArn='arn:aws:iam::123456789012:role/AgentRuntimeRole',
        lifecycleConfiguration={
            'idleRuntimeSessionTimeout': 600,   # 10 minutes
            'maxLifetime': 7200                 # 2 hours
        }
    )

    print("Lifecycle configuration updated successfully")
except client.exceptions.ValidationException as e:
    print(f"Validation error: {e}")
except client.exceptions.ResourceNotFoundException:
    print("Agent runtime not found")
except Exception as e:
    print(f"Error updating configuration: {e}")
```

## Get the lifecycle configuration for an AgentCore Runtime


You can get lifecycle configuration for an existing AgentCore Runtime.

```
import boto3

client = boto3.client('bedrock-agentcore-control', region_name='us-west-2')

def get_lifecycle_config():
    try:
        response = client.get_agent_runtime(agentRuntimeId="my_agent_runtime")

        lifecycle_config = response.get('lifecycleConfiguration', {})
        idle_timeout = lifecycle_config.get('idleRuntimeSessionTimeout', 900)
        max_lifetime = lifecycle_config.get('maxLifetime', 28800)

        print(f"Current configuration:")
        print(f"  Idle timeout: {idle_timeout}s ({idle_timeout//60} minutes)")
        print(f"  Max lifetime: {max_lifetime}s ({max_lifetime//3600} hours)")

        return lifecycle_config

    except Exception as e:
        print(f"Error retrieving configuration: {e}")
        return None

# Usage
config = get_lifecycle_config()
print(config)
```

## Validation and constraints


The lifecycle configuration includes validation rules and constraints to prevent invalid configurations.

### Common validation errors


```
import boto3

client = boto3.client('bedrock-agentcore-control', region_name='us-west-2')

try:
    client.create_agent_runtime(
        agentRuntimeName='invalid_config_agent',
        agentRuntimeArtifact={
            'containerConfiguration': {
                'containerUri': '123456789012.dkr.ecr.us-west-2.amazonaws.com/my-agent:latest'
            }
        },
        lifecycleConfiguration={
            'idleRuntimeSessionTimeout': 3600,  # 1 hour, configurable
            'maxLifetime': 1800                 # 30 minutes - INVALID!
        },
        networkConfiguration={'networkMode': 'PUBLIC'},
        roleArn='arn:aws:iam::123456789012:role/AgentRuntimeRole',
        )
except client.exceptions.ValidationException as e:
    print(f"Validation failed: {e}")
    # Output: idleRuntimeSessionTimeout must be less than or equal to maxLifetime
```

### Validation helper function


```
def validate_lifecycle_config(idle_timeout, max_lifetime):
    """Validate lifecycle configuration before API call"""
    errors = []

    # Check range constraints
    if not (1 <= idle_timeout <= 28800):
        errors.append(f"idleRuntimeSessionTimeout must be between 1 and 28800 seconds")

    if not (1 <= max_lifetime <= 28800):
        errors.append(f"maxLifetime must be between 1 and 28800 seconds")

    # Check relationship constraint
    if idle_timeout > max_lifetime:
        errors.append(f"idleRuntimeSessionTimeout ({idle_timeout}s) must be ≤ maxLifetime ({max_lifetime}s)")

    return errors

# Usage
errors = validate_lifecycle_config(3600, 1800)
if errors:
    for error in errors:
        print(f"Validation error: {error}")
else:
    print("Configuration is valid")
```

## Lifecycle settings and runtime sessions


The lifecycle configuration settings you define are applied to each individual runtime session. When you invoke an agent with a specific `runtimeSessionId` , AgentCore Runtime provisions a dedicated microVM for that session. The lifecycle timeouts ( `idleRuntimeSessionTimeout` and `maxLifetime` ) govern the lifecycle of that specific microVM instance.

```
import boto3
import json
import uuid

client = boto3.client('bedrock-agentcore', region_name='us-west-2')

# Each unique runtimeSessionId gets its own microVM with lifecycle settings applied
session_id_user_1 = str(uuid.uuid4())  # User 1's session
session_id_user_2 = str(uuid.uuid4())  # User 2's session

# First invocation for User 1 - creates new microVM with lifecycle timers
response1 = client.invoke_agent_runtime(
    agentRuntimeArn='arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent',
    runtimeSessionId=session_id_user_1,  # Dedicated microVM for this session
    payload=json.dumps({"prompt": "Hello from User 1"}).encode()
)

# First invocation for User 2 - creates separate microVM with its own lifecycle timers
response2 = client.invoke_agent_runtime(
    agentRuntimeArn='arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent',
    runtimeSessionId=session_id_user_2,  # Different microVM for this session
    payload=json.dumps({"prompt": "Hello from User 2"}).encode()
)

# Subsequent invocations to same session reuse the existing microVM
# The idle timeout resets with each invocation to the same session
response3 = client.invoke_agent_runtime(
    agentRuntimeArn='arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent',
    runtimeSessionId=session_id_user_1,  # Reuses User 1's existing microVM
    payload=json.dumps({"prompt": "Follow-up from User 1"}).encode()
)
```

Key points about lifecycle settings and sessions:
+  **Per-session isolation** : Each `runtimeSessionId` gets its own microVM with independent lifecycle timers
+  **Idle timer reset** : The `idleRuntimeSessionTimeout` resets each time you invoke the same session
+  **Maximum lifetime enforcement** : The `maxLifetime` timer starts when the microVM is first created and cannot be reset
+  **Session termination** : When either timeout is reached, only that specific session’s microVM is terminated. The session can be resumed with a new microVM provisioned.

**Tip**  
Each microVM session uses the code assets ( `agentRuntimeArtifact` ) that were deployed at the time of microVM creation. If you update your agent runtime with new code, existing sessions will continue using the previous version until they terminate and new sessions are created.

## Best practices


Follow these best practices when configuring lifecycle settings for optimal resource utilization and user experience.

### Recommendations

+  **Start with defaults** (900s idle, 28800s max) and adjust based on usage patterns
+  **Monitor session duration** to optimize timeout values
+  **Use shorter timeouts** for development environments to save costs
+  **Consider user experience** - too short timeouts may interrupt active users
+  **Test configuration changes** in non-production environments first
+  **Document timeout rationale** for your specific use case

### Common patterns



| Use Case | Idle Timeout | Max Lifetime | Rationale | 
| --- | --- | --- | --- | 
|   **Interactive Chat**   |  10-15 minutes  |  2-4 hours  |  Balance responsiveness with resource usage  | 
|   **Batch Processing**   |  30 minutes  |  8 hours  |  Allow for long-running operations  | 
|   **Development**   |  5 minutes  |  30 minutes  |  Quick cleanup for cost optimization  | 
|   **Production API**   |  15 minutes  |  4 hours  |  Standard production workload  | 
|   **Demo/Testing**   |  2 minutes  |  15 minutes  |  Aggressive cleanup for temporary usage  | 

# Stop a running session
Stop a running session

The `StopRuntimeSession` operation lets you immediately terminate active agent AgentCore Runtime sessions for proper resource cleanup and session lifecycle management.

When called, this operation instantly terminates the specified session and stops any ongoing streaming responses. This lets system resources be properly released and prevents accumulation of orphaned sessions.

Use `StopRuntimeSession` in these scenarios:
+  **User-initiated end** : When users explicitly conclude their conversation
+  **Application shutdown** : Proactive cleanup before application termination
+  **Error handling** : Force termination of unresponsive or stalled sessions
+  **Quota management** : Stay within session limits by closing unused sessions
+  **Timeout handling** : Clean up sessions that exceed expected duration

## Prerequisites


To use [StopRuntimeSession](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_StopRuntimeSession.html) , you need:
+  `bedrock-agentcore:StopRuntimeSession` IAM permission
+ A valid agent AgentCore Runtime ARN
+ The ID of an active session to terminate

**Example**  

1. You can use the AWS SDK to stop AgentCore Runtime sessions programmatically.

   Python example using boto3 to stop a AgentCore Runtime session.

   ```
   import boto3
   
   # Initialize the AgentCore client
   client = boto3.client('bedrock-agentcore', region_name='us-west-2')
   
   try:
       # Stop the runtime session
       response = client.stop_runtime_session(
           agentRuntimeArn='arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent',
           runtimeSessionId='your-session-id',
           qualifier='DEFAULT'  # Optional: endpoint name
       )
   
       print(f"Session terminated successfully")
       print(f"Request ID: {response['ResponseMetadata']['RequestId']}")
   
   except client.exceptions.ResourceNotFoundException:
       print("Session not found or already terminated")
   except client.exceptions.AccessDeniedException:
       print("Insufficient permissions to stop session")
   except Exception as e:
       print(f"Error stopping session: {str(e)}")
   ```

1. For applications using OAuth authentication, make direct HTTPS requests:

   ```
   import requests
   import urllib.parse
   
   def stop_session_with_oauth(agent_arn, session_id, bearer_token, qualifier="DEFAULT"):
       # URL encode the agent ARN
       encoded_arn = agent_arn.replace(':', '%3A').replace('/', '%2F')
   
       # Construct the endpoint URL
       url = f"https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/{encoded_arn}/stopruntimesession?qualifier={qualifier}"
   
       headers = {
           "Authorization": f"Bearer {bearer_token}",
           "Content-Type": "application/json",
           "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": session_id
       }
   
       try:
           response = requests.post(url, headers=headers)
           response.raise_for_status()
   
           print(f"Session {session_id} terminated successfully")
           return response.json()
   
       except requests.exceptions.HTTPError as e:
           if e.response.status_code == 404:
               print("Session not found or already terminated")
           elif e.response.status_code == 403:
               print("Insufficient permissions or invalid token")
           else:
               print(f"HTTP error: {e.response.status_code}")
           raise
       except requests.exceptions.RequestException as e:
           print(f"Request failed: {str(e)}")
           raise
   
   # Usage
   stop_session_with_oauth(
       agent_arn="arn:aws:bedrock-agentcore:us-west-2:123456789012:runtime/my-agent",
       session_id="your-session-id",
       bearer_token="your-oauth-token"
   )
   ```

## Response format


Expected response format for successful `StopRuntimeSession` operations.

```
{
    "ResponseMetadata": {
        "RequestId": "12345678-1234-1234-1234-123456789012",
        "HTTPStatusCode": 200
    }
}
```

## Error handling


Common error responses:


| Status Code | Error | Description | 
| --- | --- | --- | 
|  404  |  ResourceNotFoundException  |  Session not found or already terminated  | 
|  403  |  AccessDeniedException  |  Insufficient permissions  | 
|  400  |  ValidationException  |  Invalid parameters  | 
|  500  |  InternalServerException  |  Service error  | 

## Best practices


### Session lifecycle management


```
class SessionManager:
    def __init__(self, client, agent_arn):
        self.client = client
        self.agent_arn = agent_arn
        self.active_sessions = set()

    def invoke_agent(self, session_id, payload):
        """Invoke agent and track session"""
        try:
            response = self.client.invoke_agent_runtime(
                agentRuntimeArn=self.agent_arn,
                runtimeSessionId=session_id,
                payload=payload
            )
            self.active_sessions.add(session_id)
            return response
        except Exception as e:
            print(f"Failed to invoke agent: {e}")
            raise

    def stop_session(self, session_id):
        """Stop session and remove from tracking"""
        try:
            self.client.stop_runtime_session(
                agentRuntimeArn=self.agent_arn,
                runtimeSessionId=session_id,
                qualifier=endpoint_name
            )
            self.active_sessions.discard(session_id)
            print(f"Session {session_id} stopped")
        except Exception as e:
            print(f"Failed to stop session {session_id}: {e}")

    def cleanup_all_sessions(self):
        """Stop all tracked sessions"""
        for session_id in list(self.active_sessions):
            self.stop_session(session_id)
```

### Recommendations

+  **Always handle exceptions** when stopping sessions
+  **Track active sessions** in your application for cleanup
+  **Set timeouts** for stop requests to avoid hanging
+  **Log session terminations** for debugging and monitoring
+  **Use session managers** for complex applications with multiple sessions

# Persist session state across stop/resume with a filesystem configuration (Preview)
Persist files across stop/resume

Agents built on Amazon Bedrock AgentCore Runtime maintain their local state during the compute lifecycle and by default, the compute associated with a session is ephemeral. Every session boots into a clean filesystem, and if the session is stopped or terminated, invoking the same session again will get a new compute and a clean filesystem. Modern agents don’t just chat — they write code, install packages, generate artifacts, and manage state through the filesystem.

AgentCore Runtime managed session storage changes this. Session storage is a fully service-managed capability where AgentCore Runtime handles all storage operations. Your agent reads and writes to a local filesystem mount and the runtime environment transparently replicates data to service storage throughout the session duration. Session storage is isolated per session — each session can only access its own storage and cannot read or write data from other sessions of the same agent runtime or sessions of different agent runtimes.

## How session storage works


When you configure session storage on an agent runtime, each session gets a persistent directory at the mount path you specify. The lifecycle works as follows:

1.  **First invoke on a session** — A new isolated compute is provisioned. Your agent sees an empty directory at the mount path.

1.  **Agent writes files** — All file operations (read, write, mkdir, rename) work as normal similar to a local filesystem and data is asynchronously replicated to durable storage.

1.  **Session stops** — The compute is terminated. Any data not yet persisted is flushed to durable storage during graceful shutdown.

1.  **Resume with same session** — A new compute is provisioned and the filesystem state is restored from durable storage. The agent can continue from where it left.

## Configure session storage


Add `filesystemConfigurations` with a `sessionStorage` entry when creating or updating an agent runtime.

**Example**  

1. 

   ```
   aws bedrock-agentcore-control create-agent-runtime \
     --agent-runtime-name "coding-agent" \
     --role-arn "arn:aws:iam::111122223333:role/AgentExecutionRole" \
     --agent-runtime-artifact '{
       "containerConfiguration": {
         "containerUri": "123456789012.dkr.ecr.us-west-2.amazonaws.com/my-agent:latest"
       }
     }' \
     --filesystem-configurations '[{
       "sessionStorage": {
         "mountPath": "/mnt/workspace"
       }
     }]'
   ```

1. Python example using boto3 to create an AgentCore Runtime with session storage.

   ```
   import boto3
   
   client = boto3.client("bedrock-agentcore-control", region_name="us-west-2")
   
   response = client.create_agent_runtime(
       agentRuntimeName="coding-agent",
       roleArn="arn:aws:iam::111122223333:role/AgentExecutionRole",
       agentRuntimeArtifact={
           "containerConfiguration": {
               "containerUri": "123456789012.dkr.ecr.us-west-2.amazonaws.com/my-agent:latest"
           }
       },
       filesystemConfigurations=[
           {
               "sessionStorage": {
                   "mountPath": "/mnt/workspace"
               }
           }
       ]
   )
   ```

You can also add session storage to an existing agent runtime using [UpdateAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_UpdateAgentRuntime.html) with the same `filesystemConfigurations` parameter.

## Invoke with session storage


Invoke the agent with a `runtimeSessionId` . Everything your agent writes to the configured mount path persists across stop/resume cycles.

 **Example Using session storage across stop/resume cycles** 

```
# First invocation — agent sets up the project
aws bedrock-agentcore invoke-agent-runtime \
  --agent-runtime-arn "arn:aws:bedrock-agentcore:us-west-2:111122223333:agent-runtime/coding-agent" \
  --runtime-session-id "session-001" \
  --payload '{"prompt": "Set up the project and install dependencies in /mnt/workspace"}'

# Stop the session
aws bedrock-agentcore stop-runtime-session \
  --agent-runtime-arn "arn:aws:bedrock-agentcore:us-west-2:111122223333:agent-runtime/coding-agent" \
  --runtime-session-id "session-001"

# Resume later — the project is exactly where the agent left it
aws bedrock-agentcore invoke-agent-runtime \
  --agent-runtime-arn "arn:aws:bedrock-agentcore:us-west-2:111122223333:agent-runtime/coding-agent" \
  --runtime-session-id "session-001" \
  --payload '{"prompt": "Run the tests and fix any failures"}'
```

The agent sees `/mnt/workspace` exactly as it left it. Source files, node\$1modules, build artifacts, .git history — everything. No re-installing packages. No re-generating boilerplate. No warm-up before the agent can do useful work.

The session’s compute environment spun down hours ago. But the filesystem was already persisted. When you resume, a new compute environment mounts the same storage, and your agent picks up mid-thought.

**Note**  
When explicitly calling `StopRuntimeSession` always wait for it to complete before resuming the session. This ensures all data is flushed to durable storage.

**Note**  
The mounted path is available only at the time of agent invocation, not during initialization.

## Filesystem semantics


Session storage provides a standard Linux filesystem at your configured mount path. Standard tools and operations work without modification — `ls` , `cat` , `mkdir` , `git` , `npm` , `pip` , and `cargo` all work as expected.

 **Supported operations** 

Regular files, directories, and symlinks. Read, write, rename, delete, `chmod` , `chown` , `stat` , and `readdir` — standard POSIX file operations used by common development tools.

 **Limits** 

For session storage limits including maximum storage size, file count, and directory depth, see [Session storage limits](bedrock-agentcore-limits.md#session-storage-limits).

 **Unsupported operations** 

The following filesystem operations are not supported:
+  **Hard links** — Use symlinks instead.
+  **Device files, FIFOs, or UNIX sockets** — `mknod` is not supported.
+  **Extended attributes (xattr)** — Tools that depend on xattr metadata are not supported.
+  **fallocate** — Sparse file preallocation is not supported.
+  **File locking across sessions** — Advisory locks work within a running session but are not persisted across stop/resume. Tools that use file-based locking (such as `git` ) are unaffected.

**Note**  
Permissions are stored but not enforced within the session. `chmod` and `stat` work correctly, but access checks always succeed because the agent runs as the only user in the microVM.

## Session storage lifecycle


Session data is deleted (reset to a clean state) in the following scenarios:
+ The session is not invoked for **14 days.** 
+ The agent runtime version is updated. Invoking a session after a version update provisions a fresh filesystem.

Use [DeleteAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_DeleteAgentRuntime.html) or [DeleteAgentRuntimeEndpoint](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_DeleteAgentRuntimeEndpoint.html) to delete all session storage data associated with the runtime or endpoint.

## Use cases

+  **Durable filesystem scratch space** — Agents write intermediate files, downloaded assets, and work-in-progress artifacts to a scratch directory that persists across session stop/resume cycles, eliminating the need to recreate temporary files after each restart.
+  **Code generation and iteration** — Your agent scaffolds a project, installs dependencies, writes code, and runs tests across multiple sessions. Stop overnight, resume the next morning with the entire project directory intact.
+  **Long-running analysis with checkpoints** — Process large datasets over multiple sessions. Intermediate results, temporary files, and partial outputs persist between invocations without custom checkpoint logic.

## Example: Coding agent with persistent workspace


This example shows a coding agent using Strands Agents with `FileSessionManager` for conversation history and session storage for project files. Both persist across stop/resume cycles.

 **Coding agent with session storage** 

```
import os

# Enable non-interactive mode for strands tools
os.environ["BYPASS_TOOL_CONSENT"] = "true"

from strands import Agent
from strands.session import FileSessionManager
from strands.models import BedrockModel
from strands_tools import file_read, file_write, shell
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()
WORKSPACE = "/mnt/workspace"

model = BedrockModel(model_id="us.anthropic.claude-sonnet-4-20250514-v1:0")
tools = [file_read, file_write, shell]

@app.entrypoint
def handle_request(payload):
    session_id = payload.get("session_id", "default")

    # Persist conversation history alongside project files
    session_manager = FileSessionManager(
        session_id=session_id,
        storage_dir=f"{WORKSPACE}/.sessions"
    )

    agent = Agent(
        model=model,
        tools=tools,
        session_manager=session_manager,
        system_prompt="You are a coding assistant. Project files are in /mnt/workspace."
    )

    response = agent(payload.get("prompt"))
    return {"response": response.message["content"][0]["text"]}

if __name__ == "__main__":
    app.run()
```

 **requirements.txt** 

```
strands-agents
strands-agents-tools
bedrock-agentcore
boto3
```

Invoke the agent, stop the session, then resume. Both project files and conversation context persist.

 **Invoke, stop, and resume cycle** 

```
import boto3, json

client = boto3.client("bedrock-agentcore")
agent_arn = "arn:aws:bedrock-agentcore:us-west-2:111122223333:agent-runtime/coding-agent"
session_id = "project-xyz-001"

def invoke(prompt):
    resp = client.invoke_agent_runtime(
        agentRuntimeArn=agent_arn,
        runtimeSessionId=session_id,
        payload=json.dumps({"prompt": prompt, "session_id": "conv-001"}).encode()
    )
    return json.loads(b"".join(resp["response"]))["response"]

# First invoke: Create a simple script
invoke("Write a Python script called calculator.py with add and subtract functions.")

# Stop session — compute terminates, storage persists
client.stop_runtime_session(agentRuntimeArn=agent_arn, runtimeSessionId=session_id)

# Resume same session — new compute, but files and conversation history restored
invoke("Add a multiply function to the script you created.")
# Agent knows it created calculator.py (conversation history)
# AND finds existing file (file persistence)
```

The `FileSessionManager` stores conversation history to `/mnt/workspace/.sessions/` , enabling the agent to remember context across stop/resume cycles.

## Networking requirements


If your agent runtime uses VPC mode with session storage, the agent will need network access to sync with remote storage. Session data is stored in AgentCore S3, so your VPC must allow outbound connectivity to S3. If you are using an S3 Gateway endpoint with a custom policy, you can scope access to your regional session storage bucket as follows:

```
"Action": [
    "s3:GetObject",
    "s3:PutObject",
    "s3:ListBucket"
],
"Resource": [
    "arn:aws:s3:::acr-storage-*-region-an",
    "arn:aws:s3:::acr-storage-*-region-an/*"
],
"Condition": {
    "StringEquals": {
        "aws:PrincipalServiceName": "bedrock-agentcore.amazonaws.com"
    }
}
```

Replace *region* with your AWS Region (for example, `us-west-2` ).

# Handle asynchronous and long running agents with Amazon Bedrock AgentCore Runtime
Handle asynchronous and long running agents

Amazon Bedrock AgentCore Runtime can handle asynchronous processing and long running agents. Asynchronous tasks allow your agent to continue processing after responding to the client and handle long-running operations without blocking responses. With async processing, your agent can:
+ Start a task that might take minutes or hours
+ Immediately respond to the user saying "I’ve started working on this"
+ Continue processing in the background
+ Allow the user to check back later for results

## Key concepts


### Asynchronous processing model


The Amazon Bedrock AgentCore SDK supports both synchronous and asynchronous processing through a unified API. This creates a flexible implementation pattern for both clients and agent developers. Agent clients can work with the same API without differentiating between synchronous and asynchronous on the client side. With the ability to invoke the same session across invocations, agent developers can reuse context and build upon this context incrementally without implementing complex task management logic.

### Runtime session lifecycle management


Agent code communicates its processing status using the "/ping" endpoint health status. 200 HTTP Status response with payload `{"status": "HealthyBusy"}` indicates the agent is busy processing background tasks. `{"status": "Healthy"}` indicates it is idle (waiting for requests). A session in idle state for 15 minutes gets automatically terminated.

## Implementing asynchronous tasks


To get started, install the `bedrock-agentcore` package:

```
pip install bedrock-agentcore
```

AgentCore SDK provides following options for integration asynchronous processing.

**Example**  

1. To build interactive agents that perform asynchronous tasks, you need to call `add_async_task` when starting a task and `complete_async_task` when the task completes. The SDK handles task tracking and manages Ping status automatically.

   ```
   # Start tracking a task manually
   task_id = app.add_async_task("data_processing")
   
   # Do work...
   
   # Mark task as complete
   app.complete_async_task(task_id)
   ```

1. You can implement your own custom ping handler to manage the Runtime Session’s state. Your agent’s health is reported through the /ping endpoint:

   ```
   @app.ping
   def custom_status():
       if system_busy():
           return PingStatus.HEALTHY_BUSY
       return PingStatus.HEALTHY
   ```

   Status values:
   + "Healthy": Ready for new work
   + "HealthyBusy": Processing background task

**Important**  
Ensure `@app.entrypoint` handler does not perform blocking operations, as this might also block the /ping health check endpoint. Use separate threads or async methods for blocking operations.

## Complete example


First, install the required package:

```
pip install strands-agents
```

Then, create a Python file with the following code:

```
import threading
import time
from strands import Agent, tool
from bedrock_agentcore.runtime import BedrockAgentCoreApp

# Initialize app with debug mode for task management
app = BedrockAgentCoreApp()

@tool
def start_background_task(duration: int = 5) -> str:
    """Start a simple background task that runs for specified duration."""
    # Start tracking the async task
    task_id = app.add_async_task("background_processing", {"duration": duration})

    # Run task in background thread
    def background_work():
        time.sleep(duration)  # Simulate work
        app.complete_async_task(task_id)  # Mark as complete

    threading.Thread(target=background_work, daemon=True).start()
    return f"Started background task (ID: {task_id}) for {duration} seconds. Agent status is now BUSY."

# Create agent with the tool
agent = Agent(tools=[start_background_task])

@app.entrypoint
def main(payload):
    """Main entrypoint - handles user messages."""
    user_message = payload.get("prompt", "Try: start_background_task(3)")
    return {"message": agent(user_message).message}

if __name__ == "__main__":
    print("🚀 Simple Async Strands Example")
    print("Test: curl -X POST http://localhost:8080/invocations -H 'Content-Type: application/json' -d '{\"prompt\": \"start a 3 second task\"}'")
    app.run()
```

This example demonstrates:
+ Creating a background task that runs asynchronously
+ Tracking the task’s status with `add_async_task` and `complete_async_task` 
+ Responding immediately to the user while processing continues
+ Managing the agent’s health status automatically

## Common issues and solutions


### Long-running agent gets terminated after 15 minutes


This can happen when the application is single threaded and the ping thread is blocked.
+ Check that blocking calls in the invocation path are in a separate thread or async non-blocking
+ Run async agent server locally and simulate scenarios while checking for ping status.

# Stream agent responses
Stream agent responses

The following Strands Agents example shows how an AgentCore Runtime agent can stream a response back to a client.

```
from strands import Agent
from bedrock_agentcore import BedrockAgentCoreApp

app = BedrockAgentCoreApp()
agent = Agent()

@app.entrypoint
async def agent_invocation(payload):
    """Handler for agent invocation"""
    user_message = payload.get(
        "prompt", "No prompt found in input, please guide customer to create a json payload with prompt key"
    )
    stream = agent.stream_async(user_message)
    async for event in stream:
        print(event)
        yield (event)

if __name__ == "__main__":
    app.run()
```

# Bidirectional streaming
Bidirectional streaming

Amazon Bedrock AgentCore Runtime supports bidirectional streaming, enabling real-time, two-way communication between clients and agents. This is essential for interactive applications such as conversational voice agents where both the client and agent need to send and receive data simultaneously.

AgentCore Runtime supports two protocols for bidirectional streaming:

WebSocket  
A persistent, full-duplex connection over TCP. WebSocket supports text-based streaming, structured message exchange, and audio streaming with no additional infrastructure required. Clients connect to the AgentCore Runtime WebSocket endpoint using SigV4 or OAuth 2.0 authentication.

WebRTC  
A protocol optimized for real-time audio and video with low latency. WebRTC is well-suited for voice agents in browser and mobile applications, using UDP-based transport for real-time media delivery.

**Topics**
+ [

# Bidirectional streaming with WebSocket
](runtime-bidirectional-streaming-websocket.md)
+ [

# Bidirectional streaming with WebRTC
](runtime-webrtc.md)
+ [

# Code examples
](runtime-bidirectional-streaming-examples.md)

# Bidirectional streaming with WebSocket
Bidirectional streaming with WebSocket

To get started with WebSocket streaming, see [Get started with bidirectional streaming using WebSocket](runtime-get-started-websocket.md).

# Bidirectional streaming with WebRTC
Bidirectional streaming with WebRTC

With Amazon Bedrock AgentCore Runtime, you can use WebRTC (Web Real-Time Communication) to enable real-time media streaming between clients and agents. WebRTC is useful when building voice agents for browser and mobile applications, where clients use the browser-native WebRTC API or mobile WebRTC SDKs for real-time media. For more information about WebRTC, see [WebRTC API](https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API) in the MDN Web Docs.

**Topics**
+ [

## Using WebRTC on AgentCore Runtime
](#runtime-webrtc-requirements)
+ [

## TURN streaming options
](#runtime-webrtc-turn-options)
+ [

# Tutorial: WebRTC with TURN relaying using Amazon Kinesis Video Streams
](runtime-webrtc-get-started-kvs.md)

## Using WebRTC on AgentCore Runtime


WebRTC on AgentCore Runtime requires:
+  **VPC network mode** – Your AgentCore Runtime must be configured with VPC network mode. For more information, see [Configure Amazon Bedrock AgentCore Runtime and tools for VPC](agentcore-vpc.md).
+  **TURN relay** – TURN relay is required for media traffic between the client and the agent. The VPC must support outbound UDP traffic to your TURN endpoints.

## TURN streaming options


The following TURN server options are available for WebRTC on AgentCore Runtime:

Amazon Kinesis Video Streams managed TURN Relay  
KVS provides managed TURN servers through the `GetIceServerConfig` API. This option requires no TURN infrastructure management and integrates natively with AWS IAM for authentication. A KVS signaling channel resource is required to obtain TURN credentials. For WebRTC signaling itself (exchanging ICE candidates and session descriptions), you can use either the AgentCore Runtime invoke API or the KVS signaling channel. To get started, see [Tutorial: WebRTC with TURN relaying using Amazon Kinesis Video Streams](runtime-webrtc-get-started-kvs.md).

Third-party managed TURN  
You can use a third-party managed TURN provider.

Self-hosted TURN  
You can host and operate your own TURN infrastructure using open-source software such as coturn on Amazon EC2 or Amazon ECS.

# Tutorial: WebRTC with TURN relaying using Amazon Kinesis Video Streams
Tutorial: WebRTC with KVS TURN

In this tutorial, you build a WebRTC voice connection between a browser client and an agent running on AgentCore Runtime, using Amazon Kinesis Video Streams (KVS) managed TURN for media relaying. The agent processes audio in real-time using an Amazon Bedrock foundation model for speech-to-speech conversation.

When complete, you will have a working WebRTC connection where audio streams bidirectionally between the browser and your agent.

This tutorial requires a VPC with internet egress for connectivity to KVS TURN endpoints. For more information, see [Internet access considerations](agentcore-vpc.md#agentcore-internet-access) . All other prerequisites are handled by the sample application. The sample uses the AgentCore CLI for deployment to AgentCore Runtime.

For the complete sample application, see [WebRTC Voice Agent with KVS TURN](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming-webrtc) on GitHub.

## Architecture


![\[Architecture diagram showing WebRTC on AgentCore Runtime with KVS TURN relaying through a VPC.\]](http://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/images/runtime-webrtc-architecture.png)


Browser client  
A web page that captures microphone audio using the browser WebRTC API and plays the agent’s audio response.

AgentCore Runtime  
Hosts the agent and attaches to the user’s VPC via an elastic network interface (ENI) in a private subnet.

Agent  
A Python application deployed to AgentCore Runtime that handles WebRTC signaling and TURN credential management through the `/invocations` endpoint, and streams audio between the client and a foundation model.

User VPC with internet egress  
Provides network connectivity from the agent to the KVS TURN relay server. Traffic routes from the ENI in the private subnet through a NAT gateway and internet gateway to reach the TURN endpoints.

KVS TURN relay server  
Relays media traffic between the browser client and the agent. The agent obtains temporary TURN credentials from KVS using the [GetIceServerConfig](https://docs.aws.amazon.com/kinesisvideostreams/latest/dg/API_signaling_GetIceServerConfig.html) API.

## How it works


1. The client invokes the agent to fetch KVS TURN credentials and ICE server configuration.

1. The client creates a WebRTC offer and sends it to the agent. The agent creates a peer connection configured with KVS TURN servers and returns an answer.

1. The client and agent exchange ICE candidates to establish connectivity through the TURN server.

1. Once connected, the client streams microphone audio to the agent over WebRTC. The agent forwards the audio to an Amazon Bedrock foundation model and streams the model’s spoken response back to the client.

# Code examples
Code examples

The following sample applications demonstrate bidirectional streaming on Amazon Bedrock AgentCore Runtime.

## WebSocket examples


Bidirectional WebSocket samples (Sonic, Strands, Echo)  
Three WebSocket examples for AgentCore Runtime: a native Amazon Nova Sonic implementation with direct event handling and a web client, a Strands Agents SDK voice agent using the Strands WebSocket transport, and a minimal echo server for testing bidirectional streaming. See [Bidirectional WebSocket samples](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming) on GitHub.

Pipecat voice agent with WebSocket  
Deploys a Pipecat voice agent to AgentCore Runtime using WebSocket for bidirectional audio streaming. The pipeline orchestrates Deepgram (speech-to-text), Amazon Nova (LLM), and Cartesia (text-to-speech). See [Pipecat on AgentCore with WebSocket](https://github.com/pipecat-ai/pipecat-examples/tree/main/deployment/aws-agentcore-websocket) on GitHub.

Serverless telephony with Vonage and Amazon Nova Sonic  
A serverless contact center agent that handles phone calls through the Vonage Voice API, processing audio in real-time with Amazon Nova Sonic over AgentCore Runtime WebSocket connections. Uses a Lambda function to generate presigned WebSocket URLs for Vonage. See [Serverless telephony with Sonic and AgentCore Runtime](https://github.com/aws-samples/sample-vonage-serverless-sonic) on GitHub.

## WebRTC examples


WebRTC voice agent with KVS TURN  
A minimal WebRTC voice agent using Amazon Kinesis Video Streams for TURN relaying and an Amazon Bedrock foundation model for speech-to-speech conversation. See [WebRTC voice agent with KVS TURN](https://github.com/awslabs/amazon-bedrock-agentcore-samples/tree/main/01-tutorials/01-AgentCore-runtime/06-bi-directional-streaming-webrtc) on GitHub.

Pipecat voice agent with WebRTC  
Deploys a Pipecat voice agent to AgentCore Runtime using SmallWebRTC as a lightweight WebRTC transport. The pipeline orchestrates Deepgram (speech-to-text), Amazon Nova (LLM), and Cartesia (text-to-speech). See [Pipecat on AgentCore with WebRTC](https://github.com/pipecat-ai/pipecat-examples/tree/main/deployment/aws-agentcore-webrtc) on GitHub.

Pipecat voice agent with Daily  
Deploys a Pipecat voice agent to AgentCore Runtime using Daily as the WebRTC transport. Users join by visiting a Daily room URL in their browser. The pipeline orchestrates Deepgram (speech-to-text), Amazon Nova (LLM), and Cartesia (text-to-speech). See [Pipecat on AgentCore with Daily](https://github.com/pipecat-ai/pipecat-examples/tree/main/deployment/aws-agentcore-daily) on GitHub.

LiveKit voice agent on AgentCore Runtime  
Deploys a LiveKit voice agent to AgentCore Runtime with KVS managed TURN or third-party TURN for WebRTC NAT traversal. See [LiveKit on AgentCore Runtime](https://github.com/livekit-examples/agent-deployment/tree/main/bedrock-agentcore) on GitHub.

# Pass custom headers to Amazon Bedrock AgentCore Runtime
Pass custom headers

Custom headers let you pass contextual information from your application directly to your agent code without cluttering the main request payload. This includes authentication tokens like JWT (JSON Web Tokens, which contain user identity and authorization claims) through the `Authorization` header, allowing your agent to make decisions based on who is calling it. You can also pass custom metadata like user preferences, session identifiers, or trace context using headers prefixed with `X-Amzn-Bedrock-AgentCore-Runtime-Custom-` , giving your agent access to up to 20 pieces of runtime context that travel alongside each request. This information can be also used in downstream systems like AgentCore Memory that you can namespace based on those characteristics like `user_id` or `aud` in claims like line of business.

Amazon Bedrock AgentCore Runtime lets you pass headers in a request to your agent code provided the headers match the following criteria:
+ Header name is one of the following:
  + Starts with `X-Amzn-Bedrock-AgentCore-Runtime-Custom-` 
  + Equal to `Authorization` . This is reserved for agents with OAuth inbound access to pass in the incoming JWT token to the agent code.
+ Header value is not greater than 4KB in size.
+ Up to 20 headers can be configured per runtime.

**Topics**
+ [

## Step 1: Create your agent
](#create-agent-headers)
+ [

## Step 2: Configure and deploy your agent with custom headers
](#deploy-agentcore-runtime)
+ [

## Step 3: Invoke your agent with custom headers
](#invoke-custom-headers)
+ [

## Step 4: (Optional) Configure inbound JWT authentication
](#pass-jwt-token)

## Step 1: Create your agent


Create an AgentCore project using the AgentCore CLI:

```
agentcore create --name MyHeaderAgent
cd MyHeaderAgent
```

Update your agent’s entrypoint file to access the custom headers from the request context:

```
import json
from bedrock_agentcore import BedrockAgentCoreApp, RequestContext
from strands import Agent

app = BedrockAgentCoreApp()
agent = Agent()

@app.entrypoint
def agent_invocation(payload, context: RequestContext):
    """Handler for agent invocation"""
    user_message = payload.get(
        "prompt", "No prompt found in input, please guide customer to create a json payload with prompt key"
    )
    app.logger.info("invoking agent with user message: %s", payload)
    response = agent(user_message)

    # access request headers here
    request_headers = context.request_headers
    app.logger.info("Headers: %s", json.dumps(request_headers))
    return response

app.run()
```

## Step 2: Configure and deploy your agent with custom headers


Configure the request header allowlist on your agent runtime so that custom headers are forwarded to your agent code at invocation time.

**Example**  

1. Add the `requestHeaderAllowlist` field to your agent configuration in `agentcore/agentcore.json` :

   ```
   {
     "agents": [
       {
         "name": "MyHeaderAgent",
         "requestHeaderAllowlist": [
           "X-Amzn-Bedrock-AgentCore-Runtime-Custom-H1",
           "X-Amzn-Bedrock-AgentCore-Runtime-Custom-UserId"
         ]
       }
     ]
   }
   ```

   Deploy your agent:

   ```
   agentcore deploy
   ```

   Note the agent runtime ARN from the output. You need it if you plan to invoke using the AWS SDK.

1. After deploying your agent, update the runtime configuration using the AWS SDK:

   ```
   import boto3
   
   client = boto3.client('bedrock-agentcore')
   
   client.update_agent_runtime(
       agentRuntimeId='your-runtime-id',
       requestHeaderConfiguration={
           'requestHeaderAllowlist': [
               'X-Amzn-Bedrock-AgentCore-Runtime-Custom-H1'
           ]
       }
   )
   ```

   You can find your runtime ID by running `agentcore status`.

## Step 3: Invoke your agent with custom headers


Pass custom headers when invoking your agent so that your agent code can access them through the request context.

**Example**  

1. Use the `-H` flag to pass custom headers with `agentcore invoke` :

   ```
   agentcore invoke "Tell me a joke" \
     -H "X-Amzn-Bedrock-AgentCore-Runtime-Custom-H1: test header1"
   ```

   You can pass multiple headers by repeating the `-H` flag:

   ```
   agentcore invoke "Tell me a joke" \
     -H "X-Amzn-Bedrock-AgentCore-Runtime-Custom-H1: test header1" \
     -H "X-Amzn-Bedrock-AgentCore-Runtime-Custom-UserId: user-123"
   ```

1. Use boto3 with event handlers to add custom headers to your agent invocation. For more details on botocore events, see [botocore events documentation](https://botocore.amazonaws.com/v1/documentation/api/latest/topics/events.html).

   ```
   import json
   import boto3
   
   agent_arn = YOUR_AGENT_ARN_HERE
   prompt = "Tell me a joke"
   
   agent_core_client = boto3.client('bedrock-agentcore')
   event_system = agent_core_client.meta.events
   
   EVENT_NAME = 'before-sign.bedrock-agentcore.InvokeAgentRuntime'
   CUSTOM_HEADER_NAME = 'X-Amzn-Bedrock-AgentCore-Runtime-Custom-H1'
   CUSTOM_HEADER_VALUE = 'test header1'
   
   def add_custom_runtime_header(request, **kwargs):
       request.headers.add_header(CUSTOM_HEADER_NAME, CUSTOM_HEADER_VALUE)
   
   handler = event_system.register_first(EVENT_NAME, add_custom_runtime_header)
   
   payload = json.dumps({"prompt": prompt}).encode()
   response = agent_core_client.invoke_agent_runtime(
       agentRuntimeArn=agent_arn,
       payload=payload
   )
   
   event_system.unregister(EVENT_NAME, handler)
   
   content = []
   for chunk in response.get("response", []):
       content.append(chunk.decode('utf-8'))
   print(json.loads(''.join(content)))
   ```

## Step 4: (Optional) Configure inbound JWT authentication


To pass the JWT token used for OAuth-based inbound access to your agent, configure `authorizerType` and `authorizerConfiguration` in your agent configuration.

**Example**  

1. Add the authorizer configuration to your agent in `agentcore/agentcore.json` :

   ```
   {
     "agents": [
       {
         "name": "MyHeaderAgent",
         "authorizerType": "CUSTOM_JWT",
         "authorizerConfiguration": {
           "customJwtAuthorizer": {
             "discoveryUrl": "https://cognito-idp.us-east-1.amazonaws.com/user-pool-id/.well-known/openid-configuration",
             "allowedAudience": ["your-client-id"],
             "allowedClients": ["your-client-id"]
           }
         },
         "requestHeaderAllowlist": [
           "Authorization"
         ]
       }
     ]
   }
   ```

   Deploy to apply the configuration:

   ```
   agentcore deploy
   ```

   With this configuration, the `Authorization` header from incoming requests is validated against your OIDC provider and forwarded to your agent code.

1. For information about setting up an agent with OAuth inbound access using the AWS SDK, see [Authenticate and authorize with Inbound Auth and Outbound Auth](runtime-oauth.md).

# Authenticate and authorize with Inbound Auth and Outbound Auth
Authenticate and authorize with Inbound Auth and Outbound Auth

This section shows you how to implement authentication and authorization for your agent runtime using OAuth and JWT bearer tokens with [AgentCore Identity](identity.md) . You’ll learn how to set up Cognito user pools, configure your agent runtime for JWT authentication (Inbound Auth), and implement OAuth-based access to third-party resources (outbound Auth).

For a complete example, see [https://github.com/awslabs/amazon-bedrock-agentcore-samples/](https://github.com/awslabs/amazon-bedrock-agentcore-samples/).

For information about using OAuth with an MCP server, see [Deploy MCP servers in AgentCore Runtime](runtime-mcp.md).

Amazon Bedrock AgentCore runtime provides two authentication mechanisms for hosted agents:

 **IAM SigV4 Authentication**   
The default authentication and authorization mechanism that works automatically without additional configuration, similar to other AWS APIs.  
 **X-Amzn-Bedrock-AgentCore-Runtime-User-Id Header**   
If your solution requires the hosted agent to retrieve OAuth tokens on behalf of end users (using Authorization Code Grant), you can specify the user identifier by including the `X-Amzn-Bedrock-AgentCore-Runtime-User-Id` header in your requests.  
Invoking InvokeAgentRuntime with the `X-Amzn-Bedrock-AgentCore-Runtime-User-Id header` will require a new IAM action: `bedrock-agentcore:InvokeAgentRuntimeForUser` , in addition to the existing `bedrock-agentcore:InvokeAgentRuntime` action.
 **Security Best Practices for X-Amzn-Bedrock-AgentCore-Runtime-User-Id Header**   
While IAM authentication secures the API access, the `X-Amzn-Bedrock-AgentCore-Runtime-User-Id` header requires additional security considerations. Only trusted principals with the `bedrock-agentcore:InvokeAgentRuntimeForUser` permission should be allowed to set this header. The user-id value should ideally be derived from the authenticated principal’s context (for example, IAM context or user token) rather than accepting arbitrary values. This prevents scenarios where an authenticated user could potentially impersonate another user by manually specifying a different `user-id`.  
Implement audit logging to track the relationship between the authenticated principal and the `user-id` being passed.  
Remember that Amazon Bedrock AgentCore treats this header value as an opaque identifier and relies on your application’s logic to maintain the security boundary between authenticated users and their corresponding `user-id` values.

 **JWT Bearer Token Authentication**   
You can configure your agent runtime to accept JWT bearer tokens by providing authorizer configuration during agent creation.  
This configuration includes:  
+ Discovery URL - A string that must match the pattern `^.+/\.well-known/openid-configuration$` for OpenID Connect discovery URLs
+ Allowed audiences - A list of permitted audiences that will be validated against the aud claim in the JWT token
+ Allowed clients - A list of permitted client identifiers that will be validated against the client\$1id claim in the JWT token
+ Allowed scopes - A list of permitted scopes that will be validated against the scope claim in the JWT token. The `allowedScopes` authorization field will be configured as a list of strings.
+ Required custom claims - A list of required claims that will be validated against the claim name and value contained in the incoming JWT token. For details on configuring the authorizer, see [Configure inbound JWT authorizer](inbound-jwt-authorizer.md) 

**Note**  
An AgentCore Runtime can support either IAM SigV4 or JWT Bearer Token based inbound auth, but not both simultaneously. You can always create different versions of your AgentCore Runtime and configure them for different inbound authorization types. When you create a runtime with Amazon Bedrock AgentCore, a Workload Identity is created automatically for your runtime with AgentCore Identity service.

**Topics**
+ [

## JWT inbound authorization and OAuth outbound access sample
](#oauth-sample-overview)
+ [

## Prerequisites
](#oauth-prerequisites)
+ [

## Step 1: Create your agent project
](#prepare-agent)
+ [

## Step 2: Set up AWS Cognito user pool and add a user
](#setup-cognito)
+ [

## Step 3: Deploy your agent
](#deploy-agent)
+ [

## Step 4: Use bearer token to invoke your agent
](#oauth-invoke-agent)
+ [

## OAuth Error Responses
](#oauth-error-responses)
+ [

## Step 5: Set up your agent to access tools using OAuth
](#oauth-outbound-access)
+ [

## Step 6: (Optional) Propagate a JWT token to AgentCore Runtime
](#oauth-propagate-jwt-token)
+ [

## Troubleshooting
](#troubleshooting)

## JWT inbound authorization and OAuth outbound access sample


This guide walks you through the process of setting up your agent runtime to be invoked with an OAuth compliant access token using JWT format. The sample agent will be authorized using AWS Cognito access tokens. Later, you’ll also learn how the agent code can fetch Google tokens on behalf of the user to check Google Drive and fetch contents.

 **What you’ll learn** 

In this guide, you’ll learn how to:
+ Set up Cognito user pool, add a user, and get a bearer token for the user
+ Set up your agent runtime to use the Cognito user pool for authorization
+ Set up your agent code to fetch OAuth tokens on behalf of the user to call tools

## Prerequisites


Before you begin, make sure you have:
+ An AWS account with appropriate permissions
+ Basic understanding of Python programming
+ Familiarity with Docker containers (for advanced deployment)
+ Set up a basic agent with runtime successfully
+ The latest AWS CLI and `jq` installed
+ Basic understanding of OAuth authorization, mainly JWT bearer tokens, claims, and the various grant flows

## Step 1: Create your agent project


Use the `agentcore create` command to set up a skeleton agent project with the framework of your choice:

```
agentcore create
```

The command will prompt you to:
+ Choose a framework (choose Strands Agents for this tutorial)
+ Provide a project name
+ Configure additional options

This generates:
+ Agent code with your selected framework
+  `agentcore/agentcore.json` configuration file
+  `requirements.txt` with necessary dependencies

**Note**  
The generated agent code will serve as the foundation for implementing OAuth authentication in the following steps.

## Step 2: Set up AWS Cognito user pool and add a user


To set up a Cognito user pool and create a user, you’ll use a shell script that automates the process.

For more information, see [Step 2: Import Identity and Auth modules](identity-getting-started-google.md#identity-getting-started-step2).

<a name="setup-cognito"></a> **To set up Cognito user pool and create a user** 
+ Create a file named `setup_cognito.sh` with the following content:

  ```
  #!/bin/bash
  
  # Create User Pool and capture Pool ID directly
  export POOL_ID=$(aws cognito-idp create-user-pool \
    --pool-name "MyUserPool" \
    --policies '{"PasswordPolicy":{"MinimumLength":8}}' \
    --region $REGION | jq -r '.UserPool.Id')
  
  # Create App Client and capture Client ID directly
  export CLIENT_ID=$(aws cognito-idp create-user-pool-client \
    --user-pool-id $POOL_ID \
    --client-name "MyClient" \
    --no-generate-secret \
    --explicit-auth-flows "ALLOW_USER_PASSWORD_AUTH" "ALLOW_REFRESH_TOKEN_AUTH" \
    --region $REGION | jq -r '.UserPoolClient.ClientId')
  
  # Create User
  aws cognito-idp admin-create-user \
    --user-pool-id $POOL_ID \
    --username $USERNAME \
    --region $REGION \
    --message-action SUPPRESS > /dev/null
  
  # Set Permanent Password
  aws cognito-idp admin-set-user-password \
    --user-pool-id $POOL_ID \
    --username $USERNAME \
    --password $PASSWORD \
    --region $REGION \
    --permanent > /dev/null
  
  # Authenticate User and capture Access Token
  export BEARER_TOKEN=$(aws cognito-idp initiate-auth \
    --client-id "$CLIENT_ID" \
    --auth-flow USER_PASSWORD_AUTH \
    --auth-parameters USERNAME=$USERNAME,PASSWORD=$PASSWORD \
    --region $REGION | jq -r '.AuthenticationResult.AccessToken')
  
  # Output the required values
  echo "Pool id: $POOL_ID"
  echo "Discovery URL: https://cognito-idp.$REGION.amazonaws.com/$POOL_ID/.well-known/openid-configuration"
  echo "Client ID: $CLIENT_ID"
  echo "Bearer Token: $BEARER_TOKEN"
  ```

  Open a terminal window and set the following environment variables:
  +  *REGION* – the AWS Region that you want to use
  +  *USERNAME* – the user name for the new user
  +  *PASSWORD* – the password for the new user

    ```
    export REGION=us-east-1 // set your desired Region
    export USERNAME=USER NAME
    export PASSWORD=PASSWORD
    ```

    In the terminal window, run the script:

    ```
    source setup_cognito.sh
    ```

    Note the output from the script. You’ll need these values in the next steps.

This script creates a Cognito user pool, a user pool client, adds a user, and generates a bearer token for the user. The token is valid for 60 minutes by default.

## Step 3: Deploy your agent


**Important**  
Starting **October 13, 2025** , Amazon Bedrock AgentCore uses a Service-Linked Role (SLR) for workload identity permissions instead of requiring manual IAM policy configuration for new agents.  
The Service-Linked Role details:  
 **Name:** `AWSServiceRoleForBedrockAgentCoreRuntimeIdentity` 
 **Service Principal:** `runtime-identity.bedrock-agentcore.amazonaws.com` 
 **Purpose:** Manages workload identity access tokens and OAuth credentials
Ensure the role you use to invoke AgentCore Control APIs has permission to create the Service-Linked Role:  

```
{
    "Sid": "CreateBedrockAgentCoreIdentityServiceLinkedRolePermissions",
    "Effect": "Allow",
    "Action": "iam:CreateServiceLinkedRole",
    "Resource": "arn:aws:iam::*:role/aws-service-role/runtime-identity.bedrock-agentcore.amazonaws.com/AWSServiceRoleForBedrockAgentCoreRuntimeIdentity",
    "Condition": {
        "StringEquals": {
            "iam:AWSServiceName": "runtime-identity.bedrock-agentcore.amazonaws.com"
        }
    }
}
```
 **Benefit** : The Service-Linked Role automatically provides the necessary permissions for workload identity access without requiring manual policy configuration.  
For detailed information about the service-linked role, see [Identity service-linked role](service-linked-roles.md#identity-service-linked-role).

Now you’ll deploy your agent with JWT authorization using the Cognito user pool you created. You will need to create an agent with authorizer configuration. The following table represents the various authorizer configuration parameters and how we use them to validate the incoming token.


| authorizer\$1configuration | claim in decoded token | Notes | 
| --- | --- | --- | 
|  discovery url → issuer  |  iss  |  The discovery url should point to an issuer url. This should match the iss claim in the decoded token.  | 
|  allowedClients  |  client\$1id  |  client\$1id in the token should match one of the allowed clients specified in the authorizer  | 
|  allowedAudience  |  aud  |  One of the values in aud claim from the token should match one of the allowed audience specified in the authorizer  | 

If both client\$1id and aud is provided, the agent runtime authorizer will verify both.

**Example**  

1. [\$1deploy-agent.title] **To configure and deploy your agent** 

1. Create your agent project with the AgentCore CLI:

   ```
   agentcore create
   ```

   When prompted, choose your framework (choose Strands Agents for this tutorial).

1. Deploy your agent:

   ```
   agentcore deploy
   ```

1. Note the agent runtime ARN from the output. You’ll need this in the next step.
**Tip**  
You can also run the `agentcore create` command without flags for a fully interactive experience that guides you through project setup.

1. 

   ```
   import boto3
   
   # Create the client
   client = boto3.client('bedrock-agentcore-control', region_name="us-east-1")
   
   # Call the CreateAgentRuntime operation
   response = client.create_agent_runtime(
       agentRuntimeName='HelloAgent',
       agentRuntimeArtifact={
           'containerConfiguration': {
               'containerUri': '111122223333.dkr.ecr.us-east-1.amazonaws.com/my-agent:latest'
           }
       },
       authorizerConfiguration={
           "customJWTAuthorizer": {
               "discoveryUrl": 'COGNITO_DISCOVERY_URL',
               "allowedClients": ['COGNITO_CLIENT_ID']
           }
       },
       networkConfiguration={"networkMode":"PUBLIC"},
       roleArn='arn:aws:iam::111122223333:role/AgentRuntimeRole',
       lifecycleConfiguration={
           'idleRuntimeSessionTimeout': 300,  # 5 min, configurable
           'maxLifetime': 1800                # 30 minutes, configurable
       },
   )
   ```

## Step 4: Use bearer token to invoke your agent


Now that your agent is deployed with JWT authorization, you can invoke it using the bearer token.

**Important**  
 **Important for existing users** : Agents created **before October 13, 2025** will continue to use the agent execution role for identity permissions and **require** the above policy to be attached to the agent’s execution role.  
 **New agents** : For agents created **on or after October 13, 2025** , this policy is **not required** as permissions are handled automatically by the Service-Linked Role.  

```
{
    "Sid": "GetAgentAccessToken",
    "Effect": "Allow",
    "Action": [
        "bedrock-agentcore:GetWorkloadAccessToken",
        "bedrock-agentcore:GetWorkloadAccessTokenForJWT",
        "bedrock-agentcore:GetWorkloadAccessTokenForUserId"
    ],
    # point to the workload identity for the runtime; the workload identity can be found in
    # the GetAgentRuntime response and has your agent name in it.
    "Resource": [
        "arn:aws:bedrock-agentcore:region:account-id:workload-identity-directory/default",
        "arn:aws:bedrock-agentcore:region:account-id:workload-identity-directory/default/workload-identity/agentname-*"
    ]
}
```

 **Invoke the agent** 

Fetch a bearer token for the user you created with Amazon Cognito.

```
# use the password and other details used when you created the cognito user

export TOKEN=$(aws cognito-idp initiate-auth \
    --client-id "$CLIENT_ID" \
    --auth-flow USER_PASSWORD_AUTH \
    --auth-parameters USERNAME='testuser',PASSWORD='PASSWORD' \
    --region us-east-1 | jq -r '.AuthenticationResult.AccessToken')
```

Proceed to invoke the agent with the rest of the following instructions.

Invoke the agent with OAuth.

**Example**  

1. 

   ```
   // Invoke with OAuth token
   export PAYLOAD='{"prompt": "hello what is 1+1?"}'
   export BEDROCK_AGENT_CORE_ENDPOINT_URL="https://bedrock-agentcore.us-east-1.amazonaws.com"
   curl -v -X POST "${BEDROCK_AGENT_CORE_ENDPOINT_URL}/runtimes/${ESCAPED_AGENT_ARN}/invocations?qualifier=DEFAULT" \
   -H "Authorization: Bearer ${TOKEN}" \
   -H "X-Amzn-Trace-Id: your-trace-id" \
   -H "Content-Type: application/json" \
   -H "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: your-session-id" \
   -d ${PAYLOAD}
   ```

1. Since boto3 doesn’t support invocation with bearer tokens, you’ll need to use an HTTP client like the requests library in Python.

   <a name="invoke-agent"></a> **To invoke your agent with a bearer token** 

1. Create a Python script named `invoke_agent.py` with the following content:

   ```
   import requests
   import urllib.parse
   import json
   import os
   
   # Configuration Constants
   REGION_NAME = "AWS_REGION"
   
   # === Agent Invocation Demo ===
   invoke_agent_arn = "YOUR_AGENT_ARN_HERE"
   auth_token = os.environ.get('TOKEN')
   print(f"Using Agent ARN from environment: {invoke_agent_arn}")
   
   # URL encode the agent ARN
   escaped_agent_arn = urllib.parse.quote(invoke_agent_arn, safe='')
   
   # Construct the URL
   url = f"https://bedrock-agentcore.{REGION_NAME}.amazonaws.com/runtimes/{escaped_agent_arn}/invocations?qualifier=DEFAULT"
   
   # Set up headers
   headers = {
       "Authorization": f"Bearer {auth_token}",
       "X-Amzn-Trace-Id": "your-trace-id",
       "Content-Type": "application/json",
       "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": "testsession123"
   }
   
   # Enable verbose logging for requests
   import logging
   logging.basicConfig(level=logging.DEBUG)
   logging.getLogger("urllib3.connectionpool").setLevel(logging.DEBUG)
   
   invoke_response = requests.post(
       url,
       headers=headers,
       data=json.dumps({"prompt": "Hello what is 1+1?"})
   )
   
   # Print response in a safe manner
   print(f"Status Code: {invoke_response.status_code}")
   print(f"Response Headers: {dict(invoke_response.headers)}")
   
   # Handle response based on status code
   if invoke_response.status_code == 200:
       response_data = invoke_response.json()
       print("Response JSON:")
       print(json.dumps(response_data, indent=2))
   elif invoke_response.status_code >= 400:
       print(f"Error Response ({invoke_response.status_code}):")
       error_data = invoke_response.json()
       print(json.dumps(error_data, indent=2))
   
   else:
       print(f"Unexpected status code: {invoke_response.status_code}")
       print("Response text:")
       print(invoke_response.text[:500])
   ```

1. Replace *AWS\$1REGION* with the AWS Region that you are using. from Step 3.

1. Replace *YOUR\$1AGENT\$1ARN\$1HERE* with your actual agent runtime ARN from Step 3.

1. Run the script:

   ```
   python invoke_agent.py
   ```

## OAuth Error Responses


OAuth-configured agents follow [RFC 6749 (OAuth 2.0)](https://datatracker.ietf.org/doc/html/rfc6749) authentication standards. When authentication is missing, the service returns a 401 Unauthorized response with a WWW-Authenticate header (per [RFC 7235](https://datatracker.ietf.org/doc/html/rfc7235) ), enabling clients to discover the authorization server endpoints through the GetRuntimeProtectedResourceMetadata API.

### 401 Unauthorized - Missing Authentication


When no Bearer token is provided in the Authorization header, the response is:

```
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{ESCAPED_ARN}/invocations/.well-known/oauth-protected-resource?qualifier={QUALIFIER}"
```

The `resource_metadata` URL in the WWW-Authenticate header points to the Protected Resource Metadata (PRM) API. The PRM API enables clients to discover which authorization servers protect this agent and their OAuth endpoint URLs.

**Note**  
You must pre-register your OAuth client in Cognito (via AWS Console or CLI) to obtain a `client_id` before using the discovered endpoints. Amazon Cognito does not support Dynamic Client Registration (RFC 7591).

## Step 5: Set up your agent to access tools using OAuth


In this section, you’ll learn how to connect your agent code with AgentCore Credential Providers for secure access to external resources using OAuth2 authentication.

The example below demonstrates how your agent running in Agent Runtime can request OAuth consent from users, enabling them to authenticate with their Google account and authorize the agent to access their Google Drive contents.

For more information about setting up identity, see [Get started with AgentCore Identity](identity-getting-started.md).

### Step 5.1: Set up Credential Providers


To set up a Google Credential Provider, you need to:

1. Register your application with Google to obtain client ID and client secret

1. Create an OAuth credential provider using the AWS CLI. Replace *your-client-id* and *your-client-secret* with your actual Google OAuth2 client ID and client secret:

   ```
   OAUTH2_CREDENTIAL_PROVIDER_RESPONSE=$(aws bedrock-agentcore-control create-oauth2-credential-provider \
     --name "google-provider" \
     --credential-provider-vendor "GoogleOauth2" \
     --oauth2-provider-config-input '{
         "googleOauth2ProviderConfig": {
           "clientId": "your-client-id",
           "clientSecret": "your-client-secret"
         }
       }' \
   --output json)
   
   OAUTH2_CALLBACK_URL=$(echo $OAUTH2_CREDENTIAL_PROVIDER_RESPONSE | jq -r '.callbackUrl')
   
   echo "OAuth2 Callback URL: $OAUTH2_CALLBACK_URL"
   ```
**Note**  
Obtain the `callbackUrl` from the [CreateOauth2CredentialProvider](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateOauth2CredentialProvider.html) response above and add the URI to your Google application’s redirect URI list. The callback URL should look like: https://bedrock-agentcore.us-east-1.amazonaws.com/identities/oauth2/callback/\$1\$1\$1\$1\$1\$1\$1\$1-\$1\$1\$1\$1-\$1\$1\$1\$1-\$1\$1\$1\$1-\$1\$1\$1\$1\$1\$1\$1\$1\$1\$1\$1\$1

Make sure your invocation role has the necessary permissions for accessing the credential provider.

### Step 5.2: Enable agent to read Google Drive contents


Create a tool with agent core SDK annotations as shown below to automatically initiate the three-legged OAuth process. When your agent invokes this tool, users will be prompted to open the authorization URL in their browser and grant consent for the agent to access their Google Drive.

```
import asyncio
from bedrock_agentcore.identity.auth import requires_access_token, requires_api_key

# This annotation helps agent developer to obtain access tokens from external applications
@requires_access_token(
    provider_name="google-provider",
    scopes=["https://www.googleapis.com/auth/drive.metadata.readonly"], # Google OAuth2 scopes
    auth_flow="USER_FEDERATION", # 3LO flow
    on_auth_url=lambda x: print("Copy and paste this authorization url to your browser: ", x), # prints authorization URL to console
    force_authentication=True,
    callback_url='insert_oauth2_callback_url_for_session_binding'
)
async def read_from_google_drive(*, access_token: str):
    print(access_token) #You can see the access_token
    # Make API calls...
    main(access_token)

asyncio.run(read_from_google_drive(access_token=""))
```

**Note**  
For a sample local callback server implementation to handle [session binding](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/oauth2-authorization-url-session-binding.html) , refer to [https://github.com/awslabs/amazon-bedrock-agentcore-samples/blob/main/01-tutorials/03-AgentCore-identity/05-Outbound_Auth_3lo/oauth2_callback_server.py](https://github.com/awslabs/amazon-bedrock-agentcore-samples/blob/main/01-tutorials/03-AgentCore-identity/05-Outbound_Auth_3lo/oauth2_callback_server.py) 

 **What happens behind the scenes** 

When this code runs, the following process occurs:

1. Agent Runtime authorizes the inbound token according to the configured authorizer.

1. Agent Runtime exchanges this token for a Workload Access Token via `bedrock-agentcore:GetWorkloadAccessTokenForJWT` API and delivers it to your agent code via the payload header `WorkloadAccessToken`.

1. During tool invocation, your agent uses this Workload Access Token to call Token Vault API `bedrock-agentcore:GetResourceOauth2Token` and generate a 3LO authentication URL.

1. Your agent sends this URL to the client application as specified in the `on_auth_url` method.

1. The client application presents this URL to the user, who grants consent for the agent to access their Google Drive.

1. AgentCore Identity service securely receives and caches the Google access token until it expires, enabling subsequent requests from the user to use this token without needing the user to provide consent for every request.

**Note**  
AgentCore Identity Service stores the Google access token in the AgentCore Token Vault using the agent workload identity and user ID (from the inbound JWT token, such as AWS Cognito token) as the binding key, eliminating repeated consent requests until the Google token expires.

## Step 6: (Optional) Propagate a JWT token to AgentCore Runtime


Optionally, you can pass an Authorization header to an AgentCore Runtime to extract claims. This can be done by using the request header allowlist configuration. For more information, see [RequestHeaderConfiguration](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_RequestHeaderConfiguration.html).

### Step 6.1: Modify your agent code to read headers


In this step you make changes to your agent code so that you can decode and extract claims from a JWT token using PyJWT library.

 **requirements.txt** 

Add PyJWT dependency to the `requirements.txt` file in your generated project.

```
PyJWT
```

 **Update your agent code** 

Modify the main agent file in your generated project (typically `src/main.py` or similar, depending on your framework choice) as shown in the following code. You can skip validating the token signature here since it has already been validated by AgentCore Runtime when the inbound authorization was done.

```
import jwt
import json
....

@app.entrypoint
def invoke(payload, context):
    auth_header = context.request_headers.get('Authorization')
    if not auth_header:
        return None

    # Remove "Bearer " prefix if present
    token = auth_header.replace('Bearer ', '') if auth_header.startswith('Bearer ') else auth_header
    try:
        # Skip signature validation as agent runtime has validated the token already.
        claims = jwt.decode(token, options={"verify_signature": False})
        app.logger.info("Claims: %s", json.dumps(claims))
    except jwt.InvalidTokenError as e:
        app.logger.exception("Invalid JWT token: %s", e)

    .....
```

### Step 6.2: Create the agent with request header allowlist


Use the AgentCore CLI to configure the agent with request header allowlist. Navigate to your generated project directory and run:

```
agentcore create --name HelloAgent --framework Strands --model-provider Bedrock --memory none

# Now deploy the agent runtime
agentcore deploy
```

**Note**  
The AgentCore CLI creates the project structure and configuration files. Adjust the agent configuration in `agentcore/agentcore.json` as needed for your framework choice.

### Step 6.3: Invoke your agent


 [Invoke](#oauth-invoke-agent) your agent using OAuth and you should see the claims in your agent logs in CloudWatch Logs.

## Troubleshooting


### How to debug token related issues


If you encounter issues with token authentication, you can decode the token to inspect its contents:

```
echo "$TOKEN" | cut -d '.' -f2 | tr '_-' '/+' | awk '{ l=4 - length($0)%4; if (l<4) printf "%s", $0; for (i=0; i<l; i++) printf "="; print "" }' | base64 -D | jq
```

This will output the token’s payload, which looks similar to:

```
{
    "sub": "subid",
    "iss": "https://cognito-idp.us-east-1.amazonaws.com/userpoolid",
    "client_id": "clientid",
    "origin_jti": "originjti",
    "event_id": "eventid",
    "token_use": "access",
    "scope": "aws.cognito.signin.user.admin",
    "auth_time": 1752275688,
    "exp": 1752279288,
    "iat": 1752275688,
    "jti": "jti",
    "username": "username"
}
```

When troubleshooting token issues, check the following:
+ Issuer url pointed to by the discovery url in the agent authorizer should match the issuer claim in the token. Do the following to confirm they match:
  + Select the discovery url you provided in the authorizer configuration when you created the agent, for example: `https://cognito-idp.us-east-1.amazonaws.com/us-east-1_nnnnnnnnn/.well-known/openid-configuration` 
    + Check the issuer url - `"issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_12345566"` . This should match the iss claim value in the token.
+  `client_id` claim in the token must match one of the authorizer allowedClients entries if provided
  + Note the client id you provided when you created the agent
  + Confirm this matches the client\$1id claim in the decoded token
+  `aud` claim in the token must match one of the authorizer `allowedAudience` entries, if provided
  + Note the audience list you provided when you created the agent
  + Confirm this matches the `aud` claim in the decoded token
+ Tokens are only valid for several minutes (the default Amazon Cognito expiry is 60 minutes). Fetch a new token as needed.

# AgentCore Runtime versioning and endpoints
AgentCore Runtime versioning and endpoints

Amazon Bedrock AgentCore implements automatic versioning for AgentCore Runtimes and lets you manage different configurations using endpoints.

Each AgentCore Runtime in Amazon Bedrock AgentCore is automatically versioned:
+ When you create an AgentCore Runtime, AgentCore Runtime automatically creates version 1 (V1)
+ Each update to the AgentCore Runtime creates a new version with a complete, self-contained configuration
+ Versions are immutable once created
+ Each version contains all the configuration needed for execution

## How endpoints reference versions


Endpoints provide a way to reference specific versions of your AgentCore Runtime:
+ The `DEFAULT` endpoint automatically points to the latest version of your AgentCore Runtime
+ Endpoints can point to specific versions, allowing you to maintain different environments (e.g., development, staging, production)
+ When you update an AgentCore Runtime, the `DEFAULT` endpoint is automatically updated to point to the new version
+ Endpoints must be explicitly updated to point to new versions

 **Example Updating an endpoint to a New Version** 

```
bedrock_agentcore_client = boto3.client('bedrock-agentcore', region_name='us-west-2')

response = bedrock_agentcore_client.update_agent_runtime_endpoint(
    agentRuntimeId='agent-runtime-12345',
    endpointName='production-endpoint',
    agentRuntimeVersion='v2.1',
    description='Updated production endpoint'
)

print(response)
```

## Versioning scenarios


The following table illustrates how versioning and endpoints interact during the lifecycle of an AgentCore Runtime:


| Change Type | Version Creation Behavior | Latest Version | Endpoint Behavior | 
| --- | --- | --- | --- | 
|  Initial Creation  |  Creates Version 1 (V1) automatically  |  V1  |  DEFAULT points to V1  | 
|  Protocol Change  |  Creates a new version with updated protocol settings  |  V2  |  DEFAULT automatically updates to V2  | 
|  Create "PROD" endpoint with V2  |  No new version created  |  V2  |  PROD endpoint points to V2  | 
|  Container Image Update  |  Creates a new version with new container reference  |  V3  |  DEFAULT updates to V3, PROD remains on V2  | 
|  Update "PROD" to V3  |  No new version created  |  V3  |  PROD updates to V3  | 
|  Network Settings Modification  |  Creates a new version with updated security parameters  |  V4  |  DEFAULT updates to V4, PROD remains on V3  | 

## Endpoint lifecycle states


AgentCore Runtime endpoints go through various states during their lifecycle:

CREATING  
Initial state when an endpoint is being created

CREATE\$1FAILED  
Indicates creation failure due to permissions, container, or other issues

READY  
Endpoint is ready to accept requests

UPDATING  
Endpoint is being updated to a new version

UPDATE\$1FAILED  
Indicates update operation failure

## Listing AgentCore Runtime versions and endpoints


You can list all versions of an AgentCore Runtime by calling the `ListAgentRuntimeVersions` operation. To list the endpoints for an AgentCore Runtime, call `ListAgentRuntimeEndpoints`.

# Invoke an AgentCore Runtime agent
Invoke an agent

The [InvokeAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntime.html) operation lets you send requests to specific AgentCore Runtime endpoints identified by their Amazon Resource Name (ARN) and receive streaming responses containing the agent’s output. The API supports session management through session identifiers, enabling you to maintain conversation context across multiple interactions. You can target specific agent endpoints using optional qualifiers.

To call `InvokeAgentRuntime` , you need `bedrock-agentcore:InvokeAgentRuntime` permissions. In the call you can also pass a bearer token that the agent can use for user authentication.

The `InvokeAgentRuntime` operation accepts your request payload as binary data up to 100 MB in size and returns a streaming response that delivers chunks of data in real-time as the agent processes your request. This streaming approach allows you to receive partial results immediately rather than waiting for the complete response, making it ideal for interactive applications.

To execute shell commands (such as running tests, git operations, or environment setup) in the same session, use the [Execute shell commands in AgentCore Runtime sessions](runtime-execute-command.md) operation. Both operations work on the same agent runtime and session.

If you plan on integrating your agent with OAuth, you can’t use the AWS SDK to call `InvokeAgentRuntime` . Instead, make a HTTPS request to InvokeAgentRuntime. For more information, see [Authenticate and authorize with Inbound Auth and Outbound Auth](runtime-oauth.md).

## Invoke streaming agents


The following example shows how to use boto3 to invoke an agent runtime:

```
import boto3
import json

# Initialize the Bedrock AgentCore client
agent_core_client = boto3.client('bedrock-agentcore')

# Prepare the payload
payload = json.dumps({"prompt": prompt}).encode()

# Invoke the agent
response = agent_core_client.invoke_agent_runtime(
    agentRuntimeArn=agent_arn,
    runtimeSessionId=session_id,
    payload=payload
)


# Process and print the response
if "text/event-stream" in response.get("contentType", ""):

    # Handle streaming response
    content = []
    for line in response["response"].iter_lines(chunk_size=10):
        if line:
            line = line.decode("utf-8")
            if line.startswith("data: "):
                line = line[6:]
                print(line)
                content.append(line)
    print("\nComplete response:", "\n".join(content))

elif response.get("contentType") == "application/json":
    # Handle standard JSON response
    content = []
    for chunk in response.get("response", []):
        content.append(chunk.decode('utf-8'))
    print(json.loads(''.join(content)))

else:
    # Print raw response for other content types
    print(response)
```

## Invoke multi-modal agents


You can use the `InvokeAgentRuntime` operation to send multi-modal requests that include both text and images. The following example shows how to invoke a multi-modal agent:

```
import boto3
import json
import base64

# Read and encode image
with open("image.jpg", "rb") as image_file:
    image_data = base64.b64encode(image_file.read()).decode('utf-8')

# Prepare multi-modal payload
payload = json.dumps({
    "prompt": "Describe what you see in this image",
    "media": {
        "type": "image",
        "format": "jpeg",
        "data": image_data
 }
}).encode()

# Invoke the agent
response = agent_core_client.invoke_agent_runtime(
    agentRuntimeArn=agent_arn,
    runtimeSessionId=session_id,
    payload=payload
)
```

## Session management


The `InvokeAgentRuntime` operation supports session management through the `runtimeSessionId` parameter. By providing the same session identifier across multiple requests, you can maintain conversation context, allowing the agent to reference previous interactions.

To start a new conversation, generate a unique session identifier. To continue an existing conversation, use the same session identifier from previous requests. This approach enables you to build interactive applications that maintain context over time.

**Tip**  
For best results, use a UUID or other unique identifier for your session IDs to avoid collisions between different users or conversations.

## Error handling


When using the `InvokeAgentRuntime` operation, you might encounter various errors. Here are some common errors and how to handle them:

 **ValidationException**   
Occurs when the request parameters are invalid. Check that your agent ARN, session ID, and payload are correctly formatted.

 **ResourceNotFoundException**   
Occurs when the specified agent runtime cannot be found. Verify that the agent ARN is correct and that the agent exists in your AWS account.

 **AccessDeniedException**   
Occurs when you don’t have the necessary permissions. Ensure that your IAM policy includes the `bedrock-agentcore:InvokeAgentRuntime` permission.

 **ThrottlingException**   
Occurs when you exceed the request rate limits. Implement exponential backoff and retry logic in your application.

Implement proper error handling in your application to provide a better user experience and to troubleshoot issues effectively.

## Best practices


Follow these best practices when using the `InvokeAgentRuntime` operation:
+ Use session management to maintain conversation context for a better user experience.
+ Process streaming responses incrementally to provide real-time feedback to users.
+ Implement proper error handling and retry logic for a robust application.
+ Consider payload size limitations (100 MB) when sending requests, especially for multi-modal content.
+ Use appropriate qualifiers to target specific agent versions or endpoints.
+ Implement authentication mechanisms when necessary using bearer tokens.
+ Use `InvokeAgentRuntimeCommand` for deterministic operations (tests, git, builds) instead of routing them through the agent’s LLM. See [Execute shell commands in AgentCore Runtime sessions](runtime-execute-command.md).

# Execute shell commands in AgentCore Runtime sessions
Execute shell commands

The [InvokeAgentRuntimeCommand](https://docs.aws.amazon.com/bedrock-agentcore/latest/APIReference/API_InvokeAgentRuntimeCommand.html) operation lets you execute shell commands directly inside a running AgentCore Runtime session and stream the output back over HTTP/2. Commands run in the same container, filesystem, and environment as your agent - the same session used by `InvokeAgentRuntime` . This enables workflows where your application uses the agent for reasoning tasks and commands for deterministic operations like running tests, git operations, or environment setup.

To call `InvokeAgentRuntimeCommand` , you need `bedrock-agentcore:InvokeAgentRuntimeCommand` permissions.

## When to use InvokeAgentRuntimeCommand



| Use InvokeAgentRuntimeCommand | Use InvokeAgentRuntime | 
| --- | --- | 
|  The operation has a known command ( `npm test` , `git push` )  |  The operation requires reasoning ("analyze this code and fix the bug")  | 
|  You want deterministic execution - same command, same result  |  You want the LLM to decide what to do  | 
|  You need streaming output from a long-running process  |  You need the agent to use tools in a loop  | 
|  The operation is a validation gate in your workflow  |  The operation is the creative or analytical work  | 
|  You’re bootstrapping the environment before the agent starts  |  You’re asking the agent to work on a task  | 

## How it works


 `InvokeAgentRuntimeCommand` runs a shell command inside the container of an active AgentCore Runtime session and streams the output back.

 **Same agent, same session** 

 `InvokeAgentRuntimeCommand` operates on the same agent runtime and session as `InvokeAgentRuntime` . You don’t create separate resources. The agent you deployed with `CreateAgentRuntime` accepts both agent invocations and command execution on any active session.

**Note**  
The AgentCore Runtime microVM does not include developer tools like `git` , `npm` , or language runtimes by default. Any tools your commands depend on must be included in your container image (via your Dockerfile) or installed dynamically at runtime.

The response is a stream of three event types:


| Event | When | Contains | 
| --- | --- | --- | 
|   `contentStart`   |  First chunk  |  Confirms the command started  | 
|   `contentDelta`   |  During execution  |   `stdout` and/or `stderr` output  | 
|   `contentStop`   |  Last chunk  |   `exitCode` and `status` ( `COMPLETED` or `TIMED_OUT` )  | 

Output streams in real time. You see results as they run, not after they finish.

## Prerequisites

+  `bedrock-agentcore:InvokeAgentRuntimeCommand` IAM permission
+ A valid AgentCore Runtime endpoint ARN

**Note**  
Agents created after March 17, 2026 support command execution automatically. If you deployed your agent before this date, you must redeploy it to update the agent runtime.

## Execute a command


**Example**  

1. The following example shows how to use boto3 to execute a command in a AgentCore Runtime session.

   ```
   import boto3
   import sys
   
   client = boto3.client('bedrock-agentcore', region_name='us-west-2')
   
   response = client.invoke_agent_runtime_command(
       agentRuntimeArn='arn:aws:bedrock-agentcore:us-west-2:account-id:runtime/my-agent',
       runtimeSessionId='session-id-at-least-33-characters-long',
       qualifier='DEFAULT',
       contentType='application/json',
       accept='application/vnd.amazon.eventstream',
       body={
           'command': '/bin/bash -c "npm test"',
           'timeout': 60
       }
   )
   
   # Process the streaming response
   for event in response.get('stream', []):
       if 'chunk' in event:
           chunk = event['chunk']
   
           if 'contentStart' in chunk:
               print("Command execution started")
   
           if 'contentDelta' in chunk:
               delta = chunk['contentDelta']
               if delta.get('stdout'):
                   print(delta['stdout'], end='')
               if delta.get('stderr'):
                   print(delta['stderr'], end='', file=sys.stderr)
   
           if 'contentStop' in chunk:
               stop = chunk['contentStop']
               print(f"\nExit code: {stop.get('exitCode')}, Status: {stop.get('status')}")
   ```

1. The following example shows how to use the AWS SDK for Java to execute a command in a AgentCore Runtime session.

   ```
   import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
   import software.amazon.awssdk.regions.Region;
   import software.amazon.awssdk.services.bedrockagentcore.BedrockAgentCoreAsyncClient;
   import software.amazon.awssdk.services.bedrockagentcore.model.*;
   import java.util.UUID;
   import java.util.concurrent.CompletableFuture;
   
   public class ExecuteCommandExample {
       public static void main(String[] args) throws Exception {
           String agentArn = "arn:aws:bedrock-agentcore:us-west-2:account-id:runtime/my-agent";
           String sessionId = UUID.randomUUID().toString();
   
           BedrockAgentCoreAsyncClient client = BedrockAgentCoreAsyncClient.builder()
                   .region(Region.US_WEST_2)
                   .credentialsProvider(DefaultCredentialsProvider.create())
                   .build();
   
           InvokeAgentRuntimeCommandRequest request = InvokeAgentRuntimeCommandRequest.builder()
                   .agentRuntimeArn(agentArn)
                   .runtimeSessionId(sessionId)
                   .qualifier("DEFAULT")
                   .contentType("application/json")
                   .accept("application/vnd.amazon.eventstream")
                   .body(InvokeAgentRuntimeCommandRequestBody.builder()
                           .command("/bin/bash -c \"npm test\"")
                           .timeout(60)
                           .build())
                   .build();
   
           InvokeAgentRuntimeCommandResponseHandler handler = InvokeAgentRuntimeCommandResponseHandler.builder()
                   .subscriber(InvokeAgentRuntimeCommandResponseHandler.Visitor.builder()
                           .onChunk(chunk -> {
                               if (chunk.contentStart() != null) {
                                   System.out.println("Command execution started");
                               }
                               if (chunk.contentDelta() != null) {
                                   ContentDeltaEvent delta = chunk.contentDelta();
                                   if (delta.stdout() != null) System.out.print(delta.stdout());
                                   if (delta.stderr() != null) System.err.print(delta.stderr());
                               }
                               if (chunk.contentStop() != null) {
                                   ContentStopEvent stop = chunk.contentStop();
                                   System.out.println("\nExit code: " + stop.exitCode() +
                                       ", Status: " + stop.statusAsString());
                               }
                           })
                           .build())
                   .build();
   
           CompletableFuture<Void> future = client.invokeAgentRuntimeCommand(request, handler);
           future.get();
           client.close();
       }
   }
   ```

1. The following example shows how to use the AWS SDK for JavaScript v3 to execute a command in a AgentCore Runtime session.

   ```
   import {
       BedrockAgentCoreClient,
       InvokeAgentRuntimeCommandCommand
   } from "@aws-sdk/client-bedrock-agentcore";
   import { randomUUID } from "crypto";
   
   const client = new BedrockAgentCoreClient({ region: "us-west-2" });
   
   const request = {
       agentRuntimeArn: "arn:aws:bedrock-agentcore:us-west-2:account-id:runtime/my-agent",
       runtimeSessionId: randomUUID(),
       qualifier: "DEFAULT",
       contentType: "application/json",
       accept: "application/vnd.amazon.eventstream",
       body: {
           command: '/bin/bash -c "npm test"',
           timeout: 60,
       },
   };
   
   const command = new InvokeAgentRuntimeCommandCommand(request);
   const response = await client.send(command);
   
   // Process the event stream
   for await (const event of response.stream) {
       if (event.chunk) {
           const chunk = event.chunk;
   
           if (chunk.contentStart) {
               console.log("Command execution started");
           }
   
           if (chunk.contentDelta) {
               if (chunk.contentDelta.stdout) process.stdout.write(chunk.contentDelta.stdout);
               if (chunk.contentDelta.stderr) process.stderr.write(chunk.contentDelta.stderr);
           }
   
           if (chunk.contentStop) {
               console.log(`\nExit code: ${chunk.contentStop.exitCode}, ` +
                   `Status: ${chunk.contentStop.status}`);
           }
       }
   }
   
   client.destroy();
   ```

## Coding agent workflow example


A common pattern is to use `InvokeAgentRuntime` for reasoning and `InvokeAgentRuntimeCommand` for deterministic operations in the same session.

 **Example End-to-end coding agent workflow** 

```
import boto3
import json

client = boto3.client('bedrock-agentcore', region_name='us-west-2')

AGENT_ARN = 'arn:aws:bedrock-agentcore:us-west-2:account-id:runtime/my-agent'
SESSION_ID = 'session-id-at-least-33-characters-long'

def run_command(command, timeout=60):
    """Helper to run a command and return the exit code."""
    response = client.invoke_agent_runtime_command(
        agentRuntimeArn=AGENT_ARN,
        runtimeSessionId=SESSION_ID,
        contentType='application/json',
        accept='application/vnd.amazon.eventstream',
        body={'command': command, 'timeout': timeout}
    )
    for event in response.get('stream', []):
        if 'chunk' in event and 'contentStop' in event['chunk']:
            return event['chunk']['contentStop'].get('exitCode')
    return None

# Step 1: Invoke the agent to analyze and write a fix
response = client.invoke_agent_runtime(
    agentRuntimeArn=AGENT_ARN,
    runtimeSessionId=SESSION_ID,
    payload=json.dumps({"prompt": "Read JIRA-1234 and implement the fix in /workspace"}).encode()
)
# Process agent response...

# Step 2: Run tests deterministically
exit_code = run_command('/bin/bash -c "cd /workspace && npm test"', timeout=300)

# Step 3: If tests pass, commit and push
if exit_code == 0:
    run_command('/bin/bash -c "cd /workspace && git checkout -b fix/JIRA-1234"')
    run_command('/bin/bash -c "cd /workspace && git add -A && git commit -m \'Fix JIRA-1234\'"')
    run_command('/bin/bash -c "cd /workspace && git push origin fix/JIRA-1234"')
```

The agent writes the code. The platform runs the commands. Each does what it’s best at.

## Common use cases


Running test suites  
After the agent writes code, run the project’s test suite as a command. The streaming response lets you detect failures early and feed specific error output back to the agent for iteration.  

```
/bin/bash -c "cd /workspace && npm test 2>&1"
```

Git operations  
Branching, committing, and pushing are deterministic operations. Run them as commands after the agent completes its work, keeping version control logic out of the LLM.  

```
/bin/bash -c "cd /workspace && git add -A && git commit -m 'Fix issue'"
```

Dependency installation  
Bootstrap the environment before invoking the agent -clone repos, install packages, set up build tooling. This preparation runs faster and more reliably as direct commands.  

```
/bin/bash -c "pip install -r requirements.txt"
```

Build and compile  
Compile steps and asset generation -anything with a known command that should run exactly as specified.  

```
/bin/bash -c "cd /workspace && cargo build --release"
```

Linting and validation  
Run code quality checks as a validation gate after the agent writes code, before committing.  

```
/bin/bash -c "cd /workspace && npx eslint src/ --format json"
```

Environment inspection  
Check runtime state, installed packages, available tools -useful for debugging agent failures.  

```
/bin/bash -c "python --version && node --version && git --version"
```

Data operations  
Fetch datasets, upload results, run data transformations -network and compute operations that run faster as direct commands.  

```
/bin/bash -c "aws s3 cp s3://my-bucket/data.csv /workspace/"
```

## Key design choices


One-shot, non-interactive execution  
Each command spawns a new bash process, runs to completion (or timeout), and returns. There is no persistent shell session between commands. This matches how agent frameworks use command execution -craft a command, run it, read the output, decide what to do next.

Streaming response over HTTP/2  
Output arrives as it’s produced, not buffered until completion. A `npm test` that takes two minutes streams results in real time. Your application can detect a failure in the first few seconds and cancel early rather than waiting for the full run.

Container isolation  
Commands execute inside the same container as your agent code. They see the same filesystem, environment variables, and installed packages. A file the agent wrote at `/workspace/fix.py` is immediately visible to a command running `cat /workspace/fix.py`.

Non-blocking to the runtime  
Command execution doesn’t block agent invocations. You can invoke the agent and run commands concurrently on the same session. The platform handles the concurrency.

Stateless between commands  
Each command starts fresh -no shell history, no environment variable changes from previous commands carry over. If you need state, encode it in the command itself: `cd /workspace && export NODE_ENV=test && npm test`.

## Security considerations


**Important**  
Under the AWS shared responsibility model, you are responsible for the security of the commands you execute in your AgentCore Runtime sessions. AWS provides the secure infrastructure and isolation at the microVM level. You are responsible for the commands you run, the data you process, and the access controls you configure.

The security boundary for command execution is the microVM. Each AgentCore Runtime session runs in an isolated microVM with its own kernel, memory, and filesystem. Commands you execute cannot access other customers' workloads or escape the VM boundary. However, within your VM, commands have full access to the container filesystem and any credentials or secrets you have configured.

 **Auditing with CloudWatch Logs** 

AgentCore Runtime sends the request ID and the input command to your agent’s Amazon CloudWatch Logs log group. You can use these logs to monitor command activity and maintain an audit trail of what commands were executed in your sessions. The command execution output (stdout and stderr) is streamed back to your application and is not logged by the service.

 **Auditing with CloudTrail** 

 AWS CloudTrail records `InvokeAgentRuntimeCommand` API calls in your account. Each record includes metadata such as the caller identity, timestamp, source IP address, and response status. CloudTrail does not log the request or response payload. Use CloudTrail to audit who executed commands and when, then correlate with CloudWatch Logs logs using the request ID to see what command was executed.

For sensitive workloads, consider implementing additional controls such as:
+ Using IAM policies to restrict which principals can call `InvokeAgentRuntimeCommand` 
+ Configuring VPC endpoints to keep traffic within your network
+ Setting up CloudWatch Logs metric filters and alarms to detect unexpected command patterns
+ Reviewing CloudTrail logs regularly for unauthorized access attempts

## Error handling


When using the `InvokeAgentRuntimeCommand` operation, you might encounter the following errors:

 **ValidationException**   
Occurs when the request parameters are invalid. Check that your agent ARN, session ID, and command are correctly formatted. The command must be between 1 byte and 64 KB, the timeout must be between 1 and 3600 seconds, and the session ID must be at least 33 characters.

 **ResourceNotFoundException**   
Occurs when the specified agent runtime or session cannot be found. Verify that the agent ARN is correct and that the session is active.

 **AccessDeniedException**   
Occurs when you don’t have the necessary permissions. Ensure that your IAM policy includes the `bedrock-agentcore:InvokeAgentRuntimeCommand` permission.

 **ThrottlingException**   
Occurs when you exceed the request rate limit of 25 TPS. Implement exponential backoff and retry logic in your application.

A command that completes with a non-zero exit code is not an API error. Check the `exitCode` in the `contentStop` event to determine if the command itself succeeded. A `status` of `TIMED_OUT` indicates the command exceeded the specified timeout.

## Best practices


Follow these best practices when using the `InvokeAgentRuntimeCommand` operation:
+ Use `InvokeAgentRuntimeCommand` for deterministic operations (tests, git, builds) and `InvokeAgentRuntime` for reasoning tasks. Don’t route deterministic operations through the LLM.
+ Include any developer tools your commands depend on (such as `git` , `npm` , or language runtimes) in your container image via your Dockerfile.
+ Always check the `exitCode` in the `contentStop` event to determine if the command succeeded.
+ Set appropriate timeouts. A test suite might need 5 minutes, while a `git push` might only need 30 seconds.
+ Process streaming output incrementally to detect failures early. You can cancel a long-running command rather than waiting for it to complete.
+ Encode state in the command itself using `&&` chaining (for example, `cd /workspace && export NODE_ENV=test && npm test` ), since each command starts a fresh bash process.
+ Use UUIDs for session IDs to meet the 33-character minimum requirement (for example, `12345678-1234-1234-1234-123456789012` ).

# Observe agents in Amazon Bedrock AgentCore Runtime
Observe agents

For information about the AgentCore Runtime observability metrics, see [Add observability to your Amazon Bedrock AgentCore resources](observability-configure.md).

# Troubleshoot AgentCore Runtime
Troubleshoot

This troubleshooting topic helps you identify and resolve common issues when working with AgentCore Runtime. By following these solutions, you can quickly diagnose and fix problems with your agent runtimes.

**Topics**
+ [

## My agent invocations fail with 504 Gateway Timeout errors
](#troubleshoot-runtime-timeout)
+ [

## My Docker build fails with "403 Forbidden" when pulling Python base images
](#troubleshoot-runtime-docker-403)
+ [

## I get "Unknown service: 'bedrock-agent-core-runtime'" error when using boto3
](#troubleshoot-runtime-boto3)
+ [

## I get "AccessDeniedException" when trying to create an Amazon Bedrock AgentCore Runtime
](#troubleshoot-runtime-access-denied)
+ [

## My Docker build fails with "exec /bin/sh: exec format error"
](#troubleshoot-runtime-exec-format)
+ [

## What are the requirements for Docker containers used with Amazon Bedrock AgentCore Runtime?
](#troubleshoot-runtime-requirements)
+ [

## My long-running tool gets interrupted after 15 minutes
](#troubleshoot-runtime-long-running)
+ [

## How do I access the runtimeSessionId in my agent code for tagging or grouping resources?
](#troubleshoot-runtime-session-id)
+ [

## I have RuntimeClientError (403) issues
](#runtime-client-error)
+ [

## I have missing or empty CloudWatch Logs
](#missing-cloudwatch-logs)
+ [

## I have payload format issues
](#payload-format-issues)
+ [

## I need help understanding HTTP error codes
](#common-error-codes)
+ [

## I need recommendations for testing my agent
](#testing-recommendations)
+ [

## I need help debugging container issues
](#debugging-container-issues)
+ [

## I need help troubleshooting MCP protocol agents
](#troubleshooting-mcp-protocol)
+ [

## I need help troubleshooting bidirectional streaming using WebSocket
](#troubleshooting-websocket-protocol)
+ [

## My code changes aren’t reflected in existing sessions
](#troubleshoot-code-updates)
+ [

## Spans are missing when my runtime is invoked from a Lambda function
](#troubleshoot-runtime-lambda-missing-spans)
+ [

## Best practices
](#best-practices)

## My agent invocations fail with 504 Gateway Timeout errors


 **When this occurs:** During agent invocation via SDK or console

 **Why this happens:** Multiple factors can prevent your agent from responding within the timeout period

Several factors can cause this:
+  **Container Issues:** Make sure your Docker image exposes port 8080 and has the `/invocations` path
+  **ARM64 Compatibility:** Currently your container must be ARM64 compatible
+  **Retry Logic:** Review retry mechanisms for handling transient issues

## My Docker build fails with "403 Forbidden" when pulling Python base images


 **When this occurs:** During `docker build` or `docker run` when using `public.ecr.aws` base images

 **Why this happens:** ECR Public authentication issues — expired or missing authentication is a common issue.

 **Solution:** Either login to ECR Public or logout completely:

```
# Option 1: Login to ECR Public
aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws

# Option 2: Logout (recommended for avoiding token expiration)
docker logout public.ecr.aws

# Option 3: Use Docker Hub directly in Dockerfile
FROM python:3.10-slim
# instead of public.ecr.aws/docker/library/python:3.10-slim
```

## I get "Unknown service: 'bedrock-agent-core-runtime'" error when using boto3


 **When this occurs:** When invoking Amazon Bedrock AgentCore APIs using boto3 SDK

 **Why this happens:** Outdated boto3 library — common issue as most installations don’t have latest SDK

 **Solution:** Update to latest boto3 and botocore versions:

```
pip install --upgrade boto3 botocore

# Minimum versions: boto3 1.39.8+, botocore 1.33.8+
```

## I get "AccessDeniedException" when trying to create an Amazon Bedrock AgentCore Runtime


 **When this occurs:** During agent creation via console, SDK, or CLI

 **Why this happens:** Either your user lacks permissions, or the execution role isn’t properly configured for Amazon Bedrock AgentCore

 **Solution:** Several factors can cause this:
+  **Missing permissions for the caller.** Make sure that the caller’s credentials has `bedrock-agentcore:CreateAgentRuntime`.
+  **Execution Role cannot be assumed by Bedrock Amazon Bedrock AgentCore.** Make sure that the execution role follows this guidance on [permissions for Amazon Bedrock AgentCore Runtime execution role](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html).

## My Docker build fails with "exec /bin/sh: exec format error"


 **When this occurs:** When building containers for Amazon Bedrock AgentCore deployment

 **Why this happens:** Building ARM64 containers on x86 systems without proper cross-platform setup

 **Solution:** Build ARM64 compatible containers. You can consider using [buildx](https://github.com/docker/buildx) for cross-platform builds. Alternatively, you can use CodeBuild. For example code, see the [Amazon Bedrock AgentCore Samples](https://github.com/awslabs/amazon-bedrock-agentcore-samples/).

## What are the requirements for Docker containers used with Amazon Bedrock AgentCore Runtime?


Review [Amazon Bedrock AgentCore Runtime requirements](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-service-contract.html) for full details.

In summary, your Docker container must meet these requirements:
+  **Port:** Expose port 8080 (additional ports will be supported soon)
+  **Endpoint:** Must have `/invocations` path available
+  **Architecture:** Must be ARM64 compatible
+  **Response:** Should handle the expected payload format

## My long-running tool gets interrupted after 15 minutes


For information, see [Handle asynchronous and long running agents with Amazon Bedrock Amazon Bedrock AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-long-run.html) for full details.

 **When this occurs:** During long-running agent operations or complex workflows

 **Why this happens:** Amazon Bedrock AgentCore automatically terminates sessions after 15 minutes of inactivity

 **Example solution:** Implement ping handlers with HEALTHY\$1BUSY status for async tasks:

## How do I access the runtimeSessionId in my agent code for tagging or grouping resources?


 **When this applies:** You want to group, tag, or trace resources (e.g., S3 objects, logs) by the current agent runtime session.

 **Solutions:** 
+ If you’re using the Bedrock Agents SDK, use `context.session_id`.
+ If you’re building a custom runtime server, extract it from the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` HTTP header.

 **Solution 1:** For agents using the Bedrock Amazon Bedrock AgentCore SDK, use `context.session_id` from your agent entrypoint

```
@app.entrypoint
def my_agent(payload, context):
    session_id = context.session_id

    # Use session_id for S3 object tagging/organization
    s3_client = boto3.client('s3')
    s3_client.put_object(
        Bucket='my-bucket',
        Key=f'agent-outputs/{session_id}/output.json',
        Body=json.dumps(result),
        Tagging=f'SessionId={session_id}'
    )
    return result
```

 **Solution 2:** For custom runtime HTTP servers

The runtime session ID is passed in this HTTP header. Parse it from the incoming request and use it for tagging, correlation, or downstream propagation.

```
X-Amzn-Bedrock-AgentCore-Runtime-Session-Id: <value>
```

## I have RuntimeClientError (403) issues


 **Problem** 

You receive a 403 "RuntimeClientError" when attempting to invoke your agent runtime.

 **Causes** 

This error typically occurs due to:
+ Container startup failures
+ Permissions issues with execution role
+ Authentication issues with bearer token

 **Resolution** 

Follow these steps to resolve the issue:

1.  **Check CloudWatch Logs** : Any issues with starting up the container will reflect as a 403 - RuntimeClientError. Navigate to the following CloudWatch log group to check for startup errors:

   ```
   /aws/bedrock-agentcore/runtimes/<agent_id>-<endpoint_name>/[runtime-logs]
   ```

1.  **Verify Execution Role** : Ensure your agent’s execution role has the necessary permissions. For more information, see [AgentCore Runtime execution role](runtime-permissions.md#runtime-permissions-execution-role).

1.  **Validate Authentication** : For MCP protocol agents, ensure your bearer token is valid and not expired.

## I have missing or empty CloudWatch Logs


 **Problem** 

You encounter errors but don’t see any relevant logs in CloudWatch.

 **Solution** 

Try these approaches to diagnose the issue:

1.  **Check Correct Log Group** : Ensure you’re looking in the right CloudWatch log group. The standard pattern is:

   ```
   /aws/bedrock-agentcore/runtimes/<agent_id>-<endpoint_name>/runtime-logs
   ```

1.  **Run Locally for Diagnostics** : If there are no CloudWatch Logs, try running the agent container locally using the exact same payload you used for invocation in AgentCore Runtime. This can help identify issues that might not be visible in the logs.

1.  **Enable Verbose Logging** : Update your agent code to include more detailed logging, especially around the entry points and any error handling logic.

## I have payload format issues


 **Problem** 

Your agent runtime invocation fails even though the container starts successfully.

 **Resolution** 

Follow these steps to resolve payload format issues:

1.  **Verify Payload Structure** : Ensure your payload structure matches what your agent expects. Pay special attention to:
   + If your agent code expects `input` keyword in the payload, make sure to include it:

     ```
     {
         "input": {
             "prompt": "Your question here"
         }
     }
     ```
   + Not just:

     ```
     {
          "prompt": "Your question here"
     }
     ```

1.  **Check Documentation** : Review the expected input format in the documentation.

## I need help understanding HTTP error codes


 **Problem** 

Your agent returns HTTP error codes that are difficult to interpret.

 **Example error message** 

You may see an error like:

```
An error occurred (RuntimeClientError) when calling the InvokeAgentRuntime operation: Received error (<HTTP Status Code>) from runtime. Please check your CloudWatch logs for more information
```

 **Resolution** 

Here are the most common error codes and their meanings:

 **422 Unprocessable Entity**   
This happens when the container encounters validation issues with the input payload.  
Common causes:  
+ Missing required fields in the payload (e.g., missing "input" field)
+ Incorrect data types for fields
+ Invalid format for the payload

 **403 Forbidden**   
Authentication or authorization issues.  
Check your bearer token or IAM permissions.

 **500 Internal Server Error**   
Runtime exceptions in your agent code.  
Check CloudWatch logs for detailed stack traces.

## I need recommendations for testing my agent


To systematically debug agent runtime issues:

 **Test locally first** 

Before deploying to AgentCore Runtime:
+ Run your agent container locally using the same Docker image
+ Verify it works with the exact same payload

 **Compare payloads** 

Ensure consistency between environments:
+ Ensure the payload structure between local testing and AgentCore Runtime invocation is identical
+ Pay special attention to nesting of fields like "input" and "prompt"

## I need help debugging container issues


If you suspect container-related issues:

 **Pull and run locally** 

Test your container image on your local machine:

```
docker pull <your-ecr-repo-uri>
docker run -p 8080:8080 <your-ecr-repo-uri>
```

 **Test with curl** 

Send test requests to your local container:

```
curl -X POST http://localhost:8080/invocations \
     -H "Content-Type: application/json" \
     -d '{"input": {"prompt": "Hello world!"}}'
```

 **Check container logs** 

Examine the container’s output for errors:

```
docker logs <container-id>
```

## I need help troubleshooting MCP protocol agents


For MCP protocol agents, follow these specific troubleshooting steps:

 **Verify endpoint path** 

MCP servers should listen on `0.0.0.0:8000/mcp/` 

 **Use MCP Inspector** 

Test with the MCP Inspector tool:

1. Install and run the MCP Inspector: `npx @modelcontextprotocol/inspector` 

1. Connect to your local server at `http://localhost:8000/mcp` 

1. For deployed agents, use the properly URL-encoded endpoint

 **Authentication issues** 

Check authentication configuration:
+ Ensure bearer token is correctly set in the headers
+ Verify your Cognito user pool is correctly set up

## I need help troubleshooting bidirectional streaming using WebSocket


For bidirectional streaming using WebSocket agents, follow these specific troubleshooting steps:

 **Verify endpoint configuration** 

WebSocket agents must run on port 8080 and serve WebSocket connections at `/ws` path

 **Test locally with incremental complexity** 

Start with simple local testing before deploying:

1. Test basic connection: Verify your agent accepts WebSocket connections at `ws://localhost:8080/ws` 

1. Test message handling: Send simple text messages and verify responses

1. Test session management: Verify persistent conversations work as expected

1. Test error handling: Ensure your agent gracefully handles connection drops and malformed messages

 **Authentication issues** 

Check authentication configuration for deployed agents:
+ For OAuth: Ensure bearer token is valid and not expired
+ For SigV4: Make sure input to the signing algorithm is correct, including the WebSocket URL, headers, and request method
+ Use the correct authentication method that matches your agent’s configuration

 **Common connection issues** 

Address common WebSocket connection problems:
+ Verify message format compatibility between your agent and client expectations
+ Configure message frame fragmentation or implement chunking to stay within message frame size (64 KB) and message frame rate (250 frames per second) limits to prevent connection closure

## My code changes aren’t reflected in existing sessions


 **Problem** 

You’ve updated your agent runtime with new code, but existing sessions continue to use the old version.

 **Why this happens** 

Each microVM session is created with the code assets ( `agentRuntimeArtifact` ) that were deployed at the time of session creation. Once a session is established, it continues using that version of the code until the session terminates, even when code assets are updated as part of performing the [UpdateAgentRuntime](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_UpdateAgentRuntime.html) operation.

 **Solution** 

To access your updated code, use a new session ID.

## Spans are missing when my runtime is invoked from a Lambda function


 **When this occurs:** When invoking AgentCore Runtime from a Lambda function

 **Why this happens:** Lambda generates its own `X-Amzn-Trace-Id` header. If the Lambda trace has `Sampled=0` , this unsampled context propagates to AgentCore Runtime and the runtime skips span generation for that invocation.

 **Solution:** 
+  **Enable Lambda active tracing:** Turn on X-Ray active tracing on your Lambda function so that it produces sampled traces ( `Sampled=1` ).
+  **Verify CloudWatch Transaction Search:** Ensure you have completed the setup in [Configure observability](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-configure.html) and that your trace segment destination is set to CloudWatch Logs.
+  **Check the sampling decision:** Log the `_X_AMZN_TRACE_ID` environment variable inside your Lambda function. If it shows `Sampled=0` , active tracing is not enabled or an upstream caller is making the sampling decision.

## Best practices


 **Enable comprehensive logging** 

Implement thorough logging in your agent:
+ Include request/response logging in your agent
+ Log critical paths and error conditions

 **Use structured error handling** 

Implement clear error reporting:
+ Return clear error messages with specific codes
+ Include actionable information in error responses

 **Test incremental changes** 

Follow a methodical testing approach:
+ When modifying your agent, test locally before deployment
+ Validate payload compatibility with both local and deployed environments

 **Monitor performance** 

Set up monitoring for your agent:
+ Use CloudWatch metrics to track invocation patterns
+ Set up alarms for error rates and latency