Skip to main content

Overview

The Hono adapter provides a ready-to-use Hono app for integrating CopilotKit with Hono applications. It handles all CopilotKit endpoints with support for CORS configuration, including credentials for HTTP-only cookies.

Installation

npm install @copilotkit/runtime hono

Basic Usage

import { Hono } from 'hono';
import { CopilotRuntime } from '@copilotkit/runtime';
import { createCopilotEndpoint } from '@copilotkit/runtime/hono';
import { BuiltInAgent } from '@copilotkit/agent';

const app = new Hono();

const runtime = new CopilotRuntime({
  agents: {
    'default': new BuiltInAgent({
      model: 'openai/gpt-4o',
      apiKey: process.env.OPENAI_API_KEY
    })
  }
});

const copilotApp = createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
});

app.route('/api/copilotkit', copilotApp);

export default app;

API Reference

createCopilotEndpoint()

Creates a Hono app with all CopilotKit endpoints.
function createCopilotEndpoint(
  params: CopilotEndpointParams
): Hono
runtime
CopilotRuntime
required
The CopilotRuntime instance to use for handling requests.
basePath
string
required
Base path for all CopilotKit endpoints. Should match the path in app.route().
basePath: '/api/copilotkit'
cors
CopilotEndpointCorsConfig
Optional CORS configuration. Required when using credentials (HTTP-only cookies).
cors: {
  origin: 'https://myapp.com',
  credentials: true
}
Returns: A Hono app instance.

CORS Configuration

interface CopilotEndpointCorsConfig {
  origin:
    | string
    | string[]
    | ((origin: string, c: any) => string | undefined | null);
  credentials?: boolean;
}
origin
string | string[] | function
required
Allowed origin(s) for CORS. Can be:
  • A string for a single origin
  • An array of allowed origins
  • A function for dynamic origin resolution
Note: When credentials is true, origin cannot be "*".
credentials
boolean
default:"false"
Whether to allow credentials (cookies, HTTP authentication).When true, origin must be explicitly specified (not "*").

Endpoints

The adapter automatically creates the following endpoints:

POST /agent/:agentId/run

Execute an agent with new input. Parameters:
  • agentId (path): Agent identifier
Request body:
{
  "runId": "run_123",
  "threadId": "thread_456",
  "messages": [...],
  "tools": [...],
  "context": [...],
  "state": {...}
}
Response: Server-Sent Events (SSE) stream of AG-UI events

POST /agent/:agentId/connect

Connect to an existing thread to receive events. Parameters:
  • agentId (path): Agent identifier
Request body:
{
  "threadId": "thread_456"
}
Response: Server-Sent Events (SSE) stream of thread events

POST /agent/:agentId/stop/:threadId

Stop a running agent execution. Parameters:
  • agentId (path): Agent identifier
  • threadId (path): Thread identifier
Response:
{
  "success": true
}

GET /info

Get runtime information including available agents. Response:
{
  "version": "1.0.0",
  "agents": {
    "default": {
      "id": "default",
      "name": "Default Agent"
    }
  }
}

POST /transcribe

Transcribe audio input (requires transcription service). Request: Multipart form data with audio file Response:
{
  "text": "Transcribed text..."
}

Examples

Basic Setup

import { Hono } from 'hono';
import { CopilotRuntime } from '@copilotkit/runtime';
import { createCopilotEndpoint } from '@copilotkit/runtime/hono';
import { BuiltInAgent } from '@copilotkit/agent';

const app = new Hono();

const runtime = new CopilotRuntime({
  agents: {
    'assistant': new BuiltInAgent({
      model: 'openai/gpt-4o',
      apiKey: process.env.OPENAI_API_KEY,
      prompt: 'You are a helpful assistant.'
    })
  }
});

app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

export default app;

With CORS for Single Origin

const copilotApp = createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit',
  cors: {
    origin: 'https://myapp.com'
  }
});

app.route('/api/copilotkit', copilotApp);

With CORS for Multiple Origins

const copilotApp = createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit',
  cors: {
    origin: [
      'https://myapp.com',
      'https://staging.myapp.com',
      'http://localhost:5173'
    ]
  }
});

app.route('/api/copilotkit', copilotApp);

With Credentials (HTTP-only Cookies)

const copilotApp = createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit',
  cors: {
    origin: 'https://myapp.com',
    credentials: true  // Allow cookies
  }
});

app.route('/api/copilotkit', copilotApp);

Dynamic CORS

const copilotApp = createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit',
  cors: {
    origin: (origin, c) => {
      // Allow requests from any subdomain of myapp.com
      if (origin.endsWith('.myapp.com') || origin === 'https://myapp.com') {
        return origin;
      }
      // Allow localhost in development
      if (origin.startsWith('http://localhost:')) {
        return origin;
      }
      return undefined; // Reject
    },
    credentials: true
  }
});

app.route('/api/copilotkit', copilotApp);

Multiple Agents

const runtime = new CopilotRuntime({
  agents: {
    'customer-support': new BuiltInAgent({
      model: 'openai/gpt-4o',
      apiKey: process.env.OPENAI_API_KEY,
      prompt: 'You are a customer support agent.'
    }),
    'sales': new BuiltInAgent({
      model: 'anthropic/claude-sonnet-4.5',
      apiKey: process.env.ANTHROPIC_API_KEY,
      prompt: 'You are a sales assistant.'
    })
  }
});

