@creatoria/miniapp-mcp 0.1.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.
- package/README.md +469 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +144 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/defaults.d.ts +73 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +118 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/loader.d.ts +50 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +189 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/core/element-ref.d.ts +44 -0
- package/dist/core/element-ref.d.ts.map +1 -0
- package/dist/core/element-ref.js +213 -0
- package/dist/core/element-ref.js.map +1 -0
- package/dist/core/logger.d.ts +55 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +378 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/output.d.ts +21 -0
- package/dist/core/output.d.ts.map +1 -0
- package/dist/core/output.js +56 -0
- package/dist/core/output.js.map +1 -0
- package/dist/core/report-generator.d.ts +24 -0
- package/dist/core/report-generator.d.ts.map +1 -0
- package/dist/core/report-generator.js +212 -0
- package/dist/core/report-generator.js.map +1 -0
- package/dist/core/session.d.ts +83 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +306 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/timeout.d.ts +49 -0
- package/dist/core/timeout.d.ts.map +1 -0
- package/dist/core/timeout.js +67 -0
- package/dist/core/timeout.js.map +1 -0
- package/dist/core/tool-logger.d.ts +83 -0
- package/dist/core/tool-logger.d.ts.map +1 -0
- package/dist/core/tool-logger.js +453 -0
- package/dist/core/tool-logger.js.map +1 -0
- package/dist/core/validation.d.ts +39 -0
- package/dist/core/validation.d.ts.map +1 -0
- package/dist/core/validation.js +93 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +85 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/assert.d.ts +108 -0
- package/dist/tools/assert.d.ts.map +1 -0
- package/dist/tools/assert.js +291 -0
- package/dist/tools/assert.js.map +1 -0
- package/dist/tools/automator.d.ts +45 -0
- package/dist/tools/automator.d.ts.map +1 -0
- package/dist/tools/automator.js +186 -0
- package/dist/tools/automator.js.map +1 -0
- package/dist/tools/element.d.ts +253 -0
- package/dist/tools/element.d.ts.map +1 -0
- package/dist/tools/element.js +615 -0
- package/dist/tools/element.js.map +1 -0
- package/dist/tools/index.d.ts +97 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +1565 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/miniprogram.d.ts +79 -0
- package/dist/tools/miniprogram.d.ts.map +1 -0
- package/dist/tools/miniprogram.js +245 -0
- package/dist/tools/miniprogram.js.map +1 -0
- package/dist/tools/network.d.ts +65 -0
- package/dist/tools/network.d.ts.map +1 -0
- package/dist/tools/network.js +205 -0
- package/dist/tools/network.js.map +1 -0
- package/dist/tools/page.d.ts +108 -0
- package/dist/tools/page.d.ts.map +1 -0
- package/dist/tools/page.js +307 -0
- package/dist/tools/page.js.map +1 -0
- package/dist/tools/record.d.ts +86 -0
- package/dist/tools/record.d.ts.map +1 -0
- package/dist/tools/record.js +316 -0
- package/dist/tools/record.js.map +1 -0
- package/dist/tools/snapshot.d.ts +82 -0
- package/dist/tools/snapshot.d.ts.map +1 -0
- package/dist/tools/snapshot.js +258 -0
- package/dist/tools/snapshot.js.map +1 -0
- package/dist/types.d.ts +240 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/docs/SIMPLE_USAGE.md +210 -0
- package/docs/api/README.md +244 -0
- package/docs/api/assert.md +1015 -0
- package/docs/api/automator.md +345 -0
- package/docs/api/element.md +1454 -0
- package/docs/api/miniprogram.md +558 -0
- package/docs/api/network.md +883 -0
- package/docs/api/page.md +909 -0
- package/docs/api/record.md +963 -0
- package/docs/api/snapshot.md +792 -0
- package/docs/architecture.E-Docs.md +1359 -0
- package/docs/architecture.F1.md +720 -0
- package/docs/architecture.F2.md +871 -0
- package/docs/architecture.F3.md +905 -0
- package/docs/architecture.md +90 -0
- package/docs/charter.A1.align.yaml +170 -0
- package/docs/charter.A2.align.yaml +199 -0
- package/docs/charter.A3.align.yaml +242 -0
- package/docs/charter.A4.align.yaml +227 -0
- package/docs/charter.B1.align.yaml +179 -0
- package/docs/charter.B2.align.yaml +200 -0
- package/docs/charter.B3.align.yaml +200 -0
- package/docs/charter.B4.align.yaml +188 -0
- package/docs/charter.C1.align.yaml +190 -0
- package/docs/charter.C2.align.yaml +202 -0
- package/docs/charter.C3.align.yaml +211 -0
- package/docs/charter.C4.align.yaml +263 -0
- package/docs/charter.C5.align.yaml +220 -0
- package/docs/charter.D1.align.yaml +190 -0
- package/docs/charter.D2.align.yaml +234 -0
- package/docs/charter.D3.align.yaml +206 -0
- package/docs/charter.E-Docs.align.yaml +294 -0
- package/docs/charter.F1.align.yaml +193 -0
- package/docs/charter.F2.align.yaml +248 -0
- package/docs/charter.F3.align.yaml +287 -0
- package/docs/charter.G.align.yaml +174 -0
- package/docs/charter.align.yaml +111 -0
- package/docs/examples/session-report-usage.md +449 -0
- package/docs/maintenance.md +682 -0
- package/docs/playwright-mcp/350/260/203/347/240/224.md +53 -0
- package/docs/setup-guide.md +775 -0
- package/docs/tasks.A1.atomize.md +296 -0
- package/docs/tasks.A2.atomize.md +408 -0
- package/docs/tasks.A3.atomize.md +564 -0
- package/docs/tasks.A4.atomize.md +496 -0
- package/docs/tasks.B1.atomize.md +352 -0
- package/docs/tasks.B2.atomize.md +561 -0
- package/docs/tasks.B3.atomize.md +508 -0
- package/docs/tasks.B4.atomize.md +504 -0
- package/docs/tasks.C1.atomize.md +540 -0
- package/docs/tasks.C2.atomize.md +665 -0
- package/docs/tasks.C3.atomize.md +745 -0
- package/docs/tasks.C4.atomize.md +908 -0
- package/docs/tasks.C5.atomize.md +755 -0
- package/docs/tasks.D1.atomize.md +547 -0
- package/docs/tasks.D2.atomize.md +619 -0
- package/docs/tasks.D3.atomize.md +790 -0
- package/docs/tasks.E-Docs.atomize.md +1204 -0
- package/docs/tasks.atomize.md +189 -0
- package/docs/troubleshooting.md +855 -0
- package/docs//345/256/214/346/225/264/345/256/236/347/216/260/346/226/271/346/241/210.md +155 -0
- package/docs//345/274/200/345/217/221/344/273/273/345/212/241/350/256/241/345/210/222.md +110 -0
- package/docs//345/276/256/344/277/241/345/260/217/347/250/213/345/272/217/350/207/252/345/212/250/345/214/226API/345/256/214/346/225/264/346/226/207/346/241/243.md +894 -0
- package/docs//345/276/256/344/277/241/345/260/217/347/250/213/345/272/217/350/207/252/345/212/250/345/214/226/345/256/214/346/225/264/346/223/215/344/275/234/346/211/213/345/206/214.md +1885 -0
- package/docs//346/216/245/345/217/243/346/226/271/346/241/210.md +565 -0
- package/docs//347/254/254/344/270/200/347/211/210/346/234/254/346/226/271/346/241/210.md +380 -0
- package/package.json +87 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
下面是一套从架构设计到可跑通代码的完整方案:将微信小程序官方自动化能力(miniprogram-automator)封装为 MCP(Model Context Protocol) 服务,参考 playwright-mcp 的“以工具集为中心”的方式,对外暴露“打开项目、页面操作、断言、截图、网络桩、录制/回放”等工具,让 AI+MCP 可以用自然语言驱动端到端自动化测试。
|
|
2
|
+
|
|
3
|
+
要点先看
|
|
4
|
+
• 小程序自动化的底座:微信开发者工具 + miniprogram-automator(JS版官方SDK,提供 MiniProgram / Page / Element 操作、reLaunch、$ 选择器、tap、callWxMethod 等) 
|
|
5
|
+
• MCP 形态:使用 TypeScript MCP SDK(@modelcontextprotocol/sdk)实现一组“工具”,与 playwright-mcp 思路一致(定义 tool 列表、JSON Schema 入参、幂等返回) 
|
|
6
|
+
• 运行前提:在微信开发者工具中开启 CLI/HTTP 调用,并用 cli 可执行程序打开自动化端口(Windows cli.bat / macOS cli) 
|
|
7
|
+
|
|
8
|
+
⸻
|
|
9
|
+
|
|
10
|
+
一、整体架构
|
|
11
|
+
|
|
12
|
+
[AI 客户端(Claude / Cursor / 自研Agent)]
|
|
13
|
+
│ (MCP 协议/stdio 或 SSE)
|
|
14
|
+
▼
|
|
15
|
+
[miniprogram-mcp 服务(Node / TS)]
|
|
16
|
+
│ ├── 工具:launch、goto、tap、fill、assert、screenshot...
|
|
17
|
+
│ └── 状态:每个会话的 IDE 进程、MiniProgram / Page 句柄
|
|
18
|
+
▼
|
|
19
|
+
[微信开发者工具(开启 CLI/HTTP 端口)] ←→ [小程序项目(本地)]
|
|
20
|
+
│
|
|
21
|
+
└── 使用 miniprogram-automator 与 IDE 自动化接口通讯
|
|
22
|
+
|
|
23
|
+
为什么选这个组合?
|
|
24
|
+
• miniprogram-automator 是官方 JS 自动化 SDK,抽象了 MiniProgram / Page / Element 能力(选择器/事件/属性/数据/注入代码/调用 wx.* 接口);它通过 IDE 的自动化接口工作(要求 IDE 开启服务端口) 。
|
|
25
|
+
• MCP 让这些能力以 标准化的工具 形式暴露,便于被各种 LLM/Agent 主机消费;playwright-mcp 已证明这种“把浏览器自动化封成工具”的设计成熟可行,我们沿用其“工具即原子动作”的模式 。
|
|
26
|
+
• TypeScript MCP SDK 提供服务端脚手架、工具注册与校验、传输层(stdio/SSE),生态资料充足 。
|
|
27
|
+
|
|
28
|
+
⸻
|
|
29
|
+
|
|
30
|
+
二、工具(tools)设计
|
|
31
|
+
|
|
32
|
+
对齐 playwright-mcp 的“细粒度工具 + 结构化入参 + 结构化输出”哲学,避免 LLM 需要长上下文记忆状态。 
|
|
33
|
+
|
|
34
|
+
会话与上下文
|
|
35
|
+
• 每个 MCP 连接(host→server)对应一个 Session,服务端为之维护:
|
|
36
|
+
• ide: 开发者工具进程/端口信息
|
|
37
|
+
• miniProgram: automator.launch/connect() 的返回句柄
|
|
38
|
+
• pages: 当前页面栈(miniProgram.currentPage() 等)
|
|
39
|
+
• 工具入参均显式传入关键参数(如 pagePath、selector),避免状态漂移。
|
|
40
|
+
|
|
41
|
+
工具清单(核心)
|
|
42
|
+
1. launch
|
|
43
|
+
• 输入:projectPath、cliPath?、autoPort?、headless?(仅控制 IDE 是否显示窗口)、udid?(真机调试用,留扩展)
|
|
44
|
+
• 效果:启动并连接 IDE 自动化端口,创建 miniProgram 句柄(automator.launch 或 connect)。
|
|
45
|
+
• 返回:sessionId、autoPort、idePid。
|
|
46
|
+
• 参考:automator.connect/launch、IDE 需开启 CLI/HTTP 调用 
|
|
47
|
+
2. goto
|
|
48
|
+
• 输入:pagePath(如 /pages/index/index)、query?(对象或 a=1&b=2)、relaunch?(默认 true)
|
|
49
|
+
• 实现:miniProgram.reLaunch() 或 miniProgram.navigateTo() 后 page.waitFor()。
|
|
50
|
+
• 返回:pagePath、dataSnapshot(可选)、nodesSnapshot(可选)。
|
|
51
|
+
3. query
|
|
52
|
+
• 输入:selector(CSS 风格 WXML 选择器,如 #loginBtn .primary)、all?
|
|
53
|
+
• 实现:page.$ / page.$$,读取基本属性(text、attr、rect、visible)。
|
|
54
|
+
• 返回:元素数组(handleId、text、attr、rect 等)。
|
|
55
|
+
• 参考:page.$ / element.attribute() / element.tap() 
|
|
56
|
+
4. tap / longpress / scroll / input
|
|
57
|
+
• 输入:selector 或 handleId、text?、delta? 等
|
|
58
|
+
• 实现:element.tap()、element.longpress()、page.scroll()、element.input(text)(若不支持,退回 evaluate 设置值 + 触发 input 事件)。
|
|
59
|
+
• 返回:ok、afterSnapshot?。
|
|
60
|
+
5. assert
|
|
61
|
+
• 输入:kind(textEquals / exists / visible / dataEquals / urlIncludes)、selector?、expected?、pageDataPath?
|
|
62
|
+
• 实现:读取元素/页面数据后断言,返回布尔与差异。
|
|
63
|
+
• 返回:pass、actual、message?。
|
|
64
|
+
6. screenshot
|
|
65
|
+
• 输入:path?(相对工作目录)、fullPage?
|
|
66
|
+
• 实现:page.screenshot() 或 miniProgram.screenshot()(若可用);否则走 IDE 截图接口(降级策略)。
|
|
67
|
+
• 返回:文件相对路径。
|
|
68
|
+
7. callWx / inject
|
|
69
|
+
• 输入:api(如 login)、args;或注入一段 JS 到 AppService 运行
|
|
70
|
+
• 实现:miniProgram.callWxMethod(api, args)、miniProgram.evaluate(fn)(名称以官方能力为准) 
|
|
71
|
+
• 用途:登录、设置存储、打桩等。
|
|
72
|
+
8. record_actions / replay_actions
|
|
73
|
+
• 输入:start/stop;或传入已录制 actions[](tap/scroll/input 带时间戳)
|
|
74
|
+
• 实现:IDE 提供的录制转自动化脚本能力(如有)或在 MCP 层记录工具调用序列并回放;社区实践显示可依据“用户行为 JSON”回放到 automator 里执行 。
|
|
75
|
+
9. network_mock(可选扩展)
|
|
76
|
+
• 方案:通过 inject 在 AppService 层重写 wx.request 以返回固定数据;或接入三方封装(如 mpx e2e 的 mock 能力) 。
|
|
77
|
+
|
|
78
|
+
⸻
|
|
79
|
+
|
|
80
|
+
三、参考实现(可直接落地)
|
|
81
|
+
|
|
82
|
+
下面是一个 最小可用 的 MCP 服务实现(TypeScript)。目录结构:
|
|
83
|
+
|
|
84
|
+
miniprogram-mcp/
|
|
85
|
+
├─ src/
|
|
86
|
+
│ ├─ server.ts
|
|
87
|
+
│ ├─ tools.ts
|
|
88
|
+
│ └─ state.ts
|
|
89
|
+
├─ package.json
|
|
90
|
+
├─ tsconfig.json
|
|
91
|
+
└─ README.md
|
|
92
|
+
|
|
93
|
+
package.json
|
|
94
|
+
|
|
95
|
+
{
|
|
96
|
+
"name": "miniprogram-mcp",
|
|
97
|
+
"version": "0.1.0",
|
|
98
|
+
"private": true,
|
|
99
|
+
"type": "module",
|
|
100
|
+
"bin": {
|
|
101
|
+
"miniprogram-mcp": "./dist/server.js"
|
|
102
|
+
},
|
|
103
|
+
"scripts": {
|
|
104
|
+
"build": "tsc",
|
|
105
|
+
"start": "node ./dist/server.js",
|
|
106
|
+
"dev": "ts-node src/server.ts"
|
|
107
|
+
},
|
|
108
|
+
"dependencies": {
|
|
109
|
+
"@modelcontextprotocol/sdk": "^1.3.0",
|
|
110
|
+
"miniprogram-automator": "^0.12.1",
|
|
111
|
+
"zod": "^3.23.8"
|
|
112
|
+
},
|
|
113
|
+
"devDependencies": {
|
|
114
|
+
"ts-node": "^10.9.2",
|
|
115
|
+
"typescript": "^5.5.4"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
MCP TS SDK 与 miniprogram-automator 版本可按需调整;miniprogram-automator最新稳定版在 0.12.1 左右(历史版本列表可见 UNPKG) 。
|
|
120
|
+
|
|
121
|
+
src/state.ts
|
|
122
|
+
|
|
123
|
+
import type { MiniProgram } from 'miniprogram-automator';
|
|
124
|
+
|
|
125
|
+
export type SessionState = {
|
|
126
|
+
miniProgram?: MiniProgram;
|
|
127
|
+
idePid?: number;
|
|
128
|
+
autoPort?: number;
|
|
129
|
+
projectPath?: string;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
class Store {
|
|
133
|
+
private map = new Map<string, SessionState>();
|
|
134
|
+
get(id: string) {
|
|
135
|
+
if (!this.map.has(id)) this.map.set(id, {});
|
|
136
|
+
return this.map.get(id)!;
|
|
137
|
+
}
|
|
138
|
+
set(id: string, s: SessionState) {
|
|
139
|
+
this.map.set(id, s);
|
|
140
|
+
}
|
|
141
|
+
delete(id: string) {
|
|
142
|
+
this.map.delete(id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export const sessions = new Store();
|
|
147
|
+
|
|
148
|
+
src/tools.ts
|
|
149
|
+
|
|
150
|
+
import { z } from 'zod';
|
|
151
|
+
import automator from 'miniprogram-automator';
|
|
152
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types';
|
|
153
|
+
import { sessions } from './state.js';
|
|
154
|
+
|
|
155
|
+
const LaunchInput = z.object({
|
|
156
|
+
projectPath: z.string(),
|
|
157
|
+
cliPath: z.string().optional(),
|
|
158
|
+
autoPort: z.number().int().optional(),
|
|
159
|
+
headless: z.boolean().optional()
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
export const launch: Tool = {
|
|
163
|
+
name: 'launch',
|
|
164
|
+
description: '启动并连接小程序自动化(需开启IDE CLI/HTTP)',
|
|
165
|
+
inputSchema: LaunchInput,
|
|
166
|
+
async handler({ input, meta }) {
|
|
167
|
+
const { projectPath, cliPath, autoPort } = LaunchInput.parse(input);
|
|
168
|
+
const sessionId = meta?.connectionId ?? 'default';
|
|
169
|
+
|
|
170
|
+
// prefer launch(): 自动拉起并连接 IDE;如仅连接已有端口则用 connect()
|
|
171
|
+
const miniProgram = await automator.launch({
|
|
172
|
+
projectPath,
|
|
173
|
+
cliPath,
|
|
174
|
+
// autoPort 可不传由 SDK 分配;传入时需保证与 IDE 一致
|
|
175
|
+
// idePath/headless 等参数按需补充
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const state = sessions.get(sessionId);
|
|
179
|
+
state.miniProgram = miniProgram;
|
|
180
|
+
state.projectPath = projectPath;
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
sessionId,
|
|
184
|
+
projectPath
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const GotoInput = z.object({
|
|
190
|
+
pagePath: z.string(),
|
|
191
|
+
query: z.union([z.string(), z.record(z.string())]).optional(),
|
|
192
|
+
relaunch: z.boolean().optional()
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
export const goto: Tool = {
|
|
196
|
+
name: 'goto',
|
|
197
|
+
description: '跳转到指定页面(默认使用 reLaunch)',
|
|
198
|
+
inputSchema: GotoInput,
|
|
199
|
+
async handler({ input, meta }) {
|
|
200
|
+
const { pagePath, query, relaunch = true } = GotoInput.parse(input);
|
|
201
|
+
const sessionId = meta?.connectionId ?? 'default';
|
|
202
|
+
const { miniProgram } = sessions.get(sessionId);
|
|
203
|
+
if (!miniProgram) throw new Error('not launched');
|
|
204
|
+
|
|
205
|
+
const full = typeof query === 'string' || !query
|
|
206
|
+
? pagePath + (typeof query === 'string' ? `?${query}` : '')
|
|
207
|
+
: pagePath + '?' + new URLSearchParams(query).toString();
|
|
208
|
+
|
|
209
|
+
const page = relaunch
|
|
210
|
+
? await miniProgram.reLaunch(full)
|
|
211
|
+
: await miniProgram.navigateTo(full);
|
|
212
|
+
|
|
213
|
+
await page.waitFor(500);
|
|
214
|
+
return { pagePath: full };
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const QueryInput = z.object({
|
|
219
|
+
selector: z.string(),
|
|
220
|
+
all: z.boolean().optional()
|
|
221
|
+
});
|
|
222
|
+
export const query: Tool = {
|
|
223
|
+
name: 'query',
|
|
224
|
+
description: '查询元素并返回基础属性',
|
|
225
|
+
inputSchema: QueryInput,
|
|
226
|
+
async handler({ input, meta }) {
|
|
227
|
+
const { selector, all } = QueryInput.parse(input);
|
|
228
|
+
const sessionId = meta?.connectionId ?? 'default';
|
|
229
|
+
const { miniProgram } = sessions.get(sessionId);
|
|
230
|
+
if (!miniProgram) throw new Error('not launched');
|
|
231
|
+
const page = await miniProgram.currentPage();
|
|
232
|
+
|
|
233
|
+
const els = all ? await page.$$(selector) : [await page.$(selector)];
|
|
234
|
+
const items = [];
|
|
235
|
+
for (const el of els) {
|
|
236
|
+
if (!el) continue;
|
|
237
|
+
items.push({
|
|
238
|
+
text: await el.text(),
|
|
239
|
+
attr: await el.attribute('class'),
|
|
240
|
+
rect: await el.rect(),
|
|
241
|
+
exists: true
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return { count: items.length, items };
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const TapInput = z.object({ selector: z.string() });
|
|
249
|
+
export const tap: Tool = {
|
|
250
|
+
name: 'tap',
|
|
251
|
+
description: '点击元素',
|
|
252
|
+
inputSchema: TapInput,
|
|
253
|
+
async handler({ input, meta }) {
|
|
254
|
+
const { selector } = TapInput.parse(input);
|
|
255
|
+
const sessionId = meta?.connectionId ?? 'default';
|
|
256
|
+
const { miniProgram } = sessions.get(sessionId);
|
|
257
|
+
if (!miniProgram) throw new Error('not launched');
|
|
258
|
+
const page = await miniProgram.currentPage();
|
|
259
|
+
const el = await page.$(selector);
|
|
260
|
+
if (!el) throw new Error(`selector not found: ${selector}`);
|
|
261
|
+
await el.tap();
|
|
262
|
+
await page.waitFor(200);
|
|
263
|
+
return { ok: true };
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const CallWxInput = z.object({ api: z.string(), args: z.any().optional() });
|
|
268
|
+
export const callWx: Tool = {
|
|
269
|
+
name: 'call_wx',
|
|
270
|
+
description: '调用 wx.* 接口(例如 login、setStorage 等)',
|
|
271
|
+
inputSchema: CallWxInput,
|
|
272
|
+
async handler({ input, meta }) {
|
|
273
|
+
const { api, args } = CallWxInput.parse(input);
|
|
274
|
+
const sessionId = meta?.connectionId ?? 'default';
|
|
275
|
+
const { miniProgram } = sessions.get(sessionId);
|
|
276
|
+
if (!miniProgram) throw new Error('not launched');
|
|
277
|
+
|
|
278
|
+
// 若 SDK 暴露 callWxMethod:
|
|
279
|
+
// @ts-ignore
|
|
280
|
+
const result = await miniProgram.callWxMethod?.(api, args);
|
|
281
|
+
return { result };
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
export const tools: Tool[] = [launch, goto, query, tap, callWx];
|
|
286
|
+
|
|
287
|
+
以上用法与 miniprogram-automator README 示例一致(launch/connect、reLaunch、page.$、element.attribute/tap),可以在此基础上继续扩展(input/scroll/assert/screenshot/inject)。 
|
|
288
|
+
|
|
289
|
+
src/server.ts
|
|
290
|
+
|
|
291
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
292
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
293
|
+
import { tools } from './tools.js';
|
|
294
|
+
|
|
295
|
+
const server = new McpServer({
|
|
296
|
+
name: 'miniprogram-mcp',
|
|
297
|
+
version: '0.1.0'
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
for (const t of tools) server.tool(t);
|
|
301
|
+
|
|
302
|
+
const transport = new StdioServerTransport();
|
|
303
|
+
await server.connect(transport);
|
|
304
|
+
|
|
305
|
+
以上是 TS SDK 标准写法(注册工具 → 通过 stdio 暴露),与各类“MCP 入门”/Azure 示例一致。 
|
|
306
|
+
|
|
307
|
+
⸻
|
|
308
|
+
|
|
309
|
+
四、使用前的环境要求与关键设置
|
|
310
|
+
1. 安装微信开发者工具(Win/macOS),并在设置 → 安全设置中开启 CLI/HTTP 调用。
|
|
311
|
+
• Windows 可执行:cli.bat(安装目录内)
|
|
312
|
+
• macOS 可执行:/Applications/wechatwebdevtools.app/Contents/MacOS/cli
|
|
313
|
+
• 可用命令测试端口:cli.bat --auto <项目路径> --auto-port <端口>(Win;mac 路径同理) 
|
|
314
|
+
2. 安装 Node 与依赖:npm i @modelcontextprotocol/sdk miniprogram-automator zod。
|
|
315
|
+
3. 项目路径:保证 app.json 与页面路径正确;goto 工具里的 pagePath 如 /pages/index/index。
|
|
316
|
+
4. 版本注意:早期资料指出 IDE 需高于 v1.02.1906042 才支持自动化端口;按最新 IDE 版本一般已满足。 
|
|
317
|
+
|
|
318
|
+
⸻
|
|
319
|
+
|
|
320
|
+
五、从 AI 客户端驱动一次“可购物车流程”的示例
|
|
321
|
+
|
|
322
|
+
以 Claude Desktop / MCP Inspector 或任意支持 MCP 的 Host 为例:
|
|
323
|
+
• 连接 miniprogram-mcp(stdio)。
|
|
324
|
+
• 让 AI 逐步调用工具:
|
|
325
|
+
|
|
326
|
+
1. 启动
|
|
327
|
+
|
|
328
|
+
{"tool":"launch","input":{"projectPath":"/Users/me/app","cliPath":"/Applications/wechatwebdevtools.app/Contents/MacOS/cli"}}
|
|
329
|
+
|
|
330
|
+
2. 进首页
|
|
331
|
+
|
|
332
|
+
{"tool":"goto","input":{"pagePath":"/pages/index/index"}}
|
|
333
|
+
|
|
334
|
+
3. 查找并点击“加入购物车”
|
|
335
|
+
|
|
336
|
+
{"tool":"query","input":{"selector":".sku-card .add-to-cart"}}
|
|
337
|
+
{"tool":"tap","input":{"selector":".sku-card .add-to-cart"}}
|
|
338
|
+
|
|
339
|
+
4. 断言购物车角标变化(可在 assert 工具中比较元素 text 或页面 data)
|
|
340
|
+
|
|
341
|
+
⸻
|
|
342
|
+
|
|
343
|
+
六、对标 playwright-mcp 的“面向 AI 的工程化增强”
|
|
344
|
+
|
|
345
|
+
playwright-mcp 暴露了辅助调试、可视化/快照、可复用脚本生成等工具;我们可在小程序端做同等增强。 
|
|
346
|
+
|
|
347
|
+
• 稳定选择器约定:在 WXML 增加 data-testid,MCP 工具优先按 [data-testid="xxx"] 定位,减少脆弱性。
|
|
348
|
+
• 结构化页面快照:额外提供 snapshot_page 工具:返回页面节点树(tag/class/id/text/children),让 AI 不依赖截图理解页面语义。
|
|
349
|
+
• 录制/回放:IDE/SDK 不直接提供录制?可在 MCP 层记录调用序列(或参考社区“用户行为 JSON 回放”实践)并存储为可重放脚本。 
|
|
350
|
+
• 网络桩/时间冻结:inject 重写 wx.request、Date.now,使用例可重复。可参考 mpx e2e 的封装理念。 
|
|
351
|
+
• 错误诊断:每次失败自动截屏 + 导出 page.data + 节点树。
|
|
352
|
+
• 安全与资源:限制工具写文件目录;对 inject 白名单(禁止访问敏感 API)。
|
|
353
|
+
|
|
354
|
+
⸻
|
|
355
|
+
|
|
356
|
+
七、CI 集成与“开发-提测-上线”流水线
|
|
357
|
+
• 本地/CI 环境:IDE 官方主要支持 Windows/macOS;CI 建议使用这两个 Runner。Linux 端执行 IDE 存在兼容性障碍,应谨慎评估。
|
|
358
|
+
• 流水线:
|
|
359
|
+
1. miniprogram-ci 做编译/预览/上传(非 UI 自动化) 
|
|
360
|
+
2. 启动带桌面的 Runner(macOS/Windows),拉起 IDE,运行 miniprogram-mcp
|
|
361
|
+
3. 用“AI 驱动的自然语言测试”或“固定脚本回放”跑回归
|
|
362
|
+
4. 产出报告:整合截图、断言日志与用例覆盖(可上报到测试平台)
|
|
363
|
+
|
|
364
|
+
⸻
|
|
365
|
+
|
|
366
|
+
八、常见坑与策略
|
|
367
|
+
• CLI/HTTP 未启用:launch/connect 失败;请在 IDE 安全设置中开启(上文已列路径)。 
|
|
368
|
+
• 页面未稳定:需要 waitFor / wait_for_selector 辅助工具(可扩展)避免竞态。
|
|
369
|
+
• 真机差异:miniprogram-automator 主要驱动 IDE 模拟器;涉及端能力/授权的场景优先用 call_wx + 模拟数据,或结合真机远程调试流程(文档有“Remote Debugging”指引)。 
|
|
370
|
+
• 选择器脆弱:落地时务必引入 data-testid 规范。
|
|
371
|
+
• 登录:通过 call_wx('login') 拿 code,或注入自定义 token(测试环境后端配合)。 
|
|
372
|
+
|
|
373
|
+
⸻
|
|
374
|
+
|
|
375
|
+
九、下一步增强路线图(建议)
|
|
376
|
+
1. 更丰富的工具面板:fill、scroll_into_view、longpress、drag、toast_detect、modal_action、storage_get/set/clear。
|
|
377
|
+
2. 可视化调试器:模仿 playwright-mcp 的“可视化节点树/截图”,帮助 AI 选择选择器。 
|
|
378
|
+
3. 测试报告与覆盖率:统一产物(JSON + HTML),并与需求/缺陷系统对接。
|
|
379
|
+
4. 多会话/多项目并行:Session 隔离 + 资源配额。
|
|
380
|
+
5. 与 mpx e2e 或 Minium 互补:对复杂 Mock/Hook 用 Minium(Python/JS 版本,兼容三端),MCP 作为编排层。 
|
package/package.json
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@creatoria/miniapp-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for WeChat Mini Program automation using miniprogram-automator",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"packageManager": "pnpm@9.0.0",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"miniprogram-mcp": "dist/cli.js",
|
|
10
|
+
"creatoria-miniapp-mcp": "dist/cli.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"docs/"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/rn1024/creatoria-miniapp-mcp.git"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/rn1024/creatoria-miniapp-mcp#readme",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/rn1024/creatoria-miniapp-mcp/issues"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"dev": "tsc --watch",
|
|
32
|
+
"start": "node dist/server.js",
|
|
33
|
+
"test": "jest",
|
|
34
|
+
"test:watch": "jest --watch",
|
|
35
|
+
"test:unit": "jest tests/unit",
|
|
36
|
+
"test:integration": "jest tests/integration --runInBand",
|
|
37
|
+
"lint": "eslint . --ext .ts",
|
|
38
|
+
"format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
39
|
+
"format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"update-readme": "tsx scripts/update-readme.ts",
|
|
42
|
+
"smoke-test": "bash scripts/smoke-test.sh",
|
|
43
|
+
"release:patch": "bash scripts/release.sh patch",
|
|
44
|
+
"release:minor": "bash scripts/release.sh minor",
|
|
45
|
+
"release:major": "bash scripts/release.sh major",
|
|
46
|
+
"release:prerelease": "bash scripts/release.sh prerelease",
|
|
47
|
+
"simulate:ai": "tsx tests/simulation/ai-mcp-simulator.ts",
|
|
48
|
+
"simulate:quick": "tsx tests/simulation/quick-ai-test.ts",
|
|
49
|
+
"simulate:scenario": "tsx tests/simulation/scenario-runner.ts",
|
|
50
|
+
"prepare": "husky install"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"mcp",
|
|
54
|
+
"model-context-protocol",
|
|
55
|
+
"wechat",
|
|
56
|
+
"miniprogram",
|
|
57
|
+
"automation",
|
|
58
|
+
"testing",
|
|
59
|
+
"miniprogram-automator"
|
|
60
|
+
],
|
|
61
|
+
"author": "",
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
65
|
+
"commander": "^12.1.0",
|
|
66
|
+
"miniprogram-automator": "^0.12.1",
|
|
67
|
+
"zod": "^3.23.8"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/jest": "^30.0.0",
|
|
71
|
+
"@types/node": "^20.14.9",
|
|
72
|
+
"@typescript-eslint/eslint-plugin": "^7.13.1",
|
|
73
|
+
"@typescript-eslint/parser": "^7.13.1",
|
|
74
|
+
"eslint": "^8.57.0",
|
|
75
|
+
"eslint-config-prettier": "^9.1.0",
|
|
76
|
+
"husky": "^9.0.11",
|
|
77
|
+
"jest": "^29.7.0",
|
|
78
|
+
"prettier": "^3.3.2",
|
|
79
|
+
"ts-jest": "^29.1.5",
|
|
80
|
+
"ts-node": "^10.9.2",
|
|
81
|
+
"tsx": "^4.20.6",
|
|
82
|
+
"typescript": "^5.5.2"
|
|
83
|
+
},
|
|
84
|
+
"engines": {
|
|
85
|
+
"node": ">=18.0.0"
|
|
86
|
+
}
|
|
87
|
+
}
|