Back to Copilotkit

Copilot Runtime

showcase/shell-docs/src/content/docs/backend/copilot-runtime.mdx

1.62.310.6 KB
Original Source

The Copilot Runtime is the backend layer that connects your frontend application to your AI agents. It's set up during the quickstart and is the recommended way to use CopilotKit.

Setting up the runtime

The runtime is a lightweight server endpoint that you add to your backend. Here's a minimal example using Next.js:

ts
import {
  CopilotRuntime,
  ExperimentalEmptyAdapter,
  copilotRuntimeNextJSAppRouterEndpoint,
} from "@copilotkit/runtime";
import { NextRequest } from "next/server";

const serviceAdapter = new ExperimentalEmptyAdapter();

const runtime = new CopilotRuntime({
  agents: {
    // your agents go here
  },
});

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

  return handleRequest(req);
};

Then point your frontend at the endpoint:

tsx
import { CopilotKit } from "@copilotkit/react-core/v2";

<CopilotKit runtimeUrl="/api/copilotkit">
  <YourApp />
</CopilotKit>

For Express, NestJS, or plain Node.js HTTP variants, see the quickstart. For the exact HTTP routes the runtime exposes (and how to probe them with curl), see Runtime HTTP endpoints.

<Callout type="info" title="Legacy vs. v2 runtime endpoints"> The `copilotRuntimeNextJSAppRouterEndpoint` / `copilotRuntimeNodeHttpEndpoint` / `copilotRuntimeNodeExpressEndpoint` / `copilotRuntimeNestEndpoint` helpers above are the **legacy** (v1) endpoint factories, imported from `@copilotkit/runtime`. The v2 runtime exposes a smaller, framework-agnostic API under `@copilotkit/runtime/v2`:
Legacy (@copilotkit/runtime)v2 (@copilotkit/runtime/v2)
copilotRuntimeNextJSAppRouterEndpoint, copilotRuntimeNodeHttpEndpoint (Fetch / Next.js App Router, Bun, Deno, Cloudflare Workers)createCopilotRuntimeHandler
copilotRuntimeNodeExpressEndpoint (Express)createCopilotExpressHandler (from @copilotkit/runtime/v2/express)

Both styles work in v1.50. For new projects, use the v2 handlers. See Deploy to any runtime. </Callout>

Agents

The runtime supports multiple agent types. BuiltInAgent is the primary agent class:

  • Simple mode: pass a model string and let CopilotKit handle the rest. Best for quick setup. See Quickstart.
  • Factory mode: bring your own AI SDK, TanStack AI, or custom LLM backend. Best when you need full control. See Factory Mode.

The default agent

If you register an agent under the name "default", CopilotKit's prebuilt UI components will use it automatically without any additional configuration on the frontend. This is useful when you have one primary agent and don't want to specify an agentId everywhere.

ts
import { BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    // This agent is used automatically by CopilotPopup, CopilotSidebar, etc.
    default: new BuiltInAgent({ model: "openai:gpt-4.1" }),
  },
});

When you register multiple agents, the "default" agent is what powers the chat unless a specific agent is selected. Other agents can still be addressed by passing their agentId to useAgent or the prebuilt components.

What the runtime provides

Authentication & security

The runtime runs on your server, which means agent communication stays server-side. This gives you a trusted environment to enforce authentication, validate requests, and keep API keys secure. When you use the runtime, safe defaults prevent your agent endpoints from being exposed to unauthenticated access.

AG-UI middleware

The AG-UI protocol supports a middleware layer (agent.use) for logging, guardrails, request transformation, and more. Because the runtime runs server-side, this middleware executes in a trusted environment where it cannot be tampered with by the client.

Agent routing

When you register multiple agents, the runtime handles discovery and routing automatically. Your frontend doesn't need to know where each agent lives or how to reach it.

Enterprise Intelligence Platform

Threads, the inspector, and other Enterprise Intelligence Platform capabilities are provided through the runtime. These give you conversation persistence and debugging without extra setup.

Built-in middleware

The runtime exposes two first-class middleware options you can enable directly on CopilotRuntime without calling .use() on each agent manually.

A2UI

Pass a2ui: {} to automatically apply A2UIMiddleware to all registered agents:

ts
const runtime = new CopilotRuntime({
  agents: { default: myAgent },
  a2ui: {}, // enables A2UI rendering for all agents
});

To scope it to specific agents only, pass an agents list:

ts
a2ui: { agents: ["my-agent"] }

On the frontend, the A2UI renderer activates automatically. No extra configuration is needed. To override the default theme, pass an a2ui prop to <CopilotKit>:

tsx
import { CopilotKit } from "@copilotkit/react-core/v2";

<CopilotKit runtimeUrl="/api/copilotkit" a2ui={{ theme: myCustomTheme }}>
  {children}
</CopilotKit>

