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/core/smart_tools.py
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
智能 MCP 工具 - 需要 AI 密钥(可选功能)
|
|
5
|
-
|
|
6
|
-
提供智能定位和分析功能:
|
|
7
|
-
- 自然语言元素定位
|
|
8
|
-
- 智能元素识别
|
|
9
|
-
- 复杂场景分析
|
|
10
|
-
|
|
11
|
-
⚠️ 这些功能需要配置 AI 密钥才能使用
|
|
12
|
-
"""
|
|
13
|
-
|
|
14
|
-
from typing import Dict, Optional
|
|
15
|
-
import os
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class SmartMobileTools:
|
|
19
|
-
"""智能移动端工具(需要 AI 密钥)"""
|
|
20
|
-
|
|
21
|
-
def __init__(self, mobile_client):
|
|
22
|
-
"""
|
|
23
|
-
初始化智能工具
|
|
24
|
-
|
|
25
|
-
Args:
|
|
26
|
-
mobile_client: MobileClient 实例
|
|
27
|
-
"""
|
|
28
|
-
self.client = mobile_client
|
|
29
|
-
self.ai_available = self._check_ai_available()
|
|
30
|
-
|
|
31
|
-
if self.ai_available:
|
|
32
|
-
# 延迟导入,避免没有配置 AI 时报错
|
|
33
|
-
from .locator.mobile_smart_locator import MobileSmartLocator
|
|
34
|
-
self.smart_locator = MobileSmartLocator(mobile_client)
|
|
35
|
-
else:
|
|
36
|
-
self.smart_locator = None
|
|
37
|
-
|
|
38
|
-
def _check_ai_available(self) -> bool:
|
|
39
|
-
"""检查 AI 是否可用(是否配置了 AI 密钥)"""
|
|
40
|
-
try:
|
|
41
|
-
from dotenv import load_dotenv
|
|
42
|
-
load_dotenv()
|
|
43
|
-
|
|
44
|
-
ai_provider = os.getenv('AI_PROVIDER', '')
|
|
45
|
-
|
|
46
|
-
# 检查是否配置了任何 AI 提供商
|
|
47
|
-
if ai_provider in ['qwen', 'openai', 'claude', 'ollama']:
|
|
48
|
-
# 检查对应的 API Key
|
|
49
|
-
if ai_provider == 'qwen' and os.getenv('QWEN_API_KEY'):
|
|
50
|
-
return True
|
|
51
|
-
elif ai_provider == 'openai' and os.getenv('OPENAI_API_KEY'):
|
|
52
|
-
return True
|
|
53
|
-
elif ai_provider == 'claude' and os.getenv('ANTHROPIC_API_KEY'):
|
|
54
|
-
return True
|
|
55
|
-
elif ai_provider == 'ollama':
|
|
56
|
-
return True # Ollama 不需要 API Key
|
|
57
|
-
|
|
58
|
-
return False
|
|
59
|
-
except:
|
|
60
|
-
return False
|
|
61
|
-
|
|
62
|
-
def _ensure_ai_available(self):
|
|
63
|
-
"""确保 AI 可用,否则抛出友好的错误提示"""
|
|
64
|
-
if not self.ai_available:
|
|
65
|
-
raise ValueError(
|
|
66
|
-
"❌ 智能定位功能需要配置 AI 密钥!\n\n"
|
|
67
|
-
"请选择以下方案之一:\n\n"
|
|
68
|
-
"方案1:使用基础工具(推荐,不需要 AI)\n"
|
|
69
|
-
" - mobile_list_elements() - 列出所有元素\n"
|
|
70
|
-
" - mobile_click_by_id(resource_id) - 通过 ID 点击\n"
|
|
71
|
-
" - mobile_click_at_coords(x, y) - 通过坐标点击\n\n"
|
|
72
|
-
"方案2:配置 AI 密钥(启用智能功能)\n"
|
|
73
|
-
" 创建 .env 文件:\n"
|
|
74
|
-
" AI_PROVIDER=qwen\n"
|
|
75
|
-
" QWEN_API_KEY=your-api-key\n\n"
|
|
76
|
-
"详见: backend/mobile_mcp/AI_SETUP.md"
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
async def smart_click(self, description: str) -> Dict:
|
|
80
|
-
"""
|
|
81
|
-
智能定位并点击元素(需要 AI 密钥)
|
|
82
|
-
|
|
83
|
-
Args:
|
|
84
|
-
description: 元素的自然语言描述(如 "顶部搜索框"、"登录按钮")
|
|
85
|
-
|
|
86
|
-
Returns:
|
|
87
|
-
{"success": true/false, "message": "...", "method": "..."}
|
|
88
|
-
|
|
89
|
-
示例:
|
|
90
|
-
# 需要先配置 AI 密钥
|
|
91
|
-
result = await tools.smart_click("右上角的设置按钮")
|
|
92
|
-
|
|
93
|
-
⚠️ 如果没有配置 AI 密钥,请使用基础工具:
|
|
94
|
-
elements = mobile_list_elements()
|
|
95
|
-
mobile_click_by_id("com.app:id/settings")
|
|
96
|
-
"""
|
|
97
|
-
self._ensure_ai_available()
|
|
98
|
-
|
|
99
|
-
try:
|
|
100
|
-
# 使用智能定位器
|
|
101
|
-
result = await self.smart_locator.locate(description)
|
|
102
|
-
|
|
103
|
-
if result and result.get('ref'):
|
|
104
|
-
# 执行点击
|
|
105
|
-
ref = result['ref']
|
|
106
|
-
method = result.get('method', 'unknown')
|
|
107
|
-
|
|
108
|
-
# 根据不同的 ref 类型执行点击
|
|
109
|
-
if ref.startswith('[') and ']' in ref:
|
|
110
|
-
# bounds 坐标
|
|
111
|
-
import re
|
|
112
|
-
coords = re.findall(r'\[(\d+),(\d+)\]', ref)
|
|
113
|
-
if coords:
|
|
114
|
-
x1, y1 = int(coords[0][0]), int(coords[0][1])
|
|
115
|
-
x2, y2 = int(coords[1][0]), int(coords[1][1])
|
|
116
|
-
x, y = (x1 + x2) // 2, (y1 + y2) // 2
|
|
117
|
-
self.client.u2.click(x, y)
|
|
118
|
-
return {
|
|
119
|
-
"success": True,
|
|
120
|
-
"message": f"智能定位成功: {description}",
|
|
121
|
-
"method": method,
|
|
122
|
-
"ref": ref
|
|
123
|
-
}
|
|
124
|
-
elif ':id/' in ref:
|
|
125
|
-
# resource-id
|
|
126
|
-
self.client.u2(resourceId=ref).click()
|
|
127
|
-
return {
|
|
128
|
-
"success": True,
|
|
129
|
-
"message": f"智能定位成功: {description}",
|
|
130
|
-
"method": method,
|
|
131
|
-
"ref": ref
|
|
132
|
-
}
|
|
133
|
-
else:
|
|
134
|
-
# text
|
|
135
|
-
self.client.u2(text=ref).click()
|
|
136
|
-
return {
|
|
137
|
-
"success": True,
|
|
138
|
-
"message": f"智能定位成功: {description}",
|
|
139
|
-
"method": method,
|
|
140
|
-
"ref": ref
|
|
141
|
-
}
|
|
142
|
-
else:
|
|
143
|
-
return {
|
|
144
|
-
"success": False,
|
|
145
|
-
"message": f"智能定位失败: {description}",
|
|
146
|
-
"suggestion": "请使用 mobile_list_elements() 查看页面元素,然后使用 mobile_click_by_id()"
|
|
147
|
-
}
|
|
148
|
-
except Exception as e:
|
|
149
|
-
return {
|
|
150
|
-
"success": False,
|
|
151
|
-
"message": f"智能点击失败: {str(e)}",
|
|
152
|
-
"suggestion": "建议使用基础工具: mobile_list_elements() + mobile_click_by_id()"
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async def smart_input(self, description: str, text: str) -> Dict:
|
|
156
|
-
"""
|
|
157
|
-
智能定位输入框并输入文本(需要 AI 密钥)
|
|
158
|
-
|
|
159
|
-
Args:
|
|
160
|
-
description: 输入框的自然语言描述(如 "用户名输入框")
|
|
161
|
-
text: 要输入的文本
|
|
162
|
-
|
|
163
|
-
Returns:
|
|
164
|
-
{"success": true/false, "message": "..."}
|
|
165
|
-
|
|
166
|
-
示例:
|
|
167
|
-
result = await tools.smart_input("邮箱输入框", "test@example.com")
|
|
168
|
-
|
|
169
|
-
⚠️ 如果没有配置 AI 密钥,请使用基础工具:
|
|
170
|
-
mobile_input_text_by_id("com.app:id/email_input", "test@example.com")
|
|
171
|
-
"""
|
|
172
|
-
self._ensure_ai_available()
|
|
173
|
-
|
|
174
|
-
try:
|
|
175
|
-
# 使用智能定位器
|
|
176
|
-
result = await self.smart_locator.locate(description)
|
|
177
|
-
|
|
178
|
-
if result and result.get('ref'):
|
|
179
|
-
ref = result['ref']
|
|
180
|
-
|
|
181
|
-
# 根据不同的 ref 类型执行输入
|
|
182
|
-
if ':id/' in ref:
|
|
183
|
-
# resource-id
|
|
184
|
-
element = self.client.u2(resourceId=ref)
|
|
185
|
-
element.set_text(text)
|
|
186
|
-
return {
|
|
187
|
-
"success": True,
|
|
188
|
-
"message": f"智能输入成功: {description} = {text}",
|
|
189
|
-
"ref": ref
|
|
190
|
-
}
|
|
191
|
-
else:
|
|
192
|
-
return {
|
|
193
|
-
"success": False,
|
|
194
|
-
"message": f"定位成功但无法输入: {ref}",
|
|
195
|
-
"suggestion": "请使用 mobile_find_elements_by_class('android.widget.EditText') 查找输入框"
|
|
196
|
-
}
|
|
197
|
-
else:
|
|
198
|
-
return {
|
|
199
|
-
"success": False,
|
|
200
|
-
"message": f"智能定位失败: {description}",
|
|
201
|
-
"suggestion": "请使用 mobile_list_elements() 查找输入框,然后使用 mobile_input_text_by_id()"
|
|
202
|
-
}
|
|
203
|
-
except Exception as e:
|
|
204
|
-
return {
|
|
205
|
-
"success": False,
|
|
206
|
-
"message": f"智能输入失败: {str(e)}",
|
|
207
|
-
"suggestion": "建议使用基础工具: mobile_input_text_by_id()"
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
async def analyze_screenshot_with_ai(self, screenshot_path: str, description: str) -> Dict:
|
|
211
|
-
"""
|
|
212
|
-
使用 AI 分析截图并返回坐标(需要 AI 密钥)
|
|
213
|
-
|
|
214
|
-
Args:
|
|
215
|
-
screenshot_path: 截图文件路径
|
|
216
|
-
description: 要查找的元素描述
|
|
217
|
-
|
|
218
|
-
Returns:
|
|
219
|
-
{
|
|
220
|
-
"success": true/false,
|
|
221
|
-
"x": 坐标X(如果成功),
|
|
222
|
-
"y": 坐标Y(如果成功),
|
|
223
|
-
"confidence": 置信度,
|
|
224
|
-
"message": "..."
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
示例:
|
|
228
|
-
# 先截图
|
|
229
|
-
screenshot = mobile_take_screenshot("登录页面")
|
|
230
|
-
|
|
231
|
-
# 然后用 AI 分析
|
|
232
|
-
result = await tools.analyze_screenshot_with_ai(
|
|
233
|
-
screenshot['screenshot_path'],
|
|
234
|
-
"登录按钮"
|
|
235
|
-
)
|
|
236
|
-
|
|
237
|
-
# 根据返回的坐标点击
|
|
238
|
-
if result['success']:
|
|
239
|
-
mobile_click_at_coords(result['x'], result['y'])
|
|
240
|
-
|
|
241
|
-
⚠️ 需要配置支持视觉识别的 AI(如 GPT-4V、Claude 3、Qwen-VL)
|
|
242
|
-
"""
|
|
243
|
-
self._ensure_ai_available()
|
|
244
|
-
|
|
245
|
-
try:
|
|
246
|
-
# 尝试使用视觉识别
|
|
247
|
-
try:
|
|
248
|
-
from ..vision.vision_locator import MobileVisionLocator
|
|
249
|
-
|
|
250
|
-
vision_locator = MobileVisionLocator(self.client)
|
|
251
|
-
result = await vision_locator.locate_element_by_vision(
|
|
252
|
-
description,
|
|
253
|
-
screenshot_path=screenshot_path
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
if result and result.get('found'):
|
|
257
|
-
x, y = result['x'], result['y']
|
|
258
|
-
confidence = result['confidence']
|
|
259
|
-
|
|
260
|
-
return {
|
|
261
|
-
"success": True,
|
|
262
|
-
"x": x,
|
|
263
|
-
"y": y,
|
|
264
|
-
"confidence": confidence,
|
|
265
|
-
"message": f"✅ AI 视觉识别成功: ({x}, {y}), 置信度 {confidence}%"
|
|
266
|
-
}
|
|
267
|
-
else:
|
|
268
|
-
reason = result.get('reason', '未知原因') if result else '未知原因'
|
|
269
|
-
return {
|
|
270
|
-
"success": False,
|
|
271
|
-
"message": f"❌ AI 视觉识别未找到元素: {reason}",
|
|
272
|
-
"suggestion": "请检查截图和元素描述是否准确"
|
|
273
|
-
}
|
|
274
|
-
except ImportError:
|
|
275
|
-
return {
|
|
276
|
-
"success": False,
|
|
277
|
-
"message": "❌ 视觉识别模块未安装",
|
|
278
|
-
"suggestion": "安装:pip install dashscope pillow"
|
|
279
|
-
}
|
|
280
|
-
except Exception as e:
|
|
281
|
-
return {
|
|
282
|
-
"success": False,
|
|
283
|
-
"message": f"❌ 视觉识别失败: {str(e)}",
|
|
284
|
-
"suggestion": "请使用基础工具或检查 AI 配置"
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
def get_ai_status(self) -> Dict:
|
|
288
|
-
"""
|
|
289
|
-
获取 AI 功能状态
|
|
290
|
-
|
|
291
|
-
Returns:
|
|
292
|
-
{
|
|
293
|
-
"available": true/false,
|
|
294
|
-
"provider": "qwen/openai/...",
|
|
295
|
-
"message": "..."
|
|
296
|
-
}
|
|
297
|
-
"""
|
|
298
|
-
if self.ai_available:
|
|
299
|
-
provider = os.getenv('AI_PROVIDER', 'unknown')
|
|
300
|
-
return {
|
|
301
|
-
"available": True,
|
|
302
|
-
"provider": provider,
|
|
303
|
-
"message": f"✅ AI 功能已启用 (Provider: {provider})"
|
|
304
|
-
}
|
|
305
|
-
else:
|
|
306
|
-
return {
|
|
307
|
-
"available": False,
|
|
308
|
-
"provider": None,
|
|
309
|
-
"message": "⚠️ AI 功能未配置,当前仅支持基础工具。如需启用智能定位,请配置 AI 密钥。"
|
|
310
|
-
}
|
|
311
|
-
|
mobile_mcp/mcp/__init__.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Mobile MCP Server Package
|
|
3
|
-
"""
|
|
4
|
-
# 延迟导入,避免与系统的 mcp 包冲突
|
|
5
|
-
def __getattr__(name):
|
|
6
|
-
if name == 'MobileMCPServer':
|
|
7
|
-
from .mcp_server import MobileMCPServer
|
|
8
|
-
return MobileMCPServer
|
|
9
|
-
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
10
|
-
|
|
11
|
-
__all__ = ['MobileMCPServer']
|
|
12
|
-
|
|
13
|
-
|