930aaeb
CopilotKitDocs
  • Docs
  • Integrations
  • Reference
Get Started
QuickstartCoding Agents
Concepts
ArchitectureGenerative UI OverviewOSS vs Enterprise
Agentic Protocols
OverviewAG-UIAG-UI MiddlewareMCPA2A
Build Chat UIs
Prebuilt Components
CopilotChatCopilotSidebarCopilotPopup
Custom Look and Feel
CSS CustomizationSlots (Subcomponents)Fully Headless UIReasoning Messages
Multimodal AttachmentsVoice
Build Generative UI
Controlled
Tool-based Generative UITool RenderingState RenderingReasoning
Your Components
Display ComponentsInteractive Components
Declarative
A2UIDynamic Schema A2UIFixed Schema A2UI
Open-Ended
MCP Apps
Adding Agent Powers
Frontend ToolsShared State
Human-in-the-Loop
HITL OverviewPausing the Agent for InputHeadless Interrupts
Sub-AgentsAgent ConfigProgrammatic Control
Agents & Backends
Built-in Agent
Backend
Copilot RuntimeFactory ModeAG-UI
Runtime Server AdapterAuthentication
AG2
Shared state
Reading agent stateWriting agent state
Readables
Observe & Operate
InspectorVS Code Extension
Troubleshooting
Common Copilot IssuesError Debugging & ObservabilityDebug ModeAG-UI Event InspectorHook ExplorerError Observability Connectors
Enterprise
CopilotKit PremiumHow the Enterprise Intelligence Platform WorksHow Threads & Persistence WorkObservabilitySelf-Hosting IntelligenceThreads
Deploy
AWS AgentCore
What's New
Full MCP Apps SupportLangGraph Deep Agents in CopilotKitA2UI Launches with full AG-UI SupportCopilotKit v1.50Generative UI Spec SupportA2A and MCP Handshake
Migrate
Migrate to V2Migrate to 1.8.2
Other
Contributing
Code ContributionsDocumentation Contributions
Anonymous Telemetry
AG2Runtime Server Adapter

Runtime Server Adapter

Deploy the CopilotKit runtime on any backend framework — Node.js, Express, Hono, Bun, Deno, Cloudflare Workers, and more.

The CopilotKit runtime is framework-agnostic. At its core, it's a pure Fetch handler — a function that takes a Request and returns a Response. This means it runs natively on any platform that supports the Fetch API, and thin adapters are provided for Node.js-based frameworks like Express and Hono.

Quick Overview#

RuntimeImport PathKey Function
Fetch-native (Bun, Deno, CF Workers, Next.js App Router)@copilotkit/runtime/v2createCopilotRuntimeHandler
Node.js HTTP@copilotkit/runtime/v2/nodecreateCopilotNodeHandler / createCopilotNodeListener
Express@copilotkit/runtime/v2/expresscreateCopilotExpressHandler
Hono@copilotkit/runtime/v2/honocreateCopilotHonoHandler

Multi-Route vs Single-Route#

The runtime supports two endpoint modes:

  • Multi-route (default) — exposes individual URL endpoints:

    • GET {basePath}/info
    • POST {basePath}/agent/:agentId/run
    • POST {basePath}/agent/:agentId/connect
    • POST {basePath}/agent/:agentId/stop/:threadId
    • POST {basePath}/transcribe
  • Single-route — a single POST {basePath} endpoint that accepts a JSON envelope:

    { "method": "agent/run", "params": { "agentId": "default" }, "body": { ... } }
    

Single-route mode is useful when your platform only allows a single route handler (e.g. a single serverless function), or when you prefer a simpler URL structure.

Fetch-Native Runtimes#

If your platform supports the Fetch API natively (Bun, Deno, Cloudflare Workers, etc.), you can use createCopilotRuntimeHandler directly — no adapter needed.

import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

// 1. Create the runtime with your agent(s)
const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// 2. Create a Fetch handler — takes Request, returns Response
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

