mobile-mcp-ai 2.2.6__py3-none-any.whl → 2.5.3__py3-none-any.whl
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.
- mobile_mcp/config.py +3 -2
- mobile_mcp/core/basic_tools_lite.py +3193 -0
- mobile_mcp/core/ios_client_wda.py +569 -0
- mobile_mcp/core/ios_device_manager_wda.py +306 -0
- mobile_mcp/core/mobile_client.py +246 -20
- mobile_mcp/core/template_matcher.py +429 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
- mobile_mcp/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
- mobile_mcp/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
- mobile_mcp/mcp_tools/__init__.py +10 -0
- mobile_mcp/mcp_tools/mcp_server.py +992 -0
- mobile_mcp_ai-2.5.3.dist-info/METADATA +456 -0
- mobile_mcp_ai-2.5.3.dist-info/RECORD +32 -0
- mobile_mcp_ai-2.5.3.dist-info/entry_points.txt +2 -0
- mobile_mcp/core/ai/__init__.py +0 -11
- mobile_mcp/core/ai/ai_analyzer.py +0 -197
- mobile_mcp/core/ai/ai_config.py +0 -116
- mobile_mcp/core/ai/ai_platform_adapter.py +0 -399
- mobile_mcp/core/ai/smart_test_executor.py +0 -520
- mobile_mcp/core/ai/test_generator.py +0 -365
- mobile_mcp/core/ai/test_generator_from_history.py +0 -391
- mobile_mcp/core/ai/test_generator_standalone.py +0 -293
- mobile_mcp/core/assertion/__init__.py +0 -9
- mobile_mcp/core/assertion/smart_assertion.py +0 -341
- mobile_mcp/core/basic_tools.py +0 -945
- mobile_mcp/core/h5/__init__.py +0 -10
- mobile_mcp/core/h5/h5_handler.py +0 -548
- mobile_mcp/core/ios_client.py +0 -219
- mobile_mcp/core/ios_device_manager.py +0 -252
- mobile_mcp/core/locator/__init__.py +0 -10
- mobile_mcp/core/locator/cursor_ai_auto_analyzer.py +0 -119
- mobile_mcp/core/locator/cursor_vision_helper.py +0 -414
- mobile_mcp/core/locator/mobile_smart_locator.py +0 -1747
- mobile_mcp/core/locator/position_analyzer.py +0 -813
- mobile_mcp/core/locator/script_updater.py +0 -157
- mobile_mcp/core/nl_test_runner.py +0 -585
- mobile_mcp/core/smart_app_launcher.py +0 -421
- mobile_mcp/core/smart_tools.py +0 -311
- mobile_mcp/mcp/__init__.py +0 -13
- mobile_mcp/mcp/mcp_server.py +0 -1126
- mobile_mcp/mcp/mcp_server_simple.py +0 -23
- mobile_mcp/vision/__init__.py +0 -10
- mobile_mcp/vision/vision_locator.py +0 -405
- mobile_mcp_ai-2.2.6.dist-info/METADATA +0 -503
- mobile_mcp_ai-2.2.6.dist-info/RECORD +0 -49
- mobile_mcp_ai-2.2.6.dist-info/entry_points.txt +0 -2
- {mobile_mcp_ai-2.2.6.dist-info → mobile_mcp_ai-2.5.3.dist-info}/WHEEL +0 -0
- {mobile_mcp_ai-2.2.6.dist-info → mobile_mcp_ai-2.5.3.dist-info}/licenses/LICENSE +0 -0
- {mobile_mcp_ai-2.2.6.dist-info → mobile_mcp_ai-2.5.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mobile-mcp-ai
|
|
3
|
+
Version: 2.5.3
|
|
4
|
+
Summary: 移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)
|
|
5
|
+
Home-page: https://github.com/test111ddff-hash/mobile-mcp-ai
|
|
6
|
+
Author: douzi
|
|
7
|
+
Author-email: 1492994674@qq.com
|
|
8
|
+
Project-URL: Documentation, https://github.com/test111ddff-hash/mobile-mcp-ai
|
|
9
|
+
Project-URL: Source, https://github.com/test111ddff-hash/mobile-mcp-ai
|
|
10
|
+
Project-URL: Tracker, https://github.com/test111ddff-hash/mobile-mcp-ai/issues
|
|
11
|
+
Keywords: mobile,automation,testing,android,ios,mcp,ai,pytest,cursor
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Topic :: Software Development :: Testing
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: uiautomator2>=2.16.0
|
|
26
|
+
Requires-Dist: adbutils>=1.2.0
|
|
27
|
+
Requires-Dist: Pillow>=10.0.0
|
|
28
|
+
Requires-Dist: mcp>=0.9.0
|
|
29
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
30
|
+
Provides-Extra: ai
|
|
31
|
+
Requires-Dist: dashscope>=1.10.0; extra == "ai"
|
|
32
|
+
Requires-Dist: openai>=1.0.0; extra == "ai"
|
|
33
|
+
Requires-Dist: anthropic>=0.3.0; extra == "ai"
|
|
34
|
+
Provides-Extra: test
|
|
35
|
+
Requires-Dist: pytest>=8.0.0; extra == "test"
|
|
36
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
|
|
37
|
+
Requires-Dist: allure-pytest>=2.13.0; extra == "test"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
41
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
42
|
+
Requires-Dist: build>=0.10.0; extra == "dev"
|
|
43
|
+
Provides-Extra: ios
|
|
44
|
+
Requires-Dist: tidevice>=0.11.0; extra == "ios"
|
|
45
|
+
Requires-Dist: facebook-wda>=1.4.0; extra == "ios"
|
|
46
|
+
Provides-Extra: h5
|
|
47
|
+
Requires-Dist: Appium-Python-Client>=3.0.0; extra == "h5"
|
|
48
|
+
Requires-Dist: selenium>=4.0.0; extra == "h5"
|
|
49
|
+
Provides-Extra: all
|
|
50
|
+
Requires-Dist: dashscope>=1.10.0; extra == "all"
|
|
51
|
+
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
52
|
+
Requires-Dist: anthropic>=0.3.0; extra == "all"
|
|
53
|
+
Requires-Dist: Appium-Python-Client>=3.0.0; extra == "all"
|
|
54
|
+
Requires-Dist: selenium>=4.0.0; extra == "all"
|
|
55
|
+
Requires-Dist: pytest>=8.0.0; extra == "all"
|
|
56
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
|
|
57
|
+
Requires-Dist: allure-pytest>=2.13.0; extra == "all"
|
|
58
|
+
Dynamic: author
|
|
59
|
+
Dynamic: author-email
|
|
60
|
+
Dynamic: classifier
|
|
61
|
+
Dynamic: description
|
|
62
|
+
Dynamic: description-content-type
|
|
63
|
+
Dynamic: home-page
|
|
64
|
+
Dynamic: keywords
|
|
65
|
+
Dynamic: license-file
|
|
66
|
+
Dynamic: project-url
|
|
67
|
+
Dynamic: provides-extra
|
|
68
|
+
Dynamic: requires-dist
|
|
69
|
+
Dynamic: requires-python
|
|
70
|
+
Dynamic: summary
|
|
71
|
+
|
|
72
|
+
# 📱 Mobile MCP AI
|
|
73
|
+
|
|
74
|
+
> 让 Cursor 直接控制手机的 MCP 工具
|
|
75
|
+
|
|
76
|
+
<div align="center">
|
|
77
|
+
|
|
78
|
+
[](https://pypi.org/project/mobile-mcp-ai/)
|
|
79
|
+
[](https://www.python.org/)
|
|
80
|
+
[](LICENSE)
|
|
81
|
+
[](https://developer.android.com/)
|
|
82
|
+
[](docs/iOS_SETUP_GUIDE.md)
|
|
83
|
+
|
|
84
|
+
**⭐ 觉得有用?给个 Star 支持一下!**
|
|
85
|
+
|
|
86
|
+
**📱 支持 Android 和 iOS 双平台**
|
|
87
|
+
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 🎬 演示
|
|
93
|
+
|
|
94
|
+
<div align="center">
|
|
95
|
+
|
|
96
|
+

|
|
97
|
+
|
|
98
|
+
*[查看高清视频 →](docs/videos/demo.mp4)*
|
|
99
|
+
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## ✨ 核心特性
|
|
105
|
+
|
|
106
|
+
<table>
|
|
107
|
+
<tr>
|
|
108
|
+
<td width="50%">
|
|
109
|
+
|
|
110
|
+
### 🧠 AI 原生驱动
|
|
111
|
+
|
|
112
|
+
基于 MCP 协议与 Cursor AI 深度集成,自然语言直接操控手机,告别繁琐的脚本编写
|
|
113
|
+
|
|
114
|
+
</td>
|
|
115
|
+
<td width="50%">
|
|
116
|
+
|
|
117
|
+
### 👁️ 视觉智能识别
|
|
118
|
+
|
|
119
|
+
Cursor AI 自动分析截图,精准定位 UI 元素,游戏、原生应用通吃
|
|
120
|
+
|
|
121
|
+
</td>
|
|
122
|
+
</tr>
|
|
123
|
+
<tr>
|
|
124
|
+
<td width="50%">
|
|
125
|
+
|
|
126
|
+
### ⚡ 零配置启动
|
|
127
|
+
|
|
128
|
+
`pip install` 一行命令,开箱即用,无需额外 AI 密钥
|
|
129
|
+
|
|
130
|
+
</td>
|
|
131
|
+
<td width="50%">
|
|
132
|
+
|
|
133
|
+
### 🔄 一键生成脚本
|
|
134
|
+
|
|
135
|
+
操作即录制,自动生成可复用的 pytest 测试脚本
|
|
136
|
+
|
|
137
|
+
</td>
|
|
138
|
+
</tr>
|
|
139
|
+
<tr>
|
|
140
|
+
<td width="50%">
|
|
141
|
+
|
|
142
|
+
### 🎯 双模式定位
|
|
143
|
+
|
|
144
|
+
元素树 + 视觉坐标双引擎,普通 App 秒定位,游戏场景不迷路
|
|
145
|
+
|
|
146
|
+
</td>
|
|
147
|
+
<td width="50%">
|
|
148
|
+
|
|
149
|
+
### 🛡️ 智能验证机制
|
|
150
|
+
|
|
151
|
+
按键操作自动验证生效,告别"假成功"
|
|
152
|
+
|
|
153
|
+
</td>
|
|
154
|
+
</tr>
|
|
155
|
+
</table>
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 📱 平台支持
|
|
160
|
+
|
|
161
|
+
| 平台 | 支持状态 | 系统要求 | 配置指南 |
|
|
162
|
+
|:---:|:---:|:---:|:---:|
|
|
163
|
+
| **Android** | ✅ 完整支持 | Windows / macOS / Linux | 开箱即用 |
|
|
164
|
+
| **iOS** | ✅ 完整支持 | macOS(必须) | [iOS 配置指南 →](docs/iOS_SETUP_GUIDE.md) |
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 📦 安装
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
pip install mobile-mcp-ai
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**升级到最新版**
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
pip install --upgrade mobile-mcp-ai
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**查看当前版本**
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
pip show mobile-mcp-ai
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 📱 连接设备
|
|
189
|
+
|
|
190
|
+
### Android 设备
|
|
191
|
+
|
|
192
|
+
确保手机已开启 USB 调试,然后:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
adb devices
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
看到设备列表即表示连接成功。
|
|
199
|
+
|
|
200
|
+
### iOS 设备(macOS)
|
|
201
|
+
|
|
202
|
+
iOS 自动化需要额外配置 WebDriverAgent,请参考:
|
|
203
|
+
|
|
204
|
+
📖 **[iOS 配置指南 →](docs/iOS_SETUP_GUIDE.md)**
|
|
205
|
+
|
|
206
|
+
快速检查连接:
|
|
207
|
+
```bash
|
|
208
|
+
tidevice list
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 🎯 新用户快速入门
|
|
214
|
+
|
|
215
|
+
### 第一步:安装
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
pip install mobile-mcp-ai
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 第二步:连接设备
|
|
222
|
+
|
|
223
|
+
**Android 用户:**
|
|
224
|
+
```bash
|
|
225
|
+
# 开启手机 USB 调试,连接电脑
|
|
226
|
+
adb devices
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**iOS 用户:**
|
|
230
|
+
```bash
|
|
231
|
+
# 安装依赖
|
|
232
|
+
pip install tidevice facebook-wda
|
|
233
|
+
brew install libimobiledevice
|
|
234
|
+
|
|
235
|
+
# 检查连接
|
|
236
|
+
tidevice list
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
> 📖 iOS 需要额外配置 WebDriverAgent,详见 **[iOS 配置指南](docs/iOS_SETUP_GUIDE.md)**
|
|
240
|
+
|
|
241
|
+
### 第三步:配置 Cursor
|
|
242
|
+
|
|
243
|
+
编辑 `~/.cursor/mcp.json`:
|
|
244
|
+
|
|
245
|
+
```json
|
|
246
|
+
{
|
|
247
|
+
"mcpServers": {
|
|
248
|
+
"mobile-automation": {
|
|
249
|
+
"command": "mobile-mcp"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
> 💡 提示:会自动检测 Android/iOS 设备,无需额外配置
|
|
256
|
+
|
|
257
|
+
### 第四步:重启 Cursor
|
|
258
|
+
|
|
259
|
+
保存配置后,**重启 Cursor** 使配置生效。
|
|
260
|
+
|
|
261
|
+
### 第五步:开始使用
|
|
262
|
+
|
|
263
|
+
在 Cursor 中输入:
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
@MCP 检查设备连接
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
@MCP 截图看看当前页面
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
@MCP 点击"登录"按钮
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## ⚙️ 高级配置
|
|
280
|
+
|
|
281
|
+
### 方式一:pip 安装后配置(推荐)
|
|
282
|
+
|
|
283
|
+
先安装:`pip install mobile-mcp-ai`
|
|
284
|
+
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"mcpServers": {
|
|
288
|
+
"mobile-automation": {
|
|
289
|
+
"command": "mobile-mcp"
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### 方式二:源码方式配置
|
|
296
|
+
|
|
297
|
+
如果你是从源码运行:
|
|
298
|
+
|
|
299
|
+
**Android 配置:**
|
|
300
|
+
|
|
301
|
+
```json
|
|
302
|
+
{
|
|
303
|
+
"mcpServers": {
|
|
304
|
+
"mobile-automation": {
|
|
305
|
+
"command": "/path/to/your/venv/bin/python",
|
|
306
|
+
"args": ["-m", "mobile_mcp.mcp_tools.mcp_server"],
|
|
307
|
+
"cwd": "/path/to/mobile_mcp",
|
|
308
|
+
"env": {
|
|
309
|
+
"MOBILE_PLATFORM": "android"
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**iOS 配置:**
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{
|
|
320
|
+
"mcpServers": {
|
|
321
|
+
"mobile-automation": {
|
|
322
|
+
"command": "/path/to/your/venv/bin/python",
|
|
323
|
+
"args": ["-m", "mobile_mcp.mcp_tools.mcp_server"],
|
|
324
|
+
"cwd": "/path/to/mobile_mcp",
|
|
325
|
+
"env": {
|
|
326
|
+
"MOBILE_PLATFORM": "ios"
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
> ⚠️ 请将 `/path/to/` 替换为你的实际路径
|
|
334
|
+
>
|
|
335
|
+
> 📖 iOS 需要先配置 WebDriverAgent,详见 **[iOS 配置指南](docs/iOS_SETUP_GUIDE.md)**
|
|
336
|
+
|
|
337
|
+
保存后**重启 Cursor**。
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## 🚀 使用示例
|
|
342
|
+
|
|
343
|
+
在 Cursor 中直接对话:
|
|
344
|
+
|
|
345
|
+
**基础操作**
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
@MCP 列出当前页面所有元素
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
@MCP 点击"登录"按钮
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
```
|
|
356
|
+
@MCP 在用户名输入框输入 test123
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
**应用控制**
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
@MCP 启动微信
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
@MCP 打开抖音,向上滑动 3 次
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
@MCP 列出手机上所有已安装的应用
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**截图分析**
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
@MCP 截图看看当前页面
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
```
|
|
380
|
+
@MCP 截图,然后点击页面上的搜索图标
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
**测试脚本生成**
|
|
384
|
+
|
|
385
|
+
```
|
|
386
|
+
@MCP 帮我测试登录流程:输入用户名密码,点击登录
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
```
|
|
390
|
+
@MCP 把刚才的操作生成 pytest 测试脚本
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**组合操作**
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
@MCP 打开设置,找到 WLAN,点进去截图
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
```
|
|
400
|
+
@MCP 打开微信,点击发现,再点击朋友圈
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
404
|
+
|
|
405
|
+
## 🛠️ 工具列表
|
|
406
|
+
|
|
407
|
+
| 类别 | 工具 | 说明 |
|
|
408
|
+
|:---:|------|------|
|
|
409
|
+
| 📋 | `mobile_list_elements` | 列出页面元素 |
|
|
410
|
+
| 📸 | `mobile_take_screenshot` | 截图 |
|
|
411
|
+
| 📐 | `mobile_get_screen_size` | 屏幕尺寸 |
|
|
412
|
+
| 👆 | `mobile_click_by_text` | 文本点击 |
|
|
413
|
+
| 👆 | `mobile_click_by_id` | ID 点击 |
|
|
414
|
+
| 👆 | `mobile_click_at_coords` | 坐标点击 |
|
|
415
|
+
| ⌨️ | `mobile_input_text_by_id` | ID 输入 |
|
|
416
|
+
| ⌨️ | `mobile_input_at_coords` | 坐标输入 |
|
|
417
|
+
| 👆 | `mobile_swipe` | 滑动 |
|
|
418
|
+
| ⌨️ | `mobile_press_key` | 按键 |
|
|
419
|
+
| ⏱️ | `mobile_wait` | 等待 |
|
|
420
|
+
| 📦 | `mobile_launch_app` | 启动应用 |
|
|
421
|
+
| 📦 | `mobile_terminate_app` | 终止应用 |
|
|
422
|
+
| 📦 | `mobile_list_apps` | 列出应用 |
|
|
423
|
+
| 📱 | `mobile_list_devices` | 列出设备 |
|
|
424
|
+
| 🔌 | `mobile_check_connection` | 检查连接 |
|
|
425
|
+
| ✅ | `mobile_assert_text` | 断言文本 |
|
|
426
|
+
| 📜 | `mobile_get_operation_history` | 操作历史 |
|
|
427
|
+
| 🗑️ | `mobile_clear_operation_history` | 清空历史 |
|
|
428
|
+
| 📝 | `mobile_generate_test_script` | 生成测试脚本 |
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## 📞 联系作者
|
|
433
|
+
|
|
434
|
+
<div align="center">
|
|
435
|
+
|
|
436
|
+
<img src="docs/images/wechat-qr.jpg" alt="微信" width="250"/>
|
|
437
|
+
|
|
438
|
+
*添加微信交流(备注:mobile-mcp)*
|
|
439
|
+
|
|
440
|
+
</div>
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## 📄 License
|
|
445
|
+
|
|
446
|
+
Apache 2.0
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
<div align="center">
|
|
451
|
+
|
|
452
|
+
[Gitee](https://gitee.com/chang-xinping/mobile-automation-mcp-service) · [GitHub](https://github.com/test111ddff-hash/mobile-mcp-ai) · [PyPI](https://pypi.org/project/mobile-mcp-ai/)
|
|
453
|
+
|
|
454
|
+
**🚀 让移动端测试更简单**
|
|
455
|
+
|
|
456
|
+
</div>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
mobile_mcp/__init__.py,sha256=sQJZTL_sxQFzmcS7jOtS2AHCfUySz40vhX96N6u1qy4,816
|
|
2
|
+
mobile_mcp/config.py,sha256=yaFLAV4bc2wX0GQPtZDo7OYF9E88tXV-av41fQsJwK4,4480
|
|
3
|
+
mobile_mcp/core/__init__.py,sha256=ndMy-cLAIsQDG5op7gM_AIplycqZSZPWEkec1pEhvEY,170
|
|
4
|
+
mobile_mcp/core/basic_tools_lite.py,sha256=BzPT180GPQjhTNHaAKl46m1jvCM5KoQIPgySGdNSD30,149836
|
|
5
|
+
mobile_mcp/core/device_manager.py,sha256=PX3-B5bJFnKNt6C8fT7FSY8JwD-ngZ3toF88bcOV9qA,8766
|
|
6
|
+
mobile_mcp/core/dynamic_config.py,sha256=Ja1n1pfb0HspGByqk2_A472mYVniKmGtNEWyjUjmgK8,9811
|
|
7
|
+
mobile_mcp/core/ios_client_wda.py,sha256=Nq9WxevhTWpVpolM-Ymp-b0nUQV3tXLFszmJHbDC4wA,18770
|
|
8
|
+
mobile_mcp/core/ios_device_manager_wda.py,sha256=A44glqI-24un7qST-E3w6BQD8mV92YVUbxy4rLlTScY,11264
|
|
9
|
+
mobile_mcp/core/mobile_client.py,sha256=bno3HvU-QSAC3G4TnoFngTxqXeu-ZP5rGlEWdWh8jOo,62570
|
|
10
|
+
mobile_mcp/core/template_matcher.py,sha256=tv8RU6zdeDobqphaP4Y8sicb1esg3gcQlZae1tNyitM,14559
|
|
11
|
+
mobile_mcp/core/templates/close_buttons/auto_x_0112_151217.png,sha256=s7tBVaYLBApNSEXjwi5kX8GXwUqgbNyNVEhXYjN9nd4,27373
|
|
12
|
+
mobile_mcp/core/templates/close_buttons/auto_x_0112_152037.png,sha256=s7tBVaYLBApNSEXjwi5kX8GXwUqgbNyNVEhXYjN9nd4,27373
|
|
13
|
+
mobile_mcp/core/templates/close_buttons/auto_x_0112_152840.png,sha256=s7tBVaYLBApNSEXjwi5kX8GXwUqgbNyNVEhXYjN9nd4,27373
|
|
14
|
+
mobile_mcp/core/templates/close_buttons/auto_x_0112_153256.png,sha256=s7tBVaYLBApNSEXjwi5kX8GXwUqgbNyNVEhXYjN9nd4,27373
|
|
15
|
+
mobile_mcp/core/templates/close_buttons/auto_x_0112_154847.png,sha256=apPPFft1LLP_4KppbRGd2zOl3nfSGYFqzYeyzncaduk,27679
|
|
16
|
+
mobile_mcp/core/templates/close_buttons/gray_x_stock_ad.png,sha256=5D3pOa74Zj21v7I-zpYo2Pe4ZC8PB-N3_yo_LFNnlLw,4504
|
|
17
|
+
mobile_mcp/core/utils/__init__.py,sha256=RhMMsPszmEn8Q8GoNufypVSHJxyM9lio9U6jjpnuoPI,378
|
|
18
|
+
mobile_mcp/core/utils/logger.py,sha256=XXQAHUwT1jc70pq_tYFmL6f_nKrFlYm3hcgl-5RYRg0,3402
|
|
19
|
+
mobile_mcp/core/utils/operation_history_manager.py,sha256=gi8S8HJAMqvkUrY7_-kVbko3Xt7c4GAUziEujRd-N-Y,4792
|
|
20
|
+
mobile_mcp/core/utils/smart_wait.py,sha256=PvKXImfN9Irru3bQJUjf4FLGn8LjY2VLzUNEl-i7xLE,8601
|
|
21
|
+
mobile_mcp/mcp_tools/__init__.py,sha256=xkro8Rwqv_55YlVyhh-3DgRFSsLE3h1r31VIb3bpM6E,143
|
|
22
|
+
mobile_mcp/mcp_tools/mcp_server.py,sha256=jN4ooXb4yzrRhC_4POdLtxppWCAfBjVUVoov8RMLfyw,46054
|
|
23
|
+
mobile_mcp/utils/__init__.py,sha256=8EH0i7UGtx1y_j_GEgdN-cZdWn2sRtZSEOLlNF9HRnY,158
|
|
24
|
+
mobile_mcp/utils/logger.py,sha256=Sqq2Nr0Y4p03erqcrbYKVPCGiFaNGHMcE_JwCkeOfU4,3626
|
|
25
|
+
mobile_mcp/utils/xml_formatter.py,sha256=uwTRb3vLbqhT8O-udzWT7s7LsV-DyDUz2DkofD3hXOE,4556
|
|
26
|
+
mobile_mcp/utils/xml_parser.py,sha256=QhL8CWbdmNDzmBLjtx6mEnjHgMFZzJeHpCL15qfXSpI,3926
|
|
27
|
+
mobile_mcp_ai-2.5.3.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
28
|
+
mobile_mcp_ai-2.5.3.dist-info/METADATA,sha256=3SFUMmLse8MPlU8ihd1JcvTGNShkp1JgCbtAs8nS_eM,9745
|
|
29
|
+
mobile_mcp_ai-2.5.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
30
|
+
mobile_mcp_ai-2.5.3.dist-info/entry_points.txt,sha256=KB_FglozgPHBprSM1vFbIzGyheFuHFmGanscRdMJ_8A,68
|
|
31
|
+
mobile_mcp_ai-2.5.3.dist-info/top_level.txt,sha256=lLm6YpbTv855Lbh8BIA0rPxhybIrvYUzMEk9OErHT94,11
|
|
32
|
+
mobile_mcp_ai-2.5.3.dist-info/RECORD,,
|
mobile_mcp/core/ai/__init__.py
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
AI分析器 - 智能兜底,分析候选元素
|
|
5
|
-
"""
|
|
6
|
-
import sys
|
|
7
|
-
import json
|
|
8
|
-
from typing import Dict, List, Optional
|
|
9
|
-
from .ai_config import ai_config
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class AIAnalyzer:
|
|
13
|
-
"""AI分析器 - 用于智能兜底"""
|
|
14
|
-
|
|
15
|
-
def __init__(self):
|
|
16
|
-
"""初始化AI分析器"""
|
|
17
|
-
self.config = ai_config
|
|
18
|
-
|
|
19
|
-
async def analyze_candidates(self, query: str, candidates: List[Dict], context: str = "") -> Optional[Dict]:
|
|
20
|
-
"""
|
|
21
|
-
分析候选元素,选择最佳匹配
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
query: 用户查询
|
|
25
|
-
candidates: 候选元素列表
|
|
26
|
-
context: 上下文信息(可选)
|
|
27
|
-
|
|
28
|
-
Returns:
|
|
29
|
-
最佳匹配的元素信息
|
|
30
|
-
"""
|
|
31
|
-
if not self.config.is_configured():
|
|
32
|
-
print(" ⚠️ AI未配置,跳过AI分析", file=sys.stderr)
|
|
33
|
-
return None
|
|
34
|
-
|
|
35
|
-
if not candidates:
|
|
36
|
-
print(" ⚠️ 没有候选元素,跳过AI分析", file=sys.stderr)
|
|
37
|
-
return None
|
|
38
|
-
|
|
39
|
-
try:
|
|
40
|
-
import httpx
|
|
41
|
-
|
|
42
|
-
# 构建提示词
|
|
43
|
-
prompt = self._build_prompt(query, candidates, context)
|
|
44
|
-
|
|
45
|
-
print(f" 🤖 调用AI分析(模型: {self.config.model})...", file=sys.stderr)
|
|
46
|
-
|
|
47
|
-
# 调用通义千问API
|
|
48
|
-
async with httpx.AsyncClient(timeout=self.config.timeout) as client:
|
|
49
|
-
response = await client.post(
|
|
50
|
-
f"{self.config.api_base}/chat/completions",
|
|
51
|
-
headers={
|
|
52
|
-
"Authorization": f"Bearer {self.config.api_key}",
|
|
53
|
-
"Content-Type": "application/json"
|
|
54
|
-
},
|
|
55
|
-
json={
|
|
56
|
-
"model": self.config.model,
|
|
57
|
-
"messages": [
|
|
58
|
-
{
|
|
59
|
-
"role": "system",
|
|
60
|
-
"content": "你是一个专业的移动端UI元素分析助手。请根据用户查询和候选元素,选择最匹配的元素,并返回JSON格式的结果。"
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
"role": "user",
|
|
64
|
-
"content": prompt
|
|
65
|
-
}
|
|
66
|
-
],
|
|
67
|
-
"temperature": 0.1, # 低温度,更确定性
|
|
68
|
-
"response_format": {"type": "json_object"} # 强制返回JSON
|
|
69
|
-
}
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
if response.status_code != 200:
|
|
73
|
-
print(f" ❌ AI调用失败: HTTP {response.status_code}", file=sys.stderr)
|
|
74
|
-
print(f" {response.text}", file=sys.stderr)
|
|
75
|
-
return None
|
|
76
|
-
|
|
77
|
-
result = response.json()
|
|
78
|
-
ai_response = result['choices'][0]['message']['content']
|
|
79
|
-
|
|
80
|
-
# 解析AI返回的JSON
|
|
81
|
-
ai_result = json.loads(ai_response)
|
|
82
|
-
|
|
83
|
-
if not ai_result.get('selected_index'):
|
|
84
|
-
print(f" ⚠️ AI未能选择元素", file=sys.stderr)
|
|
85
|
-
return None
|
|
86
|
-
|
|
87
|
-
selected_index = ai_result['selected_index'] - 1 # 转换为0-based索引
|
|
88
|
-
|
|
89
|
-
if selected_index < 0 or selected_index >= len(candidates):
|
|
90
|
-
print(f" ⚠️ AI返回的索引无效: {selected_index + 1}", file=sys.stderr)
|
|
91
|
-
return None
|
|
92
|
-
|
|
93
|
-
selected = candidates[selected_index]
|
|
94
|
-
confidence = ai_result.get('confidence', 85)
|
|
95
|
-
reason = ai_result.get('reason', '未提供原因')
|
|
96
|
-
|
|
97
|
-
print(f" ✅ AI选择: 候选{selected_index + 1}/{len(candidates)}", file=sys.stderr)
|
|
98
|
-
print(f" 元素: {selected.get('text') or selected.get('content_desc') or selected.get('class_name')}", file=sys.stderr)
|
|
99
|
-
print(f" 置信度: {confidence}%", file=sys.stderr)
|
|
100
|
-
print(f" 理由: {reason}", file=sys.stderr)
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
'element': selected.get('text') or selected.get('content_desc') or query,
|
|
104
|
-
'ref': self._get_ref(selected),
|
|
105
|
-
'confidence': confidence,
|
|
106
|
-
'method': 'ai_analysis',
|
|
107
|
-
'reason': reason
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
except Exception as e:
|
|
111
|
-
print(f" ❌ AI分析异常: {e}", file=sys.stderr)
|
|
112
|
-
import traceback
|
|
113
|
-
traceback.print_exc()
|
|
114
|
-
return None
|
|
115
|
-
|
|
116
|
-
def _build_prompt(self, query: str, candidates: List[Dict], context: str) -> str:
|
|
117
|
-
"""构建AI提示词"""
|
|
118
|
-
# 格式化候选元素
|
|
119
|
-
candidates_text = []
|
|
120
|
-
for i, elem in enumerate(candidates, 1):
|
|
121
|
-
text = elem.get('text', '')
|
|
122
|
-
desc = elem.get('content_desc', '')
|
|
123
|
-
resource_id = elem.get('resource_id', '')
|
|
124
|
-
class_name = elem.get('class_name', '')
|
|
125
|
-
bounds = elem.get('bounds', '')
|
|
126
|
-
clickable = elem.get('clickable', False)
|
|
127
|
-
focusable = elem.get('focusable', False)
|
|
128
|
-
|
|
129
|
-
# 计算位置
|
|
130
|
-
position = "未知"
|
|
131
|
-
if bounds:
|
|
132
|
-
try:
|
|
133
|
-
# bounds格式: "[x1,y1][x2,y2]"
|
|
134
|
-
coords = bounds.replace('[', '').replace(']', ',').split(',')
|
|
135
|
-
y1 = int(coords[1])
|
|
136
|
-
if y1 < 400:
|
|
137
|
-
position = "顶部"
|
|
138
|
-
elif y1 < 800:
|
|
139
|
-
position = "中部"
|
|
140
|
-
else:
|
|
141
|
-
position = "底部"
|
|
142
|
-
except:
|
|
143
|
-
pass
|
|
144
|
-
|
|
145
|
-
parts = [f"候选{i}:"]
|
|
146
|
-
if text:
|
|
147
|
-
parts.append(f"文本=\"{text[:50]}\"")
|
|
148
|
-
if desc:
|
|
149
|
-
parts.append(f"描述=\"{desc[:50]}\"")
|
|
150
|
-
if resource_id:
|
|
151
|
-
parts.append(f"ID={resource_id}")
|
|
152
|
-
parts.append(f"类型={class_name}")
|
|
153
|
-
parts.append(f"位置={position}")
|
|
154
|
-
if clickable:
|
|
155
|
-
parts.append("可点击")
|
|
156
|
-
if focusable:
|
|
157
|
-
parts.append("可聚焦")
|
|
158
|
-
|
|
159
|
-
candidates_text.append(" | ".join(parts))
|
|
160
|
-
|
|
161
|
-
prompt = f"""
|
|
162
|
-
用户查询: "{query}"
|
|
163
|
-
|
|
164
|
-
页面上有以下候选元素:
|
|
165
|
-
{chr(10).join(candidates_text)}
|
|
166
|
-
|
|
167
|
-
{f"上下文信息: {context}" if context else ""}
|
|
168
|
-
|
|
169
|
-
请分析哪个元素最匹配用户查询,并返回JSON格式:
|
|
170
|
-
{{
|
|
171
|
-
"selected_index": <1到{len(candidates)}的数字>,
|
|
172
|
-
"confidence": <置信度0-100>,
|
|
173
|
-
"reason": "<选择理由>"
|
|
174
|
-
}}
|
|
175
|
-
|
|
176
|
-
分析要点:
|
|
177
|
-
1. 优先匹配文本/描述的语义
|
|
178
|
-
2. 考虑元素类型是否合理(如输入框应该是EditText)
|
|
179
|
-
3. 考虑元素位置(如"顶部按钮"、"底部输入框")
|
|
180
|
-
4. 考虑用户意图(如"点击"需要可点击元素)
|
|
181
|
-
"""
|
|
182
|
-
return prompt
|
|
183
|
-
|
|
184
|
-
def _get_ref(self, element: Dict) -> str:
|
|
185
|
-
"""获取元素引用"""
|
|
186
|
-
# 优先级: resource_id > content_desc > text > bounds
|
|
187
|
-
return (
|
|
188
|
-
element.get('resource_id') or
|
|
189
|
-
element.get('content_desc') or
|
|
190
|
-
element.get('text') or
|
|
191
|
-
element.get('bounds', '')
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
# 全局实例
|
|
196
|
-
ai_analyzer = AIAnalyzer()
|
|
197
|
-
|