> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stackone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool Search

> Discover tools using natural language queries with semantic, local, or auto search modes

<Info>
  New to tool search? Start with [Tool Search 101](/agents/python/tool-search-101) for an overview of why, how, and when to use it.
</Info>

## Agent-Driven Search (Recommended)

The simplest way to use tool search: give your agent `tool_search` and `tool_execute`, and let it discover tools on demand.

```python theme={null}
from stackone_ai import StackOneToolSet

toolset = StackOneToolSet()

# LLM receives only 2 tools: tool_search + tool_execute
openai_tools = toolset.openai(mode="search_and_execute")
```

<Tip>
  Only 2 tools are sent to the LLM regardless of how many tools exist in your catalog. Token usage stays constant.
</Tip>

### Framework Examples

<CodeGroup>
  ```python OpenAI theme={null}
  import json
  from openai import OpenAI
  from stackone_ai import StackOneToolSet

  toolset = StackOneToolSet()

  # LLM receives only 2 tools: tool_search + tool_execute
  openai_tools = toolset.openai(mode="search_and_execute", account_ids=["your-account-id"])

  client = OpenAI()
  messages = [
      {
          "role": "system",
          "content": (
              "Use tool_search to find relevant tools, then tool_execute to run them. "
              "Read parameter schemas from tool_search results carefully. "
              "If a tool needs a user URI, search for a 'get current user' tool first."
          ),
      },
      {"role": "user", "content": "List my upcoming Calendly events for the next week."},
  ]

  for _step in range(10):
      response = client.chat.completions.create(
          model="gpt-5.4",
          messages=messages,
          tools=openai_tools,
          tool_choice="auto",
      )

      choice = response.choices[0]
      if not choice.message.tool_calls:
          print(choice.message.content)
          break

      messages.append(choice.message.model_dump(exclude_none=True))
      for tool_call in choice.message.tool_calls:
          result = toolset.execute(tool_call.function.name, tool_call.function.arguments)
          messages.append({
              "role": "tool",
              "tool_call_id": tool_call.id,
              "content": json.dumps(result),
          })
  ```

  ```python LangChain theme={null}
  import json
  from langchain_openai import ChatOpenAI
  from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
  from stackone_ai import StackOneToolSet

  toolset = StackOneToolSet(
      search={"method": "semantic", "top_k": 3},
      execute={"account_ids": ["your-account-id"]},
  )

  # LangChain tools - framework handles execution automatically
  langchain_tools = toolset.langchain(mode="search_and_execute")
  tools_by_name = {tool.name: tool for tool in langchain_tools}
  model = ChatOpenAI(model="gpt-5.4").bind_tools(langchain_tools)

  messages = [
      SystemMessage(content="Use tool_search to find tools, then tool_execute to run them."),
      HumanMessage(content="List my upcoming Calendly events for the next week."),
  ]

  for _step in range(10):
      response = model.invoke(messages)
      if not response.tool_calls:
          print(response.content)
          break

      messages.append(response)
      for tool_call in response.tool_calls:
          tool = tools_by_name[tool_call["name"]]
          result = tool.invoke(tool_call["args"])
          messages.append(ToolMessage(content=json.dumps(result), tool_call_id=tool_call["id"]))
  ```
</CodeGroup>

## Manual Search

When you need to find tools yourself before passing them to an agent:

```python theme={null}
from stackone_ai import StackOneToolSet

toolset = StackOneToolSet()
tools = toolset.search_tools("manage employee records")

# Use with any framework
openai_tools = tools.to_openai()
langchain_tools = tools.to_langchain()
```

No configuration needed. Defaults to auto mode with sensible defaults.

