MCP Server for AI-Powered Testing¶
The Model Context Protocol (MCP) server enables AI assistants to interact with Android devices through natural language commands.
Overview¶
The CulebraTester2 MCP server exposes 20 tools that allow AI assistants like Claude Code and Kiro to:
Find and interact with UI elements
Perform coordinate-based gestures
Control device state (wake, sleep, etc.)
Launch applications and navigate
Capture screenshots and UI hierarchies
Installation¶
Install AndroidViewClient with MCP support:
pip3 install androidviewclient --upgrade
The MCP server is automatically available as the culebra-mcp command.
Quick Start¶
Start CulebraTester2 on your device:
adb install -r culebratester2.apk adb shell am instrument -w com.dtmilano.android.culebratester2/.CulebraTester2Instrumentation adb forward tcp:9987 tcp:9987
Configure your AI assistant
Claude Code — register the server with one command:
claude mcp add culebratester2 --env CULEBRATESTER2_URL=http://localhost:9987 -- culebra-mcp
Or add it to
.mcp.jsonin your project root, so everyone who clones the project gets it (Claude Code asks each user to approve the server the first time it sees it):{ "mcpServers": { "culebratester2": { "command": "culebra-mcp", "env": { "CULEBRATESTER2_URL": "http://localhost:9987" } } } }
Kiro — add the same entry to
.kiro/settings/mcp.jsonor~/.kiro/settings/mcp.json.See the MCP Configuration Guide for per-assistant details, tool permissions and troubleshooting.
Start testing with natural language:
“Get the device screen size”
“Launch the Calculator app”
“Find the button with text Submit and click it”
“Take a screenshot”
Environment Variables¶
- CULEBRATESTER2_URL¶
Base URL where CulebraTester2 is running.
Default:
http://localhost:9987
- CULEBRATESTER2_TIMEOUT¶
HTTP request timeout in seconds.
Default:
30
- CULEBRATESTER2_DEBUG¶
Enable debug logging to stderr. Accepts
1,trueoryes.Default:
0
Available Tools¶
Element-Based Interactions¶
- getDeviceInfo()¶
Get device display information including screen dimensions and density.
- Returns:
JSON with display width, height, and density
- dumpUiHierarchy()¶
Dump the current UI hierarchy as XML.
- Returns:
JSON with complete UI hierarchy
- findElementByText(text)¶
Find a UI element by its text content.
- Parameters:
text – The text to search for
- Returns:
JSON with element ID and metadata
- findElementByResourceId(resourceId)¶
Find a UI element by its resource ID.
- Parameters:
resourceId – The resource ID (e.g., “com.example:id/button”)
- Returns:
JSON with element ID and metadata
- clickElement(elementId)¶
Click on a previously found UI element.
- Parameters:
elementId – The element ID from findElementByText or findElementByResourceId
- Returns:
JSON with success status
- longClickElement(elementId)¶
Long click on a previously found UI element.
- Parameters:
elementId – The element ID
- Returns:
JSON with success status
- enterText(elementId, text)¶
Enter text into a UI element (e.g., EditText field).
- Parameters:
elementId – The element ID
text – The text to enter
- Returns:
JSON with success status
- clearText(elementId)¶
Clear text from a UI element.
- Parameters:
elementId – The element ID
- Returns:
JSON with success status
- pressBack()¶
Press the Android BACK button.
- Returns:
JSON with success status
- pressHome()¶
Press the Android HOME button.
- Returns:
JSON with success status
- takeScreenshot()¶
Take a screenshot of the current screen.
- Returns:
JSON with base64-encoded screenshot data
- startApp(packageName, activityName)¶
Start an Android application.
- Parameters:
packageName – The package name (e.g., “com.example.app”)
activityName – Optional activity name (e.g., “.MainActivity”)
- Returns:
JSON with success status
Coordinate-Based Interactions¶
- clickAtCoordinates(x, y)¶
Click at specific screen coordinates.
- Parameters:
x – X coordinate (non-negative)
y – Y coordinate (non-negative)
- Returns:
JSON with success status
- longClickAtCoordinates(x, y)¶
Long click at specific screen coordinates.
- Parameters:
x – X coordinate (non-negative)
y – Y coordinate (non-negative)
- Returns:
JSON with success status
- swipeGesture(startX, startY, endX, endY, steps)¶
Perform a swipe gesture.
- Parameters:
startX – Starting X coordinate
startY – Starting Y coordinate
endX – Ending X coordinate
endY – Ending Y coordinate
steps – Number of steps (default: 10)
- Returns:
JSON with success status
Device Actions¶
- wakeDevice()¶
Wake up the device (turn screen on).
- Returns:
JSON with success status
- sleepDevice()¶
Put the device to sleep (turn screen off).
- Returns:
JSON with success status
- pressRecentApps()¶
Press the Recent Apps button.
- Returns:
JSON with success status
- getCurrentPackage()¶
Get the package name of the currently running app.
- Returns:
JSON with package name
- forceStopApp(packageName)¶
Force stop an application.
- Parameters:
packageName – The package name to stop
- Returns:
JSON with success status
Architecture¶
The MCP server consists of three main components:
CulebraTester2Client: HTTP client wrapper for the CulebraTester2 API
ObjectStore: In-memory storage for UI element references
MCP Tools: 20 tool handlers that expose functionality to AI assistants
All tools return JSON responses with a consistent format:
{
"success": true,
"data": { ... }
}
Or on error:
{
"success": false,
"error": "Error message"
}
Examples¶
See examples/mcp_config.json for a complete Claude Code configuration,
examples/mcp_config_kiro.json for the Kiro equivalent, and
examples/test_calculator_mcp.py for usage examples.
Troubleshooting¶
Connection Refused
If you see “Connection refused” errors:
Verify CulebraTester2 is running on the device
Check port forwarding:
adb forward tcp:9987 tcp:9987Verify the device is connected:
adb devices
Element Not Found
If elements cannot be found:
Use
dumpUiHierarchy()to inspect the current UIVerify the text or resource ID is correct
Wait for the UI to load before searching
Timeout Errors
If requests timeout:
Increase
CULEBRATESTER2_TIMEOUTenvironment variableCheck network connectivity to the device
Verify CulebraTester2 is responding
API Reference¶
MCP Server Core for CulebraTester2
This module implements the main MCP server that exposes CulebraTester2 functionality to AI assistants via the Model Context Protocol.
Copyright (C) 2012-2024 Diego Torres Milano Created on 2024-12-20 by Culebra
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- com.dtmilano.android.mcp.server.main()¶
Main entry point for the MCP server.
- com.dtmilano.android.mcp.server.validateConnection()¶
Validate connection to CulebraTester2 server.
Object Store for MCP Server
This module provides an in-memory storage mechanism for UI element references returned by CulebraTester2 find operations. The object store allows MCP tools to cache and reuse object identifiers across multiple operations.
Copyright (C) 2012-2024 Diego Torres Milano Created on 2024-12-20 by Culebra
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- class com.dtmilano.android.mcp.object_store.ObjectStore¶
In-memory storage for UI element references.
The ObjectStore maintains a mapping between object IDs (returned by CulebraTester2) and metadata about those objects (such as the selector used to find them). This allows MCP tools to validate object IDs before performing operations and to retrieve information about cached objects.
- Example:
>>> store = ObjectStore() >>> store.store(123, {"selector": {"text": "Login"}, "type": "text"}) >>> store.exists(123) True >>> store.get(123) {'selector': {'text': 'Login'}, 'type': 'text'} >>> store.remove(123) >>> store.exists(123) False
- clear() None¶
Clear all objects from the store.
This removes all cached object references, effectively resetting the store to its initial empty state.
- Example:
>>> store.clear() >>> len(store._objects) 0
- exists(oid: int) bool¶
Check if an object ID exists in the store.
- Args:
oid: The object ID to check
- Returns:
True if the object ID exists, False otherwise
- Example:
>>> if store.exists(456): ... print("Object found") Object found
- get(oid: int) Dict[str, Any] | None¶
Retrieve metadata for a stored object.
- Args:
oid: The object ID to look up
- Returns:
Dictionary containing the object’s metadata, or None if the object ID is not found in the store
- Example:
>>> metadata = store.get(456) >>> if metadata: ... print(metadata['selector']) {'resourceId': 'com.example:id/button'}
- remove(oid: int) None¶
Remove an object from the store.
This method is idempotent - removing a non-existent object ID does not raise an error.
- Args:
oid: The object ID to remove
- Example:
>>> store.remove(456) >>> store.exists(456) False
- store(oid: int, metadata: Dict[str, Any]) None¶
Store an object reference with associated metadata.
- Args:
oid: The object ID returned by CulebraTester2 metadata: Dictionary containing information about the object,
typically including the selector used to find it
- Example:
>>> store.store(456, { ... "selector": {"resourceId": "com.example:id/button"}, ... "type": "resourceId" ... })
MCP Tool Handlers for CulebraTester2
This module implements all MCP tool handlers that expose CulebraTester2 functionality through the Model Context Protocol.
Copyright (C) 2012-2024 Diego Torres Milano Created on 2024-12-20 by Culebra
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- com.dtmilano.android.mcp.tools.clearText(elementId: str) str¶
Clear text from a UI element (e.g., EditText field).
- Args:
elementId: The ID of the element to clear text from
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.clickAtCoordinates(x: int, y: int) str¶
Click at specific screen coordinates.
- Args:
x: X coordinate (must be non-negative) y: Y coordinate (must be non-negative)
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.clickElement(elementId: str) str¶
Click on a previously found UI element.
- Args:
elementId: The ID of the element to click (from findElementByText or findElementByResourceId)
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.dumpUiHierarchy() str¶
Dump the current UI hierarchy as XML.
- Returns:
JSON string with UI hierarchy XML
- com.dtmilano.android.mcp.tools.enterText(elementId: str, text: str) str¶
Enter text into a UI element (e.g., EditText field).
- Args:
elementId: The ID of the element to enter text into text: The text to enter
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.findElementByResourceId(resourceId: str) str¶
Find a UI element by its resource ID.
- Args:
resourceId: The resource ID to search for (e.g., “com.example:id/button”)
- Returns:
JSON string with element info or error
- com.dtmilano.android.mcp.tools.findElementByText(text: str) str¶
Find a UI element by its text content.
- Args:
text: The text to search for
- Returns:
JSON string with element info or error
- com.dtmilano.android.mcp.tools.forceStopApp(packageName: str) str¶
Force stop an application.
- Args:
packageName: The package name to force stop (e.g., “com.example.app”)
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.getCurrentPackage() str¶
Get the package name of the currently running application.
- Returns:
JSON string with the current package name
- com.dtmilano.android.mcp.tools.getDeviceInfo() str¶
Get device display information including screen dimensions and density.
- Returns:
JSON string with display info (width, height, density, etc.)
- com.dtmilano.android.mcp.tools.longClickAtCoordinates(x: int, y: int) str¶
Long click at specific screen coordinates.
- Args:
x: X coordinate (must be non-negative) y: Y coordinate (must be non-negative)
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.longClickElement(elementId: str) str¶
Long click on a previously found UI element.
- Args:
elementId: The ID of the element to long click (from findElementByText or findElementByResourceId)
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.pressBack() str¶
Press the Android BACK button.
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.pressHome() str¶
Press the Android HOME button.
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.pressRecentApps() str¶
Press the Recent Apps button to show recently used applications.
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.sleepDevice() str¶
Put the device to sleep (turn screen off).
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.startApp(packageName: str, activityName: str = None) str¶
Start an Android application.
- Args:
packageName: The package name of the app (e.g., “com.example.app”) activityName: Optional activity name to start (e.g., “.MainActivity”)
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.swipeGesture(startX: int, startY: int, endX: int, endY: int, steps: int = 10) str¶
Perform a swipe gesture from one coordinate to another.
- Args:
startX: Starting X coordinate (must be non-negative) startY: Starting Y coordinate (must be non-negative) endX: Ending X coordinate (must be non-negative) endY: Ending Y coordinate (must be non-negative) steps: Number of steps for the swipe (default: 10, must be positive)
- Returns:
JSON string with success status
- com.dtmilano.android.mcp.tools.takeScreenshot() str¶
Take a screenshot of the current screen.
- Returns:
JSON string with base64-encoded screenshot data
- com.dtmilano.android.mcp.tools.wakeDevice() str¶
Wake up the device (turn screen on).
- Returns:
JSON string with success status