#
tokens: 2160/50000 10/10 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── .python-version
├── LICENSE
├── pyproject.toml
├── README.md
├── src
│   └── mcpandroidbuild
│       ├── __init__.py
│       ├── __main__.py
│       ├── build.sh
│       ├── instrumentedTest.sh
│       ├── server.py
│       └── test.sh
└── uv.lock
```

# Files

--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------

```
3.13

```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
.DS_Store
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Android Project MCP Server

A Model Context Protocol server that builds Android project that enables seamless workflow working with Android projects in Visual Studio Code using extensions like Cline or Roo Code.

<a href="https://glama.ai/mcp/servers/@ShenghaiWang/androidbuild">
  <img width="380" height="200" src="https://glama.ai/mcp/servers/@ShenghaiWang/androidbuild/badge" alt="Android Project Server MCP server" />
</a>

### Available Tools

- `build` - Build Android project
    - `folder` (string, required): The full path of the current folder that the Android project sits
- `test` - Run unit test
    - `folder` (string, required): The full path of the current folder that the Android project sits
- `instrumentedTest` - Run Instrumented test
  - `folder` (string, required): The full path of the current folder that the Android project sits

## Installation


### Using uv (recommended)

When using [`uv`](https://docs.astral.sh/uv/) no specific installation is needed. We will
use [`uvx`](https://docs.astral.sh/uv/guides/tools/) to directly run *mcpandroidbuild*.

### Using PIP

Alternatively you can install `mcpandroidbuild` via pip:

```
pip install mcpandroidbuild
```

After installation, you can run it as a script using:

```
python -m  mcpandroidbuild
```

## Configuration

### Configure for Claude.app

Add to your Claude settings:

<details>
<summary>Using uvx</summary>

```json
"mcpServers": {
  "mcpandroidbuild": {
    "command": "uvx",
    "args": ["mcpandroidbuild"]
  }
}
```
</details>

<details>
<summary>Using pip installation</summary>

```json
"mcpServers": {
  "mcpandroidbuild": {
    "command": "python",
    "args": ["-m", "mcpandroidbuild"]
  }
}
```
</details>


## License

mcpandroidbuild MCP tool is licensed under the MIT License. This means you are free to use, modify, and distribute the software, subject to the terms and conditions of the MIT License. For more details, please see the LICENSE file in the project repository.
```

--------------------------------------------------------------------------------
/src/mcpandroidbuild/test.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

cd $1
pwd
./gradlew test
```

--------------------------------------------------------------------------------
/src/mcpandroidbuild/build.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

cd $1
pwd
./gradlew build
```

--------------------------------------------------------------------------------
/src/mcpandroidbuild/__main__.py:
--------------------------------------------------------------------------------

```python
from mcpandroidbuild import main

if __name__ == "__main__":
    main()

```

--------------------------------------------------------------------------------
/src/mcpandroidbuild/__init__.py:
--------------------------------------------------------------------------------

```python
from .server import run

def main():
    """Android Project MCP Server - Building Android project"""
    import asyncio
    asyncio.run(run())


if __name__ == "__main__":
    main()
```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
[project]
requires-python = ">=3.13"
name = "mcpandroidbuild"
version = "0.10.0"
description = "A MCP tool that builds Android project and send error back. It also helps to run test and feed the error back to llm."
authors = [{ name = "Tim Wang" }]
maintainers = [{ name = "Tim Wang", email = "[email protected]" }]
keywords = ["http", "mcp", "llm", "automation", "Android"]
license = { text = "MIT" }
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
]
readme = "README.md"
dependencies = [
    "httpx>=0.28.1",
    "mcp[cli]>=1.3.0",
]

[project.scripts]
mcpandroidbuild = "mcpandroidbuild:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.uv]
dev-dependencies = ["pyright>=1.1.389", "ruff>=0.7.3"]
```

--------------------------------------------------------------------------------
/src/mcpandroidbuild/instrumentedTest.sh:
--------------------------------------------------------------------------------

```bash
#!/bin/bash

