Function Calling (Tools)

Transform your AI from a passive chatbot into an active assistant that can execute actions in your application.

What are Tools?

Tools (also called function calling) allow the AI to perform actions like:

  • Creating or updating records
  • Triggering workflows
  • Filtering or sorting data
  • Navigating between pages
  • Executing business logic

How It Works

  1. You define tools - Specify what functions are available and their parameters
  2. AI decides when to call - Based on user intent, AI selects the appropriate tool
  3. Handler executes - Your JavaScript function runs
  4. AI uses result - AI incorporates the result into its response

Basic Example

import { InAppAI } from '@inappai/react';
import { useState } from 'react';

function CounterApp() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Counter: {count}</p>

      <InAppAI
        agentId="your-agent-id"
        tools={[
          {
            name: 'incrementCounter',
            description: 'Increment the counter by a specified amount',
            parameters: {
              type: 'object',
              properties: {
                amount: {
                  type: 'number',
                  description: 'Amount to increment (default: 1)',
                },
              },
              required: [],
            },
            handler: async (params) => {
              const amount = params.amount || 1;
              setCount(prev => prev + amount);
              return { success: true, newCount: count + amount };
            },
          },
        ]}
      />
    </div>
  );
}

User: “Increase the counter by 5” AI: [Calls incrementCounter({ amount: 5 })] “I’ve incremented the counter by 5. It’s now at 5.”

Tool Interface

interface Tool {
  name: string;                    // Unique identifier
  description: string;             // When and how to use this tool
  parameters: {                    // JSON Schema definition
    type: 'object';
    properties: Record<string, {
      type: string;                // 'string', 'number', 'boolean', 'array', 'object'
      description: string;         // Parameter explanation
      enum?: string[];             // Optional: allowed values
    }>;
    required: string[];            // Required parameter names
  };
  handler: (params: any) => Promise<any>;  // Async function
}

Complete Example: Todo App

Let the AI manage a todo list:

import { InAppAI } from '@inappai/react';
import { useState } from 'react';

function TodoApp() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Buy groceries', completed: false },
    { id: 2, text: 'Call dentist', completed: false },
  ]);

  return (
    <InAppAI
      agentId="your-agent-id"
      tools={[
        {
          name: 'addTodo',
          description: 'Create a new todo item',
          parameters: {
            type: 'object',
            properties: {
              text: {
                type: 'string',
                description: 'The task description',
              },
            },
            required: ['text'],
          },
          handler: async (params) => {
            const newTodo = {
              id: Date.now(),
              text: params.text,
              completed: false,
            };
            setTodos([...todos, newTodo]);
            return { success: true, todo: newTodo };
          },
        },
        {
          name: 'completeTodo',
          description: 'Mark a todo as completed',
          parameters: {
            type: 'object',
            properties: {
              id: {
                type: 'number',
                description: 'The ID of the todo to complete',
              },
            },
            required: ['id'],
          },
          handler: async (params) => {
            const todo = todos.find(t => t.id === params.id);
            if (!todo) {
              return { success: false, message: 'Todo not found' };
            }
            setTodos(todos.map(t =>
              t.id === params.id ? { ...t, completed: true } : t
            ));
            return { success: true, message: `Completed: ${todo.text}` };
          },
        },
        {
          name: 'deleteTodo',
          description: 'Delete a todo item',
          parameters: {
            type: 'object',
            properties: {
              id: {
                type: 'number',
                description: 'The ID of the todo to delete',
              },
            },
            required: ['id'],
          },
          handler: async (params) => {
            setTodos(todos.filter(t => t.id !== params.id));
            return { success: true, message: 'Todo deleted' };
          },
        },
      ]}
    />
  );
}

Conversation Example:

  • User: “Add a task to finish the report”
  • AI: [Calls addTodo({ text: ‘Finish the report’ })] “I’ve added ‘Finish the report’ to your todo list.”
  • User: “Mark the groceries task as done”
  • AI: [Calls completeTodo({ id: 1 })] “I’ve marked ‘Buy groceries’ as completed.”

Multiple Tools

You can provide multiple tools for different actions:

<InAppAI
  agentId="your-agent-id"
  tools={[
    {
      name: 'searchProducts',
      description: 'Search for products by name or category',
      parameters: {
        type: 'object',
        properties: {
          query: { type: 'string', description: 'Search query' },
        },
        required: ['query'],
      },
      handler: async ({ query }) => {
        const results = await fetch(`/api/products?q=${query}`);
        return await results.json();
      },
    },
    {
      name: 'addToCart',
      description: 'Add a product to the shopping cart',
      parameters: {
        type: 'object',
        properties: {
          productId: { type: 'string', description: 'Product ID' },
          quantity: { type: 'number', description: 'Quantity to add' },
        },
        required: ['productId'],
      },
      handler: async ({ productId, quantity = 1 }) => {
        // Add to cart logic
        return { success: true, cartTotal: newTotal };
      },
    },
    {
      name: 'getCart',
      description: 'Get current shopping cart contents',
      parameters: {
        type: 'object',
        properties: {},
      },
      handler: async () => {
        return {
          items: cart,
          total: calculateTotal(cart),
        };
      },
    },
  ]}
/>

The AI will automatically choose the right tool based on user intent.

Parameter Types

String Parameters

