SIFT
Two meta-tools instead of a 30k-token catalogue: hierarchical, search-first tool discovery for LLM agents, with hybrid retrieval and response filtering.
3 min read
Stack
- Python
- fastembed
- BM25
- MCP
- OpenAPI
The problem
The standard way to give an agent tools is injecting every schema on every request. That works with ten tools. With two hundred, you're paying for the whole catalogue each turn, even when the agent will use exactly one:
| Catalogue | Cost per turn |
|---|---|
| 30 tools | ~2.4k tokens |
| 250 tools | ~24k tokens |
And it isn't only cost. The bigger the catalogue, the more often the model picks the wrong tool.
The idea
SIFT doesn't show the catalogue. It shows two meta-tools: search_tools and
execute_tool. The surface visible to the model stays constant at about 430 tokens, no
matter whether 30 or 3,000 tools sit behind it.
The agent discovers what it needs by walking a hierarchical taxonomy — category, service, function. Discovery returns results in TOON format, one line per tool, so the payload doesn't grow along with the catalogue.
from sift import Sift
sift = Sift()
@sift.tool("google_workspace.gmail.read",
description="Read emails from the inbox",
params={"q": "string:o:is:unread:search query", "m": "number:o:10:max"},
returns=["id", "subject", "from", "snippet", "date"])
def gmail_read(q="is:unread", m=10):
return {...}
sift.build_index()
sift.search_tools("read my last email")
sift.execute_tool("google_workspace.gmail.read", {"m": 1})How discovery works
Hybrid retrieval
Local embeddings and BM25 run in parallel, and the two rankings merge through Reciprocal Rank Fusion. Embeddings alone stumble on proper nouns and acronyms; BM25 alone stumbles on paraphrase. Together they cover each other's gap.
Embeddings run locally through fastembed with bge-small — 63 ms on CPU, no API key
and no network call during discovery.
Active requests
Beyond free-text search, the agent can issue an active request, separating domain —
the platform or permission area — from action, the operation itself. It's more structured
than a loose query, and the difference shows up in the numbers.
Results
On an independent dataset of 2,797 MCP tools:
| Method | Top-1 accuracy |
|---|---|
| Query search | 96.2% |
| Active request | 99.5% |
And in agent-level tests, against a flat catalogue as the baseline:
| Catalogue | Token efficiency |
|---|---|
| 100 tools | 4.1× |
| 250 tools | 8.4× |
With zero wrong-tool calls across every size tested.
Beyond discovery
The catalogue problem has a sibling on the other side: the response. A tool returning a huge JSON blob fills the context just as the schemas used to.
Hence per-tool response whitelisting, filtering sensitive or irrelevant fields before they reach the model, a global result cap — 100k characters by default — and transformation hooks.
Code mode lets the agent orchestrate several tools in a single turn through an executable snippet, instead of one call at a time. And session memory promotes frequently-used tools, so the search doesn't repeat every turn.
Where it runs
Adapters for OpenAI-compatible APIs — OpenAI, DeepSeek, Ollama — plus native Anthropic,
LangChain and MCP clients. For models without function calling there's a prompted mode
using a text JSON protocol. Importers pull catalogues from OpenAPI and the MCP ecosystem.
pip install sift-tools
pip install "sift-tools[langchain]"
pip install "sift-tools[mcp]"Published on PyPI under the MIT license. It's the tool-calling engine behind AI Workspace.