Tools: Deploying AG-UI Agents to Production with Amazon Bedrock AgentCore (2026)

Tools: Deploying AG-UI Agents to Production with Amazon Bedrock AgentCore (2026)

Amazon Bedrock AgentCore Runtime

AG-UI: the interaction protocol

How It All Connects

Deploying your AG-UI agent with Strands

Step 1: Build your AG-UI server

Step 2: Test locally

Step 3: Deploy to AgentCore

Step 4: Invoke your deployed agent

Step 5: Connect your frontend

What This Means

CopilotKit Follow The Agent-User Interaction Protocol (AG-UI) standardizes how AI agents communicate with user applications. But deploying those agents means the same manual work: configuring SSE endpoints, writing auth middleware, managing session isolation, and setting up auto-scaling. None of that is your agent logic. All of it had to be built before you could ship. Amazon recently announced native AG-UI support in Amazon Bedrock AgentCore Runtime across 14 AWS regions. You bring your agent code in a container, set one protocol flag, and AgentCore handles the rest. So I deployed Strands Agents to learn how it actually works end-to-end. This post covers everything I picked up, including what AgentCore is and the full deployment flow from a local server to a live UI. Amazon Bedrock AgentCore is an agentic platform for building, deploying, and operating agents securely at scale. AgentCore Runtime is a fully managed hosting environment. You bring your agent code in a container, and AgentCore checks who's allowed in (auth), keeps each user's session separate (Session isolation), adds capacity when traffic spikes (auto-scaling) and logs everything to CloudWatch (observability). You don't write any of this code. It's also framework-agnostic: agents built with Strands, LangGraph, CrewAI, or any other framework can deploy to AgentCore. Think of it as the production layer between your agent and your users. It supports four protocols: You can read more about the differences in message format, discovery and authentication in their docs. You configure with --protocol AGUI and the protocol layer is taken care of. Your job is just the agent logic. AG-UI (Agent-User Interaction Protocol) is an open, event-based protocol that standardises how AI agents communicate with user-facing applications. Instead of a custom WebSocket or polling layer, AG-UI defines a shared event stream between your agent backend and your frontend UI. It covers: In practice, when your agent responds, the frontend receives a sequence of typed events: Any AG-UI-compatible frontend, including CopilotKit, can consume that stream directly. In the next section, we will see how it all connects through an architecture diagram. CopilotKit is an AG-UI-compatible React client. It connects to an AG-UI endpoint, subscribes to the event stream and renders streaming responses, tool progress, and state updates inside your app. Once your agent is deployed to AgentCore, HttpAgent points to the AgentCore invocation URL with a Cognito Bearer token in the headers, and user messages flow back over AG-UI into the running agent. Here is what the full stack looks like when deployed: If you want a complete full-stack starting point, AWS also released the Fullstack AgentCore Solution Template - a ready-made stack with AgentCore, LangGraph, CopilotKit and AG-UI all wired together, deployed via CDK. It's the fastest way to go from zero to a production-ready agentic app on AWS. This example uses AWS Strands as the agent framework but the same pattern works with LangGraph, CrewAI and any other AG-UI-compatible framework. Before deploying, make sure you have: Start by installing the required packages: Create my_agui_server.py. This defines the model, wraps the agent with AG-UI support, and exposes the endpoints AgentCore needs. Here, StrandsAgent wraps your Strands agent and handles all AG-UI event encoding so you don't write any protocol logic yourself. AgentCore requires three things from your container: For LangGraph, CrewAI, and other frameworks, see the AG-UI framework integrations. The server starts on http://localhost:8080. Send a test request to confirm the SSE stream is working before deploying. You should see the SSE events stream back: The AgentCore starter toolkit handles the entire deployment process for you. It builds an ARM64 container image, pushes it to Amazon ECR, and creates the AgentCore runtime with protocol flag serverProtocol: "AGUI". You don't write a Dockerfile or configure any infrastructure manually. Create a requirements.txt so the toolkit knows what to install in the container: Now configure and deploy. The configure command sets the protocol to AG-UI and links your Cognito auth setup. The deploy command does the rest: After a successful deploy, you receive an agent runtime ARN. You will need this to invoke the agent and connect your frontend. With the agent running on AgentCore, you can now invoke it and stream AG-UI events over the wire. Set your environment variables first: Install the HTTP client libraries for making SSE requests: Then invoke the agent: You should see the agent's response stream token by token. The same AG-UI events as local testing, now served from AgentCore with managed authentication, session isolation, and auto-scaling. With the agent deployed, point HttpAgent from @ag-ui/client at the AgentCore invocation URL instead of localhost. The rest of the client code stays the same. This is the same AG-UI client used locally - you are just pointing it at the AgentCore URL instead of localhost. For SigV4 authentication, sign requests using the AWS SDK. For OAuth 2.0, obtain a token from your configured Cognito user pool. Authentication configuration is the same across all AgentCore protocols. See the AgentCore auth docs for setup. AG-UI support in AgentCore is available today across 14 AWS regions. You can start building right away. Explore the AgentCore AG-UI docs Explore the AG-UI Protocol Docs Check out the AWS Announcement If you want a complete full-stack starting point, AWS also released the Fullstack AgentCore Solution Template - AgentCore, LangGraph, CopilotKit, and AG-UI all wired together and deployed via CDK. AG-UI works with LangGraph, CrewAI, AWS Strands, Google ADK, Mastra, Microsoft Agent Framework, LlamaIndex, Agno, and more. The protocol itself isn't tied to any of them - you pick the framework, AG-UI handles the interaction layer. What was missing was a managed way to run these agents in production. That's what this support adds. Beyond AG-UI hosting, AgentCore also covers persistent memory across sessions, tool connectivity via MCP Gateway, and production observability (though I didn't explore all of these in-depth). You can connect me on GitHub, Twitter and LinkedIn. Follow CopilotKit on Twitter and say hi, and if you'd like to build something cool, join the Discord community. Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Code Block

