Skip to main content

Toolsets

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

ONLY Available in
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:

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.

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 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
|
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:

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:

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: