DEV Community

Jonathan Vogel for AWS

Posted on • Originally published at builder.aws.com

How to Build and Deploy an AI Agent on AWS with Bedrock, Strands, and AgentCore

A foundational look at building and deploying an AI agent on AWS, from a single model call to a managed cloud endpoint, so you understand what Bedrock, Strands and AgentCore each do and how they fit together.

If you prefer video format, check out this content on our YouTube channel.

TL;DR

  • What you build: the same question, "What should I make for dinner?", answered three ways. A raw model call, a local agent with one tool, then that same agent deployed to AWS. Each step shows what the next AWS layer adds.
  • The three layers: Amazon Bedrock is the model, the brain. Strands is the harness that gives it a tool and a loop. Amazon Bedrock AgentCore runs it in production.
  • What it costs: close to nothing. Chapters 1 and 2 are just local Node and a Bedrock call. Chapter 3 creates real resources you tear down at the end. Free Tier eligible accounts can cover it, and new AWS users can get up to $200 in credits.
  • Time: 30 minutes to breeze thru or 1+ hour if you're really taking your time to unpack each piece.

I asked an AI model what I should make for dinner. It gave me some suggestions and it asked me what ingredients I had on hand.

Duh! This is a critical question to get an idea of what to suggest and it had no idea. I had eggs, spinach, garlic, rice and some cheddar around the kitchen and the model couldn't see any of it. A sharp brain with no access to my world, getting one shot to guess.

That gap is the whole story of this post. A model on its own is smart and blind. To make it useful you wrap it in a harness that gives it tools and a loop. Then, once it works on your machine, you hit the next wall: running it for other people, around the clock, without babysitting a server.

I'll answer the same question three times, "What should I make for dinner?", and change what sits behind it. First a raw model call with nothing else. Then a local pantry chef agent with a single tool, get_pantry, that can actually check the kitchen. Then that same agent running on AWS. The tool only shows up in chapters two and three, once there's an agent to use it.

Here's the progression:

  1. Raw Amazon Bedrock. Just the model. It answers, but it can't see the pantry.
  2. A local Strands agent. Add one tool and a loop. Now it checks the pantry and grounds the answer.
  3. The same agent on Amazon Bedrock AgentCore Runtime. Deployed to AWS. Now running in the cloud from a managed endpoint anyone you authorize can call.

By the end you'll have run all three and you'll know which AWS piece does what.

What's in this post

The mental model

One idea to hold onto before we write any code.

An agent is a model plus a harness. The model is the brain, the part that reads your request and reasons about it. The harness is the code around the brain that gives it tools, instructions and a loop to use them.

Image showing diagram: model + harness = agent.

Map that onto AWS:

  • Amazon Bedrock gives you the brain. It's a managed, serverless way to call top models from Anthropic, Meta and others, with no GPUs to rent and no servers to run. You pick a model, send a prompt, get a response.
  • Strands is the harness. It's an open source SDK from AWS that runs the tool-calling loop for you and works with almost any model.
  • AgentCore is where the finished agent runs in production. Managed, serverless hosting for the agent, plus the extras it needs to operate out there like memory, a gateway to your APIs, observability and more.

Three layers that snap together. Pick your brain, build your agent, run it for real. The rest of this post is that sentence, in code.

Prerequisites

  • An AWS account. A personal one is fine. Free Tier eligible accounts can cover this whole thing, and new AWS users can get up to $200 in credits.
  • Bedrock model access. I use Claude in this demo, but you can use whatever model you want. Make sure it's enabled for your preferred Region in the Amazon Bedrock console.
  • Node.js 22 or newer. I ran v24. Check with node --version.
  • AWS CLI v2, configured. Run aws configure (or SSO), set a default Region, then confirm with aws sts get-caller-identity. If that returns your account, you're good. Install guide here.
  • A Region. I used us-east-1 for everything. The us.* model inference profiles resolve there and AgentCore Runtime is available there. If you pick a different Region, re-check that your model and AgentCore Runtime both exist in it before you start.

