Automate with Tools
Create tools, group them in toolsets, and connect via MCP
Tools are functions your AI agents can call during conversations -- CRM lookups, order processing, appointment scheduling, anything your business needs. Define them once in Chanl, and every agent (native or connected via MCP) can use them.
Installation
npm install @chanl/sdkSetup
import { Chanl } from '@chanl/sdk';
const chanl = new Chanl({
apiKey: process.env.CHANL_API_KEY,
});Create a tool
A tool is a function definition (name, description, JSON Schema parameters) plus an execution target (your HTTP endpoint).
const tool = await chanl.tools.create({
name: 'lookup_order',
description: 'Look up order details by order number',
parameters: {
type: 'object',
properties: {
order_id: {
type: 'string',
description: 'The order number (e.g., ORDER-12345)',
},
},
required: ['order_id'],
},
handlerUrl: 'https://api.yourstore.com/orders/lookup',
});
console.log('Created tool:', tool.id);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Function name (snake_case recommended) |
description | string | Yes | What the tool does (shown to the LLM) |
parameters | object | Yes | JSON Schema for input parameters |
handlerUrl | string | Yes | Your API endpoint |
handlerMethod | string | No | GET or POST (default: POST) |
handlerHeaders | object | No | Custom headers to send |
timeout | number | No | Timeout in ms (default: 30000) |
retries | number | No | Retry count on failure (default: 0) |
Returns: Promise<Tool>
Test the tool
Execute a tool directly to verify it works before connecting it to an agent.
const result = await chanl.tools.execute(tool.id, {
order_id: 'ORDER-12345',
});
console.log('Success:', result.success);
console.log('Output:', result.output);
console.log('Duration:', result.duration, 'ms');Returns: Promise<ToolExecutionResult>
Group tools into toolsets
A toolset bundles related tools into a reusable collection. Attach a "Customer Support" toolset to any agent and it gets all the tools at once.
const toolset = await chanl.toolsets.create({
name: 'Customer Support Tools',
description: 'Tools for customer support agents',
toolIds: [lookupOrder.id, processRefund.id, scheduleCallback.id],
});
console.log('Created toolset:', toolset.id);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Toolset name |
description | string | No | What this collection is for |
toolIds | string[] | Yes | Tool IDs to include |
Returns: Promise<Toolset>
Manage toolset membership
// Add a tool to a toolset
await chanl.toolsets.addTool('toolset_abc123', 'tool_new');
// Remove a tool from a toolset
await chanl.toolsets.removeTool('toolset_abc123', 'tool_old');
// List tools in a toolset
const toolset = await chanl.toolsets.get('toolset_abc123');
console.log('Tools:', toolset.tools.map((t) => t.name));Connect via MCP
Tools you create in Chanl are automatically available through the MCP server. Any MCP-compatible client -- Claude Desktop, Cursor, VAPI, custom agents -- connects to your workspace's MCP endpoint and gets access to all your tools.
// Get MCP configuration for your workspace
const config = await chanl.mcp.getConfig();
console.log('MCP Server URL:', config.serverUrl);
console.log('Available Tools:', config.tools.length);Configure MCP in your client:
chanl mcp config
# Follow prompts to configure the MCP serverOnce connected, tell your AI assistant things like "Look up order ORDER-12345" and it calls your tool through Chanl.
Test the MCP connection
const health = await chanl.mcp.testConnection();
console.log('Connected:', health.connected);
console.log('Tools Available:', health.toolCount);
console.log('Latency:', health.latency, 'ms');Example tool definitions
Schedule an appointment
const tool = await chanl.tools.create({
name: 'schedule_appointment',
description: 'Schedule a service appointment for the customer',
parameters: {
type: 'object',
properties: {
customer_id: { type: 'string', description: 'Customer ID' },
service_type: {
type: 'string',
enum: ['installation', 'repair', 'consultation'],
description: 'Type of service',
},
preferred_date: {
type: 'string',
format: 'date',
description: 'Preferred date (YYYY-MM-DD)',
},
},
required: ['customer_id', 'service_type', 'preferred_date'],
},
handlerUrl: 'https://api.yourcompany.com/appointments',
timeout: 10000,
});Process a refund
const tool = await chanl.tools.create({
name: 'process_refund',
description: 'Process a refund for an order',
parameters: {
type: 'object',
properties: {
order_id: { type: 'string', description: 'Order to refund' },
amount: { type: 'number', description: 'Refund amount (omit for full refund)' },
reason: {
type: 'string',
enum: ['damaged', 'wrong_item', 'not_as_described', 'changed_mind', 'other'],
description: 'Reason for refund',
},
},
required: ['order_id', 'reason'],
},
handlerUrl: 'https://api.yourstore.com/refunds/process',
timeout: 15000,
retries: 2,
});Implement a handler
Your handler receives POST requests with the tool parameters as the JSON body. Return a JSON response with the result.
// Express.js example
app.post('/tools/lookup-order', async (req, res) => {
const { order_id } = req.body;
try {
const order = await db.orders.findOne({ orderId: order_id });
if (!order) {
return res.json({
success: false,
error: `Order ${order_id} not found`,
});
}
return res.json({
success: true,
output: {
orderId: order.orderId,
status: order.status,
items: order.items,
total: order.total,
trackingNumber: order.trackingNumber,
},
});
} catch (error) {
return res.status(500).json({
success: false,
error: 'Failed to look up order',
});
}
});Manage tools
// List all tools
const { tools } = await chanl.tools.list();
// Search by name
const { tools } = await chanl.tools.list({ search: 'order' });
// Exclude system tools (kb_search, chanl_reason, etc.)
const { tools } = await chanl.tools.list({ includeSystem: false });
// Update a tool
await chanl.tools.update('tool_abc123', {
description: 'Updated description',
timeout: 20000,
handlerUrl: 'https://new-api.example.com/handler',
});
// Delete a tool
await chanl.tools.delete('tool_abc123');
// Force delete (removes from all toolsets)
await chanl.tools.delete('tool_abc123', { force: true });Full example
Set up a complete customer support tool suite.
import { Chanl } from '@chanl/sdk';
const chanl = new Chanl({
apiKey: process.env.CHANL_API_KEY,
});
async function setupSupportTools() {
const lookupOrder = await chanl.tools.create({
name: 'lookup_order',
description: 'Look up order details by order number or customer email',
parameters: {
type: 'object',
properties: {
order_id: { type: 'string', description: 'Order number' },
email: { type: 'string', description: 'Customer email (alternative to order_id)' },
},
},
handlerUrl: process.env.API_URL + '/tools/orders/lookup',
});
const processRefund = await chanl.tools.create({
name: 'process_refund',
description: 'Process a refund for an order',
parameters: {
type: 'object',
properties: {
order_id: { type: 'string' },
amount: { type: 'number', description: 'Amount to refund (omit for full)' },
reason: { type: 'string', enum: ['damaged', 'wrong_item', 'not_satisfied', 'other'] },
},
required: ['order_id', 'reason'],
},
handlerUrl: process.env.API_URL + '/tools/refunds/process',
});
const scheduleCallback = await chanl.tools.create({
name: 'schedule_callback',
description: 'Schedule a callback for the customer',
parameters: {
type: 'object',
properties: {
phone: { type: 'string', description: 'Phone number' },
preferred_time: { type: 'string', enum: ['asap', '1_hour', '4_hours', 'tomorrow'] },
reason: { type: 'string', description: 'Reason for callback' },
},
required: ['phone', 'preferred_time'],
},
handlerUrl: process.env.API_URL + '/tools/callbacks/schedule',
});
// Bundle into a toolset
const toolset = await chanl.toolsets.create({
name: 'Customer Support Suite',
description: 'Core tools for customer support agents',
toolIds: [lookupOrder.id, processRefund.id, scheduleCallback.id],
});
console.log('Toolset:', toolset.id);
console.log('Tools:', [lookupOrder.name, processRefund.name, scheduleCallback.name]);
return toolset;
}