mobile-mcp-ai 2.1.2__py3-none-any.whl → 2.5.8__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.
Files changed (65) hide show
  1. mobile_mcp/__init__.py +34 -0
  2. mobile_mcp/config.py +142 -0
  3. mobile_mcp/core/basic_tools_lite.py +3266 -0
  4. {core → mobile_mcp/core}/device_manager.py +2 -2
  5. mobile_mcp/core/dynamic_config.py +272 -0
  6. mobile_mcp/core/ios_client_wda.py +569 -0
  7. mobile_mcp/core/ios_device_manager_wda.py +306 -0
  8. {core → mobile_mcp/core}/mobile_client.py +279 -39
  9. mobile_mcp/core/template_matcher.py +429 -0
  10. mobile_mcp/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
  11. mobile_mcp/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
  12. mobile_mcp/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
  13. mobile_mcp/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
  14. mobile_mcp/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
  15. mobile_mcp/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
  16. {core → mobile_mcp/core}/utils/smart_wait.py +3 -3
  17. mobile_mcp/mcp_tools/__init__.py +10 -0
  18. mobile_mcp/mcp_tools/mcp_server.py +1071 -0
  19. mobile_mcp_ai-2.5.8.dist-info/METADATA +469 -0
  20. mobile_mcp_ai-2.5.8.dist-info/RECORD +32 -0
  21. mobile_mcp_ai-2.5.8.dist-info/entry_points.txt +2 -0
  22. mobile_mcp_ai-2.5.8.dist-info/licenses/LICENSE +201 -0
  23. mobile_mcp_ai-2.5.8.dist-info/top_level.txt +1 -0
  24. core/ai/__init__.py +0 -11
  25. core/ai/ai_analyzer.py +0 -197
  26. core/ai/ai_config.py +0 -116
  27. core/ai/ai_platform_adapter.py +0 -399
  28. core/ai/smart_test_executor.py +0 -520
  29. core/ai/test_generator.py +0 -365
  30. core/ai/test_generator_from_history.py +0 -391
  31. core/ai/test_generator_standalone.py +0 -293
  32. core/assertion/__init__.py +0 -9
  33. core/assertion/smart_assertion.py +0 -341
  34. core/basic_tools.py +0 -377
  35. core/h5/__init__.py +0 -10
  36. core/h5/h5_handler.py +0 -548
  37. core/ios_client.py +0 -219
  38. core/ios_device_manager.py +0 -252
  39. core/locator/__init__.py +0 -10
  40. core/locator/cursor_ai_auto_analyzer.py +0 -119
  41. core/locator/cursor_vision_helper.py +0 -414
  42. core/locator/mobile_smart_locator.py +0 -1640
  43. core/locator/position_analyzer.py +0 -813
  44. core/locator/script_updater.py +0 -157
  45. core/nl_test_runner.py +0 -585
  46. core/smart_app_launcher.py +0 -334
  47. core/smart_tools.py +0 -311
  48. mcp/__init__.py +0 -8
  49. mcp/mcp_server.py +0 -1919
  50. mcp/mcp_server_simple.py +0 -476
  51. mobile_mcp_ai-2.1.2.dist-info/METADATA +0 -567
  52. mobile_mcp_ai-2.1.2.dist-info/RECORD +0 -45
  53. mobile_mcp_ai-2.1.2.dist-info/entry_points.txt +0 -2
  54. mobile_mcp_ai-2.1.2.dist-info/top_level.txt +0 -4
  55. vision/__init__.py +0 -10
  56. vision/vision_locator.py +0 -404
  57. {core → mobile_mcp/core}/__init__.py +0 -0
  58. {core → mobile_mcp/core}/utils/__init__.py +0 -0
  59. {core → mobile_mcp/core}/utils/logger.py +0 -0
  60. {core → mobile_mcp/core}/utils/operation_history_manager.py +0 -0
  61. {utils → mobile_mcp/utils}/__init__.py +0 -0
  62. {utils → mobile_mcp/utils}/logger.py +0 -0
  63. {utils → mobile_mcp/utils}/xml_formatter.py +0 -0
  64. {utils → mobile_mcp/utils}/xml_parser.py +0 -0
  65. {mobile_mcp_ai-2.1.2.dist-info → mobile_mcp_ai-2.5.8.dist-info}/WHEEL +0 -0
