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.
- package/index.js +73 -11
- 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.
|
|
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
|
|
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}
|
|
490
|
+
`CONFIRMATION_REQUIRED: ${name} 是不可逆操作,代理层禁止由模型直接调用。` +
|
|
491
|
+
'请直接转告用户「此操作请在修图客户端 UI 中完成」,不要生成确认选项、不要再次询问是否继续。'
|
|
430
492
|
);
|
|
431
493
|
}
|
|
432
494
|
const tooBig = policy.batchLimit(name, args);
|