cd $1
pwd
echo "Checking for connected devices..."
DEVICE=$(adb devices | awk 'NR>1 {print $1}' | head -n 1)

if [ -z "$DEVICE" ]; then
    echo "No device found. Searching for available emulators..."
    
    # List available emulators and select the first one
    EMULATOR_NAME=$($ANDROID_HOME/emulator/emulator -list-avds | head -n 1)

    if [ -z "$EMULATOR_NAME" ]; then
        echo "No emulators found! Please create one using AVD Manager."
        exit 1
    fi

    echo "Starting emulator: $EMULATOR_NAME..."
    
    # Start the selected emulator in the background
    $ANDROID_HOME/emulator/emulator -avd "$EMULATOR_NAME" -no-window -no-audio -no-snapshot-load &

    echo "Waiting for emulator to boot..."
    adb wait-for-device
    
    # Wait until the emulator is fully booted
    while [[ "$(adb shell getprop sys.boot_completed | tr -d '\r')" != "1" ]]; do
        echo "Still booting..."
        sleep 5
    done
    
    DEVICE=$(adb devices | awk 'NR>1 {print $1}' | head -n 1)
fi

if [ -n "$DEVICE" ]; then
    MODEL=$(adb -s "$DEVICE" shell getprop ro.product.model)
    ANDROID_VERSION=$(adb -s "$DEVICE" shell getprop ro.build.version.release)
    echo "Using device: $MODEL (Android $ANDROID_VERSION) - $DEVICE"
else
    echo "Failed to detect or launch a device!"
    exit 1
fi

# Run Android tests
echo "Running Android tests on device: $DEVICE..."
./gradlew connectedAndroidTest

```

--------------------------------------------------------------------------------
/src/mcpandroidbuild/server.py:
--------------------------------------------------------------------------------

```python
from mcp.server.lowlevel import Server
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import (
    ErrorData,
    TextContent,
    Tool,
    Annotations,
    Field,
    Annotated,
    INVALID_PARAMS,
)
from pydantic import BaseModel
import subprocess
import os, json
from mcp.shared.exceptions import McpError


class Folder(BaseModel):
    """Parameters"""
    folder: Annotated[str, Field(description="The full path of the current folder that the Android project sits")]

server = Server("build")

@server.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name = "build",
            description = "Build the Android project in the folder",
            inputSchema = Folder.model_json_schema(),
        ),
        Tool(
            name="test",
            description="Run test for the Android project in the folder",
            inputSchema=Folder.model_json_schema(),
        ),
        Tool(
            name="instrumentedTest",
            description="Run instrumented test for the Android project in the folder",
            inputSchema=Folder.model_json_schema(),
        )
    ]
@server.call_tool()
async def call_tool(name, arguments: dict) -> list[TextContent]:
    try:
        args = Folder(**arguments)
    except ValueError as e:
        raise McpError(ErrorData(code=INVALID_PARAMS, message=str(e)))
    # os.chdir(args.folder)
    script_dir = os.path.dirname(os.path.abspath(__file__))

    command = [""]
    if name == "build":
        command = [os.path.join(script_dir, "build.sh"), args.folder]
    elif name == "test":
        command = [os.path.join(script_dir, "test.sh"), args.folder]
    else:
        command = [os.path.join(script_dir, "instrumentedTest.sh"), args.folder]

    result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
    stdout_lines = result.stdout.decode("utf-8").splitlines()
    stderr_lines = result.stderr.decode("utf-8").splitlines()
    all_lines = stdout_lines + stderr_lines
    
    
    error_lines = [line for line in all_lines if "failure: " in line.lower() or "e: " in line.lower() or " failed" in line.lower()]
    error_message = "\n".join(error_lines)
    if not error_message:
        error_message = "Successful"
    return [
        TextContent(type="text", text=f"{error_message}")
        ]


async def run():
    options = server.create_initialization_options()
    async with stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream,
            write_stream,
            options,
            raise_exceptions=True,
        )

if __name__ == "__main__":
    import asyncio
    asyncio.run(run())

```