mcpApps

Pass mcpApps to configure MCP servers for all agents from a single place:

ts
const runtime = new CopilotRuntime({
  agents: { default: myAgent },
  mcpApps: {
    servers: [
      { type: "http", url: "http://localhost:3108/mcp" },
    ],
  },
});

Each server entry optionally accepts an agentId field to scope that server to a single agent. Without it, the server is available to all agents.

Forwarding request headers to your agent

When a request reaches the runtime, some inbound headers are forwarded onto the outgoing call to your agent (the /run path that actually dispatches the agent). This is how a token set on <CopilotKit headers={...}> reaches a self-hosted agent — see Authentication.

By default the runtime forwards authorization and any x-* header, minus a built-in denylist of infrastructure, proxy, and platform headers that no legitimate agent integration needs forwarded from the edge. The denylist strips, among others:

  • Proxy / topology: x-forwarded-*, x-real-ip
  • Cloud / CDN tracing: x-amzn-trace-id, x-amz-* (AWS), x-azure-*, x-fastly-*, x-cloud-trace-context, x-cache, x-served-by
  • Platform-injected: x-vercel-*, x-middleware-* (Next.js)
  • CopilotKit platform: x-copilotcloud-*, including x-copilotcloud-public-api-key

Everything else still forwards: authorization, and any custom application header like x-tenant-id, x-api-key, or x-user-id.

<Callout type="warn" title="Upgrade note: x-request-id is now stripped"> Earlier runtime versions forwarded `x-request-id`. It is now stripped by default because it is predominantly a proxy-assigned trace id. If your agent relies on receiving your own `x-request-id`, re-enable it with `forwardHeaders: { allow: [...] }` or `useDefaultDenylist: false` (below). </Callout>

Server-configured headers win

Headers you set on an agent (e.g. new HttpAgent({ headers: { Authorization: "Bearer <service-token>" } })) take precedence over a forwarded inbound header of the same name, matched case-insensitively. A service-to-service token you configured on the server is never silently overridden by a browser- or edge-injected inbound header (#5782).

Configuring the policy

Pass forwardHeaders to CopilotRuntime to tune what forwards:

ts
const runtime = new CopilotRuntime({
  agents: { default: myAgent },
  forwardHeaders: {
    // Strip additional headers on top of the default denylist:
    deny: ["x-internal-debug"],
    denyPrefixes: ["x-acme-"],
  },
});

The options:

  • deny / denyPrefixes — extra exact names / name-prefixes to strip. These always strip (deny wins), even in allowlist mode, so a security-motivated deny can never be defeated by an overlapping allow.
  • allow — switches to allowlist mode: only the listed headers forward, and the usual authorization / x-* eligibility no longer applies. Your deny / denyPrefixes still subtract from this set.
  • useDefaultDenylist — defaults to true. Set false to opt out of the built-in denylist and restore the previous wide-open behavior.

Empty or whitespace-only entries are ignored in every list.

ts
// Allowlist mode: forward ONLY these two, nothing else.
forwardHeaders: { allow: ["authorization", "x-tenant-id"] }
<Callout type="warn" title="Allowlist mode bypasses the default denylist"> In allowlist mode the built-in denylist does **not** apply — only your `allow` set (minus your own `deny`) forwards. Don't allow-list protected headers such as `x-copilotcloud-public-api-key` or `x-forwarded-*` unless you truly intend to forward them, since the default protection isn't there to catch them. </Callout>

Connecting to an AG-UI agent directly

CopilotKit is built on the AG-UI protocol, which is an open standard. If you want to connect your frontend directly to an AG-UI-compatible agent without the runtime, you can do so by passing agent instances straight to the CopilotKit:

tsx
import { HttpAgent } from "@ag-ui/client";
import { CopilotKit } from "@copilotkit/react-core/v2";

const myAgent = new HttpAgent({
  url: "https://my-agent.example.com",
});

<CopilotKit agents__unsafe_dev_only={{ "my-agent": myAgent }}>
  <YourApp />
</CopilotKit>;
<Callout type="warn"> Direct agent connections are intended for development and prototyping. They are **not recommended for production** and are not officially supported by CopilotKit. </Callout>

If you intend to manage the agent connection yourself in production and have secured it, use the supported selfManagedAgents prop instead of agents__unsafe_dev_only.

Key trade-offs:

  1. Authentication is your responsibility. The runtime's safe defaults do not apply.
  2. Many ecosystem features won't work. Runtime-backed middleware and other capabilities depend on the server-side path.

Comparison

With RuntimeDirect Connection
AuthenticationSafe defaults providedYou manage it
AG-UI MiddlewareRuns server-sideNot available
Agent RoutingAutomaticManual
Ecosystem FeaturesFull supportLimited
CopilotKit SupportSupportedNot supported
SetupRequires a backend endpointFrontend-only