{
  name: 'sendEmail',
  parameters: {
    type: 'object',
    properties: {
      to: { type: 'string', description: 'Recipient email address' },
      subject: { type: 'string', description: 'Email subject' },
      body: { type: 'string', description: 'Email body content' },
    },
    required: ['to', 'subject', 'body'],
  },
  handler: async ({ to, subject, body }) => {
    // Send email logic
  },
}

Number Parameters

{
  name: 'updateQuantity',
  parameters: {
    type: 'object',
    properties: {
      itemId: { type: 'number', description: 'Item ID' },
      quantity: { type: 'number', description: 'New quantity' },
    },
    required: ['itemId', 'quantity'],
  },
  handler: async ({ itemId, quantity }) => {
    // Update quantity logic
  },
}

Enum Parameters (Constrained Values)

{
  name: 'filterProducts',
  parameters: {
    type: 'object',
    properties: {
      category: {
        type: 'string',
        enum: ['electronics', 'clothing', 'books', 'home'],
        description: 'Product category',
      },
      sortBy: {
        type: 'string',
        enum: ['price-asc', 'price-desc', 'name', 'rating'],
        description: 'Sort order',
      },
    },
    required: ['category'],
  },
  handler: async ({ category, sortBy = 'name' }) => {
    // Filter and sort logic
  },
}

Boolean Parameters

{
  name: 'toggleFeature',
  parameters: {
    type: 'object',
    properties: {
      featureName: { type: 'string', description: 'Feature identifier' },
      enabled: { type: 'boolean', description: 'Enable or disable' },
    },
    required: ['featureName', 'enabled'],
  },
  handler: async ({ featureName, enabled }) => {
    // Toggle feature logic
  },
}

Best Practices

1. Clear Descriptions

Write descriptions that explain when and why to use the tool:

// ❌ Vague
description: 'Updates a todo'

// ✅ Clear
description: 'Mark a todo as completed when the user indicates they finished a task'

2. Error Handling

Always handle errors gracefully:

handler: async (params) => {
  try {
    const result = await updateDatabase(params);
    return { success: true, data: result };
  } catch (error) {
    console.error('Tool error:', error);
    return {
      success: false,
      message: 'Failed to update. Please try again.',
    };
  }
}

3. Return Meaningful Data

Return information the AI can use in its response:

// ❌ Minimal
return { success: true };

// ✅ Informative
return {
  success: true,
  message: 'Todo added successfully',
  todo: newTodo,
  totalTodos: todos.length + 1,
};

4. Validate Inputs

Check parameters before executing:

handler: async ({ email, amount }) => {
  if (!email || !email.includes('@')) {
    return { success: false, message: 'Invalid email address' };
  }
  if (amount <= 0) {
    return { success: false, message: 'Amount must be positive' };
  }
  // Proceed with logic
}

5. Async Operations

Use async/await for API calls or database operations:

handler: async ({ userId }) => {
  const user = await fetch(`/api/users/${userId}`).then(r => r.json());
  const orders = await fetch(`/api/users/${userId}/orders`).then(r => r.json());
  return { user, orders };
}

Security Considerations

1. Validate User Permissions

Check if the user is authorized:

handler: async ({ itemId }) => {
  const user = getCurrentUser();
  if (!user.isAdmin) {
    return { success: false, message: 'Permission denied' };
  }
  // Admin-only logic
}

2. Sanitize Inputs

Never trust tool parameters directly:

handler: async ({ query }) => {
  // Sanitize query to prevent injection
  const sanitized = query.replace(/[^\w\s]/gi, '');
  const results = await searchDatabase(sanitized);
  return results;
}

3. Rate Limiting

Prevent abuse by limiting calls:

const callCounts = new Map();

handler: async ({ action }) => {
  const userId = getCurrentUser().id;
  const count = callCounts.get(userId) || 0;

  if (count > 10) {
    return { success: false, message: 'Rate limit exceeded' };
  }

  callCounts.set(userId, count + 1);
  // Execute action
}

Advanced Patterns

Tool Chaining

Tools can call other tools:

const tools = [
  {
    name: 'processOrder',
    handler: async ({ orderId }) => {
      // Validate order
      const order = await validateOrder(orderId);

      // Charge payment (could be another tool)
      const payment = await chargePayment(order);

      // Send confirmation (could be another tool)
      await sendConfirmation(order.email);

      return { success: true, order, payment };
    },
  },
];

Stateful Tools

Access and modify component state:

const [filter, setFilter] = useState('all');
const [sortBy, setSortBy] = useState('date');

tools={[
  {
    name: 'updateView',
    description: 'Update the current view filter and sort order',
    parameters: {
      type: 'object',
      properties: {
        filter: { type: 'string', enum: ['all', 'active', 'completed'] },
        sortBy: { type: 'string', enum: ['date', 'priority', 'name'] },
      },
      required: [],
    },
    handler: async ({ filter: newFilter, sortBy: newSort }) => {
      if (newFilter) setFilter(newFilter);
      if (newSort) setSortBy(newSort);
      return {
        success: true,
        filter: newFilter || filter,
        sortBy: newSort || sortBy,
      };
    },
  },
]}

Debugging Tools

Use console logging to debug tool execution:

handler: async (params) => {
  console.log('Tool called:', params);

  const result = await executeLogic(params);

  console.log('Tool result:', result);
  return result;
}

Next Steps