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, RunContextfrom 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().
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_toolfrom livekit.agents.llm import Toolsetfrom typing_extensions import Selfclass 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 selfasync def aclose(self) -> None:await super().aclose()# close connections, release resourcesasync 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
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:
ToolSearchToolsetexposes a singletool_searchfunction. 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.ToolProxyToolsetexposes exactly two fixed tools,tool_searchandcall_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_toolfrom livekit.agents.beta.toolsets import ToolProxyToolsetfrom livekit.agents.llm import Toolsetclass 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 KeywordSearchStrategytoolset = 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
Define individual tools with decorators, RunContext, and dynamic registration.
Async tools
Run long-running tools in the background so the agent can keep talking.
Model Context Protocol (MCP)
Use MCPToolset to expose tools from an MCP server to your agent.
Workflows
Hand off control between agents that each carry their own toolsets.