Tools
Add custom functionality to your agent with tools
Tools allow your agent to interact with your application by calling custom functions. The agent can intelligently decide when to use these tools based on user requests.
We believe in simplicity, so tools are just functions you provide to the agent. Our agent framework looks at function headers and reconstructs type schemas on the server in order to learn how to use your custom functions.
Overview
There are two types of tools:
tools: Require user confirmation before executionsafeTools: Auto-execute without confirmation
Basic Usage
import { ArctenAgent } from "@arcteninc/core";
function saveUserPreferences(theme: string, language: string) {
localStorage.setItem('theme', theme);
localStorage.setItem('language', language);
return `Saved preferences: theme=${theme}, language=${language}`;
}
function getUserPreferences() {
return {
theme: localStorage.getItem('theme') || 'light',
language: localStorage.getItem('language') || 'en'
};
}
<ArctenAgent
tools={[saveUserPreferences]} // requires user confirmation
safeTools={[getUserPreferences]} // runs without confirmation
/>Regular Tools
Regular tools show an approval UI before execution. Use these for actions that:
- Modify or delete data
- Send communications (emails, notifications)
- Incur costs or have side effects
- Require explicit user approval
function deleteUserAccount(userId: string) {
// Delete account logic
return `Account ${userId} deleted`;
}
function sendEmail(to: string, subject: string, body: string) {
// Send email logic
return `Email sent to ${to}`;
}
function purchaseItem(itemId: string, quantity: number) {
// Purchase logic
return `Purchased ${quantity}x item ${itemId}`;
}
<ArctenAgent
tools={[deleteUserAccount, sendEmail, purchaseItem]}
/>Safe Tools
Safe tools execute automatically without user confirmation. Use these for:
- Read-only operations (fetching data, searching)
- Non-destructive actions (getting current time, reading preferences)
- Actions that don't modify important user data
function getUserProfile(userId: string) {
return { id: userId, name: "John Doe", email: "john@example.com" };
}
function searchProducts(query: string) {
return [
{ id: 1, name: "Product 1", price: 29.99 },
{ id: 2, name: "Product 2", price: 49.99 }
];
}
function getCurrentTime() {
return new Date().toLocaleString();
}
function getWeather(city: string) {
// Fetch weather data
return { city, temp: 72, condition: "sunny" };
}
<ArctenAgent
safeTools={[getUserProfile, searchProducts, getCurrentTime, getWeather]}
/>Best Practices
Naming Functions
Use clear, descriptive function names. The agent reads these names to understand what each tool does.
// Good
function getUserOrderHistory(userId: string) { ... }
function cancelOrder(orderId: string) { ... }
function searchProductsByCategory(category: string) { ... }
// Bad
function func1(id: string) { ... }
function doStuff(x: string) { ... }
function handler(arg: string) { ... }Parameter Names
Use descriptive parameter names. The agent uses these to understand what values to pass.
// Good - clear parameter names
function createInvoice(customerId: string, amount: number, dueDate: string) {
return `Invoice created for customer ${customerId}: $${amount}, due ${dueDate}`;
}
// Less clear
function createInvoice(id: string, amt: number, date: string) {
return `Invoice created`;
}Return Values
Return meaningful, descriptive results. The agent reads these to understand what happened and communicate back to the user.
// Good
function updateSettings(theme: string, notifications: boolean) {
localStorage.setItem('theme', theme);
localStorage.setItem('notifications', String(notifications));
return `Settings updated: theme set to ${theme}, notifications ${notifications ? 'enabled' : 'disabled'}`;
}
// Less helpful
function updateSettings(theme: string, notifications: boolean) {
localStorage.setItem('theme', theme);
localStorage.setItem('notifications', String(notifications));
return "OK";
}Type Safety
Use TypeScript types for better agent understanding:
function createUser(name: string, email: string, age: number) {
// The agent will understand that age must be a number
return { id: crypto.randomUUID(), name, email, age };
}
function toggleFeature(featureName: string, enabled: boolean) {
// The agent will understand that enabled is a boolean
return `Feature ${featureName} ${enabled ? 'enabled' : 'disabled'}`;
}Complex Examples
API Calls
async function fetchUserData(userId: string) {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
return data;
}
async function updateUserProfile(userId: string, name: string, bio: string) {
const response = await fetch(`/api/users/${userId}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, bio })
});
const data = await response.json();
return `Profile updated: ${data.name}`;
}
<ArctenAgent
safeTools={[fetchUserData]}
tools={[updateUserProfile]}
/>Database Operations
async function searchOrders(userId: string, status: string) {
const orders = await db.orders.findMany({
where: { userId, status }
});
return orders;
}
async function cancelOrder(orderId: string) {
const order = await db.orders.update({
where: { id: orderId },
data: { status: 'cancelled' }
});
return `Order ${orderId} cancelled successfully`;
}
<ArctenAgent
safeTools={[searchOrders]}
tools={[cancelOrder]}
/>State Management
function getCartItems() {
const cart = useCartStore.getState().items;
return cart;
}
function addToCart(productId: string, quantity: number) {
useCartStore.getState().addItem(productId, quantity);
return `Added ${quantity}x product ${productId} to cart`;
}
function clearCart() {
useCartStore.getState().clear();
return "Cart cleared";
}
<ArctenAgent
safeTools={[getCartItems]}
tools={[addToCart, clearCart]}
/>DOM Manipulation
Tools can interact with the DOM to control UI elements, scroll positions, and more.
function scrollToSection(sectionId: string) {
const element = document.getElementById(sectionId);
if (!element) {
return `Section "${sectionId}" not found`;
}
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
return `Scrolled to ${sectionId}`;
}
function highlightElement(elementId: string) {
const element = document.getElementById(elementId);
if (!element) {
return `Element "${elementId}" not found`;
}
element.classList.add('highlight');
setTimeout(() => element.classList.remove('highlight'), 2000);
return `Highlighted ${elementId}`;
}
function toggleSidebar() {
const sidebar = document.getElementById('sidebar');
if (!sidebar) {
return 'Sidebar not found';
}
sidebar.classList.toggle('collapsed');
const isCollapsed = sidebar.classList.contains('collapsed');
return `Sidebar ${isCollapsed ? 'collapsed' : 'expanded'}`;
}
function getCurrentScrollPosition() {
return {
x: window.scrollX,
y: window.scrollY,
viewportHeight: window.innerHeight,
documentHeight: document.documentElement.scrollHeight
};
}
<ArctenAgent
safeTools={[getCurrentScrollPosition]}
tools={[scrollToSection, highlightElement, toggleSidebar]}
/>Error Handling
Tools should handle errors gracefully and return descriptive error messages:
async function deletePost(postId: string) {
try {
const response = await fetch(`/api/posts/${postId}`, {
method: 'DELETE'
});
if (!response.ok) {
throw new Error(`Failed to delete: ${response.statusText}`);
}
return `Post ${postId} deleted successfully`;
} catch (error) {
return `Error deleting post: ${error.message}`;
}
}Tips
- Start with a few essential tools and add more as needed
- Test your tools thoroughly before adding them to the agent
- Use
safeToolsfor read operations to provide faster responses - Keep tool functions focused on a single responsibility
- Document complex tools with clear return values and types
- In failure cases, return descriptive error messages (or the default verbose error message) so the agent can plan around errors and use other functions to help the user