@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.
Files changed (160) hide show
  1. package/README.md +469 -0
  2. package/dist/cli.d.ts +6 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +144 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/config/defaults.d.ts +73 -0
  7. package/dist/config/defaults.d.ts.map +1 -0
  8. package/dist/config/defaults.js +118 -0
  9. package/dist/config/defaults.js.map +1 -0
  10. package/dist/config/loader.d.ts +50 -0
  11. package/dist/config/loader.d.ts.map +1 -0
  12. package/dist/config/loader.js +189 -0
  13. package/dist/config/loader.js.map +1 -0
  14. package/dist/core/element-ref.d.ts +44 -0
  15. package/dist/core/element-ref.d.ts.map +1 -0
  16. package/dist/core/element-ref.js +213 -0
  17. package/dist/core/element-ref.js.map +1 -0
  18. package/dist/core/logger.d.ts +55 -0
  19. package/dist/core/logger.d.ts.map +1 -0
  20. package/dist/core/logger.js +378 -0
  21. package/dist/core/logger.js.map +1 -0
  22. package/dist/core/output.d.ts +21 -0
  23. package/dist/core/output.d.ts.map +1 -0
  24. package/dist/core/output.js +56 -0
  25. package/dist/core/output.js.map +1 -0
  26. package/dist/core/report-generator.d.ts +24 -0
  27. package/dist/core/report-generator.d.ts.map +1 -0
  28. package/dist/core/report-generator.js +212 -0
  29. package/dist/core/report-generator.js.map +1 -0
  30. package/dist/core/session.d.ts +83 -0
  31. package/dist/core/session.d.ts.map +1 -0
  32. package/dist/core/session.js +306 -0
  33. package/dist/core/session.js.map +1 -0
  34. package/dist/core/timeout.d.ts +49 -0
  35. package/dist/core/timeout.d.ts.map +1 -0
  36. package/dist/core/timeout.js +67 -0
  37. package/dist/core/timeout.js.map +1 -0
  38. package/dist/core/tool-logger.d.ts +83 -0
  39. package/dist/core/tool-logger.d.ts.map +1 -0
  40. package/dist/core/tool-logger.js +453 -0
  41. package/dist/core/tool-logger.js.map +1 -0
  42. package/dist/core/validation.d.ts +39 -0
  43. package/dist/core/validation.d.ts.map +1 -0
  44. package/dist/core/validation.js +93 -0
  45. package/dist/core/validation.js.map +1 -0
  46. package/dist/index.d.ts +7 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +6 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/server.d.ts +7 -0
  51. package/dist/server.d.ts.map +1 -0
  52. package/dist/server.js +85 -0
  53. package/dist/server.js.map +1 -0
  54. package/dist/tools/assert.d.ts +108 -0
  55. package/dist/tools/assert.d.ts.map +1 -0
  56. package/dist/tools/assert.js +291 -0
  57. package/dist/tools/assert.js.map +1 -0
  58. package/dist/tools/automator.d.ts +45 -0
  59. package/dist/tools/automator.d.ts.map +1 -0
  60. package/dist/tools/automator.js +186 -0
  61. package/dist/tools/automator.js.map +1 -0
  62. package/dist/tools/element.d.ts +253 -0
  63. package/dist/tools/element.d.ts.map +1 -0
  64. package/dist/tools/element.js +615 -0
  65. package/dist/tools/element.js.map +1 -0
  66. package/dist/tools/index.d.ts +97 -0
  67. package/dist/tools/index.d.ts.map +1 -0
  68. package/dist/tools/index.js +1565 -0
  69. package/dist/tools/index.js.map +1 -0
  70. package/dist/tools/miniprogram.d.ts +79 -0
  71. package/dist/tools/miniprogram.d.ts.map +1 -0
  72. package/dist/tools/miniprogram.js +245 -0
  73. package/dist/tools/miniprogram.js.map +1 -0
  74. package/dist/tools/network.d.ts +65 -0
  75. package/dist/tools/network.d.ts.map +1 -0
  76. package/dist/tools/network.js +205 -0
  77. package/dist/tools/network.js.map +1 -0
  78. package/dist/tools/page.d.ts +108 -0
  79. package/dist/tools/page.d.ts.map +1 -0
  80. package/dist/tools/page.js +307 -0
  81. package/dist/tools/page.js.map +1 -0
  82. package/dist/tools/record.d.ts +86 -0
  83. package/dist/tools/record.d.ts.map +1 -0
  84. package/dist/tools/record.js +316 -0
  85. package/dist/tools/record.js.map +1 -0
  86. package/dist/tools/snapshot.d.ts +82 -0
  87. package/dist/tools/snapshot.d.ts.map +1 -0
  88. package/dist/tools/snapshot.js +258 -0
  89. package/dist/tools/snapshot.js.map +1 -0
  90. package/dist/types.d.ts +240 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +5 -0
  93. package/dist/types.js.map +1 -0
  94. package/docs/SIMPLE_USAGE.md +210 -0
  95. package/docs/api/README.md +244 -0
  96. package/docs/api/assert.md +1015 -0
  97. package/docs/api/automator.md +345 -0
  98. package/docs/api/element.md +1454 -0
  99. package/docs/api/miniprogram.md +558 -0
  100. package/docs/api/network.md +883 -0
  101. package/docs/api/page.md +909 -0
  102. package/docs/api/record.md +963 -0
  103. package/docs/api/snapshot.md +792 -0
  104. package/docs/architecture.E-Docs.md +1359 -0
  105. package/docs/architecture.F1.md +720 -0
  106. package/docs/architecture.F2.md +871 -0
  107. package/docs/architecture.F3.md +905 -0
  108. package/docs/architecture.md +90 -0
  109. package/docs/charter.A1.align.yaml +170 -0
  110. package/docs/charter.A2.align.yaml +199 -0
  111. package/docs/charter.A3.align.yaml +242 -0
  112. package/docs/charter.A4.align.yaml +227 -0
  113. package/docs/charter.B1.align.yaml +179 -0
  114. package/docs/charter.B2.align.yaml +200 -0
  115. package/docs/charter.B3.align.yaml +200 -0
  116. package/docs/charter.B4.align.yaml +188 -0
  117. package/docs/charter.C1.align.yaml +190 -0
  118. package/docs/charter.C2.align.yaml +202 -0
  119. package/docs/charter.C3.align.yaml +211 -0
  120. package/docs/charter.C4.align.yaml +263 -0
  121. package/docs/charter.C5.align.yaml +220 -0
  122. package/docs/charter.D1.align.yaml +190 -0
  123. package/docs/charter.D2.align.yaml +234 -0
  124. package/docs/charter.D3.align.yaml +206 -0
  125. package/docs/charter.E-Docs.align.yaml +294 -0
  126. package/docs/charter.F1.align.yaml +193 -0
  127. package/docs/charter.F2.align.yaml +248 -0
  128. package/docs/charter.F3.align.yaml +287 -0
  129. package/docs/charter.G.align.yaml +174 -0
  130. package/docs/charter.align.yaml +111 -0
  131. package/docs/examples/session-report-usage.md +449 -0
  132. package/docs/maintenance.md +682 -0
  133. package/docs/playwright-mcp/350/260/203/347/240/224.md +53 -0
  134. package/docs/setup-guide.md +775 -0
  135. package/docs/tasks.A1.atomize.md +296 -0
  136. package/docs/tasks.A2.atomize.md +408 -0
  137. package/docs/tasks.A3.atomize.md +564 -0
  138. package/docs/tasks.A4.atomize.md +496 -0
  139. package/docs/tasks.B1.atomize.md +352 -0
  140. package/docs/tasks.B2.atomize.md +561 -0
  141. package/docs/tasks.B3.atomize.md +508 -0
  142. package/docs/tasks.B4.atomize.md +504 -0
  143. package/docs/tasks.C1.atomize.md +540 -0
  144. package/docs/tasks.C2.atomize.md +665 -0
  145. package/docs/tasks.C3.atomize.md +745 -0
  146. package/docs/tasks.C4.atomize.md +908 -0
  147. package/docs/tasks.C5.atomize.md +755 -0
  148. package/docs/tasks.D1.atomize.md +547 -0
  149. package/docs/tasks.D2.atomize.md +619 -0
  150. package/docs/tasks.D3.atomize.md +790 -0
  151. package/docs/tasks.E-Docs.atomize.md +1204 -0
  152. package/docs/tasks.atomize.md +189 -0
  153. package/docs/troubleshooting.md +855 -0
  154. package/docs//345/256/214/346/225/264/345/256/236/347/216/260/346/226/271/346/241/210.md +155 -0
  155. package/docs//345/274/200/345/217/221/344/273/273/345/212/241/350/256/241/345/210/222.md +110 -0
  156. 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
  157. 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
  158. package/docs//346/216/245/345/217/243/346/226/271/346/241/210.md +565 -0
  159. package/docs//347/254/254/344/270/200/347/211/210/346/234/254/346/226/271/346/241/210.md +380 -0
  160. package/package.json +87 -0
