ms-vite-plugin 1.1.2 → 1.1.3

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 (94) hide show
  1. package/dist/build.js +6 -0
  2. package/dist/cli.js +91 -1
  3. package/dist/mcp/device-config.d.ts +55 -0
  4. package/dist/mcp/device-config.js +183 -0
  5. package/dist/mcp/docs-service.d.ts +65 -0
  6. package/dist/mcp/docs-service.js +168 -0
  7. package/dist/mcp/project.d.ts +16 -0
  8. package/dist/mcp/project.js +74 -0
  9. package/dist/mcp/tools.d.ts +18 -0
  10. package/dist/mcp/tools.js +825 -0
  11. package/dist/mcp/types.d.ts +32 -0
  12. package/dist/mcp/types.js +11 -0
  13. package/dist/mcp-server.d.ts +2 -0
  14. package/dist/mcp-server.js +86 -0
  15. package/dist/project.d.ts +89 -0
  16. package/dist/project.js +306 -0
  17. package/dist/version.d.ts +12 -0
  18. package/dist/version.js +63 -0
  19. package/docs/api/action.md +922 -0
  20. package/docs/api/appleocr.md +229 -0
  21. package/docs/api/config.md +122 -0
  22. package/docs/api/cryptoUtils.md +232 -0
  23. package/docs/api/device.md +374 -0
  24. package/docs/api/file.md +516 -0
  25. package/docs/api/global.md +617 -0
  26. package/docs/api/hid.md +1032 -0
  27. package/docs/api/hotUpdate.md +166 -0
  28. package/docs/api/http.md +548 -0
  29. package/docs/api/image.md +907 -0
  30. package/docs/api/ime.md +290 -0
  31. package/docs/api/logger.md +324 -0
  32. package/docs/api/media.md +248 -0
  33. package/docs/api/mysql.md +441 -0
  34. package/docs/api/netCard.md +200 -0
  35. package/docs/api/node.md +353 -0
  36. package/docs/api/paddleocr.md +246 -0
  37. package/docs/api/pip.md +242 -0
  38. package/docs/api/system.md +572 -0
  39. package/docs/api/thread.md +269 -0
  40. package/docs/api/tomatoocr.md +425 -0
  41. package/docs/api/tts.md +334 -0
  42. package/docs/api/ui.md +947 -0
  43. package/docs/api/utils.md +265 -0
  44. package/docs/api/yolo.md +310 -0
  45. package/docs/apicn/action.md +919 -0
  46. package/docs/apicn/appleocr.md +233 -0
  47. package/docs/apicn/config.md +120 -0
  48. package/docs/apicn/device.md +385 -0
  49. package/docs/apicn/file.md +511 -0
  50. package/docs/apicn/global.md +613 -0
  51. package/docs/apicn/hid.md +1033 -0
  52. package/docs/apicn/hotUpdate.md +170 -0
  53. package/docs/apicn/http.md +672 -0
  54. package/docs/apicn/image.md +924 -0
  55. package/docs/apicn/ime.md +290 -0
  56. package/docs/apicn/logger.md +332 -0
  57. package/docs/apicn/media.md +252 -0
  58. package/docs/apicn/mysql.md +445 -0
  59. package/docs/apicn/netCard.md +200 -0
  60. package/docs/apicn/node.md +362 -0
  61. package/docs/apicn/paddleocr.md +255 -0
  62. package/docs/apicn/pip.md +242 -0
  63. package/docs/apicn/system.md +575 -0
  64. package/docs/apicn/thread.md +269 -0
  65. package/docs/apicn/tts.md +338 -0
  66. package/docs/apicn/ui.md +933 -0
  67. package/docs/apicn/utils.md +265 -0
  68. package/docs/apicn/yolo.md +314 -0
  69. package/docs/apipython/action.md +901 -0
  70. package/docs/apipython/appleocr.md +226 -0
  71. package/docs/apipython/config.md +126 -0
  72. package/docs/apipython/cryptoUtils.md +246 -0
  73. package/docs/apipython/device.md +365 -0
  74. package/docs/apipython/file.md +476 -0
  75. package/docs/apipython/g.md +154 -0
  76. package/docs/apipython/hid.md +1059 -0
  77. package/docs/apipython/hotUpdate.md +154 -0
  78. package/docs/apipython/image.md +938 -0
  79. package/docs/apipython/ime.md +306 -0
  80. package/docs/apipython/logger.md +330 -0
  81. package/docs/apipython/media.md +221 -0
  82. package/docs/apipython/mysql.md +432 -0
  83. package/docs/apipython/netCard.md +219 -0
  84. package/docs/apipython/node.md +331 -0
  85. package/docs/apipython/overview.md +66 -0
  86. package/docs/apipython/paddleocr.md +211 -0
  87. package/docs/apipython/pip.md +231 -0
  88. package/docs/apipython/system.md +458 -0
  89. package/docs/apipython/tomatoocr.md +444 -0
  90. package/docs/apipython/tts.md +331 -0
  91. package/docs/apipython/ui.md +949 -0
  92. package/docs/apipython/utils.md +284 -0
  93. package/docs/apipython/yolo.md +281 -0
  94. package/package.json +8 -4
