内容
# 工具漏斗
 <!-- logo asset (assets/logo.png) is added separately -->
> 一个零依赖的MCP网关:托管自己的工具,转发和策划其他MCP服务器的工具,精简地公开它们以降低代理令牌成本,并在每次调用前通过自己的策略钩子进行控制。
## 问题
模型上下文协议(MCP)允许AI代理从多个服务器调用工具。但是,每个连接的服务器都会将**所有**工具模式转储到模型的上下文中,每次循环都会如此。连接几个丰富的MCP服务器后,您已经花费了数千个令牌来描述代理在本次循环中不会使用的工具 - 更慢、更昂贵、更吵闹。
同样,在使用AI时,我发现自己生成了很多工具,其中一些我想在不同的AI工作流中使用,一些则不,我不想一直设置它们,我也不想为不同的工作流保留多个设置。因此,工具漏斗(ToolFunnel)诞生了,以便我可以将所有多用途工具和通用MCP服务器放在一个地方,与任何工作流一起使用,并且可以轻松选择我想在哪个工作流中使用它们。您还可以在会话期间更改或添加工具,只需在UI中简单地切换即可。我还想在运行的会话中连接和测试MCP **实时**,而无需重置CLI或重启任何东西 - 并且其工具会立即出现在工具列表中;工具漏斗正是如此。
通常,也没有一致的方法来**控制**代理可能运行的内容:钩子和策略位于**主机**(特定的CLI或其他)中,因此当您切换客户端时它们不会随之移动 - 使用工具漏斗,这很容易,因为您的钩子随工具移动 - 它可以成为许多不同工作流的万能工具。
最后,我不想审计大量的依赖关系 - 这是个人选择,是的 - 所以我想要一个可以快速轻松审计的东西。
在撰写本文时,我有14个MCP工具和93个本地工具,都通过工具漏斗访问,受需要时控制。
## 工具漏斗的作用
工具漏斗是一个小型MCP服务器,位于您的代理和所有其他工具之间:
1. **托管自己的工具** - 在JSON注册表中定义第一方工具,并直接提供。七个演示工具随附在盒子里。
2. **转发其他MCP服务器 - 精简地。** 附加上游MCP,其工具将出现在与您自己的工具相同的精简注册表中(简介 + 按需说明),可通过`toolfunnel_run_tool`和网关运行。策划哪些工具出现,推广少数工具到顶级“每次循环”工具,或默认保持精简。每个转发的调用仍然通过网关。
3. **精简注册表** - 代理看到简短的工具**简介**;工具的完整说明**按需获取**,因此上下文保持较小。*(这是令牌节省者。)*
4. **服务器端策略网关** - 每个服务器端执行路径都会触发您的PreToolUse / PostToolUse钩子**在网关内**,因此您的策略适用于**任何**客户端,而不仅仅是支持钩子的主机。网关随网关移动,并且**关闭失败**。
5. **通过文件、UI或带内配置** - 普通JSON文件,可选的循环访问Web UI(`node bin/toolfunnel.js --ui`),或八个带内**管理功能**,所有这些都可以添加/策划/切换工具、上游和钩子 - 实时,无需重启。
6. **需要时审计** - 可切换的JSONL日志(**默认关闭**)记录工具运行、每个网关允许/拒绝决定以及每个上游连接/断开/重新连接。
7. **实时和自愈** - 在运行的网关上附加/策划/切换工具、上游和钩子,无需重启;如果附加的MCP进程死亡,网关会在后台检测并重新连接,带有回退。
### 元工具表面
模型永远不会直接看到工具的长尾。它看到一个微小的固定表面,即**元工具**,并通过它们访问真正的工具:
| 元工具 | 参数 | 返回值 |
| --- | --- | --- |
| `toolfunnel_list_tools` | `{ filter?, category? }` | 仅简介:`[{ id, name, summary, category }]` |
| `toolfunnel_tool_instructions` | `{ name }` | 一个工具的完整使用说明,按需 |
| `toolfunnel_howto` | `{ topic }` | 自我扩展指南(`create-tool`, `add-mcp`, `add-hook`, `package`) |
| `toolfunnel_run_tool` | `{ name, args }` | 通过网关**运行一个注册工具** |
所有**四个**元工具都在`tools/list`中公开。流程是:列出简介,按需读取一个工具的说明,然后运行它 - 要么**自己**在自己的环境中按照说明(一个*参考*工具),要么通过`toolfunnel_run_tool`,它在网关**服务器端**执行(一个*网关*工具 - 参见[执行模式](#execution-mode-reference-vs-gateway))。任何**策划的上游**工具都与元工具一起公开,并通过同一个网关运行。
### 可见性矩阵
每个工具 - 您的工具、上游转发工具和**四个**元工具 - 都有三个独立的可见性拨盘,因此您可以确切地决定每个工作流看到什么:
| 轴 | 控制表面 | 默认 |
| --- | --- | --- |
| **启用** | **精简可见** - 出现在`toolfunnel_list_tools`中并且可运行。 | 开 |
| **热** | **顶级/每次循环** - 提升到`tools/list`中,因此代理在**每个**循环中看到其完整模式并且可以直接调用(无需`toolfunnel_run_tool`跳跃)。 | 关(元工具:**开**) |
| **隐藏** | 管理员列表去重(UI / `tf_list`)- 不会更改代理看到的内容。隐藏工具默认情况下从`tf_list`和UI工具列表中省略(“显示隐藏”切换/ `includeHidden`显示它们)。 | 关 |
拨盘生活在每个工具的`tools/tools.state.json`中,按工具的**表面名称**(本地ID、上游的`<upstream>_<tool>`或其`as`,或元工具名称)进行键控,并且**每次调用都刷新** - 因此切换是实时有效的,无需重启。禁用的工具永远不会是热的。
这就是为什么工具漏斗可以同时成为**精简注册表和传统MCP**:
- **默认(精简)。** 四个元工具是顶级的;其他一切都是精简的 - 简短的简介,按需获取模式。令牌成本随着工具的增加而保持不变。
- **推广您经常调用的工具。** 将工具的`hot`设为开启,它将加入每次循环的表面,直接像任何本地MCP工具一样可调用 - 而长尾仍然保持精简。通过`toolfunnel_run_tool`的网关路径仍然会经过网关(热点本地工具或转发上游工具通过同一个网关)。
- **将普通工具变成一个普通的MCP。** 推广一组本地/上游工具**并且**隐藏四个元工具(`hot:false`),工具漏斗将这些工具作为一个普通的顶级MCP服务器呈现 - 没有元工具,没有`toolfunnel_run_tool`跳跃 - 由脚本和其他MCP组成,无需SDK和Python。
两个容易出错的地方,UI会警告:隐藏元工具会使代理无法**按名称发现**工具(适用于“普通MCP”模式,否则会出错),而推广**许多**工具会重新引入上下文膨胀,精简注册表旨在避免这种情况。 从**工具**选项卡(每个工具的**Hot**切换按钮和顶级**表面面板**),从**MCP**选项卡(每个发现的上游工具),或使用`tf_tool_set { id, action: "hot" | "unhot" | "hide" | "unhide" }`进行推广/降级。
### 演示工具
七个第一方演示工具位于`tools/scripts/`中,在`tools/tools.register.json`中声明(该文件还声明了以下八个[管理功能](#management-functions)):
| 工具 | 类别 | 功能 |
| --- | --- | --- |
| `echo` | 演示 | 返回未更改的提供的参数 |
| `base64` | 编码 | 编码文本为/解码文本为Base64 |
| `hash` | 加密 | 计算输入文本的摘要(默认为sha256) |
| `uuid` | 生成器 | 生成一个或多个RFC 4122 v4 UUID |
| `json` | 数据 | 验证和格式化JSON字符串 |
| `text-stats` | 文本 | 计算字符、单词和行数 |
| `danger` | 演示 | 一个故意“破坏性”的演示,以证明网关(其唯一副作用是将一行附加到`TOOLFUNNEL_DANGER_LOG`命名的文件中) |
**脚本契约。** 每个工具都是一个独立的Node脚本,从环境变量`TOOLFUNNEL_TOOL_ARGS`(一个JSON字符串)中读取其结构化参数,打印**恰好一行** JSON到stdout,并退出`0`。注册表将工具的`invoke: { type: "script", path: "scripts/<file>" }`解析为`node <script>` spawn,并且只有在PreToolUse网关允许后才会发生此spawn。
它们只是为了帮助您开始 - 您可以随意关闭或删除它们。
### 管理功能
网关可以**管理其自身**。八个第一方工具位于`management`类别中,允许代理(或Web UI)添加、策划和切换网关自己的配置。它们是普通的注册工具 - 通过`toolfunnel_run_tool`访问,因此每个调用都经过与任何其他网关工具相同的网关(单个PreToolUse匹配器,如`tf_.*`锁定整个管理表面,因为每个都被命名为`tf_*`)。
| 功能 | 功能 |
| --- | --- |
| `tf_tool_add` | 注册一个新工具(可选地在`tools/scripts/`下编写其脚本正文)。实时,无需重新连接。 |
| `tf_tool_set` | 启用、禁用、**提升/降级到每次循环表面(`hot`/`unhot`)**、**隐藏/取消隐藏从管理器视图(`hide`/`unhide`)**或删除一个注册工具(启用/禁用叠加层默认开启;`hot`/`hidden`默认关闭)。 |
| `tf_mcp_add` | 注册一个上游MCP并可选地策划一些工具来公开。 |
| `tf_mcp_set` | 启用、禁用或删除上游MCP(删除会级联其公开条目)。 |
| `tf_hook_add` | 添加一个钩子清单条目(例如PreToolUse网关),可选地在`hooks/scripts/`下编写脚本。 |
| `tf_hook_set` | 启用、禁用或删除钩子(通过`hooks.state.json`覆盖实时切换)。 |
| `tf_list` | 列出注册工具、上游MCP或钩子及其实时活动状态(只读)。隐藏的工具默认情况下被省略 - 传 `{ includeHidden: true }`以显示它们。 |
| `tf_log` | 启用、禁用或检查[审计日志](#activity--audit-log)(`{ action: enable\|disable\|status }`)。 |
工具/钩子注册表编辑是**实时**的(每次调用重新读取,无需重新连接)。上游连接/策划更改也会实时生效:网关监视其配置并重新加载 - 重新连接上游并发出`notifications/tools/list_changed` - 无需重启。
### 执行模式:参考与网关
每个注册工具解析为两种执行模式之一(设置可选的`mode`字段,或让它**向后兼容推断**):
- **`参考`** - 工具漏斗仅**描述**工具。`toolfunnel_run_tool`返回工具的说明,连接的AI在**其自己的**环境中执行操作。服务器端不运行任何操作,因此**网关不会触发**(没有可触发的内容)。参考工具不需要`invoke`。这是工具不带有可执行正文的精简默认模式。
- **`网关`** - 工具漏斗**执行**工具服务器端,通过网关运行路径(`toolfunnel_run_tool` → PreToolUse网关 → 运行 → PostToolUse)。这是网关应该实际运行的工具的选项模式。
当省略`mode`时,它被推断为兼容现有工具:`script`或`shell` invoke ⇒ `gateway`;其他 ⇒ `reference`。七个演示工具和八个管理功能都带有脚本调用,因此它们解析为`gateway`。已解析的模式在Web UI中显示(并且可切换)每个工具。
### 网关
每个服务器端运行路径 - 通过`toolfunnel_run_tool`运行的`gateway`工具(包括管理功能)**和**每个转发的上游调用 - 都通过`src/mcp/gated-run.js`流入(`reference`工具在这里执行操作,因此它们会短路到网关之前)。
```
gatedRun({ engine, ctx, toolName, args, execute })
→ fire PreToolUse (可能会阻塞 - 如果引擎错误,则失败 CLOSED)
→ execute() (仅限允许)
→ fire PostToolUse (咨询性 - 无法取消工具运行)
```
钩子位于`hooks/hooks.manifest.json`(空清单 = 允许所有)。它们与工具名称匹配(`src/core/matcher.js`),由`src/core/hook-runner.js`运行,该程序使用Claude-Code钩子协议:钩子命令从stdin读取事件JSON并退出`2`以阻塞(stderr上有原因),或退出`0`并可选地返回JSON(`{ "decision": "block", ... }`或`{ "hookSpecificOutput": { "permissionDecision": "deny" | "allow", ... } }`)。负载均衡不变,证明测试:**PreToolUse拒绝意味着`execute()`永远不会被调用。**
## 为什么与众不同
托管工具和代理其他MCP服务器是一个拥挤的空间 - 但对于我自己的用例,这就是我选择自己开发的原因,我希望有所不同。
截至2026年6月的快速功能比较:
| 能力 | 工具漏斗 | FastMCP | mcpproxy-go | MetaMCP | mcp-anything |
| --- | --- | --- | --- | --- | --- |
| 配置声明的多语言工具(无SDK) | ✓ | ✗ (装饰器) | 部分(仅代理) | ✗ | ✓ |
| 精简服务器端公开(简介 + 按需模式) | ✓ | 部分 | ✓ | ✗ | ✗ |
| 服务器端失败关闭策略网关(Pre/Post) | ✓ | ✓ | ✓ | ✓ | ✗ |
| 实时附加/热重载,无需重启 | ✓ | ✗ | 部分 | 部分 | ✓ |
| 非Docker自愈重连 | ✓ | ✗ | 部分(Docker) | 部分(电路中断器) | ✗ |
| 可见性**矩阵** - 精简默认 + 每个工具提升到每次循环表面 + 隐藏 | ✓ | ✗ | 仅过滤 | 仅选择 | ✗ |
| 零运行时依赖\* | ✓ (Node,无SDK) | ✗ (框架) | ✓ (Go) | ✗ (Docker) | ✓ (Go) |
| 运行时依赖(安装/捆绑) | **0\* | 许多 | 40+(捆绑) | 许多 | 捆绑(Go) |
| 配置Web UI | ✓ | ✗ | ✓ | ✓ | ✗ |
**除了依赖计数外,四个功能选择使工具漏斗与纯代理(如mcpproxy-go)区分开来:
1. **它托管,不只是代理。** 代理/聚合器转发来自**现有**MCP服务器的工具。工具漏斗还将任意本地命令或脚本 - 任何语言 - 变成一级**受控**MCP工具,从一个JSON条目开始,无需SDK和预先存在的服务器。
2. **参考模式。** 工具漏斗仅**描述**工具:`toolfunnel_run_tool`返回说明,连接的AI在**其自己的**环境中执行操作 - 而**交接本身受到控制**(PreToolUse拒绝会扣留说明)。服务器端不执行任何操作。
3. **可移植的策略网关,而不是内容扫描器。** 网关使用**Claude-Code钩子协议**(PreToolUse/PostToolUse,退出-2-阻止)**内部**网关,因此现有的钩子/策略会旅行到**任何**客户端不变 - 一个易于适应(适用于CODEX或您正在使用的任何内容)的可编程策略,而不是内置扫描器。
4. **可见性矩阵。** 不是开/关过滤 - 精简默认情况下,您可以选择性地**提升到每次循环表面**,**隐藏**管理器视图,或完全折叠(关闭元工具并将您的工具呈现为普通的顶级MCP)。
底层:**精简**表面(简介 + 按需获取模式)保持令牌成本随着工具的增加而保持不变,**失败关闭**网关在每个服务器端路径上,并且**零运行时依赖\***在一个组件中 - 不是任何单个技巧。
### 开发状态和诚实限制
工具漏斗是一个**专注的个人构建**。我已经彻底测试了自己的用例,但它可能仍然包含超出该用例以外的错误 - 还有我依赖较少的其他功能。我特别添加了OAuth 2.1和Streamable-HTTP实现,符合最新的MCP SDK功能,并且尚未进行广泛测试,因此您的里程数可能会有所不同。
没有Prometheus/OpenTelemetry指标 - 而是可切换的JSONL日志和插件内存计数器在`/health`上 - 我不需要完整的指标来满足自己的需求;如果您想添加它,请告诉我,我会考虑一下。
如果您发现错误 - 更愿意发现错误和修复 - 或有改进建议,我会非常乐意听到您的意见。我很乐意与其他人一起合作ToolFunnel :)
因为它是手工制作的,而不是基于MCP SDK构建的,所以我需要将其与MCP本身保持同步,因为规范每季度变化一次,我的目标是跟踪新功能发布 - 再次,如果您想参与,请告诉我 :)
## 用例
- **减少上下文膨胀。** 几个丰富的 MCP 服务器同时连接,会让代理的上下文充满它在本次调用中不会使用的工具模式。在 ToolFunnel 后,代理会看到简短的简介,只有在实际需要时才会加载工具的完整模式。
- **一个工具箱,多工作流。** 将所有多用途工具和通用 MCP 服务器放在一个地方,根据工作流选择要显示的工具 - 而不是为每个客户端重新配置不同的设置。
- **控制代理可以运行的内容 - 在任何客户端。** 在网关中设置一个关闭的 PreToolUse 策略,使其随工具一起旅行,即使在没有自己的钩子系统的客户端也是如此。
- **将脚本转换为 MCP 工具,无需构建 MCP。** 有一个有用的 CLI、bash 一行命令或任何语言的脚本?在 JSON 条目中声明它,它就是一个受控的 MCP 工具 - 无需协议代码,无需 SDK (`toolfunnel_howto({ topic: "create-tool" })` 显示了形状)。
- **在会话中添加工具。** 需要工具时可以直接添加,添加到下一个回合即可生效 - 无需重启。
- **在上游崩溃时存活下来。** 如果连接的 MCP 服务器在会话中崩溃,ToolFunnel 会注意到这一点,在后台重新连接并重新广播其工具 - 无需重启,也不会丢失会话。大多数重新连接逻辑都在 Docker 范围内;这不是。
## 零依赖,有意为之
ToolFunnel **没有运行时 npm 依赖** (`"dependencies": {}`),也不使用 MCP SDK - JSON-RPC 2.0 线路协议是基于 Node 内置模块(`node:http`、`node:child_process`、`node:fs` 等)手动构建的。需要 Node **>= 18**。`npm install toolfunnel` 拉取 **零** 包。
> **\* 一个星号:OAuth 是可选的。** 启用 OAuth 2.1(默认为关闭)会添加一个依赖 - [`jose`](https://www.npmjs.com/package/jose),它本身是 **零依赖**、经过审计的,与官方 MCP SDK 使用的库相同。它 **不是** 核心的运行时依赖项;它仅作为 `devDependency` 出现(用于 OAuth 测试套件),并且在用户启用身份验证时 **按需安装** - `toolfunnel install-oauth` 或 UI 中的安装按钮。因此,默认占用空间确实为零,而项目中最关键的安全代码 - token 验证 - 被委托给一个经过审计的库,而不是手动构建的。参见 [身份验证](#身份验证---可选-oauth-21)。
这是一个刻意的 **安全** 决定,而不是为了简单而简单。ToolFunnel 处于一个特权位置 - 它 **控制工具执行** - 这正是您不希望依赖树未经审计的地方。而 npm 中的 `dependencies: {}` 是一个更强的声明,而不是“单个二进制文件”;其他地方的 Go 网关作为一个二进制文件交付,但仍然 **静态链接** 其依赖树(例如,mcpproxy-go 运行了 40+ 直接依赖项 - goja、esbuild、gRPC、一个可观察性堆栈)。那是依赖项 **捆绑**,而不是依赖项免费。在 npm 自己看到大型软件包被泄露的一年里(Axios、Mastra 事件影响了 140 个软件包),带有 **没有传递依赖项需要审计** 的工具执行网关是一种可行的工程立场 - 您审计 **您自己的** 代码,而不是 40 个供应链。(它不是一个银弹:零依赖姿势缩小了审计表面,而不是移除手工制作的线路 + 网关正确的负担 - 我已经尝试过了。)
传输层同时读取 **LSP 样式的 `Content-Length:` 帧** 和 **换行符分隔的 JSON**,并写入换行符分隔的 JSON,因此它可以与简单和框架化的客户端互操作。
## 身份验证 - 可选的 OAuth 2.1
默认情况下,网关 **仅限回环且未经身份验证** - 这是单个操作员在 `localhost` 上的正确姿势。对于网络化或多用户部署,ToolFunnel 可以作为 **OAuth 2.1 资源服务器**:它在每个请求上验证 bearer token,然后再运行任何工具。
- **按需启用,一个依赖项。** 关闭直到您启用它。启用后安装 `jose` 按需(`toolfunnel install-oauth` 或 UI 身份验证选项卡),核心对于其他人保持零运行时依赖。
- **委托,不手动构建。** token 验证通过 `jose` 进行,具有 **固定的算法允许列表**(它击败了 RS256↔HS256 混淆攻击和 `alg:none`),强制 **颁发者**,以及强制 **受众** 绑定到此网关的资源 URI - RFC 8707 混淆代理防御。JWKS 获取、缓存和密钥轮换是库的工作。
- **发现。** 启用身份验证后,网关在 `GET /.well-known/oauth-protected-resource` 处提供 RFC 9728 受保护资源元数据文档(未经身份验证),并且 `401` 带有 `WWW-Authenticate: Bearer resource_metadata="…"` 提示。
- **安全暴露。** 身份验证是解锁非回环绑定的功能:HTTP 主机 **拒绝绑定到 localhost 以外**,除非启用 OAuth,并且如果身份验证开启但依赖项丢失或配置不一致,则拒绝启动(快速失败)。
- **配置** 在 UI 身份验证选项卡或 `auth/auth.config.json` 中(`enabled`、`issuer`、`audience`,可选 `jwksUri` - 否则通过 OIDC 发现从颁发者派生 - `algorithms`、`requiredScopes`、`clockToleranceSec`)。需要 Node **>= 18**(`jose` v5,CommonJS 构建)。
OAuth **客户端** 部分(动态客户端注册、授权服务器元数据发现、逐步身份验证)以及 2026 年规范强化(RFC 9207 颁发者验证,凭证绑定)**计划中,但尚未发布** - 已发布、测试的 MVP 是资源服务器部分。
## Token 效率
该机制是精简寄存器:连接的上游通常在每次调用中将每个工具的完整 JSON 模式注入上下文中。ToolFunnel 用简短的简介(`name`、`summary`、`category`)替换它,只有当代理通过 `toolfunnel_tool_instructions` 请求时,才提供单个工具的完整指令。您连接的工具越多,节省的就越多。
## 快速入门
```bash
# 1. 获取代码
git clone <your-fork-or-clone-url> toolfunnel
cd toolfunnel
# 2. "安装" - 零运行时依赖;克隆的 npm 安装只会拉取开发/测试工具
# (jose,用于 OAuth 测试套件)。作为依赖项安装时,它不会拉取任何东西。
npm install # 运行时依赖:无
# 3a. 作为 stdio MCP 服务器运行(默认 - 大多数客户端都会启动)
node bin/toolfunnel.js
# 3b. 或作为 127.0.0.1:9998 上的 HTTP/SSE 主机运行(长寿命;许多客户端可以连接)
node bin/toolfunnel.js --http
node bin/toolfunnel.js --http --port 0 # 0 = 操作系统分配的端口
node bin/toolfunnel.js --http --host 127.0.0.1 --port 9998
# 3c. 或在 127.0.0.1:9777 上打开可选的配置 Web UI(仅限回环)
node bin/toolfunnel.js --ui
node bin/toolfunnel.js --ui --port 0 # 0 = 操作系统分配的端口
# 帮助
node bin/toolfunnel.js --help
# 烟雾测试线路(构建 → 初始化 → 工具/列表 → 调用元工具)
node test/smoke.js
```
HTTP 主机仅绑定回环,并公开 `POST/GET /mcp` 上的单个 Streamable-HTTP 端点(以及一个已弃用的 `GET /mcp/sse` 别名和一个 `GET /health` JSON 快照)。npm 脚本镜像上述内容:`npm start`(stdio)和 `npm run http`。
### 注册 MCP 客户端
通过 `.mcp.json` 将您的客户端指向 ToolFunnel。对于 **stdio** 服务器,客户端会启动进程:
```json
{ "mcpServers": { "toolfunnel": { "command": "node", "args": ["/path/to/toolfunnel/bin/toolfunnel.js"] } } }
```
对于 **HTTP** 主机(使用 `node bin/toolfunnel.js --http` 启动),请将客户端指向 `/mcp` 端点:
```json
{ "mcpServers": { "toolfunnel": { "type": "http", "url": "http://127.0.0.1:9998/mcp" } } }
```
连接后,调用 `toolfunnel_list_tools` 查看简介,调用 `toolfunnel_tool_instructions` 获取工具的完整用法,调用 `toolfunnel_howto` 学习如何扩展网关。
**或跳过配置 - 只需询问您的 AI。** 将任何 MCP 感知客户端指向 ToolFunnel 并说 *"附加此工具服务器,公开这些工具,并阻止任何破坏性操作。"* AI 读取 ToolFunnel 内置的指令并为你配置所有内容 - 上游、公开的工具和 PreToolUse 安全门 - 使用纯语言,无需编辑 JSON。更喜欢点击吗?下面的 Web UI 也可以做到这一点。 **无需编码经验 :)**
## 配置 Web UI
一个 **可选** 的本地管理控制台让人类可以查看、搜索和配置网关,而无需手动编辑 JSON:
```bash
node bin/toolfunnel.js --ui # http://127.0.0.1:9777
node bin/toolfunnel.js --ui --port 0 # 操作系统分配的端口
```
它 **仅限回环**,拒绝非回环 `Host` 头(DNS 重绑定防护),并且具有零前端依赖项(香草 HTML/CSS/JS,暗主题,离线 - 无 CDN,无框架)。五个选项卡:
- **工具** - 每个注册工具都有实时搜索和 **显示隐藏** 过滤器;一个顶级 **表面面板**(四个元工具的每次调用切换 + 促销计数和防误警告);添加工具(脚本正文、shell 或无调用引用);切换 **启用**(精简)、**热**(每次调用促销)和 **隐藏**(清理此管理器列表);翻转每个工具的 **执行模式**(引用 ↔ 网关);切换每个工具的 **前/后钩子** 门;删除。
- **MCP** - 上游 MCP 服务器及其精选的直接公开选择;添加具有公开工具的上游;启用/禁用/删除;**发现** - 上游工具的实时连接和列表,每个工具都有自己的 **精简**(启用)和 **热** 切换键控为显示的名称。
- **钩子** - 钩子清单,具有实时启用状态;添加生命周期钩子(可选地编写其脚本正文);启用/禁用/删除。
- **日志** - [审计日志](#活动--审计日志) 的开启/关闭开关和最近记录的新旧视图。
- **身份验证** - 配置可选的 OAuth 2.1 资源服务器验证(默认关闭)并按需安装其单个依赖项。
每次 UI 写入都通过 **相同的存储** 读取 MCP 服务器,因此编辑在字节上与手动编辑(和 CLI 编辑)相同,并且对正在运行的服务器可见,无需重启。
## 活动和审计日志
ToolFunnel 可以保留一个 **JSONL** 活动/审计日志 - **默认关闭**(全新检出不写入任何内容,也不创建日志文件)。启用后,它会记录工具运行和 **每个门允许/拒绝决定**。可以通过三种方式切换它,所有方式都等效:
- `tf_log` 管理功能 - `{ action: "enable" | "disable" | "status" }`;
- Web UI **日志** 选项卡开关;
- 直接编辑 `logs/log.config.json`(`{ "enabled": bool, "path": "logs/toolfunnel.log.jsonl" }`)。
配置 **每次事件读取新鲜**,因此切换立即生效。日志记录被包装,因此它 **永远不会** 抛出到工具调用或门中,并且可以禁用 `tf_log` 以撤销日志记录控制。(`logs/` 目录被 git 忽略。)
## 项目布局
ToolFunnel 在 **引擎** (`src/`,您永远不会编辑它)和 **您管理的内容** (`tools/`、`mcp/`、`hooks/` - 普通配置和顶级脚本)之间保持刻意的分离:
```
toolfunnel/
├─ bin/toolfunnel.js # 入口点 - 默认 stdio,--http 用于主机,--ui 用于控制台
├─ src/ # ToolFunnel 自己的引擎(您永远不会编辑此部分)
│ ├─ mcp/ # 服务器、协议、传输、mcp-client、聚合器、门控运行
│ ├─ core/ # 钩子引擎、钩子加载器、钩子运行器、匹配器、事件、日志记录
│ ├─ tools/ # 工具系统(注册、工具状态、漂移)
│ ├─ ui/ # 可选的配置 Web UI(回环服务器 + 香草资产)
│ └─ extend/ · packages/
├─ tools/ # 您的工具
│ ├─ tools.register.json # 第一方工具注册(7 个演示 + 8 个管理功能)
│ ├─ tools.state.json # 可见性矩阵叠加层,按表面名称键控 → `{ enabled?, hidden?, hot? }`. 默认启用(空 `{}` 启用所有内容),热点/隐藏的默认关闭(元工具默认热点开启)。
│ └─ scripts/ # 工具脚本
├─ mcp/ # 您的上游
│ ├─ expose.json # 上游服务器 +精选的工具集以公开(`{ version, upstreams: [], expose: [] }`)。**默认为空**,因此网关连接到任何内容
│ └─ expose.example.json # 注释样本,展示如何连接上游并公开两个工具
├─ hooks/ # 您的策略门
│ ├─ hooks.manifest.json # 门控(`{ version, hooks: [] }`)。空 `hooks` 数组 = 允许所有
│ ├─ hooks.state.json # 钩子的实时启用/禁用叠加层;此处的条目优先于清单种子 `enabled`
│ └─ scripts/ # 钩子命令脚本
└─ logs/ # 审计日志(git-ignored; 仅在启用日志记录时创建)
├─ log.config.json # `{ enabled, path }` - 默认关闭;仅在启用日志记录时创建
└─ toolfunnel.log.jsonl # JSONL 记录(默认路径)
```
经验法则:`src/` 是网关机械装置;`tools/`、`mcp/` 和 `hooks/` 是您的配置存储。引擎将这些顶级路径相对于存储库根目录解析。
## 配置
所有配置都是存储库下的纯 JSON - 手动编辑,通过 [配置 Web UI](#配置-web-ui) 或通过 [管理功能](#管理功能):
| 文件 | 目的 |
|-----------------------------------|-------------------------------------------------------------------------|
| `tools/tools.register.json` | 第一方工具注册 (`{ version, description, tools: [...] }`) - 7 个演示 + 8 个管理功能 |
| `tools/tools.state.json` | 可见性矩阵叠加层,按显示名称键控 → `{ enabled?, hidden?, hot? }`. 默认启用(空 `{}` 启用所有内容),默认关闭 `hot`/`hidden`(元工具默认 `hot` 开启)。读取每次调用都很新鲜 - 不需要重启 |
| `hooks/hooks.manifest.json` | 策略门控 (`{ version, hooks: [] }`). 空 `hooks` 数组 = 允许所有 |
| `hooks/hooks.state.json` | 钩子的可选实时启用/禁用叠加层;此处的条目优先于清单种子 `enabled` |
| `mcp/expose.json` | 上游 MCP 服务器 +精选的工具集以公开(`{ version, upstreams: [], expose: [] }`)。**默认为空**,因此网关连接到任何内容 |
| `mcp/expose.example.json` | 注释样本,展示如何连接上游并公开两个工具 |
| `logs/log.config.json` | 审计日志切换 (`{ enabled, path }`). **默认关闭**;仅在启用日志记录时创建 |
## 许可证
[MIT](LICENSE)
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器