For Chapter 3 (the deploy), you also need two more things. You don't need them for Chapters 1 and 2, so you can install them later:

  • AWS CDK, installed globally with npm install -g aws-cdk.
  • A one-time CDK bootstrap of your account and Region. More on this when we get there. The short version: it creates a small CloudFormation stack in your account called CDKToolkit. It lives in the cloud, not in your project folder, and you only do it once.
  • The AgentCore CLI, installed with npm install -g @aws/agentcore.

Why this matters: Chapters 1 and 2 run on nothing but Node and your AWS credentials. If you only want to see a model answer and then an agent ground that answer, you can stop after Chapter 2 and never install the CDK or the AgentCore CLI.

Chapter 1: Raw Bedrock, a brain with no eyes

For this we're simply calling the model directly. We get a good answer but it ultimately ends by asking what's in your kitchen. That missing context is the gap the next chapter closes.

About the model and why your output will look different from mine. Every code sample here uses us.anthropic.claude-sonnet-5, the US cross-region inference profile for Claude Sonnet 5. One model across all three chapters keeps the comparison honest. Just know that model output is non-deterministic. Ask "What should I make for dinner?" twice and you might get the same dish in different words, or a different dish entirely. That's expected. Your recipe might not match mine and two of your own runs might not match either.

Set up the project

mkdir 01-bedrock-raw && cd 01-bedrock-raw
npm init -y
npm pkg set type=module
npm install @aws-sdk/client-bedrock-runtime
npm install -D tsx typescript @types/node
Enter fullscreen mode Exit fullscreen mode

npm pkg set type=module matters. The code uses ES module import syntax and a top-level await, and that flag tells Node to treat the file as a module. We run TypeScript directly with tsx, so there's no separate compile step.

The whole file

Create bedrock.ts:

import { BedrockRuntimeClient, ConverseCommand } from '@aws-sdk/client-bedrock-runtime'

const client = new BedrockRuntimeClient({ region: 'us-east-1' })

const response = await client.send(new ConverseCommand({
  modelId: 'us.anthropic.claude-sonnet-5',
  messages: [
    { role: 'user', content: [{ text: 'What should I make for dinner?' }] },
  ],
}))

console.log(response.output?.message?.content?.[0]?.text)
Enter fullscreen mode Exit fullscreen mode

Let's walk through and explain each part.

The client points at Bedrock in one Region:

const client = new BedrockRuntimeClient({ region: 'us-east-1' })
Enter fullscreen mode Exit fullscreen mode

ConverseCommand is the Converse API, one consistent way to talk to any chat model on Bedrock. You name the model and hand it a list of messages:

const response = await client.send(new ConverseCommand({
  modelId: 'us.anthropic.claude-sonnet-5',
  messages: [
    { role: 'user', content: [{ text: 'What should I make for dinner?' }] },
  ],
}))
Enter fullscreen mode Exit fullscreen mode

Then you dig the text out of the response. The path looks fussy because a message can hold more than one content block, so you reach for the first one:

console.log(response.output?.message?.content?.[0]?.text)
Enter fullscreen mode Exit fullscreen mode

Run it

npx tsx bedrock.ts
Enter fullscreen mode Exit fullscreen mode

What comes back

You get a helpful, generic answer. Here's a trimmed run (yours will differ, and the full list is longer):

# Dinner Ideas

I'd love to help! To give you good suggestions, it helps to know a bit more:

- What ingredients do you have on hand (or are willing to shop for)?
- How much time do you want to spend cooking?
- Any dietary preferences/restrictions?

...

Let me know what you've got in the fridge/pantry, and I can suggest something more specific!
Enter fullscreen mode Exit fullscreen mode

Read that last line again. The model is asking me what's in my kitchen. It has no way to know so it makes some recommendations inspired by its training data and leaves us hanging a bit. Nothing is wrong with the model. It's sharp. It's just blind and it gets one shot to answer.

Chapter 2: A local Strands agent, the loop that grounds the answer

Now the same brain gets one tool and a loop. It checks the pantry before it answers and the reply changes completely.

Set up the project

mkdir 02-strands-agent && cd 02-strands-agent
npm init -y
npm pkg set type=module
npm install @strands-agents/sdk
npm install -D tsx typescript @types/node
Enter fullscreen mode Exit fullscreen mode

The whole file

