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
The CopilotRuntime instance to use for handling requests.
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 "*".
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:
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:
- Set explicit origin (not
"*")
- Enable credentials
- 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
- CORS middleware (applied to all routes)
- Before-request middleware (from
CopilotRuntime)
- Route handler
- 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:
- Ensure
credentials: true in CORS config
- Ensure origin is explicit (not
"*")
- 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