// 3. Serve it (Bun example — works the same with Deno.serve, CF Workers, etc.)
Bun.serve({ fetch: handler, port: 4000 });

For framework-specific examples, see the sections below.

Bun#

import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

// Configure the runtime with your agent(s)
const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Create a Fetch handler with multi-route endpoints (GET /info, POST /agent/:id/run, etc.)
const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

// Bun natively supports Fetch handlers
Bun.serve({ fetch: handler, port: 4000 });

Deno#

import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

// Deno.serve natively accepts a Fetch handler
Deno.serve({ port: 4000 }, handler);

Cloudflare Workers#

import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

export interface Env {
  OPENAI_API_KEY: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const runtime = new CopilotRuntime({
      agents: {
        default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
      },
    });

    // Workers use the Fetch API natively — handler plugs in directly
    const handler = createCopilotRuntimeHandler({
      runtime,
      basePath: "/api/copilotkit",
      cors: true,
    });

    return handler(request);
  },
};

Next.js App Router#

import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
});

// Export as both GET and POST to handle all CopilotKit routes
export { handler as GET, handler as POST };
Info

No cors: true needed for Next.js — same-origin requests don't require CORS headers.

React Router (Framework Mode)#

React Router v7 in framework mode uses file-based routing with Fetch API handlers — the CopilotKit handler works directly as a resource route.

import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";
import type { Route } from "./+types/api.copilotkit.$";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
});

// loader handles GET requests (e.g. /info)
export function loader({ request }: Route.LoaderArgs) {
  return handler(request);
}

// action handles POST requests (e.g. /agent/:id/run)
export function action({ request }: Route.ActionArgs) {
  return handler(request);
}
Info

The $ splat segment in the filename (api.copilotkit.$.ts) captures all subpaths under /api/copilotkit/, allowing the CopilotKit router to handle /info, /agent/:id/run, etc.

TanStack Start#

TanStack Start uses API routes that receive standard Request objects and return Response objects.

import { createAPIFileRoute } from "@tanstack/react-start/api";
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
});

// The $ splat matches all subpaths — GET for /info, POST for /agent/:id/run, etc.
export const APIRoute = createAPIFileRoute("/api/copilotkit/$")({
  GET: ({ request }) => handler(request),
  POST: ({ request }) => handler(request),
});
Info

The $ in the filename is TanStack Start's splat parameter, matching all subpaths under /api/copilotkit/.

Node.js HTTP#

For vanilla Node.js HTTP servers, use the /node subpath which bridges Fetch to Node's IncomingMessage/ServerResponse.

import { createServer } from "node:http";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// createCopilotNodeListener returns a Node request listener (req, res) => void
// that bridges to the Fetch handler internally
const listener = createCopilotNodeListener({
  runtime,
  basePath: "/api/copilotkit",
  cors: true,
});

createServer(listener).listen(4000, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});

Express#

The Express adapter returns an Express Router that you mount with app.use().

npm install express cors
import express from "express";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotExpressHandler } from "@copilotkit/runtime/v2/express";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

const app = express();

// Mount the CopilotKit router — creates Express routes under /api/copilotkit
app.use(
  createCopilotExpressHandler({
    runtime,
    basePath: "/api/copilotkit",
    cors: true,
  }),
);

app.listen(4000, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});

Express Options#

OptionTypeDefaultDescription
runtimeCopilotRuntimerequiredThe runtime instance
basePathstringrequiredURL path prefix (e.g. "/api/copilotkit")
mode"multi-route" | "single-route""multi-route"Multi-route exposes individual endpoints; single-route uses a JSON envelope
corsboolean | CorsOptionsfalseCORS configuration. true for permissive defaults, or pass a cors options object
hooksCopilotRuntimeHooks—Lifecycle hooks
Info

The Express adapter is compatible with express.json() body parsing. If you have app.use(express.json()) before the CopilotKit router, the adapter will detect the pre-parsed body and handle it correctly.

Hono#

The Hono adapter returns a Hono app that you mount with app.route().

