---
title: "Server Tools"
icon: "lucide/Wrench"
description: "Define backend tools for your Built-in Agent."
---
## What are Server Tools?

Server tools are functions that run on your backend that the Built-in Agent can invoke. They're defined using `defineTool()` with Zod schemas for type-safe parameters.

## When should I use this?

- Your agent needs to access databases, APIs, or other backend services
- You want type-safe tool parameters with validation
- The tool logic requires server-side secrets or resources

## Defining a tool

```typescript title="src/copilotkit.ts"
import { BuiltInAgent, defineTool } from "@copilotkit/runtime/v2";
import { z } from "zod";

const getWeather = defineTool({
  name: "getWeather",
  description: "Get the current weather for a location",
  parameters: z.object({
    location: z.string().describe("The location's name"),
  }),
  execute: async ({ location }) => {
    // Your implementation here
    return { temperature: 72, condition: "sunny", location };
  },
});

const builtInAgent = new BuiltInAgent({
  model: "openai:gpt-5.4-mini",
  tools: [getWeather],
  maxSteps: 2                   //Important for tool calls
});
```

## Tool response

Tools can return any JSON-serializable value. The agent uses the response to continue the conversation.

## Multiple tools

Pass an array of tools — the agent chooses which to call based on the user's request:

```typescript title="src/copilotkit.ts"
const searchDocs = defineTool({
  name: "searchDocs",
  description: "Search the documentation for relevant articles",
  parameters: z.object({
    query: z.string().describe("The search query"),
  }),
  execute: async ({ query }) => {
    const results = await search(query);
    return { results, count: results.length };
  },
});

const createTicket = defineTool({
  name: "createTicket",
  description: "Create a support ticket",
  parameters: z.object({
    title: z.string().describe("Ticket title"),
    priority: z.enum(["low", "medium", "high"]).describe("Ticket priority"),
    description: z.string().describe("Detailed description of the issue"),
  }),
  execute: async ({ title, priority, description }) => {
    const ticket = await db.tickets.create({ title, priority, description });
    return { ticketId: ticket.id, status: "created" };
  },
});

const builtInAgent = new BuiltInAgent({
  model: "openai:gpt-5.4-mini",
  tools: [searchDocs, createTicket], // [!code highlight]
  maxSteps: 2                        
});
```

## Complex Zod schemas

Use nested objects, arrays, enums, and optional fields for sophisticated tool parameters:

```typescript
const bookFlight = defineTool({
  name: "bookFlight",
  description: "Search for and book flights",
  parameters: z.object({
    trip: z.object({
      origin: z.string().describe("Origin airport code (e.g., SFO)"),
      destination: z.string().describe("Destination airport code (e.g., JFK)"),
      date: z.string().describe("Departure date in YYYY-MM-DD format"),
    }),
    passengers: z.array(
      z.object({
        name: z.string(),
        seatPreference: z.enum(["window", "middle", "aisle"]).optional(),
      })
    ).describe("List of passengers"),
    class: z.enum(["economy", "business", "first"]).default("economy"),
  }),
  execute: async ({ trip, passengers, class: seatClass }) => {
    const flights = await searchFlights(trip, seatClass);
    return { flights, passengerCount: passengers.length };
  },
});
```

## Error handling

Throw errors or return error objects from your tool — the agent will see the error and can inform the user or try a different approach:

```typescript
const getUser = defineTool({
  name: "getUser",
  description: "Look up a user by email",
  parameters: z.object({
    email: z.string().email().describe("The user's email address"),
  }),
  execute: async ({ email }) => {
    const user = await db.users.findByEmail(email);
    if (!user) {
      throw new Error(`No user found with email: ${email}`); // [!code highlight]
    }
    return { id: user.id, name: user.name, role: user.role };
  },
});
```

## Multi-step tool calling

By default, the agent performs a single step. If your agent needs to chain tool calls (e.g., search first, then create a ticket), set `maxSteps`:

```typescript
const builtInAgent = new BuiltInAgent({
  model: "openai:gpt-5.4-mini",
  maxSteps: 5, // [!code highlight]
  tools: [searchDocs, createTicket, getUser],
});
```

With `maxSteps: 5`, the agent can:
1. Call `searchDocs` to find relevant info
2. Process the result
3. Call `createTicket` with details from the search
4. Continue until done (up to 5 iterations)

<Callout type="info">
See [Advanced Configuration](/built-in-agent/advanced-configuration) for more options like `toolChoice`, `temperature`, and `providerOptions`.
</Callout>

<Callout type="info" title="Using a custom backend?">
  If you're using the [Custom Agent](/backend/custom-agent) instead of `BuiltInAgent`, see the "With Tools" examples for how to wire tools with AI SDK, TanStack AI, or custom backends.
</Callout>
