appium-mcp 1.90.1 → 1.91.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +11 -6
  3. package/dist/resources/index.d.ts.map +1 -1
  4. package/dist/resources/index.js +4 -0
  5. package/dist/resources/index.js.map +1 -1
  6. package/dist/resources/locator-generator.d.ts +4 -0
  7. package/dist/resources/locator-generator.d.ts.map +1 -0
  8. package/dist/resources/locator-generator.js +15 -0
  9. package/dist/resources/locator-generator.js.map +1 -0
  10. package/dist/resources/screenshot-viewer.d.ts +4 -0
  11. package/dist/resources/screenshot-viewer.d.ts.map +1 -0
  12. package/dist/resources/screenshot-viewer.js +15 -0
  13. package/dist/resources/screenshot-viewer.js.map +1 -0
  14. package/dist/tools/interactions/screenshot.d.ts +3 -2
  15. package/dist/tools/interactions/screenshot.d.ts.map +1 -1
  16. package/dist/tools/interactions/screenshot.js +22 -2
  17. package/dist/tools/interactions/screenshot.js.map +1 -1
  18. package/dist/tools/test-generation/locators.d.ts +13 -1
  19. package/dist/tools/test-generation/locators.d.ts.map +1 -1
  20. package/dist/tools/test-generation/locators.js +11 -12
  21. package/dist/tools/test-generation/locators.js.map +1 -1
  22. package/dist/ui/locator-generator-app.d.ts +8 -0
  23. package/dist/ui/locator-generator-app.d.ts.map +1 -0
  24. package/dist/ui/locator-generator-app.js +282 -0
  25. package/dist/ui/locator-generator-app.js.map +1 -0
  26. package/dist/ui/screenshot-viewer-app.d.ts +9 -0
  27. package/dist/ui/screenshot-viewer-app.d.ts.map +1 -0
  28. package/dist/ui/screenshot-viewer-app.js +282 -0
  29. package/dist/ui/screenshot-viewer-app.js.map +1 -0
  30. package/package.json +1 -1
  31. package/server.json +2 -2
  32. package/src/resources/index.ts +4 -0
  33. package/src/resources/locator-generator.ts +18 -0
  34. package/src/resources/screenshot-viewer.ts +18 -0
  35. package/src/tools/interactions/screenshot.ts +29 -4
  36. package/src/tools/test-generation/locators.ts +14 -2
  37. package/src/ui/locator-generator-app.ts +281 -0
  38. package/src/ui/screenshot-viewer-app.ts +281 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [1.91.0](https://github.com/appium/appium-mcp/compare/v1.90.1...v1.91.0) (2026-07-31)
2
+
3
+ ### Features
4
+
5
+ * avoid duplicate screenshot and locator UI payloads ([#478](https://github.com/appium/appium-mcp/issues/478)) ([562e84e](https://github.com/appium/appium-mcp/commit/562e84e58063d071bb46d9e1c2fe093b50f30a88))
6
+
1
7
  ## [1.90.1](https://github.com/appium/appium-mcp/compare/v1.90.0...v1.90.1) (2026-07-29)
2
8
 
3
9
  ### Miscellaneous Chores
package/README.md CHANGED
@@ -332,9 +332,14 @@ More models benchmarked can be found [here](src/tests/benchmark_model/TEST_REPOR
332
332
 
333
333
  #### MCP Apps Mode
334
334
 
335
- `appium_get_page_source` uses a static MCP App inspector by default when the client advertises MCP Apps support.
336
- The XML remains in the normal text result for the LLM, while the inspector reads that same result instead of
337
- receiving a duplicated XML copy.
335
+ `appium_get_page_source`, `appium_screenshot`, and `generate_locators` use static MCP App viewers by default when
336
+ the client advertises MCP Apps support.
337
+
338
+ - Page source XML and generated locator JSON remain in their normal text results for the LLM. Their viewers read
339
+ those existing results instead of receiving duplicated copies inside generated HTML.
340
+ - Saved screenshot base64 is delivered to the viewer through `structuredContent`, which MCP Apps keeps out of model
341
+ context. The LLM still receives the saved file path. Explicit `returnRawBase64=true` calls keep their existing raw
342
+ image result.
338
343
 
339
344
  For clients with unreliable MCP Apps rendering, set `APPIUM_MCP_APPS_ENABLED` to `false` or `0`:
340
345
 
@@ -348,9 +353,9 @@ For clients with unreliable MCP Apps rendering, set `APPIUM_MCP_APPS_ENABLED` to
348
353
  }
349
354
  ```
350
355
 
351
- This keeps interactive UI enabled but forces the previous embedded page-source inspector. The compatibility mode
352
- duplicates the XML inside the inspector HTML and therefore uses more result tokens and bandwidth. With a synthetic
353
- 95,000-character page source, the static mode reduced the result from approximately 267 KB to 95 KB (about 64%).
356
+ This keeps interactive UI enabled but forces the previous embedded viewers. The compatibility mode places viewer
357
+ data inside inline HTML and therefore uses more result tokens and bandwidth. With a synthetic 95,000-character page
358
+ source, the static mode reduced the result from approximately 267 KB to 95 KB (about 64%).
354
359
 
355
360
  `NO_UI=true` or `NO_UI=1` takes precedence over this setting and disables both static and embedded UI.
356
361
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,MAAM,EAAE,GAAG,QAMpD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,MAAM,EAAE,GAAG,QAQpD"}
@@ -2,11 +2,15 @@ import log from '../logger.js';
2
2
  import { isMcpAppsEnabled } from '../ui/mcp-apps.js';
3
3
  // Export all resources
4
4
  import javaTemplatesResource from './java/template.js';
5
+ import locatorGeneratorResource from './locator-generator.js';
5
6
  import pageSourceInspectorResource from './page-source-inspector.js';
7
+ import screenshotViewerResource from './screenshot-viewer.js';
6
8
  export default function registerResources(server) {
7
9
  javaTemplatesResource(server);
8
10
  if (isMcpAppsEnabled()) {
9
11
  pageSourceInspectorResource(server);
12
+ screenshotViewerResource(server);
13
+ locatorGeneratorResource(server);
10
14
  }
11
15
  log.info('All resources registered');
12
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AACnD,uBAAuB;AACvB,OAAO,qBAAqB,MAAM,oBAAoB,CAAC;AACvD,OAAO,2BAA2B,MAAM,4BAA4B,CAAC;AAErE,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,MAAW;IACnD,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;AACvC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAC;AAC/B,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AACnD,uBAAuB;AACvB,OAAO,qBAAqB,MAAM,oBAAoB,CAAC;AACvD,OAAO,wBAAwB,MAAM,wBAAwB,CAAC;AAC9D,OAAO,2BAA2B,MAAM,4BAA4B,CAAC;AACrE,OAAO,wBAAwB,MAAM,wBAAwB,CAAC;AAE9D,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,MAAW;IACnD,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,gBAAgB,EAAE,EAAE,CAAC;QACvB,2BAA2B,CAAC,MAAM,CAAC,CAAC;QACpC,wBAAwB,CAAC,MAAM,CAAC,CAAC;QACjC,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from 'fastmcp';
2
+ export declare const LOCATOR_GENERATOR_URI = "ui://appium-mcp/locator-generator";
3
+ export default function locatorGeneratorResource(server: FastMCP): void;
4
+ //# sourceMappingURL=locator-generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locator-generator.d.ts","sourceRoot":"","sources":["../../src/resources/locator-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAKrC,eAAO,MAAM,qBAAqB,sCAAsC,CAAC;AAEzE,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAUtE"}
@@ -0,0 +1,15 @@
1
+ import { createLocatorGeneratorAppUI } from '../ui/locator-generator-app.js';
2
+ import { MCP_APP_MIME_TYPE } from '../ui/mcp-apps.js';
3
+ export const LOCATOR_GENERATOR_URI = 'ui://appium-mcp/locator-generator';
4
+ export default function locatorGeneratorResource(server) {
5
+ server.addResource({
6
+ uri: LOCATOR_GENERATOR_URI,
7
+ name: 'Appium Locator Generator',
8
+ description: 'Interactive viewer for generate_locators results',
9
+ mimeType: MCP_APP_MIME_TYPE,
10
+ async load() {
11
+ return { text: createLocatorGeneratorAppUI() };
12
+ },
13
+ });
14
+ }
15
+ //# sourceMappingURL=locator-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locator-generator.js","sourceRoot":"","sources":["../../src/resources/locator-generator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,2BAA2B,EAAC,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAC,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAEpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,mCAAmC,CAAC;AAEzE,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAAC,MAAe;IAC9D,MAAM,CAAC,WAAW,CAAC;QACjB,GAAG,EAAE,qBAAqB;QAC1B,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,kDAAkD;QAC/D,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,CAAC,IAAI;YACR,OAAO,EAAC,IAAI,EAAE,2BAA2B,EAAE,EAAC,CAAC;QAC/C,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { FastMCP } from 'fastmcp';
2
+ export declare const SCREENSHOT_VIEWER_URI = "ui://appium-mcp/screenshot-viewer";
3
+ export default function screenshotViewerResource(server: FastMCP): void;
4
+ //# sourceMappingURL=screenshot-viewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot-viewer.d.ts","sourceRoot":"","sources":["../../src/resources/screenshot-viewer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAKrC,eAAO,MAAM,qBAAqB,sCAAsC,CAAC;AAEzE,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAUtE"}
@@ -0,0 +1,15 @@
1
+ import { MCP_APP_MIME_TYPE } from '../ui/mcp-apps.js';
2
+ import { createScreenshotViewerAppUI } from '../ui/screenshot-viewer-app.js';
3
+ export const SCREENSHOT_VIEWER_URI = 'ui://appium-mcp/screenshot-viewer';
4
+ export default function screenshotViewerResource(server) {
5
+ server.addResource({
6
+ uri: SCREENSHOT_VIEWER_URI,
7
+ name: 'Appium Screenshot Viewer',
8
+ description: 'Interactive viewer for appium_screenshot results',
9
+ mimeType: MCP_APP_MIME_TYPE,
10
+ async load() {
11
+ return { text: createScreenshotViewerAppUI() };
12
+ },
13
+ });
14
+ }
15
+ //# sourceMappingURL=screenshot-viewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot-viewer.js","sourceRoot":"","sources":["../../src/resources/screenshot-viewer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAC,2BAA2B,EAAC,MAAM,gCAAgC,CAAC;AAE3E,MAAM,CAAC,MAAM,qBAAqB,GAAG,mCAAmC,CAAC;AAEzE,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAAC,MAAe;IAC9D,MAAM,CAAC,WAAW,CAAC;QACjB,GAAG,EAAE,qBAAqB;QAC1B,IAAI,EAAE,0BAA0B;QAChC,WAAW,EAAE,kDAAkD;QAC/D,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,CAAC,IAAI;YACR,OAAO,EAAC,IAAI,EAAE,2BAA2B,EAAE,EAAC,CAAC;QAC/C,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { FastMCP } from 'fastmcp';
1
+ import type { ContentResult, FastMCP } from 'fastmcp';
2
2
  import { resolveScreenshotDir } from '../../utils/paths.js';
3
3
  export { resolveScreenshotDir };
4
4
  export interface ScreenshotDeps {
@@ -15,6 +15,7 @@ export declare function executeScreenshot(opts: {
15
15
  maxWidth?: number;
16
16
  returnRawBase64?: boolean;
17
17
  sessionId?: string;
18
- }): Promise<any>;
18
+ useMcpApps?: boolean;
19
+ }): Promise<ContentResult>;
19
20
  export default function screenshot(server: FastMCP): void;
20
21
  //# sourceMappingURL=screenshot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/screenshot.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAMrC,OAAO,EAAC,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAC,oBAAoB,EAAC,CAAC;AAE9B,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9E,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAClD,OAAO,EAAE,MAAM,MAAM,CAAC;CACvB;AASD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,GAAG,CAAC,CAmEf;AAyBD,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAiBxD"}
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/screenshot.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,aAAa,EAAE,OAAO,EAAC,MAAM,SAAS,CAAC;AAQpD,OAAO,EAAC,oBAAoB,EAAC,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAC,oBAAoB,EAAC,CAAC;AAE9B,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAChE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC9E,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAClD,OAAO,EAAE,MAAM,MAAM,CAAC;CACvB;AASD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,aAAa,CAAC,CAmFzB;AAyBD,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAuBxD"}
@@ -2,7 +2,9 @@ import { join } from 'node:path';
2
2
  import { fs, imageUtil } from '@appium/support';
3
3
  import z from 'zod';
4
4
  import { getScreenshot } from '../../command.js';
5
+ import { SCREENSHOT_VIEWER_URI } from '../../resources/screenshot-viewer.js';
5
6
  import { elementUUIDScheme } from '../../schema.js';
7
+ import { clientSupportsMcpApps, isMcpAppsEnabled } from '../../ui/mcp-apps.js';
6
8
  import { createUIResource, createScreenshotViewerUI, addUIResourceToResponse } from '../../ui/mcp-ui-utils.js';
7
9
  import { resolveScreenshotDir } from '../../utils/paths.js';
8
10
  import { resolveDriver, textResult, errorResult, toolErrorMessage } from '../tool-response.js';
@@ -14,7 +16,7 @@ const defaultDeps = {
14
16
  dateNow: () => Date.now(),
15
17
  };
16
18
  export async function executeScreenshot(opts) {
17
- const { deps = defaultDeps, elementId, maxWidth, returnRawBase64, sessionId } = opts;
19
+ const { deps = defaultDeps, elementId, maxWidth, returnRawBase64, sessionId, useMcpApps = false } = opts;
18
20
  const resolved = await resolveDriver(sessionId);
19
21
  if (!resolved.ok) {
20
22
  return resolved.result;
@@ -59,6 +61,21 @@ export async function executeScreenshot(opts) {
59
61
  // Save screenshot to disk
60
62
  await deps.writeFile(filepath, screenshotBuffer);
61
63
  const textResponse = textResult(`Screenshot saved successfully to: ${filepath}`);
64
+ // MCP Apps-capable clients receive the image through structuredContent.
65
+ // It remains available to the viewer without adding base64 data to model
66
+ // context or duplicating it inside generated HTML.
67
+ if (useMcpApps) {
68
+ return {
69
+ ...textResponse,
70
+ structuredContent: {
71
+ screenshot: {
72
+ data: displayBase64,
73
+ mimeType: 'image/png',
74
+ filepath,
75
+ },
76
+ },
77
+ };
78
+ }
62
79
  // Add interactive screenshot viewer UI
63
80
  return addUIResourceToResponse(textResponse, () => createUIResource(`ui://appium-mcp/screenshot-viewer/${Date.now()}`, createScreenshotViewerUI(displayBase64, filepath)));
64
81
  }
@@ -85,19 +102,22 @@ const screenshotSchema = z.object({
85
102
  sessionId: z.string().optional().describe('Session ID to target. If omitted, uses the active session.'),
86
103
  });
87
104
  export default function screenshot(server) {
105
+ const mcpAppsEnabled = isMcpAppsEnabled();
88
106
  server.addTool({
89
107
  name: 'appium_screenshot',
90
108
  description: 'Take a screenshot and save as PNG. Optionally provide elementUUID to capture only that element.',
109
+ _meta: mcpAppsEnabled ? { ui: { resourceUri: SCREENSHOT_VIEWER_URI } } : undefined,
91
110
  parameters: screenshotSchema,
92
111
  annotations: {
93
112
  readOnlyHint: false,
94
113
  openWorldHint: false,
95
114
  },
96
- execute: async (args, _context) => executeScreenshot({
115
+ execute: async (args, context) => executeScreenshot({
97
116
  elementId: args.elementUUID,
98
117
  maxWidth: args.maxWidth,
99
118
  returnRawBase64: args.returnRawBase64,
100
119
  sessionId: args.sessionId,
120
+ useMcpApps: mcpAppsEnabled && clientSupportsMcpApps(server, context),
101
121
  }),
102
122
  });
103
123
  }
@@ -1 +1 @@
1
- {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../../src/tools/interactions/screenshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../../src/tools/interactions/screenshot.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,14 @@
1
- export default function generateLocators(server: any): void;
1
+ /**
2
+ * Tool to get page source from the Android session
3
+ *
4
+ * TOOL EXTENSION GUIDE:
5
+ * This tool demonstrates the traditional approach where metadata is defined inline.
6
+ *
7
+ * ALTERNATIVE APPROACH: You can also use YAML metadata files for better separation.
8
+ * See src/tools/metadata/ for examples and src/tools/scroll-with-yaml.example.ts
9
+ *
10
+ * For detailed documentation on adding tools, see docs/CONTRIBUTING.md
11
+ */
12
+ import type { FastMCP } from 'fastmcp';
13
+ export default function generateLocators(server: FastMCP): void;
2
14
  //# sourceMappingURL=locators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"locators.d.ts","sourceRoot":"","sources":["../../../src/tools/test-generation/locators.ts"],"names":[],"mappings":"AAmBA,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CAyD1D"}
1
+ {"version":3,"file":"locators.d.ts","sourceRoot":"","sources":["../../../src/tools/test-generation/locators.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAWrC,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAkE9D"}
@@ -1,24 +1,17 @@
1
- /**
2
- * Tool to get page source from the Android session
3
- *
4
- * TOOL EXTENSION GUIDE:
5
- * This tool demonstrates the traditional approach where metadata is defined inline.
6
- *
7
- * ALTERNATIVE APPROACH: You can also use YAML metadata files for better separation.
8
- * See src/tools/metadata/ for examples and src/tools/scroll-with-yaml.example.ts
9
- *
10
- * For detailed documentation on adding tools, see docs/CONTRIBUTING.md
11
- */
12
1
  import { z } from 'zod';
13
2
  import { getPageSource } from '../../command.js';
14
3
  import { generateAllElementLocators } from '../../locators/generate-all-locators.js';
4
+ import { LOCATOR_GENERATOR_URI } from '../../resources/locator-generator.js';
15
5
  import { isAndroidUiautomator2DriverSession, isXCUITestDriverSession } from '../../session-store.js';
6
+ import { clientSupportsMcpApps, isMcpAppsEnabled } from '../../ui/mcp-apps.js';
16
7
  import { createUIResource, createLocatorGeneratorUI, addUIResourceToResponse } from '../../ui/mcp-ui-utils.js';
17
8
  import { resolveDriver, textResult, errorResult, toolErrorMessage } from '../tool-response.js';
18
9
  export default function generateLocators(server) {
10
+ const mcpAppsEnabled = isMcpAppsEnabled();
19
11
  server.addTool({
20
12
  name: 'generate_locators',
21
13
  description: `Generate locators for all interactable elements on the current page. [PRIORITY 3: Use this for debugging/inspection or when you need comprehensive element info with locator suggestions]`,
14
+ _meta: mcpAppsEnabled ? { ui: { resourceUri: LOCATOR_GENERATOR_URI } } : undefined,
22
15
  parameters: z.object({
23
16
  sessionId: z.string().optional().describe('Session ID to target. If omitted, uses the active session.'),
24
17
  }),
@@ -26,7 +19,8 @@ export default function generateLocators(server) {
26
19
  readOnlyHint: true,
27
20
  openWorldHint: false,
28
21
  },
29
- execute: async (args, { log }) => {
22
+ execute: async (args, context) => {
23
+ const { log } = context;
30
24
  log.info('Getting page source');
31
25
  const resolved = await resolveDriver(args.sessionId);
32
26
  if (!resolved.ok) {
@@ -55,6 +49,11 @@ export default function generateLocators(server) {
55
49
  instruction: `These are the locators for the current page. Use this to generate code for the current page.
56
50
  Using the template provided by generate://code-with-locators resource.`,
57
51
  }));
52
+ // The static MCP App reads the existing JSON text result instead of
53
+ // receiving a second copy rendered into inline HTML.
54
+ if (mcpAppsEnabled && clientSupportsMcpApps(server, context)) {
55
+ return textResponse;
56
+ }
58
57
  return addUIResourceToResponse(textResponse, () => createUIResource(`ui://appium-mcp/locator-generator/${Date.now()}`, createLocatorGeneratorUI(interactableElements)));
59
58
  }
60
59
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"locators.js","sourceRoot":"","sources":["../../../src/tools/test-generation/locators.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"locators.js","sourceRoot":"","sources":["../../../src/tools/test-generation/locators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Static MCP App used by generate_locators.
3
+ *
4
+ * Locator data is read from the tool's existing JSON text result, avoiding a
5
+ * second rendered copy of the same element and locator strings in inline HTML.
6
+ */
7
+ export declare function createLocatorGeneratorAppUI(): string;
8
+ //# sourceMappingURL=locator-generator-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locator-generator-app.d.ts","sourceRoot":"","sources":["../../src/ui/locator-generator-app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAkRpD"}
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Static MCP App used by generate_locators.
3
+ *
4
+ * Locator data is read from the tool's existing JSON text result, avoiding a
5
+ * second rendered copy of the same element and locator strings in inline HTML.
6
+ */
7
+ export function createLocatorGeneratorAppUI() {
8
+ return `
9
+ <!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="UTF-8">
13
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
14
+ <title>Locator Generator</title>
15
+ <style>
16
+ * { box-sizing: border-box; }
17
+ body {
18
+ margin: 0;
19
+ padding: 20px;
20
+ background: #f5f5f5;
21
+ color: #222;
22
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
23
+ }
24
+ .container { max-width: 1200px; margin: 0 auto; }
25
+ .header { margin-bottom: 24px; }
26
+ .header h1 { margin: 0 0 8px; font-size: 24px; }
27
+ .status { margin: 0; color: #666; }
28
+ .grid { display: grid; gap: 16px; }
29
+ .card {
30
+ padding: 16px;
31
+ border: 1px solid #e0e0e0;
32
+ border-radius: 8px;
33
+ background: #fff;
34
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
35
+ }
36
+ .card-header {
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: space-between;
40
+ gap: 12px;
41
+ margin-bottom: 12px;
42
+ }
43
+ .card-header h2 { margin: 0; font-size: 16px; font-weight: 600; }
44
+ .badges { display: flex; flex-wrap: wrap; gap: 6px; }
45
+ .badge {
46
+ padding: 2px 8px;
47
+ border-radius: 4px;
48
+ font-size: 11px;
49
+ font-weight: 500;
50
+ }
51
+ .badge-clickable { background: #d4edda; color: #155724; }
52
+ .badge-enabled { background: #d1ecf1; color: #0c5460; }
53
+ .badge-displayed { background: #fff3cd; color: #856404; }
54
+ .detail { margin: 0 0 8px; color: #666; font-size: 13px; }
55
+ .detail code {
56
+ padding: 2px 6px;
57
+ border-radius: 3px;
58
+ background: #f5f5f5;
59
+ font-size: 12px;
60
+ }
61
+ .locator-list { margin-top: 12px; }
62
+ .locator {
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 12px;
66
+ margin-bottom: 6px;
67
+ padding: 8px;
68
+ border-radius: 4px;
69
+ background: #f8f9fa;
70
+ }
71
+ .strategy {
72
+ min-width: 120px;
73
+ color: #007aff;
74
+ font-size: 12px;
75
+ font-weight: 600;
76
+ }
77
+ .selector {
78
+ flex: 1;
79
+ overflow-x: auto;
80
+ padding: 4px 8px;
81
+ border-radius: 3px;
82
+ background: #fff;
83
+ font: 12px Monaco, Menlo, monospace;
84
+ }
85
+ .test-button {
86
+ padding: 4px 12px;
87
+ border: 0;
88
+ border-radius: 4px;
89
+ background: #007aff;
90
+ color: #fff;
91
+ cursor: pointer;
92
+ font-size: 12px;
93
+ }
94
+ .test-button:hover { background: #0056b3; }
95
+ </style>
96
+ </head>
97
+ <body>
98
+ <main class="container">
99
+ <header class="header">
100
+ <h1>🔍 Generated Locators</h1>
101
+ <p class="status" id="status">Waiting for locators…</p>
102
+ </header>
103
+ <div class="grid" id="locatorGrid"></div>
104
+ </main>
105
+ <script>
106
+ (() => {
107
+ const locatorGrid = document.getElementById('locatorGrid');
108
+ const status = document.getElementById('status');
109
+ let nextRequestId = 1;
110
+ const pendingRequests = new Map();
111
+
112
+ function sendRequest(method, params) {
113
+ const id = nextRequestId++;
114
+ window.parent.postMessage({jsonrpc: '2.0', id, method, params}, '*');
115
+ return new Promise((resolve, reject) => pendingRequests.set(id, {resolve, reject}));
116
+ }
117
+
118
+ function sendNotification(method, params) {
119
+ window.parent.postMessage({jsonrpc: '2.0', method, params}, '*');
120
+ }
121
+
122
+ function locatorsFromToolResult(result) {
123
+ const textBlock = result && Array.isArray(result.content)
124
+ ? result.content.find((item) => item && item.type === 'text' && typeof item.text === 'string')
125
+ : undefined;
126
+ if (!textBlock) return undefined;
127
+
128
+ try {
129
+ const parsed = JSON.parse(textBlock.text);
130
+ return Array.isArray(parsed.interactableElements) ? parsed.interactableElements : undefined;
131
+ } catch {
132
+ return undefined;
133
+ }
134
+ }
135
+
136
+ function appendTextDetail(card, label, value, useCode) {
137
+ if (typeof value !== 'string' || !value) return;
138
+ const detail = document.createElement('p');
139
+ detail.className = 'detail';
140
+ const labelElement = document.createElement('strong');
141
+ labelElement.textContent = label + ': ';
142
+ detail.appendChild(labelElement);
143
+ const valueElement = document.createElement(useCode ? 'code' : 'span');
144
+ valueElement.textContent = value;
145
+ detail.appendChild(valueElement);
146
+ card.appendChild(detail);
147
+ }
148
+
149
+ function appendBadge(container, label, className) {
150
+ const badge = document.createElement('span');
151
+ badge.className = 'badge ' + className;
152
+ badge.textContent = label;
153
+ container.appendChild(badge);
154
+ }
155
+
156
+ async function testLocator(strategy, selector, button) {
157
+ const originalText = button.textContent;
158
+ button.disabled = true;
159
+ button.textContent = 'Testing…';
160
+ try {
161
+ const result = await sendRequest('tools/call', {
162
+ name: 'appium_find_element',
163
+ arguments: {strategy, selector},
164
+ });
165
+ button.textContent = result && result.isError ? 'Failed' : 'Found';
166
+ } catch {
167
+ button.textContent = 'Failed';
168
+ }
169
+ window.setTimeout(() => {
170
+ button.disabled = false;
171
+ button.textContent = originalText;
172
+ }, 1500);
173
+ }
174
+
175
+ function renderLocators(elements) {
176
+ locatorGrid.textContent = '';
177
+ status.textContent =
178
+ 'Found ' + elements.length + ' interactable element' + (elements.length === 1 ? '' : 's');
179
+
180
+ if (elements.length === 0) {
181
+ const empty = document.createElement('p');
182
+ empty.textContent = 'No locators found.';
183
+ locatorGrid.appendChild(empty);
184
+ return;
185
+ }
186
+
187
+ elements.forEach((element) => {
188
+ if (!element || typeof element !== 'object') return;
189
+ const card = document.createElement('section');
190
+ card.className = 'card';
191
+
192
+ const header = document.createElement('div');
193
+ header.className = 'card-header';
194
+ const heading = document.createElement('h2');
195
+ heading.textContent = typeof element.tagName === 'string' ? element.tagName : 'Element';
196
+ const badges = document.createElement('div');
197
+ badges.className = 'badges';
198
+ if (element.clickable) appendBadge(badges, 'Clickable', 'badge-clickable');
199
+ if (element.enabled) appendBadge(badges, 'Enabled', 'badge-enabled');
200
+ if (element.displayed) appendBadge(badges, 'Displayed', 'badge-displayed');
201
+ header.append(heading, badges);
202
+ card.appendChild(header);
203
+
204
+ appendTextDetail(card, 'Text', element.text, false);
205
+ appendTextDetail(card, 'Content Desc', element.contentDesc, false);
206
+ appendTextDetail(card, 'Resource ID', element.resourceId, true);
207
+
208
+ const locatorList = document.createElement('div');
209
+ locatorList.className = 'locator-list';
210
+ const locators =
211
+ element.locators && typeof element.locators === 'object' ? Object.entries(element.locators) : [];
212
+ locators.forEach(([strategy, selector]) => {
213
+ if (typeof selector !== 'string') return;
214
+ const locator = document.createElement('div');
215
+ locator.className = 'locator';
216
+ const strategyElement = document.createElement('span');
217
+ strategyElement.className = 'strategy';
218
+ strategyElement.textContent = strategy;
219
+ const selectorElement = document.createElement('code');
220
+ selectorElement.className = 'selector';
221
+ selectorElement.textContent = selector;
222
+ const button = document.createElement('button');
223
+ button.className = 'test-button';
224
+ button.textContent = 'Test';
225
+ button.addEventListener('click', () => testLocator(strategy, selector, button));
226
+ locator.append(strategyElement, selectorElement, button);
227
+ locatorList.appendChild(locator);
228
+ });
229
+ card.appendChild(locatorList);
230
+ locatorGrid.appendChild(card);
231
+ });
232
+ }
233
+
234
+ function setToolResult(result) {
235
+ const elements = locatorsFromToolResult(result);
236
+ if (!elements) {
237
+ locatorGrid.textContent = '';
238
+ status.textContent = result && result.isError
239
+ ? 'Failed to generate locators.'
240
+ : 'No locator data was returned.';
241
+ return;
242
+ }
243
+ renderLocators(elements);
244
+ }
245
+
246
+ window.addEventListener('message', (event) => {
247
+ if (event.source !== window.parent) return;
248
+ const message = event.data;
249
+ if (!message || message.jsonrpc !== '2.0') return;
250
+
251
+ if (message.id !== undefined && pendingRequests.has(message.id)) {
252
+ const pending = pendingRequests.get(message.id);
253
+ pendingRequests.delete(message.id);
254
+ if (message.error) {
255
+ pending.reject(new Error(message.error.message || 'MCP App request failed'));
256
+ } else {
257
+ pending.resolve(message.result);
258
+ }
259
+ return;
260
+ }
261
+
262
+ if (message.method === 'ui/notifications/tool-result') {
263
+ setToolResult(message.params);
264
+ }
265
+ });
266
+
267
+ sendRequest('ui/initialize', {
268
+ protocolVersion: '2026-01-26',
269
+ appInfo: {name: 'Appium Locator Generator', version: '1.0.0'},
270
+ appCapabilities: {availableDisplayModes: ['inline', 'fullscreen']},
271
+ })
272
+ .then(() => sendNotification('ui/notifications/initialized', {}))
273
+ .catch(() => {
274
+ status.textContent = 'Unable to initialize locator generator.';
275
+ });
276
+ })();
277
+ </script>
278
+ </body>
279
+ </html>
280
+ `;
281
+ }
282
+ //# sourceMappingURL=locator-generator-app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locator-generator-app.js","sourceRoot":"","sources":["../../src/ui/locator-generator-app.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B;IACzC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgRN,CAAC;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Static MCP App used by appium_screenshot.
3
+ *
4
+ * Saved screenshots are read from structuredContent so the image remains
5
+ * available to the UI without placing base64-encoded data in model context.
6
+ * Raw image results are also supported for explicit manual calls.
7
+ */
8
+ export declare function createScreenshotViewerAppUI(): string;
9
+ //# sourceMappingURL=screenshot-viewer-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot-viewer-app.d.ts","sourceRoot":"","sources":["../../src/ui/screenshot-viewer-app.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAiRpD"}