Tool Calling — Agent Actions
Register any function as a tool. The agent decides when to call it — your code does the work.
Three approaches
| Approach | Where defined | Best for |
|---|---|---|
| registerTools | JS at runtime | Tools that need closures, live state, or complex logic |
| Tool Manifest | Dashboard (JSON) | Simple client actions or server calls, configured without a deploy |
| Server Tools (OpenAPI) | Dashboard (URL) | Calling your existing REST API endpoints directly |
1 — registerTools (programmatic)
Call window.widgent('registerTools', ...) inside the widgent:ready handler. This registers browser-side behavior. The JSON Schema still belongs in the tool manifest.
window.addEventListener('widgent:ready', () => {
window.widgent('registerTools', {
navigateTo: async ({ path }) => {
window.location.href = path;
return { success: true, path };
},
getAccountInfo: async () => {
const data = await fetch('/api/account').then(r => r.json());
return data;
},
});
}); The function return value is sent back to the LLM as the tool result. Register the matching schema in toolsManifest so the LLM knows the tool exists.
2 — Tool Manifest (dashboard JSON)
Define tools in Dashboard → Tools, or via API with a session cookie (sign in at the dashboard first). X-Service-Key does not work for manifest writes.
| Goal | API |
|---|---|
| Add one tool | POST /v1/products/:id/tools |
| Update one tool | PUT /v1/products/:id/tools/:name |
| Remove one tool | DELETE /v1/products/:id/tools/:name |
| Replace all tools | PATCH /v1/products/:id with toolsManifest array |
curl -X POST https://api.widgent.app/v1/products/YOUR_PRODUCT_ID/tools \
-H "Content-Type: application/json" \
-H "Cookie: YOUR_SESSION_COOKIE" \
-d '{ "type": "client", "name": "navigate_to", ... }' {
"type": "client",
"name": "navigate_to",
"description": "Navigate the user to a specific page",
"parameters": {
"type": "object",
"properties": { "path": { "type": "string" } },
"required": ["path"]
},
"handler": "myApp.router.push",
"mockResponse": { "status": "success" }
} {
"type": "server",
"name": "search_docs",
"description": "Search the product knowledge base",
"parameters": {
"type": "object",
"properties": { "query": { "type": "string" } },
"required": ["query"]
},
"url": "https://api.example.com/search",
"method": "POST",
"requiresApproval": false,
"mockResponse": { "results": [{ "title": "Example result", "url": "/docs/example" }] }
} For server tools that need auth, pass a toolToken at init time — it's forwarded with every call, never stored by Widgent:
window.widgent.init({
productId: 'YOUR_PRODUCT_ID',
serviceKey: 'YOUR_SERVICE_KEY',
toolTokens: {
search_docs: 'Bearer sk-...'
}
}); Approval before side effects
Add "requiresApproval": true to any tool that writes data, deletes, sends messages, charges money, or triggers an irreversible action. Read-only tools usually do not need approval.
mockResponse — safe testing
Every tool in the manifest can have a mockResponse. In test sessions (Dashboard → Test tab), the LLM receives mockResponse instead of calling the real handler. This lets you test agent behavior without side effects.
Tool writing tips
- Name clearly —
create_invoicebeatsaction1. The LLM reads the name. - Describe the effect — "Creates a new invoice and returns the invoice ID." Tell the LLM what happens.
- Return structured data — The LLM uses your return value to formulate the next response. Return JSON, not strings.
- Keep tools focused — One action per tool. Compose in the agent, not in the handler.