Skip to content

Bedrock AgentCore

Bedrock AgentCore is AWS’s serverless infrastructure for agents — managed runtime with VM-level isolation, code interpreter, cloud browser, identity management, memory, and observability. It is framework-agnostic — it hosts agents built with any framework.

agentfootprint is the framework. AgentCore is the infrastructure. Together: explainable agents on serverless AWS.

┌─────────────────────────────────────────┐
│ Bedrock AgentCore │
│ (runtime, memory, observability, │
│ code interpreter, browser, identity) │
│ │
│ ┌───────────────────────────────────┐ │
│ │ agentfootprint │ │
│ │ (6 patterns, narrative, │ │
│ │ grounding, recorders) │ │
│ │ │ │
│ │ ┌─────────────────────────────┐ │ │
│ │ │ Claude on Bedrock │ │ │
│ │ │ (LLM provider) │ │ │
│ │ └─────────────────────────────┘ │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
Terminal window
npm install agentfootprint bedrock-agentcore @aws-sdk/client-bedrock-runtime zod

AgentCore Runtime hosts your agent as a serverless endpoint. The handler uses an async generator — responses are streamed to clients via SSE.

import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
import { z } from 'zod';
import { Agent, bedrock, defineTool } from 'agentfootprint';
import { agentObservability } from 'agentfootprint/observe';
// 1. Build your agent
const provider = bedrock('anthropic.claude-sonnet-4-20250514-v1:0');
const lookupOrder = defineTool({
id: 'lookup_order',
description: 'Look up an order by ID',
inputSchema: {
type: 'object',
properties: { orderId: { type: 'string' } },
required: ['orderId'],
},
handler: async ({ orderId }) => ({
content: JSON.stringify({ orderId, status: 'shipped', amount: 299 }),
}),
});
const obs = agentObservability();
const agent = Agent.create({ provider })
.system('You are a customer support agent for TechStore.')
.tool(lookupOrder)
.recorder(obs)
.build();
// 2. Create AgentCore app with invocationHandler
const app = new BedrockAgentCoreApp({
invocationHandler: {
requestSchema: z.object({
prompt: z.string(),
sessionId: z.string().optional(),
}),
process: async function* (request, context) {
// context.sessionId available for session management
const result = await agent.run(request.prompt);
// Stream the response (AgentCore uses SSE)
yield {
event: 'message',
data: { text: result.content },
};
// Optionally yield metadata
yield {
event: 'metadata',
data: {
narrative: agent.getNarrative(),
tokens: obs.tokens(),
cost: obs.cost(),
},
};
},
},
});
// 3. Start the server (HTTP on port 8080, serves /invocations and /ping)
app.run();

The async generator pattern (async function* with yield) is fundamental to AgentCore — it enables incremental SSE streaming over /invocations and WebSocket via /ws. The /ping endpoint is served automatically for health checks.

Use AgentCore’s sandboxed code execution as a tool. Sessions must be created and closed:

import { CodeInterpreterTools } from 'bedrock-agentcore/experimental/code-interpreter/strands';
import { defineTool } from 'agentfootprint';
// Note: import path includes /experimental/ — API may change.
// Check bedrock-agentcore docs for current paths.
const codeInterpreter = new CodeInterpreterTools({ region: 'us-east-1' });
const runCode = defineTool({
id: 'run_code',
description: 'Execute Python code in a sandboxed environment. Use for calculations, data analysis, plotting.',
inputSchema: {
type: 'object',
properties: { code: { type: 'string', description: 'Python code to execute' } },
required: ['code'],
},
handler: async ({ code }) => {
// Create a session, execute, close
const session = await codeInterpreter.createSession();
try {
const result = await session.execute(code);
return { content: JSON.stringify(result) };
} finally {
await session.close();
}
},
});
const agent = Agent.create({ provider })
.system('You are a data analyst. Use run_code for calculations.')
.tool(runCode)
.build();

Use AgentCore’s managed cloud browser for web automation. The browser runs in AWS — you connect via CDP WebSocket:

import { PlaywrightBrowser } from 'bedrock-agentcore/browser/playwright';
import { defineTool } from 'agentfootprint';
// Check bedrock-agentcore docs for current import paths and API
const browser = new PlaywrightBrowser({ region: 'us-east-1' });
const browsePage = defineTool({
id: 'browse_page',
description: 'Navigate to a URL and extract page content',
inputSchema: {
type: 'object',
properties: { url: { type: 'string' } },
required: ['url'],
},
handler: async ({ url }) => {
// Connects to AWS-managed browser via CDP WebSocket
const session = await browser.createSession();
try {
const page = await session.newPage();
await page.goto(url);
const text = await page.textContent('body');
return { content: text ?? 'No content found' };
} finally {
await session.close();
}
},
});

AgentCore manages credentials so you don’t put API keys in code. withAccessToken and withApiKey are credential wrappers that inject auth into your functions:

import { withAccessToken } from 'bedrock-agentcore/identity';
import { defineTool } from 'agentfootprint';
const callInternalApi = defineTool({
id: 'call_internal_api',
description: 'Call an internal API with managed credentials',
inputSchema: {
type: 'object',
properties: { endpoint: { type: 'string' } },
required: ['endpoint'],
},
handler: withAccessToken('internal-api', async (token, { endpoint }) => {
// AgentCore injects the token — no API keys in your code
const res = await fetch(endpoint, {
headers: { Authorization: `Bearer ${token}` },
});
return { content: await res.text() };
}),
});

