The ChatBotKit Agent SDK provides a powerful framework for building autonomous AI agents that can execute complex tasks using custom tools while leveraging the full capabilities of the ChatBotKit platform. This comprehensive guide covers tool creation, execution modes, integration patterns, and best practices for building intelligent agents.
Why ChatBotKit for Agent Development
Building AI agents with ChatBotKit offers significant advantages over traditional agent frameworks, making it the ideal choice for both rapid prototyping and production deployments.
Lightweight and Portable
Unlike heavyweight agent frameworks that require substantial system resources, the ChatBotKit Agent SDK is exceptionally lightweight and can be deployed anywhere:
- Minimal Footprint: Add agent capabilities to any application with minimal overhead
- Frontend & Backend: Works seamlessly in browser environments, Node.js servers, and serverless functions
- Edge Computing: Runs efficiently on small embedded devices and edge infrastructure
- No Heavy Dependencies: Streamlined architecture means faster cold starts and lower memory usage
- Universal Compatibility: Deploy the same agent code across multiple platforms without modification
This portability means you can build once and deploy everywhere - from IoT devices to enterprise cloud infrastructure.
Managed Complexity
ChatBotKit handles the intricate details of building production-grade AI agents, allowing you to focus on your application logic:
- Model Management: Automatic handling of model selection, fallbacks, and version updates
- Integration Layer: Pre-built connectors to popular services and APIs
- Context Management: Sophisticated conversation context and memory handling
- Rate Limiting & Retries: Built-in resilience and error recovery
- Security & Compliance: Enterprise-grade security measures and data handling
- Quality Assurance: Optimized prompts and behaviors refined through extensive testing
You get enterprise-level capabilities without building and maintaining complex infrastructure.
Built-in Monitoring and Auditing
Production AI agents require comprehensive observability. ChatBotKit provides this out of the box:
- Real-time Monitoring: Track agent performance, token usage, and execution metrics
- Conversation Logs: Complete audit trail of all agent interactions and decisions
- Tool Execution Tracking: Detailed logs of which tools were called, when, and with what results
- Error Reporting: Automatic capture and categorization of failures and issues
- Usage Analytics: Understand how agents are being used and optimize accordingly
- Compliance Ready: Audit logs suitable for regulatory and security requirements
No need to build custom logging infrastructure or integrate third-party monitoring solutions.
ChatBotKit Dashboard as Management Plane
The ChatBotKit dashboard serves as a complete backend for your agents, eliminating the need for custom admin interfaces:
- Team Collaboration: Invite team members with role-based access control
- Agent Configuration: Manage agent settings, prompts, and behaviors through an intuitive UI
- Direct Testing: Test and interact with agents directly from the dashboard
- Built-in Components: Use pre-built UI components to embed agents in your applications
- API Key Management: Generate and manage keys for different environments and teams
- Usage Monitoring: View detailed analytics and usage patterns across all agents
- Version Control: Track changes and roll back configurations when needed
This means faster time-to-market since you don't need to build dedicated dashboards, user management systems, or admin panels. Your team can start managing and deploying agents immediately.
Key Features
- Custom tool integration with type-safe schemas
- Dual execution modes (complete and execute)
- Built-in task planning and progress tracking
- Full ChatBotKit platform integration
- Parallel tool execution
- Streaming events and real-time feedback
- Error handling and recovery
Getting Started
Installation
npm install @chatbotkit/agent @chatbotkit/sdk # or yarn add @chatbotkit/agent @chatbotkit/sdkshell
Prerequisites
- Node.js 20.x or higher
- A ChatBotKit API key
- Basic understanding of async/await patterns
- Familiarity with Zod schema validation
Authentication
Authentication is handled through the ChatBotKit SDK client, which should be initialized with your API key.
import { ChatBotKit } from '@chatbotkit/sdk'; import { complete, execute } from '@chatbotkit/agent'; // Initialize the client const client = new ChatBotKit({ secret: process.env.CHATBOTKIT_API_TOKEN }); // Use with agent functions const stream = complete({ client, // ... other options });javascript
Best practices for API key management:
- Store API keys in environment variables
- Never commit API keys to version control
- Use different keys for development and production
- Rotate keys periodically for security
Tool Creation
Basic Tool Definition
Tools are the building blocks of agent capabilities. Each tool includes a description, input schema, and handler function.
import { z } from 'zod'; const tools = { calculateSum: { description: 'Add two numbers together', input: z.object({ a: z.number().describe('First number'), b: z.number().describe('Second number') }), handler: async ({ a, b }) => { return { result: a + b }; } } };javascript
Tool components:
description: Clear explanation of what the tool does (used by AI to decide when to use it)input: Zod schema defining expected parametershandler: Async function that executes the tool logic
Advanced Tool Examples
import { exec } from 'child_process'; import { promisify } from 'util'; import { readFile, writeFile } from 'fs/promises'; const execAsync = promisify(exec); const advancedTools = { readFile: { description: 'Read contents of a file from the local file system', input: z.object({ path: z.string().describe('File path to read'), encoding: z.string().default('utf-8').describe('File encoding') }), handler: async ({ path, encoding }) => { try { const content = await readFile(path, encoding); return { success: true, content }; } catch (error) { return { success: false, error: error.message }; } } }, executeCommand: { description: 'Execute a shell command and return the output', input: z.object({ command: z.string().describe('Shell command to execute'), cwd: z.string().optional().describe('Working directory') }), handler: async ({ command, cwd }) => { try { const { stdout, stderr } = await execAsync(command, { cwd }); return { success: true, stdout, stderr }; } catch (error) { return { success: false, error: error.message }; } } }, apiRequest: { description: 'Make an HTTP request to an external API', input: z.object({ url: z.string().url().describe('API endpoint URL'), method: z.enum(['GET', 'POST', 'PUT', 'DELETE']).default('GET'), body: z.any().optional().describe('Request body') }), handler: async ({ url, method, body }) => { const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: body ? JSON.stringify(body) : undefined }); const data = await response.json(); return { success: response.ok, data, status: response.status }; } } };javascript
Execution Modes
Complete Mode
Complete mode provides streaming agent responses with tool execution for conversational AI that can take actions.
import { complete } from '@chatbotkit/agent'; const stream = complete({ client, tools, botId: 'bot_123', // Optional: use existing bot configuration model: 'gpt-4o', // Or specify model directly messages: [ { type: 'user', text: 'What is 234 plus 567?' } ] }); // Process the stream for await (const event of stream) { if (event.type === 'token') { process.stdout.write(event.data.token); } else if (event.type === 'toolCallStart') { console.log(`\\\\nCalling tool: ${event.data.name}`); } else if (event.type === 'toolCallEnd') { console.log(`Tool result:`, event.data.result); } }javascript
Event types in complete mode:
token: Streaming text tokens from the AI responsetoolCallStart: Tool execution beginstoolCallEnd: Tool execution completes successfullytoolCallError: Tool execution failed
Execute Mode
Execute mode runs agents with built-in planning, progress tracking, and controlled exit for autonomous task completion.
import { execute } from '@chatbotkit/agent'; const stream = execute({ client, tools, botId: 'bot_123', messages: [ { type: 'user', text: 'Analyze my git changes, check for issues, and create a Jira ticket if needed' } ], maxIterations: 25 }); // Process execution events for await (const event of stream) { if (event.type === 'token') { process.stdout.write(event.data.token); } else if (event.type === 'iteration') { console.log(`\\\\n[Iteration ${event.data.iteration}]`); } else if (event.type === 'exit') { console.log(`\\\\nTask completed with code ${event.data.code}`); if (event.data.message) { console.log(`Message: ${event.data.message}`); } } }javascript
Execute mode provides additional event types:
iteration: New iteration begins (agent continues working)exit: Task completion with status code (0 = success)
System Tools
Execute mode includes three built-in system tools:
// These tools are automatically available in execute mode: plan: { // Create or update task execution plan steps: ['Step 1', 'Step 2', 'Step 3'], rationale: 'Brief explanation of approach' } progress: { // Track completion status completed: ['Finished task 1', 'Finished task 2'], current: 'Working on task 3', blockers: ['Waiting for API access'], nextSteps: ['Complete authentication', 'Test endpoints'] } exit: { // Signal task completion code: 0, // 0 = success, non-zero = failure message: 'All tasks completed successfully' }javascript
Platform Integration
Using Platform Capabilities
Agents automatically have access to all ChatBotKit platform features when you specify a botId:
const stream = execute({ client, tools: localTools, // Your custom tools botId: 'bot_123', // Bot with configured integrations messages: [{ type: 'user', text: 'Check local logs, create Jira ticket if errors found, notify team on Slack' }] });javascript
Platform capabilities available to agents:
- Integrations: Slack, Discord, Jira, Linear, Google Workspace, Notion, etc.
- Datasets: Query knowledge bases for relevant information
- Skillsets: Use pre-built abilities and custom functions
- Authenticated sessions: No need to manage OAuth flows
Combining Local and Remote Tools
The power of the Agent SDK comes from combining local operations with remote integrations:
import { execute } from '@chatbotkit/agent'; import { readFile } from 'fs/promises'; // Define local-only tools const localTools = { scanLocalFiles: { description: 'Scan local directory for specific files', input: z.object({ directory: z.string(), pattern: z.string() }), handler: async ({ directory, pattern }) => { // Local file system operations const { stdout } = await execAsync(`find ${directory} -name "${pattern}"`); return { files: stdout.trim().split('\\\\n') }; } }, analyzeCode: { description: 'Analyze code quality metrics locally', input: z.object({ filePath: z.string() }), handler: async ({ filePath }) => { const content = await readFile(filePath, 'utf-8'); // Perform local analysis return { lines: content.split('\\\\n').length, hasTests: content.includes('describe('), complexity: calculateComplexity(content) }; } } }; // Agent can use both local tools AND remote integrations const stream = execute({ client, tools: localTools, botId: 'bot_with_jira_and_slack', // Bot has Jira and Slack configured messages: [{ type: 'user', text: `Scan src/ for .js files, analyze code quality, create Jira tickets for files with high complexity, and notify #dev-team on Slack with summary` }], maxIterations: 30 });javascript
Real-World Examples
Development Workflow Automation
const devTools = { checkGitStatus: { description: 'Get current git repository status', input: z.object({}), handler: async () => { const { stdout } = await execAsync('git status --porcelain'); return { changes: stdout, hasChanges: stdout.length > 0 }; } }, runTests: { description: 'Execute test suite', input: z.object({ path: z.string().optional() }), handler: async ({ path = '.' }) => { try { const { stdout } = await execAsync(`npm test -- ${path}`); return { success: true, output: stdout }; } catch (error) { return { success: false, error: error.message }; } } } }; const stream = execute({ client, tools: devTools, botId: 'bot_123', messages: [{ type: 'user', text: 'Run all tests, check git status, and create deployment checklist in Jira' }], maxIterations: 20 });javascript
Data Processing Pipeline
const dataTools = { readCSV: { description: 'Read and parse CSV file', input: z.object({ filePath: z.string() }), handler: async ({ filePath }) => { const content = await readFile(filePath, 'utf-8'); const lines = content.split('\\\\n'); const headers = lines[0].split(','); const rows = lines.slice(1).map(line => { const values = line.split(','); return Object.fromEntries(headers.map((h, i) => [h, values[i]])); }); return { headers, rows, count: rows.length }; } }, validateData: { description: 'Validate data against rules', input: z.object({ data: z.array(z.any()), rules: z.object({ requiredFields: z.array(z.string()).optional(), emailFields: z.array(z.string()).optional() }) }), handler: async ({ data, rules }) => { const errors = []; // Validation logic return { valid: errors.length === 0, errors }; } } }; const stream = execute({ client, tools: dataTools, botId: 'bot_with_google_sheets', messages: [{ type: 'user', text: 'Read customers.csv, validate email addresses, update Google Sheet with results' }] });javascript
System Monitoring
const monitoringTools = { checkDiskSpace: { description: 'Check available disk space', input: z.object({}), handler: async () => { const { stdout } = await execAsync('df -h /'); return { output: stdout }; } }, analyzeLog: { description: 'Analyze log file for errors', input: z.object({ logPath: z.string(), pattern: z.string().default('ERROR') }), handler: async ({ logPath, pattern }) => { const { stdout } = await execAsync(`grep "${pattern}" ${logPath} | tail -n 100`); return { matches: stdout.split('\\\\n').filter(Boolean), count: stdout.split('\\\\n').filter(Boolean).length }; } } }; const stream = execute({ client, tools: monitoringTools, botId: 'bot_with_pagerduty_slack', messages: [{ type: 'user', text: 'Check disk space and error logs. If critical issues, create PagerDuty alert and notify #ops' }] });javascript
Error Handling
Tool-Level Error Handling
const tools = { riskyOperation: { description: 'An operation that might fail', input: z.object({ param: z.string() }), handler: async ({ param }) => { try { // Attempt operation const result = await performOperation(param); return { success: true, result }; } catch (error) { // Return structured error return { success: false, error: error.message, code: error.code }; } } } };javascript
Best practices for tool error handling:
- Always wrap operations in try-catch
- Return structured error objects
- Include helpful error messages
- Log errors for debugging
- Don't throw exceptions from handlers
Agent-Level Error Handling
try { for await (const event of execute({ client, tools, botId: 'bot_123', messages: [{ type: 'user', text: 'Complete this task' }] })) { if (event.type === 'toolCallError') { console.error(`Tool error: ${event.data.error}`); // Handle tool failure } else if (event.type === 'exit') { if (event.data.code !== 0) { console.error(`Task failed: ${event.data.message}`); } } } } catch (error) { console.error('Agent execution failed:', error); // Handle fatal errors }javascript
Best Practices
- Tool Design
- Keep tools focused on single responsibilities
- Provide clear, descriptive tool descriptions
- Use detailed Zod schemas with descriptions
- Return structured, consistent results
- Handle errors gracefully
- Security
- Validate all tool inputs
- Sanitize file paths and commands
- Limit tool access scope
- Use environment variables for secrets
- Implement rate limiting for external APIs
- Performance
- Design tools for parallel execution when possible
- Avoid blocking operations in tool handlers
- Implement timeouts for long-running operations
- Cache results when appropriate
- Monitor token usage
- Development
- Test tools independently before agent integration
- Use TypeScript for type safety
- Log tool executions for debugging
- Set appropriate maxIterations limits
- Provide clear task instructions
- Agent Prompts
- Be specific about desired outcomes
- Break complex tasks into clear steps
- Specify output formats
- Include validation criteria
- Provide necessary context
Advanced Patterns
Multi-Step Workflows
const stream = execute({ client, tools: comprehensiveTools, botId: 'bot_123', messages: [{ type: 'user', text: ` Phase 1: Analyze codebase for security issues Phase 2: Generate detailed report with findings Phase 3: Create Jira tickets for high-severity issues Phase 4: Update team documentation in Notion Phase 5: Send summary to #security Slack channel ` }], maxIterations: 50 });javascript
Conditional Tool Execution
const tools = { checkCondition: { description: 'Check if condition is met', input: z.object({ condition: z.string() }), handler: async ({ condition }) => { // Evaluate condition return { met: true, value: 'some_value' }; } }, conditionalAction: { description: 'Perform action based on previous check', input: z.object({ action: z.string(), data: z.any() }), handler: async ({ action, data }) => { // Execute conditional logic return { success: true }; } } };javascript
Progress Monitoring
for await (const event of execute({ client, tools, botId, messages })) { if (event.type === 'iteration') { console.log(`Progress: Iteration ${event.data.iteration}`); } else if (event.type === 'toolCallStart') { console.log(`Executing: ${event.data.name}`, event.data.args); } else if (event.type === 'toolCallEnd') { console.log(`Completed: ${event.data.name}`); } }javascript
Configuration Options
Complete Mode Options
complete({ client, // ChatBotKit client instance tools, // Tool definitions object botId, // Optional: existing bot ID model, // Optional: AI model override messages, // Conversation messages array extensions: { // Optional: additional configuration backstory: 'Custom context for this session', // ... other bot configuration } })javascript
Execute Mode Options
execute({ client, // ChatBotKit client instance tools, // Tool definitions object botId, // Optional: existing bot ID model, // Optional: AI model override messages, // Task messages array maxIterations: 50, // Maximum iteration limit extensions: { // Optional: additional configuration backstory: 'Custom agent context', // ... other bot configuration } })javascript
References
Related Tools and Libraries
- ChatBotKit CLI - Command-line interface with agent mode
- ChatBotKit Node SDK - Core SDK for platform access
- React Prompt Kit - Structured prompt building