skills/92bilal26/chatkit-botbuilder/SKILL.md
Guide for creating production-grade ChatKit chatbots that integrate OpenAI Agents SDK with MCP tools and custom backends. Use when building AI-powered chatbots with specialized capabilities, real-time task execution, and user isolation for any application.
npx skillsauth add aiskillstore/marketplace chatkit-botbuilderInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create production-grade chatbots using the OpenAI ChatKit framework. This skill enables building chatbots that:
This skill provides the complete architecture pattern for ChatKit integration, from frontend configuration to backend server implementation.
Use this skill when you need to:
User Message
↓
ChatKit Frontend (React/Next.js)
↓ [JWT Token in Authorization Header]
↓
FastAPI Backend (ChatKit Server)
↓ [Extract user_id from JWT]
↓
OpenAI Agent (Agents SDK)
↓ [Needs tool execution]
↓
MCP Tools (Custom Tool Functions)
↓ [Creates/Updates/Lists data]
↓
Database (User-Isolated Data)
↓
Response → ChatKit → Frontend → User
Frontend (Next.js + ChatKit SDK)
Backend (FastAPI + ChatKit Server)
Agent (OpenAI Agents SDK)
Tools (MCP + Custom Functions)
Database
1. Create ChatKit Server Class
from chatkit.server import ChatKitServer
from chatkit.store import Store
class MyChatKitServer(ChatKitServer):
def __init__(self):
store = CustomChatKitStore()
super().__init__(store=store)
async def respond(self, thread, input, context):
"""Process user message and stream AI response"""
user_id = getattr(context, 'user_id', None)
# Create agent with wrapped tools
# Stream response using official pattern
2. Create MCP Tool Wrappers
# Extract user_id from context and inject into tool calls
def add_task_wrapper(title: str, description: str = None):
return mcp_add_task(user_id=user_id, title=title, description=description)
def list_tasks_wrapper(status: str = "all"):
return mcp_list_tasks(user_id=user_id, status=status)
3. Create FastAPI Endpoint
@router.post("/api/v1/chatkit")
async def chatkit_protocol_endpoint(request: Request):
user_id = request.state.user_id # From JWT middleware
context = create_context_object(user_id=user_id)
result = await chatkit_server.process(body, context)
return StreamingResponse(result, media_type="text/event-stream")
4. Configure JWT Middleware
class JWTAuthMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
# Extract JWT token from Authorization header
# Decode and set request.state.user_id
# All endpoints have access to authenticated user_id
1. Configure ChatKit SDK
const chatKitConfig: UseChatKitOptions = {
api: {
url: `${API_BASE_URL}/api/v1/chatkit`,
domainKey: 'your-domain-key',
fetch: authenticatedFetch, // Custom fetch with JWT
},
theme: 'light',
header: { enabled: true, title: { text: 'AI Chat' } },
history: { enabled: true },
}
2. Create Authenticated Fetch Wrapper
async function authenticatedFetch(input, options) {
const token = localStorage.getItem('access_token')
const headers = {
...options?.headers,
'Authorization': `Bearer ${token}`,
}
return fetch(input, { ...options, headers })
}
3. Integrate ChatKit Widget
import { ChatKitWidget } from '@openai/chatkit-react'
export default function Dashboard() {
return (
<div className="flex gap-4">
{/* Your app content */}
{showChat && (
<ChatKitWidget {...chatKitConfig} />
)}
</div>
)
}
4. Add Auto-Refresh for Real-Time Sync
useEffect(() => {
if (!showChatKit) return
// Refresh immediately when chat opens
fetchTasks()
// Refresh every 1 second for real-time updates
const interval = setInterval(() => {
fetchTasks()
}, 1000)
return () => clearInterval(interval)
}, [showChatKit])
1. Create MCP Tools with User Isolation
def add_task(user_id: str, title: str, description: Optional[str] = None):
"""Create task - receives user_id from wrapper"""
task = Task(
id=str(uuid.uuid4()),
user_id=user_id, # Critical: ensure user isolation
title=title,
description=description,
completed=False,
created_at=datetime.utcnow(),
)
with Session(engine) as session:
session.add(task)
session.commit()
2. Register MCP Tools
mcp_server = MCPServer()
mcp_server.register_tool("add_task", add_task)
mcp_server.register_tool("list_tasks", list_tasks)
mcp_server.register_tool("delete_task", delete_task)
# ... more tools
Three-Level Guarantee:
# Middleware extracts user_id from token
request.state.user_id = payload.get("user_id")
# Tool wrapper captures and injects it
def add_task_wrapper(title):
return mcp_add_task(user_id=user_id, ...)
# Database enforces it
WHERE user_id = ? AND task_id = ?
User sends: "Create a task called 'Buy milk'"
↓
ChatKit Protocol: POST /api/v1/chatkit
Header: Authorization: Bearer <JWT>
Body: { "type": "message", "text": "Create..." }
↓
JWT Middleware:
Extracts user_id from token → request.state.user_id
↓
ChatKit Server (MyChatKitServer.respond):
Gets user_id from context
Creates wrapper functions capturing user_id
Passes wrappers to Agent
↓
OpenAI Agent:
Receives message: "Create a task..."
Selects tool: add_task_wrapper
Calls: add_task_wrapper(title="Buy milk")
↓
Wrapper Function:
Calls: mcp_add_task(user_id="user-123", title="Buy milk")
↓
MCP Tool:
Creates task with correct user_id
Returns: {"task_id": "...", "title": "Buy milk"}
↓
Agent Response:
"I've created 'Buy milk' task ✓"
↓
ChatKit Frontend:
Displays response
Auto-refreshes task list → Sees new task
# Official ChatKit pattern using Runner.run_streamed
result = Runner.run_streamed(
task_agent.agent,
agent_input,
context=agent_context,
)
# Stream events using official stream_agent_response
async for event in stream_agent_response(agent_context, result):
yield event
# Add user message to thread
await self.store.add_thread_item(thread.id, input, context)
# Load conversation history
items_page = await self.store.load_thread_items(
thread.id,
after=None,
limit=30,
order="desc",
context=context,
)
# Convert to agent input
agent_input = await simple_to_agent_input(items)
What it does:
Files to reference:
What it does:
Key setup:
What it does:
Implementation:
Root Cause: user_id not passed to MCP tools
Solution: Use wrapper functions that capture and inject user_id
def add_task_wrapper(title):
return mcp_add_task(user_id=user_id, title=title)
Root Cause: Missing user_id filter in database queries
Solution: Always filter by user_id at the tool level
stmt = select(Task).where(
Task.user_id == user_id,
Task.completed == False
)
Root Cause: Router not included in FastAPI app
Solution: Include router in main.py
from routes import chatkit
app.include_router(chatkit.router)
Root Cause: Custom fetch not adding JWT token
Solution: Ensure authenticatedFetch adds Bearer token
const token = localStorage.getItem('access_token')
headers['Authorization'] = `Bearer ${token}`
For true real-time (not polling):
Structure tool responses for ChatKit widgets:
return {
"tasks": task_list,
"total": len(task_list),
"pending": pending_count,
"message": "You have 5 tasks",
"widget": {
"type": "card",
"items": formatted_items,
}
}
Store conversation history in database:
This skill includes comprehensive resources for building ChatKit chatbots:
Backend Architecture: Complete FastAPI ChatKit server implementation details and patterns
Frontend Integration: Next.js ChatKit widget configuration and authentication
MCP Tools Guide: Creating wrapped tool functions with automatic user_id injection
User Isolation: Three-level user isolation strategy and verification checklist
chatkit_server_template.py - FastAPI ChatKit server boilerplate with all required methods
mcp_wrapper_generator.py - Script to auto-generate MCP tool wrappers
frontend_config_generator.ts - TypeScript config generator for ChatKit frontend setup
chatkit-nextjs-template/ - Complete Next.js project with ChatKit integrated
fastapi-backend-template/ - Complete FastAPI backend with ChatKit server implementation
When building a ChatKit chatbot, verify:
assets/fastapi-backend-template/ and assets/chatkit-nextjs-template/development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.