mcp-multi-bridge 2.1.4 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +210 -12
- package/bin.js +445 -48
- package/lib/config.js +80 -18
- package/lib/toolindex.js +425 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ bridge 拉取工具列表的规则:
|
|
|
52
52
|
|
|
53
53
|
1. 按 `path` 去重,同一 path 只查一次(不是按组织逐个查)
|
|
54
54
|
2. 多个 path 并行查询
|
|
55
|
-
3.
|
|
55
|
+
3. 按**工具名**合并,同名是否合并看参数结构(见下)
|
|
56
56
|
4. 单个 path 失败不阻断整体,能拿到多少给多少,失败的写进 `login` 工具的告警里
|
|
57
57
|
|
|
58
58
|
### `orgKey` 只能选同后端的组织
|
|
@@ -60,36 +60,232 @@ bridge 拉取工具列表的规则:
|
|
|
60
60
|
这是为了避免一种必然失败的情况:工具来自 A 后端,`orgKey` 却指向 B 后端的组织,
|
|
61
61
|
请求发过去那边不认识这个工具。bridge 会在调用前校验,跨后端直接拒绝并列出可用选项。
|
|
62
62
|
|
|
63
|
-
###
|
|
63
|
+
### 同名工具怎么合并
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
不同 path 下出现同名工具时,按 `inputSchema` 的指纹判断:
|
|
66
|
+
|
|
67
|
+
| 情况 | 处理 | 例子 |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| 结构相同 | **合并**成一个工具,`orgKey` 取并集 | sz / tc / vn 三个 MES 环境共用一套 API |
|
|
70
|
+
| 结构不同 | **拆开**,加 `<系统>__` 前缀 | MES 的 `search(rid)` 与 ERP 的 `search(keyword)` |
|
|
71
|
+
|
|
72
|
+
结构相同说明是同一套后端的不同部署环境,靠 `orgKey` 就能区分,加前缀反而让模型困惑。
|
|
73
|
+
结构不同则是真的两个工具,必须区分,否则模型会拿着 A 系统的参数去调 B 系统。
|
|
74
|
+
|
|
75
|
+
系统名从 path 末段提取并去掉 `-mcp-server` / `-api` 后缀:
|
|
76
|
+
`/mcp-servers/sz-mes-mcp-server` → `sz-mes`,冲突时暴露为 `sz-mes__search`。
|
|
77
|
+
|
|
78
|
+
**例外**:如果一个实例只连一个后端(一套系统一个 `--name`),系统名直接取
|
|
79
|
+
`--name` 的值,不再从 path 推导。部署者写什么就是什么,比从 path 猜更准:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
--name warehouse,path 是 /mcp-servers/erp-api
|
|
83
|
+
→ 系统名取 warehouse,不取 erp
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
因为这时 `--name` 和系统是一一对应的,没有歧义。多后端聚合时
|
|
87
|
+
`--name` 代表的是配置目录、下面挂着好几套系统,只能按 path 区分。
|
|
88
|
+
|
|
89
|
+
## 三个容易混淆的 name
|
|
90
|
+
|
|
91
|
+
| 名称 | 在哪 | 作用 |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `--name` | 命令行 | **只用来算配置目录** `~/.<name>-mcp-login`,不参与路由 |
|
|
94
|
+
| `orgs[].path` | config.json | **决定调用哪个 MCP server**,唯一的路由依据 |
|
|
95
|
+
| mcp.json 的 key | 客户端配置 | 客户端侧 server 名,工具前缀 `mcp__<key>__` 来自它 |
|
|
96
|
+
|
|
97
|
+
`--name` 不决定调用哪个后端。一个 `--name` 可以聚合多个 path
|
|
98
|
+
(当前 `--name mes` 下有 5 个 path、10 个组织),这种情况必须靠 path 区分系统。
|
|
99
|
+
|
|
100
|
+
## 工具很多时:按需检索
|
|
101
|
+
|
|
102
|
+
工具定义要常驻模型上下文。按中等复杂度工具约 350 tokens 估算:
|
|
103
|
+
|
|
104
|
+
| 工具数 | 常驻开销 | 能否直接用 |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| 20 | 7K | ✅ |
|
|
107
|
+
| 100 | 35K | ✅ 但选择准确率开始下降 |
|
|
108
|
+
| 500 | 175K | ⚠ 接近上限 |
|
|
109
|
+
| 1000 | 350K | ❌ 超过多数模型整个窗口 |
|
|
110
|
+
|
|
111
|
+
所以工具多了不能全塞。bridge 有三种模式:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
MCP_BRIDGE_TOOL_MODE=auto 默认。工具数超过 40 自动切 layered
|
|
115
|
+
MCP_BRIDGE_TOOL_MODE=layered 强制分层
|
|
116
|
+
MCP_BRIDGE_TOOL_MODE=flat 强制全量
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**默认 auto 而不是一直分层**:工具少时分层反而多一层间接,模型要先 `search_tools`
|
|
120
|
+
才能调,纯属负担。只有当全量塞不下时才值得这个代价。
|
|
121
|
+
|
|
122
|
+
### 分层时模型看到什么
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
login 状态查询
|
|
126
|
+
search_tools 按关键词检索工具(描述里带系统目录摘要)
|
|
127
|
+
describe_tool 看某个工具的完整参数定义
|
|
128
|
+
call_tool 调用任意工具
|
|
129
|
+
+ 常驻的 N 个高频工具 默认 20 个
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`search_tools` 的返回只给「名称 + 一句话 + 前几个参数名」,不给完整 schema——
|
|
133
|
+
一个完整 schema 就几百 token,一次返回 5 个等于回到全量暴露。需要细节时走 `describe_tool`。
|
|
134
|
+
|
|
135
|
+
实测 1200 个工具(6 套系统 × 200):
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
flat : 604 KB ≈ 177K tokens
|
|
139
|
+
layered : 12 KB ≈ 4K tokens 降幅 98%
|
|
140
|
+
检索延迟: 10-40ms
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 常驻哪些工具
|
|
144
|
+
|
|
145
|
+
默认按组织覆盖数排序取前 N 个——被多套部署共用的工具更可能是每次都用的。
|
|
146
|
+
但真实频次只有日志知道,所以应该按调用统计把名单固化下来:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
MCP_BRIDGE_RESIDENT_COUNT=20
|
|
150
|
+
MCP_BRIDGE_RESIDENT=searchRidReport,captchaGet
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
> **这两个只对分层模式生效。** 工具没超过 40 个时走 flat(全量暴露),
|
|
154
|
+
> 它们会被忽略。为免误以为配置写错,这种情况会在 stderr 和 `login` 工具的描述里
|
|
155
|
+
> 各提示一次——`login` 那条是给客户端界面看的,因为多数客户端不显示 stderr。
|
|
156
|
+
|
|
157
|
+
统计方法(如果接了 Loki,`request_body` 里有工具名):
|
|
158
|
+
|
|
159
|
+
```logql
|
|
160
|
+
sum by (tool) (count_over_time({filename=~".+"} | json | line_format "{{.request_body}}" |= "tools/call" [7d]))
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 客户端最终看到几个工具
|
|
164
|
+
|
|
165
|
+
分层模式下是 `1 + 3 + N`:
|
|
166
|
+
|
|
167
|
+
| 组成 | 数量 | 能否配置 |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| `login` | 1 | 不能,本包提供的状态查询 |
|
|
170
|
+
| `search_tools` / `describe_tool` / `call_tool` | 3 | 不能,分层机制本身 |
|
|
171
|
+
| 常驻业务工具 | 默认 20(上限 200) | `MCP_BRIDGE_RESIDENT_COUNT` / `MCP_BRIDGE_RESIDENT` |
|
|
172
|
+
|
|
173
|
+
所以默认是 24 个。但这是「工具数超过 40」之后才会出现的情况——
|
|
174
|
+
工具少的时候走 flat,看到的就是 `1 + 全部业务工具`。
|
|
175
|
+
|
|
176
|
+
### 聚合几十上百套 MCP 时
|
|
177
|
+
|
|
178
|
+
一个 bridge 可以聚合任意多个 path。数量上去之后有两个成本要处理:
|
|
179
|
+
|
|
180
|
+
**1. 全量拉取很贵**
|
|
181
|
+
|
|
182
|
+
启动时要对每个后端发一次 `tools/list`。实测(单后端 30ms 延迟):
|
|
183
|
+
|
|
184
|
+
| 后端数 | 首次拉取 |
|
|
185
|
+
|---|---|
|
|
186
|
+
| 5 | 约 1 秒 |
|
|
187
|
+
| 50 | 约 6 秒 |
|
|
188
|
+
| 200 | 约 20 秒 |
|
|
189
|
+
|
|
190
|
+
所以工具清单**按 TTL 缓存**,默认 5 分钟:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
MCP_BRIDGE_TOOLS_TTL_MS=300000
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
缓存期必须远大于拉取耗时,否则客户端每次刷新都要重来一遍——实测 200 个后端时,
|
|
197
|
+
缓存让重复的 `tools/list` 从 19 秒降到 0.1 秒。
|
|
198
|
+
|
|
199
|
+
**新增的工具怎么被发现?** 不需要把 TTL 调小。`tools/call` 进来时如果索引里
|
|
200
|
+
查不到该工具,会自动强制刷新一次再判断。所以「网关刚加了工具、模型马上要用」
|
|
201
|
+
这个场景是通的,代价只有那一次调用多花一轮拉取。
|
|
202
|
+
|
|
203
|
+
**2. 并发要限**
|
|
204
|
+
|
|
205
|
+
不设上限时 200 个后端会在同一瞬间建立 200 个连接,网关和本地 socket 都吃不住,
|
|
206
|
+
而且失败会一起失败。
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
MCP_BRIDGE_FETCH_CONCURRENCY=16
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
默认 16。实测 200 个后端下,8 并发约 28 秒、16 并发约 18 秒,再往上收益不明显
|
|
213
|
+
(压测环境是单进程 mock,序列化大 JSON 会成为新瓶颈,真实网关下最优值可能不同)。
|
|
214
|
+
|
|
215
|
+
### 检索是怎么打分的
|
|
216
|
+
|
|
217
|
+
不是向量检索,是加权关键词命中。1000 个工具用倒排就够,上 embedding 反而多一个服务要维护。
|
|
218
|
+
|
|
219
|
+
**匹配依据只有三处**:工具名、描述、参数名。全部来自工具定义本身,
|
|
220
|
+
bridge 侧**不维护任何业务词对照表**。
|
|
221
|
+
|
|
222
|
+
早期版本内置过一份约 30 条的「料号 → material/item」别名表,后来删掉了,因为实测有两个问题:
|
|
223
|
+
|
|
224
|
+
| 问题 | 说明 |
|
|
225
|
+
|---|---|
|
|
226
|
+
| 对写全中文描述的工具零贡献 | 真实 MES 的描述里本来就有「料号、批次、仓库、库位」,别名表一条都没用上 |
|
|
227
|
+
| 会误匹配 | 「料号」被映射到 `item`,而别的系统里 `item` 可能是电商商品,`queryItem` 就被错误命中 |
|
|
228
|
+
|
|
229
|
+
所以规则很简单:**工具名或描述里有这个词,就能搜到;没有就搜不到。**
|
|
230
|
+
这是可预期的,也不会出现「bridge 猜错了领域」这种难以排查的情况。
|
|
231
|
+
|
|
232
|
+
**新增工具不需要改代码**:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
新增 queryCustomsDeclaration,描述「查询报关单」
|
|
236
|
+
「报关单」→ 命中(中文来自 description)
|
|
237
|
+
「customs」→ 命中(英文来自 name)
|
|
238
|
+
「declNo」→ 命中(参数名)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
搜不到时,问题在工具定义,不在 bridge。正确的修法是**在网关侧补描述**:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
❌ doZzz / "Process ZZZ" 「质量追溯」搜不到
|
|
245
|
+
✅ doZzz / "质量追溯 · Process ZZZ" 「质量追溯」直接命中
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
描述里带上用户可能说的中文说法(含同义词),比维护一份注定对不齐的词表更省事,
|
|
249
|
+
也不会漏掉长尾——业务词根本不可能靠人工枚举穷尽。
|
|
250
|
+
|
|
251
|
+
**2. 意图分级**。业务 API 的工具名几乎都是 `queryXxx` / `createXxx` / `updateXxx` /
|
|
252
|
+
`deleteXxx` 四件套,光看业务对象四个分数完全一样。用户说「料号」时想要的是查不是创建,
|
|
253
|
+
所以按动作词加权重:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
「料号」 → queryMaterial 只说业务对象,偏向读操作
|
|
257
|
+
「创建物料」 → createMaterial 明说动作,导向写操作
|
|
258
|
+
「导出物料」 → exportMaterial 导出和查询都是读,靠动作词再分一层
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
只想业务对象时不会优先给出「删除批次」这类写操作——读操作选错代价小,写操作有数据风险。
|
|
67
262
|
|
|
68
263
|
### 拆成多个客户端条目
|
|
69
264
|
|
|
70
|
-
|
|
265
|
+
另一种办法是不分层,而是用环境变量限定本实例负责的 path:
|
|
71
266
|
|
|
72
267
|
```json
|
|
73
268
|
{
|
|
74
269
|
"mcpServers": {
|
|
75
270
|
"mes-test": {
|
|
76
271
|
"command": "mcp-multi-bridge",
|
|
77
|
-
"args": [],
|
|
78
|
-
"env": { "
|
|
272
|
+
"args": ["--name", "mes-test"],
|
|
273
|
+
"env": { "MCP_BRIDGE_PATHS": "mes-mcp-server" }
|
|
79
274
|
},
|
|
80
275
|
"mes-prod": {
|
|
81
276
|
"command": "mcp-multi-bridge",
|
|
82
|
-
"args": [],
|
|
83
|
-
"env": { "
|
|
277
|
+
"args": ["--name", "mes-prod"],
|
|
278
|
+
"env": { "MCP_BRIDGE_PATHS": "tc-mes-mcp-server" }
|
|
84
279
|
}
|
|
85
280
|
}
|
|
86
281
|
}
|
|
87
282
|
```
|
|
88
283
|
|
|
89
|
-
`
|
|
90
|
-
也可以只写末段(`xxx
|
|
284
|
+
`MCP_BRIDGE_PATHS` 用逗号分隔,可以写完整 path(`/mcp-servers/xxx`),
|
|
285
|
+
也可以只写末段(`xxx`),两种都识别。旧名 `MES_MCP_PATHS` 仍兼容。
|
|
91
286
|
|
|
92
|
-
|
|
287
|
+
两种做法的取舍:拆 server 简单但要维护多套配置和凭证;分层只维护一套,
|
|
288
|
+
代价是每次找工具多一跳。系统数量不多时,拆 server 更省事。
|
|
93
289
|
|
|
94
290
|
## 检查安装的版本
|
|
95
291
|
|
|
@@ -322,6 +518,8 @@ mcp-multi-login --home "C:/Users/你/.mes-mcp-login" --org tc
|
|
|
322
518
|
## 提供的工具
|
|
323
519
|
|
|
324
520
|
工具列表由网关返回,本包原样透传。`login` 会被本包替换成本地状态查询工具。
|
|
521
|
+
分层模式下还会额外提供 `search_tools` / `describe_tool` / `call_tool` 三个元工具,
|
|
522
|
+
用于按需发现工具,避免把上千个定义全塞进上下文。
|
|
325
523
|
|
|
326
524
|
工具本身(有哪些、叫什么、什么参数)完全取决于网关侧配置,本包不做增删。
|
|
327
525
|
当前 MES 网关提供的是:
|