Chatbot
Your project includes an AI-powered chatbot that uses the Claude API with streaming responses. The chatbot can execute MCP tools to interact with your application data, filtered by user permissions.
Setting up
Add your Anthropic API key to packages/backend/.env:
ANTHROPIC_API_KEY=sk-ant-...Without this key, the chatbot feature is disabled and a warning is logged at startup.
How it works
- User opens the chatbot panel and sends a message.
- Frontend sends a
POST /api/chatbot/messagerequest. - Backend acquires a concurrency lock, checks rate limits, and streams the response via SSE.
- The chatbot uses Claude Haiku 4.5 with an agentic loop (up to 10 iterations) — it can call MCP tools to read and modify data, then provide a final answer.
- Token usage is tracked per user, organization, and globally.
Rate limiting
Three-tier daily token limits prevent abuse:
| Tier | Default limit | Environment variable |
|---|---|---|
| User | 50,000 | CHATBOT_DAILY_TOKEN_LIMIT_USER |
| Organization | 1,000,000 | CHATBOT_DAILY_TOKEN_LIMIT_ORGANIZATION |
| Global | 1,000,000 | CHATBOT_DAILY_TOKEN_LIMIT_GLOBAL |
Usage is stored in the ChatbotUsage table with daily upserts. Limits reset at midnight UTC.
Concurrency control
Only one chatbot request per user is allowed at a time. A Redis-based lock (with in-memory fallback for development) prevents concurrent requests from mixing responses. Locks have a 5-minute TTL to prevent permanent locks from crashes.
MCP tool integration
The chatbot loads all MCP tools via getAllMcpTools() and filters them by the current user's permissions using Better Auth roles. Tools are converted to Anthropic format and executed in an agentic loop until Claude provides a final answer.
For example, a user with member: ['read'] permission can ask the chatbot to list members, and the chatbot will call the member_list tool automatically.
Permissions
The chatbot requires the chatbot:use permission. Both admin and member roles have this permission by default.
Frontend components
| Component | Purpose |
|---|---|
ChatbotSheet | Slide-out panel with header, messages, input |
ChatbotMessages | Scrollable message list with auto-scroll |
ChatbotMessage | Individual message with markdown rendering |
ChatbotToolCall | Tool execution indicator with spinner |
ChatbotInput | Text input with send/cancel buttons |
ChatbotButton | Floating action button to open chat |
Assistant messages are rendered as Markdown via react-markdown with remark-gfm support.
SSE event types
The streaming response sends these event types:
| Type | Description |
|---|---|
text | Streaming text chunk |
tool_use | Tool invocation started |
tool_result | Tool execution result |
usage | Token counts (input/output) |
done | Stream complete |
error | Error message |
Error handling
| HTTP | Error | Meaning |
|---|---|---|
| 409 | concurrent_request | User already has active request |
| 429 | limit_exceeded | Daily token quota exceeded |
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
ANTHROPIC_API_KEY | Yes | — | Anthropic API key for Claude |
CHATBOT_DAILY_TOKEN_LIMIT_USER | No | 50000 | Per-user daily token limit |
CHATBOT_DAILY_TOKEN_LIMIT_ORGANIZATION | No | 1000000 | Per-organization daily token limit |
CHATBOT_DAILY_TOKEN_LIMIT_GLOBAL | No | 1000000 | Global daily token limit |
Key files
| File | Description |
|---|---|
backend/src/features/chatbot/chatbotService.ts | AI streaming + tool execution |
backend/src/features/chatbot/chatbotController.ts | Request orchestration |
backend/src/features/chatbot/chatbotLockService.ts | Concurrency control |
backend/src/features/chatbot/chatbotUsageService.ts | Rate limiting |
frontend/src/features/chatbot/chatbotStore.ts | Zustand state management |
frontend/src/features/chatbot/useChatbot.ts | SSE client hook |
frontend/src/features/chatbot/ChatbotSheet.tsx | Main UI container |