DEV Community

Cover image for AgentCore CLI 1.0: Deploy an AI Agent with TypeScript
Sascha Mirow-Stanitzok
Sascha Mirow-Stanitzok

Posted on Originally published at sascha-ms.de

AgentCore CLI 1.0: Deploy an AI Agent with TypeScript

With the AgentCore CLI 1.0, generally available since October 28, 2026, AWS ships the tool you use to create AI agents on Amazon Bedrock AgentCore, test them locally and deploy them. Many agent experiments never make it off the laptop. That is rarely the model’s fault. Usually the boring part in between is missing: how does the code get into a runtime environment, who is allowed to call it, and where do the logs end up?

That is exactly the part the CLI wants to take off your hands. So I installed version 1.0 and built a small agent for a made-up bookstore with it. In this article I walk you through the whole path in TypeScript: from an empty folder via a local test to a call in AWS – and where you need to be careful with existing projects.

In short

The AgentCore CLI 1.0 scaffolds a project, runs the agent locally and deploys it via CDK to the AgentCore Runtime. For a TypeScript agent with your own tools you need about five commands and two files. The biggest stumbling block: projects from version 0.x have to be moved over with agentcore migrate, and parts of the online docs still describe the old commands.

What you will learn in this article

  • What runtime, harness and memory mean in AgentCore
  • How to create a TypeScript agent with your own tools and test it locally
  • How to ship the agent to AWS with agentcore deploy and call it
  • How to move a 0.x project safely with agentcore migrate
  • What it costs and how to tear everything down again

What is the AgentCore CLI?

Amazon Bedrock AgentCore is the AWS platform meant to run AI agents in production. It consists of several building blocks that you can use on their own or together. The AgentCore CLI is the command-line tool for it: it generates projects, manages these building blocks and takes care of deployment. Under the hood it uses AWS CDK, but you do not have to operate CDK yourself.

Four terms are enough to get started:

  • Runtime: the managed environment for your agent code. Every session gets its own isolated microVM (a very lightweight virtual machine).
  • Harness: an agent you describe through configuration only (model, prompt, tools). AgentCore then runs the agent loop itself, and you write no code.
  • Memory: storage for conversation histories and facts derived from them, so an agent can remember things across sessions.
  • Gateway: exposes APIs and Lambda functions as MCP tools (Model Context Protocol) for agents.

This article takes the path with your own code, which means a runtime. A harness is quicker to set up, but I want to understand what my agent does. Besides, you can later turn a harness into code with agentcore export harness if it becomes too limiting.

What is new in the AgentCore CLI 1.0?

The npm package @aws/agentcore has been around since February 2026 in preview and 0.x versions. According to the announcement, AWS rebuilt the CLI for 1.0: it starts faster, its commands follow one uniform structure, and it covers more resource types. When I tried it, these points stood out:

  • Commands are grouped into three sections: Project (create, dev, deploy, invoke …), Resources (runtime, harness, gateway, memory …) and Settings.
  • You can address existing resources directly by ID or ARN without adding them to a project first, for example with agentcore runtime invoke --id ….
  • The global options --region, --json and --debug work the same way on every command. That makes CI scripts much nicer.
  • If you run agentcore without arguments, an interactive terminal UI with menus and wizards starts.
  • You convert 0.x projects to the new format with agentcore migrate.

A practical note: when I read the online CLI reference, several places still described old options, such as agentcore import instead of migrate. For the first few weeks, rely on agentcore <command> --help instead. I checked every command in this article against version 1.0.1.

AgentCore CLI workflow in five steps: create, dev, deploy, invoke and logs, driven by agentcore.json

The five commands that take an agent from an empty folder all the way to the logs.

Prerequisites

  • Node.js 20.12 or newer (I used Node 22), plus npm.
  • An AWS account with valid credentials. You can check them with aws sts get-caller-identity.
  • IAM permissions for AgentCore and for the CDK bootstrap roles. In a sandbox account an admin role is fine; in company accounts, clarify this first.
  • Access to a Claude model in Amazon Bedrock. The template uses a Claude Sonnet model through a global inference profile.
  • No Docker: the TypeScript template is built as a ZIP package (CodeZip).

