mobile-mcp-ai 2.3.5__py3-none-any.whl → 2.3.6__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/core/basic_tools_lite.py +136 -25
- mobile_mcp/core/ios_client_wda.py +2 -0
- mobile_mcp/mcp_tools/mcp_server.py +39 -13
- {mobile_mcp_ai-2.3.5.dist-info → mobile_mcp_ai-2.3.6.dist-info}/METADATA +16 -29
- {mobile_mcp_ai-2.3.5.dist-info → mobile_mcp_ai-2.3.6.dist-info}/RECORD +9 -9
- {mobile_mcp_ai-2.3.5.dist-info → mobile_mcp_ai-2.3.6.dist-info}/WHEEL +1 -1
- {mobile_mcp_ai-2.3.5.dist-info/licenses → mobile_mcp_ai-2.3.6.dist-info}/LICENSE +0 -0
- {mobile_mcp_ai-2.3.5.dist-info → mobile_mcp_ai-2.3.6.dist-info}/entry_points.txt +0 -0
- {mobile_mcp_ai-2.3.5.dist-info → mobile_mcp_ai-2.3.6.dist-info}/top_level.txt +0 -0
|
@@ -176,6 +176,11 @@ class BasicMobileToolsLite:
|
|
|
176
176
|
|
|
177
177
|
# ========== 情况2:全屏压缩截图 ==========
|
|
178
178
|
elif compress:
|
|
179
|
+
# 🔴 关键:记录原始图片尺寸(用于坐标转换)
|
|
180
|
+
# 注意:截图尺寸可能和 u2.info 的 displayWidth 不一致!
|
|
181
|
+
original_img_width = img.width
|
|
182
|
+
original_img_height = img.height
|
|
183
|
+
|
|
179
184
|
# 第3步:缩小尺寸(保持宽高比)
|
|
180
185
|
image_width, image_height = img.width, img.height
|
|
181
186
|
|
|
@@ -224,18 +229,19 @@ class BasicMobileToolsLite:
|
|
|
224
229
|
"screenshot_path": str(final_path),
|
|
225
230
|
"screen_width": screen_width,
|
|
226
231
|
"screen_height": screen_height,
|
|
227
|
-
"
|
|
228
|
-
"
|
|
232
|
+
"original_img_width": original_img_width, # 截图原始宽度
|
|
233
|
+
"original_img_height": original_img_height, # 截图原始高度
|
|
234
|
+
"image_width": image_width, # 压缩后宽度(AI 看到的)
|
|
235
|
+
"image_height": image_height, # 压缩后高度(AI 看到的)
|
|
229
236
|
"original_size": f"{original_size/1024:.1f}KB",
|
|
230
237
|
"compressed_size": f"{compressed_size/1024:.1f}KB",
|
|
231
238
|
"saved_percent": f"{saved_percent:.0f}%",
|
|
232
239
|
"message": f"📸 截图已保存: {final_path}\n"
|
|
233
|
-
f"📐
|
|
234
|
-
f"🖼️ 图片尺寸: {image_width}x{image_height}(AI 分析用)\n"
|
|
240
|
+
f"📐 原始尺寸: {original_img_width}x{original_img_height} → 压缩后: {image_width}x{image_height}\n"
|
|
235
241
|
f"📦 已压缩: {original_size/1024:.0f}KB → {compressed_size/1024:.0f}KB (省 {saved_percent:.0f}%)\n"
|
|
236
|
-
f"⚠️
|
|
237
|
-
f"
|
|
238
|
-
f"
|
|
242
|
+
f"⚠️ 【坐标转换】AI 返回坐标后,请传入:\n"
|
|
243
|
+
f" image_width={image_width}, image_height={image_height},\n"
|
|
244
|
+
f" original_img_width={original_img_width}, original_img_height={original_img_height}"
|
|
239
245
|
}
|
|
240
246
|
|
|
241
247
|
# ========== 情况3:全屏不压缩截图 ==========
|
|
@@ -249,18 +255,21 @@ class BasicMobileToolsLite:
|
|
|
249
255
|
final_path = self.screenshot_dir / filename
|
|
250
256
|
temp_path.rename(final_path)
|
|
251
257
|
|
|
258
|
+
# 不压缩时,用截图实际尺寸(可能和 screen_width 不同)
|
|
252
259
|
return {
|
|
253
260
|
"success": True,
|
|
254
261
|
"screenshot_path": str(final_path),
|
|
255
262
|
"screen_width": screen_width,
|
|
256
263
|
"screen_height": screen_height,
|
|
257
|
-
"
|
|
258
|
-
"
|
|
264
|
+
"original_img_width": img.width, # 截图实际尺寸
|
|
265
|
+
"original_img_height": img.height,
|
|
266
|
+
"image_width": img.width, # 未压缩,和原图一样
|
|
267
|
+
"image_height": img.height,
|
|
259
268
|
"file_size": f"{original_size/1024:.1f}KB",
|
|
260
269
|
"message": f"📸 截图已保存: {final_path}\n"
|
|
261
|
-
f"📐
|
|
270
|
+
f"📐 截图尺寸: {img.width}x{img.height}\n"
|
|
262
271
|
f"📦 文件大小: {original_size/1024:.0f}KB(未压缩)\n"
|
|
263
|
-
f"💡
|
|
272
|
+
f"💡 未压缩,坐标可直接使用"
|
|
264
273
|
}
|
|
265
274
|
except ImportError:
|
|
266
275
|
# 如果没有 PIL,回退到原始方式(不压缩)
|
|
@@ -341,20 +350,23 @@ class BasicMobileToolsLite:
|
|
|
341
350
|
# ==================== 点击操作 ====================
|
|
342
351
|
|
|
343
352
|
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
|
|
353
|
+
crop_offset_x: int = 0, crop_offset_y: int = 0,
|
|
354
|
+
original_img_width: int = 0, original_img_height: int = 0) -> Dict:
|
|
345
355
|
"""点击坐标(核心功能,支持自动坐标转换)
|
|
346
356
|
|
|
347
357
|
Args:
|
|
348
358
|
x: X 坐标(来自截图分析或屏幕坐标)
|
|
349
359
|
y: Y 坐标(来自截图分析或屏幕坐标)
|
|
350
|
-
image_width:
|
|
351
|
-
image_height:
|
|
352
|
-
crop_offset_x: 局部截图的 X
|
|
353
|
-
crop_offset_y: 局部截图的 Y
|
|
360
|
+
image_width: 压缩后图片宽度(AI 看到的图片尺寸)
|
|
361
|
+
image_height: 压缩后图片高度(AI 看到的图片尺寸)
|
|
362
|
+
crop_offset_x: 局部截图的 X 偏移量(局部截图时传入)
|
|
363
|
+
crop_offset_y: 局部截图的 Y 偏移量(局部截图时传入)
|
|
364
|
+
original_img_width: 截图原始宽度(压缩前的尺寸,用于精确转换)
|
|
365
|
+
original_img_height: 截图原始高度(压缩前的尺寸,用于精确转换)
|
|
354
366
|
|
|
355
367
|
坐标转换说明:
|
|
356
|
-
1.
|
|
357
|
-
2.
|
|
368
|
+
1. 全屏压缩截图:AI 坐标 → 原图坐标(基于 image/original_img 比例)
|
|
369
|
+
2. 局部裁剪截图:AI 坐标 + 偏移量 = 屏幕坐标
|
|
358
370
|
"""
|
|
359
371
|
try:
|
|
360
372
|
# 获取屏幕尺寸
|
|
@@ -382,13 +394,19 @@ class BasicMobileToolsLite:
|
|
|
382
394
|
y = y + crop_offset_y
|
|
383
395
|
converted = True
|
|
384
396
|
conversion_type = "crop_offset"
|
|
385
|
-
# 情况2:全屏压缩截图 -
|
|
386
|
-
elif image_width > 0 and image_height > 0
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
397
|
+
# 情况2:全屏压缩截图 - 按比例转换到原图尺寸
|
|
398
|
+
elif image_width > 0 and image_height > 0:
|
|
399
|
+
# 优先使用 original_img_width/height(更精确)
|
|
400
|
+
# 如果没传,则用 screen_width/height(兼容旧版本)
|
|
401
|
+
target_width = original_img_width if original_img_width > 0 else screen_width
|
|
402
|
+
target_height = original_img_height if original_img_height > 0 else screen_height
|
|
403
|
+
|
|
404
|
+
if target_width > 0 and target_height > 0:
|
|
405
|
+
if image_width != target_width or image_height != target_height:
|
|
406
|
+
x = int(x * target_width / image_width)
|
|
407
|
+
y = int(y * target_height / image_height)
|
|
408
|
+
converted = True
|
|
409
|
+
conversion_type = "scale"
|
|
392
410
|
|
|
393
411
|
# 执行点击
|
|
394
412
|
if self._is_ios():
|
|
@@ -919,6 +937,99 @@ class BasicMobileToolsLite:
|
|
|
919
937
|
except Exception as e:
|
|
920
938
|
return [{"error": f"获取元素失败: {e}"}]
|
|
921
939
|
|
|
940
|
+
def close_popup(self) -> Dict:
|
|
941
|
+
"""智能关闭弹窗
|
|
942
|
+
|
|
943
|
+
策略:
|
|
944
|
+
1. 从控件树找可能的关闭按钮(clickable=true,尺寸小,位置靠右上角)
|
|
945
|
+
2. 如果找到,计算中心点并点击
|
|
946
|
+
3. 如果没找到,返回需要视觉识别的提示
|
|
947
|
+
"""
|
|
948
|
+
try:
|
|
949
|
+
# 获取屏幕尺寸
|
|
950
|
+
if self._is_ios():
|
|
951
|
+
return {"success": False, "message": "iOS 暂不支持,请使用截图+坐标点击"}
|
|
952
|
+
|
|
953
|
+
screen_width = self.client.u2.info.get('displayWidth', 720)
|
|
954
|
+
screen_height = self.client.u2.info.get('displayHeight', 1280)
|
|
955
|
+
|
|
956
|
+
# 获取控件树
|
|
957
|
+
xml_string = self.client.u2.dump_hierarchy()
|
|
958
|
+
elements = self.client.xml_parser.parse(xml_string)
|
|
959
|
+
|
|
960
|
+
# 找可能的关闭按钮
|
|
961
|
+
close_candidates = []
|
|
962
|
+
for elem in elements:
|
|
963
|
+
if not elem.get('clickable'):
|
|
964
|
+
continue
|
|
965
|
+
|
|
966
|
+
bounds = elem.get('bounds', '')
|
|
967
|
+
if not bounds:
|
|
968
|
+
continue
|
|
969
|
+
|
|
970
|
+
# 解析 bounds "[x1,y1][x2,y2]"
|
|
971
|
+
import re
|
|
972
|
+
match = re.match(r'\[(\d+),(\d+)\]\[(\d+),(\d+)\]', bounds)
|
|
973
|
+
if not match:
|
|
974
|
+
continue
|
|
975
|
+
|
|
976
|
+
x1, y1, x2, y2 = map(int, match.groups())
|
|
977
|
+
width = x2 - x1
|
|
978
|
+
height = y2 - y1
|
|
979
|
+
center_x = (x1 + x2) // 2
|
|
980
|
+
center_y = (y1 + y2) // 2
|
|
981
|
+
|
|
982
|
+
# 关闭按钮特征:尺寸小(30-100px),位置偏右上
|
|
983
|
+
if 30 <= width <= 100 and 30 <= height <= 100:
|
|
984
|
+
# 计算"右上角"得分(越靠右上越高)
|
|
985
|
+
right_score = center_x / screen_width # 0-1,越大越靠右
|
|
986
|
+
top_score = 1 - (center_y / screen_height) # 0-1,越大越靠上
|
|
987
|
+
# 只考虑屏幕上半部分、右半部分的按钮
|
|
988
|
+
if center_y < screen_height * 0.6 and center_x > screen_width * 0.5:
|
|
989
|
+
score = right_score * 0.5 + top_score * 0.5
|
|
990
|
+
close_candidates.append({
|
|
991
|
+
'bounds': bounds,
|
|
992
|
+
'center_x': center_x,
|
|
993
|
+
'center_y': center_y,
|
|
994
|
+
'width': width,
|
|
995
|
+
'height': height,
|
|
996
|
+
'score': score,
|
|
997
|
+
'resource_id': elem.get('resource_id', ''),
|
|
998
|
+
'text': elem.get('text', '')
|
|
999
|
+
})
|
|
1000
|
+
|
|
1001
|
+
if not close_candidates:
|
|
1002
|
+
return {
|
|
1003
|
+
"success": False,
|
|
1004
|
+
"message": "❌ 控件树未找到关闭按钮,请使用截图+视觉识别",
|
|
1005
|
+
"suggestion": "尝试局部截图右上角区域,用 crop_x, crop_y, crop_size 参数"
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
# 按得分排序,取最可能的
|
|
1009
|
+
close_candidates.sort(key=lambda x: x['score'], reverse=True)
|
|
1010
|
+
best = close_candidates[0]
|
|
1011
|
+
|
|
1012
|
+
# 点击
|
|
1013
|
+
self.client.u2.click(best['center_x'], best['center_y'])
|
|
1014
|
+
|
|
1015
|
+
# 记录操作
|
|
1016
|
+
self._record_operation(
|
|
1017
|
+
'close_popup',
|
|
1018
|
+
x=best['center_x'],
|
|
1019
|
+
y=best['center_y'],
|
|
1020
|
+
bounds=best['bounds']
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
return {
|
|
1024
|
+
"success": True,
|
|
1025
|
+
"message": f"✅ 点击关闭按钮: ({best['center_x']}, {best['center_y']})",
|
|
1026
|
+
"bounds": best['bounds'],
|
|
1027
|
+
"candidates_count": len(close_candidates)
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
except Exception as e:
|
|
1031
|
+
return {"success": False, "message": f"❌ 关闭弹窗失败: {e}"}
|
|
1032
|
+
|
|
922
1033
|
def assert_text(self, text: str) -> Dict:
|
|
923
1034
|
"""检查页面是否包含文本"""
|
|
924
1035
|
try:
|
|
@@ -99,7 +99,8 @@ class MobileMCPServer:
|
|
|
99
99
|
|
|
100
100
|
async def initialize(self):
|
|
101
101
|
"""延迟初始化设备连接"""
|
|
102
|
-
|
|
102
|
+
# 如果已成功初始化,直接返回
|
|
103
|
+
if self._initialized and self.tools is not None:
|
|
103
104
|
return
|
|
104
105
|
|
|
105
106
|
platform = self._detect_platform()
|
|
@@ -110,13 +111,13 @@ class MobileMCPServer:
|
|
|
110
111
|
|
|
111
112
|
self.client = MobileClient(platform=platform)
|
|
112
113
|
self.tools = BasicMobileToolsLite(self.client)
|
|
114
|
+
self._initialized = True # 只在成功时标记
|
|
113
115
|
print(f"📱 已连接到 {platform.upper()} 设备", file=sys.stderr)
|
|
114
116
|
except Exception as e:
|
|
115
|
-
print(f"⚠️ 设备连接失败: {e}", file=sys.stderr)
|
|
116
|
-
self.client =
|
|
117
|
+
print(f"⚠️ 设备连接失败: {e},下次调用时将重试", file=sys.stderr)
|
|
118
|
+
self.client = None
|
|
117
119
|
self.tools = None
|
|
118
|
-
|
|
119
|
-
self._initialized = True
|
|
120
|
+
# 不设置 _initialized = True,下次调用会重试
|
|
120
121
|
|
|
121
122
|
def _detect_platform(self) -> str:
|
|
122
123
|
"""自动检测设备平台"""
|
|
@@ -225,17 +226,20 @@ class MobileMCPServer:
|
|
|
225
226
|
"- 游戏(Unity/Cocos)无法获取元素\n"
|
|
226
227
|
"- mobile_list_elements 返回空\n"
|
|
227
228
|
"- 元素没有 id 和 text\n\n"
|
|
228
|
-
"⚠️
|
|
229
|
-
"
|
|
230
|
-
"
|
|
229
|
+
"⚠️ 【坐标转换】截图返回的参数直接传入:\n"
|
|
230
|
+
" - image_width/image_height: 压缩后尺寸(AI 看到的)\n"
|
|
231
|
+
" - original_img_width/original_img_height: 原图尺寸(用于转换)\n"
|
|
232
|
+
" - crop_offset_x/crop_offset_y: 局部截图偏移\n\n"
|
|
231
233
|
"✅ 自动记录百分比坐标,生成脚本时转换为跨分辨率兼容的百分比定位",
|
|
232
234
|
inputSchema={
|
|
233
235
|
"type": "object",
|
|
234
236
|
"properties": {
|
|
235
|
-
"x": {"type": "number", "description": "X
|
|
236
|
-
"y": {"type": "number", "description": "Y
|
|
237
|
-
"image_width": {"type": "number", "description": "
|
|
238
|
-
"image_height": {"type": "number", "description": "
|
|
237
|
+
"x": {"type": "number", "description": "X 坐标(来自 AI 分析截图)"},
|
|
238
|
+
"y": {"type": "number", "description": "Y 坐标(来自 AI 分析截图)"},
|
|
239
|
+
"image_width": {"type": "number", "description": "压缩后图片宽度(截图返回的 image_width)"},
|
|
240
|
+
"image_height": {"type": "number", "description": "压缩后图片高度(截图返回的 image_height)"},
|
|
241
|
+
"original_img_width": {"type": "number", "description": "原图宽度(截图返回的 original_img_width)"},
|
|
242
|
+
"original_img_height": {"type": "number", "description": "原图高度(截图返回的 original_img_height)"},
|
|
239
243
|
"crop_offset_x": {"type": "number", "description": "局部截图 X 偏移(裁剪截图时传入)"},
|
|
240
244
|
"crop_offset_y": {"type": "number", "description": "局部截图 Y 偏移(裁剪截图时传入)"}
|
|
241
245
|
},
|
|
@@ -383,6 +387,22 @@ class MobileMCPServer:
|
|
|
383
387
|
))
|
|
384
388
|
|
|
385
389
|
# ==================== 辅助工具 ====================
|
|
390
|
+
tools.append(Tool(
|
|
391
|
+
name="mobile_close_popup",
|
|
392
|
+
description="""🚫 智能关闭弹窗(推荐!)
|
|
393
|
+
|
|
394
|
+
自动从控件树识别关闭按钮并点击。
|
|
395
|
+
|
|
396
|
+
🎯 识别策略:
|
|
397
|
+
1. 找 clickable=true 且尺寸小(30-100px)的元素
|
|
398
|
+
2. 位置在屏幕右上角区域
|
|
399
|
+
3. 计算 bounds 中心点一次点击
|
|
400
|
+
|
|
401
|
+
✅ 优势:比视觉识别更精准,一次成功率高
|
|
402
|
+
❌ 限制:如果关闭按钮是图片的一部分(无独立控件),需要用截图+坐标点击""",
|
|
403
|
+
inputSchema={"type": "object", "properties": {}, "required": []}
|
|
404
|
+
))
|
|
405
|
+
|
|
386
406
|
tools.append(Tool(
|
|
387
407
|
name="mobile_assert_text",
|
|
388
408
|
description="✅ 检查页面是否包含指定文本。用于验证操作结果。",
|
|
@@ -473,7 +493,9 @@ class MobileMCPServer:
|
|
|
473
493
|
arguments.get("image_width", 0),
|
|
474
494
|
arguments.get("image_height", 0),
|
|
475
495
|
arguments.get("crop_offset_x", 0),
|
|
476
|
-
arguments.get("crop_offset_y", 0)
|
|
496
|
+
arguments.get("crop_offset_y", 0),
|
|
497
|
+
arguments.get("original_img_width", 0),
|
|
498
|
+
arguments.get("original_img_height", 0)
|
|
477
499
|
)
|
|
478
500
|
return [TextContent(type="text", text=self.format_response(result))]
|
|
479
501
|
|
|
@@ -538,6 +560,10 @@ class MobileMCPServer:
|
|
|
538
560
|
result = self.tools.list_elements()
|
|
539
561
|
return [TextContent(type="text", text=self.format_response(result))]
|
|
540
562
|
|
|
563
|
+
elif name == "mobile_close_popup":
|
|
564
|
+
result = self.tools.close_popup()
|
|
565
|
+
return [TextContent(type="text", text=self.format_response(result))]
|
|
566
|
+
|
|
541
567
|
elif name == "mobile_assert_text":
|
|
542
568
|
result = self.tools.assert_text(arguments["text"])
|
|
543
569
|
return [TextContent(type="text", text=self.format_response(result))]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: mobile-mcp-ai
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.6
|
|
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
|
|
@@ -31,20 +31,6 @@ Provides-Extra: ai
|
|
|
31
31
|
Requires-Dist: dashscope>=1.10.0; extra == "ai"
|
|
32
32
|
Requires-Dist: openai>=1.0.0; extra == "ai"
|
|
33
33
|
Requires-Dist: anthropic>=0.3.0; extra == "ai"
|
|
34
|
-
Provides-Extra: test
|
|
35
|
-
Requires-Dist: pytest>=8.0.0; extra == "test"
|
|
36
|
-
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
|
|
37
|
-
Requires-Dist: allure-pytest>=2.13.0; extra == "test"
|
|
38
|
-
Provides-Extra: dev
|
|
39
|
-
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
40
|
-
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
41
|
-
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
42
|
-
Requires-Dist: build>=0.10.0; extra == "dev"
|
|
43
|
-
Provides-Extra: ios
|
|
44
|
-
Requires-Dist: Appium-Python-Client>=3.0.0; extra == "ios"
|
|
45
|
-
Provides-Extra: h5
|
|
46
|
-
Requires-Dist: Appium-Python-Client>=3.0.0; extra == "h5"
|
|
47
|
-
Requires-Dist: selenium>=4.0.0; extra == "h5"
|
|
48
34
|
Provides-Extra: all
|
|
49
35
|
Requires-Dist: dashscope>=1.10.0; extra == "all"
|
|
50
36
|
Requires-Dist: openai>=1.0.0; extra == "all"
|
|
@@ -54,19 +40,20 @@ Requires-Dist: selenium>=4.0.0; extra == "all"
|
|
|
54
40
|
Requires-Dist: pytest>=8.0.0; extra == "all"
|
|
55
41
|
Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
|
|
56
42
|
Requires-Dist: allure-pytest>=2.13.0; extra == "all"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
43
|
+
Provides-Extra: dev
|
|
44
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
46
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
47
|
+
Requires-Dist: build>=0.10.0; extra == "dev"
|
|
48
|
+
Provides-Extra: h5
|
|
49
|
+
Requires-Dist: Appium-Python-Client>=3.0.0; extra == "h5"
|
|
50
|
+
Requires-Dist: selenium>=4.0.0; extra == "h5"
|
|
51
|
+
Provides-Extra: ios
|
|
52
|
+
Requires-Dist: Appium-Python-Client>=3.0.0; extra == "ios"
|
|
53
|
+
Provides-Extra: test
|
|
54
|
+
Requires-Dist: pytest>=8.0.0; extra == "test"
|
|
55
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
|
|
56
|
+
Requires-Dist: allure-pytest>=2.13.0; extra == "test"
|
|
70
57
|
|
|
71
58
|
# 📱 Mobile MCP AI
|
|
72
59
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
mobile_mcp/__init__.py,sha256=sQJZTL_sxQFzmcS7jOtS2AHCfUySz40vhX96N6u1qy4,816
|
|
2
2
|
mobile_mcp/config.py,sha256=yaFLAV4bc2wX0GQPtZDo7OYF9E88tXV-av41fQsJwK4,4480
|
|
3
3
|
mobile_mcp/core/__init__.py,sha256=ndMy-cLAIsQDG5op7gM_AIplycqZSZPWEkec1pEhvEY,170
|
|
4
|
-
mobile_mcp/core/basic_tools_lite.py,sha256=
|
|
4
|
+
mobile_mcp/core/basic_tools_lite.py,sha256=uqd-YG9vTxQmJEVmio_z_yj8LOeZnhvRP7YxvSueuZs,59803
|
|
5
5
|
mobile_mcp/core/device_manager.py,sha256=PX3-B5bJFnKNt6C8fT7FSY8JwD-ngZ3toF88bcOV9qA,8766
|
|
6
6
|
mobile_mcp/core/dynamic_config.py,sha256=Ja1n1pfb0HspGByqk2_A472mYVniKmGtNEWyjUjmgK8,9811
|
|
7
|
-
mobile_mcp/core/ios_client_wda.py,sha256=
|
|
7
|
+
mobile_mcp/core/ios_client_wda.py,sha256=KudSbWTy-0l8OMQjXpsDYAiL59w7HVrw-i7ApfExJLA,18755
|
|
8
8
|
mobile_mcp/core/ios_device_manager_wda.py,sha256=A44glqI-24un7qST-E3w6BQD8mV92YVUbxy4rLlTScY,11264
|
|
9
9
|
mobile_mcp/core/mobile_client.py,sha256=bno3HvU-QSAC3G4TnoFngTxqXeu-ZP5rGlEWdWh8jOo,62570
|
|
10
10
|
mobile_mcp/core/utils/__init__.py,sha256=RhMMsPszmEn8Q8GoNufypVSHJxyM9lio9U6jjpnuoPI,378
|
|
@@ -12,14 +12,14 @@ mobile_mcp/core/utils/logger.py,sha256=XXQAHUwT1jc70pq_tYFmL6f_nKrFlYm3hcgl-5RYR
|
|
|
12
12
|
mobile_mcp/core/utils/operation_history_manager.py,sha256=gi8S8HJAMqvkUrY7_-kVbko3Xt7c4GAUziEujRd-N-Y,4792
|
|
13
13
|
mobile_mcp/core/utils/smart_wait.py,sha256=PvKXImfN9Irru3bQJUjf4FLGn8LjY2VLzUNEl-i7xLE,8601
|
|
14
14
|
mobile_mcp/mcp_tools/__init__.py,sha256=xkro8Rwqv_55YlVyhh-3DgRFSsLE3h1r31VIb3bpM6E,143
|
|
15
|
-
mobile_mcp/mcp_tools/mcp_server.py,sha256=
|
|
15
|
+
mobile_mcp/mcp_tools/mcp_server.py,sha256=OUpQ53A22pOhbeHRuT6oibEQ_Ycb4PzsIz7u5Ynk7wA,27960
|
|
16
16
|
mobile_mcp/utils/__init__.py,sha256=8EH0i7UGtx1y_j_GEgdN-cZdWn2sRtZSEOLlNF9HRnY,158
|
|
17
17
|
mobile_mcp/utils/logger.py,sha256=Sqq2Nr0Y4p03erqcrbYKVPCGiFaNGHMcE_JwCkeOfU4,3626
|
|
18
18
|
mobile_mcp/utils/xml_formatter.py,sha256=uwTRb3vLbqhT8O-udzWT7s7LsV-DyDUz2DkofD3hXOE,4556
|
|
19
19
|
mobile_mcp/utils/xml_parser.py,sha256=QhL8CWbdmNDzmBLjtx6mEnjHgMFZzJeHpCL15qfXSpI,3926
|
|
20
|
-
mobile_mcp_ai-2.3.
|
|
21
|
-
mobile_mcp_ai-2.3.
|
|
22
|
-
mobile_mcp_ai-2.3.
|
|
23
|
-
mobile_mcp_ai-2.3.
|
|
24
|
-
mobile_mcp_ai-2.3.
|
|
25
|
-
mobile_mcp_ai-2.3.
|
|
20
|
+
mobile_mcp_ai-2.3.6.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
21
|
+
mobile_mcp_ai-2.3.6.dist-info/METADATA,sha256=sJOSvqLIeuVVfxEE0mR_AyuT37uzBnnCHwM1EV3k_1g,9423
|
|
22
|
+
mobile_mcp_ai-2.3.6.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
23
|
+
mobile_mcp_ai-2.3.6.dist-info/entry_points.txt,sha256=KB_FglozgPHBprSM1vFbIzGyheFuHFmGanscRdMJ_8A,68
|
|
24
|
+
mobile_mcp_ai-2.3.6.dist-info/top_level.txt,sha256=lLm6YpbTv855Lbh8BIA0rPxhybIrvYUzMEk9OErHT94,11
|
|
25
|
+
mobile_mcp_ai-2.3.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|