Skip to content

How Tool Calling Works

Corey McCallum edited this page Jan 31, 2026 · 2 revisions

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.

Overview

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.

The Complete Flow

┌─────────────────────────────────────────────────────────────────┐
│                        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"                 │
└─────────────────────────────────────────────────────────────────┘

Key Question: How Does CAAL Know What Parameters to Send?

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.

Example 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 (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)

What Makes a Good Description

  • 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 - query not "search term", days not "number of days"
  • Disambiguate similar params - explain when to use days vs target_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}.

Response Format

Workflows must return both a voice message and structured data:

return {
  message: "Brief voice response here",  // Spoken aloud
  data: [...]  // Enables follow-up questions
};

Why Both?

  • message - Read aloud by the voice assistant
  • data - Cached in conversation context for follow-up questions

Example

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.

Tool Discovery Process

When CAAL Starts

  1. Connect to n8n MCP Server

    • Configured in CAAL settings: n8n URL + MCP token
    • MCP protocol allows programmatic workflow discovery
  2. Search for MCP-Enabled Workflows

    workflows = await mcp_client.call_tool("search_workflows", {})
    • Returns list of workflows with settings.availableInMCP: true
  3. 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)
  4. Register with LLM

    • Each workflow becomes a callable tool
    • LLM sees tool name + description
    • LLM can now decide when to call it

Reload Tools

To refresh tool cache after creating/modifying workflows:

curl -X POST http://localhost:8889/reload-tools

Or say "reload tools" to the voice assistant.

Workflow Naming Convention

Tool Suites (Preferred for Related Actions)

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"

Individual Tools

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.

Voice Output Guidelines

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..."

Common Patterns

Pattern 1: Simple API Query

Webhook → HTTP Request → Code (format) → Respond

Pattern 2: Tool Suite (Switch-Based)

Webhook → Switch (action) → [get branch] → HTTP Request → Code → Respond
                           → [add branch] → HTTP Request → Code → Respond
                           → [delete branch] → HTTP Request → Code → Respond

Pattern 3: Async/Long-Running

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

Troubleshooting

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

Creating & Sharing Tools

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.