mobile-mcp-ai 2.6.12__tar.gz → 2.7.0__tar.gz

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 (61) hide show
  1. {mobile_mcp_ai-2.6.12/mobile_mcp_ai.egg-info → mobile_mcp_ai-2.7.0}/PKG-INFO +47 -19
  2. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/README.md +39 -18
  3. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/basic_tools_lite.py +39 -8
  4. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/mcp_tools/mcp_server.py +134 -98
  5. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0/mobile_mcp_ai.egg-info}/PKG-INFO +47 -19
  6. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/mobile_mcp_ai.egg-info/SOURCES.txt +10 -5
  7. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/mobile_mcp_ai.egg-info/requires.txt +8 -0
  8. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/setup.py +10 -1
  9. mobile_mcp_ai-2.7.0/tests/test_mind_cloud_my_space.py +80 -0
  10. mobile_mcp_ai-2.7.0/tests/test_mind_correct.py +73 -0
  11. mobile_mcp_ai-2.7.0/tests/test_mind_improved.py +83 -0
  12. mobile_mcp_ai-2.7.0/tests/test_mind_optimized.py +77 -0
  13. mobile_mcp_ai-2.7.0/tests/test_open_mind.py +37 -0
  14. mobile_mcp_ai-2.7.0/tests/test_priority_demo.py +81 -0
  15. mobile_mcp_ai-2.7.0/tests/test_simple.py +76 -0
  16. mobile_mcp_ai-2.7.0/tests/test_/344/270/276/346/212/245.py +136 -0
  17. mobile_mcp_ai-2.7.0/tests/test_/345/210/207/346/215/242/350/257/255/350/250/200/345/210/260English.py +158 -0
  18. mobile_mcp_ai-2.7.0/tests/test_/346/265/213/350/257/225.py +114 -0
  19. mobile_mcp_ai-2.6.12/core/tool_selection_helper.py +0 -168
  20. mobile_mcp_ai-2.6.12/docs/EXECUTION_BEST_PRACTICES.md +0 -230
  21. mobile_mcp_ai-2.6.12/docs/STRATEGY_SUMMARY.md +0 -129
  22. mobile_mcp_ai-2.6.12/docs/TOOL_SELECTION_STRATEGY.md +0 -208
  23. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/LICENSE +0 -0
  24. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/MANIFEST.in +0 -0
  25. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/__init__.py +0 -0
  26. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/config.py +0 -0
  27. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/__init__.py +0 -0
  28. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/device_manager.py +0 -0
  29. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/dynamic_config.py +0 -0
  30. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/ios_client_wda.py +0 -0
  31. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/ios_device_manager_wda.py +0 -0
  32. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/mobile_client.py +0 -0
  33. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/template_matcher.py +0 -0
  34. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/templates/close_buttons/auto_x_0112_151217.png +0 -0
  35. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/templates/close_buttons/auto_x_0112_152037.png +0 -0
  36. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/templates/close_buttons/auto_x_0112_152840.png +0 -0
  37. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/templates/close_buttons/auto_x_0112_153256.png +0 -0
  38. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/templates/close_buttons/auto_x_0112_154847.png +0 -0
  39. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/templates/close_buttons/gray_x_stock_ad.png +0 -0
  40. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/utils/__init__.py +0 -0
  41. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/utils/logger.py +0 -0
  42. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/utils/operation_history_manager.py +0 -0
  43. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/core/utils/smart_wait.py +0 -0
  44. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/docs/iOS_SETUP_GUIDE.md +0 -0
  45. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/mcp_tools/__init__.py +0 -0
  46. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/mobile_mcp_ai.egg-info/dependency_links.txt +0 -0
  47. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/mobile_mcp_ai.egg-info/entry_points.txt +0 -0
  48. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/mobile_mcp_ai.egg-info/not-zip-safe +0 -0
  49. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/mobile_mcp_ai.egg-info/top_level.txt +0 -0
  50. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/requirements.txt +0 -0
  51. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/setup.cfg +0 -0
  52. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/templates/close_buttons/auto_x_0112_151217.png +0 -0
  53. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/templates/close_buttons/auto_x_0112_152037.png +0 -0
  54. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/templates/close_buttons/auto_x_0112_152840.png +0 -0
  55. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/templates/close_buttons/auto_x_0112_153256.png +0 -0
  56. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/templates/close_buttons/auto_x_0112_154847.png +0 -0
  57. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/templates/close_buttons/gray_x_stock_ad.png +0 -0
  58. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/utils/__init__.py +0 -0
  59. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/utils/logger.py +0 -0
  60. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/utils/xml_formatter.py +0 -0
  61. {mobile_mcp_ai-2.6.12 → mobile_mcp_ai-2.7.0}/utils/xml_parser.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mobile-mcp-ai
3
- Version: 2.6.12
3
+ Version: 2.7.0
4
4
  Summary: 移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)
5
5
  Home-page: https://github.com/test111ddff-hash/mobile-mcp-ai
6
6
  Author: douzi
