内容
# 工具列表
[](https://www.npmjs.com/package/toolsift)
[](https://github.com/abdulmunimjemal/toolsift/actions/workflows/ci.yml)
[](./LICENSE)
[](https://nodejs.org)
> 一个本地的、与模型无关的 **MCP 代理**,解决了 **工具定义膨胀** 的问题。
> 它位于您的代理和 N 个上游 MCP 服务器之间,并暴露一个 *微小的* 接口 — `search_tools` + `invoke_tool` —
> 以便代理仅检索它需要的少量工具,而不是预先支付 50 多个 JSON 定义的费用。
当代理连接到多个 MCP 服务器时,它们的工具定义可能会在**第一个消息之前**占用大量上下文窗口 —
架构、描述和参数列表,用于代理在此回合中不会使用的数十种工具。toolsift 连接到您的上游服务器作为客户端,
聚合它们的工具到本地索引中,并向代理重新暴露两个元工具。代理调用 `search_tools("打开一个 GitHub 问题")`
以拉回相关定义的 handful,然后调用 `invoke_tool(...)` 以运行一个工具。这是 **RAG-MCP / 懒加载工具** 模式。
100% 本地。无需 API 密钥,无需超出您自己的上游服务器的网络,无需遥测。BM25 检索是零依赖的默认设置。
## 为什么这很重要
[RAG-MCP](https://arxiv.org/abs/2505.03275) 表明,按需检索工具而不是加载所有工具可以**提高**工具选择准确率
(从基准的 ~14% 提高到 ~43%),同时减少提示令牌 ~50%。直觉是:较小的相关工具列表更容易让模型选择,
并且更便宜。toolsift 将此模式带到任何 MCP 客户端,并提供一个评估框架,以便您可以在**您自己的**工具集上衡量令牌方面的影响。
## 安装
```bash
npx toolsift mcp # 零安装,或:
npm i -g toolsift # 然后 `toolsift` 命令在您的 PATH 上
```
需要 Node ≥ 18。
## 快速开始
**1. 告诉 toolsift 您的上游服务器。** 在您的项目中创建 `toolsift.json`(参见 [`toolsift.example.json`](./toolsift.example.json)):
```json
{
"servers": [
{
"name": "github",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_…" }
},
{
"name": "fs",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
}
],
"pinned": ["read_file"],
"retriever": "bm25",
"topK": 5
}
```
`pinned` 工具直接暴露(直通),因此始终需要的工具会跳过搜索。HTTP 上游也支持:`{ "name": "remote", "url": "https://…/mcp" }`。
**2. 将 toolsift 连接到您的代理 — 一行代码:**
```bash
npx toolsift install # 将 toolsift 添加到 Claude Code + Cursor
```
这会写入 MCP 服务器配置(非破坏性)。重启代理即可完成。`--agent claude|cursor` 针对一个;`--global` 写入您的主目录而不是项目。
如果您更喜欢手动连接?服务器命令是 `npx -y toolsift mcp`.
**Claude Code**(`.mcp.json` 或 `claude mcp add`):
```json
{
"mcpServers": {
"toolsift": { "command": "npx", "args": ["-y", "toolsift", "mcp"] }
}
}
```
**Cursor / Codex / 任何 MCP 客户端:** 使用相同的命令 — `npx -y toolsift mcp` 通过 stdio。
## MCP 工具,代理看到
| 工具 | 功能 |
|------|--------------|
| `search_tools(query, k?)` | 检索任务的前 k 个相关上游工具定义(服务器,名称,描述,输入模式) |
| `invoke_tool(server, name, arguments)` | 运行一个上游工具,并将结果不变地代理回去 |
| `list_servers()` | 上游服务器及其工具数量的摘要(方向) |
| *每个固定的工具* | 直接暴露为直通,无需搜索 |
工具描述是**为代理编写的** — 它们提示代理先搜索再调用,而不是期望每个工具都在上下文中。
## 评估框架 — 在您自己的工具集上衡量
这是实际运行的部分。`toolsift eval` 是一个**确定性、无 API 密钥**的基准,针对标记的查询→工具数据集评估检索,并报告每个检索器:
- **recall@k** — 正确的工具是否在前 k 个检索结果中?
- **MRR** — 平均而言,它排名多高?
- **token reduction** — 所有工具界面的令牌(您的代理今天支付的费用)与 toolsift 暴露的界面通过 [`gpt-tokenizer`] 比较。
在没有参数的情况下运行它以使用 BM25 默认检索,或使用 `--embeddings` 还要评估语义和混合检索器(28 个工具,涵盖 GitHub / Slack / 文件系统 / Postgres / 日历):
```text
$ toolsift eval --embeddings
tools: 28 · queries: 28 · k: 5 · baseline surface: 1342 tokens
retriever recall@5 mrr tokens saved
---------- -------- ---- ------ -----
all-tools 1.00 1.00 1342 0%
bm25 0.89 0.67 223 83%
embeddings 1.00 0.90 242 82%
hybrid 0.96 0.80 236 82%
→ embeddings: recall@5 1.00 · 比加载每个工具少 82% 的令牌
```
所有工具基准线微不足道地具有召回率 1.0 和 0% 减少 — 这是每个代理今天支付的费用。检索在前 k 个结果中保留正确的工具,同时将模型承载的表面减少约 80%。在具有现实的 **60 工具**的多服务器设置上,这变成 **~92% 更少的令牌**,召回率为 0.83(嵌入)。
**指向您自己的工具集** — 病毒神器:
```bash
toolsift eval --config toolsift.json --dataset my-queries.json
```
其中 `my-queries.json` 是数组(或 `{ "examples": [...] }`):
```json
[
{ "query": "打开一个错误报告", "expectedServer": "github", "expectedTool": "create_issue" },
{ "query": "发布到团队频道", "expectedServer": "slack", "expectedTool": "send_message" }
]
```
toolsift 连接到您配置的上游服务器,拉取它们的真实工具列表,并告诉您确切地可以节省多少令牌,以及检索是否仍然找到您需要的工具。端到端 `--llm` 准确性模式(真实模型,需要 API 密钥)正在计划中;默认评估保持确定性和无密钥。
## 它是如何工作的
1. **聚合。** toolsift 连接到每个配置的上游 MCP 服务器作为客户端(stdio 或 HTTP),调用 `tools/list`,并构建一个内存中的 `{ server, name, description, inputSchema }` 注册表。
2. **索引。** 注册表由可插拔的**检索器**索引。默认是自包含的 **BM25**,适用于每个工具的名称(加权)、描述和参数名称 — 零依赖、即时,并针对工具使用的 camelCase / snake_case 标识符进行了调整。语料库很小(数十到数百个工具),因此它驻留在内存中;无需矢量数据库。
3. **暴露。** toolsift 作为其自己的 MCP 服务器运行在 stdio 上,并宣传 `search_tools` + `invoke_tool`(+ 任何固定的工具),而不是上游定义。代理搜索,获取一些模式,然后调用。
4. **代理。** `invoke_tool` 将调用转发给所属的上游并返回其结果不变。
## 程序 API
所有内容均可导入:
```ts
import { UpstreamManager, createServer, createRetriever, runEval } from "toolsift";
const manager = new UpstreamManager(config.servers);
await manager.connect();
const retriever = createRetriever("hybrid"); // 或 "bm25" | "embeddings"
await retriever.index(manager.tools());
await retriever.search("打开一个 GitHub 问题", 5); // → 评分工具引用
// 注入您自己的嵌入后端(模型无关):
const custom = createRetriever("embeddings", { embed: async (texts) => myApi(texts) });
// 直接构建代理 MCP 服务器:
const server = createServer({ manager, config });
// 或自己基准测试工具集(比较检索器):
const report = await runEval(manager.tools(), dataset, {
k: 5,
retrievers: ["all-tools", "bm25", "embeddings", "hybrid"],
});
```
## 检索器
| 检索器 | 需要 | 备注 |
|-----------|-------|-------|
| `bm25` | 无(默认) | 自包含的词法检索;零依赖、内存、即时。擅长标识符/关键词查询。 |
| `embeddings` | `@huggingface/transformers`(可选对等方) | 通过本地运行的小模型进行语义检索(无 API 密钥,一次获取并缓存模型)。捕捉 BM25 错过的释义。 |
| `hybrid` | `@huggingface/transformers` | 融合 BM25 + 嵌入与 Reciprocal Rank Fusion — 三者中最好的召回率。 |
```bash
npm i @huggingface/transformers # 选择嵌入/混合
```
在 `toolsift.json` 中设置 `"retriever": "hybrid"`。嵌入后端是**可插拔的** — 通过编程 API 注入任何 `EmbedFn`(托管嵌入 API,不同的本地模型);BM25 保持为零安装默认,以便核心包精简。
## 限制
- 默认检索器是**词法**(BM25),擅长标识符/关键词查询;安装 `@huggingface/transformers` 并设置 `"retriever": "hybrid"` 以适用于释义繁重的工具集(它融合词法 + 语义)。
- 工具定义在启动时聚合(`tools/list`);存在刷新钩子(`UpstreamManager.refresh()`),但实时上游工具列表更改订阅未连接到代理。
- `pinned` 按服务器名称匹配;如果两个上游公开相同的工具名称,请通过审查您的配置固定。
## 路线图
- **`--llm` 评估模式** — 使用真实模型的端到端工具选择准确性。
- **实时工具列表刷新** — 响应上游 `notifications/tools/list_changed`.
- **持久嵌入缓存** — 重用工具嵌入在重启之间。
## 贡献
非常欢迎贡献 — toolsift 很小,完全测试过,其测试套件中没有网络。参见 **[CONTRIBUTING.md](./CONTRIBUTING.md)**.
```bash
pnpm install && pnpm test && pnpm typecheck && pnpm build
```
## 许可证
MIT © Abdulmunim Jemal
[`gpt-tokenizer`]: https://github.com/niieani/gpt-tokenizer