Install the AgentCore CLI and create a project

Step 1: Install the CLI

npm install -g @aws/agentcore
agentcore --version
Enter fullscreen mode Exit fullscreen mode

You should see 1.0.1 or newer. If the command prints something entirely different, the old Python tool bedrock-agentcore-starter-toolkit is probably still on your path. It is also called agentcore. Uninstall it with pip, pipx or uv and open a new terminal. By the way, the CLI collects anonymous usage data. If you do not want that, switch it off like this:

agentcore config telemetry.enabled false
Enter fullscreen mode Exit fullscreen mode

Step 2: Generate a project from the template

Without options, agentcore create starts a wizard that creates a harness by default. For an agent with your own TypeScript code, specify the template explicitly. My example is the book advisor for the fictional bookstore “Bücherfuchs” (German for “book fox”):

agentcore create --name buecherfuchs --template agent-typescript-strands
cd buecherfuchs
Enter fullscreen mode Exit fullscreen mode

The template agent-typescript-strands uses Strands Agents, the open-source framework from AWS. There is also a variant with the Vercel AI SDK (agent-typescript-vercel) and several Python templates. Afterwards the project looks like this (shortened):

buecherfuchs/
├── agentcore/
│   ├── agentcore.json      # What gets deployed: runtimes, memory, gateways ...
│   ├── aws-targets.json    # Target accounts and regions
│   ├── .env.local          # Variables for local development (gitignored)
│   └── cdk/                # Generated CDK project
└── app/
    └── agent/
        ├── main.ts         # Entry point of the agent
        ├── model/load.ts   # Which model is used
        └── memory/memory.ts
Enter fullscreen mode Exit fullscreen mode

The central file is agentcore/agentcore.json. It lists which resources belong to the project. During deployment, the CLI translates it into a CDK stack. You add your own AWS resources, such as a DynamoDB table, in agentcore/cdk/lib/cdk-stack.ts – the generated file even contains an example for that.

Step 3: Leave out memory for now

The Strands template automatically adds an AgentCore Memory with four strategies (facts, preferences, summaries, episodes). That is too much for a first agent: it costs extra, slows down the deployment, and makes it harder to see what the agent itself does. So I remove the memory for now:

agentcore remove memory --name agentMemory
rm -r app/agent/memory
Enter fullscreen mode Exit fullscreen mode

You can bring it back later with agentcore add memory once your agent really needs to remember things.

Give the agent its own tools

Step 4: Two tools for the book catalog

A tool is a function the model is allowed to call. Based on name and description, the model decides when it needs a tool and supplies suitable arguments. Strands validates these arguments with a Zod schema. Our book advisor gets two tools: a catalog search and a stock check. I keep the catalog in code on purpose so the example runs without any other AWS services.

// app/agent/tools/catalog.ts
import { tool } from '@strands-agents/sdk';
import { z } from 'zod';

export type Book = {
  isbn: string;
  title: string;
  author: string;
  genre: string;
  priceEur: number;
  inStock: number;
};

// Demo catalog of the fictional bookstore "Bücherfuchs".
// In a real project this would be a DynamoDB table or an API.
export const BOOKS: Book[] = [
  { isbn: '978-3-0000-0001-1', title: 'Fog over Hamburg', author: 'Lena Brandt', genre: 'Crime', priceEur: 14.0, inStock: 3 },
  { isbn: '978-3-0000-0002-8', title: 'Cooking for Lazy People', author: 'Tom Achterberg', genre: 'Cookbook', priceEur: 22.5, inStock: 0 },
  { isbn: '978-3-0000-0003-5', title: 'Stars over Lapland', author: 'Mira Holm', genre: 'Novel', priceEur: 18.9, inStock: 7 },
  { isbn: '978-3-0000-0004-2', title: 'Serverless for Beginners', author: 'Jonas Weber', genre: 'Non-fiction', priceEur: 34.0, inStock: 2 },
];