npm install hono
import { serve } from "@hono/node-server";
import { CopilotRuntime, BuiltInAgent } from "@copilotkit/runtime/v2";
import { createCopilotHonoHandler } from "@copilotkit/runtime/v2/hono";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Returns a Hono app with CopilotKit routes mounted at basePath
const endpoint = createCopilotHonoHandler({
  runtime,
  basePath: "/api/copilotkit",
});

// Serve the Hono app directly
serve({ fetch: endpoint.fetch, port: 4000 }, () => {
  console.log("Listening at http://localhost:4000/api/copilotkit");
});

Elysia (Bun)#

Elysia runs on Bun and supports the Fetch API natively, so you use createCopilotRuntimeHandler directly.

bun add elysia
import { Elysia } from "elysia";
import { CopilotRuntime, createCopilotRuntimeHandler, BuiltInAgent } from "@copilotkit/runtime/v2";

const runtime = new CopilotRuntime({
  agents: {
    default: new BuiltInAgent({ model: "openai/gpt-4o-mini" }),
  },
});

// Create a Fetch handler — Elysia passes the raw Request through
const handler = createCopilotRuntimeHandler({ runtime, cors: true });

new Elysia()
  .all("/api/copilotkit/*", ({ request }) => handler(request))
  .listen(4000);

Lifecycle Hooks#

All adapters support lifecycle hooks for cross-cutting concerns like authentication, logging, and response modification.

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  hooks: {
    // Before routing — auth, correlation IDs
    onRequest: async ({ request, path, runtime }) => {
      const token = request.headers.get("authorization");
      if (!token) {
        throw new Response("Unauthorized", { status: 401 });
      }
    },

    // After routing — route-specific authorization
    onBeforeHandler: async ({ request, route }) => {
      console.log(`Handling ${route.method} for agent ${route.agentId}`);
    },

    // After handler — modify response headers
    onResponse: async ({ response, request }) => {
      const headers = new Headers(response.headers);
      headers.set("x-request-id", crypto.randomUUID());
      return new Response(response.body, {
        status: response.status,
        headers,
      });
    },

    // On error — custom error responses
    onError: async ({ error, request }) => {
      console.error("Handler error:", error);
    },
  },
});
HookWhenCan modify
onRequestBefore routingThrow Response to short-circuit
onBeforeHandlerAfter routing, before handlerAccess route info (method, agentId, threadId)
onResponseAfter handlerReturn a new Response to replace it
onErrorOn unhandled errorLog or produce a custom error response

CORS Configuration#

Fetch-native runtimes#

Pass cors: true for permissive defaults (Access-Control-Allow-Origin: *), or provide a config object:

const handler = createCopilotRuntimeHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: {
    origin: "https://myapp.com",
    credentials: true,
  },
});

Express#

Pass cors: true for permissive defaults, or pass a cors options object:

createCopilotExpressHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: {
    origin: "https://myapp.com",
    credentials: true,
  },
});

Hono#

Pass a cors config with explicit origin:

createCopilotHonoHandler({
  runtime,
  basePath: "/api/copilotkit",
  cors: {
    origin: "https://myapp.com",
    credentials: true,
  },
});
Warning

When using credentials: true, you must specify an explicit origin — wildcard (*) is not allowed by the CORS spec.

Connecting Your Frontend#

Once your runtime is running, point your frontend at it:

<CopilotKit runtimeUrl="http://localhost:4000/api/copilotkit">
  <YourApp />
</CopilotKit>

For same-origin deployments (e.g. Next.js, React Router, TanStack Start), use a relative path:

<CopilotKit runtimeUrl="/api/copilotkit">
  <YourApp />
</CopilotKit>
On this page
Quick OverviewMulti-Route vs Single-RouteFetch-Native RuntimesBunDenoCloudflare WorkersNext.js App RouterReact Router (Framework Mode)TanStack StartNode.js HTTPExpressExpress OptionsHonoElysia (Bun)Lifecycle HooksCORS ConfigurationFetch-native runtimesExpressHonoConnecting Your Frontend