mcp/mcp_server_simple.py DELETED
@@ -1,476 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Mobile MCP Server(重构版)- AI 可选
5
-
6
- 架构说明:
7
- - 基础工具:不需要 AI 密钥,提供精确的元素操作
8
- - 智能工具:需要 AI 密钥(可选),提供自然语言定位
9
-
10
- 用户可以选择:
11
- 1. 只用基础工具 → 不需要配置 AI
12
- 2. 启用智能功能 → 需要配置 AI(创建 .env 文件)
13
- """
14
-
15
- import asyncio
16
- import sys
17
- from pathlib import Path
18
- from typing import Optional
19
-
20
- # 添加项目路径
21
- mobile_mcp_dir = Path(__file__).parent.parent
22
- project_root = mobile_mcp_dir.parent.parent
23
- backend_dir = project_root / "backend"
24
-
25
- sys.path.insert(0, str(project_root))
26
- sys.path.insert(0, str(backend_dir))
27
-
28
- from mcp.types import Tool, TextContent
29
- from mcp.server import Server
30
- from mcp.server.stdio import stdio_server
31
-
32
- from mobile_mcp.core.mobile_client import MobileClient
33
- from mobile_mcp.core.basic_tools import BasicMobileTools
34
- from mobile_mcp.core.smart_tools import SmartMobileTools
35
-
36
-
37
- class SimpleMobileMCPServer:
38
- """简化的 Mobile MCP Server"""
39
-
40
- def __init__(self):
41
- """初始化 MCP Server"""
42
- self.client: Optional[MobileClient] = None
43
- self.basic_tools: Optional[BasicMobileTools] = None
44
- self.smart_tools: Optional[SmartMobileTools] = None
45
- self._initialized = False
46
-
47
- async def initialize(self):
48
- """延迟初始化设备连接"""
49
- if not self._initialized:
50
- # 初始化移动客户端
51
- self.client = MobileClient()
52
-
53
- # 初始化基础工具(总是可用)
54
- self.basic_tools = BasicMobileTools(self.client)
55
-
56
- # 初始化智能工具(检查 AI 可用性)
57
- self.smart_tools = SmartMobileTools(self.client)
58
-
59
- ai_status = self.smart_tools.get_ai_status()
60
- print(f"\n{ai_status['message']}\n", file=sys.stderr)
61
-
62
- self._initialized = True
63
-
64
- def get_tools(self):
65
- """注册 MCP 工具"""
66
- tools = []
67
-
68
- # ==================== 基础工具(不需要 AI)====================
69
-
70
- tools.extend([
71
- Tool(
72
- name="mobile_list_elements",
73
- description="📋 列出页面所有可交互元素(不需要 AI)。返回 resource_id, text, bounds 等信息,供后续精确操作使用。",
74
- inputSchema={
75
- "type": "object",
76
- "properties": {},
77
- "required": []
78
- }
79
- ),
80
- Tool(
81
- name="mobile_click_by_id",
82
- description="👆 通过 resource-id 点击元素(不需要 AI)。精确可靠的点击方式。先用 mobile_list_elements 查找元素 ID。",
83
- inputSchema={
84
- "type": "object",
85
- "properties": {
86
- "resource_id": {
87
- "type": "string",
88
- "description": "元素的 resource-id,如 'com.app:id/search_btn'"
89
- }
90
- },
91
- "required": ["resource_id"]
92
- }
93
- ),
94
- Tool(
95
- name="mobile_click_by_text",
96
- description="👆 通过文本内容点击元素(不需要 AI)。适合文本完全匹配的场景。",
97
- inputSchema={
98
- "type": "object",
99
- "properties": {
100
- "text": {
101
- "type": "string",
102
- "description": "元素的文本内容(精确匹配),如 '登录'"
103
- }
104
- },
105
- "required": ["text"]
106
- }
107
- ),
108
- Tool(
109
- name="mobile_click_at_coords",
110
- description="👆 点击指定坐标(不需要 AI)。可以从 mobile_list_elements 获取的 bounds 计算坐标。",
111
- inputSchema={
112
- "type": "object",
113
- "properties": {
114
- "x": {
115
- "type": "number",
116
- "description": "X 坐标(像素)"
117
- },
118
- "y": {
119
- "type": "number",
120
- "description": "Y 坐标(像素)"
121
- }
122
- },
123
- "required": ["x", "y"]
124
- }
125
- ),
126
- Tool(
127
- name="mobile_input_text_by_id",
128
- description="⌨️ 通过 resource-id 在输入框输入文本(不需要 AI)。",
129
- inputSchema={
130
- "type": "object",
131
- "properties": {
132
- "resource_id": {
133
- "type": "string",
134
- "description": "输入框的 resource-id"
135
- },
136
- "text": {
137
- "type": "string",
138
- "description": "要输入的文本"
139
- }
140
- },
141
- "required": ["resource_id", "text"]
142
- }
143
- ),
144
- Tool(
145
- name="mobile_find_elements_by_class",
146
- description="🔍 按类名查找元素(不需要 AI)。如查找所有输入框: 'android.widget.EditText'",
147
- inputSchema={
148
- "type": "object",
149
- "properties": {
150
- "class_name": {
151
- "type": "string",
152
- "description": "类名,如 'android.widget.EditText'"
153
- }
154
- },
155
- "required": ["class_name"]
156
- }
157
- ),
158
- Tool(
159
- name="mobile_wait_for_element",
160
- description="⏳ 等待元素出现(不需要 AI)。用于等待页面加载完成。",
161
- inputSchema={
162
- "type": "object",
163
- "properties": {
164
- "resource_id": {
165
- "type": "string",
166
- "description": "元素的 resource-id"
167
- },
168
- "timeout": {
169
- "type": "number",
170
- "description": "超时时间(秒),默认 10秒",
171
- "default": 10
172
- }
173
- },
174
- "required": ["resource_id"]
175
- }
176
- ),
177
- Tool(
178
- name="mobile_take_screenshot",
179
- description="📸 截取屏幕截图(不需要 AI)。用于 Cursor AI 视觉识别、调试或记录测试过程。",
180
- inputSchema={
181
- "type": "object",
182
- "properties": {
183
- "description": {
184
- "type": "string",
185
- "description": "截图描述(可选),用于生成文件名"
186
- }
187
- },
188
- "required": []
189
- }
190
- ),
191
- Tool(
192
- name="mobile_take_screenshot_region",
193
- description="📸 截取屏幕指定区域(不需要 AI)。用于局部截图和分析。",
194
- inputSchema={
195
- "type": "object",
196
- "properties": {
197
- "x1": {
198
- "type": "number",
199
- "description": "左上角X坐标"
200
- },
201
- "y1": {
202
- "type": "number",
203
- "description": "左上角Y坐标"
204
- },
205
- "x2": {
206
- "type": "number",
207
- "description": "右下角X坐标"
208
- },
209
- "y2": {
210
- "type": "number",
211
- "description": "右下角Y坐标"
212
- },
213
- "description": {
214
- "type": "string",
215
- "description": "截图描述(可选)"
216
- }
217
- },
218
- "required": ["x1", "y1", "x2", "y2"]
219
- }
220
- ),
221
- ])
222
-
223
- # ==================== 智能工具(需要 AI,可选)====================
224
-
225
- tools.extend([
226
- Tool(
227
- name="mobile_smart_click",
228
- description="🤖 智能定位并点击(需要 AI 密钥,可选功能)。使用自然语言描述元素,如'右上角的设置按钮'。\n\n"
229
- "⚠️ 如未配置 AI,请使用基础工具:mobile_list_elements + mobile_click_by_id",
230
- inputSchema={
231
- "type": "object",
232
- "properties": {
233
- "description": {
234
- "type": "string",
235
- "description": "元素的自然语言描述,如 '顶部搜索框'、'登录按钮'"
236
- }
237
- },
238
- "required": ["description"]
239
- }
240
- ),
241
- Tool(
242
- name="mobile_smart_input",
243
- description="🤖 智能定位输入框并输入(需要 AI 密钥,可选功能)。使用自然语言描述输入框。\n\n"
244
- "⚠️ 如未配置 AI,请使用:mobile_input_text_by_id",
245
- inputSchema={
246
- "type": "object",
247
- "properties": {
248
- "description": {
249
- "type": "string",
250
- "description": "输入框的自然语言描述,如 '用户名输入框'"
251
- },
252
- "text": {
253
- "type": "string",
254
- "description": "要输入的文本"
255
- }
256
- },
257
- "required": ["description", "text"]
258
- }
259
- ),
260
- Tool(
261
- name="mobile_analyze_screenshot",
262
- description="🤖 使用 AI 分析截图并返回坐标(需要 AI 密钥,可选功能)。用于 Cursor AI 无法直接识别的复杂场景。\n\n"
263
- "使用流程:\n"
264
- "1. 先用 mobile_take_screenshot 截图\n"
265
- "2. 调用此工具分析截图\n"
266
- "3. 根据返回的坐标使用 mobile_click_at_coords 点击\n\n"
267
- "⚠️ 需要配置支持视觉识别的 AI(GPT-4V、Claude 3、Qwen-VL)",
268
- inputSchema={
269
- "type": "object",
270
- "properties": {
271
- "screenshot_path": {
272
- "type": "string",
273
- "description": "截图文件路径"
274
- },
275
- "description": {
276
- "type": "string",
277
- "description": "要查找的元素描述"
278
- }
279
- },
280
- "required": ["screenshot_path", "description"]
281
- }
282
- ),
283
- Tool(
284
- name="mobile_get_ai_status",
285
- description="ℹ️ 获取 AI 功能状态。检查是否已配置 AI 密钥,智能工具是否可用。",
286
- inputSchema={
287
- "type": "object",
288
- "properties": {},
289
- "required": []
290
- }
291
- ),
292
- ])
293
-
294
- # ==================== 通用工具 ====================
295
-
296
- tools.extend([
297
- Tool(
298
- name="mobile_snapshot",
299
- description="📸 获取页面快照。查看当前页面结构和元素信息。",
300
- inputSchema={
301
- "type": "object",
302
- "properties": {},
303
- "required": []
304
- }
305
- ),
306
- Tool(
307
- name="mobile_launch_app",
308
- description="🚀 启动应用",
309
- inputSchema={
310
- "type": "object",
311
- "properties": {
312
- "package_name": {
313
- "type": "string",
314
- "description": "应用包名"
315
- }
316
- },
317
- "required": ["package_name"]
318
- }
319
- ),
320
- Tool(
321
- name="mobile_press_key",
322
- description="⌨️ 按键操作(home, back, enter 等)",
323
- inputSchema={
324
- "type": "object",
325
- "properties": {
326
- "key": {
327
- "type": "string",
328
- "description": "按键名称:home, back, enter, search"
329
- }
330
- },
331
- "required": ["key"]
332
- }
333
- ),
334
- Tool(
335
- name="mobile_swipe",
336
- description="👆 滑动屏幕",
337
- inputSchema={
338
- "type": "object",
339
- "properties": {
340
- "direction": {
341
- "type": "string",
342
- "enum": ["up", "down", "left", "right"],
343
- "description": "滑动方向"
344
- }
345
- },
346
- "required": ["direction"]
347
- }
348
- ),
349
- ])
350
-
351
- return tools
352
-
353
- async def handle_tool_call(self, name: str, arguments: dict):
354
- """处理工具调用"""
355
- await self.initialize()
356
-
357
- try:
358
- # ==================== 基础工具 ====================
359
- if name == "mobile_list_elements":
360
- result = self.basic_tools.list_elements()
361
- return [TextContent(type="text", text=str(result))]
362
-
363
- elif name == "mobile_click_by_id":
364
- result = self.basic_tools.click_by_id(arguments["resource_id"])
365
- return [TextContent(type="text", text=str(result))]
366
-
367
- elif name == "mobile_click_by_text":
368
- result = self.basic_tools.click_by_text(arguments["text"])
369
- return [TextContent(type="text", text=str(result))]
370
-
371
- elif name == "mobile_click_at_coords":
372
- result = self.basic_tools.click_at_coords(arguments["x"], arguments["y"])
373
- return [TextContent(type="text", text=str(result))]
374
-
375
- elif name == "mobile_input_text_by_id":
376
- result = self.basic_tools.input_text_by_id(
377
- arguments["resource_id"],
378
- arguments["text"]
379
- )
380
- return [TextContent(type="text", text=str(result))]
381
-
382
- elif name == "mobile_find_elements_by_class":
383
- result = self.basic_tools.find_elements_by_class(arguments["class_name"])
384
- return [TextContent(type="text", text=str(result))]
385
-
386
- elif name == "mobile_wait_for_element":
387
- timeout = arguments.get("timeout", 10)
388
- result = self.basic_tools.wait_for_element(arguments["resource_id"], timeout)
389
- return [TextContent(type="text", text=str(result))]
390
-
391
- elif name == "mobile_take_screenshot":
392
- description = arguments.get("description", "")
393
- result = self.basic_tools.take_screenshot(description)
394
- return [TextContent(type="text", text=str(result))]
395
-
396
- elif name == "mobile_take_screenshot_region":
397
- description = arguments.get("description", "")
398
- result = self.basic_tools.take_screenshot_region(
399
- arguments["x1"], arguments["y1"],
400
- arguments["x2"], arguments["y2"],
401
- description
402
- )
403
- return [TextContent(type="text", text=str(result))]
404
-
405
- # ==================== 智能工具 ====================
406
- elif name == "mobile_smart_click":
407
- result = await self.smart_tools.smart_click(arguments["description"])
408
- return [TextContent(type="text", text=str(result))]
409
-
410
- elif name == "mobile_smart_input":
411
- result = await self.smart_tools.smart_input(
412
- arguments["description"],
413
- arguments["text"]
414
- )
415
- return [TextContent(type="text", text=str(result))]
416
-
417
- elif name == "mobile_analyze_screenshot":
418
- result = await self.smart_tools.analyze_screenshot_with_ai(
419
- arguments["screenshot_path"],
420
- arguments["description"]
421
- )
422
- return [TextContent(type="text", text=str(result))]
423
-
424
- elif name == "mobile_get_ai_status":
425
- result = self.smart_tools.get_ai_status()
426
- return [TextContent(type="text", text=str(result))]
427
-
428
- # ==================== 通用工具 ====================
429
- elif name == "mobile_snapshot":
430
- snapshot = await self.client.snapshot()
431
- return [TextContent(type="text", text=snapshot)]
432
-
433
- elif name == "mobile_launch_app":
434
- await self.client.launch_app(arguments["package_name"])
435
- return [TextContent(type="text", text=f"✅ 已启动: {arguments['package_name']}")]
436
-
437
- elif name == "mobile_press_key":
438
- await self.client.press_key(arguments["key"])
439
- return [TextContent(type="text", text=f"✅ 已按键: {arguments['key']}")]
440
-
441
- elif name == "mobile_swipe":
442
- await self.client.swipe(arguments["direction"])
443
- return [TextContent(type="text", text=f"✅ 已滑动: {arguments['direction']}")]
444
-
445
- else:
446
- return [TextContent(type="text", text=f"❌ 未知工具: {name}")]
447
-
448
- except Exception as e:
449
- error_msg = str(e)
450
- return [TextContent(type="text", text=f"❌ 执行失败: {error_msg}")]
451
-
452
-
453
- async def main():
454
- """启动 MCP Server"""
455
- server = SimpleMobileMCPServer()
456
- mcp_server = Server("mobile-mcp-simplified")
457
-
458
- @mcp_server.list_tools()
459
- async def list_tools():
460
- return server.get_tools()
461
-
462
- @mcp_server.call_tool()
463
- async def call_tool(name: str, arguments: dict):
464
- return await server.handle_tool_call(name, arguments)
465
-
466
- print("🚀 Mobile MCP Server (简化版) 启动中...", file=sys.stderr)
467
- print("📋 基础工具:总是可用(不需要 AI)", file=sys.stderr)
468
- print("🤖 智能工具:需要配置 AI 密钥(可选)", file=sys.stderr)
469
-
470
- async with stdio_server() as (read_stream, write_stream):
471
- await mcp_server.run(read_stream, write_stream, mcp_server.create_initialization_options())
472
-
473
-
474
- if __name__ == "__main__":
475
- asyncio.run(main())
476
-