Skip to content

Tool Calling

Extend AI capabilities with built-in and custom tools through an agentic loop.

Overview

Tool calling allows the AI to:

  • Execute functions and workflows
  • Search knowledge bases (RAG)
  • Browse web pages
  • Execute SQL queries
  • Interact with browser sessions
  • Send emails and notifications
  • Access external APIs

Agentic Loop

The chat system uses an agentic loop for tool calling:

User Message


┌─────────────────────────────┐
│  Agentic Loop               │
│  (max 8 iterations)         │
│                             │
│  For each iteration:        │
│  1. Send "thinking" event   │
│  2. Call LLM with tools     │
│  3. Parse tool calls        │
│  4. If no tools → respond   │
│  5. Execute each tool       │
│  6. Add results to context  │
│  7. Next iteration          │
└─────────────────────────────┘


Final Response

Key Features

  • Maximum 8 iterations: Prevents runaway loops
  • Loop detection: Stops repeated identical tool calls
  • Parallel execution: Multiple tools can run concurrently
  • Result truncation: Large results are trimmed to save context
  • Error handling: Failed tools report errors, loop continues

Built-in Tools

search_knowledge

Search the knowledge base for relevant information.

json
{
  "name": "search_knowledge",
  "arguments": {
    "query": "React hooks tutorial"
  }
}

browse_url

Fetch and read content from a URL.

json
{
  "name": "browse_url",
  "arguments": {
    "url": "https://example.com/article"
  }
}

browser_action

Execute actions in a browser session.

json
{
  "name": "browser_action",
  "arguments": {
    "sessionId": "session-123",
    "action": "click",
    "selector": "#submit-button"
  }
}

analyze_page

Analyze the current browser page content.

json
{
  "name": "analyze_page",
  "arguments": {
    "sessionId": "session-123"
  }
}

execute_sql

Execute a SQL query against the database.

json
{
  "name": "execute_sql",
  "arguments": {
    "query": "SELECT * FROM users WHERE status = 'active'"
  }
}

execute_workflow

Run a workflow and return results.

json
{
  "name": "execute_workflow",
  "arguments": {
    "workflowId": "wf-123",
    "input": {"email": "[email protected]"}
  }
}

send_email

Send an email message.

json
{
  "name": "send_email",
  "arguments": {
    "to": "[email protected]",
    "subject": "Hello",
    "body": "Message content"
  }
}

get_schedule_info

Get information about scheduled tasks.

json
{
  "name": "get_schedule_info",
  "arguments": {
    "scheduleId": "sched-123"
  }
}

create_schedule

Create a new scheduled task.

json
{
  "name": "create_schedule",
  "arguments": {
    "name": "Daily Report",
    "cron": "0 9 * * *",
    "workflowId": "wf-123"
  }
}

Tool Message Format

Tool Call (from AI)

json
{
  "role": "assistant",
  "content": null,
  "toolCalls": [
    {
      "id": "tc_abc123",
      "name": "search_knowledge",
      "arguments": {"query": "React tutorials"}
    }
  ]
}

Tool Result (from system)

json
{
  "role": "tool",
  "toolCallId": "tc_abc123",
  "content": "{\"results\": [...]}"
}

Streaming Tool Events

During streaming, tool execution emits SSE events:

tool_call Event

Sent when a tool is being called:

event: message
data: {"type": "tool_call", "id": "tc_abc123", "name": "search_knowledge", "arguments": {"query": "React"}}

tool_result Event

Sent when tool execution completes:

event: message
data: {"type": "tool_result", "toolCallId": "tc_abc123", "name": "search_knowledge", "success": true, "result": [...], "resultPreview": "Found 5 results"}

For errors:

event: message
data: {"type": "tool_result", "toolCallId": "tc_abc123", "success": false, "error": "Search failed"}

Loop Detection

The system prevents infinite loops by:

  1. Tracking recent calls: Last 10 tool calls are remembered
  2. Argument comparison: Detects same tool with same arguments
  3. Maximum repeats: Same call allowed maximum 2 times
  4. Force exit: Loop terminates after 8 iterations regardless

Enabling Tools

Via API Request

json
{
  "content": "Search for React tutorials",
  "enableTools": true
}

