Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 40 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ This will automatically configure the MCP server for use with Claude Code. Make
| `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`) |
| `SENTENCE_TRANSFORMERS_MODEL` | Optional | Hugging Face model used for semantic search in Appium documentation queries (default: `Xenova/all-MiniLM-L6-v2`) |
| `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). |
Expand Down Expand Up @@ -372,6 +373,42 @@ The following tools return lightweight text-only responses when NO_UI is enabled
- ✅ 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.
Expand Down Expand Up @@ -569,8 +606,8 @@ The default regex pattern allows any URL that starts with `http://` or `https://
| ---------------------------- | ------------------------------------------------------------------------------------------------- |
| `generate_locators` | Generate intelligent locators for all interactive elements on the current screen |
| `appium_generate_tests` | Generate automated test code from natural language scenarios |
| `appium_documentation_query` | Query Appium documentation using RAG for help and guidance |
| `appium_skills` | Return ordered setup or troubleshooting skills from `appium/skills` for local Appium environments |
| `appium_documentation_query` | **Opt-in (gated by `APPIUM_MCP_DOCS_ENABLED`).** Query Appium documentation using RAG for help and guidance |
| `appium_skills` | **Opt-in (gated by `APPIUM_MCP_DOCS_ENABLED`).** Return ordered setup or troubleshooting skills from `appium/skills` for local Appium environments |

## 🤖 Client Support

Expand Down
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,8 @@
"license": "Apache-2.0",
"description": "Intelligent MCP server providing AI assistants with powerful tools and resources for Appium mobile automation",
"dependencies": {
"@appium/mcp-documentation": "^1.0.1",
"@appium/support": "^7.0.2",
"@modelcontextprotocol/sdk": "^1.22.0",
"@xenova/transformers": "^2.17.2",
"@xmldom/xmldom": "^0.9.8",
"appium-adb": "^15.0.0",
"appium-ios-device": "^3.1.0",
Expand All @@ -70,8 +68,8 @@
"applesign": "^5.0.0",
"fastmcp": "^4.0.0",
"ios-mobileprovision-finder": "^1.2.1",
"node-simctl": "^8.0.4",
"lru-cache": "^11.5.0",
"node-simctl": "^8.0.4",
"webdriver": "^9.23.0",
"xpath": "^0.0.34",
"zod": "^4.3.6"
Expand All @@ -81,6 +79,14 @@
"@opentelemetry/exporter-trace-otlp-http": "^0.219.0",
"@opentelemetry/sdk-node": "^0.219.0"
},
"peerDependencies": {
"@appium/mcp-documentation": "^1.0.1"
},
"peerDependenciesMeta": {
Comment thread
navin772 marked this conversation as resolved.
"@appium/mcp-documentation": {
"optional": true
}
},
"devDependencies": {
"@appium/eslint-config-appium-ts": "^3.0.0",
"@appium/tsconfig": "^1.1.2",
Expand Down
75 changes: 75 additions & 0 deletions src/documentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Optional Appium documentation plugin (RAG docs query + skills tools).
*
* The documentation feature lives in a separate package, `@appium/mcp-documentation`,
* which carries a multi-megabyte embeddings cache and pulls in a heavy ML stack
* (`@xenova/transformers`, `@langchain/*`). To keep the default install lean, that
* package is declared as an OPTIONAL peer dependency: it is not installed by
* default, and nothing related to it is downloaded unless the user opts in.
*
* Contract:
* - `APPIUM_MCP_DOCS_ENABLED` unset / not truthy → docs tools are NOT registered.
* This is the default; nothing extra is loaded.
* - `APPIUM_MCP_DOCS_ENABLED` truthy → the plugin is loaded if
* `@appium/mcp-documentation` is installed. If it is not installed, the server
* logs an actionable install hint and starts normally without the docs tools.
*
* Installation is intentionally left to the user's package manager (run at install
* time, where it belongs) rather than shelled out from the running server: that
* dedupes against appium-mcp's existing dependencies, works across npm/pnpm/yarn,
* and never blocks server startup.
*/

import type { AppiumMcpPlugin } from './core.js';
import log from './logger.js';
import { isTruthyEnvValue } from './utils/env.js';

const ENABLED_FLAG = 'APPIUM_MCP_DOCS_ENABLED';
const PACKAGE_NAME = '@appium/mcp-documentation';

/** True when the user has opted into the documentation tools. */
export function isDocumentationEnabled(): boolean {
return isTruthyEnvValue(process.env[ENABLED_FLAG]);
}
Comment thread
navin772 marked this conversation as resolved.

/**
* Load the documentation plugin when the user has opted in.
*
* @returns the plugin instance, or `null` if the optional package is not
* installed or fails to load (in which case the server runs without the
* documentation tools).
*/
export async function loadDocumentationPlugin(): Promise<AppiumMcpPlugin | null> {
try {
// Widen to `string` so TypeScript treats this as a fully dynamic import and
// does not require @appium/mcp-documentation to be resolvable at build time
// (it is an optional, opt-in dependency, not installed by default).
const specifier: string = PACKAGE_NAME;
const mod = (await import(specifier)) as {
AppiumDocumentation: new () => AppiumMcpPlugin;
};
const plugin = new mod.AppiumDocumentation();
log.info(`Documentation tools enabled (${PACKAGE_NAME} loaded).`);
return plugin;
} catch (err) {
if (isModuleNotFound(err)) {
log.warn(
`${ENABLED_FLAG} is set but ${PACKAGE_NAME} is not installed. ` +
'The documentation tools (appium_documentation_query, appium_skills) ' +
'will be unavailable. Install the package to enable them:\n' +
` npm install ${PACKAGE_NAME}`
);
} else {
log.error(`${PACKAGE_NAME} is installed but failed to load:`, err);
}
return null;
}
}

function isModuleNotFound(err: unknown): boolean {
if (typeof err !== 'object' || err === null || !('code' in err)) {
return false;
}
const code = (err as { code?: unknown }).code;
return code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND';
}
17 changes: 13 additions & 4 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import type { AppiumMcpPlugin } from './core.js';
import { createAppiumMcpServer } from './create-server.js';
import {
isDocumentationEnabled,
loadDocumentationPlugin,
} from './documentation.js';

const plugins: AppiumMcpPlugin[] = [];

try {
const { AppiumDocumentation } = await import('@appium/mcp-documentation');
plugins.push(new AppiumDocumentation());
} catch (_err) {}
// Documentation tools (RAG docs query + skills) are opt-in. They live in a
// separate package only installed when the user sets
// APPIUM_MCP_DOCS_ENABLED. See ./documentation.ts for the full contract.
if (isDocumentationEnabled()) {
const documentationPlugin = await loadDocumentationPlugin();
if (documentationPlugin) {
plugins.push(documentationPlugin);
}
}

const server = await createAppiumMcpServer({ plugins });
export default server;
Loading