Appearance
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 ResponseKey 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
Every agent ships with 77 built-in tools across 16 categories — task/memory, goals & reflection, workflows, scheduling, contacts, gateways, web & browser, payments, the Stratos wallet, apps, and more.
The complete reference, with descriptions and approval flags, is on a dedicated page:
A few example calls:
json
{
"name": "web_search",
"arguments": { "query": "React hooks tutorial" }
}json
{
"name": "browser_action",
"arguments": {
"sessionId": "session-123",
"action": "click",
"ref": 5
}
}json
{
"name": "execute_workflow",
"arguments": {
"workflowId": "Daily standup",
"inputs": { "email": "user@example.com" },
"goal_id": "goal-abc"
}
}Some tools require approval
15 tools — including send_message, send_payment, stratos_transfer, execute_sql, and create_app — pause for human confirmation before running. See the approval list.
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:
- Tracking recent calls: Last 10 tool calls are remembered
- Argument comparison: Detects same tool with same arguments
- Maximum repeats: Same call allowed maximum 2 times
- 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
- Go to Tools page
- Click Import OpenAPI
- Enter the spec URL (e.g.
https://api.example.com/openapi.json) - Optionally set a cluster name (defaults to the spec's
info.title) - 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_functionEach 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:
| Mode | Description | Token Impact |
|---|---|---|
| Off | Traditional tool calling only | Baseline |
| Selective | Tools as TypeScript hints (~2,400 tokens) | -77% vs Full |
| Full | Complete 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
toolsandconsoleavailable) - 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 grouped into categories, and categories are loaded per message:
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_statusThe decision is made by the typed decision model (Cloudflare Clef by default): for every enabled category it answers "does this message need these tools?", using the latest message, the last three turns and the recently used tool names, and the categories at or above the threshold (default 0.6) are loaded. The regex trigger table below is the fallback — used when routing is switched off, when the decision model times out, or — only when typesafe/jev is pinned — when the data-flow policy withholds the partner model for a customer conversation. Which model answers a tool turn is governed by the model policy (tool role). Agents with a connected MCP server always get the custom category, so a confirmation turn with no trigger word can still reach call_custom_tool.
Every routing decision is recorded in the decision log with the per-category probabilities.
Trigger Keywords (fallback)
| Tool Category | Trigger Keywords |
|---|---|
| Browser | browse, visit, go to, navigate, click, page, website |
| Memory | remember, recall, forget, memory, preference |
| Knowledge | search, find, knowledge, document, vector |
| Workflows | workflow, run, execute, trigger, automate |
| Messaging | send, message, email, telegram, notify |
| Calendar | calendar, schedule, event, reminder, task |
| SQL | query, database, sql, select, table |
| Custom / OpenAPI | custom tool, mcp, tool call, openapi, import api, swagger |
Token Savings
| Configuration | Approximate 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"
}The routing model itself is controlled in Prompt Studio → Tool Categories → Tool routing:
json
{
"toolRouting": {
"jev": true,
"threshold": 0.6
}
}jev: false falls back to regex triggers only. The Code Mode selective prompt path goes through the same router.
Example: Multi-Tool Interaction
User: "Find React tutorials and summarize the best one"
The AI will:
Iteration 1: Call
search_knowledgejson{"name": "search_knowledge", "arguments": {"query": "React tutorials"}}Result: Returns list of tutorials
Iteration 2: Call
browse_urlfor top resultjson{"name": "browse_url", "arguments": {"url": "https://example.com/react-guide"}}Result: Returns page content
Final Response: AI synthesizes information and returns summary
API Reference
Get Available Tools
bash
GET /api/agents/{id}/chat/toolsResponse:
json
{
"tools": [
{
"name": "search_knowledge",
"description": "Search the knowledge base",
"parameters": {...}
},
...
]
}See Tools API for complete endpoint documentation.