Create agent.ts:

import { Agent, tool, BedrockModel } from '@strands-agents/sdk'

const getPantry = tool({
  name: 'get_pantry',
  description: 'Return the ingredients the user has at home right now.',
  callback: () => ['eggs', 'spinach', 'garlic', 'rice', 'cheddar cheese'],
})

const agent = new Agent({
  model: new BedrockModel({ modelId: 'us.anthropic.claude-sonnet-5', region: 'us-east-1' }),
  tools: [getPantry],
  systemPrompt:
    'Suggest a recipe to make, check the pantry',
})

await agent.invoke('What should I make for dinner?')
Enter fullscreen mode Exit fullscreen mode

Still short. The last line is the same request from Chapter 1. Everything above it is the harness. Let's break down the three pieces that matter.

The tool. This is the agent's connection to my world:

const getPantry = tool({
  name: 'get_pantry',
  description: 'Return the ingredients the user has at home right now.',
  callback: () => ['eggs', 'spinach', 'garlic', 'rice', 'cheddar cheese'],
})
Enter fullscreen mode Exit fullscreen mode

The description is not a comment. The model reads it to decide when to call the tool, so write it for the model. This tool takes no arguments, so there's nothing else to declare. The callback is the code that runs when the model calls the tool. Mine returns a hardcoded array, which is perfect for a demo. In a real app this is where you might hit a database or an API.

The agent. Model, tools, instructions, wired together:

const agent = new Agent({
  model: new BedrockModel({ modelId: 'us.anthropic.claude-sonnet-5', region: 'us-east-1' }),
  tools: [getPantry],
  systemPrompt:
    'Suggest a recipe to make, check the pantry',
})
Enter fullscreen mode Exit fullscreen mode

Same model as Chapter 1, on purpose, so you can see the brain didn't change. I pass it explicitly here, though the Strands TS SDK defaults to a Bedrock Claude Sonnet model if you leave it out. The systemPrompt tells the agent what to do and points it at the tool. The tools array is the list it's allowed to reach for.

The invocation. One line:

await agent.invoke('What should I make for dinner?')
Enter fullscreen mode Exit fullscreen mode

No orchestration code. That's the part worth pausing on.

What the loop actually does

When you call invoke, Strands runs a cycle you didn't have to write:

  1. The model reads the request and reasons about it.
  2. It decides to call get_pantry.
  3. Strands runs the tool and feeds the result back to the model.
  4. The model looks at the ingredients and decides if it's done. If not, it goes again.

That cycle is the agentic loop. The whole reason to use an SDK like Strands is that you get the loop, the tool calling and the message plumbing for free. For a simple agent, all you need to bring is a tool and a prompt.

Diagram showing the agentic loop: model reasoning, model picking tool, harness runs tool, results return

Run it

npx tsx agent.ts
Enter fullscreen mode Exit fullscreen mode

What comes back

The Strands TypeScript SDK ships with a console printer that's on by default, so you see the agent think, call the tool and answer, with no logging code from you. A representative run (again, wording will vary):

Terminal running npx tsx agent.ts where we see the agent call the pantry tool and return a recipe

Same brain. Same question. Completely different answer. It saw the eggs, spinach, garlic, rice and cheddar, and it built a real recipe around them instead of asking me what I had. I didn't write a loop, a parser or an orchestrator. I gave the model a tool and let Strands run the back and forth.

That's a working agent. On my laptop. Which is exactly where the next problem starts.

Chapter 3: Deploy to AgentCore Runtime

My agent ran great on my machine. Then I thought about letting other people use it. Now I'm thinking about hosting, scaling and keeping it healthy when more than one person shows up at once. I did not want to write and operate a web server just to expose one function.

That's what Amazon Bedrock AgentCore Runtime handles. It's a managed, serverless way to run your agent in production. You bring the agent you already wrote, the CLI wraps it and ships it, and you get an endpoint back. Same agent logic, no server for you to run.

Same pantry chef from Chapter 2, now put behind a managed endpoint with the AgentCore CLI.

Install the deploy tooling

npm install -g @aws/agentcore aws-cdk
agentcore --version   # I had 0.21.1
cdk --version         # I had 2.1128.1
Enter fullscreen mode Exit fullscreen mode