export function searchBooks(query: string, genre?: string): Book[] {
  const q = query.trim().toLowerCase();
  return BOOKS.filter(
    (b) =>
      (q === '' || b.title.toLowerCase().includes(q) || b.author.toLowerCase().includes(q)) &&
      (!genre || b.genre.toLowerCase() === genre.toLowerCase()),
  );
}

export const searchBooksTool = tool({
  name: 'search_books',
  description:
    'Searches the catalog of the Bücherfuchs bookstore by title or author, optionally filtered by genre. ' +
    'Returns ISBN, title, author, genre, price in euros and stock.',
  inputSchema: z.object({
    query: z.string().describe('Search term for title or author, empty for all books'),
    genre: z.string().optional().describe('Optional genre, e.g. Crime, Novel, Cookbook, Non-fiction'),
  }),
  callback: async ({ query, genre }) => searchBooks(query, genre),
});

export const checkStockTool = tool({
  name: 'check_stock',
  description: 'Checks by ISBN how many copies of a book are in stock at the store.',
  inputSchema: z.object({
    isbn: z.string().describe('ISBN in the format 978-3-…'),
  }),
  callback: async ({ isbn }) => {
    const book = BOOKS.find((b) => b.isbn === isbn);
    return {
      found: book !== undefined,
      isbn,
      title: book?.title ?? null,
      inStock: book?.inStock ?? 0,
    };
  },
});
Enter fullscreen mode Exit fullscreen mode

Two details matter to me. First, tool results have to be JSON-compatible. With an interface for Book, the TypeScript compiler reports an error; with a type it works. Second, the descriptions deserve care, because they are the actual prompt for tool selection. There is more on that in my article on tool descriptions for AI agents.

Step 5: The entry point main.ts

Replace main.ts completely. It starts a small HTTP server through BedrockAgentCoreApp, validates the request and streams the agent’s answer back piece by piece:

// app/agent/main.ts
import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
import { Agent } from '@strands-agents/sdk';
import { z } from 'zod';
import { loadModel } from './model/load.js';
import { searchBooksTool, checkStockTool } from './tools/catalog.js';

const SYSTEM_PROMPT = `
You are the friendly book advisor of the bookstore "Bücherfuchs".
Use search_books and check_stock before you say anything about books, prices or stock.
Never invent books. Answer briefly and in English.
`;

const requestSchema = z.object({
  prompt: z.string().min(1),
});

// One agent per session, so the conversation stays together
const agents = new Map<string, Agent>();

async function agentFor(sessionId: string): Promise<Agent> {
  let agent = agents.get(sessionId);
  if (!agent) {
    agent = new Agent({
      model: await loadModel(),
      systemPrompt: SYSTEM_PROMPT,
      tools: [searchBooksTool, checkStockTool],
    });
    agents.set(sessionId, agent);
  }
  return agent;
}

