--- title: "SearchableToolset" id: searchabletoolset slug: "/searchabletoolset" description: "Enable agents to dynamically discover tools from large catalogs using keyword-based search." --- # SearchableToolset Enable agents to dynamically discover tools from large catalogs using keyword-based search.
| | | | --- | --- | | **Mandatory init variables** | `catalog`: A list of Tools and/or Toolsets, or a single Toolset | | **API reference** | [SearchableToolset](/reference/tools-api#searchabletoolset) | | **GitHub link** | https://github.com/deepset-ai/haystack/blob/main/haystack/tools/searchable_toolset.py | | **Package name** | `haystack-ai` |
## Overview `SearchableToolset` is designed for working with large tool catalogs. Instead of exposing all tools at once, which can overwhelm the LLM context, it provides a single `search_tools` bootstrap tool. The agent uses this tool to find and load specific tools from the catalog using BM25 keyword search. Once the agent calls `search_tools`, the matching tools become immediately available and the agent can invoke them in subsequent iterations. ### Modes of operation `SearchableToolset` operates in one of two modes depending on catalog size: - **Search mode** (default for large catalogs): The agent starts with only the `search_tools` bootstrap tool and discovers other tools on demand. This is activated when the catalog size meets or exceeds `search_threshold`. - **Passthrough mode** (small catalogs): All tools are exposed directly, with no discovery step needed. This is activated automatically when the catalog has fewer tools than `search_threshold`. ### Parameters - `catalog` (required): The source of tools — a list of `Tool` and/or `Toolset` instances, or a single `Toolset`. This includes [MCPTool](mcptool.mdx) and [MCPToolset](mcptoolset.mdx) instances. - `top_k` (optional): The default number of tools returned by each `search_tools` call. Default is `3`. - `search_threshold` (optional): Minimum catalog size to activate search mode. Catalogs smaller than this value use passthrough mode instead. Default is `8`. :::info `SearchableToolset` does not support adding new tools after initialization or merging with other toolsets. Use `catalog` to provide all tools upfront. ::: ### Warm-up `SearchableToolset` builds its search index during `warm_up()`. When used with an [`Agent`](../pipeline-components/agents-1/agent.mdx), constructing the Agent does not trigger this — warm-up happens when you call `Agent.warm_up()` or automatically at run time. All tool names in the catalog must be unique: `warm_up()` raises a `ValueError` if the catalog contains tools with duplicate names, since a search hit could otherwise resolve to the wrong tool. The Agent evaluates its `exit_conditions` at runtime, so an exit condition can name any tool in the catalog, even one the agent has not discovered yet. ## Usage ### Basic usage with an Agent ```python from typing import Annotated from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage from haystack.tools import create_tool_from_function, SearchableToolset def get_weather(city: Annotated[str, "The city to get the weather for"]) -> str: """Get current weather for a city.""" return f"Sunny, 22°C in {city}" def search_web(query: Annotated[str, "The search query"]) -> str: """Search the web for information.""" return f"Results for: {query}" # Build a catalog from tools catalog = [ create_tool_from_function(get_weather), create_tool_from_function(search_web), # ... many more tools ] toolset = SearchableToolset(catalog=catalog) agent = Agent( chat_generator=OpenAIChatGenerator(), tools=toolset, ) # The agent initially sees only `search_tools`. It will call it to find relevant tools, # then use the discovered tools to answer the question. result = agent.run(messages=[ChatMessage.from_user("What's the weather in Milan?")]) print(result["messages"][-1].text) ``` ### Customizing the bootstrap tool You can customize the name, description, and parameter descriptions of the `search_tools` bootstrap tool: - `search_tool_name`: Custom name for the bootstrap tool. Default is `"search_tools"`. - `search_tool_description`: Custom description for the bootstrap tool. - `search_tool_parameters_description`: Custom descriptions for the bootstrap tool's parameters. Keys must be a subset of `{"tool_keywords", "k"}`. ```python toolset = SearchableToolset( catalog=catalog, search_tool_name="find_tools", search_tool_description="Search for tools in the catalog by keyword.", search_tool_parameters_description={ "tool_keywords": "Keywords to find tools, e.g. 'email send'", "k": "Max number of tools to return", }, ) ``` ### Reusing the toolset across multiple agent runs You can safely reuse the same `SearchableToolset` instance across multiple agent runs, including concurrent ones. Each `Agent` run operates on an isolated, run-scoped copy of the toolset (created with [`spawn()`](toolset.mdx#run-scoped-copies-and-tool-selection)), so tools discovered in one run do not persist into, or collide with, other runs — every run starts fresh from the catalog: ```python agent = Agent( chat_generator=OpenAIChatGenerator(), tools=toolset, ) result1 = agent.run(messages=[ChatMessage.from_user("What's the weather in Milan?")]) # The next run starts fresh: tools discovered in the previous run are not carried over result2 = agent.run(messages=[ChatMessage.from_user("Search for news about AI.")]) ``` If you drive the toolset directly (outside an `Agent`), you can call `clear()` to reset the discovered tools yourself.