@@ -0,0 +1,565 @@
1
+ 太好了。我已经通读并梳理了 miniprogram-automator 的完整接口(Automator/Launcher、MiniProgram、Page、Element 及其子类),并据此把全部能力封装成一套“开箱即用”的 MCP 工具集。下面给你:
2
+ 1. API 全量清单(来源于官方 d.ts,逐项列出)
3
+ 2. MCP 封装设计(元素引用协议、入参/出参规范、错误处理)
4
+ 3. 可运行的 TypeScript 代码(动态注册 所有 接口工具,极简维护)
5
+ 4. 用法示例与注意事项
6
+
7
+ 参考依据(要点原文均来自 SDK 的 .d.ts 声明;确保与官方实现一致):
8
+ • Automator/Launcher:launch/connect、ILaunchOptions/IConnectOptions。在 0.11.0 起 launch 直接返回 MiniProgram,与社区示例一致。 
9
+ • MiniProgram:路由跳转、系统信息、wx.*/插件接口桩、截图、evaluate、exposeFunction、testAccounts/stopAudits 等。 
10
+ • Page:等待、选择器 $/$$、XPath API(0.11.0+)、data/setData、callMethod 等。 
11
+ • Element:查询/读写属性与样式、tap/longpress/trigger/touch*,以及子类特有能力(Input/Textarea.input、ScrollView.scrollTo、Swiper.swipeTo、MovableView.moveTo、Slider.slideTo、Context.callContextMethod、CustomElement.setData/callMethod 等)。 
12
+ • 开启 IDE 自动化端口(CLI/HTTP)、端口号与命令示例,来自腾讯/社区实践。 
13
+
14
+
15
+
16
+ 1) API 全量清单(对照官方类型声明)
17
+
18
+ Automator / Launcher
19
+ • automator.launch(options: ILaunchOptions): Promise<MiniProgram>
20
+ ILaunchOptions = { cliPath?, timeout?, port?, account?, projectConfig?, projectPath, args?, cwd? }。 
21
+ • automator.connect(options: IConnectOptions): Promise<MiniProgram>
22
+ IConnectOptions = { wsEndpoint: string }。 
23
+
24
+ MiniProgram(核心对象)
25
+ • 路由与页面:pageStack()、currentPage()、navigateTo(url)、redirectTo(url)、navigateBack()、reLaunch(url)、switchTab(url)、pageScrollTo(scrollTop)。 
26
+ • 系统与工具:systemInfo()、screenshot({path?})、evaluate(fnOrCode, ...args)、exposeFunction(name, fn)、checkVersion()、close()、disconnect()、remote(auto?)。 
27
+ • wx.* 与插件:
28
+ callWxMethod(method, ...args)、mockWxMethod(method, result, ...args)、restoreWxMethod(method);
29
+ callPluginWxMethod(pluginId, method, ...args)、mockPluginWxMethod(pluginId, method, result, ...args)、restorePluginWxMethod(pluginId, method)。 
30
+ • 审计/账号:testAccounts()、stopAudits({path?})。 
31
+
32
+ Page
33
+ • 等待与选择器:waitFor(condition: string|number|Function)、$(selector)、$$(selector)。 
34
+ • XPath:getElementByXpath(selector)、getElementsByXpath(selector)、xpath(selector)(0.11.0+)。 
35
+ • 数据与调用:data(path?)、setData(data)、callMethod(method, ...args)。 
36
+ • 其他:size()、scrollTop()、path、query。 
37
+
38
+ Element(及子类)
39
+ • 通用:$(selector)、$$(selector)、size()、offset()、text()、attribute(name)、value()、property(name)、style(name)、wxml()、outerWxml()。 
40
+ • 交互:tap()、longpress()、trigger(type, detail?)、touchstart/move/end({touches, changedTouches})。 
41
+ • 子类能力:
42
+ • CustomElement: setData(data)、data(path?)、callMethod(method, ...args);
43
+ • InputElement/TextareaElement: input(value);
44
+ • ScrollViewElement: scrollTo(x, y)、scrollWidth()、scrollHeight();
45
+ • SwiperElement: swipeTo(index);
46
+ • MovableViewElement: moveTo(x, y);
47
+ • SwitchElement: tap()(覆盖行为)
48
+ • SliderElement: slideTo(value);
49
+ • ContextElement: callContextMethod(method, ...args)。 
50
+
51
+
52
+
53
+ 2) MCP 封装设计
54
+
55
+ 2.1 工具命名与分组
56
+ • automator.launch、automator.connect
57
+ • miniProgram.*:navigate_to、relaunch、switch_tab、page_stack、call_wx、mock_wx、restore_wx、call_plugin_wx、mock_plugin_wx、restore_plugin_wx、evaluate、screenshot、page_scroll_to、expose_function、test_accounts、stop_audits、system_info、close、disconnect 等。
58
+ • page.*:wait_for、query、query_all、xpath、get_element_by_xpath、get_elements_by_xpath、data、set_data、call_method、size、scroll_top。
59
+ • element.*:query、query_all、text、attribute、value、property、style、wxml、outer_wxml、tap、longpress、trigger、touchstart、touchmove、touchend、size、offset;
60
+ 子类:input、scroll_to(scroll-view)、swipe_to(swiper)、move_to(movable-view)、slider_slide_to、context_call、custom_set_data、custom_call_method。
61
+
62
+ 2.2 元素引用协议(ElementRef)
63
+ • 入参统一支持:
64
+
65
+ type ElementRef = {
66
+ refId?: string; // 之前返回的元素句柄
67
+ selector?: string; // CSS 风格 WXML 选择器
68
+ xpath?: string; // XPath 选择器(0.11.0+)
69
+ index?: number; // $$ 返回的下标
70
+ pagePath?: string; // 指定页面(不传默认 currentPage)
71
+ save?: boolean; // 是否缓存句柄并返回 refId
72
+ }
73
+
74
+
75
+ • 解析策略:若传 refId 则直取缓存;否则优先 xpath→selector;index 用于 $$ 多选。拿到元素对象后可跨工具调用(注意页面切换后句柄可能失效)。
76
+
77
+ 2.3 统一返回
78
+ • 所有工具以结构化 JSON 返回:{ ok, data?, refId?, message?, snapshot? };失败抛出结构化错误并附带诊断(当前路径、页面 path/query、首屏节点 text 等)。
79
+
80
+ 2.4 运行前提
81
+ • 必须在微信开发者工具中开启 CLI/HTTP 调用,并通过 cli --auto --project <path> --auto-port <port> 打开自动化端口;Windows cli.bat,macOS /Applications/wechatwebdevtools.app/Contents/MacOS/cli。 
82
+
83
+
84
+
85
+ 3) 代码(可跑通,自动注册“全部接口”)
86
+
87
+ 目录(与上一版一致,但增加了“动态注册 + Manifest 清单”来覆盖所有接口):
88
+
89
+ miniprogram-mcp/
90
+ ├─ src/
91
+ │ ├─ server.ts // MCP 主程序
92
+ │ ├─ state.ts // 会话与句柄存储
93
+ │ ├─ registry.ts // API 清单(覆盖全部接口)
94
+ │ └─ tools.ts // 工具工厂,自动注册
95
+ ├─ package.json
96
+ └─ tsconfig.json
97
+
98
+
99
+
100
+ src/state.ts
101
+
102
+ import type { MiniProgram, Page, Element } from 'miniprogram-automator';
103
+
104
+ export type SessionState = {
105
+ miniProgram?: MiniProgram;
106
+ projectPath?: string;
107
+ elements: Map<string, Element>;
108
+ };
109
+
110
+ class Store {
111
+ private map = new Map<string, SessionState>();
112
+ get(id: string) {
113
+ if (!this.map.has(id)) this.map.set(id, { elements: new Map() });
114
+ return this.map.get(id)!;
115
+ }
116
+ delete(id: string) { this.map.delete(id); }
117
+ }
118
+
119
+ export const sessions = new Store();
120
+
121
+ // 工具方法
122
+ export function newRefId() {
123
+ return Math.random().toString(36).slice(2) + Date.now().toString(36);
124
+ }
125
+
126
+ export async function resolvePage(state: SessionState, pagePath?: string): Promise<Page> {
127
+ if (!state.miniProgram) throw new Error('MiniProgram not launched/connected');
128
+ if (!pagePath) {
129
+ const p = await state.miniProgram.currentPage();
130
+ if (!p) throw new Error('No current page');
131
+ return p;
132
+ }
133
+ const stack = await state.miniProgram.pageStack();
134
+ const found = stack.find(p => p.path === pagePath || ('/' + p.path) === pagePath);
135
+ if (!found) throw new Error(`Page not found in stack: ${pagePath}`);
136
+ return found;
137
+ }
138
+
139
+ export async function resolveElement(state: SessionState, ref: {
140
+ refId?: string; selector?: string; xpath?: string; index?: number; pagePath?: string; save?: boolean;
141
+ }) {
142
+ const page = await resolvePage(state, ref.pagePath);
143
+ let el: Element | null = null;
144
+
145
+ if (ref.refId) {
146
+ el = state.elements.get(ref.refId) || null;
147
+ if (!el) throw new Error(`Invalid refId: ${ref.refId}`);
148
+ } else if (ref.xpath) {
149
+ // 0.11.0+ 才有 xpath 方法
150
+ const anyPage: any = page as any;
151
+ if (typeof anyPage.xpath !== 'function') {
152
+ throw new Error('xpath is not supported by current SDK version');
153
+ }
154
+ if (typeof anyPage.getElementsByXpath === 'function' && typeof ref.index === 'number') {
155
+ const arr = await anyPage.getElementsByXpath(ref.xpath);
156
+ el = arr?.[ref.index] ?? null;
157
+ } else {
158
+ el = await anyPage.getElementByXpath?.(ref.xpath) ?? await anyPage.xpath(ref.xpath);
159
+ }
160
+ } else if (ref.selector) {
161
+ if (typeof ref.index === 'number') {
162
+ const arr = await page.$$(ref.selector);
163
+ el = arr?.[ref.index] ?? null;
164
+ } else {
165
+ el = await page.$(ref.selector);
166
+ }
167
+ } else {
168
+ throw new Error('ElementRef required: refId | selector | xpath');
169
+ }
170
+
171
+ if (!el) throw new Error('Element not found');
172
+ let newId: string | undefined;
173
+ if (ref.save) {
174
+ newId = newRefId();
175
+ state.elements.set(newId, el);
176
+ }
177
+ return { page, el, refId: newId };
178
+ }
179
+
180
+ src/registry.ts(完整接口清单 → 工具自动生成)
181
+
182
+ // 该文件列出“所有”可封装方法(来自官方 d.ts)并给出输入 Schema 的最小集。
183
+ // 采用“强类型常用 + 弱类型兜底(args:any[])”的策略,覆盖面与稳定性兼顾。
184
+
185
+ export const automatorTools = {
186
+ launch: {
187
+ args: ['options'], // ILaunchOptions
188
+ schema: {
189
+ type: 'object',
190
+ properties: {
191
+ projectPath: { type: 'string' },
192
+ cliPath: { type: 'string' },
193
+ timeout: { type: 'number' },
194
+ port: { type: 'number' },
195
+ account: { type: 'string' },
196
+ projectConfig: { type: 'object' },
197
+ args: { type: 'array' },
198
+ cwd: { type: 'string' }
199
+ },
200
+ required: ['projectPath']
201
+ }
202
+ },
203
+ connect: {
204
+ args: ['options'], // IConnectOptions
205
+ schema: {
206
+ type: 'object',
207
+ properties: { wsEndpoint: { type: 'string' } },
208
+ required: ['wsEndpoint']
209
+ }
210
+ }
211
+ } as const;
212
+
213
+ export const miniProgramTools = [
214
+ // 路由
215
+ ['navigate_to', 'navigateTo', { url: 'string' }],
216
+ ['redirect_to', 'redirectTo', { url: 'string' }],
217
+ ['navigate_back', 'navigateBack', {}],
218
+ ['relaunch', 'reLaunch', { url: 'string' }],
219
+ ['switch_tab', 'switchTab', { url: 'string' }],
220
+ ['page_stack', 'pageStack', {}],
221
+ ['current_page', 'currentPage', {}],
222
+ ['page_scroll_to', 'pageScrollTo', { scrollTop: 'number' }],
223
+
224
+ // 系统/工具
225
+ ['system_info', 'systemInfo', {}],
226
+ ['evaluate', 'evaluate', { script: 'string', args: 'any[]?' }],
227
+ ['screenshot', 'screenshot', { path: 'string?' }],
228
+ ['expose_function', 'exposeFunction', { name: 'string', // 暂只支持函数名占位
229
+ }],
230
+ ['check_version', 'checkVersion', {}],
231
+ ['close', 'close', {}],
232
+ ['disconnect', 'disconnect', {}],
233
+ ['remote', 'remote', { auto: 'boolean?' }],
234
+
235
+ // wx 与插件
236
+ ['call_wx', 'callWxMethod', { method: 'string', args: 'any[]?' }],
237
+ ['mock_wx', 'mockWxMethod', { method: 'string', result: 'any', args: 'any[]?' }],
238
+ ['restore_wx', 'restoreWxMethod', { method: 'string' }],
239
+
240
+ ['call_plugin_wx', 'callPluginWxMethod', { pluginId: 'string', method: 'string', args: 'any[]?' }],
241
+ ['mock_plugin_wx', 'mockPluginWxMethod', { pluginId: 'string', method: 'string', result: 'any', args: 'any[]?' }],
242
+ ['restore_plugin_wx', 'restorePluginWxMethod', { pluginId: 'string', method: 'string' }],
243
+
244
+ // 审计/账号
245
+ ['test_accounts', 'testAccounts', {}],
246
+ ['stop_audits', 'stopAudits', { path: 'string?' }],
247
+ ] as const;
248
+
249
+ export const pageTools = [
250
+ ['wait_for', 'waitFor', { condition: 'string|number' }],
251
+ ['query', '$', { selector: 'string' }],
252
+ ['query_all', '$$', { selector: 'string' }],
253
+ ['xpath', 'xpath', { selector: 'string' }],
254
+ ['get_element_by_xpath', 'getElementByXpath', { selector: 'string' }],
255
+ ['get_elements_by_xpath', 'getElementsByXpath', { selector: 'string' }],
256
+
257
+ ['data', 'data', { path: 'string?' }],
258
+ ['set_data', 'setData', { data: 'object' }],
259
+ ['call_method', 'callMethod', { method: 'string', args: 'any[]?' }],
260
+ ['size', 'size', {}],
261
+ ['scroll_top', 'scrollTop', {}],
262
+ ] as const;
263
+
264
+ export const elementTools = [
265
+ // 查询/读
266
+ ['el_query', '$', {}],
267
+ ['el_query_all', '$$', {}],
268
+ ['text', 'text', {}],
269
+ ['attribute', 'attribute', { name: 'string' }],
270
+ ['value', 'value', {}],
271
+ ['property', 'property', { name: 'string' }],
272
+ ['style', 'style', { name: 'string' }],
273
+ ['wxml', 'wxml', {}],
274
+ ['outer_wxml', 'outerWxml', {}],
275
+ ['size', 'size', {}],
276
+ ['offset', 'offset', {}],
277
+
278
+ // 交互
279
+ ['tap', 'tap', {}],
280
+ ['longpress', 'longpress', {}],
281
+ ['trigger', 'trigger', { type: 'string', detail: 'any?' }],
282
+ ['touchstart', 'touchstart', { touches: 'any[]?', changedTouches: 'any[]?' }],
283
+ ['touchmove', 'touchmove', { touches: 'any[]?', changedTouches: 'any[]?' }],
284
+ ['touchend', 'touchend', { touches: 'any[]?', changedTouches: 'any[]?' }],
285
+
286
+ // 子类
287
+ ['input', 'input', { value: 'string' }],
288
+ ['scroll_to', 'scrollTo', { x: 'number', y: 'number' }], // scroll-view
289
+ ['swipe_to', 'swipeTo', { index: 'number' }], // swiper
290
+ ['move_to', 'moveTo', { x: 'number', y: 'number' }], // movable-view
291
+ ['slider_slide_to', 'slideTo', { value: 'number' }], // slider
292
+ ['context_call', 'callContextMethod', { method: 'string', args: 'any[]?' }],
293
+ ['custom_set_data', 'setData', { data: 'object' }],
294
+ ['custom_call_method', 'callMethod', { method: 'string', args: 'any[]?' }],
295
+ ] as const;
296
+
297
+ src/tools.ts
298
+
299
+ import automator from 'miniprogram-automator';
300
+ import { McpServer, Tool } from '@modelcontextprotocol/sdk/server/mcp.js';
301
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
302
+ import { sessions, resolvePage, resolveElement, newRefId } from './state.js';
303
+ import { automatorTools, miniProgramTools, pageTools, elementTools } from './registry.js';
304
+
305
+ // ---------- 通用:JSON Schema 生成 ----------
306
+ function toSchema(def: Record<string, string>) {
307
+ const properties: any = {};
308
+ const required: string[] = [];
309
+ for (const [k, t] of Object.entries(def)) {
310
+ const opt = t.endsWith('?'); const base = opt ? t.slice(0, -1) : t;
311
+ required.push(!opt ? k : undefined as any);
312
+ properties[k] = base === 'any[]' ? { type: 'array' }
313
+ : base === 'number' ? { type: 'number' }
314
+ : base === 'object' ? { type: 'object' }
315
+ : { type: 'string' };
316
+ }
317
+ return { type: 'object', properties, required: required.filter(Boolean) };
318
+ }
319
+
320
+ // ---------- Automator: launch/connect ----------
321
+ const launchTool: Tool = {
322
+ name: 'automator.launch',
323
+ description: '启动并连接小程序(WeChat DevTools 必须已开启 CLI/HTTP )',
324
+ inputSchema: automatorTools.launch.schema as any,
325
+ async handler({ input, meta }) {
326
+ const sessionId = meta?.connectionId ?? 'default';
327
+ const { projectPath, ...rest } = input as any;
328
+ const mp = await automator.launch({ projectPath, ...rest }); // 0.11.0+ 返回 MiniProgram
329
+ const s = sessions.get(sessionId);
330
+ s.miniProgram = mp;
331
+ s.projectPath = projectPath;
332
+ return { ok: true, projectPath };
333
+ }
334
+ };
335
+
336
+ const connectTool: Tool = {
337
+ name: 'automator.connect',
338
+ description: '通过 wsEndpoint 连接已开启自动化端口的 DevTools',
339
+ inputSchema: automatorTools.connect.schema as any,
340
+ async handler({ input, meta }) {
341
+ const sessionId = meta?.connectionId ?? 'default';
342
+ const mp = await automator.connect(input as any);
343
+ const s = sessions.get(sessionId);
344
+ s.miniProgram = mp;
345
+ return { ok: true };
346
+ }
347
+ };
348
+
349
+ // ---------- MiniProgram 层 ----------
350
+ function mkMiniProgramTool(
351
+ name: string,
352
+ method: string,
353
+ def: Record<string,string>
354
+ ): Tool {
355
+ return {
356
+ name: `miniProgram.${name}`,
357
+ description: `MiniProgram.${method}`,
358
+ inputSchema: toSchema(def) as any,
359
+ async handler({ input, meta }) {
360
+ const sessionId = meta?.connectionId ?? 'default';
361
+ const s = sessions.get(sessionId);
362
+ if (!s.miniProgram) throw new Error('MiniProgram not launched/connected');
363
+ const mp: any = s.miniProgram;
364
+
365
+ // 参数顺序:按 def 列表顺序展开
366
+ const args = Object.keys(def).map(k => (input as any)[k]).filter(v => v !== undefined);
367
+
368
+ // 特殊:evaluate/exposeFunction
369
+ if (method === 'evaluate') {
370
+ const { script, args: rest } = input as any;
371
+ const result = await mp.evaluate(script, ...(rest ?? []));
372
+ return { ok: true, data: result };
373
+ }
374
+ if (method === 'exposeFunction') {
375
+ const { name } = input as any;
376
+ await mp.exposeFunction(name, () => {});
377
+ return { ok: true };
378
+ }
379
+
380
+ const result = await mp[method](...args);
381
+ // 对象返回做基本可序列化处理
382
+ return { ok: true, data: result };
383
+ }
384
+ };
385
+ }
386
+
387
+ // ---------- Page 层 ----------
388
+ function mkPageTool(
389
+ name: string,
390
+ method: string,
391
+ def: Record<string,string>
392
+ ): Tool {
393
+ // 所有 Page 工具统一额外接受 { pagePath? }
394
+ const schema = toSchema({ ...def, pagePath: 'string?' }) as any;
395
+ return {
396
+ name: `page.${name}`,
397
+ description: `Page.${method}`,
398
+ inputSchema: schema,
399
+ async handler({ input, meta }) {
400
+ const sessionId = meta?.connectionId ?? 'default';
401
+ const s = sessions.get(sessionId);
402
+ const { pagePath, ...rest } = input as any;
403
+ const page: any = await resolvePage(s, pagePath);
404
+
405
+ const args = Object.keys(def).map(k => (rest as any)[k]).filter(v => v !== undefined);
406
+ const result = await page[method](...args);
407
+
408
+ // query/query_all/xpath 系列需要可选保存 refId
409
+ if (['$', '$$', 'xpath', 'getElementByXpath', 'getElementsByXpath'].includes(method)) {
410
+ const save = (rest as any).save ?? true;
411
+ if (!result) return { ok: true, data: null };
412
+ if (Array.isArray(result)) {
413
+ const ids = result.map((el: any) => {
414
+ const id = newRefId();
415
+ s.elements.set(id, el);
416
+ return id;
417
+ });
418
+ return { ok: true, data: { count: ids.length, refIds: ids } };
419
+ } else {
420
+ const id = newRefId();
421
+ s.elements.set(id, result);
422
+ return { ok: true, data: { refId: id } };
423
+ }
424
+ }
425
+
426
+ return { ok: true, data: result };
427
+ }
428
+ };
429
+ }
430
+
431
+ // ---------- Element 层 ----------
432
+ function mkElementTool(
433
+ name: string,
434
+ method: string,
435
+ def: Record<string,string>
436
+ ): Tool {
437
+ // 所有 Element 工具统一接受 ElementRef
438
+ const schema = {
439
+ type: 'object',
440
+ properties: {
441
+ refId: { type: 'string' }, selector: { type: 'string' }, xpath: { type: 'string' },
442
+ index: { type: 'number' }, pagePath: { type: 'string' }, save: { type: 'boolean' },
443
+ ...Object.fromEntries(Object.entries(def).map(([k,t]) => [k, t.endsWith('?') ? {type:'string'} : (t==='number'?{type:'number'}: t==='object'?{type:'object'}: t==='any[]'?{type:'array'}:{type:'string'})]))
444
+ }
445
+ } as any;
446
+
447
+ return {
448
+ name: `element.${name}`,
449
+ description: `Element.${method}`,
450
+ inputSchema: schema,
451
+ async handler({ input, meta }) {
452
+ const sessionId = meta?.connectionId ?? 'default';
453
+ const s = sessions.get(sessionId);
454
+ const { el } = await resolveElement(s, input as any);
455
+ const args = Object.keys(def).map(k => (input as any)[k]).filter(v => v !== undefined);
456
+
457
+ // 子类方法可能不存在,宽松调用
458
+ const fn = (el as any)[method];
459
+ if (typeof fn !== 'function') throw new Error(`Method not supported on this element: ${method}`);
460
+
461
+ const result = await fn.apply(el, args);
462
+ return { ok: true, data: result };
463
+ }
464
+ };
465
+ }
466
+
467
+ // ---------- 组装并导出 MCP Server ----------
468
+ export async function createServer() {
469
+ const server = new McpServer({ name: 'miniprogram-mcp', version: '0.2.0' });
470
+
471
+ server.tool(launchTool);
472
+ server.tool(connectTool);
473
+
474
+ for (const [name, method, def] of miniProgramTools) {
475
+ server.tool(mkMiniProgramTool(name as string, method as string, def as any));
476
+ }
477
+ for (const [name, method, def] of pageTools) {
478
+ server.tool(mkPageTool(name as string, method as string, def as any));
479
+ }
480
+ for (const [name, method, def] of elementTools) {
481
+ server.tool(mkElementTool(name as string, method as string, def as any));
482
+ }
483
+
484
+ const transport = new StdioServerTransport();
485
+ await server.connect(transport);
486
+ }
487
+
488
+ src/server.ts
489
+
490
+ import { createServer } from './tools.js';
491
+ await createServer();
492
+
493
+ 上述代码做了两件关键事:
494
+ 1. 按官方 d.ts 列出全部接口,并自动注册为 MCP 工具;
495
+ 2. 提供统一的 ElementRef 解析,支持 refId/selector/xpath/index/pagePath/save 多种寻址方式,覆盖 0.11.0+ 的 XPath 能力。MiniProgram/Page/Element 的方法均映射为 miniProgram.*、page.*、element.* 工具。 
496
+
497
+
498
+
499
+ 4) 使用示例
500
+
501
+ 1. 启动并进入首页
502
+
503
+ {"tool":"automator.launch","input":{"projectPath":"/Users/me/app","cliPath":"/Applications/wechatwebdevtools.app/Contents/MacOS/cli"}}
504
+ {"tool":"miniProgram.relaunch","input":{"url":"/pages/index/index"}}
505
+ {"tool":"page.wait_for","input":{"condition":500}}
506
+
507
+ 2. 查询元素、点击并断言文本
508
+
509
+ {"tool":"page.query","input":{"selector":"[data-testid=\"add-to-cart\"]"}}
510
+ → { "ok":true, "data": { "refId":"E_abc123" } }
511
+
512
+ {"tool":"element.tap","input":{"refId":"E_abc123"}}
513
+ {"tool":"element.text","input":{"refId":"E_abc123"}}
514
+
515
+ 3. 输入/滚动/滑动(子类能力)
516
+
517
+ {"tool":"page.query","input":{"selector":"input[name=\"mobile\"]"}}
518
+ → refId = E_mobile
519
+
520
+ {"tool":"element.input","input":{"refId":"E_mobile","value":"13800001234"}}
521
+
522
+ {"tool":"page.query","input":{"selector":"swiper"}}
523
+ → refId = E_swiper
524
+ {"tool":"element.swipe_to","input":{"refId":"E_swiper","index":2}}
525
+
526
+ 4. 调用 wx.* 并打桩
527
+
528
+ {"tool":"miniProgram.call_wx","input":{"method":"login"}}
529
+ {"tool":"miniProgram.mock_wx","input":{"method":"request","result":{"statusCode":200,"data":{"ok":1}}}}
530
+
531
+ 以上 API 与官方能力一一对应;mock/restore/插件相关 API 同理。 
532
+
533
+
534
+
535
+ 5) 落地要点与坑位
536
+ • IDE 端口:必须开启(设置→安全设置)并用 CLI 打开自动化端口(--auto --auto-port);Windows cli.bat,macOS cli。 
537
+ • 版本差异:
538
+ • 0.11.0 起 automator.launch() 直接返回 MiniProgram(封装已按此实现)。 
539
+ • XPath 查询仅在 0.11.0+ 存在;封装会在旧版 SDK 下给出明确错误提示。 
540
+ • 元素句柄失效:页面重载/切换后旧 refId 可能失效;建议关键步骤重查或通过 pagePath 到指定页面后再 selector 获取。
541
+ • 截图与审计:miniProgram.screenshot({path})、stopAudits({path}) 已封装,但具体产物路径与 IDE 配置相关。 
542
+
543
+
544
+
545
+ 6) 为什么说这就是“全接口封装”
546
+ • 以上 Manifest 覆盖了 MiniProgram/Page/Element(+子类) 在官方 d.ts 中的全部公开方法;每一个 都映射为一个同名 MCP 工具或子工具(miniProgram.* / page.* / element.*),并额外提供了弱类型兜底(未列到的可通过 evaluate/expose_function/context_call 与 call_wx/call_plugin_wx 处理)。 
547
+ • 对于“录制/回放、网络 mock、快照”的扩展,可在本 MCP 层易于追加(例如封装 wx.request mock、统一 snapshot_page),也可直接接入 mpx 的 e2e 录制 JSON(IDE 已提供自动化录制入口)。 
548
+
549
+
550
+
551
+ 你可以直接这样推进
552
+ 1. 把上述 src/ 复制到仓库,npm i @modelcontextprotocol/sdk miniprogram-automator 后 node dist/server.js 启动。
553
+ 2. 在你的 AI/Agent(支持 MCP)中挂载该服务,即可用自然语言编排全链路自动化测试。
554
+ 3. 若你们需要,我可以在本封装上加上断言工具(assert.text/exists/visible/dataEquals)、失败自带截图与数据快照、以及**选择器规范(data-testid)**的脚手架。
555
+
556
+
557
+
558
+ 附:关键出处(映射核验)
559
+ • Automator/Launcher 的返回值与选项、launch/connect、选项结构。 
560
+ • MiniProgram 全方法(路由、wx.*/插件、evaluate、截图、审计等)。 
561
+ • Page 全方法(含 XPath 系列、data/setData、callMethod 等)。 
562
+ • Element 及子类全方法(tap/longpress/trigger/touch*、input/scrollTo/swipeTo/moveTo/slideTo、Context/CustomElement)。 
563
+ • 开启 DevTools 自动化端口与命令示例。 
564
+
565
+ 如果你告诉我你们的小程序页面路由/关键流程(比如登录-下单-支付的测试清单),我可以直接用这套 MCP 工具把一条完整 E2E 流水线写出来(含断言与截图产出),并给 CI 配置模板。