mcp-multi-bridge 2.4.0 → 2.6.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 +57 -0
- package/bin.js +79 -15
- package/lib/preset.js +25 -3
- package/lib/secretkeys.js +121 -0
- package/lib/settings.js +11 -0
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -76,6 +76,63 @@ npm i -g mcp-multi-bridge mcp-multi-login
|
|
|
76
76
|
MCP_MULTI_HOME=~/.my-mcp mcp-multi-bridge --preset ebs-plus
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
## 免登录:按组织使用访问密钥
|
|
80
|
+
|
|
81
|
+
后端支持 `X-Api-Key` 时,可以用密钥代替登录令牌,免去登录和过期重登。
|
|
82
|
+
密钥按组织配置,一个变量装全部(`orgs[].key` 里可能有连字符,所以不能拆成变量名):
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
"env": {
|
|
86
|
+
"MCP_BRIDGE_SECRET_KEYS": "{\"test-taicang\":\"mom_xxx\",\"tc\":\"mom_yyy\"}"
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
行为:
|
|
91
|
+
|
|
92
|
+
| 组织 | 调用时 | 结果 |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| 配了密钥 | 只带 `apiKey`,**不带** `access_token` | MES 用密钥认证 |
|
|
95
|
+
| 没配密钥 | 照旧带 `access_token` | 与原来完全一致 |
|
|
96
|
+
|
|
97
|
+
两种凭据不混发——MES 收到空头时的行为不确定,混发会让排错变难。
|
|
98
|
+
|
|
99
|
+
### 密钥失效时自动回退
|
|
100
|
+
|
|
101
|
+
各系统对「密钥失效」的定义不统一,所以判据是**配置的错误码**,不是语义:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
"protocol": { "authFailureCodes": [626, 649, 613, 614, 615, 616] }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
默认值覆盖 MES 的两种情形:`626`(设备未注册,等同没带密钥)、`649`(秘钥无效),
|
|
108
|
+
再加上登录失效的 `613~616`(有些系统密钥失败会回落到令牌校验)。
|
|
109
|
+
|
|
110
|
+
命中后:
|
|
111
|
+
|
|
112
|
+
1. 用登录令牌重试一次
|
|
113
|
+
2. 成功 → 该 **(组织 + 工具)** 冷却 5 分钟,期间直接走令牌(`MCP_BRIDGE_KEY_RETRY_MS`)
|
|
114
|
+
3. 失败 → 返回**密钥调用的原始错误**,并列出令牌方式的错误,避免把「密钥配错」显示成「登录失效」
|
|
115
|
+
|
|
116
|
+
冷却粒度到工具级:只有部分工具配了密钥透传时,一个工具的失败不会拖累其他工具。
|
|
117
|
+
|
|
118
|
+
### 网关侧必须两边都配
|
|
119
|
+
|
|
120
|
+
插件是按 `requestTemplate` 重建请求的,**不会透传客户端请求头**,所以密钥只能走「参数 + 模板」:
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
# ① 工具参数里声明
|
|
124
|
+
- description: "访问密钥,由调用方服务自动注入,无需人工填写"
|
|
125
|
+
name: apiKey
|
|
126
|
+
required: false
|
|
127
|
+
type: "string"
|
|
128
|
+
|
|
129
|
+
# ② 请求模板里引用
|
|
130
|
+
- key: "X-Api-Key"
|
|
131
|
+
value: "{{.args.apiKey}}"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
缺任意一条都会静默失效(渲染成空值,MES 返回 626),不报错。
|
|
135
|
+
|
|
79
136
|
## 多后端与工具列表
|
|
80
137
|
|
|
81
138
|
`config.json` 里的每个组织都带一个 `path`,指向它在 Higress 上的 MCP 路由。
|
package/bin.js
CHANGED
|
@@ -56,6 +56,13 @@ import { rpc, extractPayload } from './lib/higress.js';
|
|
|
56
56
|
import { settings, DEFAULT_SETTINGS } from './lib/settings.js';
|
|
57
57
|
import { buildIndex, searchTools, summarize } from './lib/toolindex.js';
|
|
58
58
|
import { ensureConfig, listPresets, resolvePresetsDir } from './lib/preset.js';
|
|
59
|
+
import {
|
|
60
|
+
secretKeyFor,
|
|
61
|
+
inKeyCooldown,
|
|
62
|
+
markKeyCooldown,
|
|
63
|
+
keyRetryMs,
|
|
64
|
+
API_KEY_FIELD
|
|
65
|
+
} from './lib/secretkeys.js';
|
|
59
66
|
|
|
60
67
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
61
68
|
const require = createRequire(import.meta.url);
|
|
@@ -454,9 +461,12 @@ function orgOverview(cfg) {
|
|
|
454
461
|
const sections = groups.map((g) => {
|
|
455
462
|
const rows = g.orgs.map((o) => {
|
|
456
463
|
const isLogged = logged.has(o.key);
|
|
464
|
+
const hasKey = Boolean(secretKeyFor(o.key));
|
|
457
465
|
const marks = [];
|
|
458
466
|
if (o.key === current) marks.push('当前生效');
|
|
459
|
-
|
|
467
|
+
// 配了密钥的组织不需要登录;标出来,避免看到「未登录」误以为不能用
|
|
468
|
+
if (hasKey) marks.push('密钥认证');
|
|
469
|
+
else if (!isLogged) marks.push('未登录');
|
|
460
470
|
const suffix = marks.length ? `(${marks.join(',')})` : '';
|
|
461
471
|
const auth = isLogged ? getOrgAuth(o.key) : null;
|
|
462
472
|
const who = auth && auth.username ? `,账号 ${auth.username}` : '';
|
|
@@ -1095,22 +1105,76 @@ async function main() {
|
|
|
1095
1105
|
}
|
|
1096
1106
|
|
|
1097
1107
|
const { org, auth, reason } = selectOrg(cfg, orgKey, info.orgKeys);
|
|
1098
|
-
if (!org
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1108
|
+
if (!org) return textResult(reason || loginGuide('未登录'), true);
|
|
1109
|
+
|
|
1110
|
+
// 该组织配了密钥且不在冷却期 → 走密钥模式,不依赖登录令牌
|
|
1111
|
+
const key = secretKeyFor(org.key);
|
|
1112
|
+
const keyMode = Boolean(key) && !inKeyCooldown(org.key, name);
|
|
1113
|
+
if (!keyMode && !auth) return textResult(reason || loginGuide('未登录'), true);
|
|
1114
|
+
|
|
1115
|
+
// 密钥模式只带 apiKey;令牌模式只带 credentialField。
|
|
1116
|
+
// 两种凭据不混发:MES 收到空头时的行为不确定,混发会让排错变难。
|
|
1117
|
+
const buildArgs = (useKey) => ({
|
|
1118
|
+
...bizArgs,
|
|
1119
|
+
...(useKey ? { [API_KEY_FIELD]: key } : { [S.credentialField]: auth.token }),
|
|
1120
|
+
// 租户 ID 必须由本层注入:网关模板通常是
|
|
1121
|
+
// {{if .args.orgId}}{{.args.orgId}}{{else}}1{{end}},
|
|
1122
|
+
// 不传就一律按 1 查,同后端下的其他租户会查错组织。
|
|
1123
|
+
// 取值固定来自配置的 orgs[].orgId,tenantIdField 只决定发给网关的参数名。
|
|
1124
|
+
...(org.orgId != null ? { [S.tenantIdField]: String(org.orgId) } : {})
|
|
1111
1125
|
});
|
|
1112
1126
|
|
|
1113
|
-
const
|
|
1127
|
+
const callOnce = (useKey) => rpc(cfg, org, 'tools/call', { name, arguments: buildArgs(useKey) });
|
|
1128
|
+
|
|
1129
|
+
let usedKey = keyMode;
|
|
1130
|
+
let result = await callOnce(usedKey);
|
|
1131
|
+
let payload = extractPayload(result);
|
|
1132
|
+
|
|
1133
|
+
// 密钥失效:各系统的失效定义不统一,只认配置里的 authFailureCodes。
|
|
1134
|
+
// 命中后用登录令牌重试一次;令牌也失败则返回密钥调用的原始错误,
|
|
1135
|
+
// 避免把「密钥配错了」显示成「登录已失效」。
|
|
1136
|
+
if (usedKey && payload && S.authFailureCodes.includes(payload.code)) {
|
|
1137
|
+
const why = S.invalidReasons[payload.code] || payload.message || '凭据不可用';
|
|
1138
|
+
|
|
1139
|
+
if (!auth) {
|
|
1140
|
+
console.error(
|
|
1141
|
+
`[bridge] ${S.tenantLabel} ${org.key} 的密钥不可用(code=${payload.code}),且未登录无法回退`
|
|
1142
|
+
);
|
|
1143
|
+
return textResult(
|
|
1144
|
+
`工具 ${name} 调用失败:密钥不可用(code=${payload.code}:${why})。\n\n` +
|
|
1145
|
+
`该${S.tenantLabel}尚未登录,无法回退到令牌方式。\n` +
|
|
1146
|
+
`请检查 MCP_BRIDGE_SECRET_KEYS 里 ${org.key} 的密钥是否正确,或先运行登录命令。`,
|
|
1147
|
+
true
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
console.error(
|
|
1152
|
+
`[bridge] ${S.tenantLabel} ${org.key} 的密钥不可用(code=${payload.code}:${why}),改用登录令牌重试`
|
|
1153
|
+
);
|
|
1154
|
+
const retry = await callOnce(false);
|
|
1155
|
+
const retryPayload = extractPayload(retry);
|
|
1156
|
+
|
|
1157
|
+
if (retryPayload && S.authFailureCodes.includes(retryPayload.code)) {
|
|
1158
|
+
console.error(`[bridge] ${S.tenantLabel} ${org.key} 的令牌同样不可用(code=${retryPayload.code})`);
|
|
1159
|
+
return textResult(
|
|
1160
|
+
`工具 ${name} 调用失败,两种凭据都不可用。\n\n` +
|
|
1161
|
+
`密钥方式(X-Api-Key):code=${payload.code} ${payload.message || why}\n` +
|
|
1162
|
+
`令牌方式(${S.credentialField}):code=${retryPayload.code} ${retryPayload.message || '凭据不可用'}\n\n` +
|
|
1163
|
+
`请检查 MCP_BRIDGE_SECRET_KEYS 里 ${org.key} 的密钥,或重新登录后再试。`,
|
|
1164
|
+
true
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// 令牌能通 → 确认是密钥的问题,冷却一段时间直接走令牌
|
|
1169
|
+
markKeyCooldown(org.key, name);
|
|
1170
|
+
console.error(
|
|
1171
|
+
`[bridge] ${S.tenantLabel} ${org.key} 的「${name}」已回退到令牌,` +
|
|
1172
|
+
`${Math.round(keyRetryMs() / 60000)} 分钟内不再尝试密钥`
|
|
1173
|
+
);
|
|
1174
|
+
result = retry;
|
|
1175
|
+
payload = retryPayload;
|
|
1176
|
+
usedKey = false;
|
|
1177
|
+
}
|
|
1114
1178
|
|
|
1115
1179
|
// 失效判定有两个维度:
|
|
1116
1180
|
// invalidCodes 业务码,绝大多数系统走这条(HTTP 200 + code)
|
package/lib/preset.js
CHANGED
|
@@ -16,21 +16,43 @@
|
|
|
16
16
|
import fs from 'node:fs';
|
|
17
17
|
import path from 'node:path';
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { createRequire } from 'node:module';
|
|
19
20
|
|
|
20
21
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
21
22
|
/** 本包位于 <node_modules>/mcp-multi-bridge/lib,往上两层是 node_modules */
|
|
22
23
|
const NODE_MODULES = path.resolve(HERE, '..', '..');
|
|
24
|
+
const require = createRequire(import.meta.url);
|
|
23
25
|
|
|
24
26
|
/**
|
|
25
|
-
* preset
|
|
27
|
+
* preset 目录的查找顺序
|
|
26
28
|
*
|
|
27
29
|
* 1. 环境变量 MCP_BRIDGE_PRESETS —— 自定义目录,或本机没装 login 包时指过去
|
|
28
|
-
* 2.
|
|
30
|
+
* 2. 按模块解析定位已安装的 mcp-multi-login —— 不猜目录层级,
|
|
31
|
+
* npm 提升/嵌套安装都能找到
|
|
32
|
+
* 3. 兄弟目录 mcp-multi-login/presets —— npm 全局安装的常见形态
|
|
33
|
+
* 4. 兄弟目录 mes-login-cli/presets —— 源码工作区(仓库目录名与包名不同)
|
|
34
|
+
*
|
|
35
|
+
* 返回第一个真实存在的目录;都不存在时返回第 3 项,
|
|
36
|
+
* 让调用方的报错里给出「按默认位置找不到」的提示。
|
|
29
37
|
*/
|
|
30
38
|
export function resolvePresetsDir() {
|
|
31
39
|
const env = (process.env.MCP_BRIDGE_PRESETS || '').trim();
|
|
32
40
|
if (env) return env;
|
|
33
|
-
|
|
41
|
+
|
|
42
|
+
const candidates = [];
|
|
43
|
+
try {
|
|
44
|
+
const pkgPath = require.resolve('mcp-multi-login/package.json');
|
|
45
|
+
candidates.push(path.join(path.dirname(pkgPath), 'presets'));
|
|
46
|
+
} catch (e) {
|
|
47
|
+
// login 包未安装(optionalDependencies 被跳过),继续试兄弟目录
|
|
48
|
+
}
|
|
49
|
+
candidates.push(path.join(NODE_MODULES, 'mcp-multi-login', 'presets'));
|
|
50
|
+
candidates.push(path.join(NODE_MODULES, 'mes-login-cli', 'presets'));
|
|
51
|
+
|
|
52
|
+
for (const dir of candidates) {
|
|
53
|
+
if (fs.existsSync(dir)) return dir;
|
|
54
|
+
}
|
|
55
|
+
return candidates[1];
|
|
34
56
|
}
|
|
35
57
|
|
|
36
58
|
/** 列出可用的预设名,目录不存在时返回空数组 */
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* secretkeys.js —— 按组织解析访问密钥(X-Api-Key 模式)
|
|
3
|
+
*
|
|
4
|
+
* MES 一侧支持用请求头 X-Api-Key 代替登录令牌。密钥按组织分配,
|
|
5
|
+
* 一个桥接实例下可能同时存在「某组织用密钥、另一组织用登录令牌」的情况,
|
|
6
|
+
* 所以这里只做「按 orgKey 查表」,是否启用由调用方判定。
|
|
7
|
+
*
|
|
8
|
+
* 配置格式(一个环境变量装全部,避免 orgKey 里的连字符污染变量名):
|
|
9
|
+
*
|
|
10
|
+
* MCP_BRIDGE_SECRET_KEYS={"tc":"k1","hn":"k2","vn-plan":"k3"}
|
|
11
|
+
*
|
|
12
|
+
* 解析失败不抛错:整表视为空,全部退回登录令牌模式,并在 stderr 提示一次。
|
|
13
|
+
* 这条路径在请求链路上,任何异常都不该让服务不可用。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/** 网关模板里承载密钥的参数名,必须与 Higress 的 requestTemplate 保持一致 */
|
|
17
|
+
export const API_KEY_FIELD = 'apiKey';
|
|
18
|
+
|
|
19
|
+
/** 缓存解析结果,避免每次调用都 JSON.parse */
|
|
20
|
+
let cachedRaw = null;
|
|
21
|
+
let cached = { keys: new Map(), error: null };
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 解析 MCP_BRIDGE_SECRET_KEYS
|
|
25
|
+
*
|
|
26
|
+
* @param {string|undefined} raw 环境变量原文
|
|
27
|
+
* @returns {{keys: Map<string,string>, error: string|null}}
|
|
28
|
+
* 空值返回空表且无错误(未启用);非法 JSON 返回空表 + 错误说明
|
|
29
|
+
*/
|
|
30
|
+
export function parseSecretKeys(raw) {
|
|
31
|
+
const text = typeof raw === 'string' ? raw.trim() : '';
|
|
32
|
+
if (!text) return { keys: new Map(), error: null };
|
|
33
|
+
|
|
34
|
+
let obj;
|
|
35
|
+
try {
|
|
36
|
+
obj = JSON.parse(text);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
return { keys: new Map(), error: `MCP_BRIDGE_SECRET_KEYS 不是合法 JSON:${e.message}` };
|
|
39
|
+
}
|
|
40
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
41
|
+
return { keys: new Map(), error: 'MCP_BRIDGE_SECRET_KEYS 必须是对象,形如 {"tc":"<密钥>"}' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const keys = new Map();
|
|
45
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
46
|
+
// 空值视为「该组织不启用密钥」,跳过而不是当成错误——
|
|
47
|
+
// 编排里把某个环境的密钥留空是很自然的做法
|
|
48
|
+
if (typeof v !== 'string' || !v.trim()) continue;
|
|
49
|
+
keys.set(String(k).trim(), v.trim());
|
|
50
|
+
}
|
|
51
|
+
return { keys, error: null };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 当前进程生效的密钥表(读环境变量,带缓存)
|
|
56
|
+
*
|
|
57
|
+
* @returns {{keys: Map<string,string>, error: string|null}}
|
|
58
|
+
*/
|
|
59
|
+
export function secretKeys() {
|
|
60
|
+
const raw = process.env.MCP_BRIDGE_SECRET_KEYS;
|
|
61
|
+
if (raw !== cachedRaw) {
|
|
62
|
+
cachedRaw = raw;
|
|
63
|
+
cached = parseSecretKeys(raw);
|
|
64
|
+
if (cached.error) console.error(`[bridge] ${cached.error},将全部使用登录令牌模式`);
|
|
65
|
+
}
|
|
66
|
+
return cached;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 取某个组织的密钥
|
|
71
|
+
*
|
|
72
|
+
* @param {string} orgKey 配置里的 orgs[].key
|
|
73
|
+
* @returns {string|null} 未配置返回 null
|
|
74
|
+
*/
|
|
75
|
+
export function secretKeyFor(orgKey) {
|
|
76
|
+
if (!orgKey) return null;
|
|
77
|
+
return secretKeys().keys.get(String(orgKey)) || null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** 清缓存,仅供测试使用 */
|
|
81
|
+
export function _resetSecretKeysCache() {
|
|
82
|
+
cachedRaw = null;
|
|
83
|
+
cached = { keys: new Map(), error: null };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 密钥失效后的冷却表
|
|
88
|
+
*
|
|
89
|
+
* 某个 (组织, 工具) 回退到令牌后,接下来一段时间直接走令牌,
|
|
90
|
+
* 不再每次都「先失败一次再重试」——否则一个没配透传的工具会让
|
|
91
|
+
* 每个请求都多打一次 MES。
|
|
92
|
+
*
|
|
93
|
+
* 粒度是「组织 + 工具」而不是「组织」:只有部分工具配了密钥透传时,
|
|
94
|
+
* 一个工具的失败不该把其他工具的密钥能力也拖下水。
|
|
95
|
+
*/
|
|
96
|
+
const cooldowns = new Map();
|
|
97
|
+
|
|
98
|
+
/** 冷却时长,默认 5 分钟 */
|
|
99
|
+
export function keyRetryMs() {
|
|
100
|
+
const v = Number(process.env.MCP_BRIDGE_KEY_RETRY_MS);
|
|
101
|
+
return Number.isFinite(v) && v >= 0 ? v : 300000;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function markKeyCooldown(orgKey, toolName) {
|
|
105
|
+
cooldowns.set(`${orgKey}:${toolName}`, Date.now() + keyRetryMs());
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function inKeyCooldown(orgKey, toolName) {
|
|
109
|
+
const until = cooldowns.get(`${orgKey}:${toolName}`);
|
|
110
|
+
if (!until) return false;
|
|
111
|
+
if (Date.now() >= until) {
|
|
112
|
+
cooldowns.delete(`${orgKey}:${toolName}`);
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** 清空冷却表,仅供测试使用 */
|
|
119
|
+
export function _clearCooldowns() {
|
|
120
|
+
cooldowns.clear();
|
|
121
|
+
}
|
package/lib/settings.js
CHANGED
|
@@ -28,6 +28,16 @@ export const DEFAULT_TENANT_ID_FIELD = 'orgId';
|
|
|
28
28
|
export const DEFAULT_HIDDEN_ARGS = [DEFAULT_CREDENTIAL_FIELD, DEFAULT_TENANT_ID_FIELD];
|
|
29
29
|
/** 默认的登录失效错误码 */
|
|
30
30
|
export const DEFAULT_INVALID_CODES = [613, 614, 615, 616];
|
|
31
|
+
/**
|
|
32
|
+
* 默认的「密钥失效」错误码(X-Api-Key 模式)
|
|
33
|
+
*
|
|
34
|
+
* 各系统对密钥失效的定义不统一,所以不靠语义判断,只认配置里的码:
|
|
35
|
+
* 626 设备未注册(等于没带密钥,MES 侧对空的 X-Api-Key 就是这个)
|
|
36
|
+
* 649 秘钥无效
|
|
37
|
+
* 再加上登录失效的 613~616:某些系统密钥失败时直接回落到令牌校验,
|
|
38
|
+
* 报的仍是登录相关码。
|
|
39
|
+
*/
|
|
40
|
+
export const DEFAULT_AUTH_FAILURE_CODES = [626, 649, 613, 614, 615, 616];
|
|
31
41
|
/**
|
|
32
42
|
* 默认的登录失效 HTTP 状态码
|
|
33
43
|
*
|
|
@@ -130,6 +140,7 @@ export function settings(cfg) {
|
|
|
130
140
|
tenantIdField,
|
|
131
141
|
hiddenArgs,
|
|
132
142
|
invalidCodes: codeList(cfg?.invalidCodes, DEFAULT_INVALID_CODES),
|
|
143
|
+
authFailureCodes: codeList(cfg?.authFailureCodes, DEFAULT_AUTH_FAILURE_CODES),
|
|
133
144
|
invalidHttpStatus: codeList(cfg?.invalidHttpStatus, DEFAULT_INVALID_HTTP_STATUS),
|
|
134
145
|
invalidReasons: reasonMap(cfg?.invalidCodeReasons),
|
|
135
146
|
loginCommand: isNonEmptyString(cfg?.loginCommand) ? cfg.loginCommand.trim() : DEFAULT_LOGIN_COMMAND
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-multi-bridge",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "多后端 MCP 桥接:把网关后多个后端、多个租户的工具集合并成一个 MCP 服务,凭据由登录 CLI 提供",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"scripts": {
|
|
18
18
|
"start": "node bin.js",
|
|
19
19
|
"pack:check": "npm pack --dry-run",
|
|
20
|
-
"test": "node test/toolindex.test.mjs && node test/headers.test.mjs && node test/preset.test.mjs"
|
|
20
|
+
"test": "node test/toolindex.test.mjs && node test/headers.test.mjs && node test/preset.test.mjs && node test/secretkeys.test.mjs"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"mcp",
|
|
@@ -30,5 +30,8 @@
|
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@modelcontextprotocol/sdk": "^1.20.2"
|
|
33
|
+
},
|
|
34
|
+
"optionalDependencies": {
|
|
35
|
+
"mcp-multi-login": "^3.5.0"
|
|
33
36
|
}
|
|
34
37
|
}
|