mcp-multi-bridge 2.1.4 → 2.2.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/bin.js +27 -5
- package/lib/config.js +69 -18
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -40,7 +40,16 @@ import { createRequire } from 'node:module';
|
|
|
40
40
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
41
41
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
42
42
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
loadConfig,
|
|
45
|
+
findOrg,
|
|
46
|
+
defaultOrg,
|
|
47
|
+
allowedPaths,
|
|
48
|
+
CONFIG_FILE,
|
|
49
|
+
CONFIG_DIR,
|
|
50
|
+
CONFIG_DIR_ERROR,
|
|
51
|
+
NAME_DIR_SUFFIX
|
|
52
|
+
} from './lib/config.js';
|
|
44
53
|
import { getOrgAuth, getCurrentOrgKey, listLoggedInOrgs } from './lib/token.js';
|
|
45
54
|
import { rpc, extractPayload } from './lib/higress.js';
|
|
46
55
|
import { settings, DEFAULT_SETTINGS } from './lib/settings.js';
|
|
@@ -143,11 +152,16 @@ async function handleCli(argv) {
|
|
|
143
152
|
MCP_BRIDGE_PATHS=/mcp-servers/tc-mes-mcp-server
|
|
144
153
|
然后在 mcp.json 里配多个带不同 env 的条目。
|
|
145
154
|
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
配置目录(二选一,必须指定):
|
|
156
|
+
MCP_MULTI_HOME=<绝对路径> 旧名 MCP_BRIDGE_HOME 仍兼容
|
|
157
|
+
--name <系统名> 目录为 ~/.<系统名>${NAME_DIR_SUFFIX}
|
|
148
158
|
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
以前没配时会静默用 ~/.mes-mcp-login,导致新系统忘配就读到 MES 的数据。
|
|
160
|
+
现在没配直接报错,不再猜。
|
|
161
|
+
|
|
162
|
+
当前配置目录: ${CONFIG_DIR || '(未配置)'}
|
|
163
|
+
配置文件: ${CONFIG_FILE || '(未配置)'}
|
|
164
|
+
登录文件: ${CONFIG_DIR ? CONFIG_DIR + '/token.json' : '(未配置)'}(由 ${L.loginCommand} 写入)
|
|
151
165
|
|
|
152
166
|
本程序只读上述文件,不会修改或删除。`);
|
|
153
167
|
return true;
|
|
@@ -209,6 +223,14 @@ if (await handleCli(process.argv.slice(2))) {
|
|
|
209
223
|
process.exit(0);
|
|
210
224
|
}
|
|
211
225
|
|
|
226
|
+
// 走到这里说明要进入 stdio 服务模式。没有配置目录就没法干活,
|
|
227
|
+
// 直接退出并说明原因——绝不像以前那样悄悄回落到某个既有目录,
|
|
228
|
+
// 那会让这个实例读到别的系统的配置与 token。
|
|
229
|
+
if (!CONFIG_DIR) {
|
|
230
|
+
console.error(`[bridge] ${CONFIG_DIR_ERROR}`);
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
233
|
+
|
|
212
234
|
function textResult(text, isError = false) {
|
|
213
235
|
return {
|
|
214
236
|
content: [{ type: 'text', text }],
|
package/lib/config.js
CHANGED
|
@@ -5,48 +5,99 @@
|
|
|
5
5
|
* 配置结构由登录侧 CLI(如 mes-login-cli)定义,两边各自维护默认值必然漂移,
|
|
6
6
|
* 所以桥接层只认文件;文件缺失时明确报错,引导用户去跑登录命令。
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* 配置目录必须显式指定,两种方式:
|
|
9
|
+
* 1. MCP_MULTI_HOME=<绝对路径> 直接指目录
|
|
10
|
+
* 2. --name <系统名> 用约定目录 ~/.<系统名>-mcp-login
|
|
11
|
+
*
|
|
12
|
+
* 故意不设「默认目录」。以前没配时会静默回落到 ~/.mes-mcp-login,
|
|
13
|
+
* 结果是新加的系统忘了配就默默读到 MES 的配置和 token,
|
|
14
|
+
* 工具名显示 erp、数据却是 MES 的,且不报错——跨系统串数据极难排查。
|
|
15
|
+
* 现在没配就报错,宁可启动失败也不要读错数据。
|
|
11
16
|
*/
|
|
12
17
|
import fs from 'node:fs';
|
|
13
18
|
import path from 'node:path';
|
|
14
19
|
import os from 'node:os';
|
|
15
20
|
|
|
16
|
-
/**
|
|
17
|
-
const
|
|
21
|
+
/** 用 --name 推导目录时统一加的后缀 */
|
|
22
|
+
export const NAME_DIR_SUFFIX = '-mcp-login';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 从命令行取 --name 的值
|
|
26
|
+
*
|
|
27
|
+
* 支持 `--name erp` / `--name=erp` / `-n erp`。
|
|
28
|
+
* 放在 config.js 里读 process.argv 是因为 CONFIG_DIR 是模块级常量,
|
|
29
|
+
* 在 import 阶段就要算出来——此时 argv 已就绪,可用。
|
|
30
|
+
*/
|
|
31
|
+
function nameFromArgv() {
|
|
32
|
+
const argv = process.argv.slice(2);
|
|
33
|
+
for (let i = 0; i < argv.length; i++) {
|
|
34
|
+
const a = argv[i];
|
|
35
|
+
if (a === '--name' || a === '-n') return String(argv[i + 1] || '').trim();
|
|
36
|
+
if (a.startsWith('--name=')) return a.slice('--name='.length).trim();
|
|
37
|
+
}
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** 展开 ~ 与相对路径 */
|
|
42
|
+
function expand(raw) {
|
|
43
|
+
const s = String(raw || '').trim();
|
|
44
|
+
if (!s) return '';
|
|
45
|
+
if (s === '~') return os.homedir();
|
|
46
|
+
if (s.startsWith('~/') || s.startsWith('~\\')) return path.join(os.homedir(), s.slice(2));
|
|
47
|
+
return path.resolve(s);
|
|
48
|
+
}
|
|
18
49
|
|
|
19
50
|
/**
|
|
20
51
|
* 解析配置目录
|
|
21
52
|
*
|
|
22
|
-
* 优先级:MCP_MULTI_HOME > MCP_BRIDGE_HOME(旧名) >
|
|
53
|
+
* 优先级:MCP_MULTI_HOME > MCP_BRIDGE_HOME(旧名) > --name 推导
|
|
23
54
|
* 用环境变量而不是 config.json 里的字段——要读配置就得先知道目录,写进配置里是循环依赖。
|
|
24
55
|
*
|
|
25
|
-
*
|
|
56
|
+
* @returns {{dir:string, error:string|null}} dir 为空表示未配置
|
|
26
57
|
*/
|
|
27
58
|
function resolveDir() {
|
|
28
59
|
const pick = (v) => (typeof v === 'string' && v.trim() ? v.trim() : '');
|
|
60
|
+
|
|
29
61
|
const custom = pick(process.env.MCP_MULTI_HOME) || pick(process.env.MCP_BRIDGE_HOME);
|
|
30
|
-
if (custom) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
62
|
+
if (custom) return { dir: expand(custom), error: null };
|
|
63
|
+
|
|
64
|
+
const name = pick(nameFromArgv());
|
|
65
|
+
if (name) {
|
|
66
|
+
// --name 会拼进路径,限定字符集,避免 ../ 之类跑出 home 目录
|
|
67
|
+
if (!/^[A-Za-z0-9._-]+$/.test(name) || name === '.' || name === '..') {
|
|
68
|
+
return {
|
|
69
|
+
dir: '',
|
|
70
|
+
error: `--name 的值不合法:「${name}」。只允许字母、数字、点、下划线、连字符。`
|
|
71
|
+
};
|
|
35
72
|
}
|
|
36
|
-
return path.
|
|
73
|
+
return { dir: path.join(os.homedir(), `.${name}${NAME_DIR_SUFFIX}`), error: null };
|
|
37
74
|
}
|
|
38
|
-
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
dir: '',
|
|
78
|
+
error:
|
|
79
|
+
'未指定配置目录。启动时加其中之一:\n' +
|
|
80
|
+
' 1) 环境变量 MCP_MULTI_HOME=<配置目录绝对路径>\n' +
|
|
81
|
+
` 2) 命令行参数 --name <系统名>(目录为 ~/.<系统名>${NAME_DIR_SUFFIX})\n` +
|
|
82
|
+
'MCP 客户端里一般写在 mcp.json 的 env 或 args。'
|
|
83
|
+
};
|
|
39
84
|
}
|
|
40
85
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
86
|
+
const RESOLVED = resolveDir();
|
|
87
|
+
|
|
88
|
+
/** 配置目录;未配置时为空字符串 */
|
|
89
|
+
export const CONFIG_DIR = RESOLVED.dir;
|
|
90
|
+
/** 未配置时的说明文字;已配置为 null */
|
|
91
|
+
export const CONFIG_DIR_ERROR = RESOLVED.error;
|
|
92
|
+
export const CONFIG_FILE = CONFIG_DIR ? path.join(CONFIG_DIR, 'config.json') : '';
|
|
93
|
+
export const TOKEN_FILE = CONFIG_DIR ? path.join(CONFIG_DIR, 'token.json') : '';
|
|
44
94
|
|
|
45
95
|
/**
|
|
46
96
|
* 读取配置
|
|
47
|
-
* @returns {object|null}
|
|
97
|
+
* @returns {object|null} 配置对象;未配置目录、文件缺失或损坏都返回 null
|
|
48
98
|
*/
|
|
49
99
|
export function loadConfig() {
|
|
100
|
+
if (!CONFIG_FILE) return null;
|
|
50
101
|
try {
|
|
51
102
|
if (!fs.existsSync(CONFIG_FILE)) return null;
|
|
52
103
|
const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|