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.
Files changed (3) hide show
  1. package/bin.js +27 -5
  2. package/lib/config.js +69 -18
  3. 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 { loadConfig, findOrg, defaultOrg, allowedPaths, CONFIG_FILE, CONFIG_DIR } from './lib/config.js';
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
- 配置文件: ${CONFIG_FILE}
147
- 登录文件: ${CONFIG_DIR}/token.json(由 ${L.loginCommand} 写入)
155
+ 配置目录(二选一,必须指定):
156
+ MCP_MULTI_HOME=<绝对路径> 旧名 MCP_BRIDGE_HOME 仍兼容
157
+ --name <系统名> 目录为 ~/.<系统名>${NAME_DIR_SUFFIX}
148
158
 
149
- 配置目录可用环境变量 MCP_MULTI_HOME 改到别处(旧名 MCP_BRIDGE_HOME 仍兼容)。
150
- 放在客户端配置的 env 里,可以一个客户端同时接多套系统。默认 ~/.mes-mcp-login。
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
- * 目录可换:默认沿用 ~/.mes-mcp-login(历史约定),
9
- * 用 MCP_BRIDGE_HOME 指向别的目录即可支持第二套系统并存,
10
- * 两套互不干扰,各自登录各自的 token。
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 LEGACY_DIR_NAME = '.mes-mcp-login';
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(旧名) > ~/.mes-mcp-login
53
+ * 优先级:MCP_MULTI_HOME > MCP_BRIDGE_HOME(旧名) > --name 推导
23
54
  * 用环境变量而不是 config.json 里的字段——要读配置就得先知道目录,写进配置里是循环依赖。
24
55
  *
25
- * 与登录 CLI 用同一套变量名,两边必须读到同一个目录,否则登录完 bridge 找不到 token。
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
- if (custom === '~') return os.homedir();
33
- if (custom.startsWith('~/') || custom.startsWith('~\\')) {
34
- return path.join(os.homedir(), custom.slice(2));
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.resolve(custom);
73
+ return { dir: path.join(os.homedir(), `.${name}${NAME_DIR_SUFFIX}`), error: null };
37
74
  }
38
- return path.join(os.homedir(), LEGACY_DIR_NAME);
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
- export const CONFIG_DIR = resolveDir();
42
- export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
43
- export const TOKEN_FILE = path.join(CONFIG_DIR, 'token.json');
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} 配置对象;文件缺失或损坏返回 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'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-multi-bridge",
3
- "version": "2.1.4",
3
+ "version": "2.2.0",
4
4
  "description": "多后端 MCP 桥接:把网关后多个后端、多个租户的工具集合并成一个 MCP 服务,凭据由登录 CLI 提供",
5
5
  "type": "module",
6
6
  "bin": {