mobile-mcp-ai 2.5.5__py3-none-any.whl → 2.5.7__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/__init__.py CHANGED
@@ -22,7 +22,7 @@
22
22
  await client.type_text("用户名输入框", "test@example.com")
23
23
  """
24
24
 
25
- __version__ = "2.5.5"
25
+ __version__ = "1.0.0"
26
26
 
27
27
  from .core.mobile_client import MobileClient
28
28
  from .core.device_manager import DeviceManager
@@ -1126,9 +1126,16 @@ class BasicMobileToolsLite:
1126
1126
  except Exception:
1127
1127
  return None
1128
1128
 
1129
- def click_by_id(self, resource_id: str) -> Dict:
1130
- """通过 resource-id 点击"""
1129
+ def click_by_id(self, resource_id: str, index: int = 0) -> Dict:
1130
+ """通过 resource-id 点击(支持点击第 N 个元素)
1131
+
1132
+ Args:
1133
+ resource_id: 元素的 resource-id
1134
+ index: 第几个元素(从 0 开始),默认 0 表示第一个
1135
+ """
1131
1136
  try:
1137
+ index_desc = f"[{index}]" if index > 0 else ""
1138
+
1132
1139
  if self._is_ios():
1133
1140
  ios_client = self._get_ios_client()
1134
1141
  if ios_client and hasattr(ios_client, 'wda'):
@@ -1136,18 +1143,28 @@ class BasicMobileToolsLite:
1136
1143
  if not elem.exists:
1137
1144
  elem = ios_client.wda(name=resource_id)
1138
1145
  if elem.exists:
1139
- elem.click()
1140
- time.sleep(0.3)
1141
- self._record_operation('click', element=resource_id, ref=resource_id)
1142
- return {"success": True, "message": f"✅ 点击成功: {resource_id}"}
1146
+ # 获取所有匹配的元素
1147
+ elements = elem.find_elements()
1148
+ if index < len(elements):
1149
+ elements[index].click()
1150
+ time.sleep(0.3)
1151
+ self._record_operation('click', element=f"{resource_id}{index_desc}", ref=resource_id, index=index)
1152
+ return {"success": True, "message": f"✅ 点击成功: {resource_id}{index_desc}"}
1153
+ else:
1154
+ return {"success": False, "message": f"❌ 索引超出范围: 找到 {len(elements)} 个元素,但请求索引 {index}"}
1143
1155
  return {"success": False, "message": f"❌ 元素不存在: {resource_id}"}
1144
1156
  else:
1145
1157
  elem = self.client.u2(resourceId=resource_id)
1146
1158
  if elem.exists(timeout=0.5):
1147
- elem.click()
1148
- time.sleep(0.3)
1149
- self._record_operation('click', element=resource_id, ref=resource_id)
1150
- return {"success": True, "message": f"✅ 点击成功: {resource_id}"}
1159
+ # 获取匹配元素数量
1160
+ count = elem.count
1161
+ if index < count:
1162
+ elem[index].click()
1163
+ time.sleep(0.3)
1164
+ self._record_operation('click', element=f"{resource_id}{index_desc}", ref=resource_id, index=index)
1165
+ return {"success": True, "message": f"✅ 点击成功: {resource_id}{index_desc}" + (f" (共 {count} 个)" if count > 1 else "")}
1166
+ else:
1167
+ return {"success": False, "message": f"❌ 索引超出范围: 找到 {count} 个元素,但请求索引 {index}"}
1151
1168
  return {"success": False, "message": f"❌ 元素不存在: {resource_id}"}
1152
1169
  except Exception as e:
1153
1170
  return {"success": False, "message": f"❌ 点击失败: {e}"}
@@ -2360,22 +2377,43 @@ class BasicMobileToolsLite:
2360
2377
  return 0.5
2361
2378
 
2362
2379
  def assert_text(self, text: str) -> Dict:
2363
- """检查页面是否包含文本"""
2380
+ """检查页面是否包含文本(支持精确匹配和包含匹配)"""
2364
2381
  try:
2382
+ exists = False
2383
+ match_type = ""
2384
+
2365
2385
  if self._is_ios():
2366
2386
  ios_client = self._get_ios_client()
2367
2387
  if ios_client and hasattr(ios_client, 'wda'):
2368
- exists = ios_client.wda(name=text).exists or ios_client.wda(label=text).exists
2369
- else:
2370
- exists = False
2388
+ # 先尝试精确匹配
2389
+ if ios_client.wda(name=text).exists or ios_client.wda(label=text).exists:
2390
+ exists = True
2391
+ match_type = "精确匹配"
2392
+ # 再尝试包含匹配
2393
+ elif ios_client.wda(nameContains=text).exists or ios_client.wda(labelContains=text).exists:
2394
+ exists = True
2395
+ match_type = "包含匹配"
2396
+ else:
2397
+ # Android: 先尝试精确匹配
2398
+ if self.client.u2(text=text).exists():
2399
+ exists = True
2400
+ match_type = "精确匹配"
2401
+ # 再尝试包含匹配
2402
+ elif self.client.u2(textContains=text).exists():
2403
+ exists = True
2404
+ match_type = "包含匹配"
2405
+
2406
+ if exists:
2407
+ message = f"✅ 文本'{text}' 存在({match_type})"
2371
2408
  else:
2372
- exists = self.client.u2(text=text).exists()
2409
+ message = f"❌ 文本'{text}' 不存在"
2373
2410
 
2374
2411
  return {
2375
2412
  "success": True,
2376
2413
  "found": exists,
2377
2414
  "text": text,
2378
- "message": f"✅ 文本'{text}' {'存在' if exists else '不存在'}"
2415
+ "match_type": match_type if exists else None,
2416
+ "message": message
2379
2417
  }
2380
2418
  except Exception as e:
2381
2419
  return {"success": False, "message": f"❌ 断言失败: {e}"}
@@ -331,11 +331,13 @@ class MobileMCPServer:
331
331
  description="👆 通过 resource-id 点击元素(最推荐)\n\n"
332
332
  "✅ 最稳定的定位方式\n"
333
333
  "✅ 实时检测元素是否存在,元素不存在会报错\n"
334
- "📋 使用前先调用 mobile_list_elements 获取元素 ID",
334
+ "📋 使用前先调用 mobile_list_elements 获取元素 ID\n"
335
+ "💡 当有多个相同 ID 的元素时,用 index 指定第几个(从 0 开始)",
335
336
  inputSchema={
336
337
  "type": "object",
337
338
  "properties": {
338
- "resource_id": {"type": "string", "description": "元素的 resource-id"}
339
+ "resource_id": {"type": "string", "description": "元素的 resource-id"},
340
+ "index": {"type": "integer", "description": "第几个元素(从 0 开始),默认 0 表示第一个", "default": 0}
339
341
  },
340
342
  "required": ["resource_id"]
341
343
  }
@@ -860,7 +862,10 @@ class MobileMCPServer:
860
862
  return [TextContent(type="text", text=self.format_response(result))]
861
863
 
862
864
  elif name == "mobile_click_by_id":
863
- result = self.tools.click_by_id(arguments["resource_id"])
865
+ result = self.tools.click_by_id(
866
+ arguments["resource_id"],
867
+ arguments.get("index", 0)
868
+ )
864
869
  return [TextContent(type="text", text=self.format_response(result))]
865
870
 
866
871
  elif name == "mobile_click_by_percent":
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: mobile-mcp-ai
3
- Version: 2.5.5
3
+ Version: 2.5.7
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,21 +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: tidevice>=0.11.0; extra == "ios"
45
- Requires-Dist: facebook-wda>=1.4.0; extra == "ios"
46
- Provides-Extra: h5
47
- Requires-Dist: Appium-Python-Client>=3.0.0; extra == "h5"
48
- Requires-Dist: selenium>=4.0.0; extra == "h5"
49
34
  Provides-Extra: all
50
35
  Requires-Dist: dashscope>=1.10.0; extra == "all"
51
36
  Requires-Dist: openai>=1.0.0; extra == "all"
@@ -55,19 +40,21 @@ Requires-Dist: selenium>=4.0.0; extra == "all"
55
40
  Requires-Dist: pytest>=8.0.0; extra == "all"
56
41
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
57
42
  Requires-Dist: allure-pytest>=2.13.0; extra == "all"
58
- Dynamic: author
59
- Dynamic: author-email
60
- Dynamic: classifier
61
- Dynamic: description
62
- Dynamic: description-content-type
63
- Dynamic: home-page
64
- Dynamic: keywords
65
- Dynamic: license-file
66
- Dynamic: project-url
67
- Dynamic: provides-extra
68
- Dynamic: requires-dist
69
- Dynamic: requires-python
70
- Dynamic: summary
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: tidevice>=0.11.0; extra == "ios"
53
+ Requires-Dist: facebook-wda>=1.4.0; extra == "ios"
54
+ Provides-Extra: test
55
+ Requires-Dist: pytest>=8.0.0; extra == "test"
56
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
57
+ Requires-Dist: allure-pytest>=2.13.0; extra == "test"
71
58
 
72
59
  # 📱 Mobile MCP AI
73
60
 
@@ -1,7 +1,7 @@
1
- mobile_mcp/__init__.py,sha256=8FmpYKR75HYmrgWwCgzV4iellF0FMwSxfoy9u_fzioU,816
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=rvVCCQEtVhLt24lBgaXMdG0ATb4O89lXEiczD3IrA1w,151423
4
+ mobile_mcp/core/basic_tools_lite.py,sha256=wPrn9y7xUspQUhXRNQXhksekz5CNVVH7CbEfiD8yUeE,153449
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
7
  mobile_mcp/core/ios_client_wda.py,sha256=Nq9WxevhTWpVpolM-Ymp-b0nUQV3tXLFszmJHbDC4wA,18770
@@ -19,14 +19,14 @@ mobile_mcp/core/utils/logger.py,sha256=XXQAHUwT1jc70pq_tYFmL6f_nKrFlYm3hcgl-5RYR
19
19
  mobile_mcp/core/utils/operation_history_manager.py,sha256=gi8S8HJAMqvkUrY7_-kVbko3Xt7c4GAUziEujRd-N-Y,4792
20
20
  mobile_mcp/core/utils/smart_wait.py,sha256=PvKXImfN9Irru3bQJUjf4FLGn8LjY2VLzUNEl-i7xLE,8601
21
21
  mobile_mcp/mcp_tools/__init__.py,sha256=xkro8Rwqv_55YlVyhh-3DgRFSsLE3h1r31VIb3bpM6E,143
22
- mobile_mcp/mcp_tools/mcp_server.py,sha256=XP4nWbeNW0jM7QFvUiQ1DM0CU3UMW8Do8uxwRLAARYc,49527
22
+ mobile_mcp/mcp_tools/mcp_server.py,sha256=fr42BjulYQCBreWoLx5lkWEZjT60Df6RCo_kyXMOHfI,49868
23
23
  mobile_mcp/utils/__init__.py,sha256=8EH0i7UGtx1y_j_GEgdN-cZdWn2sRtZSEOLlNF9HRnY,158
24
24
  mobile_mcp/utils/logger.py,sha256=Sqq2Nr0Y4p03erqcrbYKVPCGiFaNGHMcE_JwCkeOfU4,3626
25
25
  mobile_mcp/utils/xml_formatter.py,sha256=uwTRb3vLbqhT8O-udzWT7s7LsV-DyDUz2DkofD3hXOE,4556
26
26
  mobile_mcp/utils/xml_parser.py,sha256=QhL8CWbdmNDzmBLjtx6mEnjHgMFZzJeHpCL15qfXSpI,3926
27
- mobile_mcp_ai-2.5.5.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
28
- mobile_mcp_ai-2.5.5.dist-info/METADATA,sha256=hIceENDIHhcKXeiGHg9tS3iADHSky7HEPPTHR0QJ9EA,10495
29
- mobile_mcp_ai-2.5.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
- mobile_mcp_ai-2.5.5.dist-info/entry_points.txt,sha256=KB_FglozgPHBprSM1vFbIzGyheFuHFmGanscRdMJ_8A,68
31
- mobile_mcp_ai-2.5.5.dist-info/top_level.txt,sha256=lLm6YpbTv855Lbh8BIA0rPxhybIrvYUzMEk9OErHT94,11
32
- mobile_mcp_ai-2.5.5.dist-info/RECORD,,
27
+ mobile_mcp_ai-2.5.7.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
28
+ mobile_mcp_ai-2.5.7.dist-info/METADATA,sha256=RN8xuzGt0p_qbnQkSkxcUznXmNVJgtn9KY9lotAN49Q,10213
29
+ mobile_mcp_ai-2.5.7.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
30
+ mobile_mcp_ai-2.5.7.dist-info/entry_points.txt,sha256=KB_FglozgPHBprSM1vFbIzGyheFuHFmGanscRdMJ_8A,68
31
+ mobile_mcp_ai-2.5.7.dist-info/top_level.txt,sha256=lLm6YpbTv855Lbh8BIA0rPxhybIrvYUzMEk9OErHT94,11
32
+ mobile_mcp_ai-2.5.7.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: bdist_wheel (0.45.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5