Export agentfootprint’s structured recorder data to CloudWatch:

import { CloudWatch } from '@aws-sdk/client-cloudwatch';
import { agentObservability } from 'agentfootprint/observe';
const cw = new CloudWatch({ region: 'us-east-1' });
const obs = agentObservability();
// After each agent run:
const tokens = obs.tokens();
await cw.putMetricData({
Namespace: 'AgentFootprint',
MetricData: [
{ MetricName: 'LLMCalls', Value: tokens.totalCalls, Unit: 'Count' },
{ MetricName: 'InputTokens', Value: tokens.totalInputTokens, Unit: 'Count' },
{ MetricName: 'OutputTokens', Value: tokens.totalOutputTokens, Unit: 'Count' },
{ MetricName: 'EstCostUSD', Value: obs.cost(), Unit: 'None' },
],
});

agentfootprint events → OpenTelemetry spans

Section titled “agentfootprint events → OpenTelemetry spans”

AgentCore provides built-in OpenTelemetry tracing to CloudWatch. You can also export agentfootprint’s lifecycle events as spans:

import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('agentfootprint');
const spanMap = new Map<string, any>();
await agent.run(message, {
onEvent: (event) => {
if (event.type === 'llm_start') {
spanMap.set(`llm-${event.iteration}`, tracer.startSpan(`llm.call.${event.iteration}`));
}
if (event.type === 'llm_end') {
const span = spanMap.get(`llm-${event.iteration}`);
if (span) {
span.setAttribute('llm.model', event.model ?? 'unknown');
span.setAttribute('llm.latency_ms', event.latencyMs);
span.setAttribute('llm.tool_calls', event.toolCallCount);
span.end();
}
}
if (event.type === 'tool_start') {
const span = tracer.startSpan(`tool.${event.toolName}`);
span.setAttribute('tool.name', event.toolName);
spanMap.set(event.toolCallId, span);
}
if (event.type === 'tool_end') {
const span = spanMap.get(event.toolCallId);
if (span) {
span.setAttribute('tool.latency_ms', event.latencyMs);
span.setAttribute('tool.error', event.error ?? false);
span.end();
}
}
},
});

AgentCore Runtime automatically sends traces and metrics to CloudWatch when deployed. This works alongside agentfootprint’s recorders — AgentCore captures infrastructure metrics (request latency, error rate, cold starts), while agentfootprint captures agent-level metrics (tokens, tool usage, narrative).

The connected narrative is the richest debugging data. Log it alongside AgentCore’s traces:

import { CloudWatchLogs } from '@aws-sdk/client-cloudwatch-logs';
const logs = new CloudWatchLogs({ region: 'us-east-1' });
await logs.putLogEvents({
logGroupName: '/agentfootprint/narrative',
logStreamName: `session-${context.sessionId}`,
logEvents: agent.getNarrative().map((line, i) => ({
timestamp: Date.now() + i,
message: line,
})),
});

AgentCore provides a managed memory service with short-term (session) and long-term (cross-session) memory. The TypeScript SDK memory module is in development — when available, it will provide AWS-managed persistence without running your own database.

For now, use agentfootprint’s pluggable memory with any backend:

import { InMemoryStore } from 'agentfootprint';
// Development: in-memory
const agent = Agent.create({ provider })
.memory({
store: new InMemoryStore(),
conversationId: context.sessionId, // use AgentCore's session ID
})
.build();

For production, implement the store interface backed by DynamoDB:

import { DynamoDBClient, GetItemCommand, PutItemCommand } from '@aws-sdk/client-dynamodb';
class DynamoMemoryStore {
private client = new DynamoDBClient({ region: 'us-east-1' });
async load(conversationId: string) {
const result = await this.client.send(new GetItemCommand({
TableName: 'agent-conversations',
Key: { id: { S: conversationId } },
}));
return result.Item?.messages ? JSON.parse(result.Item.messages.S!) : [];
}
async save(conversationId: string, messages: unknown[]) {
await this.client.send(new PutItemCommand({
TableName: 'agent-conversations',
Item: {
id: { S: conversationId },
messages: { S: JSON.stringify(messages) },
updatedAt: { N: String(Date.now()) },
},
}));
}
}

The AgentCore execution role needs these policies:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BedrockConverse",
"Effect": "Allow",
"Action": [
"bedrock:Converse",
"bedrock:ConverseStream"
],
"Resource": "arn:aws:bedrock:*::foundation-model/anthropic.*"
}
]
}

Add AgentCore permissions as needed for Code Interpreter, Browser, and Identity — check the AgentCore IAM documentation for current action names.


AgentCore providesagentfootprint provides
Serverless runtime with VM isolation6 composable agent patterns
Auto-scaling and deploymentConnected narrative trace
Code interpreter (sandboxed Python)Grounding analysis (hallucination detection)
Cloud browser (managed Playwright)Conditional instructions (Decision Scope)
Identity and credential managementmock() for $0 testing
Managed memory (short + long term)Human-in-the-loop (pause/resume)
Built-in OpenTelemetry observability6 passive recorders + narrative
Health checks and session managementTool gating (defense in depth)
Provider failover (Claude → GPT → Ollama)

AgentCore makes your agent production-ready. agentfootprint makes your agent explainable.