7acadae
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
Built-in Agent (TanStack AI)
Advanced ConfigurationMCP ServersModel SelectionServer Tools
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
Built-in Agent (TanStack AI)Runtime 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#

server.ts
ts
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#

server.ts
ts
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#

src/index.ts
ts
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#

app/api/copilotkit/[...path]/route.ts
ts
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.

app/routes/api.copilotkit.$.ts
ts
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.

app/routes/api/copilotkit/$.ts
ts
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.

server.ts
ts
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
server.ts
ts
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
server.ts
ts
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
server.ts
ts
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