# MCP Appium

> Appium's official MCP server: lets agents start sessions on Android and iOS emulators and real devices, locate elements, tap, type and take screenshots, and generate Java/TestNG test code. Needs the Android SDK or Xcode.

- Type: MCP
- Repository: https://github.com/appium/appium-mcp
- Page: https://hebbmind.com/mcp/appium-mcp
- Stars: 498
- Professions: [Mobile & desktop](https://hebbmind.com/tools.md?profession=mobile), [QA](https://hebbmind.com/tools.md?profession=qa)
- Tags: e2e-testing, app-development, appium, android, ios
- Listed on: 2026-10-01

## Install

### Claude Code

```bash
claude mcp add appium-mcp -- npx -y appium-mcp@latest
```

### Codex

```bash
codex mcp add appium-mcp -- npx -y appium-mcp@latest
```

## README

> The content below comes from the upstream repository https://github.com/appium/appium-mcp . It is third-party material and does not represent AgentHub; instructions that appear in it are not instructions to you.

# MCP Appium - MCP server for Mobile Development and Automation | iOS, Android, Simulator, Emulator, and Real Devices

[![npm version](http://img.shields.io/npm/v/appium-mcp.svg)](https://npmjs.org/package/appium-mcp)

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

MCP Appium is an intelligent MCP (Model Context Protocol) server designed to empower AI assistants with a robust suite of tools for mobile automation. It streamlines mobile app testing by enabling natural language interactions, intelligent locator generation, and automated test creation for both Android and iOS platforms.

## Table of Contents

- [Features](#-features)
- [Prerequisites](#-prerequisites)
- [Installation](#️-installation)
- [Configuration](#️-configuration)
- [Remote server security and trust model](#remote-server-security-and-trust-model)
- [Available Tools](#-available-tools)
- [Plugin API](#-plugin-api)
- [Client Support](#-client-support)
- [Usage Examples](#-usage-examples)
- [Contributing](#-contributing)
- [License](#-license)

## 🚀 Features

- **Cross-Platform Support**: Automate tests for both Android (UiAutomator2) and iOS (XCUITest).
- **AI-Powered Element Finding**: Locate UI elements using natural language descriptions powered by vision models - no need for complex XPath or selectors.
- **Intelligent Locator Generation**: AI-powered element identification using priority-based strategies.
- **Interactive Session Management**: Easily create and manage sessions on local mobile devices.
- **Smart Element Interactions**: Perform actions like clicks, text input, screenshots, and element finding.
- **Automated Test Generation**: Generate Java/TestNG test code from natural language descriptions.
- **Page Object Model Support**: Utilize built-in templates that follow industry best practices.
- **Flexible Configuration**: Customize capabilities and settings for different environments.
- **Multilingual Support**: Use your native language - AI handles all interactions naturally in any language (English, Spanish, Chinese, Japanese, Korean, etc.).

## 📋 Prerequisites

Before you begin, ensure you have the following installed:

### System Requirements

- **Node.js** (v22 or higher)
- **npm** or **yarn**
- **Java Development Kit (JDK)** (8 or higher)
- **Android SDK** (for Android testing)
- **Xcode** (for iOS testing on macOS)

MCP Appium supports two driver modes:

- **Embedded local drivers**: when `appium_session_management` creates an `android` or `ios` session without `remoteServerUrl`, MCP Appium uses the bundled `appium-uiautomator2-driver` or `appium-xcuitest-driver` dependency directly. You still need the platform toolchains below, but you do not need to install a global Appium server or run `appium driver install uiautomator2` / `appium driver install xcuitest` for this mode.
- **Remote WebDriver/Appium server**: when `remoteServerUrl` is provided to `action=create` or `action=attach`, MCP Appium uses the `webdriver` client to talk to that existing server. In this mode the remote server is responsible for its installed drivers, plugins, device access, and capability handling. Use this mode for `platform=general`; embedded local creation is available only for Android and iOS.

### Mobile Testing Setup for embedded local drivers

#### Android

1.  Install Android Studio and the Android SDK.
2.  Set the `ANDROID_HOME` environment variable.
3.  Add the Android SDK tools to your system's PATH.
4.  Enable USB debugging on your Android device.
5.  Install the Android platform tools/build tools and keep `adb` available on `PATH`.

#### iOS (macOS only)

1.  Install Xcode from the App Store.
2.  Install the Xcode Command Line Tools: `xcode-select --install`.
3.  Install iOS simulators through Xcode.
4.  For real device testing, enable Developer Mode on the device and sign in to your Apple ID in Xcode (Settings → Accounts). Use `appium_prepare_ios_real_device` to download and sign WebDriverAgent in a single call - it will guide you through provisioning profile selection and return capabilities for session startup.

## 🛠️ Installation

Standard config works in most of the tools::

```json
{
  "mcpServers": {
    "appium-mcp": {
      "disabled": false,
      "timeout": 100,
      "type": "stdio",
      "command": "npx",
      "args": ["appium-mcp@latest"],
      "env": {
        "ANDROID_HOME": "/path/to/android/sdk",
        "CAPABILITIES_CONFIG": "/path/to/your/capabilities.json"
      }
    }
  }
}
```

### In Cursor IDE

The easiest way to install MCP Appium in Cursor IDE is using the one-click install button:

[![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=appium-mcp&config=eyJkaXNhYmxlZCI6ZmFsc2UsInRpbWVvdXQiOjEwMCwidHlwZSI6InN0ZGlvIiwiZW52Ijp7IkFORFJPSURfSE9NRSI6Ii9Vc2Vycy94eXovTGlicmFyeS9BbmRyb2lkL3NkayJ9LCJjb21tYW5kIjoibnB4IGFwcGl1bS1tY3BAbGF0ZXN0In0%3D)

This will automatically configure the MCP server in your Cursor IDE settings. Make sure to update the `ANDROID_HOME` environment variable in the configuration to match your Android SDK path.

#### Or install manually:

Go to **Cursor Settings → MCP → Add new MCP Server**. Name it to your liking, use command type with the command `npx -y appium-mcp@latest`. You can also verify config or add command arguments via clicking **Edit**.

Here is the recommended configuration:

```json
{
  "appium-mcp": {
    "disabled": false,
    "timeout": 100,
    "type": "stdio",
    "command": "npx",
    "args": ["appium-mcp@latest"],
    "env": {
      "ANDROID_HOME": "/Users/xyz/Library/Android/sdk"
    }
  }
}
```

**Note:** Make sure to update the `ANDROID_HOME` path to match your Android SDK installation path.

### With Gemini CLI

Use the Gemini CLI to add the MCP Appium server:

```bash
gemini mcp add appium-mcp npx -y appium-mcp@latest
```

This will automatically configure the MCP server for use with Gemini. Make sure to update the `ANDROID_HOME` environment variable in the configuration to match your Android SDK path.

### With Claude Code CLI

Use the Claude Code CLI to add the MCP Appium server:

```bash
claude mcp add appium-mcp -- npx -y appium-mcp@latest
```

This will automatically configure the MCP server for use with Claude Code. Make sure to update the `ANDROID_HOME` environment variable in the configuration to match your Android SDK path.

## ⚙️ Configuration

### Environment Variables

> **Note:** For embedded local Android/iOS sessions, MCP Appium already includes the UiAutomator2 and XCUITest driver packages. The system-level requirements are the platform toolchains (`ANDROID_HOME`, Java, Android SDK tools, Xcode/iOS signing or simulator setup). For remote sessions, configure those requirements on the remote Appium/WebDriver server instead.

| Variable                                  | Required                               | Description                                                                                                                                                                                                                                                                                                                                        |
| ----------------------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CAPABILITIES_CONFIG`                     | Optional                               | Absolute path to a `capabilities.json` file with per-platform capability presets                                                                                                                                                                                                                                                                   |
| `SCREENSHOTS_DIR`                         | Optional                               | Directory where screenshots and screen recordings are saved. Defaults to the system temp directory (`os.tmpdir()`)                                                                                                                                                                                                                                             |
| `PAGE_SOURCES_DIR` | Optional | Directory for fresh XML snapshots when `appium_get_page_source` uses `saveToFile: true`. Defaults to the OS temporary directory; relative paths resolve from the server working directory. Has no effect when file saving is disabled. |
| `NO_UI`                                   | Optional                               | Set to `true` or `1` to disable HTML UI components — faster responses, fewer tokens. See [NO_UI Mode](#no_ui-mode)                                                                                                                                                                                                                                 |
| `APPIUM_MCP_APPS_ENABLED`                 | Optional                               | MCP Apps static UI mode. Enabled by default. Set to `false` or `0` to force the embedded UI compatibility fallback. See [MCP Apps Mode](#mcp-apps-mode)                                                                                                                                                                                            |
| `APPIUM_MCP_ON_CLIENT_DISCONNECT`         | Optional                               | Session cleanup when the MCP client disconnects: `delete_all` (default) deletes **MCP-owned** Appium sessions (`safeDeleteAllSessions`); `skip` keeps those sessions across disconnects (e.g. HTTP/stream clients that reconnect). Attached/remote sessions are not removed by this path. See [MCP disconnect behavior](#mcp-disconnect-behavior). |
| `APPIUM_MCP_WDA_APP_PATH`                 | Optional                               | Absolute path to a pre-extracted `WebDriverAgentRunner-Runner.app` bundle. When set, `prepare_ios_simulator` skips all GitHub downloads and uses this bundle directly — useful in environments where external downloads are blocked                                                                                                                |
| REMOTE_SERVER_URL_ALLOW_REGEX | Optional | Regular expression applied to the complete remoteServerUrl value before MCP Appium connects to a remote Appium/WebDriver server. When unset, any valid HTTP(S) server URL is accepted. This checks the supplied URL only, not destinations selected by the WebDriver client from server responses. See [Remote server security and trust model](#remote-server-security-and-trust-model). |
| REMOTE_SERVER_ENABLE_DIRECT_CONNECT | Optional | `true` (default) or `false`. Controls the WebDriver client's `enableDirectConnect` option for remote session creation, attachment, and persisted-session reconnection. Set `false` to disable switching to Appium-advertised direct-connect endpoints. Does not disable HTTP redirects or WebDriver BiDi connections. |
| ALLOW_REMOTE_APP_URLS | Optional | `true` (default) or `false`. Set `false` to reject HTTP(S) app URLs for embedded-driver installation and session creation (`appium:app` and `appium:otherApps`, including legacy unprefixed and `appium:options` forms). Local app paths remain supported. Remote Appium sessions are unaffected: their server resolves app inputs. |
| `AI_VISION_ENABLED`                       | Optional                               | Set to `true` to register the `appium_ai` tool (vision-based element finding). When unset or `false`, the AI tool is **not registered** and the LLM has no way to invoke vision-based finding. Requires `AI_VISION_API_BASE_URL` and `AI_VISION_API_KEY` to also be set, otherwise the server fails to start.                                      |
| `AI_VISION_API_BASE_URL`                  | Required when `AI_VISION_ENABLED=true` | Base URL of the OpenAI-compatible vision model API                                                                                                                                                                                                                                                                                                 |
| `AI_VISION_API_KEY`                       | Required when `AI_VISION_ENABLED=true` | API key for the vision model provider                                                                                                                                                                                                                                                                                                              |
| `AI_VISION_MODEL`                         | Optional                               | Vision model name (default: `Qwen3-VL-235B-A22B-Instruct`)                                                                                                                                                                                                                                                                                         |
| `AI_VISION_COORD_TYPE`                    | Optional                               | Coordinate type: `normalized` (default) or `absolute`                                                                                                                                                                                                                                                                                              |
| `AI_VISION_IMAGE_MAX_WIDTH`               | Optional                               | Max image width in pixels before compression (default: `1080`)                                                                                                                                                                                                                                                                                     |
| `AI_VISION_IMAGE_QUALITY`                 | Optional                               | JPEG quality 1–100 for compressed screenshots sent to the vision API (default: `80`)                                                                                                                                                                                                                                                               |
| `APPIUM_MCP_DOCS_ENABLED`                 | Optional                               | Set to `true` (or `1`/`yes`/`on`) to register the documentation tools (`appium_documentation_query`, `appium_skills`). **Opt-in and disabled by default.** Requires the optional `@appium/mcp-documentation` package (embeddings cache + ML stack) to be installed separately; when unset it is **never downloaded**. See [Documentation Tools (opt-in)](#documentation-tools-opt-in).                                                                                                                                                                                                                                                |
| `SENTENCE_TRANSFORMERS_MODEL`             | Optional                               | Hugging Face model used for semantic search in Appium documentation queries (default: `Xenova/all-MiniLM-L6-v2`). Only applies when `APPIUM_MCP_DOCS_ENABLED` is set.                                                                                                                                                                                                                                   |
| `APPIUM_MCP_PERSIST_REMOTE_SESSIONS_PATH` | Optional | Directory path for persisted attached remote session info. When set, attached remote sessions are stored as JSON files in that directory and can be rehydrated after restart. |
| `APPIUM_MCP_EVIDENCE`                     | Optional                               | Set to `true` or `1` to attach a structured **action evidence record** (locator, resolved element id, context, timing, normalized error code) to `appium_find_element` and `appium_gesture` responses as an `application/vnd.appium.evidence+json` resource block, for CI/debugging. Disabled by default; responses are unchanged when unset.        |
| `APPIUM_MCP_OTEL_ENABLED` | Optional | Set to `true` to enable OpenTelemetry tracing (disabled by default). |
| `APPIUM_MCP_OTEL_INCLUDE_ARGUMENT_VALUES` | Optional | Set to `true` to include sanitized non-sensitive argument values in spans; disabled by default because values may contain sensitive data. |
| `OTEL_SERVICE_NAME` | Optional | Service name reported to the OpenTelemetry collector (example: `appium-mcp`). |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | Optional | OTLP/HTTP traces endpoint (example: `http://127.0.0.1:4318/v1/traces`). |
| `OTEL_TRACES_SAMPLER` | Optional | Trace sampling strategy; `parentbased_always_on` samples new root traces and follows parent decisions. |
| `OTEL_RESOURCE_ATTRIBUTES` | Optional | Comma-separated `key=value` pairs attached as resource attributes to every span (example: `testcase.id=my-test-123,team=platform`). |

`REMOTE_SERVER_ENABLE_DIRECT_CONNECT` and `ALLOW_REMOTE_APP_URLS` preserve existing behavior
when unset. Values are case-insensitive and may contain surrounding whitespace; other values
(including an empty string) fail server startup. They are operator-controlled environment
settings, not MCP tool arguments.

URL-based app installation is an intentional Appium feature. For embedded sessions it downloads
on the MCP host; for remote sessions it downloads on the remote Appium server. Disabling
`ALLOW_REMOTE_APP_URLS` limits these app inputs, not all host network access or all driver
capabilities that may use the network. When URL installation is enabled, redirect handling,
download size limits, and cache management remain the responsibility of the Appium downloader
and deployment controls. The app lifecycle tool uses `openWorldHint: true` because its operations
can interact with external services.

### OpenTelemetry tracing

OpenTelemetry tracing is disabled by default. Set `APPIUM_MCP_OTEL_ENABLED=true` to initialize the Node.js OpenTelemetry SDK before the MCP server is constructed. The SDK uses standard `OTEL_*` environment variables, for example:

```bash
APPIUM_MCP_OTEL_ENABLED=true
# Optional: include sanitized non-sensitive argument values in spans.
# APPIUM_MCP_OTEL_INCLUDE_ARGUMENT_VALUES=true
# Optional: attach custom key=value pairs to every span (e.g. test case ID, team name).
# OTEL_RESOURCE_ATTRIBUTES=testcase.id=my-test-123,team=platform
OTEL_SERVICE_NAME=appium-mcp
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://127.0.0.1:4318/v1/traces
OTEL_TRACES_SAMPLER=parentbased_always_on
```

(Please check the [official document](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) as well)

When enabled, appium-mcp creates spans for MCP tool calls, prompt loads, resource reads, and resource template reads. Error status is recorded for thrown operation errors and MCP tool results marked with `isError`. Span attributes intentionally avoid raw screenshots, XML page source, prompts, credentials, and other high-cardinality or sensitive payloads.

Tool spans include payload-free result-size attributes: `mcp.tool.result.content_count`, `content_types`, `text_chars`, `resource_count`, `resource_text_chars`, `image_count`, `audio_count`, `base64_chars`, and `base64_bytes_estimate` (all prefixed with `mcp.tool.result.`). `content_types` contains only known MCP types or `other`; payload values, resource URIs, MIME types, and unknown type strings are never recorded. Sizes are counted directly from known result fields without serializing or copying the complete result.

For local trace inspection, use the Jaeger setup in `tools/telemetry`:

```bash
npm run telemetry:jaeger:start
```

Then open `http://127.0.0.1:16686` and run appium-mcp with the environment values in `tools/telemetry/jaeger.env`.

### Capabilities

Create a `capabilities.json` file to define your device capabilities:

```json
{
  "android": {
    "appium:app": "/path/to/your/android/app.apk",
    "appium:deviceName": "Android Device",
    "appium:platformVersion": "11.0",
    "appium:automationName": "UiAutomator2",
    "appium:udid": "your-device-udid"
  },
  "ios": {
    "appium:app": "/path/to/your/ios/app.ipa",
    "appium:deviceName": "iPhone 15 Pro",
    "appium:platformVersion": "17.0",
    "appium:automationName": "XCUITest",
    "appium:udid": "your-device-udid"
  },
  "general": {
    "platformName": "mac",
    "appium:automationName": "mac2",
    "appium:bundleId": "com.apple.Safari"
  }
}
```

Set the `CAPABILITIES_CONFIG` environment variable to point to your configuration file.

#### Platform names and "general" mode

- You can pass any platform name to `appium_session_management` (action=create).
- If the platform is `ios` or `android`, the server builds capabilities for that platform (including selected device info when local).
- If the platform is any other value, it is treated internally as `general`:
  - The session will use the provided `capabilities` exactly as given, or
  - If `CAPABILITIES_CONFIG` is set, it will merge with the `general` section from your capabilities file.
- This allows custom setups and non-standard platforms to work without changing server logic.

### Integrator notes (sessions, transport, logging)

For **CI**, **device farms**, or **multi-session** setups:

#### Multi-session and `sessionId`

The process keeps one **active** Appium session; tools use it when **`sessionId` is omitted**. If a tool call does not include a `sessionId`, it will target the active session instead of a specific one. If more than one session exists (see **`appium_session_management`** with **`action=list`**), pass **`sessionId` on every tool call** that must target a specific session. Do not assume the active session is stable if other clients or flows can create, select, or delete sessions.

#### Session persistence

If **`APPIUM_MCP_PERSIST_REMOTE_SESSIONS_PATH`** is set, MCP Appium persists **attached remote sessions** to that directory as JSON files. The path may be absolute or relative to the current working directory. Each session is stored under a canonical filename derived from a hash of the `sessionId`; older legacy filenames are migrated, and duplicate files for the same session are removed when the directory is read. When a persisted attached session is used again, the server tries to reattach to the remote Appium session; unreachable entries are pruned automatically.

#### Client disconnect

When the MCP **client disconnects**, the server **deletes only MCP-owned sessions** it is tracking (Appium **`deleteSession`** for each, via `safeDeleteAllSessions`). **Attached** sessions (`ownership=attached`) are intentionally left on the remote Appium server. Transports that drop often—**`httpStream`** behind proxies, idle timeouts, or flaky clients—can **wipe owned automation** in one go under the default policy. **stdio** is usually safer for a single long-lived operator; if you use **httpStream**, expect reconnects to require **new owned sessions** where applicable.

#### Remote Appium, CI, and device farms

For **grids, cloud labs, or CI**, prefer **`remoteServerUrl`** plus explicit **`capabilities`** on **`appium_session_management`** (**`action=create`**)—for example **`appium:udid`**, app path or id, platform version—rather than depending on local discovery. **`select_device`** is geared toward **local** ADB / simulator picking; use it as a **dev convenience**, not the main path for allocated remote devices.

#### Tool logging and argument size

Tool calls are logged with argument **redaction** implemented via **`JSON.stringify`**. Oversized payloads (especially long **base64 strings**, e.g., screenshot/image payloads, and also very large **`capabilities`** objects) cost **CPU** and **log volume**. Prefer **`CAPABILITIES_CONFIG`** and avoid passing large inline blobs in tool arguments when possible.

### Screenshots

Set the `SCREENSHOTS_DIR` environment variable to specify where screenshots are saved. If not set, screenshots are saved to the system temp directory (`os.tmpdir()`). Supports both absolute and relative paths (relative paths are resolved from the current working directory). The directory is created automatically if it doesn't exist.

### Screen Recording

Screen recordings are saved as MP4 files to the same directory as screenshots (`SCREENSHOTS_DIR`, or `os.tmpdir()` if not set).

- **iOS**: Requires [ffmpeg](https://ffmpeg.org/download.html) to be installed and available on `PATH`. The default codec is `libx264` with `yuv420p` pixel format for QuickTime compatibility.
- **Android**: Uses the built-in `screenrecord` command via UiAutomator2. No additional dependencies required.

To start recording, call `appium_screen_recording` with `action="start"`. You may provide `timeLimit` in seconds to limit the maximum recording duration, but the start call still returns immediately. To finalize the recording, save the video, and receive the file path, call `appium_screen_recording` again with `action="stop"`.

For iOS, `videoScale` accepts `width:height` with dimensions from 1 to 16384 (for example, `1280:720`). One dimension may be `-1` or `-2` to preserve the aspect ratio; `-2` also makes that dimension divisible by two. Scaling expressions and custom `videoFilters` are no longer supported by this MCP tool.

### AI Vision Element Finding

Configure AI-powered element finding using vision models. When enabled, a separate tool — **`appium_ai`** — is registered alongside `appium_find_element`. It exposes `action=find_element`, which locates UI elements from natural-language descriptions and returns a coordinate UUID (`ai-element:x,y:bbox`) that can be passed to `appium_gesture` (`tap` / `double_tap` / `long_press`).

**This feature is opt-in.** When `AI_VISION_ENABLED` is unset or `false`, the `appium_ai` tool is **not registered** and the LLM has no way to invoke vision-based finding — keeping `appium_find_element` purely traditional. This deliberate gating prevents the model from defaulting to a slow, paid vision call when a stable locator (accessibility id, resource-id, etc.) would do the job.

**Required Environment Variables:**

```json
{
  "appium-mcp": {
    "env": {
      "ANDROID_HOME": "/path/to/android/sdk",
      "AI_VISION_ENABLED": "true",
      "AI_VISION_API_BASE_URL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
      "AI_VISION_API_KEY": "your_api_key_here"
    }
  }
}
```

If `AI_VISION_ENABLED=true` is set without both API vars, the server fails to start with a clear error message — misconfiguration is surfaced immediately rather than mid-test.

**Optional Environment Variables:**

See the [Environment Variables](#environment-variables) table above for the full list of `AI_VISION_*` options and their defaults.

**Supported Vision Model Providers:**

Based on benchmark testing, the following models are recommended:

1. **Qwen3-VL-235B-A22B-Instruct**
   - Provider: Alibaba Cloud DashScope
   - Accuracy: 100%
   - Speed: 12649ms
   - API: `https://dashscope.aliyuncs.com/compatible-mode/v1`

2. **gemini-3-flash-preview**
   - Provider: Google AI
   - Accuracy: 100%
   - Speed: 17353
   - API: `https://generativelanguage.googleapis.com/v1beta`

More models benchmarked can be found [here](https://github.com/appium/appium-mcp/blob/HEAD/src/tests/benchmark_model/TEST_REPORT.md).

**Performance Features:**

- **Image Compression**: Automatically compresses screenshots to reduce API latency and token costs (50-80% size reduction)
- **Result Caching**: Caches results for 5 minutes using a module-level LRU cache (max 50 entries) that persists across tool calls, avoiding redundant API calls for identical screenshot + instruction pairs
- **Coordinate Handling**: In `normalized` mode (default), the model returns 0–1000 range coordinates that are automatically scaled to absolute pixel coordinates using the original image dimensions — independent of any image compression. In `absolute` mode, image resizing is disabled so the model's returned pixel coordinates always map directly to the original screen dimensions.

### Performance Optimization

#### MCP Apps Mode

`appium_get_page_source`, `appium_screenshot`, and `generate_locators` use static MCP App viewers by default when
the client advertises MCP Apps support.

- Page source XML and generated locator JSON remain in their normal text results for the LLM. Their viewers read
  those existing results instead of receiving duplicated copies inside generated HTML.
- Saved screenshot base64 is delivered to the viewer through `structuredContent`, which MCP Apps keeps out of model
  context. The LLM still receives the saved file path. Explicit `returnRawBase64=true` calls keep their existing raw
  image result.

For clients with unreliable MCP Apps rendering, set `APPIUM_MCP_APPS_ENABLED` to `false` or `0`:

```json
{
  "appium-mcp": {
    "env": {
      "APPIUM_MCP_APPS_ENABLED": "false"
    }
  }
}
```

This keeps interactive UI enabled but forces the previous embedded viewers. The compatibility mode places viewer
data inside inline HTML and therefore uses more result tokens and bandwidth. With a synthetic 95,000-character page
source, the static mode reduced the result from approximately 267 KB to 95 KB (about 64%).

`NO_UI=true` or `NO_UI=1` takes precedence over this setting and disables both static and embedded UI.

#### NO_UI Mode

Set the `NO_UI` environment variable to `true` or `1` to disable UI components and improve performance:

```json
{
  "appium-mcp": {
    "env": {
      "NO_UI": "true",
      "ANDROID_HOME": "/path/to/android/sdk"
    }
  }
}
```

**Benefits:**

- **Significantly Faster Response Times**: UI rendering and data processing are completely skipped, resulting in 50-80% faster tool responses depending on the operation.
- **Major Token Savings**: Eliminates 500-5000+ tokens per request by removing HTML UI components from responses, dramatically reducing LLM API costs.
- **Massive Bandwidth Reduction**:
  - Screenshots: Saves 1-5MB of base64-encoded image data per screenshot
  - Page source: Saves 50-200KB+ of duplicated XML data in HTML UI
  - Locators: Saves 10-100KB+ of element data in interactive UI
  - Device/App lists: Saves 5-50KB of HTML UI per selection
- **Lower Memory Usage**: Client applications consume less memory without HTML rendering and embedded data.
- **Perfect for Headless Environments**: Ideal for CI/CD pipelines, automated testing scripts, batch operations, or any scenario where visual UI feedback is not required.
- **Better Scalability**: Reduced resource consumption allows handling more concurrent sessions.

**Affected Tools:**

The following tools return lightweight text-only responses when NO_UI is enabled:

- `appium_screenshot` - Screenshot files are still saved to disk, but base64 data is not embedded in responses
- `appium_get_page_source` - Returns XML as text without interactive inspector UI by default. With `saveToFile: true`, returns the saved XML path and byte size instead; `NO_UI` does not enable file saving.
- `generate_locators` - Returns locator data as JSON without interactive UI
- `select_device` - Returns device list as text without picker UI
- `appium_session_management` (action=create) - Returns session info as text without dashboard UI
- `appium_context` - Returns context list as text with `action=list` without switcher UI
- `appium_app_lifecycle` (action=`list`) - Returns app list as JSON without interactive UI

**When to Enable NO_UI:**

- ✅ Automated test execution in CI/CD pipelines
- ✅ Batch processing multiple devices/sessions
- ✅ Cost-sensitive LLM API usage (reduces token consumption by 60-90%)
- ✅ Network-constrained environments
- ✅ Scripted automation where human interaction is not needed
- ❌ Interactive debugging and exploration (keep UI enabled for better experience)

#### Documentation Tools (opt-in)

The documentation tools — `appium_documentation_query` (RAG search over the Appium docs) and `appium_skills` — live in a separate package, `@appium/mcp-documentation`, that carries a multi-megabyte embeddings cache and pulls in a heavy ML stack (`@xenova/transformers`, `@langchain/*`). To keep the default install lean, **this package is not a runtime dependency of appium-mcp and is never downloaded unless you opt in.** It is declared as an _optional peer dependency_.

Enabling the tools is a two-step opt-in:

**1. Install the optional package** (in the same project/environment as appium-mcp):

```bash
npm install @appium/mcp-documentation
```

Installing it with your own package manager dedupes against appium-mcp's existing dependencies, so only the genuinely new code is added.

**2. Set `APPIUM_MCP_DOCS_ENABLED`** in your MCP server config:

```json
{
  "appium-mcp": {
    "env": {
      "APPIUM_MCP_DOCS_ENABLED": "true",
      "ANDROID_HOME": "/path/to/android/sdk"
    }
  }
}
```

Behavior:

- **Unset / not truthy (default):** the documentation tools are **not registered**, and nothing related to them (cache, embeddings, ML dependencies) is loaded.
- **Truthy (`true`/`1`/`yes`/`on`):** the server registers the documentation tools if `@appium/mcp-documentation` is installed. If the flag is set but the package is **not** installed, the server starts normally **without** the documentation tools and logs a hint to run `npm install @appium/mcp-documentation`.

The gate is governed by the env var, not by mere presence of the package: with `APPIUM_MCP_DOCS_ENABLED` unset, the tools stay hidden even if the package happens to be installed.

Pre-installing it that way also avoids the first-run download delay.

#### MCP disconnect behavior

By default (`APPIUM_MCP_ON_CLIENT_DISCONNECT` unset or `delete_all`), when the **MCP client disconnects**, this server **deletes every MCP-owned Appium session** (the same sessions `safeDeleteAllSessions` targets) so embedded drivers are not left running after a short-lived assistant run. **Attached** sessions (`ownership=attached`) are unchanged by this teardown.

HTTP and streamable MCP clients may **disconnect briefly** (reconnect, reload, proxy). If that tears down drivers you still need, set `APPIUM_MCP_ON_CLIENT_DISCONNECT` to `skip` in your MCP server `env` (same pattern as `NO_UI` above). With `skip`, sessions **survive** disconnect until you call `appium_session_management` with `action=delete`, or you stop the Appium server / process.

**Tradeoff:** `skip` can leave **orphaned sessions** on your Appium server if nothing cleans up — use it when disconnect is not the same as “automation finished.”

### Remote server security and trust model

MCP Appium is designed to run as a local, single-user MCP server or as part of a trusted CI job. It is not intended to be exposed as a shared service to untrusted MCP clients.

The `remoteServerUrl` argument is intentionally configurable because MCP Appium acts as an Appium/WebDriver client and may need to connect to local, remote, private-network, or CI-hosted Appium servers.

Only allow trusted users and trusted workflow configuration to control `remoteServerUrl`. In particular:

* Do not expose the MCP tool surface directly to untrusted users.
* In CI, do not construct `remoteServerUrl` from untrusted pull request content, repository data, prompts, or other externally controlled input.
* Keep remote server URLs in trusted MCP or CI configuration where possible.
* Use `REMOTE_SERVER_URL_ALLOW_REGEX` to restrict the supplied Appium server URLs. Trust the permitted servers and their responses as described below.

When `REMOTE_SERVER_URL_ALLOW_REGEX` is not set, MCP Appium accepts any syntactically valid HTTP or HTTPS destination. Remote server URLs must not contain a query string or fragment. The variable is a regular-expression check against the complete `remoteServerUrl` value and can only narrow the HTTP(S) destinations accepted by the built-in validation.

Capability discovery for attached sessions does not follow HTTP redirects. This keeps a permitted endpoint from redirecting the initial metadata request to a destination outside the configured URL policy.

`REMOTE_SERVER_URL_ALLOW_REGEX` validates the supplied server URL only. Subsequent
destinations selected by HTTP redirects, Appium direct-connect metadata, or a
WebDriver BiDi `webSocketUrl` are handled by the WebDriver client library and are
not checked against this regex. Intercepting or modifying the client library's
internal transport is outside appium-mcp's scope. Operators must therefore trust
the permitted Appium servers and their responses. Set
`REMOTE_SERVER_ENABLE_DIRECT_CONNECT=false` to disable direct connect through the
client's public option; this does not disable HTTP redirects or BiDi connections.
Deployments that need to restrict all outbound destinations should enforce that
policy through network egress controls.

If a remote URL contains credentials, MCP Appium redacts its userinfo from logs and error responses. Opt-in persisted-session files may still contain credentials and sensitive capabilities because they are needed for reattachment; those files are created with owner-only (`0600`) permissions, and permissions on existing regular session files are repaired when read. Use a dedicated persistence directory owned by the MCP process.

For example, to permit only a specific Appium server:

```bash
REMOTE_SERVER_URL_ALLOW_REGEX='^https://appium\.example\.com:4723(?:/wd/hub)?/?$'
```

To permit Appium servers under a controlled internal domain:

```bash
REMOTE_SERVER_URL_ALLOW_REGEX='^https://[a-z0-9-]+\.appium\.example\.internal(?::[0-9]+)?(?:/.*)?$'
```

Treat this setting as an additional deployment safeguard. Network-level controls, CI isolation, and trusted MCP client configuration should remain the primary security boundaries.


## 🔌 Plugin API

### Load plugins from the command line

Pass `--plugin` to start the standard server with a plugin, without writing a server bootstrap module. Repeat the option to load multiple plugins in order; both `--plugin <module>` and `--plugin=<module>` work with stdio and `--httpStream`:

```bash
appium-mcp --plugin ./checkout-plugin.mjs --plugin=@acme/appium-mcp-plugin
```

Each module must **default-export** either an `AppiumMcpPlugin` object or a plugin class whose constructor takes no arguments. For example, save this as `checkout-plugin.mjs`:

```javascript
export default class CheckoutPlugin {
  name = 'checkout-plugin';
  version = '1.0.0';

  register(registry) {
    registry.addTool({
      name: 'checkout_status',
      description: 'Return the checkout integration status.',
      execute: async () => ({content: [{type: 'text', text: 'Checkout plugin ready'}]}),
    });
  }
}
```

Use an exported object (for example, `export default new CheckoutPlugin(options)`) when a plugin needs constructor options. TypeScript plugins must be compiled to JavaScript before loading.

Relative paths and installed package names resolve from the working directory where `appium-mcp` is launched. Package names use Node's CommonJS resolution (`createRequire(...).resolve()`), including `require` or `default` export conditions and legacy `main` entries. Packages exposing only an `import` export must be loaded through an explicit file path (for example, `--plugin ./node_modules/my-plugin/plugin.mjs`) or a `file:` URL. Loading still uses dynamic `import()`, so ESM plugin files, including those with top-level `await`, remain supported.


> The README is long; the above is its first 40000 characters. Full content: https://github.com/appium/appium-mcp
