-
Notifications
You must be signed in to change notification settings - Fork 109
How Tool Calling Works
This guide explains how CAAL discovers, understands, and executes n8n workflows as voice-callable tools. For the complete tool system (spec, registry, submission pipeline), see the CAAL Tool API.
CAAL transforms n8n workflows into LLM tools via the Model Context Protocol (MCP). Any n8n workflow with a webhook trigger and MCP enabled becomes a tool the voice assistant can call.
┌─────────────────────────────────────────────────────────────────┐
│ Voice Request │
│ "What's the weather?" │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 1. Tool Discovery │
│ │
│ On startup (or /reload-tools): │
│ • CAAL calls n8n's MCP server │
│ • search_workflows → list of MCP-enabled workflows │
│ • get_workflow_details → extracts webhook node notes │
│ • Creates tool definitions for LLM │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. LLM Sees Tool │
│ │
│ Tool Name: weather_aus │
│ │
│ Description (from webhook notes): │
│ "Weather tool - forecast and current conditions │
│ for Australia. │
│ Parameters: │
│ - action (required): 'forecast' or 'current' │
│ - location (required): city, suburb, or postcode │
│ - days (optional, default 7): forecast days ahead" │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. LLM Decides to Call Tool │
│ │
│ User said: "What's the weather in Sydney?" │
│ LLM extracts: action = "current", location = "Sydney" │
│ LLM calls: weather_aus({"action":"current","location":"Sydney"}) │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 4. CAAL Executes Tool │
│ │
│ POST http://n8n:5678/webhook/weather_aus │
│ Content-Type: application/json │
│ Body: {"action": "current", "location": "Sydney"} │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 5. n8n Workflow Executes │
│ │
│ Webhook → Switch (action) → HTTP Request → Code → Respond │
│ │
│ Returns: { │
│ "message": "Sydney is 24 degrees and sunny", │
│ "weather": {"temp": 24, "condition": "sunny"} │
│ } │
└─────────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 6. CAAL Speaks Response │
│ │
│ • Reads the "message" field aloud │
│ • Caches the "weather" data for follow-up questions │
│ │
│ Voice Output: "Sydney is 24 degrees and sunny" │
└─────────────────────────────────────────────────────────────────┘
Answer: The webhook node's notes field.
This is the critical piece that makes everything work. The webhook node's notes become the tool description the LLM sees.
Weather tool - forecast and current conditions for Australia.
Parameters:
- action (required): 'forecast' or 'current'
- location (required): city, suburb, or postcode
- days (optional, default 7): forecast days ahead (1-7), use for 'next week' queries
- target_day (optional): specific weekday like 'Monday', use only for 'on Friday' style queries
Examples: 'Sydney weather', 'Melbourne next week' (days=7)
- Start broad, then scope - "Weather tool - forecast for Australia" not "Australian weather tool" (the restrictive version won't match "Sydney weather" queries)
-
Use exact parameter names -
querynot "search term",daysnot "number of days" -
Disambiguate similar params - explain when to use
daysvstarget_day - Include examples mapping natural language to parameters
The LLM reads this description and learns what the tool does, what parameters it accepts, and what format to use. When the user says "Melbourne weather next week", the LLM maps that to {"action": "forecast", "location": "Melbourne", "days": 7}.
Workflows must return both a voice message and structured data:
return {
message: "Brief voice response here", // Spoken aloud
data: [...] // Enables follow-up questions
};-
message- Read aloud by the voice assistant -
data- Cached in conversation context for follow-up questions
return {
message: "Bills 20, Browns 10. Seahawks beat Rams 38-37.",
games: [
{ away: "BUF", awayScore: 20, home: "CLE", homeScore: 10, status: "live" },
{ away: "SEA", awayScore: 38, home: "LAR", homeScore: 37, status: "final" }
]
};Now if the user asks "What about the Cowboys game?", the LLM can check the cached games array without re-calling the tool.
Never return just { message: "..." } - always include the data.
-
Connect to n8n MCP Server
- Configured in CAAL settings: n8n URL + MCP token
- MCP protocol allows programmatic workflow discovery
-
Search for MCP-Enabled Workflows
workflows = await mcp_client.call_tool("search_workflows", {})
- Returns list of workflows with
settings.availableInMCP: true
- Returns list of workflows with
-
Get Workflow Details
for workflow in workflows: details = await mcp_client.call_tool("get_workflow_details", { "workflowId": workflow.id })
- Extracts webhook node
- Reads webhook path (becomes tool name)
- Reads webhook notes (becomes tool description)
-
Register with LLM
- Each workflow becomes a callable tool
- LLM sees tool name + description
- LLM can now decide when to call it
To refresh tool cache after creating/modifying workflows:
curl -X POST http://localhost:8889/reload-toolsOr say "reload tools" to the voice assistant.
A single workflow handling multiple actions via a Switch node.
Naming: service (snake_case) - just the service name
| Workflow Name | Actions | Example Queries |
|---|---|---|
google_tasks |
get, add, complete, delete | "what's on my task list", "add task buy groceries" |
truenas |
status, control | "TrueNAS status", "restart Plex on TrueNAS" |
espn_nhl |
scores, schedule, standings | "NHL scores", "when do the Bruins play next" |
Single-purpose tools that don't fit a suite pattern.
Naming: service_action_object (snake_case)
| Workflow Name | Description |
|---|---|
weather_get_forecast |
Get weather forecast |
date_calculate_days_until |
Calculate days until a date |
Important: The workflow name becomes both the webhook path (/webhook/google_tasks) and the tool name the LLM sees.
All responses are read aloud. Follow these rules:
| Do | Don't |
|---|---|
| Brief and conversational | Long detailed explanations |
| Plain text | Markdown, JSON, symbols |
| Natural language | Technical formats |
| Short names ("Falcons") | Full names ("Atlanta Falcons") |
| Limit lists (3-5 items) | Long enumerated lists |
Good: "Falcons 29, Bucs 28. Seahawks 16, Bears 7."
Bad: "Game 1: Atlanta Falcons versus Tampa Bay Buccaneers, final score 29 to 28..."
Webhook → HTTP Request → Code (format) → Respond
Webhook → Switch (action) → [get branch] → HTTP Request → Code → Respond
→ [add branch] → HTTP Request → Code → Respond
→ [delete branch] → HTTP Request → Code → Respond
For tasks taking >5 seconds, respond immediately then announce when done:
Webhook → Respond ("On it, I'll let you know")
↓
[Long work]
↓
HTTP Request → POST http://caal:8889/announce
| Issue | Solution |
|---|---|
| Tool not appearing | Check workflow is active and "Available in MCP" is enabled |
| Wrong parameters | Update webhook node notes with clear parameter docs |
| Empty response | Ensure Code node returns {message: "...", data: ...}
|
| Webhook 404 | Verify workflow name matches webhook path exactly |
| Timeout | Use async pattern for long-running tasks |
| LLM doesn't call tool | Description may be too restrictive - start broad then scope |
Browse community tools or submit your own via the CAAL Tool Registry.
See the CAAL Tool API for the complete tool spec and submission pipeline, or the Contributing Guide for step-by-step submission instructions.
Getting Started
Architecture
Tools
Integrations
Language Support
Deployment
Links