Bootstrap once (this is the "we bootstrap this thing" step)

AgentCore deploys through the AWS CDK, and the CDK needs a one-time setup per account and Region called a bootstrap:

cdk bootstrap aws://<ACCOUNT_ID>/us-east-1
Enter fullscreen mode Exit fullscreen mode

Swap in your 12-digit account ID. This creates a CloudFormation stack named CDKToolkit and a small supporting S3 bucket. You only do this once per account and Region, so if you've bootstrapped here before you can skip it. To check:

aws cloudformation describe-stacks --region us-east-1 --stack-name CDKToolkit
Enter fullscreen mode Exit fullscreen mode

If that returns a stack with status CREATE_COMPLETE, you're already bootstrapped. More on bootstrapping here.

Scaffold the project

The agentcore create command scaffolds a new agent project. It can walk you through an interactive wizard, but I'll pass the options directly so the step is repeatable and you can see exactly what we picked:

agentcore create \
  --project-name PantryChef --name PantryChef --type create \
  --build CodeZip --language TypeScript --framework Strands \
  --model-provider Bedrock --memory none --skip-git
Enter fullscreen mode Exit fullscreen mode

Those flags say: a TypeScript project called PantryChef, built as a CodeZip (your code shipped as a zip), on the Strands framework with Bedrock as the model provider and no memory feature for now. It runs npm install under the hood, so give it a moment. When it's done you have a PantryChef/ directory that looks like this:

PantryChef/
  agentcore/
    agentcore.json          # runtime config: CodeZip, NODE_22, PUBLIC, HTTP
    cdk/                    # the CDK app the CLI deploys for you
  app/PantryChef/
    main.ts                 # entrypoint: wraps your agent in a runtime handler
    model/load.ts           # the model config lives HERE, not in main.ts
    mcp_client/client.ts    # an example MCP client, unused by our agent
    package.json
    tsconfig.json
Enter fullscreen mode Exit fullscreen mode

The scaffold is a sample agent, not our agent

Here's the thing the video does off camera. agentcore create does not hand you a blank project. It generates a working sample agent, and the sample is not the pantry chef. Two files ship with content you have to replace.

First, the model. Open app/PantryChef/model/load.ts and you'll see this:

import { BedrockModel } from '@strands-agents/sdk/models/bedrock';

export function loadModel(): BedrockModel {
  return new BedrockModel({ modelId: 'global.anthropic.claude-sonnet-4-5-20250929-v1:0' });
}
Enter fullscreen mode Exit fullscreen mode

That's a real, pinned model ID, and it is not the one this demo uses. The scaffold defaults to Claude Sonnet 4.5. We've been running Sonnet 5 everywhere, so this file has to change.

Second, the agent itself. Open app/PantryChef/main.ts and you'll find a sample that adds two numbers and wires up an example MCP client:

import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
import { Agent, McpClient, tool, type ToolList } from '@strands-agents/sdk';
import { z } from 'zod';
import { loadModel } from './model/load.js';
import { getStreamableHttpMcpClient } from './mcp_client/client.js';

// Define a collection of MCP clients (filter out anything that failed to initialize)
const mcpClients: McpClient[] = [getStreamableHttpMcpClient()].filter(
  (client): client is McpClient => Boolean(client)
);

// Define a collection of tools used by the model
const tools: ToolList = [];

// Define a simple function tool — the Zod schema gives us type inference and runtime validation for free
const addNumbers = tool({
  name: 'add_numbers',
  description: 'Return the sum of two numbers',
  inputSchema: z.object({
    a: z.number(),
    b: z.number(),
  }),
  callback: async ({ a, b }) => a + b,
});
tools.push(addNumbers);

// Add MCP clients to tools
tools.push(...mcpClients);

const SYSTEM_PROMPT = `
You are a helpful assistant. Use tools when appropriate.
`;

// ... the rest of the file (the runtime handler) is shown below
Enter fullscreen mode Exit fullscreen mode

New to MCP? Don't worry about it too much right now. It's a standard way to plug external tools into an agent. The scaffold includes an example client to show it's possible but the pantry chef doesn't need it. We're about to replace the whole sample with our own tool and prompt.

