Back to Copilotkit

Index

docs/snippets/integrations/agentcore/index.mdx

1.57.012.5 KB
Original Source

import { Callout } from "fumadocs-ui/components/callout"; import { Tabs, Tab } from "fumadocs-ui/components/tabs"; import { Cards, Card } from "fumadocs-ui/components/card"; import { PaintbrushIcon, WrenchIcon, RepeatIcon } from "lucide-react"; import { TailoredContent, TailoredContentOption, } from "@/components/react/tailored-content.tsx"; import { AgentCoreCommandTabs } from "@/components/content/agentcore-command-tabs";

CopilotKit + AWS AgentCore

AWS Bedrock AgentCore gives you a secure, serverless runtime for deploying AG-UI agents at scale — handling auth, session isolation, and infrastructure. CopilotKit gives those agents a production-ready frontend:

Chat UI · Shared State · Generative UI · Human-in-the-loop

How it works

The two connect through CopilotKit Runtime, a lightweight server-side layer that sits between your browser and AgentCore. It's the same runtime you'd use with any CopilotKit-powered agent — AgentCore just requires it to run server-side (browsers can't call AgentCore directly due to SigV4/OAuth2 authentication).

Browser → CopilotKit Runtime → AgentCore Runtime → your agent

What you get

  • Chat UI — prebuilt chat interface, or headless hooks to build your own
  • Shared state — bidirectional sync between agent state and your React UI
  • Generative UI — render custom components from tool calls in real time
  • Human-in-the-loop — let users review, approve, or redirect agent actions
  • AgentCore memory — conversation history persists across sessions via AgentCore's memory layer

Quickstart

<Callout type="info" title="AWS CLI required"> Both paths below require AWS credentials configured locally. If you haven't done this yet, follow the [AWS CLI getting started guide](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html) before continuing. </Callout> <Steps> <TailoredContent className="step" id="starting-point" header={ <div> <p className="text-xl font-semibold">Choose your starting point</p> <p className="text-base"> Start fresh with our CLI, or connect CopilotKit to an agent you've already deployed on AgentCore. </p> </div> } > <TailoredContentOption id="scratch" title="Start from scratch" description="Scaffold a complete AgentCore + CopilotKit application with one command." > <Step> ### Run the CLI
    <AgentCoreCommandTabs
      framework={props.framework}
      lgCommand="npx copilotkit@latest create -f agentcore-langgraph"
      stCommand="npx copilotkit@latest create -f agentcore-strands"
    />

    The CLI downloads the full AgentCore template and sets up `config.yaml` for you.
  </Step>

  <Step>
    ### Configure

    Open `config.yaml` and set your details:

    ```yaml title="config.yaml"
    stack_name_base: my-agentcore-app   # prefix for all AWS resources (max 35 chars)
    admin_user_email: [email protected]   # where your Cognito invite will be sent
    ```
  </Step>

  <Step>
    ### Deploy

    One command deploys everything — CDK infrastructure, AgentCore Runtime, and the frontend:

    <AgentCoreCommandTabs
      framework={props.framework}
      lgCommand="./deploy-langgraph.sh"
      stCommand="./deploy-strands.sh"
    />
  </Step>

  <Step>
    ### 🎉 Open your app

    The deploy script prints an Amplify URL when it finishes. Open it and sign in with your `admin_user_email`.
  </Step>

  <Step>
    ### Run locally

    To run the full stack locally — agent, CopilotKit runtime, and frontend — without deploying:

    ```bash
    cd docker
    cp .env.example .env
    # Fill in AWS credentials and STACK_NAME
    ./up.sh --build
    ```

    | Service | Port | Description |
    |---|---|---|
    | `agent` | 8080 | Your agent, hot-reloads on `.py` changes |
    | `runtime` | 3001 | CopilotKit Runtime |
    | `frontend` | 3000 | Vite dev server |
  </Step>

  <Step>
    ### Tear down

    When done, delete all AWS resources to stop charges:

    ```bash
    cd infra-cdk && npx cdk destroy --all
    ```
  </Step>
</TailoredContentOption>

<TailoredContentOption
  id="existing"
  title="I already have an agent"
  description="I've followed the AWS guide and have an AG-UI agent deployed on AgentCore Runtime."