<AccordionGroup>
  <Accordion title="Search modes (auto, semantic, local)">
    | Mode       | Behaviour                                                                                                                                                            |
    | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `auto`     | Tries semantic API first, falls back to local search on failure. Recommended for production.                                                                         |
    | `semantic` | Strict mode. Uses semantic API only, raises `SemanticSearchError` on failure.                                                                                        |
    | `local`    | Hybrid BM25 + TF-IDF search entirely in-process (powered by [bm25s](https://github.com/xhluca/bm25s)). No network call. Useful for offline or low-latency scenarios. |

    ```python theme={null}
    # Auto (default)
    tools = toolset.search_tools("manage employees", search="auto")

    # Semantic only
    tools = toolset.search_tools("manage employees", search="semantic")

    # Local only
    tools = toolset.search_tools("manage employees", search="local")
    ```
  </Accordion>

  <Accordion title="Constructor configuration">
    Set default search options at the constructor level. These apply to all search calls unless overridden per-call.

    ### SearchConfig Options

    | Option           | Type                              | Description                             |
    | ---------------- | --------------------------------- | --------------------------------------- |
    | `method`         | `"auto" \| "semantic" \| "local"` | Search mode (default: `"auto"`)         |
    | `top_k`          | `int`                             | Maximum number of tools to return       |
    | `min_similarity` | `float`                           | Minimum relevance score threshold (0-1) |

    ```python theme={null}
    from stackone_ai import StackOneToolSet

    # Custom search config
    toolset = StackOneToolSet(search={"method": "semantic", "top_k": 5})

    # Disable search
    toolset = StackOneToolSet(search=None)

    # Default: search enabled with method="auto"
    toolset = StackOneToolSet()
    ```

    Per-call overrides take precedence over constructor defaults:

    ```python theme={null}
    # Constructor sets top_k=5 for all search calls
    toolset = StackOneToolSet(search={"method": "semantic", "top_k": 5})

    # Uses constructor defaults: method="semantic", top_k=5
    tools = toolset.search_tools("manage employees")

    # top_k overridden to 10 for this call; method remains "semantic"
    tools = toolset.search_tools("manage employees", top_k=10)
    ```
  </Accordion>

  <Accordion title="Parameters reference">
    | Parameter        | Type                              | Description                                         |
    | ---------------- | --------------------------------- | --------------------------------------------------- |
    | `query`          | `str`                             | Natural language description of what you want to do |
    | `top_k`          | `int \| None`                     | Maximum number of tools to return                   |
    | `search`         | `"auto" \| "semantic" \| "local"` | Search mode (default: `"auto"`)                     |
    | `connector`      | `str \| None`                     | Filter to a specific provider (e.g., `"workday"`)   |
    | `min_similarity` | `float \| None`                   | Minimum relevance score threshold (0-1)             |
    | `account_ids`    | `list[str] \| None`               | Override account IDs for this search                |
  </Accordion>
</AccordionGroup>

## Advanced Patterns

<AccordionGroup>
  <Accordion title="search_action_names(), lightweight preview">
    Returns action IDs and similarity scores without fetching full tool definitions. Use it to inspect results before committing to a full fetch.

    ```python theme={null}
    results = toolset.search_action_names("manage employees", top_k=5)

    for r in results:
        print(f"{r.id}: {r.similarity_score:.2f}")
    ```

    <Tip>
      `search_action_names()` works with just `STACKONE_API_KEY`, no account ID needed. When called without `account_ids`, results come from the full StackOne catalog.
    </Tip>
  </Accordion>

  <Accordion title="get_search_tool(), callable for custom agent loops">
    Returns a callable `SearchTool` for agent loops where the LLM decides what to search for.

    ```python theme={null}
    search_tool = toolset.get_search_tool(search="auto")

    # In an agent loop, call it with natural language queries
    queries = [
        "create a new employee",
        "list job candidates",
        "send a message to a channel",
    ]

    for query in queries:
        tools = search_tool(query, top_k=3)
        tool_names = [t.name for t in tools]
        print(f'"{query}" -> {", ".join(tool_names)}')
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

* [LangChain Integration](/agents/python/frameworks/langchain-integration)
* [OpenAI Integration](/agents/python/frameworks/openai-integration)
* [CrewAI Integration](/agents/python/frameworks/crewai-integration)
* [Tool Filtering](/agents/python/tool-filtering)