So "the agent logic stays the same" is true for the tool, the prompt and the model, but there's real runtime plumbing around it that the CLI wrote for you. Turning this sample into the pantry chef is exactly two edits.

Edit 1: swap the model in app/PantryChef/model/load.ts

Replace the whole file with this:

import { BedrockModel } from '@strands-agents/sdk/models/bedrock';

export function loadModel(): BedrockModel {
  return new BedrockModel({ modelId: 'us.anthropic.claude-sonnet-5', region: 'us-east-1' });
}
Enter fullscreen mode Exit fullscreen mode

Two changes from the scaffold. The model ID is now us.anthropic.claude-sonnet-5, and I added region: 'us-east-1' so the model resolves in the Region we've been using.

Edit 2: make it the pantry chef in app/PantryChef/main.ts

Replace the whole file with this. It's our get_pantry tool and prompt from Chapter 2, dropped into the runtime handler the CLI generated:

import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
import { Agent, tool, type ToolList } from '@strands-agents/sdk';
import { loadModel } from './model/load.js';

// The one tool this agent has: what is in the kitchen right now.
const getPantry = tool({
  name: 'get_pantry',
  description: 'Return the ingredients the user has at home right now.',
  callback: () => ['eggs', 'spinach', 'garlic', 'rice', 'cheddar cheese'],
});

const tools: ToolList = [getPantry];

const SYSTEM_PROMPT = `
Suggest a recipe to make, check the pantry
`;

let cachedAgent: Agent | null = null;

async function getOrCreateAgent(): Promise<Agent> {
  if (!cachedAgent) {
    const model = await loadModel();
    cachedAgent = new Agent({
      model,
      systemPrompt: SYSTEM_PROMPT,
      tools,
    });
  }
  return cachedAgent;
}

const app = new BedrockAgentCoreApp({
  invocationHandler: {
    async *process(payload: any, context: any) {
      const agent = await getOrCreateAgent();

      for await (const event of agent.stream(payload.prompt ?? '')) {
        if (
          event.type === 'modelStreamUpdateEvent' &&
          event.event?.type === 'modelContentBlockDeltaEvent' &&
          event.event.delta?.type === 'textDelta'
        ) {
          yield { data: event.event.delta.text };
        }
      }
    },
  },
});

app.run({ port: parseInt(process.env.PORT ?? '8080') });
Enter fullscreen mode Exit fullscreen mode

The top half is the Chapter 2 agent, unchanged. Notice there's no zod here. The scaffold imported it for its sample add_numbers tool, which takes arguments that need a schema, but get_pantry takes none. The Strands tool() helper treats inputSchema as optional and defaults to an empty schema, so dropping zod changes nothing about the tool the model sees. The bottom half is the part the CLI gave you, and it's worth understanding because it's what makes this a deployable service instead of a script.

getOrCreateAgent builds the agent once and caches it, so you're not rebuilding it on every request:

let cachedAgent: Agent | null = null;

async function getOrCreateAgent(): Promise<Agent> {
  if (!cachedAgent) {
    const model = await loadModel();
    cachedAgent = new Agent({ model, systemPrompt: SYSTEM_PROMPT, tools });
  }
  return cachedAgent;
}
Enter fullscreen mode Exit fullscreen mode

BedrockAgentCoreApp is the runtime handler. This is the part you'd otherwise hand write as a web server. The process generator receives the incoming request payload, streams the agent's output and yields just the text as it's produced:

const app = new BedrockAgentCoreApp({
  invocationHandler: {
    async *process(payload: any, context: any) {
      const agent = await getOrCreateAgent();
      for await (const event of agent.stream(payload.prompt ?? '')) {
        if (
          event.type === 'modelStreamUpdateEvent' &&
          event.event?.type === 'modelContentBlockDeltaEvent' &&
          event.event.delta?.type === 'textDelta'
        ) {
          yield { data: event.event.delta.text };
        }
      }
    },
  },
});

app.run({ port: parseInt(process.env.PORT ?? '8080') });
Enter fullscreen mode Exit fullscreen mode

