mobile-mcp-ai 2.3.2__tar.gz → 2.3.4__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.
- {mobile_mcp_ai-2.3.2/mobile_mcp_ai.egg-info → mobile_mcp_ai-2.3.4}/PKG-INFO +1 -1
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/basic_tools_lite.py +60 -11
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/mcp_tools/mcp_server.py +19 -7
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4/mobile_mcp_ai.egg-info}/PKG-INFO +1 -1
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/setup.py +1 -1
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/LICENSE +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/MANIFEST.in +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/README.md +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/__init__.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/config.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/__init__.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/device_manager.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/dynamic_config.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/ios_client_wda.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/ios_device_manager_wda.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/mobile_client.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/utils/__init__.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/utils/logger.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/utils/operation_history_manager.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/core/utils/smart_wait.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/docs/iOS_SETUP_GUIDE.md +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/mcp_tools/__init__.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/mobile_mcp_ai.egg-info/SOURCES.txt +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/mobile_mcp_ai.egg-info/dependency_links.txt +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/mobile_mcp_ai.egg-info/entry_points.txt +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/mobile_mcp_ai.egg-info/not-zip-safe +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/mobile_mcp_ai.egg-info/requires.txt +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/mobile_mcp_ai.egg-info/top_level.txt +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/requirements.txt +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/setup.cfg +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_mind_cloud_my_space.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_mind_correct.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_mind_improved.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_mind_optimized.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_open_mind.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_priority_demo.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_simple.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_/344/270/276/346/212/245.py" +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_/345/210/207/346/215/242/350/257/255/350/250/200/345/210/260English.py" +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/tests/test_/346/265/213/350/257/225.py" +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/utils/__init__.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/utils/logger.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/utils/xml_formatter.py +0 -0
- {mobile_mcp_ai-2.3.2 → mobile_mcp_ai-2.3.4}/utils/xml_parser.py +0 -0
|
@@ -109,6 +109,9 @@ class BasicMobileToolsLite:
|
|
|
109
109
|
img = Image.open(temp_path)
|
|
110
110
|
|
|
111
111
|
# 第3步:缩小尺寸(保持宽高比)
|
|
112
|
+
# 记录压缩后的图片尺寸(用于坐标转换)
|
|
113
|
+
image_width, image_height = img.width, img.height
|
|
114
|
+
|
|
112
115
|
if img.width > max_width:
|
|
113
116
|
ratio = max_width / img.width
|
|
114
117
|
new_w = max_width
|
|
@@ -125,6 +128,8 @@ class BasicMobileToolsLite:
|
|
|
125
128
|
# Pillow 旧版本
|
|
126
129
|
resample = Image.ANTIALIAS
|
|
127
130
|
img = img.resize((new_w, new_h), resample)
|
|
131
|
+
# 更新为压缩后的尺寸
|
|
132
|
+
image_width, image_height = new_w, new_h
|
|
128
133
|
|
|
129
134
|
# 第4步:生成最终文件名(JPEG 格式)
|
|
130
135
|
if description:
|
|
@@ -160,14 +165,18 @@ class BasicMobileToolsLite:
|
|
|
160
165
|
"screenshot_path": str(final_path),
|
|
161
166
|
"screen_width": screen_width,
|
|
162
167
|
"screen_height": screen_height,
|
|
168
|
+
"image_width": image_width,
|
|
169
|
+
"image_height": image_height,
|
|
163
170
|
"original_size": f"{original_size/1024:.1f}KB",
|
|
164
171
|
"compressed_size": f"{compressed_size/1024:.1f}KB",
|
|
165
172
|
"saved_percent": f"{saved_percent:.0f}%",
|
|
166
173
|
"message": f"📸 截图已保存: {final_path}\n"
|
|
167
174
|
f"📐 屏幕尺寸: {screen_width}x{screen_height}\n"
|
|
175
|
+
f"🖼️ 图片尺寸: {image_width}x{image_height}(AI 分析用)\n"
|
|
168
176
|
f"📦 已压缩: {original_size/1024:.0f}KB → {compressed_size/1024:.0f}KB (省 {saved_percent:.0f}%)\n"
|
|
169
|
-
f"
|
|
170
|
-
f"
|
|
177
|
+
f"⚠️ 【重要】AI 返回的坐标需要转换!\n"
|
|
178
|
+
f" 请使用 mobile_click_at_coords 并传入 image_width={image_width}, image_height={image_height}\n"
|
|
179
|
+
f" 工具会自动将图片坐标转换为屏幕坐标"
|
|
171
180
|
}
|
|
172
181
|
else:
|
|
173
182
|
# 不压缩,直接重命名临时文件
|
|
@@ -180,11 +189,14 @@ class BasicMobileToolsLite:
|
|
|
180
189
|
final_path = self.screenshot_dir / filename
|
|
181
190
|
temp_path.rename(final_path)
|
|
182
191
|
|
|
192
|
+
# 不压缩时,图片尺寸 = 屏幕尺寸
|
|
183
193
|
return {
|
|
184
194
|
"success": True,
|
|
185
195
|
"screenshot_path": str(final_path),
|
|
186
196
|
"screen_width": screen_width,
|
|
187
197
|
"screen_height": screen_height,
|
|
198
|
+
"image_width": screen_width,
|
|
199
|
+
"image_height": screen_height,
|
|
188
200
|
"file_size": f"{original_size/1024:.1f}KB",
|
|
189
201
|
"message": f"📸 截图已保存: {final_path}\n"
|
|
190
202
|
f"📐 屏幕尺寸: {screen_width}x{screen_height}\n"
|
|
@@ -226,11 +238,14 @@ class BasicMobileToolsLite:
|
|
|
226
238
|
width = info.get('displayWidth', 0)
|
|
227
239
|
height = info.get('displayHeight', 0)
|
|
228
240
|
|
|
241
|
+
# 不压缩时,图片尺寸 = 屏幕尺寸
|
|
229
242
|
return {
|
|
230
243
|
"success": True,
|
|
231
244
|
"screenshot_path": str(screenshot_path),
|
|
232
245
|
"screen_width": width,
|
|
233
246
|
"screen_height": height,
|
|
247
|
+
"image_width": width,
|
|
248
|
+
"image_height": height,
|
|
234
249
|
"message": f"📸 截图已保存: {screenshot_path}\n"
|
|
235
250
|
f"📐 屏幕尺寸: {width}x{height}\n"
|
|
236
251
|
f"⚠️ 未压缩(PIL 未安装),建议安装: pip install Pillow"
|
|
@@ -266,25 +281,51 @@ class BasicMobileToolsLite:
|
|
|
266
281
|
|
|
267
282
|
# ==================== 点击操作 ====================
|
|
268
283
|
|
|
269
|
-
def click_at_coords(self, x: int, y: int) -> Dict:
|
|
270
|
-
"""
|
|
284
|
+
def click_at_coords(self, x: int, y: int, image_width: int = 0, image_height: int = 0) -> Dict:
|
|
285
|
+
"""点击坐标(核心功能,支持自动坐标转换)
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
x: X 坐标(来自截图分析或屏幕坐标)
|
|
289
|
+
y: Y 坐标(来自截图分析或屏幕坐标)
|
|
290
|
+
image_width: 截图的宽度(可选,传入后自动转换坐标)
|
|
291
|
+
image_height: 截图的高度(可选,传入后自动转换坐标)
|
|
292
|
+
|
|
293
|
+
坐标转换说明:
|
|
294
|
+
如果截图被压缩过(如 1080→720),AI 返回的坐标是基于压缩图的。
|
|
295
|
+
传入 image_width/image_height 后,工具会自动将坐标转换为屏幕坐标。
|
|
296
|
+
"""
|
|
271
297
|
try:
|
|
272
|
-
#
|
|
298
|
+
# 获取屏幕尺寸
|
|
273
299
|
screen_width, screen_height = 0, 0
|
|
274
300
|
if self._is_ios():
|
|
275
301
|
ios_client = self._get_ios_client()
|
|
276
302
|
if ios_client and hasattr(ios_client, 'wda'):
|
|
277
|
-
ios_client.wda.click(x, y)
|
|
278
303
|
size = ios_client.wda.window_size()
|
|
279
304
|
screen_width, screen_height = size[0], size[1]
|
|
280
305
|
else:
|
|
281
306
|
return {"success": False, "message": "❌ iOS 客户端未初始化"}
|
|
282
307
|
else:
|
|
283
|
-
self.client.u2.click(x, y)
|
|
284
308
|
info = self.client.u2.info
|
|
285
309
|
screen_width = info.get('displayWidth', 0)
|
|
286
310
|
screen_height = info.get('displayHeight', 0)
|
|
287
311
|
|
|
312
|
+
# 🎯 坐标转换:如果传入了图片尺寸,将图片坐标转换为屏幕坐标
|
|
313
|
+
original_x, original_y = x, y
|
|
314
|
+
converted = False
|
|
315
|
+
if image_width > 0 and image_height > 0 and screen_width > 0 and screen_height > 0:
|
|
316
|
+
if image_width != screen_width or image_height != screen_height:
|
|
317
|
+
# 按比例转换坐标
|
|
318
|
+
x = int(x * screen_width / image_width)
|
|
319
|
+
y = int(y * screen_height / image_height)
|
|
320
|
+
converted = True
|
|
321
|
+
|
|
322
|
+
# 执行点击
|
|
323
|
+
if self._is_ios():
|
|
324
|
+
ios_client = self._get_ios_client()
|
|
325
|
+
ios_client.wda.click(x, y)
|
|
326
|
+
else:
|
|
327
|
+
self.client.u2.click(x, y)
|
|
328
|
+
|
|
288
329
|
time.sleep(0.3)
|
|
289
330
|
|
|
290
331
|
# 计算百分比坐标(用于跨设备兼容)
|
|
@@ -303,10 +344,18 @@ class BasicMobileToolsLite:
|
|
|
303
344
|
ref=f"coords_{x}_{y}"
|
|
304
345
|
)
|
|
305
346
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
347
|
+
if converted:
|
|
348
|
+
return {
|
|
349
|
+
"success": True,
|
|
350
|
+
"message": f"✅ 点击成功: ({x}, {y})\n"
|
|
351
|
+
f" 📐 坐标已转换: ({original_x},{original_y}) → ({x},{y})\n"
|
|
352
|
+
f" 🖼️ 图片尺寸: {image_width}x{image_height} → 屏幕: {screen_width}x{screen_height}"
|
|
353
|
+
}
|
|
354
|
+
else:
|
|
355
|
+
return {
|
|
356
|
+
"success": True,
|
|
357
|
+
"message": f"✅ 点击成功: ({x}, {y}) [相对位置: {x_percent}%, {y_percent}%]"
|
|
358
|
+
}
|
|
310
359
|
except Exception as e:
|
|
311
360
|
return {"success": False, "message": f"❌ 点击失败: {e}"}
|
|
312
361
|
|
|
@@ -156,12 +156,15 @@ class MobileMCPServer:
|
|
|
156
156
|
# ==================== 截图(视觉兜底)====================
|
|
157
157
|
tools.append(Tool(
|
|
158
158
|
name="mobile_take_screenshot",
|
|
159
|
-
description="📸
|
|
159
|
+
description="📸 截图(视觉定位用)。返回截图路径、屏幕尺寸和图片尺寸。\n\n"
|
|
160
160
|
"🎯 使用场景:\n"
|
|
161
161
|
"- 游戏(Unity/Cocos)无法获取元素时\n"
|
|
162
162
|
"- mobile_list_elements 返回空时\n"
|
|
163
163
|
"- 需要确认页面状态时\n\n"
|
|
164
|
-
"⚠️
|
|
164
|
+
"⚠️ 【重要】截图会被压缩!\n"
|
|
165
|
+
" - screen_width/screen_height = 原始屏幕尺寸\n"
|
|
166
|
+
" - image_width/image_height = 压缩后图片尺寸(AI 看到的)\n"
|
|
167
|
+
" - 点击时必须传入 image_width/image_height 让工具自动转换坐标!",
|
|
165
168
|
inputSchema={
|
|
166
169
|
"type": "object",
|
|
167
170
|
"properties": {
|
|
@@ -215,13 +218,17 @@ class MobileMCPServer:
|
|
|
215
218
|
"- 游戏(Unity/Cocos)无法获取元素\n"
|
|
216
219
|
"- mobile_list_elements 返回空\n"
|
|
217
220
|
"- 元素没有 id 和 text\n\n"
|
|
218
|
-
"
|
|
219
|
-
"
|
|
221
|
+
"⚠️ 【重要】如果坐标来自压缩截图,必须传入 image_width 和 image_height!\n"
|
|
222
|
+
" 截图返回的 image_width/image_height 字段就是需要传入的值。\n"
|
|
223
|
+
" 工具会自动将图片坐标转换为屏幕坐标。\n\n"
|
|
224
|
+
"✅ 自动记录百分比坐标,生成脚本时会转换为跨分辨率兼容的百分比定位",
|
|
220
225
|
inputSchema={
|
|
221
226
|
"type": "object",
|
|
222
227
|
"properties": {
|
|
223
|
-
"x": {"type": "number", "description": "X
|
|
224
|
-
"y": {"type": "number", "description": "Y
|
|
228
|
+
"x": {"type": "number", "description": "X 坐标(像素,来自截图分析或屏幕坐标)"},
|
|
229
|
+
"y": {"type": "number", "description": "Y 坐标(像素,来自截图分析或屏幕坐标)"},
|
|
230
|
+
"image_width": {"type": "number", "description": "截图的宽度(可选,传入后自动转换坐标)"},
|
|
231
|
+
"image_height": {"type": "number", "description": "截图的高度(可选,传入后自动转换坐标)"}
|
|
225
232
|
},
|
|
226
233
|
"required": ["x", "y"]
|
|
227
234
|
}
|
|
@@ -446,7 +453,12 @@ class MobileMCPServer:
|
|
|
446
453
|
|
|
447
454
|
# 点击
|
|
448
455
|
elif name == "mobile_click_at_coords":
|
|
449
|
-
result = self.tools.click_at_coords(
|
|
456
|
+
result = self.tools.click_at_coords(
|
|
457
|
+
arguments["x"],
|
|
458
|
+
arguments["y"],
|
|
459
|
+
arguments.get("image_width", 0),
|
|
460
|
+
arguments.get("image_height", 0)
|
|
461
|
+
)
|
|
450
462
|
return [TextContent(type="text", text=self.format_response(result))]
|
|
451
463
|
|
|
452
464
|
elif name == "mobile_click_by_text":
|
|
@@ -25,7 +25,7 @@ if requirements_file.exists():
|
|
|
25
25
|
|
|
26
26
|
setup(
|
|
27
27
|
name="mobile-mcp-ai",
|
|
28
|
-
version="2.3.
|
|
28
|
+
version="2.3.4", # 修复截图坐标偏移问题:支持图片坐标自动转换为屏幕坐标
|
|
29
29
|
author="douzi",
|
|
30
30
|
author_email="1492994674@qq.com",
|
|
31
31
|
description="移动端自动化 MCP Server - 支持 Android/iOS,AI 功能可选(基础工具不需要 AI)",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|