> ## Documentation Index
> Fetch the complete documentation index at: https://algolia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Client-side tools

> Run custom functions in your app to access user data, trigger UI updates, and perform authenticated actions.

export const Filter = () => <Tooltip tip="A filter is a condition that limits which records Algolia returns. Filters often use one or more facet-value pairs, such as brand:Apple AND color:red. You can also filter by numeric values, dates, tags, booleans, or geographic constraints." cta="Filtering" href="/doc/guides/managing-results/refine-results/faceting">
    filter
  </Tooltip>;

Runs custom functions in your app (frontend or backend) to access user data, trigger UI updates, and perform authenticated actions.
Client-side tools follow the [OpenAI Function Calling specification](https://platform.openai.com/docs/guides/function-calling).

For example, for the query "What's in my cart?",
the agent calls `get_user_cart` function in your app, retrieves the user's shopping cart data, and responds with personalized information about items, quantities, and total price.

<img src="https://mintcdn.com/algolia/n_gdBwT_QFZcudWB/doc/guides/algolia-ai/agent-studio/how-to/agent-studio-client-side-tools.png?fit=max&auto=format&n=n_gdBwT_QFZcudWB&q=85&s=58a8dd4fc5b292becceb8ca62c6ee205" alt="Screenshot of a 'Configure client-side tool' dialog with fields for 'Tool name', 'Description', and JSON 'Input schema', plus 'Cancel' and 'Update tool' buttons." width="1892" height="1720" data-path="doc/guides/algolia-ai/agent-studio/how-to/agent-studio-client-side-tools.png" />

## How client-side tools work

```mermaid theme={"system"}
sequenceDiagram
    participant EndUser as user
    participant Agent as Algolia Agent
    participant App as Your app

    EndUser->>Agent: Send message
    Agent->>Agent: Determine client-side tool needed
    Agent->>App: Return client-side tool call request
    App->>App: Run tool locally
    App->>Agent: Send tool result
    Agent->>Agent: Process result
    Agent->>EndUser: Respond with answer
```

<Accordion title="Key capabilities">
  * **user context access**: retrieve shopping cart contents, preferences, order history, and authentication tokens
  * **Action execution**: add items to cart, apply a <Filter />, update profiles, submit forms
  * **UI interaction**: trigger UI updates, show or hide elements, refine search results dynamically
  * **Security**: run in user's security context with proper authentication
  * **Flexibility**: use existing frontend or backend APIs without additional infrastructure
  * **Client-side context**: access local storage, session data, and other browser-specific states
</Accordion>

<Note>
  **Security**: Runs in your app's security context. Agent Studio never stores credentials. Always validate authentication, sanitise inputs, and enforce access control. For more information, see [Client-side security patterns](/doc/guides/algolia-ai/agent-studio/how-to/tools/security#client-side-tools).
</Note>

You must configure client-side tools in two places:

* **Agent Studio:** to define the tool schema.
* **Your frontend or backend code** to run the tool.

## Agent Studio configuration

Define your tools in your agent using JSON schema format.
For example:

<Tabs>
  <Tab title="Get shopping cart contents">
    ```json JSON icon=braces theme={"system"}
    {
      "type": "function",
      "function": {
        "name": "get_user_cart",
        "description": "Retrieves the user's shopping cart contents including items, quantities, and prices.",
        "strict": true,
        "parameters": {
          "type": "object",
          "properties": {},
          "required": [],
          "additionalProperties": false
        }
      }
    }
    ```
  </Tab>

  <Tab title="Add item to cart">
    ```json JSON icon=braces theme={"system"}
    {
      "type": "function",
      "function": {
        "name": "add_to_cart",
        "description": "Adds a product to the user's shopping cart. Use this when the user wants to purchase an item.",
        "strict": true,
        "parameters": {
          "type": "object",
          "properties": {
            "productId": {
              "type": "string",
              "description": "The Algolia objectID of the product to add"
            },
            "quantity": {
              "type": ["integer", "null"],
              "description": "Number of items to add (defaults to 1 if not specified)",
              "minimum": 1,
              "maximum": 99
            }
          },
          "required": ["productId", "quantity"],
          "additionalProperties": false
        }
      }
    }
    ```
  </Tab>
</Tabs>

### Required fields

* `type`: must be `"function"`
* `function.name`: function name (3-64 characters, alphanumeric and underscores only)
* `function.description`: clear explanation of what the tool does and when to use it
* `function.parameters`: JSON schema object
  * `type`: must be `"object"`
  * `properties`: object defining each parameter with type and description
  * `required`: array of required parameter names (can be empty)
  * `additionalProperties`: set to `false` for strict mode. If you're using strict mode:
    * All fields in `properties` must be in `required`
    * Use `["type", "null"]` for optional fields (for example, `"type": ["string", "null"]`)
    * Always set `additionalProperties: false` for objects
    * Use `enum` for restricted values
    * Set `minimum` and `maximum` constraints for numbers

### Optional but recommended fields

* `function.strict`: set to `true` to ensure reliable schema adherence (recommended for production)

### JSON schema

```json JSON icon=braces theme={"system"}
{
  "properties": {
    "email": {
      "type": "string",
      "format": "email",
      "description": "User's email address"
    },
    "priority": {
      "type": "string",
      "enum": ["low", "medium", "high"],
      "default": "medium",
      "description": "Priority level"
    },
    "quantity": {
      "type": "integer",
      "minimum": 1,
      "maximum": 99,
      "description": "Number of items (1-99)"
    }
  }
}
```

**Implementation steps:**

1. Define your tool using JSON schema (follows [OpenAI Function Calling](https://platform.openai.com/docs/guides/function-calling) format)
2. Add the tool to your agent with an API or using **Other tools** in the dashboard

## Frontend or backend code configuration

Run tools from your frontend or backend code.
For example:

<Tabs>
  <Tab title="JavaScript">
    ```js JavaScript icon=code theme={"system"}
    // Handle tool calls from Agent Studio
    async function handleToolCall(toolCall) {
      const { name, arguments: args } = toolCall.function;
      const params = JSON.parse(args);

      switch (name) {
        case 'get_user_cart':
          // Fetch current user's cart
          const cart = await getUserCart(currentUserId);
          return {
            tool_call_id: toolCall.id,
            output: JSON.stringify({
              items: cart.items,
              itemCount: cart.items.length,
              total: cart.total
            })
          };

        case 'add_to_cart':
          // Add product to cart
          await addToCart(
            currentUserId,
            params.productId,
            params.quantity || 1
          );
          return {
            tool_call_id: toolCall.id,
            output: JSON.stringify({
              success: true,
              message: 'Product added to cart'
            })
          };

        default:
          throw new Error(`Unknown tool: ${name}`);
      }
    }
    ```

    For more information about how to implement client-side tools with the InstantSearch Chat UI component,
    see [Client-side tools integration](/doc/guides/algolia-ai/agent-studio/how-to/integration#tools).
  </Tab>

  <Tab title="React InstantSearch">
    ```jsx JSX icon=code theme={"system"}
    import { liteClient as algoliasearch } from 'algoliasearch/lite';
    import { InstantSearch, Chat } from 'react-instantsearch';

    const appID = "ALGOLIA_APPLICATION_ID";
    const apiKey = "ALGOLIA_API_KEY";
    const agentId = "AGENT_ID";

    const searchClient = algoliasearch(appID, apiKey);

    export function App() {
      return (
        <InstantSearch searchClient={searchClient}>
          <Chat
            agentId={agentId}
            tools={{
              get_user_cart: {
                onToolCall: async ({ addToolResult }) => {
                  // Fetch user's shopping cart
                  const cart = await getUserCart(currentUserId);

                  addToolResult({
                    output: {
                      items: cart.items,
                      itemCount: cart.items.length,
                      total: cart.total
                    }
                  });
                }
              },
              add_to_cart: {
                onToolCall: async ({ input, addToolResult }) => {
                  // Add product to cart
                  const { productId, quantity } = input;
                  await addToCart(currentUserId, productId, quantity || 1);

                  addToolResult({
                    output: {
                      success: true,
                      message: 'Product added to cart'
                    }
                  });
                }
              }
            }}
          />
        </InstantSearch>
      );
    }
    ```

    **Key points:**

    * The `tools` prop keys (`get_user_cart`, `add_to_cart`) must match your agent's tool names
    * `onToolCall` runs when the agent calls the tool
    * `addToolResult` sends the output back to the agent
    * `input` contains the parameters the agent passed to the tool

    For more information about how to implement client-side tools with the InstantSearch Chat UI component,
    see [Client-side tools integration](/doc/guides/algolia-ai/agent-studio/how-to/integration#tools).
  </Tab>
</Tabs>

## Advanced usage

### Strict mode

Strict mode ensures the LLM reliably adheres to your JSON Schema instead of using "best-effort" matching.

<Tabs>
  <Tab title="Without strict mode">
    ```json JSON icon=braces theme={"system"}
    {
      "function": {
        "name": "update_cart",
        "parameters": {
          "type": "object",
          "properties": {
            "productId": {"type": "string"},
            "quantity": {"type": "integer"}
          },
          "required": ["productId"]
        }
      }
    }
    ```

    Potential issues:

    * LLM might omit optional fields unpredictably
    * Field types may not match exactly
    * Additional unexpected fields might be included
  </Tab>

  <Tab title="With strict mode">
    ```json JSON icon=braces theme={"system"}
    {
      "function": {
        "name": "update_cart",
        "strict": true,
        "parameters": {
          "type": "object",
          "properties": {
            "productId": {"type": "string"},
            "quantity": {"type": ["integer", "null"]}
          },
          "required": ["productId", "quantity"],
          "additionalProperties": false
        }
      }
    }
    ```
  </Tab>
</Tabs>

#### Strict mode requirements

* All fields in `properties` must be in `required`
* Use `["type", "null"]` for optional fields (for example, `"type": ["string", "null"]`)
* Set `additionalProperties: false` for all objects
* The LLM strictly follows the schema (no best-effort guessing)

#### Benefits of strict mode

* Predictable function calls every time
* Prevents schema drift
* Easier debugging (failures are schema violations, not interpretation issues)
* Recommended for production use

### Render dynamic UI from agent context

Instead of returning data for the agent to describe in text, a client-side tool can return structured data that your frontend renders as UI.
The agent decides what to show, and your app decides how to render it.

Define a `render_component` tool whose parameters describe the component to render and the items it needs:

```json JSON icon=braces theme={"system"}
{
  "type": "function",
  "function": {
    "name": "render_component",
    "description": "Renders a UI component in the app from a typed payload. Use this to show structured results, such as a product carousel, instead of describing them in text.",
    "strict": true,
    "parameters": {
      "type": "object",
      "properties": {
        "component": {
          "type": "string",
          "enum": ["product_carousel", "product_grid", "comparison_table"],
          "description": "The component the frontend renders"
        },
        "items": {
          "type": "array",
          "description": "Records to render, each with the fields the component needs",
          "items": {
            "type": "object",
            "properties": {
              "objectID": { "type": "string", "description": "The Algolia objectID" },
              "title": { "type": "string", "description": "Display title" },
              "price": { "type": "number", "description": "Item price" },
              "image": { "type": "string", "description": "Image URL" }
            },
            "required": ["objectID", "title", "price", "image"],
            "additionalProperties": false
          }
        }
      },
      "required": ["component", "items"],
      "additionalProperties": false
    }
  }
}
```

The round trip works like this:

1. The user asks to compare or browse products.
2. The agent searches with the [Algolia Search tool](/doc/guides/algolia-ai/agent-studio/how-to/tools/algolia-search), then calls `render_component` with a `component` and the matching `items`.
3. Your frontend receives the tool call, renders the named component, and returns a confirmation as the tool result.
4. The agent continues the conversation, referring to what the user now sees.

Render the payload in your frontend, then send the result back to the agent:

```js JavaScript icon=code theme={"system"}
async function handleToolCall(toolCall) {
  const { name, arguments: args } = toolCall.function;
  const params = JSON.parse(args);

  if (name === 'render_component') {
    // Render the named component with the provided items
    renderComponent(params.component, params.items);
    return {
      tool_call_id: toolCall.id,
      output: JSON.stringify({
        success: true,
        rendered: params.component,
        itemCount: params.items.length
      })
    };
  }

  throw new Error(`Unknown tool: ${name}`);
}
```

### Error handling patterns

Return structured errors that the agent can interpret and explain to users:

<Tabs>
  <Tab title="Successful response">
    ```json JSON icon=braces theme={"system"}
    {
      "success": true,
      "data": {
        "cartId": "cart_123",
        "itemCount": 3,
        "total": 149.97
      }
    }
    ```
  </Tab>

  <Tab title="Error response">
    ```json theme={"system"}
    {
      "success": false,
      "error": {
        "code": "OUT_OF_STOCK",
        "message": "This product is currently out of stock",
        "productId": "abc123",
        "suggestedAlternatives": ["def456", "ghi789"]
      }
    }
    ```
  </Tab>
</Tabs>

#### Common error codes

* `NOT_FOUND`: Resource doesn't exist
* `UNAUTHORIZED`: User lacks permissions
* `OUT_OF_STOCK`: Product unavailable
* `INVALID_QUANTITY`: Quantity exceeds limits
* `VALIDATION_ERROR`: Input validation failed

### Security

You must run client-side tools securely within your app.
Use the following patterns to validate users, sanitise inputs, and enforce access control.

#### Always validate authentication

```js JavaScript icon=code theme={"system"}
async function executeToolSecurely(toolCall, userSession) {
  // Verify user is authenticated
  if (!userSession.isAuthenticated) {
    return JSON.stringify({
      success: false,
      error: {code: "UNAUTHORIZED", message: "User not authenticated"}
    });
  }

  // Check authorisation
  if (!hasPermission(userSession.userId, toolCall.function.name)) {
    return JSON.stringify({
      success: false,
      error: {code: "FORBIDDEN", message: "Insufficient permissions"}
    });
  }

  // Run with user context
  return await executeTool(toolCall.function.name, args, userSession);
}
```

#### Sanitise all inputs

```js JavaScript icon=code theme={"system"}
function validateAndSanitize(args) {
  const parsed = JSON.parse(args);

  // Validate productId format
  if (!/^[a-zA-Z0-9_-]+$/.test(parsed.productId)) {
    throw new Error("Invalid productId format");
  }

  // Sanitise quantity
  if (parsed.quantity < 1 || parsed.quantity > 99) {
    throw new Error("Quantity must be between 1 and 99");
  }

  return parsed;
}
```

#### Rate limiting

```js JavaScript icon=code theme={"system"}
const rateLimiter = new RateLimiter({
  tokensPerInterval: 10,
  interval: "minute"
});

async function executeTool(toolCall, userId) {
  if (!await rateLimiter.removeTokens(userId, 1)) {
    return JSON.stringify({
      success: false,
      error: {code: "RATE_LIMIT", message: "Too many requests"}
    });
  }

  // Run the tool
}
```

#### Don't expose sensitive data

```js JavaScript icon=code theme={"system"}
// Bad: returns full user object with sensitive fields
return JSON.stringify(user);

// Good: return only necessary fields
return JSON.stringify({
  name: user.name,
  email: user.email,
  // Password, tokens, and so on are excluded
});
```

## See also

* [Tools overview](/doc/guides/algolia-ai/agent-studio/how-to/tools/overview)
* [Client-side tools integration](/doc/guides/algolia-ai/agent-studio/how-to/integration#tools)
* [Credentials](/doc/guides/algolia-ai/agent-studio/how-to/tools/security)
