mobile-mcp-ai 2.3.4__tar.gz → 2.3.5__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.4/mobile_mcp_ai.egg-info → mobile_mcp_ai-2.3.5}/PKG-INFO +1 -1
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/basic_tools_lite.py +108 -30
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/ios_client_wda.py +1 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/mcp_tools/mcp_server.py +29 -13
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5/mobile_mcp_ai.egg-info}/PKG-INFO +1 -1
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/setup.py +1 -1
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/LICENSE +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/MANIFEST.in +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/README.md +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/__init__.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/config.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/__init__.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/device_manager.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/dynamic_config.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/ios_device_manager_wda.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/mobile_client.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/utils/__init__.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/utils/logger.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/utils/operation_history_manager.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/core/utils/smart_wait.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/docs/iOS_SETUP_GUIDE.md +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/mcp_tools/__init__.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/mobile_mcp_ai.egg-info/SOURCES.txt +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/mobile_mcp_ai.egg-info/dependency_links.txt +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/mobile_mcp_ai.egg-info/entry_points.txt +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/mobile_mcp_ai.egg-info/not-zip-safe +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/mobile_mcp_ai.egg-info/requires.txt +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/mobile_mcp_ai.egg-info/top_level.txt +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/requirements.txt +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/setup.cfg +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/tests/test_mind_cloud_my_space.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/tests/test_mind_correct.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/tests/test_mind_improved.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/tests/test_mind_optimized.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/tests/test_open_mind.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/tests/test_priority_demo.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/tests/test_simple.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/tests/test_/344/270/276/346/212/245.py" +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/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.4 → mobile_mcp_ai-2.3.5}/tests/test_/346/265/213/350/257/225.py" +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/utils/__init__.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/utils/logger.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/utils/xml_formatter.py +0 -0
- {mobile_mcp_ai-2.3.4 → mobile_mcp_ai-2.3.5}/utils/xml_parser.py +0 -0
|
@@ -56,8 +56,9 @@ class BasicMobileToolsLite:
|
|
|
56
56
|
# ==================== 截图 ====================
|
|
57
57
|
|
|
58
58
|
def take_screenshot(self, description: str = "", compress: bool = True,
|
|
59
|
-
max_width: int = 720, quality: int = 75
|
|
60
|
-
|
|
59
|
+
max_width: int = 720, quality: int = 75,
|
|
60
|
+
crop_x: int = 0, crop_y: int = 0, crop_size: int = 0) -> Dict:
|
|
61
|
+
"""截图(支持压缩和局部裁剪)
|
|
61
62
|
|
|
62
63
|
压缩原理:
|
|
63
64
|
1. 先截取原始 PNG 图片
|
|
@@ -65,11 +66,20 @@ class BasicMobileToolsLite:
|
|
|
65
66
|
3. 转换为 JPEG 格式 + 降低质量(如 100% → 75%)
|
|
66
67
|
4. 最终文件从 2MB 压缩到约 80KB(节省 96%)
|
|
67
68
|
|
|
69
|
+
局部裁剪(用于精确识别小元素):
|
|
70
|
+
- 第一次全屏截图,AI 返回大概坐标
|
|
71
|
+
- 第二次传入 crop_x, crop_y, crop_size 截取局部区域
|
|
72
|
+
- 局部区域不压缩,保持清晰度,AI 可精确识别
|
|
73
|
+
- 返回 crop_offset_x/y 用于坐标换算
|
|
74
|
+
|
|
68
75
|
Args:
|
|
69
76
|
description: 截图描述(可选)
|
|
70
77
|
compress: 是否压缩(默认 True,推荐开启省 token)
|
|
71
78
|
max_width: 压缩后最大宽度(默认 720,对 AI 识别足够)
|
|
72
79
|
quality: JPEG 质量 1-100(默认 75,肉眼几乎看不出区别)
|
|
80
|
+
crop_x: 裁剪中心点 X 坐标(屏幕坐标,0 表示不裁剪)
|
|
81
|
+
crop_y: 裁剪中心点 Y 坐标(屏幕坐标,0 表示不裁剪)
|
|
82
|
+
crop_size: 裁剪区域大小(默认 0 不裁剪,推荐 200-400)
|
|
73
83
|
|
|
74
84
|
压缩效果示例:
|
|
75
85
|
原图 PNG: 2048KB
|
|
@@ -104,12 +114,69 @@ class BasicMobileToolsLite:
|
|
|
104
114
|
|
|
105
115
|
original_size = temp_path.stat().st_size
|
|
106
116
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
117
|
+
# 第2步:打开图片
|
|
118
|
+
img = Image.open(temp_path)
|
|
119
|
+
|
|
120
|
+
# 第2.5步:局部裁剪(如果指定了裁剪参数)
|
|
121
|
+
crop_offset_x, crop_offset_y = 0, 0
|
|
122
|
+
is_cropped = False
|
|
123
|
+
|
|
124
|
+
if crop_x > 0 and crop_y > 0 and crop_size > 0:
|
|
125
|
+
# 计算裁剪区域(以 crop_x, crop_y 为中心)
|
|
126
|
+
half_size = crop_size // 2
|
|
127
|
+
left = max(0, crop_x - half_size)
|
|
128
|
+
top = max(0, crop_y - half_size)
|
|
129
|
+
right = min(img.width, crop_x + half_size)
|
|
130
|
+
bottom = min(img.height, crop_y + half_size)
|
|
131
|
+
|
|
132
|
+
# 记录偏移量(用于坐标换算)
|
|
133
|
+
crop_offset_x = left
|
|
134
|
+
crop_offset_y = top
|
|
135
|
+
|
|
136
|
+
# 裁剪
|
|
137
|
+
img = img.crop((left, top, right, bottom))
|
|
138
|
+
is_cropped = True
|
|
139
|
+
|
|
140
|
+
# ========== 情况1:局部裁剪截图(不压缩,保持清晰度)==========
|
|
141
|
+
if is_cropped:
|
|
142
|
+
# 生成文件名
|
|
143
|
+
if description:
|
|
144
|
+
safe_desc = re.sub(r'[^\w\s-]', '', description).strip().replace(' ', '_')
|
|
145
|
+
filename = f"screenshot_{platform}_crop_{safe_desc}_{timestamp}.png"
|
|
146
|
+
else:
|
|
147
|
+
filename = f"screenshot_{platform}_crop_{timestamp}.png"
|
|
110
148
|
|
|
149
|
+
final_path = self.screenshot_dir / filename
|
|
150
|
+
|
|
151
|
+
# 保存为 PNG(保持清晰度)
|
|
152
|
+
img.save(str(final_path), "PNG")
|
|
153
|
+
|
|
154
|
+
# 删除临时文件
|
|
155
|
+
temp_path.unlink()
|
|
156
|
+
|
|
157
|
+
cropped_size = final_path.stat().st_size
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
"success": True,
|
|
161
|
+
"screenshot_path": str(final_path),
|
|
162
|
+
"screen_width": screen_width,
|
|
163
|
+
"screen_height": screen_height,
|
|
164
|
+
"image_width": img.width,
|
|
165
|
+
"image_height": img.height,
|
|
166
|
+
"crop_offset_x": crop_offset_x,
|
|
167
|
+
"crop_offset_y": crop_offset_y,
|
|
168
|
+
"file_size": f"{cropped_size/1024:.1f}KB",
|
|
169
|
+
"message": f"🔍 局部截图已保存: {final_path}\n"
|
|
170
|
+
f"📐 裁剪区域: ({crop_offset_x}, {crop_offset_y}) 起,{img.width}x{img.height} 像素\n"
|
|
171
|
+
f"📦 文件大小: {cropped_size/1024:.0f}KB\n"
|
|
172
|
+
f"🎯 【坐标换算】AI 返回坐标 (x, y) 后:\n"
|
|
173
|
+
f" 实际屏幕坐标 = ({crop_offset_x} + x, {crop_offset_y} + y)\n"
|
|
174
|
+
f" 或直接调用 mobile_click_at_coords(x, y, crop_offset_x={crop_offset_x}, crop_offset_y={crop_offset_y})"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
# ========== 情况2:全屏压缩截图 ==========
|
|
178
|
+
elif compress:
|
|
111
179
|
# 第3步:缩小尺寸(保持宽高比)
|
|
112
|
-
# 记录压缩后的图片尺寸(用于坐标转换)
|
|
113
180
|
image_width, image_height = img.width, img.height
|
|
114
181
|
|
|
115
182
|
if img.width > max_width:
|
|
@@ -118,20 +185,16 @@ class BasicMobileToolsLite:
|
|
|
118
185
|
new_h = int(img.height * ratio)
|
|
119
186
|
# 兼容不同版本的 Pillow
|
|
120
187
|
try:
|
|
121
|
-
# Pillow 10.0.0+
|
|
122
188
|
resample = Image.Resampling.LANCZOS
|
|
123
189
|
except AttributeError:
|
|
124
190
|
try:
|
|
125
|
-
# Pillow 9.x
|
|
126
191
|
resample = Image.LANCZOS
|
|
127
192
|
except AttributeError:
|
|
128
|
-
# Pillow 旧版本
|
|
129
193
|
resample = Image.ANTIALIAS
|
|
130
194
|
img = img.resize((new_w, new_h), resample)
|
|
131
|
-
# 更新为压缩后的尺寸
|
|
132
195
|
image_width, image_height = new_w, new_h
|
|
133
196
|
|
|
134
|
-
#
|
|
197
|
+
# 生成文件名(JPEG 格式)
|
|
135
198
|
if description:
|
|
136
199
|
safe_desc = re.sub(r'[^\w\s-]', '', description).strip().replace(' ', '_')
|
|
137
200
|
filename = f"screenshot_{platform}_{safe_desc}_{timestamp}.jpg"
|
|
@@ -140,10 +203,8 @@ class BasicMobileToolsLite:
|
|
|
140
203
|
|
|
141
204
|
final_path = self.screenshot_dir / filename
|
|
142
205
|
|
|
143
|
-
#
|
|
144
|
-
# 先转换为 RGB 模式,处理可能的 RGBA 或 P 模式
|
|
206
|
+
# 保存为 JPEG(处理透明通道)
|
|
145
207
|
if img.mode in ('RGBA', 'LA', 'P'):
|
|
146
|
-
# 创建白色背景
|
|
147
208
|
background = Image.new('RGB', img.size, (255, 255, 255))
|
|
148
209
|
if img.mode == 'P':
|
|
149
210
|
img = img.convert('RGBA')
|
|
@@ -153,8 +214,6 @@ class BasicMobileToolsLite:
|
|
|
153
214
|
img = img.convert("RGB")
|
|
154
215
|
|
|
155
216
|
img.save(str(final_path), "JPEG", quality=quality)
|
|
156
|
-
|
|
157
|
-
# 第6步:删除临时 PNG
|
|
158
217
|
temp_path.unlink()
|
|
159
218
|
|
|
160
219
|
compressed_size = final_path.stat().st_size
|
|
@@ -178,8 +237,9 @@ class BasicMobileToolsLite:
|
|
|
178
237
|
f" 请使用 mobile_click_at_coords 并传入 image_width={image_width}, image_height={image_height}\n"
|
|
179
238
|
f" 工具会自动将图片坐标转换为屏幕坐标"
|
|
180
239
|
}
|
|
240
|
+
|
|
241
|
+
# ========== 情况3:全屏不压缩截图 ==========
|
|
181
242
|
else:
|
|
182
|
-
# 不压缩,直接重命名临时文件
|
|
183
243
|
if description:
|
|
184
244
|
safe_desc = re.sub(r'[^\w\s-]', '', description).strip().replace(' ', '_')
|
|
185
245
|
filename = f"screenshot_{platform}_{safe_desc}_{timestamp}.png"
|
|
@@ -189,7 +249,6 @@ class BasicMobileToolsLite:
|
|
|
189
249
|
final_path = self.screenshot_dir / filename
|
|
190
250
|
temp_path.rename(final_path)
|
|
191
251
|
|
|
192
|
-
# 不压缩时,图片尺寸 = 屏幕尺寸
|
|
193
252
|
return {
|
|
194
253
|
"success": True,
|
|
195
254
|
"screenshot_path": str(final_path),
|
|
@@ -281,7 +340,8 @@ class BasicMobileToolsLite:
|
|
|
281
340
|
|
|
282
341
|
# ==================== 点击操作 ====================
|
|
283
342
|
|
|
284
|
-
def click_at_coords(self, x: int, y: int, image_width: int = 0, image_height: int = 0
|
|
343
|
+
def click_at_coords(self, x: int, y: int, image_width: int = 0, image_height: int = 0,
|
|
344
|
+
crop_offset_x: int = 0, crop_offset_y: int = 0) -> Dict:
|
|
285
345
|
"""点击坐标(核心功能,支持自动坐标转换)
|
|
286
346
|
|
|
287
347
|
Args:
|
|
@@ -289,10 +349,12 @@ class BasicMobileToolsLite:
|
|
|
289
349
|
y: Y 坐标(来自截图分析或屏幕坐标)
|
|
290
350
|
image_width: 截图的宽度(可选,传入后自动转换坐标)
|
|
291
351
|
image_height: 截图的高度(可选,传入后自动转换坐标)
|
|
352
|
+
crop_offset_x: 局部截图的 X 偏移量(可选,局部截图时传入)
|
|
353
|
+
crop_offset_y: 局部截图的 Y 偏移量(可选,局部截图时传入)
|
|
292
354
|
|
|
293
355
|
坐标转换说明:
|
|
294
|
-
|
|
295
|
-
|
|
356
|
+
1. 全屏压缩截图:传入 image_width/image_height,自动按比例转换
|
|
357
|
+
2. 局部裁剪截图:传入 crop_offset_x/crop_offset_y,自动加上偏移量
|
|
296
358
|
"""
|
|
297
359
|
try:
|
|
298
360
|
# 获取屏幕尺寸
|
|
@@ -309,15 +371,24 @@ class BasicMobileToolsLite:
|
|
|
309
371
|
screen_width = info.get('displayWidth', 0)
|
|
310
372
|
screen_height = info.get('displayHeight', 0)
|
|
311
373
|
|
|
312
|
-
# 🎯
|
|
374
|
+
# 🎯 坐标转换
|
|
313
375
|
original_x, original_y = x, y
|
|
314
376
|
converted = False
|
|
315
|
-
|
|
377
|
+
conversion_type = ""
|
|
378
|
+
|
|
379
|
+
# 情况1:局部裁剪截图 - 加上偏移量
|
|
380
|
+
if crop_offset_x > 0 or crop_offset_y > 0:
|
|
381
|
+
x = x + crop_offset_x
|
|
382
|
+
y = y + crop_offset_y
|
|
383
|
+
converted = True
|
|
384
|
+
conversion_type = "crop_offset"
|
|
385
|
+
# 情况2:全屏压缩截图 - 按比例转换
|
|
386
|
+
elif image_width > 0 and image_height > 0 and screen_width > 0 and screen_height > 0:
|
|
316
387
|
if image_width != screen_width or image_height != screen_height:
|
|
317
|
-
# 按比例转换坐标
|
|
318
388
|
x = int(x * screen_width / image_width)
|
|
319
389
|
y = int(y * screen_height / image_height)
|
|
320
390
|
converted = True
|
|
391
|
+
conversion_type = "scale"
|
|
321
392
|
|
|
322
393
|
# 执行点击
|
|
323
394
|
if self._is_ios():
|
|
@@ -345,12 +416,19 @@ class BasicMobileToolsLite:
|
|
|
345
416
|
)
|
|
346
417
|
|
|
347
418
|
if converted:
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
419
|
+
if conversion_type == "crop_offset":
|
|
420
|
+
return {
|
|
421
|
+
"success": True,
|
|
422
|
+
"message": f"✅ 点击成功: ({x}, {y})\n"
|
|
423
|
+
f" 🔍 局部截图坐标转换: ({original_x},{original_y}) + 偏移({crop_offset_x},{crop_offset_y}) → ({x},{y})"
|
|
424
|
+
}
|
|
425
|
+
else:
|
|
426
|
+
return {
|
|
427
|
+
"success": True,
|
|
428
|
+
"message": f"✅ 点击成功: ({x}, {y})\n"
|
|
429
|
+
f" 📐 坐标已转换: ({original_x},{original_y}) → ({x},{y})\n"
|
|
430
|
+
f" 🖼️ 图片尺寸: {image_width}x{image_height} → 屏幕: {screen_width}x{screen_height}"
|
|
431
|
+
}
|
|
354
432
|
else:
|
|
355
433
|
return {
|
|
356
434
|
"success": True,
|
|
@@ -156,19 +156,26 @@ 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
|
+
"🔍 【局部裁剪】精确识别小元素(如广告关闭按钮):\n"
|
|
165
|
+
" 1. 先全屏截图,AI 返回大概坐标 (600, 200)\n"
|
|
166
|
+
" 2. 再调用 crop_x=600, crop_y=200, crop_size=200 截取局部\n"
|
|
167
|
+
" 3. 局部图不压缩,AI 可精确识别\n"
|
|
168
|
+
" 4. 点击时传入 crop_offset_x/y 自动换算坐标\n\n"
|
|
164
169
|
"⚠️ 【重要】截图会被压缩!\n"
|
|
165
|
-
" -
|
|
166
|
-
" -
|
|
167
|
-
" - 点击时必须传入 image_width/image_height 让工具自动转换坐标!",
|
|
170
|
+
" - 全屏截图:点击时传 image_width/image_height 转换坐标\n"
|
|
171
|
+
" - 局部截图:点击时传 crop_offset_x/crop_offset_y 转换坐标",
|
|
168
172
|
inputSchema={
|
|
169
173
|
"type": "object",
|
|
170
174
|
"properties": {
|
|
171
|
-
"description": {"type": "string", "description": "截图描述(可选)"}
|
|
175
|
+
"description": {"type": "string", "description": "截图描述(可选)"},
|
|
176
|
+
"crop_x": {"type": "integer", "description": "局部裁剪中心 X 坐标(屏幕坐标,0 表示不裁剪)"},
|
|
177
|
+
"crop_y": {"type": "integer", "description": "局部裁剪中心 Y 坐标(屏幕坐标,0 表示不裁剪)"},
|
|
178
|
+
"crop_size": {"type": "integer", "description": "裁剪区域大小(推荐 200-400,0 表示不裁剪)"}
|
|
172
179
|
},
|
|
173
180
|
"required": []
|
|
174
181
|
}
|
|
@@ -218,17 +225,19 @@ class MobileMCPServer:
|
|
|
218
225
|
"- 游戏(Unity/Cocos)无法获取元素\n"
|
|
219
226
|
"- mobile_list_elements 返回空\n"
|
|
220
227
|
"- 元素没有 id 和 text\n\n"
|
|
221
|
-
"⚠️
|
|
222
|
-
"
|
|
223
|
-
"
|
|
224
|
-
"✅
|
|
228
|
+
"⚠️ 【坐标转换】两种场景:\n"
|
|
229
|
+
" 1. 全屏压缩截图:传入 image_width + image_height → 自动按比例转换\n"
|
|
230
|
+
" 2. 局部裁剪截图:传入 crop_offset_x + crop_offset_y → 自动加偏移\n\n"
|
|
231
|
+
"✅ 自动记录百分比坐标,生成脚本时转换为跨分辨率兼容的百分比定位",
|
|
225
232
|
inputSchema={
|
|
226
233
|
"type": "object",
|
|
227
234
|
"properties": {
|
|
228
235
|
"x": {"type": "number", "description": "X 坐标(像素,来自截图分析或屏幕坐标)"},
|
|
229
236
|
"y": {"type": "number", "description": "Y 坐标(像素,来自截图分析或屏幕坐标)"},
|
|
230
|
-
"image_width": {"type": "number", "description": "
|
|
231
|
-
"image_height": {"type": "number", "description": "
|
|
237
|
+
"image_width": {"type": "number", "description": "全屏截图宽度(压缩截图时传入)"},
|
|
238
|
+
"image_height": {"type": "number", "description": "全屏截图高度(压缩截图时传入)"},
|
|
239
|
+
"crop_offset_x": {"type": "number", "description": "局部截图 X 偏移(裁剪截图时传入)"},
|
|
240
|
+
"crop_offset_y": {"type": "number", "description": "局部截图 Y 偏移(裁剪截图时传入)"}
|
|
232
241
|
},
|
|
233
242
|
"required": ["x", "y"]
|
|
234
243
|
}
|
|
@@ -444,7 +453,12 @@ class MobileMCPServer:
|
|
|
444
453
|
try:
|
|
445
454
|
# 截图
|
|
446
455
|
if name == "mobile_take_screenshot":
|
|
447
|
-
result = self.tools.take_screenshot(
|
|
456
|
+
result = self.tools.take_screenshot(
|
|
457
|
+
description=arguments.get("description", ""),
|
|
458
|
+
crop_x=arguments.get("crop_x", 0),
|
|
459
|
+
crop_y=arguments.get("crop_y", 0),
|
|
460
|
+
crop_size=arguments.get("crop_size", 0)
|
|
461
|
+
)
|
|
448
462
|
return [TextContent(type="text", text=self.format_response(result))]
|
|
449
463
|
|
|
450
464
|
elif name == "mobile_get_screen_size":
|
|
@@ -457,7 +471,9 @@ class MobileMCPServer:
|
|
|
457
471
|
arguments["x"],
|
|
458
472
|
arguments["y"],
|
|
459
473
|
arguments.get("image_width", 0),
|
|
460
|
-
arguments.get("image_height", 0)
|
|
474
|
+
arguments.get("image_height", 0),
|
|
475
|
+
arguments.get("crop_offset_x", 0),
|
|
476
|
+
arguments.get("crop_offset_y", 0)
|
|
461
477
|
)
|
|
462
478
|
return [TextContent(type="text", text=self.format_response(result))]
|
|
463
479
|
|
|
@@ -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.5", # 新增局部截图功能:精确识别小元素(广告关闭按钮等)
|
|
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
|