Universal, embeddable AI chatbot widget. Add an AI assistant to any website with a single <script> tag.
Multi-provider (Claude, OpenAI, Gemini), lead capture, knowledge base, conversation persistence, analytics, and i18n.
<script
src="https://cdn.jsdelivr.net/npm/@chatcops/widget@1/dist/chatcops.min.js"
data-api-url="https://your-api.com/chat"
data-accent="#6366f1">
</script>npm install @chatcops/server expressimport express from 'express';
import { chatcopsMiddleware } from '@chatcops/server';
const app = express();
app.use(express.json());
app.use('/chat', chatcopsMiddleware({
provider: {
type: 'claude',
apiKey: process.env.ANTHROPIC_API_KEY!,
},
systemPrompt: 'You are a helpful assistant for our website.',
sessionSecrets: [process.env.CHATCOPS_SESSION_SECRET!], // openssl rand -base64 32
}));
app.listen(3001);That's it. Your site now has an AI chat widget.
- Multi-provider - Claude, OpenAI, Gemini. Switch with one config change.
- Zero-dependency widget - Shadow DOM isolated. The script tag loads a launcher under 5 kB gzipped, and the chat itself loads when it's opened.
- Streaming responses - Real-time SSE streaming from all providers.
- Lead capture - Built-in tool for capturing contact info, with webhook support.
- Knowledge base - Feed text chunks or FAQ pairs for context-aware responses.
- Conversation persistence - localStorage on client, pluggable stores on server.
- Analytics - Track conversations, messages, and lead captures.
- i18n - Ships in English with custom string overrides; the model replies in the visitor's language.
- Deployable anywhere - Express, Vercel, Cloudflare Workers adapters included.
- Customizable - Theme, branding, position, welcome messages, callbacks.
| Package | Description |
|---|---|
@chatcops/core |
AI provider abstraction, tools, knowledge base, i18n |
@chatcops/widget |
Embeddable chat widget (IIFE + ESM) |
@chatcops/server |
Server handler + Express/Vercel/Cloudflare adapters |
Host Website Server
┌──────────────┐ SSE ┌──────────────┐
│ <script> │ ──────────> │ Handler │
│ Shadow DOM │ <────────── │ ├─ Provider │
│ Chat Widget │ streaming │ ├─ Knowledge │
└──────────────┘ │ ├─ Tools │
│ └─ Analytics │
└──────────────┘
import ChatCops from '@chatcops/widget';
ChatCops.init({
apiUrl: 'https://your-api.com/chat',
theme: {
accent: '#E07A5F',
position: 'bottom-right',
},
branding: {
name: 'Support Bot',
subtitle: 'Online',
},
welcomeMessage: 'Hi! How can I help you today?',
welcomeBubble: {
text: 'Need help? Chat with us!',
delay: 3000,
},
locale: 'en',
persistence: 'local', // the default; 'session' keeps it for the tab, 'none' saves nothing
onMessage: (msg) => console.log('New message:', msg),
});import { chatcopsVercelHandler } from '@chatcops/server/vercel';
export default chatcopsVercelHandler({
provider: { type: 'claude', apiKey: process.env.ANTHROPIC_API_KEY! },
systemPrompt: 'You are a helpful assistant.',
sessionSecrets: [process.env.CHATCOPS_SESSION_SECRET!],
});Add api/chat/session.ts with export { default } from '../chat'; so the widget can get its session token there.
import { chatcopsCloudflareHandler } from '@chatcops/server/cloudflare';
let handler: ReturnType<typeof chatcopsCloudflareHandler> | undefined;
function getHandler(env: Env) {
handler ??= chatcopsCloudflareHandler({
provider: { type: 'openai', apiKey: env.OPENAI_API_KEY },
systemPrompt: 'You are a helpful assistant.',
sessionSecrets: [env.CHATCOPS_SESSION_SECRET],
});
return handler;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return getHandler(env)(request);
},
};pnpm install
pnpm -r build
pnpm testSee CONTRIBUTING.md for details.
Questions, or want to chat with other contributors? Join the CODERCOPS Discord.
ChatCops is maintained by CODERCOPS together with contributors who have Triage access. GOVERNANCE.md lists who they are and how to become one. The project is free and MIT licensed, and CODERCOPS doesn't sell it or a hosted version of it. If it's useful to you, you can sponsor the maintainers. The money pays for review time and hosting.
MIT