@@ -46,6 +46,10 @@ Requires-Dist: facebook-wda>=1.4.0; extra == "ios"
46
46
  Provides-Extra: h5
47
47
  Requires-Dist: Appium-Python-Client>=3.0.0; extra == "h5"
48
48
  Requires-Dist: selenium>=4.0.0; extra == "h5"
49
+ Provides-Extra: windows
50
+ Requires-Dist: pyautogui>=0.9.0; extra == "windows"
51
+ Requires-Dist: pyperclip>=1.8.0; extra == "windows"
52
+ Requires-Dist: pygetwindow>=0.0.9; extra == "windows"
49
53
  Provides-Extra: all
50
54
  Requires-Dist: dashscope>=1.10.0; extra == "all"
51
55
  Requires-Dist: openai>=1.0.0; extra == "all"
@@ -55,6 +59,9 @@ Requires-Dist: selenium>=4.0.0; extra == "all"
55
59
  Requires-Dist: pytest>=8.0.0; extra == "all"
56
60
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
57
61
  Requires-Dist: allure-pytest>=2.13.0; extra == "all"
62
+ Requires-Dist: pyautogui>=0.9.0; extra == "all"
63
+ Requires-Dist: pyperclip>=1.8.0; extra == "all"
64
+ Requires-Dist: pygetwindow>=0.0.9; extra == "all"
58
65
  Dynamic: author
59
66
  Dynamic: author-email
60
67
  Dynamic: classifier
@@ -336,6 +343,34 @@ tidevice list
336
343
 
337
344
  保存后**重启 Cursor**。
338
345
 
346
+ ### 批量执行用例(飞书集成)
347
+
348
+ 如果你需要从飞书多维表格批量执行用例,`mobile_open_new_chat` 功能会自动打开新会话继续执行。
349
+
350
+ **macOS 用户:** 需要开启辅助功能权限
351
+
352
+ | 步骤 | 操作 |
353
+ |:---:|------|
354
+ | 1 | 打开「系统设置」 |
355
+ | 2 | 点击「隐私与安全性」 |
356
+ | 3 | 点击「辅助功能」 |
357
+ | 4 | 点击 + 号,添加 **Cursor.app** |
358
+ | 5 | 确保开关已打开 ✅ |
359
+
360
+ > ⚠️ 没有此权限,无法自动打开新会话继续执行
361
+
362
+ **Windows 用户:** 需要安装额外依赖
363
+
364
+ ```bash
365
+ pip install mobile-mcp-ai[windows]
366
+ ```
367
+
368
+ 或手动安装:
369
+
370
+ ```bash
371
+ pip install pyautogui pyperclip pygetwindow
372
+ ```
373
+
339
374
  ---
340
375
 
341
376
  ## 🚀 使用示例
@@ -404,24 +439,17 @@ tidevice list
404
439
 
405
440
  ## 🛠️ 工具列表
406
441
 
407
- | 类别 | 工具 | 说明 | 优先级 |
408
- |:---:|------|------|:---:|
409
- | 📋 | `mobile_list_elements` | 列出页面元素 | ⭐⭐⭐ 第一优先级 |
410
- | 👆 | `mobile_click_by_text` | 文本点击 | ⭐⭐⭐ 第一优先级 |
411
- | 👆 | `mobile_click_by_id` | ID 点击 | ⭐⭐⭐ 第一优先级 |
412
- | 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) | ⚠️ 兜底方案 |
413
- | 📸 | `mobile_take_screenshot` | 截图 | ⚠️ 兜底方案 |
414
- | 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 | ⚠️ 兜底方案 |
415
- | 👆 | `mobile_click_by_som` | SoM 编号点击 | ⚠️ 兜底方案 |
416
- | 👆 | `mobile_click_by_percent` | 百分比点击 | ⚠️ 最后兜底 |
417
- | 👆 | `mobile_click_at_coords` | 坐标点击 | ⚠️ 最后兜底 |
418
- | 📐 | `mobile_get_screen_size` | 屏幕尺寸 | 辅助工具 |
419
-
420
- > 💡 **工具选择策略**:优先使用控件树定位(`list_elements` + `click_by_text/id`),截图仅作为兜底方案。
421
- >
422
- > 📖 详细指南:
423
- > - [工具选择策略指南](docs/TOOL_SELECTION_STRATEGY.md) - 详细的工具选择策略和决策树
424
- > - [用例执行最佳实践](docs/EXECUTION_BEST_PRACTICES.md) - 用例执行流程和优化建议
442
+ | 类别 | 工具 | 说明 |
443
+ |:---:|------|------|
444
+ | 📋 | `mobile_list_elements` | 列出页面元素 |
445
+ | 📸 | `mobile_take_screenshot` | 截图 |
446
+ | 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) |
447
+ | 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 |
448
+ | 📐 | `mobile_get_screen_size` | 屏幕尺寸 |
449
+ | 👆 | `mobile_click_by_text` | 文本点击 |
450
+ | 👆 | `mobile_click_by_id` | ID 点击 |
451
+ | 👆 | `mobile_click_at_coords` | 坐标点击 |
452
+ | 👆 | `mobile_click_by_percent` | 百分比点击 |
425
453
  | 👆 | `mobile_click_by_som` | SoM 编号点击 |
