androidviewclient 24.1.0__tar.gz → 25.0.1__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.
- androidviewclient-25.0.1/PKG-INFO +40 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/README.md +86 -0
- androidviewclient-25.0.1/setup.py +53 -0
- androidviewclient-25.0.1/src/androidviewclient.egg-info/PKG-INFO +40 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/androidviewclient.egg-info/SOURCES.txt +6 -0
- androidviewclient-25.0.1/src/androidviewclient.egg-info/entry_points.txt +2 -0
- androidviewclient-25.0.1/src/androidviewclient.egg-info/requires.txt +12 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/adb/adbclient.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/adb/dumpsys.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/code_generator.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/common.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/concertina.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/controlpanel.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/culebron.py +2 -7
- androidviewclient-25.0.1/src/com/dtmilano/android/mcp/__init__.py +33 -0
- androidviewclient-25.0.1/src/com/dtmilano/android/mcp/object_store.py +139 -0
- androidviewclient-25.0.1/src/com/dtmilano/android/mcp/server.py +111 -0
- androidviewclient-25.0.1/src/com/dtmilano/android/mcp/tools.py +721 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/plot.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/robotframework/viewclientwrapper.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/uiautomator/uiautomatorhelper.py +1 -1
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/viewclient.py +19 -15
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/tools/culebra +1 -1
- androidviewclient-25.0.1/tools/culebra-mcp +35 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/tools/dump +1 -1
- androidviewclient-24.1.0/PKG-INFO +0 -18
- androidviewclient-24.1.0/setup.py +0 -26
- androidviewclient-24.1.0/src/androidviewclient.egg-info/PKG-INFO +0 -18
- androidviewclient-24.1.0/src/androidviewclient.egg-info/requires.txt +0 -5
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/LICENSE +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/setup.cfg +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/androidviewclient.egg-info/dependency_links.txt +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/androidviewclient.egg-info/top_level.txt +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/adb/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/adb/androidkeymap.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/distance.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/kato/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/kato/kato.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/keyevent.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/robotframework/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/uiautomator/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/uiautomator/utils.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/com/dtmilano/android/window.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/culebra.png +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/plugins/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/plugins/android/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/plugins/android/lib/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/plugins/android/lib/device-art-resources/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/plugins/android/lib/device-art-resources/samsung_s4/__init__.py +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/plugins/android/lib/device-art-resources/samsung_s4/port_back.png +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/plugins/android/lib/device-art-resources/samsung_s4/port_fore.png +0 -0
- {androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/share/pixmaps/plugins/android/lib/device-art-resources/samsung_s4/port_shadow.png +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: androidviewclient
|
|
3
|
+
Version: 25.0.1
|
|
4
|
+
Summary: AndroidViewClient is a 100% pure python library and tools
|
|
5
|
+
Home-page: https://github.com/dtmilano/AndroidViewClient/
|
|
6
|
+
Author: Diego Torres Milano
|
|
7
|
+
Author-email: dtmilano@gmail.com
|
|
8
|
+
License: Apache
|
|
9
|
+
Keywords: android uiautomator viewclient monkeyrunner test automation mcp
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: setuptools
|
|
20
|
+
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: matplotlib
|
|
23
|
+
Requires-Dist: culebratester-client>=2.0.73
|
|
24
|
+
Requires-Dist: mcp<2,>=0.9.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
28
|
+
Requires-Dist: hypothesis>=6.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: responses>=0.23.0; extra == "dev"
|
|
30
|
+
Dynamic: author
|
|
31
|
+
Dynamic: author-email
|
|
32
|
+
Dynamic: classifier
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: keywords
|
|
35
|
+
Dynamic: license
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
Dynamic: provides-extra
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
|
@@ -39,6 +39,92 @@ pip3 install androidviewclient --upgrade
|
|
|
39
39
|
```
|
|
40
40
|
Or check the wiki for more alternatives.
|
|
41
41
|
|
|
42
|
+
# AI-Powered Testing with MCP
|
|
43
|
+
|
|
44
|
+
**NEW!** AndroidViewClient now includes a Model Context Protocol (MCP) server that enables AI assistants like Claude Code and Kiro to interact with Android devices through natural language.
|
|
45
|
+
|
|
46
|
+
## Quick Start with MCP
|
|
47
|
+
|
|
48
|
+
1. **Install with MCP support:**
|
|
49
|
+
```bash
|
|
50
|
+
pip3 install androidviewclient --upgrade
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. **Start CulebraTester2 on your device:**
|
|
54
|
+
|
|
55
|
+
Check the details at [How to run CulebraTester2 ?](https://github.com/dtmilano/CulebraTester2-public?tab=readme-ov-file#how-to-run-culebratester2-)
|
|
56
|
+
|
|
57
|
+
3. **Configure your AI assistant:**
|
|
58
|
+
|
|
59
|
+
**Claude Code** — register the server with one command:
|
|
60
|
+
```bash
|
|
61
|
+
claude mcp add culebratester2 --env CULEBRATESTER2_URL=http://localhost:9987 -- culebra-mcp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or add it to `.mcp.json` in your project root, so everyone who clones the project gets it:
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"mcpServers": {
|
|
68
|
+
"culebratester2": {
|
|
69
|
+
"command": "culebra-mcp",
|
|
70
|
+
"env": {
|
|
71
|
+
"CULEBRATESTER2_URL": "http://localhost:9987"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Kiro** — add the same entry to `.kiro/settings/mcp.json` or `~/.kiro/settings/mcp.json`.
|
|
79
|
+
|
|
80
|
+
4. **Start testing with natural language:**
|
|
81
|
+
- "_Get the device screen size_"
|
|
82
|
+
- "_Launch the Calculator app_"
|
|
83
|
+
- "_Find the button with text Submit and click it_"
|
|
84
|
+
- "_Take a screenshot_"
|
|
85
|
+
- "_Swipe up to scroll_"
|
|
86
|
+
|
|
87
|
+
## MCP Tools Available
|
|
88
|
+
|
|
89
|
+
The MCP server provides 20 tools for Android automation:
|
|
90
|
+
|
|
91
|
+
**Element-based interactions:**
|
|
92
|
+
- Find elements by text or resource ID
|
|
93
|
+
- Click, long-click, enter text, clear text
|
|
94
|
+
- Navigate with back/home buttons
|
|
95
|
+
- Launch applications
|
|
96
|
+
|
|
97
|
+
**Coordinate-based interactions:**
|
|
98
|
+
- Click/long-click at coordinates
|
|
99
|
+
- Swipe gestures
|
|
100
|
+
|
|
101
|
+
**Device actions:**
|
|
102
|
+
- Wake/sleep device
|
|
103
|
+
- Get current app
|
|
104
|
+
- Force stop apps
|
|
105
|
+
- Take screenshots
|
|
106
|
+
|
|
107
|
+
## Configuration
|
|
108
|
+
|
|
109
|
+
For detailed MCP configuration options, see the [MCP Configuration Guide](docs/MCP_CONFIGURATION.md).
|
|
110
|
+
|
|
111
|
+
Quick reference:
|
|
112
|
+
|
|
113
|
+
| | Claude Code | Kiro |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| **Project config** | `.mcp.json` (repository root) | `.kiro/settings/mcp.json` |
|
|
116
|
+
| **User config** | `~/.claude.json` (via `claude mcp add --scope user`) | `~/.kiro/settings/mcp.json` |
|
|
117
|
+
| **Tool pre-approval** | `permissions.allow` in `.claude/settings.json` | `autoApprove` in the server entry |
|
|
118
|
+
| **Example** | `examples/mcp_config.json` | `examples/mcp_config_kiro.json` |
|
|
119
|
+
|
|
120
|
+
- **Usage examples:** `examples/test_calculator_mcp.py`
|
|
121
|
+
|
|
122
|
+
## Environment Variables
|
|
123
|
+
|
|
124
|
+
- `CULEBRATESTER2_URL`: Base URL for CulebraTester2 (default: `http://localhost:9987`)
|
|
125
|
+
- `CULEBRATESTER2_TIMEOUT`: HTTP timeout in seconds (default: `30`)
|
|
126
|
+
- `CULEBRATESTER2_DEBUG`: Enable debug logging (`1`, `true`, or `yes`)
|
|
127
|
+
|
|
42
128
|
# Want to learn more?
|
|
43
129
|
|
|
44
130
|
> 🚀 Check [Examples](https://github.com/dtmilano/AndroidViewClient/wiki/Resources#examples) and [Screencasts and videos](https://github.com/dtmilano/AndroidViewClient/wiki/Resources#screencasts-and-videos) page to see it in action.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from setuptools import setup, find_packages
|
|
4
|
+
|
|
5
|
+
setup(name='androidviewclient',
|
|
6
|
+
version='25.0.1',
|
|
7
|
+
description='''AndroidViewClient is a 100% pure python library and tools
|
|
8
|
+
that simplifies test script creation providing higher level
|
|
9
|
+
operations and the ability of obtaining the tree of Views present at
|
|
10
|
+
any given moment on the device or emulator screen.
|
|
11
|
+
''',
|
|
12
|
+
license='Apache',
|
|
13
|
+
keywords='android uiautomator viewclient monkeyrunner test automation mcp',
|
|
14
|
+
author='Diego Torres Milano',
|
|
15
|
+
author_email='dtmilano@gmail.com',
|
|
16
|
+
url='https://github.com/dtmilano/AndroidViewClient/',
|
|
17
|
+
packages=find_packages('src'),
|
|
18
|
+
package_dir={'': 'src'},
|
|
19
|
+
package_data={'': ['*.png']},
|
|
20
|
+
include_package_data=True,
|
|
21
|
+
scripts=['tools/culebra', 'tools/dump', 'tools/culebra-mcp'],
|
|
22
|
+
entry_points={
|
|
23
|
+
'console_scripts': [
|
|
24
|
+
'culebra-mcp=com.dtmilano.android.mcp.server:main',
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
python_requires='>=3.9',
|
|
28
|
+
classifiers=['Development Status :: 5 - Production/Stable',
|
|
29
|
+
'Intended Audience :: Developers',
|
|
30
|
+
'License :: OSI Approved :: Apache Software License',
|
|
31
|
+
'Programming Language :: Python :: 3.9',
|
|
32
|
+
'Programming Language :: Python :: 3.10',
|
|
33
|
+
'Programming Language :: Python :: 3.11',
|
|
34
|
+
'Programming Language :: Python :: 3.12'],
|
|
35
|
+
install_requires=[
|
|
36
|
+
'setuptools',
|
|
37
|
+
'requests',
|
|
38
|
+
'numpy',
|
|
39
|
+
'matplotlib',
|
|
40
|
+
'culebratester-client >= 2.0.73',
|
|
41
|
+
# mcp 2.x renamed FastMCP to MCPServer and changed other APIs;
|
|
42
|
+
# com.dtmilano.android.mcp.server still uses the v1 API.
|
|
43
|
+
'mcp >= 0.9.0, < 2',
|
|
44
|
+
],
|
|
45
|
+
extras_require={
|
|
46
|
+
'dev': [
|
|
47
|
+
'pytest >= 7.0.0',
|
|
48
|
+
'pytest-asyncio >= 0.21.0',
|
|
49
|
+
'hypothesis >= 6.0.0',
|
|
50
|
+
'responses >= 0.23.0',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: androidviewclient
|
|
3
|
+
Version: 25.0.1
|
|
4
|
+
Summary: AndroidViewClient is a 100% pure python library and tools
|
|
5
|
+
Home-page: https://github.com/dtmilano/AndroidViewClient/
|
|
6
|
+
Author: Diego Torres Milano
|
|
7
|
+
Author-email: dtmilano@gmail.com
|
|
8
|
+
License: Apache
|
|
9
|
+
Keywords: android uiautomator viewclient monkeyrunner test automation mcp
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: setuptools
|
|
20
|
+
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: numpy
|
|
22
|
+
Requires-Dist: matplotlib
|
|
23
|
+
Requires-Dist: culebratester-client>=2.0.73
|
|
24
|
+
Requires-Dist: mcp<2,>=0.9.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
28
|
+
Requires-Dist: hypothesis>=6.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: responses>=0.23.0; extra == "dev"
|
|
30
|
+
Dynamic: author
|
|
31
|
+
Dynamic: author-email
|
|
32
|
+
Dynamic: classifier
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: keywords
|
|
35
|
+
Dynamic: license
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
Dynamic: provides-extra
|
|
38
|
+
Dynamic: requires-dist
|
|
39
|
+
Dynamic: requires-python
|
|
40
|
+
Dynamic: summary
|
{androidviewclient-24.1.0 → androidviewclient-25.0.1}/src/androidviewclient.egg-info/SOURCES.txt
RENAMED
|
@@ -4,6 +4,7 @@ setup.py
|
|
|
4
4
|
src/androidviewclient.egg-info/PKG-INFO
|
|
5
5
|
src/androidviewclient.egg-info/SOURCES.txt
|
|
6
6
|
src/androidviewclient.egg-info/dependency_links.txt
|
|
7
|
+
src/androidviewclient.egg-info/entry_points.txt
|
|
7
8
|
src/androidviewclient.egg-info/requires.txt
|
|
8
9
|
src/androidviewclient.egg-info/top_level.txt
|
|
9
10
|
src/com/__init__.py
|
|
@@ -25,6 +26,10 @@ src/com/dtmilano/android/adb/androidkeymap.py
|
|
|
25
26
|
src/com/dtmilano/android/adb/dumpsys.py
|
|
26
27
|
src/com/dtmilano/android/kato/__init__.py
|
|
27
28
|
src/com/dtmilano/android/kato/kato.py
|
|
29
|
+
src/com/dtmilano/android/mcp/__init__.py
|
|
30
|
+
src/com/dtmilano/android/mcp/object_store.py
|
|
31
|
+
src/com/dtmilano/android/mcp/server.py
|
|
32
|
+
src/com/dtmilano/android/mcp/tools.py
|
|
28
33
|
src/com/dtmilano/android/robotframework/__init__.py
|
|
29
34
|
src/com/dtmilano/android/robotframework/viewclientwrapper.py
|
|
30
35
|
src/com/dtmilano/android/uiautomator/__init__.py
|
|
@@ -42,4 +47,5 @@ src/share/pixmaps/plugins/android/lib/device-art-resources/samsung_s4/port_back.
|
|
|
42
47
|
src/share/pixmaps/plugins/android/lib/device-art-resources/samsung_s4/port_fore.png
|
|
43
48
|
src/share/pixmaps/plugins/android/lib/device-art-resources/samsung_s4/port_shadow.png
|
|
44
49
|
tools/culebra
|
|
50
|
+
tools/culebra-mcp
|
|
45
51
|
tools/dump
|
|
@@ -38,7 +38,7 @@ from com.dtmilano.android.keyevent import KEY_EVENT
|
|
|
38
38
|
from com.dtmilano.android.uiautomator.uiautomatorhelper import UiAutomatorHelper
|
|
39
39
|
from com.dtmilano.android.viewclient import ViewClient, View, VERSION_SDK_PROPERTY
|
|
40
40
|
|
|
41
|
-
__version__ = '
|
|
41
|
+
__version__ = '25.0.1'
|
|
42
42
|
|
|
43
43
|
import sys
|
|
44
44
|
import threading
|
|
@@ -240,13 +240,8 @@ On Ubuntu install
|
|
|
240
240
|
|
|
241
241
|
$ sudo apt-get install python-imaging python-imaging-tk
|
|
242
242
|
|
|
243
|
-
On
|
|
243
|
+
On macOS install
|
|
244
244
|
|
|
245
|
-
$ brew install homebrew/python/pillow
|
|
246
|
-
|
|
247
|
-
or, preferred since El Capitan
|
|
248
|
-
|
|
249
|
-
$ sudo easy_install pip
|
|
250
245
|
$ sudo pip install pillow
|
|
251
246
|
|
|
252
247
|
''')
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
AndroidViewClient MCP Server Module
|
|
5
|
+
|
|
6
|
+
This module provides a Model Context Protocol (MCP) server that exposes
|
|
7
|
+
CulebraTester2 functionality to AI assistants, enabling AI-powered Android
|
|
8
|
+
test automation.
|
|
9
|
+
|
|
10
|
+
The MCP server acts as a bridge between MCP-compatible AI tools (like Kiro)
|
|
11
|
+
and Android devices running the CulebraTester2 service.
|
|
12
|
+
|
|
13
|
+
Example usage:
|
|
14
|
+
# Start the MCP server
|
|
15
|
+
$ culebra-mcp
|
|
16
|
+
|
|
17
|
+
# Or with custom URL
|
|
18
|
+
$ culebra-mcp --url http://192.168.1.100:9987
|
|
19
|
+
|
|
20
|
+
For more information, see:
|
|
21
|
+
https://github.com/dtmilano/AndroidViewClient/
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
__version__ = '25.0.1'
|
|
25
|
+
__author__ = 'Diego Torres Milano'
|
|
26
|
+
__email__ = 'dtmilano@gmail.com'
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
'server',
|
|
30
|
+
'client',
|
|
31
|
+
'object_store',
|
|
32
|
+
'tools',
|
|
33
|
+
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Object Store for MCP Server
|
|
5
|
+
|
|
6
|
+
This module provides an in-memory storage mechanism for UI element references
|
|
7
|
+
returned by CulebraTester2 find operations. The object store allows MCP tools
|
|
8
|
+
to cache and reuse object identifiers across multiple operations.
|
|
9
|
+
|
|
10
|
+
Copyright (C) 2012-2024 Diego Torres Milano
|
|
11
|
+
Created on 2024-12-20 by Culebra
|
|
12
|
+
|
|
13
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
14
|
+
you may not use this file except in compliance with the License.
|
|
15
|
+
You may obtain a copy of the License at
|
|
16
|
+
|
|
17
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
18
|
+
|
|
19
|
+
Unless required by applicable law or agreed to in writing, software
|
|
20
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
21
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
22
|
+
See the License for the specific language governing permissions and
|
|
23
|
+
limitations under the License.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from typing import Dict, Any, Optional
|
|
27
|
+
|
|
28
|
+
__version__ = '25.0.1'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ObjectStore:
|
|
32
|
+
"""
|
|
33
|
+
In-memory storage for UI element references.
|
|
34
|
+
|
|
35
|
+
The ObjectStore maintains a mapping between object IDs (returned by
|
|
36
|
+
CulebraTester2) and metadata about those objects (such as the selector
|
|
37
|
+
used to find them). This allows MCP tools to validate object IDs before
|
|
38
|
+
performing operations and to retrieve information about cached objects.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
>>> store = ObjectStore()
|
|
42
|
+
>>> store.store(123, {"selector": {"text": "Login"}, "type": "text"})
|
|
43
|
+
>>> store.exists(123)
|
|
44
|
+
True
|
|
45
|
+
>>> store.get(123)
|
|
46
|
+
{'selector': {'text': 'Login'}, 'type': 'text'}
|
|
47
|
+
>>> store.remove(123)
|
|
48
|
+
>>> store.exists(123)
|
|
49
|
+
False
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self):
|
|
53
|
+
"""Initialize an empty object store."""
|
|
54
|
+
self._objects: Dict[int, Dict[str, Any]] = {}
|
|
55
|
+
|
|
56
|
+
def store(self, oid: int, metadata: Dict[str, Any]) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Store an object reference with associated metadata.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
oid: The object ID returned by CulebraTester2
|
|
62
|
+
metadata: Dictionary containing information about the object,
|
|
63
|
+
typically including the selector used to find it
|
|
64
|
+
|
|
65
|
+
Example:
|
|
66
|
+
>>> store.store(456, {
|
|
67
|
+
... "selector": {"resourceId": "com.example:id/button"},
|
|
68
|
+
... "type": "resourceId"
|
|
69
|
+
... })
|
|
70
|
+
"""
|
|
71
|
+
self._objects[oid] = metadata
|
|
72
|
+
|
|
73
|
+
def get(self, oid: int) -> Optional[Dict[str, Any]]:
|
|
74
|
+
"""
|
|
75
|
+
Retrieve metadata for a stored object.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
oid: The object ID to look up
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Dictionary containing the object's metadata, or None if the
|
|
82
|
+
object ID is not found in the store
|
|
83
|
+
|
|
84
|
+
Example:
|
|
85
|
+
>>> metadata = store.get(456)
|
|
86
|
+
>>> if metadata:
|
|
87
|
+
... print(metadata['selector'])
|
|
88
|
+
{'resourceId': 'com.example:id/button'}
|
|
89
|
+
"""
|
|
90
|
+
return self._objects.get(oid)
|
|
91
|
+
|
|
92
|
+
def exists(self, oid: int) -> bool:
|
|
93
|
+
"""
|
|
94
|
+
Check if an object ID exists in the store.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
oid: The object ID to check
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
True if the object ID exists, False otherwise
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
>>> if store.exists(456):
|
|
104
|
+
... print("Object found")
|
|
105
|
+
Object found
|
|
106
|
+
"""
|
|
107
|
+
return oid in self._objects
|
|
108
|
+
|
|
109
|
+
def remove(self, oid: int) -> None:
|
|
110
|
+
"""
|
|
111
|
+
Remove an object from the store.
|
|
112
|
+
|
|
113
|
+
This method is idempotent - removing a non-existent object ID
|
|
114
|
+
does not raise an error.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
oid: The object ID to remove
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
>>> store.remove(456)
|
|
121
|
+
>>> store.exists(456)
|
|
122
|
+
False
|
|
123
|
+
"""
|
|
124
|
+
if oid in self._objects:
|
|
125
|
+
del self._objects[oid]
|
|
126
|
+
|
|
127
|
+
def clear(self) -> None:
|
|
128
|
+
"""
|
|
129
|
+
Clear all objects from the store.
|
|
130
|
+
|
|
131
|
+
This removes all cached object references, effectively resetting
|
|
132
|
+
the store to its initial empty state.
|
|
133
|
+
|
|
134
|
+
Example:
|
|
135
|
+
>>> store.clear()
|
|
136
|
+
>>> len(store._objects)
|
|
137
|
+
0
|
|
138
|
+
"""
|
|
139
|
+
self._objects.clear()
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
MCP Server Core for CulebraTester2
|
|
5
|
+
|
|
6
|
+
This module implements the main MCP server that exposes CulebraTester2
|
|
7
|
+
functionality to AI assistants via the Model Context Protocol.
|
|
8
|
+
|
|
9
|
+
Copyright (C) 2012-2024 Diego Torres Milano
|
|
10
|
+
Created on 2024-12-20 by Culebra
|
|
11
|
+
|
|
12
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
+
you may not use this file except in compliance with the License.
|
|
14
|
+
You may obtain a copy of the License at
|
|
15
|
+
|
|
16
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
+
|
|
18
|
+
Unless required by applicable law or agreed to in writing, software
|
|
19
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
+
See the License for the specific language governing permissions and
|
|
22
|
+
limitations under the License.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
import logging
|
|
28
|
+
from mcp.server import FastMCP
|
|
29
|
+
|
|
30
|
+
from culebratester_client import ApiClient, Configuration
|
|
31
|
+
from culebratester_client.api import DefaultApi
|
|
32
|
+
from com.dtmilano.android.mcp.object_store import ObjectStore
|
|
33
|
+
|
|
34
|
+
__version__ = '25.0.1'
|
|
35
|
+
|
|
36
|
+
# Configure logging
|
|
37
|
+
DEBUG = os.environ.get('CULEBRATESTER2_DEBUG', '').lower() in ('1', 'true', 'yes')
|
|
38
|
+
logging.basicConfig(
|
|
39
|
+
level=logging.DEBUG if DEBUG else logging.INFO,
|
|
40
|
+
format='[%(asctime)s] %(levelname)s [%(name)s] %(message)s',
|
|
41
|
+
stream=sys.stderr
|
|
42
|
+
)
|
|
43
|
+
logger = logging.getLogger('culebratester2-mcp')
|
|
44
|
+
|
|
45
|
+
# Get configuration from environment variables
|
|
46
|
+
BASE_URL = os.environ.get('CULEBRATESTER2_URL', 'http://localhost:9987')
|
|
47
|
+
TIMEOUT = int(os.environ.get('CULEBRATESTER2_TIMEOUT', '30'))
|
|
48
|
+
|
|
49
|
+
logger.info("Starting CulebraTester2 MCP Server")
|
|
50
|
+
logger.info(" Base URL: {}".format(BASE_URL))
|
|
51
|
+
logger.info(" Timeout: {}s".format(TIMEOUT))
|
|
52
|
+
logger.info(" Debug mode: {}".format(DEBUG))
|
|
53
|
+
|
|
54
|
+
# Create FastMCP server instance
|
|
55
|
+
mcp = FastMCP(
|
|
56
|
+
name="culebratester2-mcp-server",
|
|
57
|
+
instructions="MCP server for Android UI automation using CulebraTester2. "
|
|
58
|
+
"Provides tools to interact with Android devices through the CulebraTester2 backend."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Initialize official CulebraTester2 client
|
|
62
|
+
# Note: The official client's OpenAPI spec doesn't include /v2 prefix in paths,
|
|
63
|
+
# so we add it to the host URL
|
|
64
|
+
configuration = Configuration()
|
|
65
|
+
configuration.host = "{}/v2".format(BASE_URL)
|
|
66
|
+
api_client = ApiClient(configuration)
|
|
67
|
+
api_client.rest_client.pool_manager.connection_pool_kw['timeout'] = TIMEOUT
|
|
68
|
+
client = DefaultApi(api_client)
|
|
69
|
+
|
|
70
|
+
# Initialize object store (shared across all tool calls)
|
|
71
|
+
objectStore = ObjectStore()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def validateConnection():
|
|
75
|
+
"""Validate connection to CulebraTester2 server."""
|
|
76
|
+
try:
|
|
77
|
+
logger.debug("Validating connection to CulebraTester2...")
|
|
78
|
+
# Use the info endpoint to check service health and version
|
|
79
|
+
info = client.culebra_info_get()
|
|
80
|
+
logger.info("Connected to CulebraTester2 at {}".format(BASE_URL))
|
|
81
|
+
logger.info(" Version: {} (code: {})".format(
|
|
82
|
+
info.version_name if hasattr(info, 'version_name') else 'unknown',
|
|
83
|
+
info.version_code if hasattr(info, 'version_code') else 'unknown'
|
|
84
|
+
))
|
|
85
|
+
print("Connected to CulebraTester2 at {}".format(BASE_URL), file=sys.stderr)
|
|
86
|
+
print(" Version: {} (code: {})".format(
|
|
87
|
+
info.version_name if hasattr(info, 'version_name') else 'unknown',
|
|
88
|
+
info.version_code if hasattr(info, 'version_code') else 'unknown'
|
|
89
|
+
), file=sys.stderr)
|
|
90
|
+
return True
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.error("Could not connect to CulebraTester2 at {}: {}".format(BASE_URL, e))
|
|
93
|
+
print("Warning: Could not connect to CulebraTester2 at {}: {}".format(BASE_URL, e), file=sys.stderr)
|
|
94
|
+
print("Server will start but tools may fail until connection is established.", file=sys.stderr)
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# Import tools after mcp instance is created (tools will register themselves)
|
|
99
|
+
from com.dtmilano.android.mcp import tools # noqa: F401, E402
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def main():
|
|
103
|
+
"""Main entry point for the MCP server."""
|
|
104
|
+
logger.info("Initializing MCP server...")
|
|
105
|
+
validateConnection()
|
|
106
|
+
logger.info("MCP server ready, starting event loop...")
|
|
107
|
+
mcp.run()
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
111
|
+
main()
|