New to tool search? Start with Tool Search 101 for an overview of why, how, and when to use it.
The Python SDK provides three methods for tool discovery:
search_tools(): search by intent, returns a Tools collection ready for any framework
search_action_names(): lightweight search returning action IDs and scores without fetching full tool definitions
get_search_tool(): returns a callable SearchTool for agent loops
Constructor Configuration
You can set default search options at the constructor level. These defaults apply to all search calls unless overridden per-call.
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()
Search Config 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) |
Per-call overrides take precedence over constructor defaults:
# 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)
search_tools() is the recommended method for agent frameworks. It fetches tools from your linked accounts, searches each connector in parallel via the semantic search API, and returns a Tools collection sorted by relevance.
from stackone_ai import StackOneToolSet
toolset = StackOneToolSet()
# Search for tools by intent
tools = toolset.search_tools(
"manage employee records and time off",
top_k=5,
search="auto", # tries semantic API first, falls back to local search
)
print(f"Found {len(tools)} tools")
# Use with any framework
openai_tools = tools.to_openai()
langchain_tools = tools.to_langchain()
Search Modes
# Auto (default): tries semantic API first, falls back to local search on failure
tools = toolset.search_tools("manage employees", search="auto")
# Semantic only: uses the semantic API, raises SemanticSearchError if unavailable
tools = toolset.search_tools("manage employees", search="semantic")
# Local only: hybrid BM25+TF-IDF search, no network call to the semantic endpoint
tools = toolset.search_tools("manage employees", search="local")
auto: Recommended for production. Searches each connector in parallel via the semantic API. If the API call fails, automatically falls back to local search and logs a warning.
semantic: Strict mode. Same parallel per-connector search, but raises SemanticSearchError on failure instead of falling back.
local: Runs a hybrid BM25 + TF-IDF search entirely in-process (powered by bm25s for BM25). No network call. Useful for offline or low-latency scenarios.
Parameters
| 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., "bamboohr") |
min_similarity | float | None | Minimum relevance score threshold (0-1) |
account_ids | list[str] | None | Override account IDs for this search |
Search Action Names
search_action_names() queries the semantic API directly and returns action IDs with similarity scores, without fetching full tool definitions. Use it to preview results before committing to a full fetch.
results = toolset.search_action_names("manage employees", top_k=5)
for r in results:
print(f"{r.id}: {r.similarity_score:.2f}")
For most use cases, use search_tools() instead - it handles searching, matching, and building executable tools in one call. search_action_names() is for previewing search results before committing to a full fetch.
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.
get_search_tool() returns a callable SearchTool for agent loops where the LLM decides what to search for.
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)}')
Agent-Driven Search
Instead of searching upfront, you can let the LLM discover and execute tools autonomously. Pass mode="search_and_execute" to get two tools - tool_search and tool_execute - that the LLM uses to find and run tools on-demand.
from openai import OpenAI
from stackone_ai import StackOneToolSet
toolset = StackOneToolSet(
search={"method": "semantic", "top_k": 3},
execute={"account_ids": ["your-account-id"]},
)
# LLM receives only 2 tools: tool_search + tool_execute
openai_tools = toolset.openai(mode="search_and_execute")
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),
})
With mode="search_and_execute", the LLM always receives exactly 2 tools regardless of how many tools exist in your catalog. Token usage stays constant.
Framework Integration
Tool search returns a Tools collection with built-in framework converters. See the framework guides for full examples:
CrewAI accepts LangChain tools. Use tools.to_langchain() for CrewAI integration.
Next Steps