426
454
  | 👆 | `mobile_long_press_by_id` | ID 长按 |
427
455
  | 👆 | `mobile_long_press_by_text` | 文本长按 |
@@ -265,6 +265,34 @@ tidevice list
265
265
 
266
266
  保存后**重启 Cursor**。
267
267
 
268
+ ### 批量执行用例(飞书集成)
269
+
270
+ 如果你需要从飞书多维表格批量执行用例,`mobile_open_new_chat` 功能会自动打开新会话继续执行。
271
+
272
+ **macOS 用户:** 需要开启辅助功能权限
273
+
274
+ | 步骤 | 操作 |
275
+ |:---:|------|
276
+ | 1 | 打开「系统设置」 |
277
+ | 2 | 点击「隐私与安全性」 |
278
+ | 3 | 点击「辅助功能」 |
279
+ | 4 | 点击 + 号,添加 **Cursor.app** |
280
+ | 5 | 确保开关已打开 ✅ |
281
+
282
+ > ⚠️ 没有此权限,无法自动打开新会话继续执行
283
+
284
+ **Windows 用户:** 需要安装额外依赖
285
+
286
+ ```bash
287
+ pip install mobile-mcp-ai[windows]
288
+ ```
289
+
290
+ 或手动安装:
291
+
292
+ ```bash
293
+ pip install pyautogui pyperclip pygetwindow
294
+ ```
295
+
268
296
  ---
269
297
 
270
298
  ## 🚀 使用示例
@@ -333,24 +361,17 @@ tidevice list
333
361
 
334
362
  ## 🛠️ 工具列表
335
363
 
336
- | 类别 | 工具 | 说明 | 优先级 |
337
- |:---:|------|------|:---:|
338
- | 📋 | `mobile_list_elements` | 列出页面元素 | ⭐⭐⭐ 第一优先级 |
339
- | 👆 | `mobile_click_by_text` | 文本点击 | ⭐⭐⭐ 第一优先级 |
340
- | 👆 | `mobile_click_by_id` | ID 点击 | ⭐⭐⭐ 第一优先级 |
341
- | 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) | ⚠️ 兜底方案 |
342
- | 📸 | `mobile_take_screenshot` | 截图 | ⚠️ 兜底方案 |
343
- | 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 | ⚠️ 兜底方案 |
344
- | 👆 | `mobile_click_by_som` | SoM 编号点击 | ⚠️ 兜底方案 |
345
- | 👆 | `mobile_click_by_percent` | 百分比点击 | ⚠️ 最后兜底 |
346
- | 👆 | `mobile_click_at_coords` | 坐标点击 | ⚠️ 最后兜底 |
347
- | 📐 | `mobile_get_screen_size` | 屏幕尺寸 | 辅助工具 |
348
-
349
- > 💡 **工具选择策略**:优先使用控件树定位(`list_elements` + `click_by_text/id`),截图仅作为兜底方案。
350
- >
351
- > 📖 详细指南:
352
- > - [工具选择策略指南](docs/TOOL_SELECTION_STRATEGY.md) - 详细的工具选择策略和决策树
353
- > - [用例执行最佳实践](docs/EXECUTION_BEST_PRACTICES.md) - 用例执行流程和优化建议
364
+ | 类别 | 工具 | 说明 |
365
+ |:---:|------|------|
366
+ | 📋 | `mobile_list_elements` | 列出页面元素 |
367
+ | 📸 | `mobile_take_screenshot` | 截图 |
368
+ | 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) |
369
+ | 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 |
370
+ | 📐 | `mobile_get_screen_size` | 屏幕尺寸 |
371
+ | 👆 | `mobile_click_by_text` | 文本点击 |
372
+ | 👆 | `mobile_click_by_id` | ID 点击 |
373
+ | 👆 | `mobile_click_at_coords` | 坐标点击 |
374
+ | 👆 | `mobile_click_by_percent` | 百分比点击 |
354
375
  | 👆 | `mobile_click_by_som` | SoM 编号点击 |
355
376
  | 👆 | `mobile_long_press_by_id` | ID 长按 |
356
377
  | 👆 | `mobile_long_press_by_text` | 文本长按 |
@@ -2197,6 +2197,13 @@ class BasicMobileToolsLite:
2197
2197
  def wait(self, seconds: float) -> Dict:
2198
2198
  """等待指定时间"""
2199
2199
  time.sleep(seconds)
2200
+ # 记录等待操作
2201
+ record = {
2202
+ 'action': 'wait',
2203
+ 'timestamp': datetime.now().isoformat(),
2204
+ 'seconds': seconds,
2205
+ }
2206
+ self.operation_history.append(record)
2200
2207
  return {"success": True}
2201
2208
 
2202
2209
  # ==================== 应用管理 ====================
