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

  1. 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
    
  2. 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.json in 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.json or ~/.kiro/settings/mcp.json.

    See the MCP Configuration Guide for per-assistant details, tool permissions and troubleshooting.

  3. 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, true or yes.

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:

  1. CulebraTester2Client: HTTP client wrapper for the CulebraTester2 API

  2. ObjectStore: In-memory storage for UI element references

  3. 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:

  1. Verify CulebraTester2 is running on the device

  2. Check port forwarding: adb forward tcp:9987 tcp:9987

  3. Verify the device is connected: adb devices

Element Not Found

If elements cannot be found:

  1. Use dumpUiHierarchy() to inspect the current UI

  2. Verify the text or resource ID is correct

  3. Wait for the UI to load before searching

Timeout Errors

If requests timeout:

  1. Increase CULEBRATESTER2_TIMEOUT environment variable

  2. Check network connectivity to the device

  3. 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