appium-mcp 1.85.10 → 1.86.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [1.86.0](https://github.com/appium/appium-mcp/compare/v1.85.10...v1.86.0) (2026-06-19)
2
+
3
+ ### Features
4
+
5
+ * **docs:** make documentation plugin optional with opt-in config ([#413](https://github.com/appium/appium-mcp/issues/413)) ([21d709f](https://github.com/appium/appium-mcp/commit/21d709fb285ece5633484775b2fca354b2a80063))
6
+
1
7
  ## [1.85.10](https://github.com/appium/appium-mcp/compare/v1.85.9...v1.85.10) (2026-06-18)
2
8
 
3
9
  ### Bug Fixes
package/README.md CHANGED
@@ -157,7 +157,8 @@ This will automatically configure the MCP server for use with Claude Code. Make
157
157
  | `AI_VISION_COORD_TYPE` | Optional | Coordinate type: `normalized` (default) or `absolute` |
158
158
  | `AI_VISION_IMAGE_MAX_WIDTH` | Optional | Max image width in pixels before compression (default: `1080`) |
159
159
  | `AI_VISION_IMAGE_QUALITY` | Optional | JPEG quality 1–100 for compressed screenshots sent to the vision API (default: `80`) |
160
- | `SENTENCE_TRANSFORMERS_MODEL` | Optional | Hugging Face model used for semantic search in Appium documentation queries (default: `Xenova/all-MiniLM-L6-v2`) |
160
+ | `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). |
161
+ | `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. |
161
162
  | `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. |
162
163
  | `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. |
163
164
  | `APPIUM_MCP_OTEL_ENABLED` | Optional | Set to `true` to enable OpenTelemetry tracing (disabled by default). |
@@ -372,6 +373,42 @@ The following tools return lightweight text-only responses when NO_UI is enabled
372
373
  - ✅ Scripted automation where human interaction is not needed
373
374
  - ❌ Interactive debugging and exploration (keep UI enabled for better experience)
374
375
 
376
+ #### Documentation Tools (opt-in)
377
+
378
+ 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_.
379
+
380
+ Enabling the tools is a two-step opt-in:
381
+
382
+ **1. Install the optional package** (in the same project/environment as appium-mcp):
383
+
384
+ ```bash
385
+ npm install @appium/mcp-documentation
386
+ ```
387
+
388
+ Installing it with your own package manager dedupes against appium-mcp's existing dependencies, so only the genuinely new code is added.
389
+
390
+ **2. Set `APPIUM_MCP_DOCS_ENABLED`** in your MCP server config:
391
+
392
+ ```json
393
+ {
394
+ "appium-mcp": {
395
+ "env": {
396
+ "APPIUM_MCP_DOCS_ENABLED": "true",
397
+ "ANDROID_HOME": "/path/to/android/sdk"
398
+ }
399
+ }
400
+ }
401
+ ```
402
+
403
+ Behavior:
404
+
405
+ - **Unset / not truthy (default):** the documentation tools are **not registered**, and nothing related to them (cache, embeddings, ML dependencies) is loaded.
406
+ - **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`.
407
+
408
+ 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.
409
+
410
+ Pre-installing it that way also avoids the first-run download delay.
411
+
375
412
  #### MCP disconnect behavior
376
413
 
377
414
  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.
@@ -569,8 +606,8 @@ The default regex pattern allows any URL that starts with `http://` or `https://
569
606
  | ---------------------------- | ------------------------------------------------------------------------------------------------- |
570
607
  | `generate_locators` | Generate intelligent locators for all interactive elements on the current screen |
571
608
  | `appium_generate_tests` | Generate automated test code from natural language scenarios |
572
- | `appium_documentation_query` | Query Appium documentation using RAG for help and guidance |
573
- | `appium_skills` | Return ordered setup or troubleshooting skills from `appium/skills` for local Appium environments |
609
+ | `appium_documentation_query` | **Opt-in (gated by `APPIUM_MCP_DOCS_ENABLED`).** Query Appium documentation using RAG for help and guidance |
610
+ | `appium_skills` | **Opt-in (gated by `APPIUM_MCP_DOCS_ENABLED`).** Return ordered setup or troubleshooting skills from `appium/skills` for local Appium environments |
574
611
 
575
612
  ## 🤖 Client Support
576
613
 
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Optional Appium documentation plugin (RAG docs query + skills tools).
3
+ *
4
+ * The documentation feature lives in a separate package, `@appium/mcp-documentation`,
5
+ * which carries a multi-megabyte embeddings cache and pulls in a heavy ML stack
6
+ * (`@xenova/transformers`, `@langchain/*`). To keep the default install lean, that
7
+ * package is declared as an OPTIONAL peer dependency: it is not installed by
8
+ * default, and nothing related to it is downloaded unless the user opts in.
9
+ *
10
+ * Contract:
11
+ * - `APPIUM_MCP_DOCS_ENABLED` unset / not truthy → docs tools are NOT registered.
12
+ * This is the default; nothing extra is loaded.
13
+ * - `APPIUM_MCP_DOCS_ENABLED` truthy → the plugin is loaded if
14
+ * `@appium/mcp-documentation` is installed. If it is not installed, the server
15
+ * logs an actionable install hint and starts normally without the docs tools.
16
+ *
17
+ * Installation is intentionally left to the user's package manager (run at install
18
+ * time, where it belongs) rather than shelled out from the running server: that
19
+ * dedupes against appium-mcp's existing dependencies, works across npm/pnpm/yarn,
20
+ * and never blocks server startup.
21
+ */
22
+ import type { AppiumMcpPlugin } from './core.js';
23
+ /** True when the user has opted into the documentation tools. */
24
+ export declare function isDocumentationEnabled(): boolean;
25
+ /**
26
+ * Load the documentation plugin when the user has opted in.
27
+ *
28
+ * @returns the plugin instance, or `null` if the optional package is not
29
+ * installed or fails to load (in which case the server runs without the
30
+ * documentation tools).
31
+ */
32
+ export declare function loadDocumentationPlugin(): Promise<AppiumMcpPlugin | null>;
33
+ //# sourceMappingURL=documentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentation.d.ts","sourceRoot":"","sources":["../src/documentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAOjD,iEAAiE;AACjE,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAyB/E"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Optional Appium documentation plugin (RAG docs query + skills tools).
3
+ *
4
+ * The documentation feature lives in a separate package, `@appium/mcp-documentation`,
5
+ * which carries a multi-megabyte embeddings cache and pulls in a heavy ML stack
6
+ * (`@xenova/transformers`, `@langchain/*`). To keep the default install lean, that
7
+ * package is declared as an OPTIONAL peer dependency: it is not installed by
8
+ * default, and nothing related to it is downloaded unless the user opts in.
9
+ *
10
+ * Contract:
11
+ * - `APPIUM_MCP_DOCS_ENABLED` unset / not truthy → docs tools are NOT registered.
12
+ * This is the default; nothing extra is loaded.
13
+ * - `APPIUM_MCP_DOCS_ENABLED` truthy → the plugin is loaded if
14
+ * `@appium/mcp-documentation` is installed. If it is not installed, the server
15
+ * logs an actionable install hint and starts normally without the docs tools.
16
+ *
17
+ * Installation is intentionally left to the user's package manager (run at install
18
+ * time, where it belongs) rather than shelled out from the running server: that
19
+ * dedupes against appium-mcp's existing dependencies, works across npm/pnpm/yarn,
20
+ * and never blocks server startup.
21
+ */
22
+ import log from './logger.js';
23
+ import { isTruthyEnvValue } from './utils/env.js';
24
+ const ENABLED_FLAG = 'APPIUM_MCP_DOCS_ENABLED';
25
+ const PACKAGE_NAME = '@appium/mcp-documentation';
26
+ /** True when the user has opted into the documentation tools. */
27
+ export function isDocumentationEnabled() {
28
+ return isTruthyEnvValue(process.env[ENABLED_FLAG]);
29
+ }
30
+ /**
31
+ * Load the documentation plugin when the user has opted in.
32
+ *
33
+ * @returns the plugin instance, or `null` if the optional package is not
34
+ * installed or fails to load (in which case the server runs without the
35
+ * documentation tools).
36
+ */
37
+ export async function loadDocumentationPlugin() {
38
+ try {
39
+ // Widen to `string` so TypeScript treats this as a fully dynamic import and
40
+ // does not require @appium/mcp-documentation to be resolvable at build time
41
+ // (it is an optional, opt-in dependency, not installed by default).
42
+ const specifier = PACKAGE_NAME;
43
+ const mod = (await import(specifier));
44
+ const plugin = new mod.AppiumDocumentation();
45
+ log.info(`Documentation tools enabled (${PACKAGE_NAME} loaded).`);
46
+ return plugin;
47
+ }
48
+ catch (err) {
49
+ if (isModuleNotFound(err)) {
50
+ log.warn(`${ENABLED_FLAG} is set but ${PACKAGE_NAME} is not installed. ` +
51
+ 'The documentation tools (appium_documentation_query, appium_skills) ' +
52
+ 'will be unavailable. Install the package to enable them:\n' +
53
+ ` npm install ${PACKAGE_NAME}`);
54
+ }
55
+ else {
56
+ log.error(`${PACKAGE_NAME} is installed but failed to load:`, err);
57
+ }
58
+ return null;
59
+ }
60
+ }
61
+ function isModuleNotFound(err) {
62
+ if (typeof err !== 'object' || err === null || !('code' in err)) {
63
+ return false;
64
+ }
65
+ const code = err.code;
66
+ return code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND';
67
+ }
68
+ //# sourceMappingURL=documentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"documentation.js","sourceRoot":"","sources":["../src/documentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,GAAG,MAAM,aAAa,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,YAAY,GAAG,yBAAyB,CAAC;AAC/C,MAAM,YAAY,GAAG,2BAA2B,CAAC;AAEjD,iEAAiE;AACjE,MAAM,UAAU,sBAAsB;IACpC,OAAO,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,IAAI,CAAC;QACH,4EAA4E;QAC5E,4EAA4E;QAC5E,oEAAoE;QACpE,MAAM,SAAS,GAAW,YAAY,CAAC;QACvC,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAEnC,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,mBAAmB,EAAE,CAAC;QAC7C,GAAG,CAAC,IAAI,CAAC,gCAAgC,YAAY,WAAW,CAAC,CAAC;QAClE,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,GAAG,CAAC,IAAI,CACN,GAAG,YAAY,eAAe,YAAY,qBAAqB;gBAC7D,sEAAsE;gBACtE,4DAA4D;gBAC5D,iBAAiB,YAAY,EAAE,CAClC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,KAAK,CAAC,GAAG,YAAY,mCAAmC,EAAE,GAAG,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC;QAChE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAI,GAA0B,CAAC,IAAI,CAAC;IAC9C,OAAO,IAAI,KAAK,sBAAsB,IAAI,IAAI,KAAK,kBAAkB,CAAC;AACxE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,MAAM,iEAA2C,CAAC;AACxD,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAmBA,QAAA,MAAM,MAAM,iEAA2C,CAAC;AACxD,eAAe,MAAM,CAAC"}
package/dist/server.js CHANGED
@@ -1,10 +1,15 @@
1
1
  import { createAppiumMcpServer } from './create-server.js';
2
+ import { isDocumentationEnabled, loadDocumentationPlugin, } from './documentation.js';
2
3
  const plugins = [];
3
- try {
4
- const { AppiumDocumentation } = await import('@appium/mcp-documentation');
5
- plugins.push(new AppiumDocumentation());
4
+ // Documentation tools (RAG docs query + skills) are opt-in. They live in a
5
+ // separate package only installed when the user sets
6
+ // APPIUM_MCP_DOCS_ENABLED. See ./documentation.ts for the full contract.
7
+ if (isDocumentationEnabled()) {
8
+ const documentationPlugin = await loadDocumentationPlugin();
9
+ if (documentationPlugin) {
10
+ plugins.push(documentationPlugin);
11
+ }
6
12
  }
7
- catch (_err) { }
8
13
  const server = await createAppiumMcpServer({ plugins });
9
14
  export default server;
10
15
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,OAAO,GAAsB,EAAE,CAAC;AAEtC,IAAI,CAAC;IACH,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;IAC1E,OAAO,CAAC,IAAI,CAAC,IAAI,mBAAmB,EAAE,CAAC,CAAC;AAC1C,CAAC;AAAC,OAAO,IAAI,EAAE,CAAC,CAAA,CAAC;AAEjB,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;AACxD,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAE5B,MAAM,OAAO,GAAsB,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,qDAAqD;AACrD,yEAAyE;AACzE,IAAI,sBAAsB,EAAE,EAAE,CAAC;IAC7B,MAAM,mBAAmB,GAAG,MAAM,uBAAuB,EAAE,CAAC;IAC5D,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAED,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;AACxD,eAAe,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "appium-mcp",
3
3
  "mcpName": "io.github.appium/appium-mcp",
4
- "version": "1.85.10",
4
+ "version": "1.86.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
@@ -57,10 +57,8 @@
57
57
  "license": "Apache-2.0",
58
58
  "description": "Intelligent MCP server providing AI assistants with powerful tools and resources for Appium mobile automation",
59
59
  "dependencies": {
60
- "@appium/mcp-documentation": "^1.0.1",
61
60
  "@appium/support": "^7.0.2",
62
61
  "@modelcontextprotocol/sdk": "^1.22.0",
63
- "@xenova/transformers": "^2.17.2",
64
62
  "@xmldom/xmldom": "^0.9.8",
65
63
  "appium-adb": "^15.0.0",
66
64
  "appium-ios-device": "^3.1.0",
@@ -70,8 +68,8 @@
70
68
  "applesign": "^5.0.0",
71
69
  "fastmcp": "^4.0.0",
72
70
  "ios-mobileprovision-finder": "^1.2.1",
73
- "node-simctl": "^8.0.4",
74
71
  "lru-cache": "^11.5.0",
72
+ "node-simctl": "^8.0.4",
75
73
  "webdriver": "^9.23.0",
76
74
  "xpath": "^0.0.34",
77
75
  "zod": "^4.3.6"
@@ -81,6 +79,14 @@
81
79
  "@opentelemetry/exporter-trace-otlp-http": "^0.219.0",
82
80
  "@opentelemetry/sdk-node": "^0.219.0"
83
81
  },
82
+ "peerDependencies": {
83
+ "@appium/mcp-documentation": "^1.0.1"
84
+ },
85
+ "peerDependenciesMeta": {
86
+ "@appium/mcp-documentation": {
87
+ "optional": true
88
+ }
89
+ },
84
90
  "devDependencies": {
85
91
  "@appium/eslint-config-appium-ts": "^3.0.0",
86
92
  "@appium/tsconfig": "^1.1.2",
package/server.json CHANGED
@@ -3,12 +3,12 @@
3
3
  "name": "io.github.appium/appium-mcp",
4
4
  "title": "MCP Appium - Mobile Development and Automation Server",
5
5
  "description": "MCP server for Appium mobile automation on iOS and Android devices with test creation tools.",
6
- "version": "1.85.10",
6
+ "version": "1.86.0",
7
7
  "packages": [
8
8
  {
9
9
  "registryType": "npm",
10
10
  "identifier": "appium-mcp",
11
- "version": "1.85.10",
11
+ "version": "1.86.0",
12
12
  "transport": {
13
13
  "type": "stdio"
14
14
  }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Optional Appium documentation plugin (RAG docs query + skills tools).
3
+ *
4
+ * The documentation feature lives in a separate package, `@appium/mcp-documentation`,
5
+ * which carries a multi-megabyte embeddings cache and pulls in a heavy ML stack
6
+ * (`@xenova/transformers`, `@langchain/*`). To keep the default install lean, that
7
+ * package is declared as an OPTIONAL peer dependency: it is not installed by
8
+ * default, and nothing related to it is downloaded unless the user opts in.
9
+ *
10
+ * Contract:
11
+ * - `APPIUM_MCP_DOCS_ENABLED` unset / not truthy → docs tools are NOT registered.
12
+ * This is the default; nothing extra is loaded.
13
+ * - `APPIUM_MCP_DOCS_ENABLED` truthy → the plugin is loaded if
14
+ * `@appium/mcp-documentation` is installed. If it is not installed, the server
15
+ * logs an actionable install hint and starts normally without the docs tools.
16
+ *
17
+ * Installation is intentionally left to the user's package manager (run at install
18
+ * time, where it belongs) rather than shelled out from the running server: that
19
+ * dedupes against appium-mcp's existing dependencies, works across npm/pnpm/yarn,
20
+ * and never blocks server startup.
21
+ */
22
+
23
+ import type { AppiumMcpPlugin } from './core.js';
24
+ import log from './logger.js';
25
+ import { isTruthyEnvValue } from './utils/env.js';
26
+
27
+ const ENABLED_FLAG = 'APPIUM_MCP_DOCS_ENABLED';
28
+ const PACKAGE_NAME = '@appium/mcp-documentation';
29
+
30
+ /** True when the user has opted into the documentation tools. */
31
+ export function isDocumentationEnabled(): boolean {
32
+ return isTruthyEnvValue(process.env[ENABLED_FLAG]);
33
+ }
34
+
35
+ /**
36
+ * Load the documentation plugin when the user has opted in.
37
+ *
38
+ * @returns the plugin instance, or `null` if the optional package is not
39
+ * installed or fails to load (in which case the server runs without the
40
+ * documentation tools).
41
+ */
42
+ export async function loadDocumentationPlugin(): Promise<AppiumMcpPlugin | null> {
43
+ try {
44
+ // Widen to `string` so TypeScript treats this as a fully dynamic import and
45
+ // does not require @appium/mcp-documentation to be resolvable at build time
46
+ // (it is an optional, opt-in dependency, not installed by default).
47
+ const specifier: string = PACKAGE_NAME;
48
+ const mod = (await import(specifier)) as {
49
+ AppiumDocumentation: new () => AppiumMcpPlugin;
50
+ };
51
+ const plugin = new mod.AppiumDocumentation();
52
+ log.info(`Documentation tools enabled (${PACKAGE_NAME} loaded).`);
53
+ return plugin;
54
+ } catch (err) {
55
+ if (isModuleNotFound(err)) {
56
+ log.warn(
57
+ `${ENABLED_FLAG} is set but ${PACKAGE_NAME} is not installed. ` +
58
+ 'The documentation tools (appium_documentation_query, appium_skills) ' +
59
+ 'will be unavailable. Install the package to enable them:\n' +
60
+ ` npm install ${PACKAGE_NAME}`
61
+ );
62
+ } else {
63
+ log.error(`${PACKAGE_NAME} is installed but failed to load:`, err);
64
+ }
65
+ return null;
66
+ }
67
+ }
68
+
69
+ function isModuleNotFound(err: unknown): boolean {
70
+ if (typeof err !== 'object' || err === null || !('code' in err)) {
71
+ return false;
72
+ }
73
+ const code = (err as { code?: unknown }).code;
74
+ return code === 'ERR_MODULE_NOT_FOUND' || code === 'MODULE_NOT_FOUND';
75
+ }
package/src/server.ts CHANGED
@@ -1,12 +1,21 @@
1
1
  import type { AppiumMcpPlugin } from './core.js';
2
2
  import { createAppiumMcpServer } from './create-server.js';
3
+ import {
4
+ isDocumentationEnabled,
5
+ loadDocumentationPlugin,
6
+ } from './documentation.js';
3
7
 
4
8
  const plugins: AppiumMcpPlugin[] = [];
5
9
 
6
- try {
7
- const { AppiumDocumentation } = await import('@appium/mcp-documentation');
8
- plugins.push(new AppiumDocumentation());
9
- } catch (_err) {}
10
+ // Documentation tools (RAG docs query + skills) are opt-in. They live in a
11
+ // separate package only installed when the user sets
12
+ // APPIUM_MCP_DOCS_ENABLED. See ./documentation.ts for the full contract.
13
+ if (isDocumentationEnabled()) {
14
+ const documentationPlugin = await loadDocumentationPlugin();
15
+ if (documentationPlugin) {
16
+ plugins.push(documentationPlugin);
17
+ }
18
+ }
10
19
 
11
20
  const server = await createAppiumMcpServer({ plugins });
12
21
  export default server;