@@ -0,0 +1,825 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DEFAULT_DEVICE_PORT = void 0;
37
+ exports.createMcpServer = createMcpServer;
38
+ const mcp_1 = require("@modelcontextprotocol/sdk/server/mcp");
39
+ const z = __importStar(require("zod/v4"));
40
+ const fsExtra = __importStar(require("fs-extra"));
41
+ const os = __importStar(require("os"));
42
+ const path = __importStar(require("path"));
43
+ const build_1 = require("../build");
44
+ const packager_1 = require("../packager");
45
+ const project_1 = require("../project");
46
+ const ws_manager_1 = require("../ws-manager");
47
+ const project_2 = require("./project");
48
+ const device_config_1 = require("./device-config");
49
+ Object.defineProperty(exports, "DEFAULT_DEVICE_PORT", { enumerable: true, get: function () { return device_config_1.DEFAULT_DEVICE_PORT; } });
50
+ const docs_service_1 = require("./docs-service");
51
+ const types_1 = require("./types");
52
+ const version_1 = require("../version");
53
+ /**
54
+ * 注册文档资源
55
+ * @param server MCP 服务实例
56
+ * @returns 无返回值
57
+ * @example
58
+ * registerDocResources(server)
59
+ */
60
+ function registerDocResources(server) {
61
+ server.registerResource("current-language", "kuaijs://docs/current-language", {
62
+ title: "Current KuaiJS API language",
63
+ mimeType: "application/json",
64
+ }, async () => {
65
+ const language = (0, docs_service_1.getCurrentDocsLanguage)();
66
+ return {
67
+ contents: [
68
+ {
69
+ uri: "kuaijs://docs/current-language",
70
+ mimeType: "application/json",
71
+ text: JSON.stringify({
72
+ currentLanguage: language,
73
+ label: types_1.DOC_LANGUAGE_LABELS[language],
74
+ }, null, 2),
75
+ },
76
+ ],
77
+ };
78
+ });
79
+ server.registerResource("api-doc", new mcp_1.ResourceTemplate("kuaijs://docs/current/{slug}", {
80
+ list: async () => {
81
+ const language = (0, docs_service_1.getCurrentDocsLanguage)();
82
+ const docs = await (0, docs_service_1.getApiDocsByLanguage)(language);
83
+ return {
84
+ resources: docs.map((doc) => ({
85
+ uri: `kuaijs://docs/current/${doc.slug}`,
86
+ name: doc.title,
87
+ description: `[${types_1.DOC_LANGUAGE_LABELS[language]}] ${doc.slug}`,
88
+ mimeType: "text/markdown",
89
+ })),
90
+ };
91
+ },
92
+ }), {
93
+ title: "KuaiJS API documents",
94
+ mimeType: "text/markdown",
95
+ }, async (_uri, variables) => {
96
+ const language = (0, docs_service_1.getCurrentDocsLanguage)();
97
+ const docs = await (0, docs_service_1.getApiDocsByLanguage)(language);
98
+ const slug = String(variables.slug || "");
99
+ const target = docs.find((doc) => doc.slug === slug);
100
+ if (!target) {
101
+ throw new Error(`文档不存在: ${slug}`);
102
+ }
103
+ return {
104
+ contents: [
105
+ {
106
+ uri: `kuaijs://docs/current/${target.slug}`,
107
+ mimeType: "text/markdown",
108
+ text: target.content,
109
+ },
110
+ ],
111
+ };
112
+ });
113
+ }
114
+ /**
115
+ * 注册文档工具
116
+ * @param server MCP 服务实例
117
+ * @returns 无返回值
118
+ * @example
119
+ * registerDocTools(server)
120
+ */
121
+ function registerDocTools(server) {
122
+ server.registerTool("get_docs_language", {
123
+ title: "Get Docs Language",
124
+ description: "获取当前 KuaiJS API 文档语言。",
125
+ inputSchema: {},
126
+ }, async () => {
127
+ const language = (0, docs_service_1.getCurrentDocsLanguage)();
128
+ return {
129
+ content: [
130
+ {
131
+ type: "text",
132
+ text: `当前文档语言: ${language} (${types_1.DOC_LANGUAGE_LABELS[language]})`,
133
+ },
134
+ ],
135
+ };
136
+ });
137
+ server.registerTool("set_docs_language", {
138
+ title: "Set Docs Language",
139
+ description: "设置当前 KuaiJS API 文档语言,影响后续文档查询与读取。",
140
+ inputSchema: {
141
+ language: z
142
+ .enum(["js", "js_zh", "python"])
143
+ .describe("文档语言:js | js_zh | python"),
144
+ },
145
+ }, async ({ language }) => {
146
+ const active = (0, docs_service_1.setCurrentDocsLanguage)(language);
147
+ const docsDir = (0, docs_service_1.getDocsDirByLanguage)(active);
148
+ return {
149
+ content: [
150
+ {
151
+ type: "text",
152
+ text: `文档语言已切换为 ${active} (${types_1.DOC_LANGUAGE_LABELS[active]})\n目录: ${docsDir}`,
153
+ },
154
+ ],
155
+ };
156
+ });
157
+ server.registerTool("list_api_docs", {
158
+ title: "List API Docs",
159
+ description: "列出当前(或指定)语言下可用的 API 文档。",
160
+ inputSchema: {
161
+ language: z
162
+ .enum(["js", "js_zh", "python"])
163
+ .optional()
164
+ .describe("可选语言,不传则使用当前语言"),
165
+ },
166
+ }, async ({ language }) => {
167
+ const activeLanguage = (0, docs_service_1.resolveDocsLanguage)(language);
168
+ const docs = await (0, docs_service_1.getApiDocsByLanguage)(activeLanguage);
169
+ const lines = docs.map((doc, index) => `${index + 1}. ${doc.title}\nslug: ${doc.slug}\nuri: ${(0, docs_service_1.getDocUri)(activeLanguage, doc.slug)}`);
170
+ return {
171
+ content: [
172
+ {
173
+ type: "text",
174
+ text: lines.length === 0
175
+ ? `当前语言 ${activeLanguage} 下没有可用文档。`
176
+ : [
177
+ `当前语言: ${activeLanguage} (${types_1.DOC_LANGUAGE_LABELS[activeLanguage]})`,
178
+ "",
179
+ ...lines,
180
+ ].join("\n"),
181
+ },
182
+ ],
183
+ };
184
+ });
185
+ server.registerTool("search_api_docs", {
186
+ title: "Search API Docs",
187
+ description: "在当前(或指定)语言文档中按关键字搜索。",
188
+ inputSchema: {
189
+ query: z.string().min(1).describe("搜索关键字"),
190
+ limit: z
191
+ .number()
192
+ .int()
193
+ .min(1)
194
+ .max(20)
195
+ .optional()
196
+ .default(5)
197
+ .describe("返回条数上限,默认 5"),
198
+ language: z
199
+ .enum(["js", "js_zh", "python"])
200
+ .optional()
201
+ .describe("可选语言,不传则使用当前语言"),
202
+ },
203
+ }, async ({ query, limit, language }) => {
204
+ const activeLanguage = (0, docs_service_1.resolveDocsLanguage)(language);
205
+ const docs = await (0, docs_service_1.getApiDocsByLanguage)(activeLanguage);
206
+ const normalizedQuery = query.toLowerCase();
207
+ const matches = docs
208
+ .map((doc) => ({ doc, score: (0, docs_service_1.scoreApiDoc)(doc, normalizedQuery) }))
209
+ .filter((item) => item.score > 0)
210
+ .sort((a, b) => b.score - a.score || a.doc.slug.localeCompare(b.doc.slug))
211
+ .slice(0, limit)
212
+ .map((item) => item.doc);
213
+ const text = matches.length === 0
214
+ ? `当前语言: ${activeLanguage} (${types_1.DOC_LANGUAGE_LABELS[activeLanguage]})\n未找到与 "${query}" 匹配的文档。`
215
+ : [
216
+ `当前语言: ${activeLanguage} (${types_1.DOC_LANGUAGE_LABELS[activeLanguage]})`,
217
+ "",
218
+ ...matches.map((doc, index) => `${index + 1}. ${doc.title}\nslug: ${doc.slug}\nuri: ${(0, docs_service_1.getDocUri)(activeLanguage, doc.slug)}`),
219
+ ].join("\n");
220
+ return {
221
+ content: [
222
+ {
223
+ type: "text",
224
+ text,
225
+ },
226
+ ],
227
+ };
228
+ });
229
+ server.registerTool("read_api_doc", {
230
+ title: "Read API Doc",
231
+ description: "读取当前(或指定)语言下某个文档的完整 markdown 内容。",
232
+ inputSchema: {
233
+ slug: z.string().min(1).describe("文档 slug(文件名,不含 .md)"),
234
+ language: z
235
+ .enum(["js", "js_zh", "python"])
236
+ .optional()
237
+ .describe("可选语言,不传则使用当前语言"),
238
+ },
239
+ }, async ({ slug, language }) => {
240
+ const activeLanguage = (0, docs_service_1.resolveDocsLanguage)(language);
241
+ const docs = await (0, docs_service_1.getApiDocsByLanguage)(activeLanguage);
242
+ const target = docs.find((doc) => doc.slug === slug);
243
+ if (!target) {
244
+ return {
245
+ content: [
246
+ {
247
+ type: "text",
248
+ text: `未找到文档 ${slug}.md(语言: ${activeLanguage})。`,
249
+ },
250
+ ],
251
+ isError: true,
252
+ };
253
+ }
254
+ return {
255
+ content: [
256
+ {
257
+ type: "text",
258
+ text: `标题: ${target.title}\n语言: ${activeLanguage}\nURI: ${(0, docs_service_1.getDocUri)(activeLanguage, target.slug)}\n\n${target.content}`,
259
+ },
260
+ ],
261
+ };
262
+ });
263
+ }
264
+ /**
265
+ * 注册设备与项目执行工具
266
+ * @param server MCP 服务实例
267
+ * @returns 无返回值
268
+ * @example
269
+ * registerRuntimeTools(server)
270
+ */
271
+ function registerRuntimeTools(server) {
272
+ /**
273
+ * 解析运行目标:
274
+ * - 显式 transport=ws: 强制走 ws
275
+ * - 显式 transport=http: 强制走 http
276
+ * - 未显式 transport:
277
+ * 1) 若传入 ip/port,则走 http
278
+ * 2) 若当前已有 ws 设备连接,则优先走 ws
279
+ * 3) 否则回退到 http 默认设备配置
280
+ * @param options 工具调用参数
281
+ * @returns 返回标准化后的请求目标
282
+ * @example
283
+ * const target = await resolvePreferredRuntimeTarget({ ip: "192.168.1.10" })
284
+ */
285
+ async function resolvePreferredRuntimeTarget(options) {
286
+ const hasHttpHint = options.ip !== undefined || options.port !== undefined;
287
+ const shouldUseWs = options.transport === "ws" ||
288
+ (options.transport === undefined &&
289
+ !hasHttpHint &&
290
+ ws_manager_1.WSManager.isConnected());
291
+ if (shouldUseWs) {
292
+ const wsPort = String(options.wsPort ?? 31111);
293
+ const wsWaitMs = String(options.wsWaitMs ?? 30000);
294
+ return {
295
+ transport: "ws",
296
+ wsPort,
297
+ wsWaitMs,
298
+ label: `transport=ws (wsPort=${wsPort})`,
299
+ };
300
+ }
301
+ const device = await (0, device_config_1.resolveDeviceConfig)(options.ip, options.port);
302
+ return {
303
+ transport: "http",
304
+ ip: device.ip,
305
+ port: (0, device_config_1.normalizePort)(device.port),
306
+ label: `${device.ip}:${device.port}`,
307
+ };
308
+ }
309
+ server.registerTool("set_device", {
310
+ title: "Set Device",
311
+ description: "设置默认设备连接信息。首次配置后,后续 run/stop 可不再传 ip。",
312
+ inputSchema: {
313
+ ip: z.string().min(1).describe("设备 IP 地址,例如 192.168.1.100"),
314
+ port: z
315
+ .number()
316
+ .int()
317
+ .min(1)
318
+ .max(65535)
319
+ .optional()
320
+ .describe("设备端口,默认 9800"),
321
+ },
322
+ }, async ({ ip, port }) => {
323
+ const config = await (0, device_config_1.setDeviceConfig)(ip, port);
324
+ return {
325
+ content: [
326
+ {
327
+ type: "text",
328
+ text: `默认设备已设置为 ${config.ip}:${config.port}`,
329
+ },
330
+ ],
331
+ };
332
+ });
333
+ server.registerTool("get_device", {
334
+ title: "Get Device",
335
+ description: "查看当前默认设备配置。",
336
+ inputSchema: {},
337
+ }, async () => {
338
+ const config = await (0, device_config_1.readDeviceConfig)();
339
+ if (!config) {
340
+ return {
341
+ content: [
342
+ { type: "text", text: "当前未设置默认设备,请先调用 set_device。" },
343
+ ],
344
+ };
345
+ }
346
+ return {
347
+ content: [
348
+ { type: "text", text: `当前默认设备: ${config.ip}:${config.port}` },
349
+ ],
350
+ };
351
+ });
352
+ server.registerTool("take_screenshot", {
353
+ title: "Take Screenshot",
354
+ description: "获取设备截图。可返回 base64,或落地到文件后返回文件路径(默认写入系统临时目录)。",
355
+ inputSchema: {
356
+ transport: z
357
+ .enum(["http", "ws"])
358
+ .optional()
359
+ .describe("传输方式:http|ws;不传时自动优先 ws(若已连接)"),
360
+ ip: z
361
+ .string()
362
+ .min(1)
363
+ .optional()
364
+ .describe("设备 IP 地址(transport=http 时可传;未传则使用 set_device 默认值)"),
365
+ port: z
366
+ .number()
367
+ .int()
368
+ .min(1)
369
+ .max(65535)
370
+ .optional()
371
+ .describe("设备端口,默认 9800"),
372
+ wsPort: z
373
+ .number()
374
+ .int()
375
+ .min(1)
376
+ .max(65535)
377
+ .optional()
378
+ .describe("WS 服务端口(transport=ws 时可选,默认 31111)"),
379
+ wsWaitMs: z
380
+ .number()
381
+ .int()
382
+ .min(1)
383
+ .max(600000)
384
+ .optional()
385
+ .describe("WS 等待连接超时毫秒(transport=ws 时可选,默认 30000)"),
386
+ format: z
387
+ .enum(["file", "base64"])
388
+ .optional()
389
+ .default("file")
390
+ .describe("截图返回格式:file=文件路径,base64=图片 base64"),
391
+ outputPath: z
392
+ .string()
393
+ .min(1)
394
+ .optional()
395
+ .describe("当 format=file 时可指定输出路径,不传则写入系统临时目录"),
396
+ },
397
+ }, async ({ transport, ip, port, wsPort, wsWaitMs, format, outputPath }) => {
398
+ const target = await resolvePreferredRuntimeTarget({
399
+ transport,
400
+ ip,
401
+ port,
402
+ wsPort,
403
+ wsWaitMs,
404
+ });
405
+ const requestOptions = target.transport === "ws"
406
+ ? {
407
+ transport: "ws",
408
+ wsPort: target.wsPort,
409
+ wsWaitMs: target.wsWaitMs,
410
+ }
411
+ : {
412
+ ip: target.ip,
413
+ port: target.port,
414
+ transport: "http",
415
+ };
416
+ if (format === "base64") {
417
+ const base64 = await (0, project_1.getScreenshotBase64OnDevice)(requestOptions);
418
+ return {
419
+ content: [
420
+ {
421
+ type: "text",
422
+ text: `截图成功: ${target.label}\nformat: base64\n${base64}`,
423
+ },
424
+ ],
425
+ };
426
+ }
427
+ const image = await (0, project_1.getScreenshotOnDevice)(requestOptions);
428
+ const targetPath = outputPath && outputPath.trim()
429
+ ? path.resolve(outputPath.trim())
430
+ : path.join(os.tmpdir(), `ms-mcp-screenshot-${Date.now()}-${Math.random()
431
+ .toString(36)
432
+ .slice(2, 8)}.jpg`);
433
+ await fsExtra.ensureDir(path.dirname(targetPath));
434
+ await fsExtra.writeFile(targetPath, image);
435
+ return {
436
+ content: [
437
+ {
438
+ type: "text",
439
+ text: `截图成功: ${target.label}\nformat: file\npath: ${targetPath}\nsize: ${image.length} bytes`,
440
+ },
441
+ ],
442
+ };
443
+ });
444
+ server.registerTool("get_node_source", {
445
+ title: "Get Node Source",
446
+ description: "获取设备当前页面节点 XML(/api/source)。",
447
+ inputSchema: {
448
+ transport: z
449
+ .enum(["http", "ws"])
450
+ .optional()
451
+ .describe("传输方式:http|ws;不传时自动优先 ws(若已连接)"),
452
+ ip: z
453
+ .string()
454
+ .min(1)
455
+ .optional()
456
+ .describe("设备 IP 地址(transport=http 时可传;未传则使用 set_device 默认值)"),
457
+ port: z
458
+ .number()
459
+ .int()
460
+ .min(1)
461
+ .max(65535)
462
+ .optional()
463
+ .describe("设备端口,默认 9800"),
464
+ wsPort: z
465
+ .number()
466
+ .int()
467
+ .min(1)
468
+ .max(65535)
469
+ .optional()
470
+ .describe("WS 服务端口(transport=ws 时可选,默认 31111)"),
471
+ wsWaitMs: z
472
+ .number()
473
+ .int()
474
+ .min(1)
475
+ .max(600000)
476
+ .optional()
477
+ .describe("WS 等待连接超时毫秒(transport=ws 时可选,默认 30000)"),
478
+ maxDepth: z
479
+ .number()
480
+ .int()
481
+ .min(1)
482
+ .max(200)
483
+ .optional()
484
+ .default(50)
485
+ .describe("节点树最大深度,默认 50"),
486
+ timeout: z
487
+ .number()
488
+ .int()
489
+ .min(1)
490
+ .max(600)
491
+ .optional()
492
+ .default(120)
493
+ .describe("设备端节点抓取超时秒数,默认 120"),
494
+ },
495
+ }, async ({ transport, ip, port, wsPort, wsWaitMs, maxDepth, timeout }) => {
496
+ const target = await resolvePreferredRuntimeTarget({
497
+ transport,
498
+ ip,
499
+ port,
500
+ wsPort,
501
+ wsWaitMs,
502
+ });
503
+ const requestOptions = target.transport === "ws"
504
+ ? {
505
+ transport: "ws",
506
+ wsPort: target.wsPort,
507
+ wsWaitMs: target.wsWaitMs,
508
+ }
509
+ : {
510
+ ip: target.ip,
511
+ port: target.port,
512
+ transport: "http",
513
+ };
514
+ const source = await (0, project_1.getSourceOnDevice)(requestOptions, maxDepth, timeout);
515
+ return {
516
+ content: [
517
+ {
518
+ type: "text",
519
+ text: `节点获取成功: ${target.label}\nmaxDepth: ${maxDepth}\ntimeout: ${timeout}\n\n${source}`,
520
+ },
521
+ ],
522
+ };
523
+ });
524
+ server.registerTool("watch_logs", {
525
+ title: "Watch Logs",
526
+ description: "通过 SSE 监听设备日志并自动判定异常。首次可传 ip,也可复用 set_device 保存的默认设备。",
527
+ inputSchema: {
528
+ ip: z
529
+ .string()
530
+ .min(1)
531
+ .optional()
532
+ .describe("设备 IP 地址,未传则使用 set_device 保存的默认值"),
533
+ port: z
534
+ .number()
535
+ .int()
536
+ .min(1)
537
+ .max(65535)
538
+ .optional()
539
+ .describe("设备端口,默认 9800"),
540
+ durationSeconds: z
541
+ .number()
542
+ .int()
543
+ .min(1)
544
+ .max(300)
545
+ .optional()
546
+ .default(15)
547
+ .describe("监听时长(秒),默认 15 秒,最大 300 秒"),
548
+ maxLogs: z
549
+ .number()
550
+ .int()
551
+ .min(10)
552
+ .max(5000)
553
+ .optional()
554
+ .default(200)
555
+ .describe("最多收集日志条数,默认 200"),
556
+ },
557
+ }, async ({ ip, port, durationSeconds, maxLogs }) => {
558
+ await (0, project_2.ensureValidKuaiJSProject)(process.cwd());
559
+ const device = await (0, device_config_1.resolveDeviceConfig)(ip, port);
560
+ const result = await (0, project_1.watchDeviceLogsBySse)(device.ip, device.port, durationSeconds * 1000, maxLogs);
561
+ const findingsPreview = result.findings
562
+ .slice(0, 20)
563
+ .map((item, index) => `${index + 1}. [${item.severity}] ${item.rule} | ${item.log.timestamp} | ${item.log.message}`)
564
+ .join("\n");
565
+ const logsPreview = result.logs
566
+ .slice(-20)
567
+ .map((log) => `[${log.timestamp}] [${log.level.toUpperCase()}] ${log.message}`)
568
+ .join("\n");
569
+ return {
570
+ content: [
571
+ {
572
+ type: "text",
573
+ text: [
574
+ `监听目标: ${device.ip}:${device.port}`,
575
+ `监听时长: ${durationSeconds}s,收集日志: ${result.logs.length} 条,runtime_status: ${result.runtimeStatus.length} 条`,
576
+ result.summary,
577
+ "",
578
+ "异常判定(最多展示前20条):",
579
+ findingsPreview || "未命中异常规则",
580
+ "",
581
+ "最新日志(最多展示最后20条):",
582
+ logsPreview || "无日志",
583
+ ].join("\n"),
584
+ },
585
+ ],
586
+ };
587
+ });
588
+ server.registerTool("build_project", {
589
+ title: "Build Project",
590
+ description: "构建 KuaiJS 项目,支持开发模式与生产模式。",
591
+ inputSchema: {
592
+ dev: z
593
+ .boolean()
594
+ .optional()
595
+ .describe("是否开发模式构建,true=开发模式,false=生产模式"),
596
+ },
597
+ }, async ({ dev }) => {
598
+ await (0, project_2.ensureValidKuaiJSProject)(process.cwd());
599
+ await (0, build_1.buildAll)(Boolean(dev), process.cwd());
600
+ return {
601
+ content: [
602
+ { type: "text", text: `构建完成,模式: ${dev ? "dev" : "prod"}` },
603
+ ],
604
+ };
605
+ });
606
+ server.registerTool("package_project", {
607
+ title: "Package Project",
608
+ description: "执行生产构建并加密,返回 enc.msbundle 路径。",
609
+ inputSchema: {},
610
+ }, async () => {
611
+ await (0, project_2.ensureValidKuaiJSProject)(process.cwd());
612
+ const encryptedPath = await (0, packager_1.packageProject)(process.cwd());
613
+ return {
614
+ content: [{ type: "text", text: `打包完成: ${encryptedPath}` }],
615
+ };
616
+ });
617
+ server.registerTool("run_project", {
618
+ title: "Run Project",
619
+ description: "构建并同步到设备后运行项目。不传 transport 时会自动优先使用已连接的 WS 设备。",
620
+ inputSchema: {
621
+ transport: z
622
+ .enum(["http", "ws"])
623
+ .optional()
624
+ .describe("传输方式:http|ws;不传时自动优先 ws(若已连接)"),
625
+ ip: z
626
+ .string()
627
+ .min(1)
628
+ .optional()
629
+ .describe("设备 IP 地址(transport=http 时可传;未传则使用 set_device 默认值)"),
630
+ port: z
631
+ .number()
632
+ .int()
633
+ .min(1)
634
+ .max(65535)
635
+ .optional()
636
+ .describe("设备端口,默认 9800"),
637
+ wsPort: z
638
+ .number()
639
+ .int()
640
+ .min(1)
641
+ .max(65535)
642
+ .optional()
643
+ .describe("WS 服务端口(transport=ws 时可选,默认 31111)"),
644
+ wsWaitMs: z
645
+ .number()
646
+ .int()
647
+ .min(1)
648
+ .max(600000)
649
+ .optional()
650
+ .describe("WS 等待连接超时毫秒(transport=ws 时可选,默认 30000)"),
651
+ },
652
+ }, async ({ transport, ip, port, wsPort, wsWaitMs }) => {
653
+ await (0, project_2.ensureValidKuaiJSProject)(process.cwd());
654
+ const target = await resolvePreferredRuntimeTarget({
655
+ transport,
656
+ ip,
657
+ port,
658
+ wsPort,
659
+ wsWaitMs,
660
+ });
661
+ await (0, project_1.runOnDevice)(target.transport === "ws"
662
+ ? {
663
+ transport: "ws",
664
+ wsPort: target.wsPort,
665
+ wsWaitMs: target.wsWaitMs,
666
+ }
667
+ : {
668
+ ip: target.ip,
669
+ port: target.port,
670
+ transport: "http",
671
+ });
672
+ return {
673
+ content: [
674
+ {
675
+ type: "text",
676
+ text: `运行请求已发送到 ${target.label}`,
677
+ },
678
+ ],
679
+ };
680
+ });
681
+ server.registerTool("run_ui_project", {
682
+ title: "Run UI Project",
683
+ description: "构建并同步到设备后预览 UI。不传 transport 时会自动优先使用已连接的 WS 设备。",
684
+ inputSchema: {
685
+ transport: z
686
+ .enum(["http", "ws"])
687
+ .optional()
688
+ .describe("传输方式:http|ws;不传时自动优先 ws(若已连接)"),
689
+ ip: z
690
+ .string()
691
+ .min(1)
692
+ .optional()
693
+ .describe("设备 IP 地址(transport=http 时可传;未传则使用 set_device 默认值)"),
694
+ port: z
695
+ .number()
696
+ .int()
697
+ .min(1)
698
+ .max(65535)
699
+ .optional()
700
+ .describe("设备端口,默认 9800"),
701
+ wsPort: z
702
+ .number()
703
+ .int()
704
+ .min(1)
705
+ .max(65535)
706
+ .optional()
707
+ .describe("WS 服务端口(transport=ws 时可选,默认 31111)"),
708
+ wsWaitMs: z
709
+ .number()
710
+ .int()
711
+ .min(1)
712
+ .max(600000)
713
+ .optional()
714
+ .describe("WS 等待连接超时毫秒(transport=ws 时可选,默认 30000)"),
715
+ },
716
+ }, async ({ transport, ip, port, wsPort, wsWaitMs }) => {
717
+ await (0, project_2.ensureValidKuaiJSProject)(process.cwd());
718
+ const target = await resolvePreferredRuntimeTarget({
719
+ transport,
720
+ ip,
721
+ port,
722
+ wsPort,
723
+ wsWaitMs,
724
+ });
725
+ await (0, project_1.runUIOnDevice)(target.transport === "ws"
726
+ ? {
727
+ transport: "ws",
728
+ wsPort: target.wsPort,
729
+ wsWaitMs: target.wsWaitMs,
730
+ }
731
+ : {
732
+ ip: target.ip,
733
+ port: target.port,
734
+ transport: "http",
735
+ });
736
+ return {
737
+ content: [
738
+ {
739
+ type: "text",
740
+ text: `UI 预览请求已发送到 ${target.label}`,
741
+ },
742
+ ],
743
+ };
744
+ });
745
+ server.registerTool("stop_project", {
746
+ title: "Stop Project",
747
+ description: "停止设备上的项目。不传 transport 时会自动优先使用已连接的 WS 设备。",
748
+ inputSchema: {
749
+ transport: z
750
+ .enum(["http", "ws"])
751
+ .optional()
752
+ .describe("传输方式:http|ws;不传时自动优先 ws(若已连接)"),
753
+ ip: z
754
+ .string()
755
+ .min(1)
756
+ .optional()
757
+ .describe("设备 IP 地址(transport=http 时可传;未传则使用 set_device 默认值)"),
758
+ port: z
759
+ .number()
760
+ .int()
761
+ .min(1)
762
+ .max(65535)
763
+ .optional()
764
+ .describe("设备端口,默认 9800"),
765
+ wsPort: z
766
+ .number()
767
+ .int()
768
+ .min(1)
769
+ .max(65535)
770
+ .optional()
771
+ .describe("WS 服务端口(transport=ws 时可选,默认 31111)"),
772
+ wsWaitMs: z
773
+ .number()
774
+ .int()
775
+ .min(1)
776
+ .max(600000)
777
+ .optional()
778
+ .describe("WS 等待连接超时毫秒(transport=ws 时可选,默认 30000)"),
779
+ },
780
+ }, async ({ transport, ip, port, wsPort, wsWaitMs }) => {
781
+ await (0, project_2.ensureValidKuaiJSProject)(process.cwd());
782
+ const target = await resolvePreferredRuntimeTarget({
783
+ transport,
784
+ ip,
785
+ port,
786
+ wsPort,
787
+ wsWaitMs,
788
+ });
789
+ await (0, project_1.stopOnDevice)(target.transport === "ws"
790
+ ? {
791
+ transport: "ws",
792
+ wsPort: target.wsPort,
793
+ wsWaitMs: target.wsWaitMs,
794
+ }
795
+ : {
796
+ ip: target.ip,
797
+ port: target.port,
798
+ transport: "http",
799
+ });
800
+ return {
801
+ content: [
802
+ {
803
+ type: "text",
804
+ text: `停止请求已发送到 ${target.label}`,
805
+ },
806
+ ],
807
+ };
808
+ });
809
+ }
810
+ /**
811
+ * 创建并注册 MCP 工具
812
+ * @returns 返回已注册工具的 MCP Server 实例
813
+ * @example
814
+ * const server = createMcpServer()
815
+ */
816
+ function createMcpServer() {
817
+ const server = new mcp_1.McpServer({
818
+ name: "ms-vite-plugin-mcp",
819
+ version: version_1.PACKAGE_VERSION,
820
+ });
821
+ registerDocResources(server);
822
+ registerDocTools(server);
823
+ registerRuntimeTools(server);
824
+ return server;
825
+ }