@@ -2353,6 +2360,15 @@ class BasicMobileToolsLite:
2353
2360
  '_shadow', 'shadow_', '_divider', 'divider_', '_line', 'line_'
2354
2361
  }
2355
2362
 
2363
+ # 状态栏相关关键词(这些元素对测试没有意义,直接过滤)
2364
+ STATUS_BAR_KEYWORDS = {
2365
+ 'status_bar', 'statusbar', 'notification_icon', 'notificationicons',
2366
+ 'system_icons', 'statusicons', 'battery', 'wifi_', 'wifi_combo',
2367
+ 'wifi_group', 'wifi_signal', 'wifi_in', 'wifi_out', 'signal_',
2368
+ 'clock', 'cutout', 'networkspeed', 'speed_container',
2369
+ 'carrier', 'operator', 'sim_', 'mobile_signal'
2370
+ }
2371
+
2356
2372
  # Token 优化:构建精简元素(只返回非空字段)
2357
2373
  def build_compact_element(resource_id, text, content_desc, bounds, likely_click, class_name):
2358
2374
  """只返回有值的字段,节省 token"""
@@ -2390,6 +2406,12 @@ class BasicMobileToolsLite:
2390
2406
  if bounds == '[0,0][0,0]':
2391
2407
  continue
2392
2408
 
2409
+ # 1.5 过滤状态栏元素(对测试没有意义)
2410
+ if resource_id:
2411
+ resource_id_lower = resource_id.lower()
2412
+ if any(keyword in resource_id_lower for keyword in STATUS_BAR_KEYWORDS):
2413
+ continue
2414
+
2393
2415
  # 2. 检查是否是功能控件(直接保留)
2394
2416
  if class_name in FUNCTIONAL_WIDGETS:
2395
2417
  # 使用启发式判断可点击性(替代不准确的 clickable 属性)
@@ -3379,6 +3401,15 @@ class BasicMobileToolsLite:
3379
3401
 
3380
3402
  # 生成脚本
3381
3403
  safe_name = re.sub(r'[^\w\s-]', '', test_name).strip().replace(' ', '_')
3404
+ # 确保 safe_name 不为空,否则使用默认名称
3405
+ if not safe_name:
3406
+ safe_name = 'generated_case'
3407
+
3408
+ # 提前处理文件名,确保文档字符串中的文件名正确
3409
+ if not filename.endswith('.py'):
3410
+ filename = f"{filename}.py"
3411
+ if not filename.startswith('test_'):
3412
+ filename = f"test_{filename}"
3382
3413
 