Copy

data: {"type": "RUN_STARTED", "threadId": "test-123", "runId": "run-456"} data: {"type": "TEXT_MESSAGE_START", "messageId": "msg-abc", "role": "assistant"} data: {"type": "TEXT_MESSAGE_CONTENT", "messageId": "msg-abc", "delta": "Hello!"} data: {"type": "TEXT_MESSAGE_END", "messageId": "msg-abc"} data: {"type": "RUN_FINISHED", "threadId": "test-123", "runId": "run-456"} data: {"type": "RUN_STARTED", "threadId": "test-123", "runId": "run-456"} data: {"type": "TEXT_MESSAGE_START", "messageId": "msg-abc", "role": "assistant"} data: {"type": "TEXT_MESSAGE_CONTENT", "messageId": "msg-abc", "delta": "Hello!"} data: {"type": "TEXT_MESSAGE_END", "messageId": "msg-abc"} data: {"type": "RUN_FINISHED", "threadId": "test-123", "runId": "run-456"} data: {"type": "RUN_STARTED", "threadId": "test-123", "runId": "run-456"} data: {"type": "TEXT_MESSAGE_START", "messageId": "msg-abc", "role": "assistant"} data: {"type": "TEXT_MESSAGE_CONTENT", "messageId": "msg-abc", "delta": "Hello!"} data: {"type": "TEXT_MESSAGE_END", "messageId": "msg-abc"} data: {"type": "RUN_FINISHED", "threadId": "test-123", "runId": "run-456"} Your agent framework [Strands · LangGraph · CrewAI] │ │ deployed to ▼ Amazon Bedrock AgentCore Runtime [Bearer token via Cognito OAuth 2.0] │ auth · session isolation · auto-scaling · observability │ │ invokes container ▼ Agent container [ARM64 · port 8080] │ FastAPI · /invocations · /ping │ │ calls model ▼ Amazon Bedrock model │ │ streams back via ▼ AG-UI protocol [SSE event stream] │ │ consumed by ▼ CopilotKit [HttpAgent · @ag-ui/client] │ ├─ renders streaming responses + tool progress ├─ syncs state updates in real time └─ forwards user messages → agent via AG-UI ▼ Your application UI Your agent framework [Strands · LangGraph · CrewAI] │ │ deployed to ▼ Amazon Bedrock AgentCore Runtime [Bearer token via Cognito OAuth 2.0] │ auth · session isolation · auto-scaling · observability │ │ invokes container ▼ Agent container [ARM64 · port 8080] │ FastAPI · /invocations · /ping │ │ calls model ▼ Amazon Bedrock model │ │ streams back via ▼ AG-UI protocol [SSE event stream] │ │ consumed by ▼ CopilotKit [HttpAgent · @ag-ui/client] │ ├─ renders streaming responses + tool progress ├─ syncs state updates in real time └─ forwards user messages → agent via AG-UI ▼ Your application UI Your agent framework [Strands · LangGraph · CrewAI] │ │ deployed to ▼ Amazon Bedrock AgentCore Runtime [Bearer token via Cognito OAuth 2.0] │ auth · session isolation · auto-scaling · observability │ │ invokes container ▼ Agent container [ARM64 · port 8080] │ FastAPI · /invocations · /ping │ │ calls model ▼ Amazon Bedrock model │ │ streams back via ▼ AG-UI protocol [SSE event stream] │ │ consumed by ▼ CopilotKit [HttpAgent · @ag-ui/client] │ ├─ renders streaming responses + tool progress ├─ syncs state updates in real time └─ forwards user messages → agent via AG-UI ▼ Your application UI pip install fastapi uvicorn ag_ui_strands strands-agents boto3 pip install fastapi uvicorn ag_ui_strands strands-agents boto3 pip install fastapi uvicorn ag_ui_strands strands-agents boto3 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 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.", ) agui_agent = StrandsAgent( agent=strands_agent, name="my_agent", description="A helpful assistant", ) app = FastAPI() @app.post("/invocations") async def invocations(input_data: dict, request: Request): 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) 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 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.", ) agui_agent = StrandsAgent( agent=strands_agent, name="my_agent", description="A helpful assistant", ) app = FastAPI() @app.post("/invocations") async def invocations(input_data: dict, request: Request): 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) 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 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.", ) agui_agent = StrandsAgent( agent=strands_agent, name="my_agent", description="A helpful assistant", ) app = FastAPI() @app.post("/invocations") async def invocations(input_data: dict, request: Request): 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) python my_agui_server.py python my_agui_server.py python my_agui_server.py curl -N -X POST http://localhost:8080/invocations \ -H "Content-Type: application/json" \ -d '{ "thread_id": "test-123", "run_id": "run-456", "state": {}, "messages": [{"role": "user", "content": "Hello!", "id": "msg-1"}], "tools": [], "context": [], "forwardedProps": {} }' curl -N -X POST http://localhost:8080/invocations \ -H "Content-Type: application/json" \ -d '{ "thread_id": "test-123", "run_id": "run-456", "state": {}, "messages": [{"role": "user", "content": "Hello!", "id": "msg-1"}], "tools": [], "context": [], "forwardedProps": {} }' curl -N -X POST http://localhost:8080/invocations \ -H "Content-Type: application/json" \ -d '{ "thread_id": "test-123", "run_id": "run-456", "state": {}, "messages": [{"role": "user", "content": "Hello!", "id": "msg-1"}], "tools": [], "context": [], "forwardedProps": {} }' data: {"type": "RUN_STARTED", "threadId": "test-123", "runId": "run-456"} data: {"type": "TEXT_MESSAGE_START", "messageId": "msg-abc", "role": "assistant"} data: {"type": "TEXT_MESSAGE_CONTENT", "messageId": "msg-abc", "delta": "Hello!"} data: {"type": "TEXT_MESSAGE_END", "messageId": "msg-abc"} data: {"type": "RUN_FINISHED", "threadId": "test-123", "runId": "run-456"} data: {"type": "RUN_STARTED", "threadId": "test-123", "runId": "run-456"} data: {"type": "TEXT_MESSAGE_START", "messageId": "msg-abc", "role": "assistant"} data: {"type": "TEXT_MESSAGE_CONTENT", "messageId": "msg-abc", "delta": "Hello!"} data: {"type": "TEXT_MESSAGE_END", "messageId": "msg-abc"} data: {"type": "RUN_FINISHED", "threadId": "test-123", "runId": "run-456"} data: {"type": "RUN_STARTED", "threadId": "test-123", "runId": "run-456"} data: {"type": "TEXT_MESSAGE_START", "messageId": "msg-abc", "role": "assistant"} data: {"type": "TEXT_MESSAGE_CONTENT", "messageId": "msg-abc", "delta": "Hello!"} data: {"type": "TEXT_MESSAGE_END", "messageId": "msg-abc"} data: {"type": "RUN_FINISHED", "threadId": "test-123", "runId": "run-456"} pip install bedrock-agentcore-starter-toolkit pip install bedrock-agentcore-starter-toolkit pip install bedrock-agentcore-starter-toolkit fastapi uvicorn ag_ui_strands fastapi uvicorn ag_ui_strands fastapi uvicorn ag_ui_strands # Configure with AG-UI protocol + OAuth authentication agentcore configure -e my_agui_server.py --protocol AGUI # Deploy to AWS agentcore deploy # Configure with AG-UI protocol + OAuth authentication agentcore configure -e my_agui_server.py --protocol AGUI # Deploy to AWS agentcore deploy # Configure with AG-UI protocol + OAuth authentication agentcore configure -e my_agui_server.py --protocol AGUI # Deploy to AWS agentcore deploy arn:aws:bedrock-agentcore:us-west-2:ACCOUNT_ID:runtime/my_agui_server-xyz123 arn:aws:bedrock-agentcore:us-west-2:ACCOUNT_ID:runtime/my_agui_server-xyz123 arn:aws:bedrock-agentcore:us-west-2:ACCOUNT_ID:runtime/my_agui_server-xyz123 export BEARER_TOKEN="<your-cognito-bearer-token>" export AGENT_ARN="arn:aws:bedrock-agentcore:us-west-2:ACCOUNT_ID:runtime/my_agui_server-xyz123" export BEARER_TOKEN="<your-cognito-bearer-token>" export AGENT_ARN="arn:aws:bedrock-agentcore:us-west-2:ACCOUNT_ID:runtime/my_agui_server-xyz123" export BEARER_TOKEN="<your-cognito-bearer-token>" export AGENT_ARN="arn:aws:bedrock-agentcore:us-west-2:ACCOUNT_ID:runtime/my_agui_server-xyz123" pip install httpx httpx-sse pip install httpx httpx-sse pip install httpx httpx-sse 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 from production!")) 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 from production!")) 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 from production!")) import { HttpAgent } from "@ag-ui/client"; const agent = new HttpAgent({ url: `https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/${encodedArn}/invocations?qualifier=DEFAULT`, headers: { "Authorization": `Bearer ${token}`, "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": sessionId, } }); import { HttpAgent } from "@ag-ui/client"; const agent = new HttpAgent({ url: `https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/${encodedArn}/invocations?qualifier=DEFAULT`, headers: { "Authorization": `Bearer ${token}`, "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": sessionId, } }); import { HttpAgent } from "@ag-ui/client"; const agent = new HttpAgent({ url: `https://bedrock-agentcore.us-west-2.amazonaws.com/runtimes/${encodedArn}/invocations?qualifier=DEFAULT`, headers: { "Authorization": `Bearer ${token}`, "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": sessionId, } }); - HTTP: default agent invocation - MCP: agent ↔ tools - A2A: agent ↔ agent - AG-UI: agent ↔ user (new) - Streaming text responses as they generate - Tool call lifecycle (start to result) - State updates and progress - User interactions sent back into the agent mid-execution - Python 3.12+ - AWS account with credentials configured (aws configure) - IAM permissions for AgentCore Runtime - Docker, Finch, or Podman for building container images - Amazon Cognito user pool configured (Setup guide here) - ag_ui_strands wraps your Strands agent with AG-UI protocol support, handling event encoding automatically. - strands-agents is the AWS agent framework used in this example. - boto3 is the AWS SDK, used internally by Strands to call Bedrock models. - POST /invocations - handles AG-UI requests, returns the SSE event stream. - GET /ping - health check, must return HTTP 200. - Port 8080 - required, do not change. - Explore the AgentCore AG-UI docs - Explore the AG-UI Protocol Docs - Check out the AWS Announcement