ms-vite-plugin 1.1.16 → 1.1.18
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/dist/mcp/control-tools.d.ts +9 -0
- package/dist/mcp/control-tools.js +201 -0
- package/dist/mcp/device-log.d.ts +25 -0
- package/dist/mcp/device-log.js +391 -0
- package/dist/mcp/doc-tools.d.ts +17 -0
- package/dist/mcp/doc-tools.js +210 -0
- package/dist/mcp/hid-tools.d.ts +9 -0
- package/dist/mcp/hid-tools.js +254 -0
- package/dist/mcp/ime-tools.d.ts +9 -0
- package/dist/mcp/ime-tools.js +130 -0
- package/dist/mcp/mirror-tools.d.ts +9 -0
- package/dist/mcp/mirror-tools.js +82 -0
- package/dist/mcp/runtime-tools.d.ts +9 -0
- package/dist/mcp/runtime-tools.js +391 -0
- package/dist/mcp/tool-utils.d.ts +104 -0
- package/dist/mcp/tool-utils.js +170 -0
- package/dist/mcp/tools.d.ts +1 -1
- package/dist/mcp/tools.js +6 -928
- package/docs/api/hid.md +21 -0
- package/docs/apicn/hid.md +21 -0
- package/docs/apipython/hid.md +22 -0
- package/package.json +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
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.registerMirrorTools = registerMirrorTools;
|
|
37
|
+
const z = __importStar(require("zod/v4"));
|
|
38
|
+
const tool_utils_1 = require("./tool-utils");
|
|
39
|
+
/**
|
|
40
|
+
* 注册镜像流相关 MCP 工具
|
|
41
|
+
* @param server MCP 服务实例
|
|
42
|
+
* @returns 无返回值
|
|
43
|
+
* @example
|
|
44
|
+
* registerMirrorTools(server)
|
|
45
|
+
*/
|
|
46
|
+
function registerMirrorTools(server) {
|
|
47
|
+
server.registerTool("get_mirror_mjpeg_url", {
|
|
48
|
+
title: "Get Mirror MJPEG URL",
|
|
49
|
+
description: "生成设备镜像 MJPEG 流地址。MCP 普通文本/图片响应不适合持续显示 MJPEG,建议将返回 URL 交给浏览器或支持 MJPEG 的查看器打开。",
|
|
50
|
+
inputSchema: {
|
|
51
|
+
fps: z.number().int().min(1).max(60).optional().default(10).describe("帧率,默认 10"),
|
|
52
|
+
quality: z
|
|
53
|
+
.number()
|
|
54
|
+
.min(0.1)
|
|
55
|
+
.max(1)
|
|
56
|
+
.optional()
|
|
57
|
+
.default(0.25)
|
|
58
|
+
.describe("压缩质量,默认 0.25"),
|
|
59
|
+
scalingFactor: z
|
|
60
|
+
.number()
|
|
61
|
+
.min(0.1)
|
|
62
|
+
.max(1)
|
|
63
|
+
.optional()
|
|
64
|
+
.default(1)
|
|
65
|
+
.describe("缩放因子,默认 1.0"),
|
|
66
|
+
},
|
|
67
|
+
}, async ({ fps, quality, scalingFactor }) => {
|
|
68
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
69
|
+
const url = new URL(`http://${target.ip}:${target.port}/mirror/image`);
|
|
70
|
+
url.searchParams.set("fps", String(fps));
|
|
71
|
+
url.searchParams.set("quality", String(quality));
|
|
72
|
+
url.searchParams.set("scalingFactor", String(scalingFactor));
|
|
73
|
+
return (0, tool_utils_1.createTextToolResult)([
|
|
74
|
+
`MJPEG 地址: ${url.toString()}`,
|
|
75
|
+
`设备: ${target.label}`,
|
|
76
|
+
`fps: ${fps}`,
|
|
77
|
+
`quality: ${quality}`,
|
|
78
|
+
`scalingFactor: ${scalingFactor}`,
|
|
79
|
+
"说明: MCP 工具本身不适合持续渲染 MJPEG 流,建议把这个地址交给浏览器或支持 MJPEG 的播放器打开。",
|
|
80
|
+
].join("\n"));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
@@ -0,0 +1,391 @@
|
|
|
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.registerRuntimeTools = registerRuntimeTools;
|
|
37
|
+
const z = __importStar(require("zod/v4"));
|
|
38
|
+
const fsExtra = __importStar(require("fs-extra"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const packager_1 = require("../packager");
|
|
42
|
+
const project_1 = require("../project");
|
|
43
|
+
const project_2 = require("./project");
|
|
44
|
+
const device_config_1 = require("./device-config");
|
|
45
|
+
const device_log_1 = require("./device-log");
|
|
46
|
+
const tool_utils_1 = require("./tool-utils");
|
|
47
|
+
const control_tools_1 = require("./control-tools");
|
|
48
|
+
const hid_tools_1 = require("./hid-tools");
|
|
49
|
+
const ime_tools_1 = require("./ime-tools");
|
|
50
|
+
const mirror_tools_1 = require("./mirror-tools");
|
|
51
|
+
/**
|
|
52
|
+
* 注册设备与项目执行工具
|
|
53
|
+
* @param server MCP 服务实例
|
|
54
|
+
* @returns 无返回值
|
|
55
|
+
* @example
|
|
56
|
+
* registerRuntimeTools(server)
|
|
57
|
+
*/
|
|
58
|
+
function registerRuntimeTools(server) {
|
|
59
|
+
/**
|
|
60
|
+
* MCP 进程内当前工作目录(仅内存态,不落盘)
|
|
61
|
+
* 说明:用于同一轮 MCP 进程内复用目录,进程重启后需由 AI 再次调用 set_workspace 设置。
|
|
62
|
+
*/
|
|
63
|
+
let currentWorkspacePath;
|
|
64
|
+
/**
|
|
65
|
+
* 解析本次调用使用的工作目录
|
|
66
|
+
* 优先级:workspacePath 参数 > set_workspace 记忆值
|
|
67
|
+
* @param workspacePath 可选工作目录
|
|
68
|
+
* @returns 返回生效的绝对路径
|
|
69
|
+
* @example
|
|
70
|
+
* const workspace = resolveWorkspacePath("/Users/demo/project")
|
|
71
|
+
*/
|
|
72
|
+
function resolveWorkspacePath(workspacePath) {
|
|
73
|
+
if (workspacePath && workspacePath.trim()) {
|
|
74
|
+
currentWorkspacePath = path.resolve(workspacePath.trim());
|
|
75
|
+
return currentWorkspacePath;
|
|
76
|
+
}
|
|
77
|
+
if (currentWorkspacePath) {
|
|
78
|
+
return currentWorkspacePath;
|
|
79
|
+
}
|
|
80
|
+
throw new Error("未设置工作目录:请先调用 set_workspace,或在本次工具调用时传 workspacePath。");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 校验并返回有效工作目录
|
|
84
|
+
* @param workspacePath 可选工作目录
|
|
85
|
+
* @returns 返回校验通过的绝对路径
|
|
86
|
+
* @example
|
|
87
|
+
* const workspace = await ensureWorkspacePath("/Users/demo/project")
|
|
88
|
+
*/
|
|
89
|
+
async function ensureWorkspacePath(workspacePath) {
|
|
90
|
+
const workspace = resolveWorkspacePath(workspacePath);
|
|
91
|
+
await (0, project_2.ensureValidKuaiJSProject)(workspace);
|
|
92
|
+
return workspace;
|
|
93
|
+
}
|
|
94
|
+
server.registerTool("set_workspace", {
|
|
95
|
+
title: "Set Workspace",
|
|
96
|
+
description: "设置当前 MCP 进程内的默认工作目录。后续 build/run/package 优先使用该目录。",
|
|
97
|
+
inputSchema: {
|
|
98
|
+
workspacePath: z
|
|
99
|
+
.string()
|
|
100
|
+
.min(1)
|
|
101
|
+
.describe("快点JS 项目目录绝对路径(需包含 package.json 与 scripts)"),
|
|
102
|
+
},
|
|
103
|
+
}, async ({ workspacePath }) => {
|
|
104
|
+
const workspace = await ensureWorkspacePath(workspacePath);
|
|
105
|
+
return (0, tool_utils_1.createTextToolResult)(`默认工作目录已设置为: ${workspace}`);
|
|
106
|
+
});
|
|
107
|
+
server.registerTool("get_workspace", {
|
|
108
|
+
title: "Get Workspace",
|
|
109
|
+
description: "查看当前 MCP 进程内记忆的工作目录。",
|
|
110
|
+
inputSchema: {},
|
|
111
|
+
}, async () => {
|
|
112
|
+
if (!currentWorkspacePath) {
|
|
113
|
+
return (0, tool_utils_1.createTextToolResult)("当前未设置工作目录,请先调用 set_workspace。");
|
|
114
|
+
}
|
|
115
|
+
return (0, tool_utils_1.createTextToolResult)(`当前工作目录: ${currentWorkspacePath}`);
|
|
116
|
+
});
|
|
117
|
+
server.registerTool("set_device", {
|
|
118
|
+
title: "Set Device",
|
|
119
|
+
description: "设置当前唯一设备连接信息。设置后其余设备工具均复用该设备。",
|
|
120
|
+
inputSchema: {
|
|
121
|
+
ip: z.string().min(1).describe("设备 IP 地址,例如 192.168.1.100"),
|
|
122
|
+
port: z
|
|
123
|
+
.number()
|
|
124
|
+
.int()
|
|
125
|
+
.min(1)
|
|
126
|
+
.max(65535)
|
|
127
|
+
.optional()
|
|
128
|
+
.describe("设备端口,默认 9800"),
|
|
129
|
+
},
|
|
130
|
+
}, async ({ ip, port }) => {
|
|
131
|
+
const config = await (0, device_config_1.setDeviceConfig)(ip, port);
|
|
132
|
+
const reusedSubscription = (0, device_log_1.ensureDeviceLogSubscription)(config.ip, config.port);
|
|
133
|
+
return (0, tool_utils_1.createTextToolResult)([
|
|
134
|
+
`默认设备已设置为 ${config.ip}:${config.port}`,
|
|
135
|
+
reusedSubscription
|
|
136
|
+
? "SSE 日志后台订阅已复用现有连接"
|
|
137
|
+
: `SSE 日志后台订阅已启动,内存缓存上限 ${device_log_1.DEVICE_LOG_MEMORY_LIMIT} 条`,
|
|
138
|
+
].join("\n"));
|
|
139
|
+
});
|
|
140
|
+
server.registerTool("get_device", {
|
|
141
|
+
title: "Get Device",
|
|
142
|
+
description: "查看当前默认设备配置。",
|
|
143
|
+
inputSchema: {},
|
|
144
|
+
}, async () => {
|
|
145
|
+
const config = await (0, device_config_1.readDeviceConfig)();
|
|
146
|
+
if (!config) {
|
|
147
|
+
return (0, tool_utils_1.createTextToolResult)("当前未设置默认设备,请先调用 set_device。");
|
|
148
|
+
}
|
|
149
|
+
return (0, tool_utils_1.createTextToolResult)(`当前默认设备: ${config.ip}:${config.port}`);
|
|
150
|
+
});
|
|
151
|
+
server.registerTool("get_active_app_info", {
|
|
152
|
+
title: "Get Active App Info",
|
|
153
|
+
description: "获取当前激活应用信息。",
|
|
154
|
+
inputSchema: {},
|
|
155
|
+
}, async () => {
|
|
156
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
157
|
+
const payload = await (0, tool_utils_1.requestRuntimeJsonApi)(target, "activeAppInfo");
|
|
158
|
+
return (0, tool_utils_1.createTextToolResult)(`当前激活应用信息:\n${(0, tool_utils_1.formatRuntimeJsonText)((0, tool_utils_1.unwrapRuntimeData)(payload, "activeAppInfo"))}`);
|
|
159
|
+
});
|
|
160
|
+
server.registerTool("take_me_to_front", {
|
|
161
|
+
title: "Take Me To Front",
|
|
162
|
+
description: "将当前应用切到前台。",
|
|
163
|
+
inputSchema: {},
|
|
164
|
+
}, async () => {
|
|
165
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
166
|
+
const payload = await (0, tool_utils_1.requestRuntimeJsonApi)(target, "takeMeToFront");
|
|
167
|
+
return (0, tool_utils_1.createTextToolResult)(payload.success === true
|
|
168
|
+
? `切到前台成功: ${target.label}`
|
|
169
|
+
: `切到前台失败: ${target.label}`, payload.success !== true);
|
|
170
|
+
});
|
|
171
|
+
server.registerTool("get_all_config", {
|
|
172
|
+
title: "Get All Config",
|
|
173
|
+
description: "获取当前设备全部配置参数。",
|
|
174
|
+
inputSchema: {},
|
|
175
|
+
}, async () => {
|
|
176
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
177
|
+
const payload = await (0, tool_utils_1.requestRuntimeJsonApi)(target, "getAllConfig");
|
|
178
|
+
return (0, tool_utils_1.createTextToolResult)(`当前配置:\n${(0, tool_utils_1.formatRuntimeJsonText)((0, tool_utils_1.unwrapRuntimeData)(payload, "getAllConfig"))}`);
|
|
179
|
+
});
|
|
180
|
+
server.registerTool("set_all_config", {
|
|
181
|
+
title: "Set All Config",
|
|
182
|
+
description: "批量设置当前设备全部配置参数。",
|
|
183
|
+
inputSchema: {
|
|
184
|
+
config: z
|
|
185
|
+
.record(z.string(), z.unknown())
|
|
186
|
+
.describe("完整配置对象,会直接提交到 setAllConfig"),
|
|
187
|
+
},
|
|
188
|
+
}, async ({ config }) => {
|
|
189
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
190
|
+
const payload = await (0, tool_utils_1.requestRuntimeJsonApi)(target, "setAllConfig", "POST", config);
|
|
191
|
+
return (0, tool_utils_1.createTextToolResult)(payload.success === true
|
|
192
|
+
? `批量设置配置成功: ${target.label}`
|
|
193
|
+
: `批量设置配置失败: ${target.label}`, payload.success !== true);
|
|
194
|
+
});
|
|
195
|
+
server.registerTool("set_config", {
|
|
196
|
+
title: "Set Config",
|
|
197
|
+
description: "设置单个配置参数。",
|
|
198
|
+
inputSchema: {
|
|
199
|
+
key: z.string().min(1).describe("配置键名"),
|
|
200
|
+
value: z.unknown().describe("配置值,支持任意 JSON 兼容类型"),
|
|
201
|
+
},
|
|
202
|
+
}, async ({ key, value }) => {
|
|
203
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
204
|
+
const payload = await (0, tool_utils_1.requestRuntimeJsonApi)(target, "setConfig", "POST", {
|
|
205
|
+
key,
|
|
206
|
+
value,
|
|
207
|
+
});
|
|
208
|
+
return (0, tool_utils_1.createTextToolResult)(payload.success === true
|
|
209
|
+
? `配置设置成功: ${key}`
|
|
210
|
+
: `配置设置失败: ${key}`, payload.success !== true);
|
|
211
|
+
});
|
|
212
|
+
server.registerTool("run_script", {
|
|
213
|
+
title: "Run Script",
|
|
214
|
+
description: "在当前设备上执行一段 JavaScript 并返回结果。",
|
|
215
|
+
inputSchema: {
|
|
216
|
+
script: z.string().min(1).describe("要执行的 JavaScript 代码"),
|
|
217
|
+
},
|
|
218
|
+
}, async ({ script }) => {
|
|
219
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
220
|
+
const payload = await (0, tool_utils_1.requestRuntimeJsonApi)(target, "runScript", "POST", {
|
|
221
|
+
script,
|
|
222
|
+
});
|
|
223
|
+
const success = payload.success === true;
|
|
224
|
+
return (0, tool_utils_1.createTextToolResult)(success
|
|
225
|
+
? `脚本执行成功:\n${(0, tool_utils_1.formatRuntimeJsonText)(payload)}`
|
|
226
|
+
: `脚本执行失败:\n${(0, tool_utils_1.formatRuntimeJsonText)(payload)}`, !success);
|
|
227
|
+
});
|
|
228
|
+
server.registerTool("take_screenshot", {
|
|
229
|
+
title: "Take Screenshot",
|
|
230
|
+
description: "获取当前默认设备截图。可返回 base64,或落地到文件后返回文件路径(默认写入系统临时目录)。",
|
|
231
|
+
inputSchema: {
|
|
232
|
+
format: z
|
|
233
|
+
.enum(["file", "base64"])
|
|
234
|
+
.optional()
|
|
235
|
+
.default("file")
|
|
236
|
+
.describe("截图返回格式:file=文件路径,base64=图片 base64"),
|
|
237
|
+
outputPath: z
|
|
238
|
+
.string()
|
|
239
|
+
.min(1)
|
|
240
|
+
.optional()
|
|
241
|
+
.describe("当 format=file 时可指定输出路径,不传则写入系统临时目录"),
|
|
242
|
+
},
|
|
243
|
+
}, async ({ format, outputPath }) => {
|
|
244
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
245
|
+
const requestOptions = (0, tool_utils_1.createRuntimeHttpRequestOptions)(target);
|
|
246
|
+
if (format === "base64") {
|
|
247
|
+
const base64 = await (0, project_1.getScreenshotBase64OnDevice)(requestOptions);
|
|
248
|
+
return (0, tool_utils_1.createTextToolResult)(`截图成功: ${target.label}\nformat: base64\n${base64}`);
|
|
249
|
+
}
|
|
250
|
+
const image = await (0, project_1.getScreenshotOnDevice)(requestOptions);
|
|
251
|
+
const targetPath = outputPath && outputPath.trim()
|
|
252
|
+
? path.resolve(outputPath.trim())
|
|
253
|
+
: path.join(os.tmpdir(), `ms-mcp-screenshot-${Date.now()}-${Math.random()
|
|
254
|
+
.toString(36)
|
|
255
|
+
.slice(2, 8)}.jpg`);
|
|
256
|
+
await fsExtra.ensureDir(path.dirname(targetPath));
|
|
257
|
+
await fsExtra.writeFile(targetPath, image);
|
|
258
|
+
return (0, tool_utils_1.createTextToolResult)(`截图成功: ${target.label}\nformat: file\npath: ${targetPath}\nsize: ${image.length} bytes`);
|
|
259
|
+
});
|
|
260
|
+
server.registerTool("get_node_source", {
|
|
261
|
+
title: "Get Node Source",
|
|
262
|
+
description: "获取当前默认设备页面节点 XML(/api/source),写入文件后返回路径,避免长文本响应被截断。",
|
|
263
|
+
inputSchema: {
|
|
264
|
+
maxDepth: z
|
|
265
|
+
.number()
|
|
266
|
+
.int()
|
|
267
|
+
.min(1)
|
|
268
|
+
.max(200)
|
|
269
|
+
.optional()
|
|
270
|
+
.default(50)
|
|
271
|
+
.describe("节点树最大深度,默认 50"),
|
|
272
|
+
timeout: z
|
|
273
|
+
.number()
|
|
274
|
+
.int()
|
|
275
|
+
.min(1)
|
|
276
|
+
.max(600)
|
|
277
|
+
.optional()
|
|
278
|
+
.default(120)
|
|
279
|
+
.describe("设备端节点抓取超时秒数,默认 120"),
|
|
280
|
+
outputPath: z
|
|
281
|
+
.string()
|
|
282
|
+
.min(1)
|
|
283
|
+
.optional()
|
|
284
|
+
.describe("可选输出文件路径,不传则写入系统临时目录"),
|
|
285
|
+
},
|
|
286
|
+
}, async ({ maxDepth, timeout, outputPath }) => {
|
|
287
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
288
|
+
const requestOptions = (0, tool_utils_1.createRuntimeHttpRequestOptions)(target);
|
|
289
|
+
const source = await (0, project_1.getSourceOnDevice)(requestOptions, maxDepth, timeout);
|
|
290
|
+
const targetPath = outputPath && outputPath.trim()
|
|
291
|
+
? path.resolve(outputPath.trim())
|
|
292
|
+
: path.join(os.tmpdir(), `ms-mcp-node-source-${Date.now()}-${Math.random()
|
|
293
|
+
.toString(36)
|
|
294
|
+
.slice(2, 8)}.xml`);
|
|
295
|
+
await fsExtra.ensureDir(path.dirname(targetPath));
|
|
296
|
+
await fsExtra.writeFile(targetPath, source, "utf8");
|
|
297
|
+
return (0, tool_utils_1.createTextToolResult)(`节点获取成功: ${target.label}\nmaxDepth: ${maxDepth}\ntimeout: ${timeout}\npath: ${targetPath}\nsize: ${Buffer.byteLength(source, "utf8")} bytes`);
|
|
298
|
+
});
|
|
299
|
+
server.registerTool("get_logs", {
|
|
300
|
+
title: "Get Device Logs",
|
|
301
|
+
description: "获取当前默认设备的日志缓存快照。调用 set_device 后会自动建立 SSE 后台订阅,此工具只返回当前已缓存的日志内容。",
|
|
302
|
+
inputSchema: {
|
|
303
|
+
limit: z
|
|
304
|
+
.number()
|
|
305
|
+
.int()
|
|
306
|
+
.min(1)
|
|
307
|
+
.max(5000)
|
|
308
|
+
.optional()
|
|
309
|
+
.default(200)
|
|
310
|
+
.describe("返回最近日志条数,默认 200,最大 5000"),
|
|
311
|
+
runtimeStatusLimit: z
|
|
312
|
+
.number()
|
|
313
|
+
.int()
|
|
314
|
+
.min(0)
|
|
315
|
+
.max(200)
|
|
316
|
+
.optional()
|
|
317
|
+
.default(20)
|
|
318
|
+
.describe("返回最近 runtime_status 条数,默认 20"),
|
|
319
|
+
},
|
|
320
|
+
}, async ({ limit, runtimeStatusLimit }) => {
|
|
321
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
322
|
+
(0, device_log_1.ensureDeviceLogSubscription)(target.ip, Number.parseInt(target.port, 10));
|
|
323
|
+
return (0, tool_utils_1.createTextToolResult)((0, device_log_1.buildDeviceLogSnapshotText)(limit, runtimeStatusLimit));
|
|
324
|
+
});
|
|
325
|
+
server.registerTool("package_project", {
|
|
326
|
+
title: "Package Project",
|
|
327
|
+
description: "执行生产构建并加密,返回 enc.msbundle 路径。",
|
|
328
|
+
inputSchema: {
|
|
329
|
+
workspacePath: z
|
|
330
|
+
.string()
|
|
331
|
+
.min(1)
|
|
332
|
+
.optional()
|
|
333
|
+
.describe("可选工作目录;不传时使用 set_workspace 记忆值"),
|
|
334
|
+
},
|
|
335
|
+
}, async ({ workspacePath }) => {
|
|
336
|
+
const workspace = await ensureWorkspacePath(workspacePath);
|
|
337
|
+
const encryptedPath = await (0, packager_1.packageProject)(workspace);
|
|
338
|
+
return (0, tool_utils_1.createTextToolResult)(`打包完成: ${encryptedPath}`);
|
|
339
|
+
});
|
|
340
|
+
server.registerTool("run_project", {
|
|
341
|
+
title: "Run Project",
|
|
342
|
+
description: "构建并同步到当前默认设备后运行项目,仅使用 HTTP 设备连接。",
|
|
343
|
+
inputSchema: {
|
|
344
|
+
workspacePath: z
|
|
345
|
+
.string()
|
|
346
|
+
.min(1)
|
|
347
|
+
.optional()
|
|
348
|
+
.describe("可选工作目录;不传时使用 set_workspace 记忆值"),
|
|
349
|
+
},
|
|
350
|
+
}, async ({ workspacePath }) => {
|
|
351
|
+
const workspace = await ensureWorkspacePath(workspacePath);
|
|
352
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
353
|
+
await (0, project_1.runOnDevice)({
|
|
354
|
+
...(0, tool_utils_1.createRuntimeHttpRequestOptions)(target),
|
|
355
|
+
workspacePath: workspace,
|
|
356
|
+
});
|
|
357
|
+
return (0, tool_utils_1.createTextToolResult)(`运行请求已发送到 ${target.label}`);
|
|
358
|
+
});
|
|
359
|
+
server.registerTool("run_ui_project", {
|
|
360
|
+
title: "Run UI Project",
|
|
361
|
+
description: "构建并同步到当前默认设备后预览 UI,仅使用 HTTP 设备连接。",
|
|
362
|
+
inputSchema: {
|
|
363
|
+
workspacePath: z
|
|
364
|
+
.string()
|
|
365
|
+
.min(1)
|
|
366
|
+
.optional()
|
|
367
|
+
.describe("可选工作目录;不传时使用 set_workspace 记忆值"),
|
|
368
|
+
},
|
|
369
|
+
}, async ({ workspacePath }) => {
|
|
370
|
+
const workspace = await ensureWorkspacePath(workspacePath);
|
|
371
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
372
|
+
await (0, project_1.runUIOnDevice)({
|
|
373
|
+
...(0, tool_utils_1.createRuntimeHttpRequestOptions)(target),
|
|
374
|
+
workspacePath: workspace,
|
|
375
|
+
});
|
|
376
|
+
return (0, tool_utils_1.createTextToolResult)(`UI 预览请求已发送到 ${target.label}`);
|
|
377
|
+
});
|
|
378
|
+
server.registerTool("stop_project", {
|
|
379
|
+
title: "Stop Project",
|
|
380
|
+
description: "停止当前默认设备上的项目,仅使用 HTTP 设备连接。",
|
|
381
|
+
inputSchema: {},
|
|
382
|
+
}, async () => {
|
|
383
|
+
const target = await (0, tool_utils_1.resolveRuntimeHttpTarget)();
|
|
384
|
+
await (0, project_1.stopOnDevice)((0, tool_utils_1.createRuntimeHttpRequestOptions)(target));
|
|
385
|
+
return (0, tool_utils_1.createTextToolResult)(`停止请求已发送到 ${target.label}`);
|
|
386
|
+
});
|
|
387
|
+
(0, control_tools_1.registerControlTools)(server);
|
|
388
|
+
(0, hid_tools_1.registerHidTools)(server);
|
|
389
|
+
(0, ime_tools_1.registerImeTools)(server);
|
|
390
|
+
(0, mirror_tools_1.registerMirrorTools)(server);
|
|
391
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { ApiDocsLanguage } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* MCP 文本响应内容结构
|
|
4
|
+
*/
|
|
5
|
+
export type TextContent = {
|
|
6
|
+
type: "text";
|
|
7
|
+
text: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* 通用文本工具返回值结构
|
|
11
|
+
*/
|
|
12
|
+
export type TextToolResult = {
|
|
13
|
+
content: TextContent[];
|
|
14
|
+
isError?: boolean;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* HTTP 设备目标信息
|
|
18
|
+
*/
|
|
19
|
+
export type RuntimeHttpTarget = {
|
|
20
|
+
ip: string;
|
|
21
|
+
port: string;
|
|
22
|
+
label: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* 查询参数允许的基础值类型
|
|
26
|
+
*/
|
|
27
|
+
export type RuntimeQueryValue = string | number | boolean | null | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* 创建标准 MCP 文本工具返回值
|
|
30
|
+
* @param text 返回文本
|
|
31
|
+
* @param isError 是否标记为错误响应
|
|
32
|
+
* @returns 返回包含单条文本内容的 MCP Tool 结果
|
|
33
|
+
* @example
|
|
34
|
+
* createTextToolResult("done")
|
|
35
|
+
*/
|
|
36
|
+
export declare function createTextToolResult(text: string, isError?: boolean): TextToolResult;
|
|
37
|
+
/**
|
|
38
|
+
* 构建运行时 HTTP 请求参数
|
|
39
|
+
* @param target 已解析的设备目标
|
|
40
|
+
* @returns 返回项目内部设备请求参数
|
|
41
|
+
* @example
|
|
42
|
+
* const options = createRuntimeHttpRequestOptions(target)
|
|
43
|
+
*/
|
|
44
|
+
export declare function createRuntimeHttpRequestOptions(target: RuntimeHttpTarget): {
|
|
45
|
+
ip: string;
|
|
46
|
+
port: string;
|
|
47
|
+
transport: "http";
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* 解析当前默认 HTTP 设备
|
|
51
|
+
* @returns 返回标准化后的 HTTP 请求目标
|
|
52
|
+
* @example
|
|
53
|
+
* const target = await resolveRuntimeHttpTarget()
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveRuntimeHttpTarget(): Promise<RuntimeHttpTarget>;
|
|
56
|
+
/**
|
|
57
|
+
* 请求设备通用 JSON API
|
|
58
|
+
* @param target 当前默认设备目标
|
|
59
|
+
* @param endpoint API 路径(不含前导 `/api/`)
|
|
60
|
+
* @param method 请求方法
|
|
61
|
+
* @param body 可选 JSON 请求体
|
|
62
|
+
* @returns 返回解析后的 JSON 数据
|
|
63
|
+
* @example
|
|
64
|
+
* const payload = await requestRuntimeJsonApi(target, "activeAppInfo")
|
|
65
|
+
*/
|
|
66
|
+
export declare function requestRuntimeJsonApi(target: RuntimeHttpTarget, endpoint: string, method?: "GET" | "POST", body?: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
67
|
+
/**
|
|
68
|
+
* 请求任意相对路径 JSON API,并可附带 query 参数
|
|
69
|
+
* @param target 当前默认设备目标
|
|
70
|
+
* @param routePath 相对路径,例如 `/api/control/click`
|
|
71
|
+
* @param query 可选查询参数
|
|
72
|
+
* @returns 返回解析后的 JSON 数据
|
|
73
|
+
* @example
|
|
74
|
+
* const payload = await requestRuntimePathJson(target, "/api/ime/getText")
|
|
75
|
+
*/
|
|
76
|
+
export declare function requestRuntimePathJson(target: RuntimeHttpTarget, routePath: string, query?: Record<string, RuntimeQueryValue>): Promise<Record<string, unknown>>;
|
|
77
|
+
/**
|
|
78
|
+
* 从通用 JSON 响应中读取 `data` 字段
|
|
79
|
+
* @param payload API 返回 JSON
|
|
80
|
+
* @param endpoint API 路径名,用于错误提示
|
|
81
|
+
* @returns 返回 `data` 字段内容
|
|
82
|
+
* @example
|
|
83
|
+
* const data = unwrapRuntimeData(payload, "getAllConfig")
|
|
84
|
+
*/
|
|
85
|
+
export declare function unwrapRuntimeData(payload: Record<string, unknown>, endpoint: string): unknown;
|
|
86
|
+
/**
|
|
87
|
+
* 将任意 JSON 兼容值格式化为文本
|
|
88
|
+
* @param value 任意待展示值
|
|
89
|
+
* @returns 返回便于 MCP 文本输出的内容
|
|
90
|
+
* @example
|
|
91
|
+
* formatRuntimeJsonText({ success: true })
|
|
92
|
+
*/
|
|
93
|
+
export declare function formatRuntimeJsonText(value: unknown): string;
|
|
94
|
+
/**
|
|
95
|
+
* 格式化单条 API 文档摘要
|
|
96
|
+
* @param language 文档语言
|
|
97
|
+
* @param title 文档标题
|
|
98
|
+
* @param slug 文档 slug
|
|
99
|
+
* @param index 列表序号
|
|
100
|
+
* @returns 返回用于列表展示的文本块
|
|
101
|
+
* @example
|
|
102
|
+
* formatApiDocSummary("js", "Tap", "tap", 0)
|
|
103
|
+
*/
|
|
104
|
+
export declare function formatApiDocSummary(language: ApiDocsLanguage, title: string, slug: string, index: number): string;
|