LiveKit docs › Logic & Structure › Tool definition & use › Toolsets

---

# Toolsets

> Group related tools and add or remove them as a unit.

Available in:
- [ ] Node.js
- [x] Python

## Overview

A `Toolset` bundles related tools under a single ID so you can add or remove them as a group.

Pass a list of tools to the `Toolset` constructor:

```python
from livekit.agents import Agent, function_tool, RunContext
from livekit.agents.llm import Toolset

@function_tool()
async def lookup_user(context: RunContext, user_id: str) -> dict:
    """Look up a user by ID."""
    return {"name": "Jane Doe", "email": "jane@example.com"}

@function_tool()
async def update_user(context: RunContext, user_id: str, email: str) -> str:
    """Update a user's email address."""
    return f"Updated email for {user_id}."

class MyAgent(Agent):
    def __init__(self):
        super().__init__(
            instructions="You are a helpful assistant.",
            tools=[
                Toolset(id="user-management", tools=[lookup_user, update_user])
            ],
        )

```

Toolsets are flattened automatically when sent to the LLM. You can add or remove a toolset as a group using `update_tools()`.

> ℹ️ **Note**
> 
> Tool names must be unique across all tools and toolsets. If a toolset and a standalone tool, or two toolsets, share a tool with the same name, the agent raises a `ValueError`.

## Custom toolsets

Subclass `Toolset` when you need custom initialization, teardown, or dynamic tool loading. Override `setup()` to initialize resources and `aclose()` to clean them up. Both are called automatically by `AgentSession`.

```python
from livekit.agents import Agent, RunContext, function_tool
from livekit.agents.llm import Toolset
from typing_extensions import Self

class WeatherToolset(Toolset):
    def __init__(self):
        super().__init__(id="weather_tools")

        self._lookup = function_tool(
            self._lookup_weather,
            name="lookup_weather",
            description="Look up current weather for a location.",
        )
        self._forecast = function_tool(
            self._get_forecast,
            name="get_forecast",
            description="Get a multi-day weather forecast.",
        )
        self._tools = [self._lookup, self._forecast]

    async def setup(self) -> Self:
        await super().setup()
        # initialize external connections, load config, etc.
        return self

    async def aclose(self) -> None:
        await super().aclose()
        # close connections, release resources

    async def _lookup_weather(self, context: RunContext, location: str) -> str:
        return f"The weather in {location} is sunny."

    async def _get_forecast(
        self, context: RunContext, location: str, days: int = 3
    ) -> str:
        return f"{days}-day forecast for {location}: sunny."


class MyAgent(Agent):
    def __init__(self):
        super().__init__(
            instructions="You are a helpful weather assistant.",
            tools=[WeatherToolset()],
        )

```

The built-in [MCP Toolset](https://docs.livekit.io/agents/logic/tools/mcp.md) is an example of a custom toolset. It connects to an MCP server on `setup()` and disconnects on `aclose()`.

## Dynamic tool discovery

Available in (BETA):
- [ ] Node.js
- [x] Python

Agents with many tools can suffer from degraded LLM accuracy and wasted tokens. Dynamic tool discovery solves this by loading tool definitions on demand instead of all at once. Two kinds of toolsets are available:

- `ToolSearchToolset` exposes a single `tool_search` function. When the LLM calls it, matching tools are added to the LLM's native tool list for the next turn. May be simpler for the model to understand.
- `ToolProxyToolset` exposes exactly two fixed tools, `tool_search` and `call_tool`. The tool list never changes, so providers like OpenAI and Anthropic can reuse their prompt cache across turns. May be better for many tools or cost-sensitive workloads.

Both accept toolsets (including `MCPToolset`), function tools, and standalone tools:

```python
from livekit.agents import Agent, RunContext, function_tool
from livekit.agents.beta.toolsets import ToolProxyToolset
from livekit.agents.llm import Toolset

class WeatherToolset(Toolset):
    def __init__(self):
        super().__init__(id="weather")

    @function_tool()
    async def get_weather(self, context: RunContext, location: str) -> str:
        """Get current weather for a location."""
        return f"Sunny, 72F in {location}"

class FlightToolset(Toolset):
    def __init__(self):
        super().__init__(id="flights")

    @function_tool()
    async def search_flights(self, context: RunContext, origin: str, destination: str) -> str:
        """Search for available flights."""
        return f"Found 3 flights from {origin} to {destination}"

@function_tool()
async def convert_currency(context: RunContext, amount: float, code: str) -> str:
    """Convert an amount to the given currency code."""
    return f"{amount} converted to {code}"

class TravelAgent(Agent):
    def __init__(self):
        super().__init__(
            instructions="You are a travel planning assistant. Use tool_search to find the right tools.",
            tools=[
                ToolProxyToolset(
                    id="travel_tools",
                    tools=[
                        WeatherToolset(),
                        FlightToolset(),
                        convert_currency,
                    ],
                    max_results=3,
                )
            ],
        )

```

Swap `ToolProxyToolset` for `ToolSearchToolset` to use native tool calls instead of the proxy pattern. Both classes share the same core arguments.

The default search strategy uses BM25 ranking, which returns tools ordered by relevance to the query rather than by literal pattern match. For simpler regex-based matching, pass a `KeywordSearchStrategy`:

```python
from livekit.agents.beta.toolsets.tool_search import KeywordSearchStrategy

toolset = ToolProxyToolset(
    id="my_tools",
    tools=[...],
    search_strategy=KeywordSearchStrategy(),
)

```

You can also implement your own strategy by conforming to the `SearchStrategy` protocol.

## Additional resources

The following articles provide more information about the topics discussed in this guide:

- **[Function tools](https://docs.livekit.io/agents/logic/tools/definition.md)**: Define individual tools with decorators, RunContext, and dynamic registration.

- **[Async tools](https://docs.livekit.io/agents/logic/tools/async.md)**: Run long-running tools in the background so the agent can keep talking.

- **[Model Context Protocol (MCP)](https://docs.livekit.io/agents/logic/tools/mcp.md)**: Use `MCPToolset` to expose tools from an MCP server to your agent.

- **[Workflows](https://docs.livekit.io/agents/logic/workflows.md)**: Hand off control between agents that each carry their own toolsets.

---

This document was rendered at 2026-06-07T11:34:27.283Z.
For the latest version of this document, see [https://docs.livekit.io/agents/logic/tools/toolsets.md](https://docs.livekit.io/agents/logic/tools/toolsets.md).

To explore all LiveKit documentation, see [llms.txt](https://docs.livekit.io/llms.txt).