Tool Design: Connecting Agents to Reality
Tools are how your agent accesses truth — real-time inventory, current availability, actual pricing, and the ability to take actions like creating bookings or processing orders. Without tools, agents can only guess. With tools, they become reliably useful.
VOX provides two categories of tools:
- Core Platform Tools — Pre-built utilities available to every agent
- Custom HTTP Tools — API integrations you define to connect your agent to your systems
Core Platform Tools
These tools come built-in with every VOX agent. No configuration needed — just reference them in your prompt.
| Tool Name | What It Does | When to Use | Parameters |
|---|---|---|---|
| getCurrentTime | Returns current local time and timezone | User asks for time, or agent needs to check business hours | None |
| launchWebsite | Opens a URL in a new browser tab | Direct users to external resources, documentation, or your checkout page | url (string, http/https) |
| copyToClipboard | Copies text to user's clipboard | Share confirmation codes, IDs, or referral links | text (string) |
| scrapeWebsite | Fetches and returns website content as markdown/HTML | Summarize dynamic content from your site or external sources | url (string, http/https) |
| changeBackgroundColor | Toggles between light and dark UI themes | User requests dark mode for accessibility | None |
| partyMode | Triggers confetti and celebration animations | Celebrate successful bookings or purchases | None |
| show_component | Renders rich UI components (catalogs, galleries, confirmations, forms) | Display visual results from API calls | component_name, title, description, size, url, media, props |
show_component: The Visual Workhorse
The show_component tool is your most powerful UI renderer. It automatically transforms API data into rich visual experiences.
Supported Components:
catalog_results— Product/room listings with images, prices, and detailsreservation_confirmation— Booking confirmations with all detailspayment_form— Stripe payment forms for transaction completionquote_summary— Price breakdowns and quotesroom— Detailed room/unit display with photos and amenitiesvideo— Video playerimage_viewer— Single image displaymedia_gallery— Multiple images/videos in a gallery
Example Usage in Prompt:
{
"show_component": {
"when": ["Displaying search results", "Showing booking confirmation"],
"args": ["component_name", "props"],
"success_say": "Reference the visual and guide next steps"
}
}
Custom HTTP Tools: The Real Power
Custom tools connect your agent to your APIs — enabling searches, bookings, inventory checks, and any other business logic your APIs support.
HTTP Tool Anatomy
Every custom tool is a JSON descriptor following this schema:
{
"kind": "http_tool",
"name": "search_units",
"description": "Search available hotel rooms by dates and preferences",
"parameters": {
"type": "object",
"properties": {
"check_in": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
"check_out": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
"guests": { "type": "number", "minimum": 1 },
"room_type": { "type": "string", "enum": ["villa", "suite", "standard"] }
},
"required": ["check_in", "check_out", "guests"]
},
"http": {
"method": "GET",
"urlTemplate": "https://your-api.com/api/units/search?check_in={{args.check_in}}&check_out={{args.check_out}}&guests={{args.guests | number}}",
"headers": {
"authorization": "Bearer {{secrets.api_key}}",
"content-type": "application/json"
},
"okField": "ok",
"timeoutMs": 8000,
"pruneEmpty": true
},
"ui": {
"onSuccess": {
"open": {
"component_name": "catalog_results",
"title": "Available Rooms",
"props": {
"items": "{{response.units}}"
}
}
},
"onError": {
"open": {
"component_name": "alert",
"title": "No Availability",
"description": "We couldn't find rooms matching your criteria"
}
}
},
"enabled": true,
"priority": 5,
"version": 1
}
Field-by-Field Breakdown
Always 'http_tool' for HTTP-based tools
Unique identifier used in prompts and logs
Clear explanation of what the tool does — helps the LLM decide when to use it
JSON Schema defining required and optional arguments
The HTTP Section
This is where the magic happens — defining how to call your API.
{
"http": {
"method": "GET | POST | PUT | PATCH | DELETE",
"urlTemplate": "https://your-api.com/endpoint/{{args.param}}",
"headers": {
"authorization": "Bearer {{secrets.api_key}}",
"content-type": "application/json"
},
"jsonBodyTemplate": {
"key": "{{args.value}}",
"nested": {
"field": "{{args.nested_value | json}}"
}
},
"okField": "ok",
"timeoutMs": 8000,
"pruneEmpty": true
}
}
Field Explanations:
| Field | Purpose | Notes |
|---|---|---|
method | HTTP verb | GET, POST, PUT, PATCH, DELETE |
urlTemplate | API endpoint with parameter interpolation | Use {{args.param_name}} for substitution |
headers | HTTP headers (auth, content-type, etc.) | Use {{secrets.key}} for secure values |
jsonBodyTemplate | Request body for POST/PUT/PATCH | Objects are templated recursively |
okField | Field in response indicating success | If present and truthy, call succeeded; if omitted, any 2xx is success |
timeoutMs | Max wait time for API response | Default 8000ms; max 120000ms |
pruneEmpty | Remove empty strings, nulls, empty arrays/objects before sending | Prevents API errors from unexpected empty values |
Templating Rules (CRITICAL)
The platform uses a templating system with strict rules. Follow these or tool calls will fail:
-
Always prefix with
args.✅ Correct: Useargs.check_inin template ❌ Wrong: Usecheck_inwithout prefix -
Use type coercion filters ✅ Correct:
args.guests | number❌ Wrong:args.guestswithout filter (might send string "2" instead of number 2) -
Common Filters:
| number— Coerce to number| bool— Coerce to boolean| json— Pass complex objects| upper— Uppercase strings| default('value')— Fallback for missing values| default('USD') | upper— Chain filters
-
For nested objects, use
| json✅"customer": "{{args.customer | json}}"❌"customer": "{{args.customer}}"(would stringify the object) -
Secrets are available via
secrets.*✅"authorization": "Bearer {{secrets.api_key}}"Never hardcode API keys in tool descriptors
The UI Section
Define what happens visually when the tool succeeds or fails.
{
"ui": {
"onSuccess": {
"open": {
"component_name": "catalog_results",
"title": "Available Rooms",
"description": "{{response.count}} rooms match your search",
"size": "lg",
"props": {
"items": "{{response.units}}",
"currency": "{{args.currency | default('USD') | upper}}"
}
}
},
"onError": {
"open": {
"component_name": "alert",
"title": "Search Failed",
"description": "We couldn't complete your search. Please try again."
}
}
}
}
UI Rendering Flow:
- Tool calls your API
- If
okFieldis truthy (or 2xx response withoutokField), useonSuccessUI - If
okFieldis falsy or non-2xx, useonErrorUI - Template strings in UI props are filled with
{{response.*}}and{{args.*}} - Component renders automatically in the chat interface
Real-World Tool Examples
Example 1: Search Products (E-Commerce)
{
"kind": "http_tool",
"name": "search_products",
"description": "Search product catalog by query string",
"parameters": {
"type": "object",
"properties": {
"query": { "type": "string" },
"limit": { "type": "number", "default": 10 },
"category": { "type": "string" }
},
"required": ["query"]
},
"http": {
"method": "GET",
"urlTemplate": "https://your-store.com/api/products?q={{args.query}}&limit={{args.limit | number}}&category={{args.category}}",
"headers": {
"authorization": "Bearer {{secrets.store_api_key}}"
},
"okField": "success",
"timeoutMs": 5000,
"pruneEmpty": true
},
"ui": {
"onSuccess": {
"open": {
"component_name": "catalog_results",
"title": "Product Results",
"props": {
"items": "{{response.products}}"
}
}
}
},
"enabled": true,
"priority": 5
}
Example 2: Create Reservation (Hospitality)
{
"kind": "http_tool",
"name": "create_reservation",
"description": "Book a hotel room",
"parameters": {
"type": "object",
"properties": {
"unit_id": { "type": "string" },
"check_in": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
"check_out": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
"guest": {
"type": "object",
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" },
"phone": { "type": "string" }
},
"required": ["name", "email"]
}
},
"required": ["unit_id", "check_in", "check_out", "guest"]
},
"http": {
"method": "POST",
"urlTemplate": "https://your-hotel.com/api/reservations",
"headers": {
"authorization": "Bearer {{secrets.hotel_api_key}}",
"content-type": "application/json"
},
"jsonBodyTemplate": {
"unit_id": "{{args.unit_id}}",
"check_in": "{{args.check_in}}",
"check_out": "{{args.check_out}}",
"guest": "{{args.guest | json}}"
},
"okField": "success",
"timeoutMs": 10000
},
"ui": {
"onSuccess": {
"open": {
"component_name": "reservation_confirmation",
"title": "Reservation Confirmed!",
"props": {
"confirmation_number": "{{response.confirmation_id}}",
"unit_name": "{{response.unit.name}}",
"check_in": "{{response.check_in}}",
"check_out": "{{response.check_out}}",
"guest_name": "{{response.guest.name}}"
}
}
},
"onError": {
"open": {
"component_name": "alert",
"title": "Booking Failed",
"description": "{{response.message}}"
}
}
},
"enabled": true,
"priority": 10
}
Tool Design Best Practices
Do:
- ✅ Design APIs with agents in mind — Return structured JSON with consistent field names
- ✅ Include an
okorsuccessfield — Makes success/failure detection reliable - ✅ Use descriptive tool names —
search_unitsis better thansearch1 - ✅ Provide detailed descriptions — Helps the LLM choose the right tool
- ✅ Use parameter patterns — Validate date formats, emails, phone numbers
- ✅ Set reasonable timeouts — 5-10s for most APIs; longer for complex operations
- ✅ Always use
pruneEmpty— Prevents accidental empty value bugs
Don't:
- ❌ Don't forget type coercion — Always use
| number,| bool,| jsonfilters - ❌ Don't hardcode secrets — Use
{{secrets.*}}instead - ❌ Don't skip error UI — Users need to know when things fail
- ❌ Don't make tools too broad —
do_everythingis not a good tool; make focused tools - ❌ Don't assume fast APIs — Set timeouts and handle delays gracefully
Testing Your Tools
Before integrating into your agent, test tools independently:
Call the tool with correct parameters and verify successful API call and UI rendering
Omit required fields — tool should fail gracefully with clear error
Simulate timeouts or errors — verify onError UI displays correctly
Test boundary conditions: empty results, maximum limits, special characters
Integration Checklist
Before deploying your agent with custom tools:
- All tool names are unique and descriptive
- Tool descriptions clearly explain when to use each one
- Parameters use proper JSON Schema types and patterns
- URL templates use
{{args.param}}syntax correctly - Type coercion filters (
| number,| json, etc.) are applied - Secrets are referenced via
{{secrets.*}}, not hardcoded -
okFieldmatches your API's success indicator - Timeouts are set appropriately for each API
- Both
onSuccessandonErrorUI are defined - Tools have been tested with real API calls
- Prompt references each tool with
when,args,success_say, andhandle_errors
Next Steps
With your tools designed, complete your agent configuration and deploy: