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
mobile_mcp/mcp/mcp_server.py
DELETED
|
@@ -1,1126 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Mobile MCP Server - 统一版本(合并了基础工具和智能工具)
|
|
5
|
-
|
|
6
|
-
架构说明:
|
|
7
|
-
- 基础工具:不需要 AI 密钥,提供精确的元素操作(设备管理、应用管理、高级交互等)
|
|
8
|
-
- 智能工具:需要 AI 密钥(可选),提供自然语言定位
|
|
9
|
-
|
|
10
|
-
用户可以选择:
|
|
11
|
-
1. 只用基础工具 → 不需要配置 AI
|
|
12
|
-
2. 启用智能功能 → 需要配置 AI(创建 .env 文件)
|
|
13
|
-
|
|
14
|
-
v2.2.0: 合并了两个 MCP Server,移除了 browser_mcp 依赖
|
|
15
|
-
"""
|
|
16
|
-
|
|
17
|
-
import asyncio
|
|
18
|
-
import json
|
|
19
|
-
import os
|
|
20
|
-
import sys
|
|
21
|
-
from pathlib import Path
|
|
22
|
-
from typing import Optional
|
|
23
|
-
|
|
24
|
-
# 添加项目路径
|
|
25
|
-
mobile_mcp_dir = Path(__file__).parent.parent
|
|
26
|
-
project_root = mobile_mcp_dir.parent.parent
|
|
27
|
-
backend_dir = project_root / "backend"
|
|
28
|
-
|
|
29
|
-
# 确保系统的 mcp 包优先导入(避免与 mobile_mcp.mcp 冲突)
|
|
30
|
-
# 将 site-packages 路径插入到最前面
|
|
31
|
-
import site
|
|
32
|
-
for site_dir in site.getsitepackages():
|
|
33
|
-
if (Path(site_dir) / 'mcp').exists():
|
|
34
|
-
sys.path.insert(0, str(site_dir))
|
|
35
|
-
break
|
|
36
|
-
|
|
37
|
-
sys.path.insert(0, str(project_root))
|
|
38
|
-
sys.path.insert(0, str(backend_dir))
|
|
39
|
-
|
|
40
|
-
# 检测运行模式:full(完整版) 或 simple(简化版)
|
|
41
|
-
SERVER_MODE = os.getenv("MOBILE_MCP_MODE", "full").lower()
|
|
42
|
-
|
|
43
|
-
# 导入系统的 mcp 包(现在应该能正确导入)
|
|
44
|
-
from mcp.types import Tool, TextContent
|
|
45
|
-
from mcp.server import Server
|
|
46
|
-
from mcp.server.stdio import stdio_server
|
|
47
|
-
|
|
48
|
-
from mobile_mcp.core.mobile_client import MobileClient
|
|
49
|
-
from mobile_mcp.core.basic_tools import BasicMobileTools
|
|
50
|
-
from mobile_mcp.core.smart_tools import SmartMobileTools
|
|
51
|
-
from mobile_mcp.core.dynamic_config import DynamicConfig
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class MobileMCPServer:
|
|
55
|
-
"""简化的 Mobile MCP Server"""
|
|
56
|
-
|
|
57
|
-
def __init__(self):
|
|
58
|
-
"""初始化 MCP Server"""
|
|
59
|
-
self.client: Optional[MobileClient] = None
|
|
60
|
-
self.basic_tools: Optional[BasicMobileTools] = None
|
|
61
|
-
self.smart_tools: Optional[SmartMobileTools] = None
|
|
62
|
-
self._initialized = False
|
|
63
|
-
|
|
64
|
-
@staticmethod
|
|
65
|
-
def format_response(result) -> str:
|
|
66
|
-
"""
|
|
67
|
-
统一格式化返回值为JSON字符串
|
|
68
|
-
|
|
69
|
-
Args:
|
|
70
|
-
result: 可以是字典、列表或字符串
|
|
71
|
-
|
|
72
|
-
Returns:
|
|
73
|
-
格式化后的字符串(字典和列表会转为JSON)
|
|
74
|
-
"""
|
|
75
|
-
if isinstance(result, (dict, list)):
|
|
76
|
-
return json.dumps(result, ensure_ascii=False, indent=2)
|
|
77
|
-
return str(result)
|
|
78
|
-
|
|
79
|
-
async def initialize(self):
|
|
80
|
-
"""延迟初始化设备连接"""
|
|
81
|
-
if not self._initialized:
|
|
82
|
-
# 初始化移动客户端
|
|
83
|
-
self.client = MobileClient()
|
|
84
|
-
|
|
85
|
-
# 初始化基础工具(总是可用)
|
|
86
|
-
self.basic_tools = BasicMobileTools(self.client)
|
|
87
|
-
|
|
88
|
-
# 初始化智能工具(检查 AI 可用性)
|
|
89
|
-
self.smart_tools = SmartMobileTools(self.client)
|
|
90
|
-
|
|
91
|
-
ai_status = self.smart_tools.get_ai_status()
|
|
92
|
-
print(f"\n{ai_status['message']}\n", file=sys.stderr)
|
|
93
|
-
|
|
94
|
-
self._initialized = True
|
|
95
|
-
|
|
96
|
-
def get_tools(self):
|
|
97
|
-
"""注册 MCP 工具"""
|
|
98
|
-
tools = []
|
|
99
|
-
|
|
100
|
-
# ==================== 基础工具(不需要 AI)====================
|
|
101
|
-
|
|
102
|
-
tools.extend([
|
|
103
|
-
Tool(
|
|
104
|
-
name="mobile_list_elements",
|
|
105
|
-
description="📋 列出页面所有可交互元素(不需要 AI)。返回 resource_id, text, bounds 等信息,供后续精确操作使用。",
|
|
106
|
-
inputSchema={
|
|
107
|
-
"type": "object",
|
|
108
|
-
"properties": {},
|
|
109
|
-
"required": []
|
|
110
|
-
}
|
|
111
|
-
),
|
|
112
|
-
Tool(
|
|
113
|
-
name="mobile_click_by_id",
|
|
114
|
-
description="👆 通过 resource-id 点击元素(不需要 AI)。精确可靠的点击方式。先用 mobile_list_elements 查找元素 ID。\n\n"
|
|
115
|
-
"✅ 点击成功后会自动等待 0.3 秒,无需重复点击!\n"
|
|
116
|
-
"💡 提示:如果已经用 mobile_click_by_text 点击成功了,就不需要再用 ID 点击同一个元素。",
|
|
117
|
-
inputSchema={
|
|
118
|
-
"type": "object",
|
|
119
|
-
"properties": {
|
|
120
|
-
"resource_id": {
|
|
121
|
-
"type": "string",
|
|
122
|
-
"description": "元素的 resource-id,如 'com.app:id/search_btn'"
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
"required": ["resource_id"]
|
|
126
|
-
}
|
|
127
|
-
),
|
|
128
|
-
Tool(
|
|
129
|
-
name="mobile_click_by_text",
|
|
130
|
-
description="👆 通过文本内容点击元素(不需要 AI)。适合文本完全匹配的场景。\n\n"
|
|
131
|
-
"✅ 点击成功后会自动等待 0.3 秒,无需重复点击!\n"
|
|
132
|
-
"⚠️ 如果需要确认是否成功,可以用 mobile_list_elements 查看页面变化,但不要重复点击同一个按钮。",
|
|
133
|
-
inputSchema={
|
|
134
|
-
"type": "object",
|
|
135
|
-
"properties": {
|
|
136
|
-
"text": {
|
|
137
|
-
"type": "string",
|
|
138
|
-
"description": "元素的文本内容(精确匹配),如 '登录'"
|
|
139
|
-
}
|
|
140
|
-
},
|
|
141
|
-
"required": ["text"]
|
|
142
|
-
}
|
|
143
|
-
),
|
|
144
|
-
Tool(
|
|
145
|
-
name="mobile_click_at_coords",
|
|
146
|
-
description="👆 点击指定坐标(不需要 AI)。可以从 mobile_list_elements 获取的 bounds 计算坐标。",
|
|
147
|
-
inputSchema={
|
|
148
|
-
"type": "object",
|
|
149
|
-
"properties": {
|
|
150
|
-
"x": {
|
|
151
|
-
"type": "number",
|
|
152
|
-
"description": "X 坐标(像素)"
|
|
153
|
-
},
|
|
154
|
-
"y": {
|
|
155
|
-
"type": "number",
|
|
156
|
-
"description": "Y 坐标(像素)"
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
"required": ["x", "y"]
|
|
160
|
-
}
|
|
161
|
-
),
|
|
162
|
-
Tool(
|
|
163
|
-
name="mobile_input_text_by_id",
|
|
164
|
-
description="⌨️ 通过 resource-id 在输入框输入文本(不需要 AI)。",
|
|
165
|
-
inputSchema={
|
|
166
|
-
"type": "object",
|
|
167
|
-
"properties": {
|
|
168
|
-
"resource_id": {
|
|
169
|
-
"type": "string",
|
|
170
|
-
"description": "输入框的 resource-id"
|
|
171
|
-
},
|
|
172
|
-
"text": {
|
|
173
|
-
"type": "string",
|
|
174
|
-
"description": "要输入的文本"
|
|
175
|
-
}
|
|
176
|
-
},
|
|
177
|
-
"required": ["resource_id", "text"]
|
|
178
|
-
}
|
|
179
|
-
),
|
|
180
|
-
Tool(
|
|
181
|
-
name="mobile_find_elements_by_class",
|
|
182
|
-
description="🔍 按类名查找元素(不需要 AI)。如查找所有输入框: 'android.widget.EditText'",
|
|
183
|
-
inputSchema={
|
|
184
|
-
"type": "object",
|
|
185
|
-
"properties": {
|
|
186
|
-
"class_name": {
|
|
187
|
-
"type": "string",
|
|
188
|
-
"description": "类名,如 'android.widget.EditText'"
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
"required": ["class_name"]
|
|
192
|
-
}
|
|
193
|
-
),
|
|
194
|
-
Tool(
|
|
195
|
-
name="mobile_wait_for_element",
|
|
196
|
-
description="⏳ 等待元素出现(不需要 AI)。用于等待页面加载完成。",
|
|
197
|
-
inputSchema={
|
|
198
|
-
"type": "object",
|
|
199
|
-
"properties": {
|
|
200
|
-
"resource_id": {
|
|
201
|
-
"type": "string",
|
|
202
|
-
"description": "元素的 resource-id"
|
|
203
|
-
},
|
|
204
|
-
"timeout": {
|
|
205
|
-
"type": "number",
|
|
206
|
-
"description": "超时时间(秒),默认 10秒",
|
|
207
|
-
"default": 10
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
"required": ["resource_id"]
|
|
211
|
-
}
|
|
212
|
-
),
|
|
213
|
-
])
|
|
214
|
-
|
|
215
|
-
# ==================== 完整版独有工具 ====================
|
|
216
|
-
if SERVER_MODE == "full":
|
|
217
|
-
tools.append(
|
|
218
|
-
Tool(
|
|
219
|
-
name="mobile_wait",
|
|
220
|
-
description="⏰ 通用等待工具 - AI 可根据场景灵活控制等待(不需要 AI)。\n\n"
|
|
221
|
-
"🔥 强烈建议在以下场景使用:\n"
|
|
222
|
-
"1. App 启动后:mobile_launch_app() → mobile_wait(seconds=2-3)\n"
|
|
223
|
-
"2. 等待广告:mobile_wait(seconds=3-5)\n"
|
|
224
|
-
"3. 等待搜索结果:mobile_wait(wait_for_text='搜索结果')\n"
|
|
225
|
-
"4. 等待页面加载:mobile_wait(wait_for_id='com.app:id/home')\n\n"
|
|
226
|
-
"⚠️ 不要立即操作刚启动的 App,先等待加载完成!",
|
|
227
|
-
inputSchema={
|
|
228
|
-
"type": "object",
|
|
229
|
-
"properties": {
|
|
230
|
-
"seconds": {
|
|
231
|
-
"type": "number",
|
|
232
|
-
"description": "固定等待时间(秒)。适用于等待广告、动画等"
|
|
233
|
-
},
|
|
234
|
-
"wait_for_text": {
|
|
235
|
-
"type": "string",
|
|
236
|
-
"description": "等待指定文本出现。如 '首页'、'搜索结果'"
|
|
237
|
-
},
|
|
238
|
-
"wait_for_id": {
|
|
239
|
-
"type": "string",
|
|
240
|
-
"description": "等待指定元素ID出现。如 'com.app:id/home'"
|
|
241
|
-
},
|
|
242
|
-
"timeout": {
|
|
243
|
-
"type": "number",
|
|
244
|
-
"description": "等待元素的超时时间(秒),默认 10秒",
|
|
245
|
-
"default": 10
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
"required": []
|
|
249
|
-
}
|
|
250
|
-
)
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
tools.extend([
|
|
254
|
-
Tool(
|
|
255
|
-
name="mobile_take_screenshot",
|
|
256
|
-
description="📸 截取屏幕截图(不需要 AI)。用于 Cursor AI 视觉识别、调试或记录测试过程。",
|
|
257
|
-
inputSchema={
|
|
258
|
-
"type": "object",
|
|
259
|
-
"properties": {
|
|
260
|
-
"description": {
|
|
261
|
-
"type": "string",
|
|
262
|
-
"description": "截图描述(可选),用于生成文件名"
|
|
263
|
-
}
|
|
264
|
-
},
|
|
265
|
-
"required": []
|
|
266
|
-
}
|
|
267
|
-
),
|
|
268
|
-
Tool(
|
|
269
|
-
name="mobile_take_screenshot_region",
|
|
270
|
-
description="📸 截取屏幕指定区域(不需要 AI)。用于局部截图和分析。",
|
|
271
|
-
inputSchema={
|
|
272
|
-
"type": "object",
|
|
273
|
-
"properties": {
|
|
274
|
-
"x1": {
|
|
275
|
-
"type": "number",
|
|
276
|
-
"description": "左上角X坐标"
|
|
277
|
-
},
|
|
278
|
-
"y1": {
|
|
279
|
-
"type": "number",
|
|
280
|
-
"description": "左上角Y坐标"
|
|
281
|
-
},
|
|
282
|
-
"x2": {
|
|
283
|
-
"type": "number",
|
|
284
|
-
"description": "右下角X坐标"
|
|
285
|
-
},
|
|
286
|
-
"y2": {
|
|
287
|
-
"type": "number",
|
|
288
|
-
"description": "右下角Y坐标"
|
|
289
|
-
},
|
|
290
|
-
"description": {
|
|
291
|
-
"type": "string",
|
|
292
|
-
"description": "截图描述(可选)"
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
"required": ["x1", "y1", "x2", "y2"]
|
|
296
|
-
}
|
|
297
|
-
),
|
|
298
|
-
# ==================== 设备管理工具 ====================
|
|
299
|
-
Tool(
|
|
300
|
-
name="mobile_list_devices",
|
|
301
|
-
description="📱 列出所有已连接的Android设备(不需要 AI)。",
|
|
302
|
-
inputSchema={
|
|
303
|
-
"type": "object",
|
|
304
|
-
"properties": {},
|
|
305
|
-
"required": []
|
|
306
|
-
}
|
|
307
|
-
),
|
|
308
|
-
Tool(
|
|
309
|
-
name="mobile_get_screen_size",
|
|
310
|
-
description="📐 获取设备屏幕尺寸(不需要 AI)。",
|
|
311
|
-
inputSchema={
|
|
312
|
-
"type": "object",
|
|
313
|
-
"properties": {},
|
|
314
|
-
"required": []
|
|
315
|
-
}
|
|
316
|
-
),
|
|
317
|
-
Tool(
|
|
318
|
-
name="mobile_get_orientation",
|
|
319
|
-
description="🔄 获取当前屏幕方向(portrait/landscape)。",
|
|
320
|
-
inputSchema={
|
|
321
|
-
"type": "object",
|
|
322
|
-
"properties": {},
|
|
323
|
-
"required": []
|
|
324
|
-
}
|
|
325
|
-
),
|
|
326
|
-
Tool(
|
|
327
|
-
name="mobile_set_orientation",
|
|
328
|
-
description="🔄 设置屏幕方向。",
|
|
329
|
-
inputSchema={
|
|
330
|
-
"type": "object",
|
|
331
|
-
"properties": {
|
|
332
|
-
"orientation": {
|
|
333
|
-
"type": "string",
|
|
334
|
-
"enum": ["portrait", "landscape"],
|
|
335
|
-
"description": "屏幕方向:portrait(竖屏) 或 landscape(横屏)"
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
"required": ["orientation"]
|
|
339
|
-
}
|
|
340
|
-
),
|
|
341
|
-
Tool(
|
|
342
|
-
name="mobile_check_connection",
|
|
343
|
-
description="🔌 检查设备连接状态。返回设备信息和连接状态。",
|
|
344
|
-
inputSchema={
|
|
345
|
-
"type": "object",
|
|
346
|
-
"properties": {},
|
|
347
|
-
"required": []
|
|
348
|
-
}
|
|
349
|
-
),
|
|
350
|
-
Tool(
|
|
351
|
-
name="mobile_reconnect_device",
|
|
352
|
-
description="🔄 重新连接设备。当设备连接断开时使用。",
|
|
353
|
-
inputSchema={
|
|
354
|
-
"type": "object",
|
|
355
|
-
"properties": {},
|
|
356
|
-
"required": []
|
|
357
|
-
}
|
|
358
|
-
),
|
|
359
|
-
# ==================== 应用管理工具 ====================
|
|
360
|
-
Tool(
|
|
361
|
-
name="mobile_list_apps",
|
|
362
|
-
description="📦 列出设备上已安装的应用(不需要 AI)。可按关键词过滤。",
|
|
363
|
-
inputSchema={
|
|
364
|
-
"type": "object",
|
|
365
|
-
"properties": {
|
|
366
|
-
"filter": {
|
|
367
|
-
"type": "string",
|
|
368
|
-
"description": "过滤关键词(可选),如包名的一部分"
|
|
369
|
-
}
|
|
370
|
-
},
|
|
371
|
-
"required": []
|
|
372
|
-
}
|
|
373
|
-
),
|
|
374
|
-
Tool(
|
|
375
|
-
name="mobile_install_app",
|
|
376
|
-
description="📲 安装APK文件(不需要 AI)。",
|
|
377
|
-
inputSchema={
|
|
378
|
-
"type": "object",
|
|
379
|
-
"properties": {
|
|
380
|
-
"apk_path": {
|
|
381
|
-
"type": "string",
|
|
382
|
-
"description": "APK文件路径"
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
"required": ["apk_path"]
|
|
386
|
-
}
|
|
387
|
-
),
|
|
388
|
-
Tool(
|
|
389
|
-
name="mobile_uninstall_app",
|
|
390
|
-
description="🗑️ 卸载应用(不需要 AI)。",
|
|
391
|
-
inputSchema={
|
|
392
|
-
"type": "object",
|
|
393
|
-
"properties": {
|
|
394
|
-
"package_name": {
|
|
395
|
-
"type": "string",
|
|
396
|
-
"description": "应用包名,如 'com.example.app'"
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
"required": ["package_name"]
|
|
400
|
-
}
|
|
401
|
-
),
|
|
402
|
-
Tool(
|
|
403
|
-
name="mobile_terminate_app",
|
|
404
|
-
description="⏹️ 终止应用(强制停止)。",
|
|
405
|
-
inputSchema={
|
|
406
|
-
"type": "object",
|
|
407
|
-
"properties": {
|
|
408
|
-
"package_name": {
|
|
409
|
-
"type": "string",
|
|
410
|
-
"description": "应用包名,如 'com.example.app'"
|
|
411
|
-
}
|
|
412
|
-
},
|
|
413
|
-
"required": ["package_name"]
|
|
414
|
-
}
|
|
415
|
-
),
|
|
416
|
-
Tool(
|
|
417
|
-
name="mobile_get_current_package",
|
|
418
|
-
description="📍 获取当前前台应用的包名。",
|
|
419
|
-
inputSchema={
|
|
420
|
-
"type": "object",
|
|
421
|
-
"properties": {},
|
|
422
|
-
"required": []
|
|
423
|
-
}
|
|
424
|
-
),
|
|
425
|
-
# ==================== 高级交互工具 ====================
|
|
426
|
-
Tool(
|
|
427
|
-
name="mobile_double_click",
|
|
428
|
-
description="👆👆 双击屏幕上的元素(不需要 AI)。",
|
|
429
|
-
inputSchema={
|
|
430
|
-
"type": "object",
|
|
431
|
-
"properties": {
|
|
432
|
-
"x": {
|
|
433
|
-
"type": "number",
|
|
434
|
-
"description": "X坐标"
|
|
435
|
-
},
|
|
436
|
-
"y": {
|
|
437
|
-
"type": "number",
|
|
438
|
-
"description": "Y坐标"
|
|
439
|
-
}
|
|
440
|
-
},
|
|
441
|
-
"required": ["x", "y"]
|
|
442
|
-
}
|
|
443
|
-
),
|
|
444
|
-
Tool(
|
|
445
|
-
name="mobile_long_press",
|
|
446
|
-
description="👆⏱️ 长按屏幕上的元素。",
|
|
447
|
-
inputSchema={
|
|
448
|
-
"type": "object",
|
|
449
|
-
"properties": {
|
|
450
|
-
"x": {
|
|
451
|
-
"type": "number",
|
|
452
|
-
"description": "X坐标"
|
|
453
|
-
},
|
|
454
|
-
"y": {
|
|
455
|
-
"type": "number",
|
|
456
|
-
"description": "Y坐标"
|
|
457
|
-
},
|
|
458
|
-
"duration": {
|
|
459
|
-
"type": "number",
|
|
460
|
-
"default": 1.0,
|
|
461
|
-
"description": "长按时长(秒),默认1秒"
|
|
462
|
-
}
|
|
463
|
-
},
|
|
464
|
-
"required": ["x", "y"]
|
|
465
|
-
}
|
|
466
|
-
),
|
|
467
|
-
Tool(
|
|
468
|
-
name="mobile_open_url",
|
|
469
|
-
description="🌐 在设备浏览器中打开URL。",
|
|
470
|
-
inputSchema={
|
|
471
|
-
"type": "object",
|
|
472
|
-
"properties": {
|
|
473
|
-
"url": {
|
|
474
|
-
"type": "string",
|
|
475
|
-
"description": "要打开的URL,如 'https://example.com'"
|
|
476
|
-
}
|
|
477
|
-
},
|
|
478
|
-
"required": ["url"]
|
|
479
|
-
}
|
|
480
|
-
),
|
|
481
|
-
Tool(
|
|
482
|
-
name="mobile_assert_text",
|
|
483
|
-
description="✅ 断言页面中是否包含指定文本。用于验证操作结果。",
|
|
484
|
-
inputSchema={
|
|
485
|
-
"type": "object",
|
|
486
|
-
"properties": {
|
|
487
|
-
"text": {
|
|
488
|
-
"type": "string",
|
|
489
|
-
"description": "要检查的文本内容"
|
|
490
|
-
}
|
|
491
|
-
},
|
|
492
|
-
"required": ["text"]
|
|
493
|
-
}
|
|
494
|
-
),
|
|
495
|
-
])
|
|
496
|
-
|
|
497
|
-
# ==================== 智能工具(需要 AI,可选)====================
|
|
498
|
-
|
|
499
|
-
tools.extend([
|
|
500
|
-
Tool(
|
|
501
|
-
name="mobile_smart_click",
|
|
502
|
-
description="🤖 智能定位并点击(需要 AI 密钥,可选功能)。使用自然语言描述元素,如'右上角的设置按钮'。\n\n"
|
|
503
|
-
"⚠️ 如未配置 AI,请使用基础工具:mobile_list_elements + mobile_click_by_id",
|
|
504
|
-
inputSchema={
|
|
505
|
-
"type": "object",
|
|
506
|
-
"properties": {
|
|
507
|
-
"description": {
|
|
508
|
-
"type": "string",
|
|
509
|
-
"description": "元素的自然语言描述,如 '顶部搜索框'、'登录按钮'"
|
|
510
|
-
}
|
|
511
|
-
},
|
|
512
|
-
"required": ["description"]
|
|
513
|
-
}
|
|
514
|
-
),
|
|
515
|
-
Tool(
|
|
516
|
-
name="mobile_smart_input",
|
|
517
|
-
description="🤖 智能定位输入框并输入(需要 AI 密钥,可选功能)。使用自然语言描述输入框。\n\n"
|
|
518
|
-
"⚠️ 如未配置 AI,请使用:mobile_input_text_by_id",
|
|
519
|
-
inputSchema={
|
|
520
|
-
"type": "object",
|
|
521
|
-
"properties": {
|
|
522
|
-
"description": {
|
|
523
|
-
"type": "string",
|
|
524
|
-
"description": "输入框的自然语言描述,如 '用户名输入框'"
|
|
525
|
-
},
|
|
526
|
-
"text": {
|
|
527
|
-
"type": "string",
|
|
528
|
-
"description": "要输入的文本"
|
|
529
|
-
}
|
|
530
|
-
},
|
|
531
|
-
"required": ["description", "text"]
|
|
532
|
-
}
|
|
533
|
-
),
|
|
534
|
-
Tool(
|
|
535
|
-
name="mobile_analyze_screenshot",
|
|
536
|
-
description="🤖 使用 AI 分析截图并返回坐标(需要 AI 密钥,可选功能)。用于 Cursor AI 无法直接识别的复杂场景。\n\n"
|
|
537
|
-
"使用流程:\n"
|
|
538
|
-
"1. 先用 mobile_take_screenshot 截图\n"
|
|
539
|
-
"2. 调用此工具分析截图\n"
|
|
540
|
-
"3. 根据返回的坐标使用 mobile_click_at_coords 点击\n\n"
|
|
541
|
-
"⚠️ 需要配置支持视觉识别的 AI(GPT-4V、Claude 3、Qwen-VL)",
|
|
542
|
-
inputSchema={
|
|
543
|
-
"type": "object",
|
|
544
|
-
"properties": {
|
|
545
|
-
"screenshot_path": {
|
|
546
|
-
"type": "string",
|
|
547
|
-
"description": "截图文件路径"
|
|
548
|
-
},
|
|
549
|
-
"description": {
|
|
550
|
-
"type": "string",
|
|
551
|
-
"description": "要查找的元素描述"
|
|
552
|
-
}
|
|
553
|
-
},
|
|
554
|
-
"required": ["screenshot_path", "description"]
|
|
555
|
-
}
|
|
556
|
-
),
|
|
557
|
-
Tool(
|
|
558
|
-
name="mobile_get_ai_status",
|
|
559
|
-
description="ℹ️ 获取 AI 功能状态。检查是否已配置 AI 密钥,智能工具是否可用。",
|
|
560
|
-
inputSchema={
|
|
561
|
-
"type": "object",
|
|
562
|
-
"properties": {},
|
|
563
|
-
"required": []
|
|
564
|
-
}
|
|
565
|
-
),
|
|
566
|
-
])
|
|
567
|
-
|
|
568
|
-
# ==================== 完整版独有:智能测试工具 ====================
|
|
569
|
-
if SERVER_MODE == "full":
|
|
570
|
-
tools.extend([
|
|
571
|
-
Tool(
|
|
572
|
-
name="mobile_execute_test_case",
|
|
573
|
-
description="🤖 智能执行测试用例(需要 AI)。AI 会自动规划、执行、验证每一步操作,遇到问题自动分析解决。",
|
|
574
|
-
inputSchema={
|
|
575
|
-
"type": "object",
|
|
576
|
-
"properties": {
|
|
577
|
-
"test_description": {
|
|
578
|
-
"type": "string",
|
|
579
|
-
"description": "自然语言描述的测试用例,如:'打开 com.im30.mind\\n点击底部云文档\\n点击我的空间'"
|
|
580
|
-
}
|
|
581
|
-
},
|
|
582
|
-
"required": ["test_description"]
|
|
583
|
-
}
|
|
584
|
-
),
|
|
585
|
-
Tool(
|
|
586
|
-
name="mobile_generate_test_script",
|
|
587
|
-
description="📝 基于操作历史生成 pytest 格式的测试脚本(不需要 AI)。\n\n"
|
|
588
|
-
"🔥 重要功能:\n"
|
|
589
|
-
"1. 自动记录所有 mobile_click、mobile_input 等操作\n"
|
|
590
|
-
"2. 一键生成可执行的 pytest 测试脚本\n"
|
|
591
|
-
"3. 支持 pytest 批量执行和 allure 报告\n\n"
|
|
592
|
-
"使用场景:\n"
|
|
593
|
-
"- 手动测试完成后,生成自动化脚本\n"
|
|
594
|
-
"- 快速创建回归测试用例\n"
|
|
595
|
-
"- 录制复杂的操作流程\n\n"
|
|
596
|
-
"💡 提示:执行完一系列操作后,调用此工具即可生成脚本!",
|
|
597
|
-
inputSchema={
|
|
598
|
-
"type": "object",
|
|
599
|
-
"properties": {
|
|
600
|
-
"test_name": {
|
|
601
|
-
"type": "string",
|
|
602
|
-
"description": "测试用例名称,如 '登录测试'"
|
|
603
|
-
},
|
|
604
|
-
"package_name": {
|
|
605
|
-
"type": "string",
|
|
606
|
-
"description": "App 包名,如 'com.im30.mind'"
|
|
607
|
-
},
|
|
608
|
-
"filename": {
|
|
609
|
-
"type": "string",
|
|
610
|
-
"description": "生成的脚本文件名(不含 .py 后缀),如 'test_login'"
|
|
611
|
-
},
|
|
612
|
-
"output_dir": {
|
|
613
|
-
"type": "string",
|
|
614
|
-
"description": "输出目录路径(可选),默认为 tests 子目录"
|
|
615
|
-
}
|
|
616
|
-
},
|
|
617
|
-
"required": ["test_name", "package_name", "filename"]
|
|
618
|
-
}
|
|
619
|
-
),
|
|
620
|
-
])
|
|
621
|
-
|
|
622
|
-
# ==================== 通用工具 ====================
|
|
623
|
-
|
|
624
|
-
tools.extend([
|
|
625
|
-
Tool(
|
|
626
|
-
name="mobile_snapshot",
|
|
627
|
-
description="📸 获取页面快照。查看当前页面结构和元素信息。",
|
|
628
|
-
inputSchema={
|
|
629
|
-
"type": "object",
|
|
630
|
-
"properties": {},
|
|
631
|
-
"required": []
|
|
632
|
-
}
|
|
633
|
-
),
|
|
634
|
-
Tool(
|
|
635
|
-
name="mobile_launch_app",
|
|
636
|
-
description="🚀 启动应用\n\n"
|
|
637
|
-
"⚠️ 重要提示:\n"
|
|
638
|
-
"1. 启动后需要等待 App 加载完成\n"
|
|
639
|
-
"2. 建议启动后主动调用 mobile_wait(seconds=2-3) 等待界面稳定\n"
|
|
640
|
-
"3. 或使用 mobile_wait(wait_for_text='首页关键文本') 等待特定元素\n"
|
|
641
|
-
"4. 如果有广告/弹窗,可能需要等待 3-5 秒\n\n"
|
|
642
|
-
"示例:\n"
|
|
643
|
-
"mobile_launch_app('com.example.app')\n"
|
|
644
|
-
"mobile_wait(seconds=3) # 等待 App 加载\n"
|
|
645
|
-
"mobile_click_by_text('开始使用')",
|
|
646
|
-
inputSchema={
|
|
647
|
-
"type": "object",
|
|
648
|
-
"properties": {
|
|
649
|
-
"package_name": {
|
|
650
|
-
"type": "string",
|
|
651
|
-
"description": "应用包名"
|
|
652
|
-
}
|
|
653
|
-
},
|
|
654
|
-
"required": ["package_name"]
|
|
655
|
-
}
|
|
656
|
-
),
|
|
657
|
-
Tool(
|
|
658
|
-
name="mobile_press_key",
|
|
659
|
-
description="⌨️ 按键操作(home, back, enter 等)",
|
|
660
|
-
inputSchema={
|
|
661
|
-
"type": "object",
|
|
662
|
-
"properties": {
|
|
663
|
-
"key": {
|
|
664
|
-
"type": "string",
|
|
665
|
-
"description": "按键名称:home, back, enter, search"
|
|
666
|
-
}
|
|
667
|
-
},
|
|
668
|
-
"required": ["key"]
|
|
669
|
-
}
|
|
670
|
-
),
|
|
671
|
-
Tool(
|
|
672
|
-
name="mobile_swipe",
|
|
673
|
-
description="👆 滑动屏幕",
|
|
674
|
-
inputSchema={
|
|
675
|
-
"type": "object",
|
|
676
|
-
"properties": {
|
|
677
|
-
"direction": {
|
|
678
|
-
"type": "string",
|
|
679
|
-
"enum": ["up", "down", "left", "right"],
|
|
680
|
-
"description": "滑动方向"
|
|
681
|
-
}
|
|
682
|
-
},
|
|
683
|
-
"required": ["direction"]
|
|
684
|
-
}
|
|
685
|
-
),
|
|
686
|
-
])
|
|
687
|
-
|
|
688
|
-
# ==================== 完整版独有:操作历史管理工具 ====================
|
|
689
|
-
if SERVER_MODE == "full":
|
|
690
|
-
tools.extend([
|
|
691
|
-
Tool(
|
|
692
|
-
name="mobile_get_operation_history",
|
|
693
|
-
description="📜 获取操作历史记录。查看之前执行的所有操作。",
|
|
694
|
-
inputSchema={
|
|
695
|
-
"type": "object",
|
|
696
|
-
"properties": {
|
|
697
|
-
"limit": {
|
|
698
|
-
"type": "number",
|
|
699
|
-
"description": "返回最近的N条记录,不指定则返回全部"
|
|
700
|
-
}
|
|
701
|
-
},
|
|
702
|
-
"required": []
|
|
703
|
-
}
|
|
704
|
-
),
|
|
705
|
-
Tool(
|
|
706
|
-
name="mobile_clear_operation_history",
|
|
707
|
-
description="🗑️ 清空操作历史记录。清空后将无法生成测试脚本。",
|
|
708
|
-
inputSchema={
|
|
709
|
-
"type": "object",
|
|
710
|
-
"properties": {},
|
|
711
|
-
"required": []
|
|
712
|
-
}
|
|
713
|
-
),
|
|
714
|
-
# ==================== 动态配置工具 ====================
|
|
715
|
-
Tool(
|
|
716
|
-
name="mobile_configure",
|
|
717
|
-
description="⚙️ 动态配置自动化行为 - AI 可根据 App 特性和测试场景优化参数(不需要 AI)。\n\n"
|
|
718
|
-
"适用场景:\n"
|
|
719
|
-
"1. 游戏App:增加等待时间、调整页面变化阈值、使用横屏\n"
|
|
720
|
-
"2. 电商App:启用广告自动关闭、使用竖屏\n"
|
|
721
|
-
"3. 回归测试:禁用验证、减少等待时间、不截图\n"
|
|
722
|
-
"4. 慢速设备:增加所有超时时间\n\n"
|
|
723
|
-
"💡 提示:不调用此工具则使用默认配置(与当前行为完全一致)",
|
|
724
|
-
inputSchema={
|
|
725
|
-
"type": "object",
|
|
726
|
-
"properties": {
|
|
727
|
-
"wait_strategy": {
|
|
728
|
-
"type": "object",
|
|
729
|
-
"description": "等待时间策略",
|
|
730
|
-
"properties": {
|
|
731
|
-
"click_wait": {
|
|
732
|
-
"type": "number",
|
|
733
|
-
"description": "点击后等待时间(秒),默认0.3"
|
|
734
|
-
},
|
|
735
|
-
"input_wait": {
|
|
736
|
-
"type": "number",
|
|
737
|
-
"description": "输入后等待时间(秒),默认0.3"
|
|
738
|
-
},
|
|
739
|
-
"page_stable_wait": {
|
|
740
|
-
"type": "number",
|
|
741
|
-
"description": "页面稳定等待时间(秒),默认0.8"
|
|
742
|
-
},
|
|
743
|
-
"element_timeout": {
|
|
744
|
-
"type": "number",
|
|
745
|
-
"description": "元素等待超时(秒),默认10"
|
|
746
|
-
},
|
|
747
|
-
"page_change_timeout": {
|
|
748
|
-
"type": "number",
|
|
749
|
-
"description": "页面变化检测超时(秒),默认2"
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
},
|
|
753
|
-
"verify_strategy": {
|
|
754
|
-
"type": "object",
|
|
755
|
-
"description": "验证策略",
|
|
756
|
-
"properties": {
|
|
757
|
-
"verify_clicks": {
|
|
758
|
-
"type": "boolean",
|
|
759
|
-
"description": "是否验证点击操作,默认true"
|
|
760
|
-
},
|
|
761
|
-
"verify_inputs": {
|
|
762
|
-
"type": "boolean",
|
|
763
|
-
"description": "是否验证输入操作,默认false"
|
|
764
|
-
},
|
|
765
|
-
"verify_keys": {
|
|
766
|
-
"type": "boolean",
|
|
767
|
-
"description": "是否验证按键操作,默认true"
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
},
|
|
771
|
-
"page_change_threshold": {
|
|
772
|
-
"type": "number",
|
|
773
|
-
"description": "页面变化阈值(0-1),游戏App建议0.1-0.15,工具App建议0.05,默认0.05"
|
|
774
|
-
},
|
|
775
|
-
"screen_orientation": {
|
|
776
|
-
"type": "string",
|
|
777
|
-
"enum": ["portrait", "landscape", "auto"],
|
|
778
|
-
"description": "屏幕方向:portrait=竖屏, landscape=横屏, auto=跟随App,默认portrait"
|
|
779
|
-
},
|
|
780
|
-
"ad_handling": {
|
|
781
|
-
"type": "object",
|
|
782
|
-
"description": "广告/弹窗处理策略",
|
|
783
|
-
"properties": {
|
|
784
|
-
"auto_close": {
|
|
785
|
-
"type": "boolean",
|
|
786
|
-
"description": "是否自动关闭广告,默认true"
|
|
787
|
-
},
|
|
788
|
-
"wait_before_close": {
|
|
789
|
-
"type": "number",
|
|
790
|
-
"description": "点击关闭前等待(秒),默认0.3"
|
|
791
|
-
},
|
|
792
|
-
"max_close_buttons": {
|
|
793
|
-
"type": "number",
|
|
794
|
-
"description": "最多点击几个关闭按钮,默认1"
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
},
|
|
798
|
-
"screenshot_strategy": {
|
|
799
|
-
"type": "string",
|
|
800
|
-
"enum": ["always", "on_failure", "never", "smart"],
|
|
801
|
-
"description": "截图策略:always=总是, on_failure=失败时, never=从不, smart=智能,默认smart"
|
|
802
|
-
},
|
|
803
|
-
"retry_strategy": {
|
|
804
|
-
"type": "object",
|
|
805
|
-
"description": "重试策略",
|
|
806
|
-
"properties": {
|
|
807
|
-
"max_retries": {
|
|
808
|
-
"type": "number",
|
|
809
|
-
"description": "最大重试次数,默认3"
|
|
810
|
-
},
|
|
811
|
-
"retry_delay": {
|
|
812
|
-
"type": "number",
|
|
813
|
-
"description": "重试间隔(秒),默认1.0"
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
},
|
|
817
|
-
"reset": {
|
|
818
|
-
"type": "boolean",
|
|
819
|
-
"description": "是否重置为默认配置,默认false"
|
|
820
|
-
}
|
|
821
|
-
},
|
|
822
|
-
"required": []
|
|
823
|
-
}
|
|
824
|
-
),
|
|
825
|
-
Tool(
|
|
826
|
-
name="mobile_get_config",
|
|
827
|
-
description="📋 获取当前动态配置。查看当前所有配置值。",
|
|
828
|
-
inputSchema={
|
|
829
|
-
"type": "object",
|
|
830
|
-
"properties": {},
|
|
831
|
-
"required": []
|
|
832
|
-
}
|
|
833
|
-
),
|
|
834
|
-
])
|
|
835
|
-
|
|
836
|
-
return tools
|
|
837
|
-
|
|
838
|
-
async def handle_tool_call(self, name: str, arguments: dict):
|
|
839
|
-
"""处理工具调用"""
|
|
840
|
-
await self.initialize()
|
|
841
|
-
|
|
842
|
-
try:
|
|
843
|
-
# ==================== 基础工具 ====================
|
|
844
|
-
if name == "mobile_list_elements":
|
|
845
|
-
result = self.basic_tools.list_elements()
|
|
846
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
847
|
-
|
|
848
|
-
elif name == "mobile_click_by_id":
|
|
849
|
-
result = self.basic_tools.click_by_id(arguments["resource_id"])
|
|
850
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
851
|
-
|
|
852
|
-
elif name == "mobile_click_by_text":
|
|
853
|
-
result = self.basic_tools.click_by_text(arguments["text"])
|
|
854
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
855
|
-
|
|
856
|
-
elif name == "mobile_click_at_coords":
|
|
857
|
-
result = self.basic_tools.click_at_coords(arguments["x"], arguments["y"])
|
|
858
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
859
|
-
|
|
860
|
-
elif name == "mobile_input_text_by_id":
|
|
861
|
-
result = self.basic_tools.input_text_by_id(
|
|
862
|
-
arguments["resource_id"],
|
|
863
|
-
arguments["text"]
|
|
864
|
-
)
|
|
865
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
866
|
-
|
|
867
|
-
elif name == "mobile_find_elements_by_class":
|
|
868
|
-
result = self.basic_tools.find_elements_by_class(arguments["class_name"])
|
|
869
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
870
|
-
|
|
871
|
-
elif name == "mobile_wait_for_element":
|
|
872
|
-
timeout = arguments.get("timeout", 10)
|
|
873
|
-
result = self.basic_tools.wait_for_element(arguments["resource_id"], timeout)
|
|
874
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
875
|
-
|
|
876
|
-
elif name == "mobile_take_screenshot":
|
|
877
|
-
description = arguments.get("description", "")
|
|
878
|
-
result = self.basic_tools.take_screenshot(description)
|
|
879
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
880
|
-
|
|
881
|
-
elif name == "mobile_take_screenshot_region":
|
|
882
|
-
description = arguments.get("description", "")
|
|
883
|
-
result = self.basic_tools.take_screenshot_region(
|
|
884
|
-
arguments["x1"], arguments["y1"],
|
|
885
|
-
arguments["x2"], arguments["y2"],
|
|
886
|
-
description
|
|
887
|
-
)
|
|
888
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
889
|
-
|
|
890
|
-
# ==================== 设备管理工具 ====================
|
|
891
|
-
elif name == "mobile_list_devices":
|
|
892
|
-
result = self.basic_tools.list_devices()
|
|
893
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
894
|
-
|
|
895
|
-
elif name == "mobile_get_screen_size":
|
|
896
|
-
result = self.basic_tools.get_screen_size()
|
|
897
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
898
|
-
|
|
899
|
-
elif name == "mobile_get_orientation":
|
|
900
|
-
result = self.basic_tools.get_orientation()
|
|
901
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
902
|
-
|
|
903
|
-
elif name == "mobile_set_orientation":
|
|
904
|
-
result = self.basic_tools.set_orientation(arguments["orientation"])
|
|
905
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
906
|
-
|
|
907
|
-
elif name == "mobile_check_connection":
|
|
908
|
-
result = self.basic_tools.check_connection()
|
|
909
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
910
|
-
|
|
911
|
-
elif name == "mobile_reconnect_device":
|
|
912
|
-
result = self.basic_tools.reconnect_device()
|
|
913
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
914
|
-
|
|
915
|
-
# ==================== 应用管理工具 ====================
|
|
916
|
-
elif name == "mobile_list_apps":
|
|
917
|
-
filter_keyword = arguments.get("filter", "")
|
|
918
|
-
result = self.basic_tools.list_apps(filter_keyword)
|
|
919
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
920
|
-
|
|
921
|
-
elif name == "mobile_install_app":
|
|
922
|
-
result = self.basic_tools.install_app(arguments["apk_path"])
|
|
923
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
924
|
-
|
|
925
|
-
elif name == "mobile_uninstall_app":
|
|
926
|
-
result = self.basic_tools.uninstall_app(arguments["package_name"])
|
|
927
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
928
|
-
|
|
929
|
-
elif name == "mobile_terminate_app":
|
|
930
|
-
result = self.basic_tools.terminate_app(arguments["package_name"])
|
|
931
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
932
|
-
|
|
933
|
-
elif name == "mobile_get_current_package":
|
|
934
|
-
result = self.basic_tools.get_current_package()
|
|
935
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
936
|
-
|
|
937
|
-
# ==================== 高级交互工具 ====================
|
|
938
|
-
elif name == "mobile_double_click":
|
|
939
|
-
result = self.basic_tools.double_click_at_coords(
|
|
940
|
-
int(arguments["x"]), int(arguments["y"])
|
|
941
|
-
)
|
|
942
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
943
|
-
|
|
944
|
-
elif name == "mobile_long_press":
|
|
945
|
-
duration = arguments.get("duration", 1.0)
|
|
946
|
-
result = self.basic_tools.long_press_at_coords(
|
|
947
|
-
int(arguments["x"]), int(arguments["y"]), duration
|
|
948
|
-
)
|
|
949
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
950
|
-
|
|
951
|
-
elif name == "mobile_open_url":
|
|
952
|
-
result = self.basic_tools.open_url(arguments["url"])
|
|
953
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
954
|
-
|
|
955
|
-
elif name == "mobile_assert_text":
|
|
956
|
-
result = self.basic_tools.assert_text(arguments["text"])
|
|
957
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
958
|
-
|
|
959
|
-
# ==================== 智能工具 ====================
|
|
960
|
-
elif name == "mobile_smart_click":
|
|
961
|
-
result = await self.smart_tools.smart_click(arguments["description"])
|
|
962
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
963
|
-
|
|
964
|
-
elif name == "mobile_smart_input":
|
|
965
|
-
result = await self.smart_tools.smart_input(
|
|
966
|
-
arguments["description"],
|
|
967
|
-
arguments["text"]
|
|
968
|
-
)
|
|
969
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
970
|
-
|
|
971
|
-
elif name == "mobile_analyze_screenshot":
|
|
972
|
-
result = await self.smart_tools.analyze_screenshot_with_ai(
|
|
973
|
-
arguments["screenshot_path"],
|
|
974
|
-
arguments["description"]
|
|
975
|
-
)
|
|
976
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
977
|
-
|
|
978
|
-
elif name == "mobile_get_ai_status":
|
|
979
|
-
result = self.smart_tools.get_ai_status()
|
|
980
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
981
|
-
|
|
982
|
-
# ==================== 通用工具 ====================
|
|
983
|
-
elif name == "mobile_snapshot":
|
|
984
|
-
snapshot = await self.client.snapshot()
|
|
985
|
-
return [TextContent(type="text", text=snapshot)]
|
|
986
|
-
|
|
987
|
-
elif name == "mobile_launch_app":
|
|
988
|
-
await self.client.launch_app(arguments["package_name"])
|
|
989
|
-
return [TextContent(type="text", text=f"✅ 已启动: {arguments['package_name']}")]
|
|
990
|
-
|
|
991
|
-
elif name == "mobile_press_key":
|
|
992
|
-
await self.client.press_key(arguments["key"])
|
|
993
|
-
return [TextContent(type="text", text=f"✅ 已按键: {arguments['key']}")]
|
|
994
|
-
|
|
995
|
-
elif name == "mobile_swipe":
|
|
996
|
-
await self.client.swipe(arguments["direction"])
|
|
997
|
-
return [TextContent(type="text", text=f"✅ 已滑动: {arguments['direction']}")]
|
|
998
|
-
|
|
999
|
-
# ==================== 完整版独有工具处理 ====================
|
|
1000
|
-
elif name == "mobile_wait":
|
|
1001
|
-
if SERVER_MODE != "full":
|
|
1002
|
-
return [TextContent(type="text", text=f"❌ 此工具仅在完整版可用,当前为简化版")]
|
|
1003
|
-
seconds = arguments.get("seconds")
|
|
1004
|
-
wait_for_text = arguments.get("wait_for_text")
|
|
1005
|
-
wait_for_id = arguments.get("wait_for_id")
|
|
1006
|
-
timeout = arguments.get("timeout", 10)
|
|
1007
|
-
result = self.basic_tools.wait(
|
|
1008
|
-
seconds=seconds,
|
|
1009
|
-
wait_for_text=wait_for_text,
|
|
1010
|
-
wait_for_id=wait_for_id,
|
|
1011
|
-
timeout=timeout
|
|
1012
|
-
)
|
|
1013
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
1014
|
-
|
|
1015
|
-
elif name == "mobile_get_operation_history":
|
|
1016
|
-
if SERVER_MODE != "full":
|
|
1017
|
-
return [TextContent(type="text", text=f"❌ 此工具仅在完整版可用,当前为简化版")]
|
|
1018
|
-
limit = arguments.get("limit")
|
|
1019
|
-
result = self.basic_tools.get_operation_history(limit)
|
|
1020
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
1021
|
-
|
|
1022
|
-
elif name == "mobile_clear_operation_history":
|
|
1023
|
-
if SERVER_MODE != "full":
|
|
1024
|
-
return [TextContent(type="text", text=f"❌ 此工具仅在完整版可用,当前为简化版")]
|
|
1025
|
-
result = self.basic_tools.clear_operation_history()
|
|
1026
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
1027
|
-
|
|
1028
|
-
elif name == "mobile_configure":
|
|
1029
|
-
if SERVER_MODE != "full":
|
|
1030
|
-
return [TextContent(type="text", text=f"❌ 此工具仅在完整版可用,当前为简化版")]
|
|
1031
|
-
if arguments.get("reset", False):
|
|
1032
|
-
result = DynamicConfig.reset()
|
|
1033
|
-
else:
|
|
1034
|
-
result = DynamicConfig.update(arguments)
|
|
1035
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
1036
|
-
|
|
1037
|
-
elif name == "mobile_get_config":
|
|
1038
|
-
if SERVER_MODE != "full":
|
|
1039
|
-
return [TextContent(type="text", text=f"❌ 此工具仅在完整版可用,当前为简化版")]
|
|
1040
|
-
current_config = DynamicConfig.get_current()
|
|
1041
|
-
config_str = json.dumps(current_config, indent=2, ensure_ascii=False)
|
|
1042
|
-
return [TextContent(type="text", text=f"📋 当前配置:\n{config_str}")]
|
|
1043
|
-
|
|
1044
|
-
elif name == "mobile_execute_test_case":
|
|
1045
|
-
if SERVER_MODE != "full":
|
|
1046
|
-
return [TextContent(type="text", text=f"❌ 此工具仅在完整版可用,当前为简化版")]
|
|
1047
|
-
try:
|
|
1048
|
-
from mobile_mcp.core.ai.smart_test_executor import SmartTestExecutor
|
|
1049
|
-
executor = SmartTestExecutor(self.client)
|
|
1050
|
-
result = await executor.execute_test_case(arguments["test_description"])
|
|
1051
|
-
return [TextContent(type="text", text=self.format_response(result))]
|
|
1052
|
-
except ImportError:
|
|
1053
|
-
return [TextContent(type="text", text="❌ 智能测试执行器模块未安装")]
|
|
1054
|
-
except Exception as e:
|
|
1055
|
-
return [TextContent(type="text", text=f"❌ 测试执行失败: {str(e)}")]
|
|
1056
|
-
|
|
1057
|
-
elif name == "mobile_generate_test_script":
|
|
1058
|
-
if SERVER_MODE != "full":
|
|
1059
|
-
return [TextContent(type="text", text=f"❌ 此工具仅在完整版可用,当前为简化版")]
|
|
1060
|
-
try:
|
|
1061
|
-
from mobile_mcp.core.ai.test_generator_from_history import TestGeneratorFromHistory
|
|
1062
|
-
from mobile_mcp.core.utils.operation_history_manager import OperationHistoryManager
|
|
1063
|
-
|
|
1064
|
-
history_manager = OperationHistoryManager()
|
|
1065
|
-
operation_history = history_manager.get_all()
|
|
1066
|
-
|
|
1067
|
-
if not operation_history:
|
|
1068
|
-
return [TextContent(type="text", text="❌ 没有操作历史,请先执行一些操作")]
|
|
1069
|
-
|
|
1070
|
-
generator = TestGeneratorFromHistory()
|
|
1071
|
-
script = generator.generate_from_history(
|
|
1072
|
-
test_name=arguments["test_name"],
|
|
1073
|
-
package_name=arguments["package_name"],
|
|
1074
|
-
operation_history=operation_history
|
|
1075
|
-
)
|
|
1076
|
-
|
|
1077
|
-
output_dir = arguments.get("output_dir", "tests")
|
|
1078
|
-
filename = arguments["filename"]
|
|
1079
|
-
if not filename.endswith('.py'):
|
|
1080
|
-
filename = f"{filename}.py"
|
|
1081
|
-
|
|
1082
|
-
from pathlib import Path
|
|
1083
|
-
output_path = Path(output_dir) / filename
|
|
1084
|
-
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1085
|
-
generator.save(str(output_path), script)
|
|
1086
|
-
|
|
1087
|
-
return [TextContent(type="text", text=f"✅ 测试脚本已生成: {output_path}\n\n{script[:500]}...")]
|
|
1088
|
-
except ImportError as e:
|
|
1089
|
-
return [TextContent(type="text", text=f"❌ 模块导入失败: {str(e)}")]
|
|
1090
|
-
except Exception as e:
|
|
1091
|
-
return [TextContent(type="text", text=f"❌ 脚本生成失败: {str(e)}")]
|
|
1092
|
-
|
|
1093
|
-
else:
|
|
1094
|
-
return [TextContent(type="text", text=f"❌ 未知工具: {name}")]
|
|
1095
|
-
|
|
1096
|
-
except Exception as e:
|
|
1097
|
-
error_msg = str(e)
|
|
1098
|
-
return [TextContent(type="text", text=f"❌ 执行失败: {error_msg}")]
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
async def main():
|
|
1102
|
-
"""启动 MCP Server"""
|
|
1103
|
-
server = MobileMCPServer()
|
|
1104
|
-
mcp_server = Server("mobile-mcp")
|
|
1105
|
-
|
|
1106
|
-
@mcp_server.list_tools()
|
|
1107
|
-
async def list_tools():
|
|
1108
|
-
return server.get_tools()
|
|
1109
|
-
|
|
1110
|
-
@mcp_server.call_tool()
|
|
1111
|
-
async def call_tool(name: str, arguments: dict):
|
|
1112
|
-
return await server.handle_tool_call(name, arguments)
|
|
1113
|
-
|
|
1114
|
-
mode_name = "完整版 (39工具)" if SERVER_MODE == "full" else "简化版 (32工具)"
|
|
1115
|
-
print(f"🚀 Mobile MCP Server v2.2.6 启动中... [{mode_name}]", file=sys.stderr)
|
|
1116
|
-
print(f"📋 运行模式: {SERVER_MODE.upper()}", file=sys.stderr)
|
|
1117
|
-
if SERVER_MODE == "simple":
|
|
1118
|
-
print("💡 提示: 使用完整版可获得更多功能(操作历史、动态配置等)", file=sys.stderr)
|
|
1119
|
-
|
|
1120
|
-
async with stdio_server() as (read_stream, write_stream):
|
|
1121
|
-
await mcp_server.run(read_stream, write_stream, mcp_server.create_initialization_options())
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
if __name__ == "__main__":
|
|
1125
|
-
asyncio.run(main())
|
|
1126
|
-
|