>
  <Step>
    ### Install CopilotKit

    <Callout type="info" title="Deploying your AG-UI agent on AgentCore">
      This path assumes you already have an AG-UI-compatible agent deployed on AgentCore Runtime.
      If not, follow AWS's [Deploy an AG-UI agent to AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html)
      guide first, then return here.
    </Callout>

    ```npm
    npm install @copilotkit/react-core @copilotkit/react-ui @copilotkit/runtime @ag-ui/client rxjs
    ```
  </Step>

  <Step>
    ### Set up CopilotKit Runtime

    CopilotKit Runtime is the server-side layer that connects your frontend to AgentCore. It handles
    the communication protocol, middleware, and — with AgentCore specifically — the server-side
    authentication that browsers can't perform directly.

    Create an API route:

    ```typescript title="app/api/copilotkit/route.ts"
    import {
      CopilotRuntime,
      ExperimentalEmptyAdapter,
      copilotRuntimeNextJSAppRouterEndpoint,
    } from "@copilotkit/runtime";
    import { HttpAgent } from "@ag-ui/client";
    import { NextRequest } from "next/server";
    import { AgentCoreRunner } from "./agent-core-runner";

    const runtime = new CopilotRuntime({
      agents: {
        my_agent: new HttpAgent({
          url: process.env.AGENTCORE_ENDPOINT_URL!, // your /invocations URL
          headers: {
            Authorization: `Bearer ${process.env.AGENTCORE_ACCESS_TOKEN}`,
          },
        }),
      },
      runner: new AgentCoreRunner(),
    });

    export const POST = async (req: NextRequest) => {
      const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
        runtime,
        serviceAdapter: new ExperimentalEmptyAdapter(),
        endpoint: "/api/copilotkit",
      });
      return handleRequest(req);
    };
    ```

    <Callout type="info" title="What is AGENTCORE_ENDPOINT_URL?">
      This is the invocation URL for your deployed AgentCore Runtime — the URL you call to reach
      your agent. It takes the form:
      `https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{encoded-arn}/invocations`
    </Callout>

    <Callout type="info" title="Auth in production">
      The example above uses a static token for simplicity. In production you'll want to pass the
      user's Cognito JWT from the frontend through to AgentCore. See the
      [full-stack example](/agentcore/full-stack-example) for a complete Cognito OIDC implementation.
    </Callout>
  </Step>

  <Step>
    ### Add the AgentCoreRunner

    AgentCore's memory layer stores conversation history server-side. When CopilotKit reconnects
    to an existing thread (e.g. on page refresh), two issues arise that require a custom runner:

    1. **Unknown threads** — CopilotKit may call `connect()` before any `run()` has happened.
       Without handling this, you'll get an error on first load.
    2. **Missing tool-call results** — AgentCore's replayed history includes assistant messages
       with tool calls, but omits the corresponding results. CopilotKit needs those to reconcile
       its message state, so the runner synthesises empty results for each past tool call.

    Create the runner file alongside your API route:

    ```typescript title="app/api/copilotkit/agent-core-runner.ts"
    import {
      EventType,
      type BaseEvent,
      type Message,
      type MessagesSnapshotEvent,
      type ToolCall,
      type ToolCallResultEvent,
    } from "@ag-ui/client";
    import { InMemoryAgentRunner } from "@copilotkit/runtime";
    import { concatMap, of } from "rxjs";
    import { randomUUID } from "node:crypto";

    export class AgentCoreRunner extends InMemoryAgentRunner {
      private readonly knownThreadIds = new Set<string>();

      override run(request: Parameters<InMemoryAgentRunner["run"]>[0]) {
        if (request.threadId) this.knownThreadIds.add(request.threadId);
        return super.run(request);
      }

      override connect(request: Parameters<InMemoryAgentRunner["connect"]>[0]) {
        if (!request.threadId || !this.knownThreadIds.has(request.threadId)) {
          const threadId = request.threadId ?? randomUUID();
          const runId = randomUUID();
          return of<BaseEvent>(
            { type: EventType.RUN_STARTED, threadId, runId },
            { type: EventType.MESSAGES_SNAPSHOT, messages: [] },
            { type: EventType.RUN_FINISHED, threadId, runId },
          );
        }

        return super.connect(request).pipe(
          concatMap((event: BaseEvent) => {
            if (event.type !== EventType.MESSAGES_SNAPSHOT) return of(event);
            const snapshot = event as MessagesSnapshotEvent;
            const replayedResults: ToolCallResultEvent[] = snapshot.messages.flatMap(
              (message: Message) => {
                if (message.role !== "assistant" || !message.toolCalls?.length) return [];
                return message.toolCalls.map<ToolCallResultEvent>((toolCall: ToolCall) => ({
                  type: EventType.TOOL_CALL_RESULT,
                  toolCallId: toolCall.id,
                  messageId: `${toolCall.id}-result`,
                  content: "",
                  role: "tool",
                }));
              },
            );
            return of<BaseEvent>(...replayedResults, snapshot);
          }),
        );
      }
    }
    ```
  </Step>

  <Step>
    ### Add the CopilotKit provider

    ```tsx title="app/layout.tsx"
    import { CopilotKit } from "@copilotkit/react-core";
    import "@copilotkit/react-ui/styles.css";

    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en">
          <body>
            <CopilotKit runtimeUrl="/api/copilotkit" agent="my_agent">
              {children}
            </CopilotKit>
          </body>
        </html>
      );
    }
    ```
  </Step>

  <Step>
    ### Add the chat interface

    ```tsx title="app/page.tsx"
    import { CopilotSidebar } from "@copilotkit/react-ui";

    export default function Page() {
      return (
        <main>
          <h1>My App</h1>
          <CopilotSidebar />
        </main>
      );
    }
    ```
  </Step>

  <Step>
    ### 🎉 Run it

    Point the Next.js app at the AgentCore runtime you deployed via AWS's
    [Deploy an AG-UI agent to AgentCore Runtime](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html)
    guide. Create `.env.local`:

    ```bash title=".env.local"
    AGENTCORE_ENDPOINT_URL=https://bedrock-agentcore.{region}.amazonaws.com/runtimes/{encoded-arn}/invocations
    AGENTCORE_ACCESS_TOKEN=<your-bearer-token>
    ```

    Then:

    ```bash
    npm run dev
    ```

    Navigate to `localhost:3000`. The frontend runs locally while the agent stays on AgentCore —
    the `/api/copilotkit` route forwards each call to the invocation URL using your token.
  </Step>
</TailoredContentOption>
</TailoredContent> </Steps>

What's next?

<Cards> <Card title="Generative UI" description="Render custom React components directly from your agent's tool calls." href="/langgraph/generative-ui/tool-rendering" icon={<PaintbrushIcon />} /> <Card title="Shared State" description="Sync structured state between your agent and your React UI bidirectionally." href="/langgraph/shared-state/in-app-agent-read" icon={<RepeatIcon />} /> </Cards>