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

# Client Tools

> Create custom tools that extend AI agents with your own functions and APIs

## Overview

Client tools are custom functions that the voice agent can use during the conversation. They are defined and implemented on the client side.

## Simple Example

Here's a complete example with one tool:

### Step 1: Define the Tool

```typescript theme={null}
const getTimeSchema = {
  name: "get_time",
  type: "function",
  description: "Get the current time",
  parameters: {
    type: "object",
    properties: {},
    required: [],
  },
};
```

<Note>Tool schemas are OpenAI compatible. See the [OpenAI Function Calling guide](https://platform.openai.com/docs/guides/function-calling) for more details on defining functions.</Note>

### Step 2: Implement the Function

```typescript theme={null}
import { type ClientTool } from "@outspeed/client";

// params & context can be skipped here since this tool doesn't use them
const getTime: ClientTool<{}> = (params, context) => {
  return new Date().toLocaleTimeString();
};
```

<Note>The return value from your function is sent directly to the AI model, which uses it to generate its response to the user.</Note>

**Always return a value** - even for action-based tools:

* **Data tools** (weather, calculations): Return the actual data
* **Action tools** (generate image, open browser): Return acknowledgment like "Image generated successfully" or "Browser tab opened"
* **On failure**: Return error description like "Failed to generate image: rate limit error"

This tells the AI whether your tool succeeded or failed. See the [implementation section](#implementation) for more details.

### Step 3: Configure Session

```typescript theme={null}
const sessionConfig = {
  // rest of config...
  tools: [getTimeSchema],
};

const conversation = useConversation({
  clientTools: {
    get_time: getTime,
  },
});
```

That's it! When the user asks "What time is it?", the agent will:

1. Call your `getTime()` function
2. Receive the return value (e.g., "2:30:45 PM")
3. Use that information to respond to the user

## Advanced Example with Context

Here's a more complex tool with typed parameters and context usage:

```typescript theme={null}
import { type ClientTool } from "@outspeed/client";

const setTimer: ClientTool<{ time: number; prompt: string }> = ({ time, prompt }, context) => {
  setTimeout(() => {
    // show a toast if you want to
    // toast.info("Timer completed!");

    // we let the model know that the timer is done so that it can respond to the user
    context.sendText(
      `🔔 TIMER ALERT: The timer you set ${time} seconds ago has finished.
  
Timer prompt: "${prompt}"
Completed at: ${new Date().toLocaleString()}
  
This is an automated system notification. Please proceed with any actions related to this timer.`,
    );
  }, time * 1000);

  // we set the timer and let the model know that the timer is set
  return "Timer set";
};
```

The `context` parameter provides access to conversation methods like `sendText()` for sending messages back to the AI after your tool completes.

## Tool Schema Format

Tool schemas are OpenAI compatible. See the [OpenAI Function Calling guide](https://platform.openai.com/docs/guides/function-calling) for more details on defining functions.

```typescript theme={null}
{
  name: string,           // Unique tool identifier
  type: "function",       // Always "function" for client tools
  description: string,    // Clear description for the AI
  parameters: {
    type: "object",
    properties: {
      [paramName]: {
        type: string,       // "string", "number", "boolean", "array", "object"
        description: string // Parameter description
      }
    },
    required: string[]      // Required parameter names
  }
}
```

## Best Practices

### Tool Design

* **Clear descriptions**: Help the AI understand when and how to use each tool
* **Specific parameters**: Define precise parameter types and descriptions
* **Single purpose**: Each tool should do one thing well
* **Predictable naming**: Use descriptive, consistent naming conventions

### Implementation

**Always return meaningful values** - the AI uses your return value to respond to the user:

```typescript theme={null}
import { type ClientTool } from "@outspeed/client";

// ✅ Good: Return actual data
const getWeather: ClientTool<{ city: string }> = ({ city }, context) => {
  return "72°F and sunny in San Francisco";
};

// ✅ Good: Return success confirmation
const sendEmail: ClientTool<{ to: string; subject: string }> = ({ to, subject }, context) => {
  // ... send email logic
  return `Email sent to ${to}`;
};

// ✅ Good: Return error details
const uploadFile: ClientTool<{ filename: string }> = ({ filename }, context) => {
  try {
    // ... upload logic
    return "File uploaded successfully";
  } catch (error) {
    return `Upload failed: ${error.message}`;
  }
};

// ❌ Bad: Don't return undefined/null
const badTool: ClientTool<{}> = (params, context) => {
  // The AI gets nothing to work with
  return null;
};
```

**Handle errors gracefully**:

```typescript theme={null}
const robustTool: ClientTool<{ param: string }> = ({ param }, context) => {
  try {
    if (!param?.trim()) {
      return "Parameter is required";
    }

    const result = performOperation(param);

    // notice that we're returning something that model can use to respond to the user
    return result || "Operation completed but no data returned";
  } catch (error) {
    return `Error: ${error.message}`; // for the model to understand the error
  }
};
```

**Use async/await for API calls**:

```typescript theme={null}
const fetchData: ClientTool<{ query: string }> = async ({ query }, context) => {
  try {
    const response = await fetch(`/api/search?q=${query}`);
    const data = await response.json();
    return `Found ${data.results.length} results for "${query}"`;
  } catch (error) {
    return "Search service unavailable"; // again, for the model to understand what went wrong
  }
};
```

### Performance

* **Cache results**: Cache API responses when appropriate
* **Timeout handling**: Set reasonable timeouts for external calls
* **Rate limiting**: Respect API rate limits
* **Graceful degradation**: Provide fallbacks when tools fail

## Error Handling

```typescript theme={null}
export async function robustToolFunction({ param }: { param: string }) {
  try {
    // Validate input
    if (!param || param.trim() === "") {
      return "Parameter is required";
    }

    // Perform operation
    const result = await someApiCall(param);

    // Validate result
    if (!result) {
      return "No data available";
    }

    return result;
  } catch (error) {
    console.error("Tool error:", error);

    // Return user-friendly error message
    if (error instanceof Error) {
      return `Error: ${error.message}`;
    }

    return "An unexpected error occurred";
  }
}
```

## Combining with System Tools

You can use client tools alongside system tools:

```typescript theme={null}
const sessionConfig = {
  // rest of config...
  tools: [getTimeSchema],
  system_tools: [
    { name: "end_call", enabled: true },
    { name: "skip_turn", enabled: true },
  ],
};
```
