> ## 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 Discovery

> Implement search-based tool discovery so your agent loads only the tools it needs, reducing context size and token usage.

Large action catalogs would overflow the model's context if every tool were loaded up front. Search-based discovery lets the agent find the right action at runtime, keeping the token footprint flat as your catalog grows — see the [overview](/optimize/search-and-execute) for why it matters.

<Note>
  StackOne [actions](/gateway/concepts/actions) are exposed to agents as **tools** — an action in the catalog becomes a tool in your agent's hands. The two terms describe the same operation from either side.
</Note>

## Overview

There are two levels of control over which tools an agent sees — the **connector profile** sets hard boundaries that everything inherits, then each **protocol** discovers or narrows within them.

1. **Connector profile level** — [Scoping Connectors](/secure/scoping-connectors) on the connector profile sets hard, admin-level boundaries that every protocol and caller inherits.

2. **Protocol level** — chosen per toolset or per call:

| Method                                                | What it does                                                                                                       | Compatible protocols | When to use                                               |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -------------------- | --------------------------------------------------------- |
| [Search & Execute](#search-and-execute) (Recommended) | The agent is only given two tools: `tool_search` + `tool_execute`. Token cost stays flat however large the catalog | MCP, Agent SDK, A2A  | Open-ended assistants, large catalogs, unknown tasks      |
| [Tool Filtering](#fetch-tools-with-filters)           | Narrow what a fetch returns by provider or action glob patterns                                                    | Agent SDK            | Constrained agents with a focused, predictable scope      |
| [Manual search](#manual-search)                       | Your code searches the catalog by natural language upfront, then hands the matches to the agent                    | Agent SDK            | Known tasks where your code assembles the toolset upfront |

<Accordion title="How Search works" icon="layer-group">
  1. **Search** — the agent (or your code) calls `search` with a natural language query.
  2. **Discover** — available connectors and tool definitions are fetched for your linked accounts.
  3. **Match** — each connector's tools are searched in parallel via the semantic search API.
  4. **Rank** — results are matched to tool definitions, ranked by relevance, and deduplicated.
  5. **Return** — a ready-to-use tools collection, filtered to what the configured account IDs can access.

  ```mermaid theme={null}
  graph TB
      User["User"] --> Agent["Your AI Agent"]
      Agent --> SearchTools["Search Tools"]
      Agent --> SearchActionNames["Search Action Names"]
      SearchTools --> MCP["MCP Server"]
      SearchTools --> SemanticAPI["Semantic Search API"]
      SearchTools --> Agent
      Agent --> Execute["Execute Tool"]

      style User fill:#f3f4f6,stroke:#d1d5db,stroke-width:2px,color:#374151
      style Agent fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e40af
      style SearchTools fill:#4ade80,stroke:#22c55e,stroke-width:2px,color:#15803d
      style SearchActionNames fill:#4ade80,stroke:#22c55e,stroke-width:2px,color:#15803d
      style SemanticAPI fill:#16a34a,stroke:#15803d,stroke-width:2px,color:#fff
      style MCP fill:#16a34a,stroke:#15803d,stroke-width:2px,color:#fff
      style Execute fill:#16a34a,stroke:#15803d,stroke-width:2px,color:#fff
  ```
</Accordion>

## Search and Execute

### MCP

StackOne's MCP server exposes Search & Execute through the `tool-mode` query parameter. See [MCP Tool modes](/embed/call-actions/mcp#tool-discovery-modes) for the parameters, client setup examples, and the search/execute tool reference.

To use it in your agent's client, follow the respective [Agent Setup](/connect/agent-setup/overview) walkthroughs.

### Agent SDK

Hand the agent `tool_search` and `tool_execute` — only two tools reach the LLM regardless of catalog size. For examples, see the [framework guide](/embed/call-actions/agent-sdk#calling-actions) for the framework you're building with.

### Actions API

Search directly over the catalog, then run the result via RPC. See [Search connector actions](/platform/api-reference/actions/search-connector-actions-by-semantic-similarity) and [Make an RPC call](/platform/api-reference/actions/make-an-rpc-call-to-an-action).

## Tool filtering

<Note>
  Tool Filtering is only available when using the [Agent SDK](/embed/call-actions/agent-sdk) protocol.
</Note>

Tool filtering allows you to perform a primitive matching pattern against the tool names to reduce the tools exposed to the agent. Filter on either attribute, or combine both in one fetch:

| Attribute   | Accepts                                | Example                                 |
| ----------- | -------------------------------------- | --------------------------------------- |
| `providers` | Connector keys                         | `["hibob", "workday"]`                  |
| `actions`   | Action names — exact or wildcard (`*`) | `["hibob_create_employee", "*_list_*"]` |

Tool names follow the format `provider_action`. Use the wildcard `*` to match similarly named actions, for example:

| Actions Attribute         | Matches                                                                                                                                  |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `*_list_*`                | `bamboohr_list_employees` <br /> `bamboohr_list_company_files`                                                                           |
| `*_employees`             | `bamboohr_list_employees` <br /> `bamboohr_get_changed_employees`                                                                        |
| `*_employees, *employee*` | `bamboohr_list_employees` <br /> `bamboohr_get_changed_employees` <br /> `bamboohr_create_employee` <br /> `bamboohr_get_employee_photo` |

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Filter by providers
  const byProviders = await toolset.fetchTools({
    providers: ["hibob", "workday"],
    accountIds: ["your-account-id"],
  });

  // Filter by actions with exact match
  const byActions = await toolset.fetchTools({
    actions: ["hibob_search_employees", "hibob_create_employee"],
    accountIds: ["your-account-id"],
  });

  // Filter by actions with glob patterns
  const byGlob = await toolset.fetchTools({
    actions: ["*_list_*"],
    accountIds: ["your-account-id"],
  });

  // Combine multiple filters
  const combined = await toolset.fetchTools({
    providers: ["hibob"],
    actions: ["*_list_*"],
    accountIds: ["your-account-id"],
  });
  ```

  ```python Python theme={null}
  # Filter by providers
  tools = toolset.fetch_tools(
      providers=["hibob", "workday"],
      account_ids=["your-account-id"],
  )

  # Filter by actions with exact match
  tools = toolset.fetch_tools(
      actions=["hibob_search_employees", "hibob_create_employee"],
      account_ids=["your-account-id"],
  )

  # Filter by actions with glob patterns
  tools = toolset.fetch_tools(
      actions=["*_list_*"],
      account_ids=["your-account-id"],
  )

  # Combine multiple filters
  tools = toolset.fetch_tools(
      providers=["hibob"],
      actions=["*_list_*"],
      account_ids=["your-account-id"],
  )
  ```
</CodeGroup>

More examples: [stackone-ai-node](https://github.com/StackOneHQ/stackone-ai-node/tree/main/examples) (TypeScript) and [stackone-ai-python](https://github.com/StackOneHQ/stackone-ai-python/tree/main/examples) (Python).

## Manual search

<Note>
  Manual Search is only available when using the [Agent SDK](/embed/call-actions/agent-sdk) protocol.
</Note>

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

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { StackOneToolSet } from '@stackone/ai';

  const toolset = new StackOneToolSet();
  const tools = await toolset.searchTools('manage employee records');

  // Use with any framework
  const openAITools = tools.toOpenAI();
  const aiSdkTools = await tools.toAISDK();
  ```

  ```python 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()
  ```
</CodeGroup>

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

<Tip>
  Write queries that describe the business task ("find employees hired this year") rather than the technical operation ("list employees filtered by start date"). Search is semantic.
</Tip>

### Search modes

| Mode       | Behavior                                                                                                       |
| ---------- | -------------------------------------------------------------------------------------------------------------- |
| `auto`     | Tries the semantic API first, falls back to local search on failure. Recommended for production.               |
| `semantic` | Strict mode. Uses the semantic API only, and throws / raises `SemanticSearchError` on failure.                 |
| `local`    | Hybrid BM25 + TF-IDF search entirely in-process. No network call. Useful for offline or low-latency scenarios. |

<Note>
  Local search is powered by [Orama](https://orama.com/) in TypeScript and [bm25s](https://github.com/xhluca/bm25s) in Python.
</Note>

<CodeGroup>
  ```typescript TypeScript theme={null}
  // Auto (default)
  const tools = await toolset.searchTools('manage employees', { search: 'auto' });

  // Semantic only
  const semantic = await toolset.searchTools('manage employees', { search: 'semantic' });

  // Local only
  const local = await toolset.searchTools('manage employees', { search: 'local' });
  ```

  ```python 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")
  ```
</CodeGroup>

<Accordion title="Search Accuracy">
  Semantic search uses enriched embeddings: action descriptions are expanded with related terms before embedding, so "onboard new hire" matches "Create Employee" even though the wording differs. Local search relies on BM25 keyword matching, which works for exact terms but misses natural language phrasing. Tested across 103 semantically-challenging queries against 9,340 actions:

  | Approach                         | Hit\@5 (all connectors) | Hit\@5 (per connector) |
  | -------------------------------- | ----------------------- | ---------------------- |
  | BM25 only (`local`)              | 21%                     | 65%                    |
  | Enriched embeddings (`semantic`) | 84%                     | 90%                    |
</Accordion>

### Search options

<AccordionGroup>
  <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'`)         |
    | `topK` / `top_k`                   | `number` / `int`                  | Maximum number of tools to return       |
    | `minSimilarity` / `min_similarity` | `number` / `float`                | Minimum relevance score threshold (0-1) |

    <CodeGroup>
      ```typescript TypeScript theme={null}
      import { StackOneToolSet } from '@stackone/ai';

      // Custom search config
      const toolset = new StackOneToolSet({ search: { method: 'semantic', topK: 5 } });

      // Disable search
      const disabled = new StackOneToolSet({ search: null });

      // Default: search enabled with method: 'auto'
      const defaultToolset = new StackOneToolSet();

      // Per-call overrides take precedence over constructor defaults:
      // method stays 'semantic' from the constructor, topK overridden to 10
      const more = await toolset.searchTools('manage employees', { topK: 10 });
      ```

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

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

      # Disable search
      disabled = StackOneToolSet(search=None)

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

      # Per-call overrides take precedence over constructor defaults:
      # method stays "semantic" from the constructor, top_k overridden to 10
      more = toolset.search_tools("manage employees", top_k=10)
      ```
    </CodeGroup>
  </Accordion>

  <Accordion title="Parameters reference">
    **TypeScript**

    | Parameter       | Type                              | Description                                       |
    | --------------- | --------------------------------- | ------------------------------------------------- |
    | `topK`          | `number`                          | Maximum number of tools to return                 |
    | `search`        | `'auto' \| 'semantic' \| 'local'` | Search mode (default: `'auto'`)                   |
    | `connector`     | `string`                          | Filter to a specific provider (e.g., `'workday'`) |
    | `minSimilarity` | `number`                          | Minimum relevance score threshold (0-1)           |
    | `accountIds`    | `string[]`                        | Override account IDs for this search              |

    **Python**

    | 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="Lightweight preview (action names + scores)">
    Returns action names and similarity scores without fetching full tool definitions. Use it to inspect results before committing to a full fetch.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const results = await toolset.searchActionNames('manage employees', {
        topK: 5,
      });

      for (const result of results) {
        console.log(`${result.id}: ${result.similarityScore}`);
      }
      ```

      ```python 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}")
      ```
    </CodeGroup>

    <Tip>
      This 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="Reusable search tool for custom agent loops">
    Returns a reusable `SearchTool` for agent loops where the LLM decides what to search for.

    <CodeGroup>
      ```typescript TypeScript theme={null}
      const searchTool = toolset.getSearchTool({ search: 'auto' });

      // In an agent loop, search for tools as needed
      const queries = [
        'create a new employee',
        'list job candidates',
        'send a message to a channel',
      ];

      for (const query of queries) {
        const tools = await searchTool.search(query, { topK: 3 });
        const toolNames = tools.toArray().map((t) => t.name);
        console.log(`"${query}" -> ${toolNames.join(', ')}`);
      }
      ```

      ```python 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)}')
      ```
    </CodeGroup>
  </Accordion>
</AccordionGroup>

The returned `Tools` collection converts to OpenAI, Vercel AI SDK, LangChain, and more — see the framework guides under [Agent SDK](/embed/call-actions/agent-sdk#calling-actions).