app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

With Authentication Middleware

const runtime = new CopilotRuntime({
  agents: { ... },
  beforeRequestMiddleware: async (request) => {
    const token = request.headers.get('Authorization');
    if (!token) {
      return new Response('Unauthorized', { status: 401 });
    }
    
    const user = await verifyToken(token);
    if (!user) {
      return new Response('Invalid token', { status: 401 });
    }
    
    // Add user ID to headers
    return new Request(request.url, {
      method: request.method,
      headers: {
        ...Object.fromEntries(request.headers.entries()),
        'X-User-Id': user.id
      },
      body: request.body,
      duplex: 'half'
    });
  }
});

app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

With Transcription

import { OpenAITranscriptionService } from '@copilotkit/runtime/transcription';

const runtime = new CopilotRuntime({
  agents: { ... },
  transcriptionService: new OpenAITranscriptionService({
    apiKey: process.env.OPENAI_API_KEY,
    model: 'whisper-1'
  })
});

app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

Additional Routes

import { Hono } from 'hono';
import { createCopilotEndpoint } from '@copilotkit/runtime/hono';

const app = new Hono();

// CopilotKit routes
app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

// Other API routes
app.get('/api/health', (c) => {
  return c.json({ status: 'ok' });
});

app.post('/api/other', async (c) => {
  const body = await c.req.json();
  return c.json({ message: 'Hello', data: body });
});

export default app;

Cloudflare Workers

import { Hono } from 'hono';
import { CopilotRuntime } from '@copilotkit/runtime';
import { createCopilotEndpoint } from '@copilotkit/runtime/hono';
import { BuiltInAgent } from '@copilotkit/agent';

const app = new Hono();

const runtime = new CopilotRuntime({
  agents: {
    'default': new BuiltInAgent({
      model: 'openai/gpt-4o',
      apiKey: OPENAI_API_KEY // From Cloudflare environment
    })
  }
});

app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit',
  cors: {
    origin: 'https://myapp.com'
  }
}));

export default app;

Deno Deploy

import { Hono } from 'https://deno.land/x/hono/mod.ts';
import { CopilotRuntime } from '@copilotkit/runtime';
import { createCopilotEndpoint } from '@copilotkit/runtime/hono';
import { BuiltInAgent } from '@copilotkit/agent';

const app = new Hono();

const runtime = new CopilotRuntime({
  agents: {
    'default': new BuiltInAgent({
      model: 'openai/gpt-4o',
      apiKey: Deno.env.get('OPENAI_API_KEY')!
    })
  }
});

app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

Deno.serve(app.fetch);

Bun

import { Hono } from 'hono';
import { CopilotRuntime } from '@copilotkit/runtime';
import { createCopilotEndpoint } from '@copilotkit/runtime/hono';
import { BuiltInAgent } from '@copilotkit/agent';

const app = new Hono();

const runtime = new CopilotRuntime({
  agents: {
    'default': new BuiltInAgent({
      model: 'openai/gpt-4o',
      apiKey: process.env.OPENAI_API_KEY!
    })
  }
});

app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

export default {
  port: 3000,
  fetch: app.fetch,
};

CORS and Cookies

When using HTTP-only cookies for authentication, you must:
  1. Set explicit origin (not "*")
  2. Enable credentials
  3. Configure frontend to send credentials
Backend:
const copilotApp = createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit',
  cors: {
    origin: 'https://myapp.com',
    credentials: true
  }
});
Frontend (Angular):
provideCopilotKit({
  runtimeUrl: 'https://api.myapp.com/api/copilotkit',
  headers: {
    // Cookies are sent automatically when credentials are included
  }
})
The browser will automatically include HTTP-only cookies in requests to the runtime.

Middleware Execution Order

  1. CORS middleware (applied to all routes)
  2. Before-request middleware (from CopilotRuntime)
  3. Route handler
  4. After-request middleware (from CopilotRuntime, non-blocking)

Error Handling

Errors are automatically handled and returned as JSON responses:
// Handled automatically:
app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

// Custom error handling:
app.onError((err, c) => {
  console.error('Error:', err);
  return c.json({
    error: 'Internal server error',
    message: err.message
  }, 500);
});

Troubleshooting

CORS Issues with Credentials

If cookies aren’t being sent:
  1. Ensure credentials: true in CORS config
  2. Ensure origin is explicit (not "*")
  3. Ensure frontend and backend domains match CORS policy
// ✅ Correct
cors: {
  origin: 'https://myapp.com',
  credentials: true
}

// ❌ Wrong (credentials with wildcard)
cors: {
  origin: '*',
  credentials: true
}

Base Path Mismatch

Ensure basePath matches the route mounting:
// ✅ Correct
app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/api/copilotkit'
}));

// ❌ Wrong
app.route('/api/copilotkit', createCopilotEndpoint({
  runtime,
  basePath: '/copilotkit'  // Mismatch!
}));

SSE Streaming Issues

Some edge runtimes may have different SSE handling. Ensure your runtime supports streaming responses. For Cloudflare Workers, streaming is supported but may require specific configuration:
export default {
  fetch: app.fetch,
  // Enable streaming
  async scheduled(event, env, ctx) {
    // ...
  }
};

See Also