Via Agent Settings

Configure in agent settings:

json
{
  "tools_enabled": true,
  "enabled_tools": ["search_knowledge", "browse_url", "execute_sql"]
}

Via Chat Interface

Toggle the "Tools" switch in the chat settings panel.

Custom Tools via MCP

Add custom tools using MCP (Model Context Protocol) servers:

json
{
  "type": "mcp",
  "config": {
    "server_id": "my-mcp-server",
    "tool_name": "custom_search"
  }
}

See MCP Servers for configuration details.

OpenAPI Auto-Import

Import all endpoints from an OpenAPI 3.x JSON spec as callable HTTP tools, grouped under a tool cluster.

Importing via UI

  1. Go to Tools page
  2. Click Import OpenAPI
  3. Enter the spec URL (e.g. https://api.example.com/openapi.json)
  4. Optionally set a cluster name (defaults to the spec's info.title)
  5. All paths and operations become individual HTTP tools

Importing via API

bash
POST /api/agents/{id}/tools/import-openapi
Content-Type: application/json

{
  "url": "https://api.example.com/openapi.json",
  "cluster": "my_api"
}

Returns the list of created tools.

Agent Self-Provisioning

The agent can import APIs during conversation using the built-in import_openapi_tools tool:

User: "I need to work with the Petstore API"

Agent: [calls import_openapi_tools]
       url: "https://petstore.swagger.io/v2/swagger.json"
       cluster: "petstore"

Agent: "I've imported 20 Petstore API endpoints. What would you like to do?"

User: "Look up pet #42"

Agent: [calls call_custom_tool]
       toolName: "getPetById"
       arguments: { "id": 42 }

The agent can also remove imported APIs with delete_tool_cluster.

Tool Clusters

Imported tools are grouped by cluster name. On the Tools page, clusters appear as collapsible sections:

▼ Petstore API (3 tools)
  GET  /pets           - List all pets
  GET  /pets/{petId}   - Get pet by ID
  POST /pets           - Create a pet

▼ Ungrouped (2 tools)
  my_webhook_tool
  my_js_function

Each cluster can be deleted as a group, removing all tools in the cluster at once.

How Imported Tools Execute

Imported HTTP tools use baseUrl + pathTemplate from the OpenAPI spec:

  • Path parameters are substituted: /pets/{petId} becomes /pets/42
  • Query parameters are appended for GET requests
  • GET/HEAD requests don't send a body
  • POST/PUT/PATCH requests send non-path arguments as JSON body

Trigger Keywords

The custom tools category (including OpenAPI import) activates when messages contain: openapi, import api, api spec, swagger, custom tool, mcp, tool call

Tool Schema

Define tools with JSON Schema parameters:

json
{
  "name": "get_weather",
  "description": "Get current weather for a location",
  "parameters": {
    "type": "object",
    "properties": {
      "location": {
        "type": "string",
        "description": "City name or coordinates"
      },
      "units": {
        "type": "string",
        "enum": ["celsius", "fahrenheit"],
        "default": "celsius"
      }
    },
    "required": ["location"]
  }
}

Result Handling

Truncation

Large results are automatically truncated:

  • Results over 10,000 characters are truncated
  • Base64 images are replaced with placeholders
  • Preview text is generated for UI display

Screenshot Results

Browser tools that return screenshots:

  • Full image stored in R2
  • Placeholder added to context: [image displayed above]
  • URL provided for display in UI

Best Practices

1. Clear Tool Descriptions

Write descriptive tool descriptions:

json
{
  "description": "Search the product catalog by name, category, or price range. Returns up to 10 matching products with name, price, and availability."
}

2. Validate Parameters

Define required fields and types:

json
{
  "parameters": {
    "properties": {
      "email": {
        "type": "string",
        "format": "email",
        "description": "Customer email address"
      }
    },
    "required": ["email"]
  }
}

3. Handle Errors Gracefully

Return clear error messages:

json
{
  "error": "Product not found",
  "code": "NOT_FOUND",
  "suggestion": "Try searching with a different query"
}

4. Limit Tool Count

Only enable needed tools:

  • Reduces model confusion
  • Faster response times
  • Lower token costs
  • Better focused responses

5. Monitor Tool Usage

Track tool metrics:

  • Call frequency
  • Success/failure rates
  • Response times
  • Token usage per tool

Code Mode

Code Mode allows the LLM to write JavaScript code instead of making structured tool calls. This is more efficient for complex, multi-step tasks.

Why Code Mode?

Traditional tool calling requires one LLM round-trip per tool call. With Code Mode:

  • Fewer API calls - Multiple tool calls in one code block
  • Better orchestration - Use JavaScript for loops, conditionals, error handling
  • More natural - LLM can express complex logic as code
  • Cost savings - Reduced token usage and latency

Code Mode Options

Configure in Settings → Prompts → Code Mode:

ModeDescriptionToken Impact
OffTraditional tool calling onlyBaseline
SelectiveTools as TypeScript hints (~2,400 tokens)-77% vs Full
FullComplete TypeScript API (~10,700 tokens)+8x vs Selective

How It Works

When Code Mode is enabled, the LLM can respond with JavaScript code blocks:

javascript
// Example: Browse a site and take action
const page = await tools.browseUrl({ url: "https://example.com" });
const analysis = await tools.analyzePage({
  sessionId: page.sessionId,
  task: "find login button"
});
await tools.browserAction({
  sessionId: page.sessionId,
  action: "click",
  selector: analysis.selector
});

Available Functions

The tools object exposes all agent tools as async functions:

typescript
// Memory
await tools.remember({ key: "user_preference", value: "dark mode" });
await tools.recall({ key: "user_preference" });

// Browser
await tools.browseUrl({ url: "https://...", task: "..." });
await tools.browserAction({ action: "click", selector: "#btn" });

// Knowledge
await tools.searchKnowledge({ query: "..." });

// Workflows
await tools.executeWorkflow({ workflowId: "...", inputs: {} });

// Messaging
await tools.sendMessage({ gateway: "telegram", recipient: "...", message: "..." });

// And more...

Code Execution Sandbox

Code runs in a secure sandbox with:

  • Limited scope (only tools and console available)
  • No access to fetch, globals, or environment
  • Async execution with timeout limits
  • Captured logs and tool call history

Selective Tool Loading

By default, all 30+ tools are included in every prompt, consuming ~10,000 tokens. Selective loading dramatically reduces this.

How It Works

Tools are loaded based on message triggers:

User: "Search for React tutorials"
       → Loads: search_knowledge, browse_url

User: "Send a message to John on Telegram"
       → Loads: send_message, list_gateways, get_contact

User: "Run my daily report workflow"
       → Loads: execute_workflow, list_workflows, get_workflow_status

Trigger Keywords

Tool CategoryTrigger Keywords
Browserbrowse, visit, go to, navigate, click, page, website
Memoryremember, recall, forget, memory, preference
Knowledgesearch, find, knowledge, document, vector
Workflowsworkflow, run, execute, trigger, automate
Messagingsend, message, email, telegram, notify
Calendarcalendar, schedule, event, reminder, task
SQLquery, database, sql, select, table
Custom / OpenAPIcustom tool, mcp, tool call, openapi, import api, swagger

Token Savings

ConfigurationApproximate Tokens
All tools~10,700
Selective (typical)~2,400
Minimal context~800

Configuration

Enable selective loading in Settings → Prompts:

json
{
  "codeMode": "selective",
  "toolLoadingMode": "context-aware"
}

Example: Multi-Tool Interaction

User: "Find React tutorials and summarize the best one"

The AI will:

  1. Iteration 1: Call search_knowledge

    json
    {"name": "search_knowledge", "arguments": {"query": "React tutorials"}}
  2. Result: Returns list of tutorials

  3. Iteration 2: Call browse_url for top result

    json
    {"name": "browse_url", "arguments": {"url": "https://example.com/react-guide"}}
  4. Result: Returns page content

  5. Final Response: AI synthesizes information and returns summary

API Reference

Get Available Tools

bash
GET /api/agents/{id}/chat/tools

Response:

json
{
  "tools": [
    {
      "name": "search_knowledge",
      "description": "Search the knowledge base",
      "parameters": {...}
    },
    ...
  ]
}

See Tools API for complete endpoint documentation.