aipic-mcp-stdio 1.1.2 → 1.1.4

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 (2) hide show
  1. package/index.js +73 -11
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -25,7 +25,9 @@ const policy = require('./tool-policy');
25
25
 
26
26
  const PROTOCOL_VERSION = '2025-06-18';
27
27
  const SERVER_NAME = 'aipic-mcp-stdio';
28
- const SERVER_VERSION = '1.1.2';
28
+ const SERVER_VERSION = '1.1.4';
29
+ /** 要求的最低修图客户端版本:低于此(或 endpoint 文件没有 version 字段的旧客户端)都提示更新 */
30
+ const MIN_CLIENT_VERSION = '3.18.11-alpha.1';
29
31
  const ENDPOINT_FILENAME = 'aipic-mcp-endpoint.json';
30
32
  /** 客户端 productName(electron-builder.yml),userData 目录名即此值 */
31
33
  const PRODUCT_NAME = 'NetdiskAIPhoto';
@@ -39,6 +41,7 @@ const ERR = {
39
41
  UPSTREAM_FAILED: -32005,
40
42
  BATCH_TOO_LARGE: -32006,
41
43
  CLIENT_CRASHED: -32007,
44
+ CLIENT_VERSION_LOW: -32008,
42
45
  };
43
46
 
44
47
  /**
@@ -49,8 +52,8 @@ const STATUS_TOOL_NAME = 'aipic_client_status';
49
52
  const STATUS_TOOL = {
50
53
  name: STATUS_TOOL_NAME,
51
54
  description:
52
- '探测百度网盘AI修图客户端是否在运行。' +
53
- '当其他工具报「客户端未在运行 / 可能已崩溃」时,可调用本工具获取诊断与操作建议。',
55
+ '探测百度网盘AI修图客户端状态(是否运行、版本是否过低)。' +
56
+ '当其他工具报「客户端未在运行 / 连接异常 / 版本过低」时,可调用本工具获取诊断与操作建议。',
54
57
  inputSchema: { type: 'object', properties: {}, additionalProperties: false },
55
58
  };
56
59
 
@@ -58,6 +61,50 @@ function log(...args) {
58
61
  process.stderr.write(`[aipic-stdio] ${args.join(' ')}\n`);
59
62
  }
60
63
 
64
+ /**
65
+ * 极简 semver 比较,兼容 app.getVersion() 的 "major.minor.patch[-prerelease]" 形态。
66
+ * 只比前三位数字,数字相同时「有 prerelease 的版本 < 同版本无 prerelease」。足够支撑
67
+ * 「是否低于最低版本」的判断;不引入外部依赖(npm 包 files 只发 index.js + tool-policy.js)。
68
+ */
69
+ function compareSemver(a, b) {
70
+ const pa = parseSemver(a);
71
+ const pb = parseSemver(b);
72
+ for (let i = 0; i < 3; i += 1) {
73
+ if (pa.nums[i] !== pb.nums[i]) {
74
+ return pa.nums[i] < pb.nums[i] ? -1 : 1;
75
+ }
76
+ }
77
+ if (pa.pre && !pb.pre) {
78
+ return -1;
79
+ }
80
+ if (!pa.pre && pb.pre) {
81
+ return 1;
82
+ }
83
+ return 0;
84
+ }
85
+
86
+ function parseSemver(v) {
87
+ const s = String(v || '').trim();
88
+ const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(s);
89
+ if (!m) {
90
+ return { nums: [0, 0, 0], pre: null };
91
+ }
92
+ return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] || null };
93
+ }
94
+
95
+ /** 版本是否过低:endpoint 没有 version 字段(旧客户端)或低于 MIN_CLIENT_VERSION,都按过低处理 */
96
+ function isVersionTooLow(version) {
97
+ if (typeof version !== 'string' || !version) {
98
+ return true;
99
+ }
100
+ return compareSemver(version, MIN_CLIENT_VERSION) < 0;
101
+ }
102
+
103
+ function clientVersionLowHint(version) {
104
+ const current = typeof version === 'string' && version ? version : '未知';
105
+ return `百度网盘AI修图客户端版本 ${current} 过低,需要 ${MIN_CLIENT_VERSION} 及以上版本,请更新客户端后重试`;
106
+ }
107
+
61
108
  /**
62
109
  * endpoint 文件路径。客户端写在 app.getPath('userData'),各平台展开如下;
63
110
  * 允许用 AIPIC_MCP_ENDPOINT_FILE 覆盖(调试 / 多实例)。
@@ -95,7 +142,7 @@ function tryReadEndpoint() {
95
142
  err.rpcCode = ERR.ENDPOINT_MALFORMED;
96
143
  throw err;
97
144
  }
98
- const { host, port, path: rpcPath, token } = json || {};
145
+ const { host, port, path: rpcPath, token, version } = json || {};
99
146
  if (
100
147
  typeof host !== 'string' ||
101
148
  typeof port !== 'number' ||
@@ -106,7 +153,7 @@ function tryReadEndpoint() {
106
153
  err.rpcCode = ERR.ENDPOINT_MALFORMED;
107
154
  throw err;
108
155
  }
109
- return { host, port, path: rpcPath, token };
156
+ return { host, port, path: rpcPath, token, version };
110
157
  }
111
158
 
112
159
  /** 读 endpoint;文件不存在时抛 CLIENT_NOT_RUNNING(给 forward 用) */