Notice what this handler streams: only the text deltas. So a caller sees the recipe but not the get_pantry tool-call line. The tool still runs on the server. You just don't stream that part to the client. Hold that thought for the observability note near the end.

You do not need to run npm run build. Local dev runs your TypeScript directly, and the deploy compiles and bundles during the CDK step. There's no manual build in this workflow.

Run it locally first

The CLI gives you a local server that behaves like the deployed one. Open two terminals, both inside the PantryChef directory.

Terminal 1, start the server (give it a few seconds to come up):

cd PantryChef
agentcore dev --logs
Enter fullscreen mode Exit fullscreen mode

Terminal 2, send the prompt:

agentcore dev "What should I make for dinner?" --stream
Enter fullscreen mode Exit fullscreen mode

You'll see the fried rice recipe stream back in Terminal 2. Over in Terminal 1, the --logs output shows the get_pantry tool firing on the server side, which is the tool call the streamed client output doesn't show.

Deploy it

If you want to see what the deploy will do before it does it, preview first:

agentcore deploy --dry-run
Enter fullscreen mode Exit fullscreen mode

Then ship it:

agentcore deploy -y -v
Enter fullscreen mode Exit fullscreen mode

This takes a minute or so. Under the hood the CLI zips your code and uses the CDK to create a handful of resources: a CloudFormation stack named AgentCore-PantryChef-default, an IAM execution role with its policy and the AWS::BedrockAgentCore::Runtime itself. When it finishes it prints the outputs, which look like this (account ID shown as a placeholder):

Runtime ARN: arn:aws:bedrock-agentcore:us-east-1:111122223333:runtime/PantryChef_PantryChef-xxxxxxxxxx
Runtime ID:  PantryChef_PantryChef-xxxxxxxxxx
Role ARN:    arn:aws:iam::111122223333:role/AgentCore-PantryChef-defa-ApplicationAgentPantryChe-xxxxxxxxxxxx
Stack:       AgentCore-PantryChef-default
Enter fullscreen mode Exit fullscreen mode

Invoke it from the cloud

agentcore invoke "What should I make for dinner?" --stream
Enter fullscreen mode Exit fullscreen mode

A few seconds later, the same kitchen assistant answers, this time from the managed runtime instead of your laptop:

Terminal command agentcore invoke to test our running the agent from a managed endpoint in the cloud

That Session ID at the end is worth noticing. Each invoke without a session ID starts a fresh conversation. Same grounded answer as the laptop, now coming from an endpoint other people can call, with no server for you to run.

Want to check on it later?

agentcore status
# PantryChef: Deployed - Runtime: READY
Enter fullscreen mode Exit fullscreen mode

The four commands, start to finish

That's the whole deploy loop, and it really is four commands once the tooling is in place:

agentcore create   # scaffold: TypeScript, Strands, CodeZip
agentcore dev      # run and test locally
agentcore deploy   # ship to AWS via CDK
agentcore invoke "What should I make for dinner?"
Enter fullscreen mode Exit fullscreen mode

The only thing the video hides between create and dev is the two-file edit you just did by hand.

Who can actually call this thing?

The runtime we deployed uses networkMode: PUBLIC. That phrase sounds alarming, so let's be precise about what it means, because it does not mean an open, anonymous endpoint.

Public here means reachable over the internet, not open to everyone. By default an AgentCore Runtime endpoint is public on the network, but every request has to be authenticated, either with AWS IAM (SigV4) or an OAuth bearer token. With the default IAM setup, "anyone can call it" really means "anyone you grant the bedrock-agentcore:InvokeAgentRuntime permission to." No credentials, no call. It is not a URL a stranger can hit.

If you've read my IAM post, you know where this is going. The execution role and policy the AgentCore CLI generated are fine for a demo, but AWS is explicit that CLI-generated policies are meant for development and testing, not production. Before you put anything real behind this, scope the permissions down to the specific runtime ARN and the specific callers that need it. Least privilege, same as everywhere else in AWS.

Cost and teardown

The deploy left real, billable resources running: the AgentCore runtime, an IAM role and a CloudFormation stack. When you're done experimenting, tear them down. From inside the PantryChef directory:

agentcore remove all -y     # clears the agentcore config
agentcore deploy -y         # applies the removal, tears down the AWS resources
Enter fullscreen mode Exit fullscreen mode

Yes, you run deploy to tear down. The first command empties the config, the second pushes that empty state to AWS, which removes the stack.

Verify it's actually gone:

aws cloudformation describe-stacks --region us-east-1 \
  --stack-name AgentCore-PantryChef-default
# should error: Stack ... does not exist

aws bedrock-agentcore-control list-agent-runtimes --region us-east-1 \
  --query "agentRuntimes[].agentRuntimeName" --output text
# your PantryChef runtime should no longer be listed
Enter fullscreen mode Exit fullscreen mode

Leave the CDKToolkit bootstrap stack in place. It costs almost nothing, it's shared by any CDK work in the account, and you don't want to re-bootstrap next time. Only remove it if you're certain nothing else in that account and Region uses the CDK.

The extras you grow into: memory, gateway, observability

Hosting is the headline, but AgentCore brings more building blocks you pull in when you actually need them. Three worth naming.

  • Memory lets your agent remember people across conversations. My pantry chef could remember that I like spicy food without me saying it every time.
  • Gateway turns APIs and Lambda functions you already have into tools the agent can call, so you're not hand writing every integration. Today get_pantry returns a hardcoded list. A real version would call an API, and Gateway is how you'd wire that up.
  • Observability shows you what the agent actually did. Remember how the streamed output hid the get_pantry tool call? This is where you'd see it, the full trace of the agent's reasoning and tool use, for when something looks off.

You reach for these when your agent needs them. Not before. There's more we didn't cover, like Identity and payments. If you're curious, the Amazon Bedrock AgentCore docs walk through the full set.

The whole stack in one picture

Step back and look at what you built.

  • Bedrock is the brain. It reasons, but on its own it can't see your world.
  • Strands is the harness. It gives the brain a tool and runs the agentic loop, which turns a smart guess into a grounded answer.
  • AgentCore is production. It takes the agent off your laptop and runs it as a managed endpoint, with memory, gateway and observability waiting when you need them.

Three layers that snap together. Pick your brain, build your agent, run it for real. Same pantry chef the whole way, same dinner question, and you watched the answer go from a generic list to a real recipe to that same recipe served from the cloud.

Full AI stack showing bedrock, strands and agentcore

Reproduce this yourself

Everything above runs on AWS today. Here's the checklist I use to confirm a clean run, top to bottom.

  • [ ] aws sts get-caller-identity returns the account you intend to use, and your Region is set.
  • [ ] Bedrock model access is enabled for us.anthropic.claude-sonnet-5 in your Region.
  • [ ] The model resolves: aws bedrock list-inference-profiles --region us-east-1 shows it ACTIVE.
  • [ ] Chapter 1: npx tsx bedrock.ts prints a generic answer that asks what ingredients you have.
  • [ ] Chapter 2: npx tsx agent.ts shows the get_pantry tool call and a recipe built from the pantry.
  • [ ] Deploy tooling: agentcore --version and cdk --version both succeed.
  • [ ] CDK is bootstrapped, or CDKToolkit already shows CREATE_COMPLETE.
  • [ ] agentcore create produced the PantryChef/ scaffold.
  • [ ] Both edits are applied: the model in model/load.ts and the get_pantry tool plus prompt in main.ts.
  • [ ] agentcore dev with --stream returns the grounded recipe locally.
  • [ ] agentcore deploy succeeds and prints the runtime outputs.
  • [ ] agentcore invoke --stream returns the recipe from the cloud and a Session ID.
  • [ ] Teardown done: the stack and the runtime are both gone, and CDKToolkit is left in place.

Your turn

You've watched me type every command so we could see the concepts with nothing in the way. In practice, most people building agents aren't typing this by hand. You might be using an agent to help you move faster, examples include agentic coding tools like Kiro, Claude Code, Codex or something similiar to move faster. Whatever you use, set up the Agent Toolkit for AWS so your agent knows best how to work with AWS.

Build something and ship it. Tell me what your agent does in the comments.


A model on its own is all brains. Make it an agent, give it a tool, a loop and a place to run, and it gets to work.

Top comments (0)