const app = new BedrockAgentCoreApp({
  invocationHandler: {
    requestSchema,
    async *process(payload, context) {
      const agent = await agentFor(context?.sessionId ?? 'local');
      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', 10) });
Enter fullscreen mode Exit fullscreen mode

The agent is created once per session and kept in a map. Because AgentCore gives every session its own microVM, the conversation history lives as long as the session does. For long-term memory across sessions, AgentCore Memory takes over. Which model is used is defined in app/agent/model/load.ts. Change the model ID there if you want a different model or region.

Step 6: Test the tool logic without a model

The search function is plain TypeScript, so I test it like plain TypeScript. That is fast and does not cost a single token:

// app/agent/tools/catalog.test.ts
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { searchBooks } from './catalog.js';

test('finds books by author', () => {
  const result = searchBooks('holm');
  assert.equal(result.length, 1);
  assert.equal(result[0].title, 'Stars over Lapland');
});

test('filters by genre', () => {
  const result = searchBooks('', 'Crime');
  assert.deepEqual(result.map((b) => b.isbn), ['978-3-0000-0001-1']);
});

test('returns an empty list for an unknown term', () => {
  assert.equal(searchBooks('unicorn').length, 0);
});
Enter fullscreen mode Exit fullscreen mode
cd app/agent
node --import tsx --test tools/catalog.test.ts
npx tsc --noEmit
cd ../..
Enter fullscreen mode Exit fullscreen mode

Expect three passing tests and a compiler run without output. tsx is already a dependency in the template.

Test locally with agentcore dev

Step 7: Start the development server

agentcore dev
Enter fullscreen mode Exit fullscreen mode

This starts your agent with hot reload on port 8080 and collects traces locally (OpenTelemetry). With agentcore dev --mode browser, the Agent Inspector opens as well – a web UI for chatting and clicking through traces. A simple request is enough for the health check:

curl -s localhost:8080/ping
# {"status":"Healthy","time_of_last_update":...}
Enter fullscreen mode Exit fullscreen mode

Step 8: Ask the local agent

In a second terminal, send a prompt to the local server. --runtime is important; otherwise the CLI stops and tells you to specify exactly one resource:

agentcore invoke --runtime agent --local \
  --payload '{"prompt":"Do you have any crime novels in stock?"}'
Enter fullscreen mode Exit fullscreen mode

The agent calls Amazon Bedrock with your local AWS credentials. If they are missing or the region cannot be reached, you get an event: error in the stream instead of an answer. That is exactly what I saw in my test environment without AWS access: server, routing and tools worked, and only the model call failed. It is a good hint where to look when something goes wrong.

Deploy the agent to AWS with the AgentCore CLI

Step 9: Build and deploy

agentcore build
agentcore deploy
agentcore status
Enter fullscreen mode Exit fullscreen mode

build compiles the code and generates the CloudFormation template. In my project, the stack AgentCore-buecherfuchs contained exactly three relevant resources: an IAM role, the matching policy and the runtime itself. On the first run, deploy automatically creates the target default in aws-targets.json from your current account and region, and bootstraps CDK if needed. Expect a few minutes for the first run. Afterwards, status shows what is deployed and in which state.

🎬 Flow of a call with agentcore invoke – this animated diagram only runs on my site: watch it on sascha-ms.de

Step 10: Call the deployed agent

agentcore invoke --runtime agent \
  --payload '{"prompt":"How much is Stars over Lapland and is it in stock?"}' \
  --session-id "customer-4711-$(date +%Y%m%d)-book-consultation"
Enter fullscreen mode Exit fullscreen mode

With the same --session-id, follow-up questions end up in the same session and therefore with the same agent object. If you omit the option, the CLI creates a new session. In a real application you would of course not call the runtime through the CLI but via the SDK from your backend, for example from a Lambda function behind an API.

Step 11: Read logs and traces

agentcore logs runtime --name agent --since 30m --level error
agentcore traces runtime list --name agent --since 1h
Enter fullscreen mode Exit fullscreen mode

The logs come from CloudWatch, and the traces show every model and tool call with its duration. With agents, traces are a must. Without them you cannot tell whether a wrong answer came from the prompt, the tool or the model. If you want to build on that and check systematically how well your agent works, my article on evaluating AI agents will help.

Move existing projects with agentcore migrate

Anyone who has worked with a 0.x version needs to convert their project. The format of agentcore.json jumps from version 1 to version 2. I tried it with a freshly generated 0.31.2 project. The process is pleasantly cautious:

  • Without --yes, agentcore migrate only writes a plan to agentcore/.cli/migrate/ and changes nothing.
  • The project has to live in a Git repository with a clean working tree. Otherwise the CLI refuses to migrate, so that you can review and undo everything with git diff.
  • Resources that are already deployed keep their names. Afterwards, agentcore.json contains "migratedFrom": "v1" so the CDK construct keeps using the old names.
  • In the generated CDK folder, the Jest configuration and the sample test disappear, among other things. Back up your own changes there beforehand.
git status            # must be clean
agentcore migrate     # show the plan only
agentcore migrate --yes
git diff --stat
agentcore deploy
Enter fullscreen mode Exit fullscreen mode

My advice: migrate in a branch first and deploy to a test account before you touch production. The plan lists precisely which files change. Actually read it.

Common mistakes and how to avoid them

The wrong agentcore command on your path

The old Python toolkit and the new CLI share the same name. If options like --template are unknown, check with which agentcore which program you are actually running.

A harness instead of a code project

The interactive wizard suggests a harness by default. If you want to write code, pass --template. Otherwise you will wonder why there is no app folder.

Tool results that are not JSON

Date objects, class instances or interfaces without an index signature are rejected by the JSONValue type. Return flat objects and convert dates to ISO strings.

Forgotten resources after the experiment

Deleting the project folder deletes nothing in AWS. Always tear down through the CLI, as described below.

Costs, security and cleanup

The runtime is billed per second based on consumption, split into CPU and memory. According to the pricing page, no CPU time is charged while your agent is only waiting for the model or a tool. For agents that spend most of their time waiting, that is a big advantage. On top of that come the model tokens in Bedrock, CloudWatch logs and, if you use it, AgentCore Memory. Model costs are usually the biggest item. A budget alarm in the account never hurts, though.

On security: the CLI creates a dedicated IAM role for the runtime. When your agent later needs other services, grant permissions specifically through the grant… methods in the CDK stack instead of broadly extending the role. Secrets and API keys do not belong in .env.local or in the code, but in AgentCore Identity (agentcore add credentials) or Secrets Manager.

Cleanup takes two steps. First you remove all resources from the project description, then the next deployment sees that nothing is left to deploy and removes the stack:

agentcore remove all --yes
agentcore deploy --yes
Enter fullscreen mode Exit fullscreen mode

My take

To me, the AgentCore CLI 1.0 feels like what the Serverless Framework used to be for Lambda: it handles the scaffolding, and you take care of the logic. The path from create via dev to deploy is short, and I like that a normal CDK project sits underneath. I can look inside at any time and add my own resources without leaving the abstraction.

I see two things critically. The templates are rather generous: a memory with four strategies as the default is too much for a first agent and obscures the costs. In addition, the CLI documentation is currently lagging behind. That is normal after a major release, but annoying for teams that work from the docs. For new agent projects on AWS I would still use the CLI today. For existing 0.x projects I would plan the migration deliberately rather than doing it on the side.

And one more thing that matters to me: an agent does not become good just because it is deployed. The real work lies in good tools, clear boundaries and tests. The CLI only shortens the way there.

FAQ

What is the difference between the AgentCore CLI and the starter toolkit?

The starter toolkit was an older Python tool with the same command name. The AgentCore CLI is the current npm package @aws/agentcore and covers far more resources. Having both installed at the same time leads to confusion.

Do I need Docker for the AgentCore CLI?

No, not for the TypeScript templates and most Python templates. They are deployed as ZIP packages. You only need Docker, Podman or Finch for container templates and testing them locally.

Can I use models other than Claude with the AgentCore CLI?

Yes. The model is set in model/load.ts, where you can enter a different Bedrock model. According to their descriptions, several templates also support other model providers.

How much does an agent in the AgentCore Runtime cost?

You pay per second for the runtime’s CPU and memory, plus model tokens and logs. According to AWS, no CPU time is charged during pure waiting time. Current prices per region are on the AgentCore pricing page.

Your next step

Install the CLI, generate the Bücherfuchs project and replace the catalog with something from your own life, such as a list of your favorite recipes or running routes. Test locally first, then deploy to a sandbox account and tear everything down at the end. After that you will know whether the runtime path suits you or a harness is enough. I am curious: do you prefer building your agents with your own code or purely through configuration – and why?

Sources

This post was created with AI assistance.


👉 Next step: Working with AWS CDK? Grab my printable AWS CDK cheat sheet with commands, concepts and a stack skeleton.

Originally published on sascha-ms.de. More articles on serverless, AI in everyday development and leading tech teams: sascha-ms.de/en

Top comments (0)