@@ -179,6 +226,11 @@ function parseBody(text) {
179
226
  */
180
227
  async function forward(body, timeoutMs) {
181
228
  let ep = readEndpoint();
229
+ if (isVersionTooLow(ep.version)) {
230
+ const err = new Error(clientVersionLowHint(ep.version));
231
+ err.rpcCode = ERR.CLIENT_VERSION_LOW;
232
+ throw err;
233
+ }
182
234
  let res;
183
235
  try {
184
236
  res = await post(ep, body, timeoutMs);
@@ -222,7 +274,7 @@ async function forward(body, timeoutMs) {
222
274
  function wrapConnError(e) {
223
275
  const code = e && e.code;
224
276
  if (code === 'ECONNREFUSED' || code === 'ECONNRESET' || code === 'EPIPE') {
225
- const err = new Error('百度网盘AI修图客户端可能已崩溃或异常退出,请重启客户端后重试');
277
+ const err = new Error('百度网盘AI修图客户端连接异常或已退出,请重启客户端后重试');
226
278
  err.rpcCode = ERR.CLIENT_CRASHED;
227
279
  return err;
228
280
  }
@@ -230,10 +282,11 @@ function wrapConnError(e) {
230
282
  }
231
283
 
232
284
  /**
233
- * 探测客户端状态,分三类:
285
+ * 探测客户端状态,分四类:
234
286
  * - not_running:endpoint 文件不存在(没装 / 没启动 / 正常退出),统一提示安装并启动;
287
+ * - version_too_low:endpoint 文件在但版本过低(或旧客户端没写 version 字段),提示更新客户端;
235
288
  * - crashed:endpoint 文件在,但端口连不上(崩溃 / 异常退出残留),提示重启;
236
- * - running:端口有 HTTP 应答。
289
+ * - running:版本合格且端口有 HTTP 应答。
237
290
  * 只在本地做(读文件 + 短超时探端口),不触发任何业务。
238
291
  */
239
292
  const PROBE_TIMEOUT_MS = 2000;
@@ -254,14 +307,22 @@ async function probeClient() {
254
307
  hint: '百度网盘AI修图客户端未在运行,请安装并启动客户端后重试',
255
308
  };
256
309
  }
310
+ if (isVersionTooLow(ep.version)) {
311
+ return {
312
+ state: 'version_too_low',
313
+ version: ep.version || null,
314
+ min_version: MIN_CLIENT_VERSION,
315
+ hint: clientVersionLowHint(ep.version),
316
+ };
317
+ }
257
318
  try {
258
319
  // 用不存在的 method 探活:只要端口有 HTTP 应答就说明客户端在跑,body 忽略
259
320
  await post(ep, { jsonrpc: '2.0', id: 0, method: 'ping', params: {} }, PROBE_TIMEOUT_MS);
260
- return { state: 'running', hint: '百度网盘AI修图客户端运行正常' };
321
+ return { state: 'running', version: ep.version, hint: '百度网盘AI修图客户端运行正常' };
261
322
  } catch (e) {
262
323
  return {
263
324
  state: 'crashed',
264
- hint: '百度网盘AI修图客户端可能已崩溃或异常退出,请重启客户端后重试',
325
+ hint: '百度网盘AI修图客户端连接异常或已退出,请重启客户端后重试',
265
326
  };
266
327
  }
267
328
  }
@@ -426,7 +487,8 @@ async function handleToolsCall(msg) {
426
487
  if (policy.requiresConfirmation(name)) {
427
488
  return toolError(
428
489
  msg.id,
429
- `CONFIRMATION_REQUIRED: ${name} 会丢数据,代理层禁止由模型直接调用。` + '请引导用户在修图客户端 UI 里操作。'
490
+ `CONFIRMATION_REQUIRED: ${name} 是不可逆操作,代理层禁止由模型直接调用。` +
491
+ '请直接转告用户「此操作请在修图客户端 UI 中完成」,不要生成确认选项、不要再次询问是否继续。'
430
492
  );
431
493
  }
432
494
  const tooBig = policy.batchLimit(name, args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aipic-mcp-stdio",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "stdio <-> HTTP proxy for the Baidu Netdisk AI Photo (NetdiskAIPhoto) local MCP server",
5
5
  "keywords": [
6
6
  "mcp",