3383
3414
  script_lines = [
3384
3415
  "#!/usr/bin/env python3",
@@ -3393,8 +3424,8 @@ class BasicMobileToolsLite:
3393
3424
  "3. 百分比定位 - 跨分辨率兼容(坐标自动转换)",
3394
3425
  "",
3395
3426
  "运行方式:",
3396
- " pytest {filename} -v # 使用 pytest 运行",
3397
- " python {filename} # 直接运行",
3427
+ f" pytest {filename} -v # 使用 pytest 运行",
3428
+ f" python {filename} # 直接运行",
3398
3429
  f'"""',
3399
3430
  "import time",
3400
3431
  "import pytest",
@@ -3655,6 +3686,12 @@ class BasicMobileToolsLite:
3655
3686
  script_lines.append(f" d.press('{key}')")
3656
3687
  script_lines.append(" time.sleep(0.5)")
3657
3688
  script_lines.append(" ")
3689
+
3690
+ elif action == 'wait':
3691
+ seconds = op.get('seconds', 1)
3692
+ script_lines.append(f" # 步骤{step_num}: 等待 {seconds} 秒")
3693
+ script_lines.append(f" time.sleep({seconds})")
3694
+ script_lines.append(" ")
3658
3695
 
3659
3696
  script_lines.extend([
3660
3697
  " print('✅ 测试完成')",
@@ -3678,12 +3715,6 @@ class BasicMobileToolsLite:
3678
3715
  output_dir = Path("tests")
3679
3716
  output_dir.mkdir(exist_ok=True)
3680
3717
 
3681
- # 确保文件名符合 pytest 规范(以 test_ 开头)
3682
- if not filename.endswith('.py'):
3683
- filename = f"{filename}.py"
3684
- if not filename.startswith('test_'):
3685
- filename = f"test_{filename}"
3686
-
3687
3718
  file_path = output_dir / filename
3688
3719
  file_path.write_text(script, encoding='utf-8')
3689
3720
 
@@ -204,6 +204,115 @@ class MobileMCPServer:
204
204
 
205
205
  return "android"
206
206
 
207
+ def _open_new_chat(self, message: str = "继续执行飞书用例", delay: float = 5) -> dict:
208
+ """
209
+ 打开新Chat窗口并发送消息
210
+
211
+ 原理:用后台线程延迟执行键盘操作,避免打断当前AI响应
212
+ 跨平台统一用 pyautogui + pyperclip,配合系统API激活窗口
213
+ """
214
+ import threading
215
+ import platform
216
+
217
+ def delayed_action():
218
+ import time
219
+ time.sleep(delay) # 等待当前响应结束
220
+
221
+ try:
222
+ system = platform.system()
223
+
224
+ if system == "Darwin": # ========== macOS: 纯 AppleScript(无需额外安装)==========
225
+ import subprocess
226
+
227
+ # AppleScript: 激活Cursor → Cmd+L聚焦Chat → Cmd+N新建 → 粘贴 → 回车
228
+ script = f'''
229
+ -- 设置剪贴板(支持中文)
230
+ set the clipboard to "{message}"
231
+
232
+ -- 激活 Cursor
233
+ tell application "Cursor" to activate
234
+ delay 0.5
235
+
236
+ tell application "System Events"
237
+ tell process "Cursor"
238
+ -- Cmd+L 聚焦 Chat
239
+ keystroke "l" using command down
240
+ delay 0.5
241
+ -- Cmd+N 新建对话
242
+ keystroke "n" using command down
243
+ delay 1
244
+ -- Cmd+V 粘贴
245
+ keystroke "v" using command down
246
+ delay 0.3
247
+ -- Enter 发送
248
+ key code 36
249
+ end tell
250
+ end tell
251
+ '''
252
+ result = subprocess.run(['osascript', '-e', script], capture_output=True, text=True)
253
+ if result.returncode == 0:
254
+ print(f"[open_new_chat] macOS成功: {message}", file=sys.stderr)
255
+ else:
256
+ print(f"[open_new_chat] AppleScript错误: {result.stderr}", file=sys.stderr)
257
+
258
+ elif system == "Windows": # ========== Windows: 需要 pip install pyautogui pyperclip ==========
259
+ import pyautogui
260
+ import pyperclip
261
+
262
+ # 尝试激活 Cursor 窗口
263
+ try:
264
+ import pygetwindow as gw
265
+ windows = gw.getWindowsWithTitle('Cursor')
266
+ if windows:
267
+ windows[0].activate()
268
+ time.sleep(0.5)
269
+ except ImportError:
270
+ pass
271
+
272
+ pyautogui.PAUSE = 0.3
273
+
274
+ # Ctrl+L 聚焦 Chat → Ctrl+N 新建
275
+ pyautogui.hotkey('ctrl', 'l')
276
+ time.sleep(0.5)
277
+ pyautogui.hotkey('ctrl', 'n')
278
+ time.sleep(1)
279
+
280
+ # 粘贴消息
281
+ pyperclip.copy(message)
282
+ pyautogui.hotkey('ctrl', 'v')
283
+ time.sleep(0.3)
284
+ pyautogui.press('enter')
285
+
286
+ print(f"[open_new_chat] Windows成功: {message}", file=sys.stderr)
287
+
288
+ else: # Linux: 写信号文件
289
+ from pathlib import Path
290
+ signal_file = Path(__file__).parent.parent / ".new_chat_signal"
291
+ signal_file.write_text(message)
292
+ print(f"[open_new_chat] Linux: 已写入信号文件", file=sys.stderr)
293
+
294
+ except ImportError as e:
295
+ # Windows 缺少依赖时写信号文件
296
+ from pathlib import Path
297
+ signal_file = Path(__file__).parent.parent / ".new_chat_signal"
298
+ signal_file.write_text(message)
299
+ print(f"[open_new_chat] Windows缺少依赖,请运行: pip install pyautogui pyperclip", file=sys.stderr)
300
+
301
+ except Exception as e:
302
+ # 写错误日志
303
+ import sys
304
+ print(f"[open_new_chat] 错误: {e}", file=sys.stderr)
305
+
306
+ # 后台线程执行,不阻塞MCP响应
307
+ thread = threading.Thread(target=delayed_action, daemon=True)
308
+ thread.start()
309
+
310
+ return {
311
+ "success": True,
312
+ "message": f"⏰ {delay}秒后将打开新Chat并发送: {message}",
313
+ "tip": "请不要手动操作,等待自动执行"
314
+ }
315
+
207
316
  def get_tools(self):
208
317
  """注册 MCP 工具"""
209
318
  tools = []
@@ -226,36 +335,7 @@ class MobileMCPServer:
226
335
 
227
336
  tools.append(Tool(
228
337
  name="mobile_list_elements",
229
- description="""📋 列出页面所有可交互元素(⭐⭐ 第一优先级!)
230
-
231
- ⚠️ 【核心原则】优先使用控件树定位,截图仅作为兜底方案
232
-
233
- 🎯 使用场景(优先使用):
234
- 1. ✅ 首次进入页面时:获取所有可交互元素
235
- 2. ✅ 查找特定元素时:检查元素是否存在
236
- 3. ✅ 验证操作结果时:确认元素是否出现/消失
237
- 4. ✅ 需要精确点击时:获取元素的 resource_id 或 text
238
-
239
- 📌 控件树定位优势:
240
- - ⚡ 速度快:直接获取 XML,无需截图和图像处理(快 5-20 倍)
241
- - 🎯 准确:获取完整的元素属性(resource_id、text、bounds、clickable 等)
242
- - 💾 高效:不生成图片文件,节省存储和传输
243
- - 🔄 可复用:一次获取的元素列表可用于多次操作
244
-
245
- ⚡ 推荐流程:
246
- 1. 调用 mobile_list_elements() 获取元素列表
247
- 2. 从元素列表中找到目标元素(通过 text 或 resource_id)
248
- 3. 使用 mobile_click_by_text() 或 mobile_click_by_id() 点击
249
- 4. 仅在控件树找不到元素时,才使用 mobile_screenshot_with_som()
250
-
251
- ❌ 错误用法:
252
- - 每次都先截图,不先调用 list_elements
253
- - 重复调用 list_elements,不复用已获取的元素列表
254
-
255
- ✅ 正确用法:
256
- elements = mobile_list_elements() # 一次获取
257
- mobile_click_by_text("设置") # 直接点击
258
- mobile_click_by_text("退出登录") # 复用元素列表""",
338
+ description=desc_list_elements,
259
339
  inputSchema={"type": "object", "properties": {}, "required": []}
260
340
  ))
261
341
 
@@ -309,49 +389,8 @@ mobile_click_by_text("退出登录") # 复用元素列表""",
309
389
 
310
390
  tools.append(Tool(
311
391
  name="mobile_screenshot_with_som",
312
- description="""📸🏷️ Set-of-Mark 截图(⚠️ 兜底方案,非首选)
313
-
314
- ⚠️ 【重要】仅在控件树定位失败时使用!
315
-
316
- 🎯 使用场景(兜底方案):
317
- 1. ⚠️ 控件树定位失败:
318
- - mobile_list_elements() 返回空或找不到目标元素
319
- - 元素不在控件树中(如游戏、Unity 应用)
320
- 2. ✅ 需要视觉确认:
321
- - 首次进入新页面,需要了解整体布局
322
- - 操作后需要视觉确认页面变化
323
- - 需要给用户展示页面状态
324
-
325
- ⚡ 推荐流程(优先使用控件树):
326
- 1. 先调用 mobile_list_elements() 获取元素列表
327
- 2. 如果找到目标元素 → 使用 mobile_click_by_text() 或 mobile_click_by_id()
328
- 3. 如果找不到 → 才调用 mobile_screenshot_with_som()
329
- 4. 从截图分析找到元素编号 → 使用 mobile_click_by_som(编号)
330
-
331
- ❌ 错误用法:
332
- - 每次都先截图,不先调用 list_elements
333
- - 在控件树能找到元素时也使用截图
334
-
335
- ✅ 正确用法:
336
- elements = mobile_list_elements() # 先尝试控件树
337
- if not find_target(elements):
338
- screenshot = mobile_screenshot_with_som() # 控件树失败才截图
339
- mobile_click_by_som(index)
340
-
341
- 💡 弹窗检测:
342
- - check_popup=True: 明确弹窗场景时使用(如调用 mobile_close_popup 前)
343
- - check_popup=False: 普通截图,不检测弹窗(默认)""",
344
- inputSchema={
345
- "type": "object",
346
- "properties": {
347
- "check_popup": {
348
- "type": "boolean",
349
- "description": "是否检测弹窗,默认 False。仅在明确弹窗场景时设置为 True",
350
- "default": False
351
- }
352
- },
353
- "required": []
354
- }
392
+ description=desc_som,
393
+ inputSchema={"type": "object", "properties": {}, "required": []}
355
394
  ))
356
395
 
357
396
  tools.append(Tool(
@@ -397,31 +436,7 @@ if not find_target(elements):
397
436
 
398
437
  tools.append(Tool(
399
438
  name="mobile_click_by_text",
400
- description="""👆 通过文本点击元素(⭐⭐ 第一优先级点击方式)
401
-
402
- ✅ 最稳定的定位方式,跨设备兼容
403
- ✅ 实时检测元素是否存在,元素不存在会报错
404
- ✅ 不会误点击到其他位置
405
-
406
- ⚡ 使用流程(必须):
407
- 1. 先调用 mobile_list_elements() 获取元素列表
408
- 2. 从元素列表中找到目标元素的 text
409
- 3. 调用 mobile_click_by_text("文本") 点击
410
-
411
- 💡 定位优先级:文本 > ID > 百分比 > 坐标
412
-
413
- 📍 当页面有多个相同文案时,可使用 position 参数指定位置:
414
- - 垂直方向: "top"/"upper"/"上", "bottom"/"lower"/"下", "middle"/"center"/"中"
415
- - 水平方向: "left"/"左", "right"/"右", "center"/"中"
416
- 例如:点击"底部"的"微剧"tab,使用 position="bottom"
417
-
418
- ❌ 错误用法:
419
- - 不先调用 list_elements,直接猜测文本点击
420
- - 在控件树能找到元素时使用截图+坐标点击
421
-
422
- ✅ 正确用法:
423
- elements = mobile_list_elements() # 先获取元素列表
424
- mobile_click_by_text("设置") # 从元素列表中找到后直接点击""",
439
+ description=desc_click_text,
425
440
  inputSchema={
426
441
  "type": "object",
427
442
  "properties": {
@@ -881,6 +896,20 @@ mobile_click_by_text("设置") # 从元素列表中找到后直接点击"
881
896
  }
882
897
  ))
883
898
 
899
+ # ==================== Cursor自动化 ====================
900
+ tools.append(Tool(
901
+ name="mobile_open_new_chat",
902
+ description="🔄 打开新Chat继续执行。执行完10个用例后调用,会延迟5秒后按Cmd+T打开新会话并输入继续命令。",
903
+ inputSchema={
904
+ "type": "object",
905
+ "properties": {
906
+ "message": {"type": "string", "description": "新会话中要发送的消息", "default": "继续执行飞书用例"},
907
+ "delay": {"type": "number", "description": "延迟秒数(等待当前响应结束)", "default": 5}
908
+ },
909
+ "required": []
910
+ }
911
+ ))
912
+
884
913
  return tools
885
914
 
886
915
  async def handle_tool_call(self, name: str, arguments: dict):
@@ -1145,6 +1174,13 @@ mobile_click_by_text("设置") # 从元素列表中找到后直接点击"
1145
1174
  result = {"success": False, "error": "请提供 x_percent/y_percent 或 screenshot_path/x/y/width/height"}
1146
1175
  return [TextContent(type="text", text=self.format_response(result))]
1147
1176
 
1177
+ # Cursor自动化:打开新Chat
1178
+ elif name == "mobile_open_new_chat":
1179
+ message = arguments.get("message", "继续执行飞书用例")
1180
+ delay = arguments.get("delay", 5)
1181
+ result = self._open_new_chat(message, delay)
1182
+ return [TextContent(type="text", text=self.format_response(result))]
1183
+
1148
1184
  else:
1149
1185
  return [TextContent(type="text", text=f"❌ 未知工具: {name}")]
1150
1186
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mobile-mcp-ai
3
- Version: 2.6.12
3
+ Version: 2.7.0
4
4
  Summary: 移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)
5
5
  Home-page: https://github.com/test111ddff-hash/mobile-mcp-ai
6
6
  Author: douzi
@@ -46,6 +46,10 @@ Requires-Dist: facebook-wda>=1.4.0; extra == "ios"
46
46
  Provides-Extra: h5
47
47
  Requires-Dist: Appium-Python-Client>=3.0.0; extra == "h5"
48
48
  Requires-Dist: selenium>=4.0.0; extra == "h5"
49
+ Provides-Extra: windows
50
+ Requires-Dist: pyautogui>=0.9.0; extra == "windows"
51
+ Requires-Dist: pyperclip>=1.8.0; extra == "windows"
52
+ Requires-Dist: pygetwindow>=0.0.9; extra == "windows"
49
53
  Provides-Extra: all
50
54
  Requires-Dist: dashscope>=1.10.0; extra == "all"
51
55
  Requires-Dist: openai>=1.0.0; extra == "all"
@@ -55,6 +59,9 @@ Requires-Dist: selenium>=4.0.0; extra == "all"
55
59
  Requires-Dist: pytest>=8.0.0; extra == "all"
56
60
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
57
61
  Requires-Dist: allure-pytest>=2.13.0; extra == "all"
62
+ Requires-Dist: pyautogui>=0.9.0; extra == "all"
63
+ Requires-Dist: pyperclip>=1.8.0; extra == "all"
64
+ Requires-Dist: pygetwindow>=0.0.9; extra == "all"
58
65
  Dynamic: author
59
66
  Dynamic: author-email
60
67
  Dynamic: classifier
@@ -336,6 +343,34 @@ tidevice list
336
343
 
337
344
  保存后**重启 Cursor**。
338
345
 
346
+ ### 批量执行用例(飞书集成)
347
+
348
+ 如果你需要从飞书多维表格批量执行用例,`mobile_open_new_chat` 功能会自动打开新会话继续执行。
349
+
350
+ **macOS 用户:** 需要开启辅助功能权限
351
+
352
+ | 步骤 | 操作 |
353
+ |:---:|------|
354
+ | 1 | 打开「系统设置」 |
355
+ | 2 | 点击「隐私与安全性」 |
356
+ | 3 | 点击「辅助功能」 |
357
+ | 4 | 点击 + 号,添加 **Cursor.app** |
358
+ | 5 | 确保开关已打开 ✅ |
359
+
360
+ > ⚠️ 没有此权限,无法自动打开新会话继续执行
361
+
362
+ **Windows 用户:** 需要安装额外依赖
363
+
364
+ ```bash
365
+ pip install mobile-mcp-ai[windows]
366
+ ```
367
+
368
+ 或手动安装:
369
+
370
+ ```bash
371
+ pip install pyautogui pyperclip pygetwindow
372
+ ```
373
+
339
374
  ---
340
375
 
341
376
  ## 🚀 使用示例
@@ -404,24 +439,17 @@ tidevice list
404
439
 
405
440
  ## 🛠️ 工具列表
406
441
 
407
- | 类别 | 工具 | 说明 | 优先级 |
408
- |:---:|------|------|:---:|
409
- | 📋 | `mobile_list_elements` | 列出页面元素 | ⭐⭐⭐ 第一优先级 |
410
- | 👆 | `mobile_click_by_text` | 文本点击 | ⭐⭐⭐ 第一优先级 |
411
- | 👆 | `mobile_click_by_id` | ID 点击 | ⭐⭐⭐ 第一优先级 |
412
- | 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) | ⚠️ 兜底方案 |
413
- | 📸 | `mobile_take_screenshot` | 截图 | ⚠️ 兜底方案 |
414
- | 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 | ⚠️ 兜底方案 |
415
- | 👆 | `mobile_click_by_som` | SoM 编号点击 | ⚠️ 兜底方案 |
416
- | 👆 | `mobile_click_by_percent` | 百分比点击 | ⚠️ 最后兜底 |
417
- | 👆 | `mobile_click_at_coords` | 坐标点击 | ⚠️ 最后兜底 |
418
- | 📐 | `mobile_get_screen_size` | 屏幕尺寸 | 辅助工具 |
419
-
420
- > 💡 **工具选择策略**:优先使用控件树定位(`list_elements` + `click_by_text/id`),截图仅作为兜底方案。
421
- >
422
- > 📖 详细指南:
423
- > - [工具选择策略指南](docs/TOOL_SELECTION_STRATEGY.md) - 详细的工具选择策略和决策树
424
- > - [用例执行最佳实践](docs/EXECUTION_BEST_PRACTICES.md) - 用例执行流程和优化建议
442
+ | 类别 | 工具 | 说明 |
443
+ |:---:|------|------|
444
+ | 📋 | `mobile_list_elements` | 列出页面元素 |
445
+ | 📸 | `mobile_take_screenshot` | 截图 |
446
+ | 📸 | `mobile_screenshot_with_som` | Set-of-Mark 截图(智能标注) |
447
+ | 📸 | `mobile_screenshot_with_grid` | 带网格坐标的截图 |
448
+ | 📐 | `mobile_get_screen_size` | 屏幕尺寸 |
449
+ | 👆 | `mobile_click_by_text` | 文本点击 |
450
+ | 👆 | `mobile_click_by_id` | ID 点击 |
451
+ | 👆 | `mobile_click_at_coords` | 坐标点击 |
452
+ | 👆 | `mobile_click_by_percent` | 百分比点击 |
425
453
  | 👆 | `mobile_click_by_som` | SoM 编号点击 |
426
454
  | 👆 | `mobile_long_press_by_id` | ID 长按 |
427
455
  | 👆 | `mobile_long_press_by_text` | 文本长按 |
@@ -15,7 +15,6 @@ setup.py
15
15
  ./core/ios_device_manager_wda.py
16
16
  ./core/mobile_client.py
17
17
  ./core/template_matcher.py
18
- ./core/tool_selection_helper.py
19
18
  ./core/templates/close_buttons/auto_x_0112_151217.png
20
19
  ./core/templates/close_buttons/auto_x_0112_152037.png
21
20
  ./core/templates/close_buttons/auto_x_0112_152840.png
@@ -40,7 +39,6 @@ core/ios_client_wda.py
40
39
  core/ios_device_manager_wda.py
41
40
  core/mobile_client.py
42
41
  core/template_matcher.py
43
- core/tool_selection_helper.py
44
42
  core/templates/close_buttons/auto_x_0112_151217.png
45
43
  core/templates/close_buttons/auto_x_0112_152037.png
46
44
  core/templates/close_buttons/auto_x_0112_152840.png
@@ -51,9 +49,6 @@ core/utils/__init__.py
51
49
  core/utils/logger.py
52
50
  core/utils/operation_history_manager.py
53
51
  core/utils/smart_wait.py
54
- docs/EXECUTION_BEST_PRACTICES.md
55
- docs/STRATEGY_SUMMARY.md
56
- docs/TOOL_SELECTION_STRATEGY.md
57
52
  docs/iOS_SETUP_GUIDE.md
58
53
  mcp_tools/__init__.py
59
54
  mcp_tools/mcp_server.py
@@ -70,6 +65,16 @@ templates/close_buttons/auto_x_0112_152840.png
70
65
  templates/close_buttons/auto_x_0112_153256.png
71
66
  templates/close_buttons/auto_x_0112_154847.png
72
67
  templates/close_buttons/gray_x_stock_ad.png
68
+ tests/test_mind_cloud_my_space.py
69
+ tests/test_mind_correct.py
70
+ tests/test_mind_improved.py
71
+ tests/test_mind_optimized.py
72
+ tests/test_open_mind.py
73
+ tests/test_priority_demo.py
74
+ tests/test_simple.py
75
+ tests/test_举报.py
76
+ tests/test_切换语言到English.py
77
+ tests/test_测试.py
73
78
  utils/__init__.py
74
79
  utils/logger.py
75
80
  utils/xml_formatter.py