appium-mcp 1.82.2 → 1.84.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 +12 -0
- package/README.md +1 -0
- package/dist/create-server.d.ts.map +1 -1
- package/dist/create-server.js +4 -0
- package/dist/create-server.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +7 -4
- package/dist/server.js.map +1 -1
- package/dist/tests/create-server.test.js +8 -0
- package/dist/tests/create-server.test.js.map +1 -1
- package/dist/tests/tools/evidence.test.d.ts +2 -0
- package/dist/tests/tools/evidence.test.d.ts.map +1 -0
- package/dist/tests/tools/evidence.test.js +158 -0
- package/dist/tests/tools/evidence.test.js.map +1 -0
- package/dist/tests/utils/paths.test.js +5 -6
- package/dist/tests/utils/paths.test.js.map +1 -1
- package/dist/tools/evidence.d.ts +82 -0
- package/dist/tools/evidence.d.ts.map +1 -0
- package/dist/tools/evidence.js +124 -0
- package/dist/tools/evidence.js.map +1 -0
- package/dist/tools/gestures/gesture.d.ts.map +1 -1
- package/dist/tools/gestures/gesture.js +47 -25
- package/dist/tools/gestures/gesture.js.map +1 -1
- package/dist/tools/interactions/find.d.ts.map +1 -1
- package/dist/tools/interactions/find.js +27 -4
- package/dist/tools/interactions/find.js.map +1 -1
- package/package.json +8 -19
- package/server.json +2 -2
- package/src/create-server.ts +4 -0
- package/src/server.ts +9 -4
- package/src/tools/evidence.ts +198 -0
- package/src/tools/gestures/gesture.ts +63 -26
- package/src/tools/interactions/find.ts +40 -6
- package/dist/scripts/eval-documentation-rag.d.ts +0 -50
- package/dist/scripts/eval-documentation-rag.d.ts.map +0 -1
- package/dist/scripts/eval-documentation-rag.js +0 -287
- package/dist/scripts/eval-documentation-rag.js.map +0 -1
- package/dist/scripts/generate-embeddings-cache.d.ts +0 -13
- package/dist/scripts/generate-embeddings-cache.d.ts.map +0 -1
- package/dist/scripts/generate-embeddings-cache.js +0 -24
- package/dist/scripts/generate-embeddings-cache.js.map +0 -1
- package/dist/scripts/rag-eval-dataset.json +0 -516
- package/dist/scripts/simple-index-documentation.d.ts +0 -21
- package/dist/scripts/simple-index-documentation.d.ts.map +0 -1
- package/dist/scripts/simple-index-documentation.js +0 -77
- package/dist/scripts/simple-index-documentation.js.map +0 -1
- package/dist/scripts/simple-query-documentation.d.ts +0 -13
- package/dist/scripts/simple-query-documentation.d.ts.map +0 -1
- package/dist/scripts/simple-query-documentation.js +0 -52
- package/dist/scripts/simple-query-documentation.js.map +0 -1
- package/dist/tests/tools/documentation/appium-skills.test.d.ts +0 -2
- package/dist/tests/tools/documentation/appium-skills.test.d.ts.map +0 -1
- package/dist/tests/tools/documentation/appium-skills.test.js +0 -106
- package/dist/tests/tools/documentation/appium-skills.test.js.map +0 -1
- package/dist/tests/tools/documentation/plugin.test.d.ts +0 -2
- package/dist/tests/tools/documentation/plugin.test.d.ts.map +0 -1
- package/dist/tests/tools/documentation/plugin.test.js +0 -34
- package/dist/tests/tools/documentation/plugin.test.js.map +0 -1
- package/dist/tests/tools/documentation/simple-pdf-indexer.test.d.ts +0 -2
- package/dist/tests/tools/documentation/simple-pdf-indexer.test.d.ts.map +0 -1
- package/dist/tests/tools/documentation/simple-pdf-indexer.test.js +0 -46
- package/dist/tests/tools/documentation/simple-pdf-indexer.test.js.map +0 -1
- package/dist/tools/documentation/answer-appium.d.ts +0 -8
- package/dist/tools/documentation/answer-appium.d.ts.map +0 -1
- package/dist/tools/documentation/answer-appium.js +0 -38
- package/dist/tools/documentation/answer-appium.js.map +0 -1
- package/dist/tools/documentation/appium-skills.d.ts +0 -5
- package/dist/tools/documentation/appium-skills.d.ts.map +0 -1
- package/dist/tools/documentation/appium-skills.js +0 -168
- package/dist/tools/documentation/appium-skills.js.map +0 -1
- package/dist/tools/documentation/index.d.ts +0 -29
- package/dist/tools/documentation/index.d.ts.map +0 -1
- package/dist/tools/documentation/index.js +0 -66
- package/dist/tools/documentation/index.js.map +0 -1
- package/dist/tools/documentation/markdown-header-splitter.d.ts +0 -32
- package/dist/tools/documentation/markdown-header-splitter.d.ts.map +0 -1
- package/dist/tools/documentation/markdown-header-splitter.js +0 -180
- package/dist/tools/documentation/markdown-header-splitter.js.map +0 -1
- package/dist/tools/documentation/plugin.d.ts +0 -14
- package/dist/tools/documentation/plugin.d.ts.map +0 -1
- package/dist/tools/documentation/plugin.js +0 -19
- package/dist/tools/documentation/plugin.js.map +0 -1
- package/dist/tools/documentation/reasoning-rag.d.ts +0 -89
- package/dist/tools/documentation/reasoning-rag.d.ts.map +0 -1
- package/dist/tools/documentation/reasoning-rag.js +0 -282
- package/dist/tools/documentation/reasoning-rag.js.map +0 -1
- package/dist/tools/documentation/sentence-transformers-embeddings.d.ts +0 -40
- package/dist/tools/documentation/sentence-transformers-embeddings.d.ts.map +0 -1
- package/dist/tools/documentation/sentence-transformers-embeddings.js +0 -119
- package/dist/tools/documentation/sentence-transformers-embeddings.js.map +0 -1
- package/dist/tools/documentation/simple-pdf-indexer.d.ts +0 -47
- package/dist/tools/documentation/simple-pdf-indexer.d.ts.map +0 -1
- package/dist/tools/documentation/simple-pdf-indexer.js +0 -572
- package/dist/tools/documentation/simple-pdf-indexer.js.map +0 -1
- package/dist/tools/documentation/uploads/documents.json +0 -1
- package/dist/tools/documentation/uploads/embeddings-Xenova-bge-small-en-v1.5.json +0 -1
- package/scripts/setup-submodules-sparse.sh +0 -53
- package/scripts/update-submodules.sh +0 -14
- package/scripts/zip-assets.mjs +0 -55
- package/src/resources/submodules.zip +0 -0
- package/src/scripts/eval-documentation-rag.ts +0 -433
- package/src/scripts/generate-embeddings-cache.ts +0 -24
- package/src/scripts/rag-eval-dataset.json +0 -516
- package/src/scripts/simple-index-documentation.ts +0 -93
- package/src/scripts/simple-query-documentation.ts +0 -61
- package/src/tools/documentation/answer-appium.ts +0 -47
- package/src/tools/documentation/appium-skills.ts +0 -244
- package/src/tools/documentation/index.ts +0 -99
- package/src/tools/documentation/markdown-header-splitter.ts +0 -209
- package/src/tools/documentation/plugin.ts +0 -21
- package/src/tools/documentation/reasoning-rag.ts +0 -438
- package/src/tools/documentation/sentence-transformers-embeddings.ts +0 -152
- package/src/tools/documentation/simple-pdf-indexer.ts +0 -755
- package/src/tools/documentation/uploads/documents.json +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../src/tools/evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gesture.d.ts","sourceRoot":"","sources":["../../../src/tools/gestures/gesture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"gesture.d.ts","sourceRoot":"","sources":["../../../src/tools/gestures/gesture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AA0BtD,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwCrD"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { errorResult, resolveDriver, textResult, toolErrorMessage, } from '../tool-response.js';
|
|
2
|
-
import { GESTURE_ACTIONS, gestureSchema } from './schema.js';
|
|
2
|
+
import { GESTURE_ACTIONS, gestureSchema, } from './schema.js';
|
|
3
3
|
import { handleTap, handleDoubleTap, handleLongPress } from './handlers/tap.js';
|
|
4
4
|
import { handleScroll, handleSwipe } from './handlers/swipe-scroll.js';
|
|
5
5
|
import { handlePinchZoom } from './handlers/pinch.js';
|
|
6
6
|
import { handleScrollToElement } from './handlers/scroll-to-element.js';
|
|
7
7
|
import { back } from '../../command.js';
|
|
8
|
+
import { withEvidence, evidenceContext, } from '../evidence.js';
|
|
9
|
+
import { isAiElementUUID } from './handlers/ai-element.js';
|
|
8
10
|
export default function gesture(server) {
|
|
9
11
|
server.addTool({
|
|
10
12
|
name: 'appium_gesture',
|
|
@@ -23,31 +25,51 @@ export default function gesture(server) {
|
|
|
23
25
|
return resolved.result;
|
|
24
26
|
}
|
|
25
27
|
const { driver } = resolved;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
case 'scroll':
|
|
42
|
-
return handleScroll(driver, args);
|
|
43
|
-
case 'swipe':
|
|
44
|
-
return handleSwipe(driver, args);
|
|
45
|
-
case 'pinch_zoom':
|
|
46
|
-
return handlePinchZoom(driver, args);
|
|
47
|
-
case 'scroll_to_element':
|
|
48
|
-
return handleScrollToElement(driver, args);
|
|
49
|
-
}
|
|
28
|
+
const startedAt = Date.now();
|
|
29
|
+
const context = await evidenceContext(args.sessionId);
|
|
30
|
+
const result = await dispatchGesture(driver, args);
|
|
31
|
+
return withEvidence(result, {
|
|
32
|
+
name: 'appium_gesture',
|
|
33
|
+
stage: gestureStage(args.action),
|
|
34
|
+
startedAt,
|
|
35
|
+
context,
|
|
36
|
+
...(args.strategy && args.selector
|
|
37
|
+
? { locator: { strategy: args.strategy, selector: args.selector } }
|
|
38
|
+
: {}),
|
|
39
|
+
...(args.elementUUID && !isAiElementUUID(args.elementUUID)
|
|
40
|
+
? { element: { webdriverId: args.elementUUID } }
|
|
41
|
+
: {}),
|
|
42
|
+
});
|
|
50
43
|
},
|
|
51
44
|
});
|
|
52
45
|
}
|
|
46
|
+
async function dispatchGesture(driver, args) {
|
|
47
|
+
switch (args.action) {
|
|
48
|
+
case 'back':
|
|
49
|
+
try {
|
|
50
|
+
await back(driver);
|
|
51
|
+
return textResult('Successfully performed back action.');
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
return errorResult(`Failed to perform back action. ${toolErrorMessage(err)}`);
|
|
55
|
+
}
|
|
56
|
+
case 'tap':
|
|
57
|
+
return handleTap(driver, args);
|
|
58
|
+
case 'double_tap':
|
|
59
|
+
return handleDoubleTap(driver, args);
|
|
60
|
+
case 'long_press':
|
|
61
|
+
return handleLongPress(driver, args);
|
|
62
|
+
case 'scroll':
|
|
63
|
+
return handleScroll(driver, args);
|
|
64
|
+
case 'swipe':
|
|
65
|
+
return handleSwipe(driver, args);
|
|
66
|
+
case 'pinch_zoom':
|
|
67
|
+
return handlePinchZoom(driver, args);
|
|
68
|
+
case 'scroll_to_element':
|
|
69
|
+
return handleScrollToElement(driver, args);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function gestureStage(action) {
|
|
73
|
+
return action === 'scroll_to_element' ? 'locate' : 'interact';
|
|
74
|
+
}
|
|
53
75
|
//# sourceMappingURL=gesture.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gesture.js","sourceRoot":"","sources":["../../../src/tools/gestures/gesture.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"gesture.js","sourceRoot":"","sources":["../../../src/tools/gestures/gesture.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/find.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../../src/tools/interactions/find.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,OAAO,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;iBAqC5B,CAAC;AAEH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CA4EzD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { resolveDriver, textResultWithPrimaryElementId, errorResult, toolErrorMessage, readWebElementId, } from '../tool-response.js';
|
|
3
|
+
import { withEvidence, evidenceContext } from '../evidence.js';
|
|
3
4
|
export const findElementSchema = z.object({
|
|
4
5
|
strategy: z
|
|
5
6
|
.enum([
|
|
@@ -57,17 +58,39 @@ export default function findElement(server) {
|
|
|
57
58
|
return resolved.result;
|
|
58
59
|
}
|
|
59
60
|
const { driver } = resolved;
|
|
61
|
+
const startedAt = Date.now();
|
|
62
|
+
const locator = { strategy: args.strategy, selector: args.selector };
|
|
63
|
+
const context = await evidenceContext(args.sessionId);
|
|
60
64
|
try {
|
|
61
65
|
const element = await driver.findElement(args.strategy, args.selector);
|
|
62
66
|
const elementId = readWebElementId(element);
|
|
63
67
|
if (!elementId) {
|
|
64
|
-
return errorResult('Element was returned without a valid element ID')
|
|
68
|
+
return withEvidence(errorResult('Element was returned without a valid element ID'), {
|
|
69
|
+
name: 'appium_find_element',
|
|
70
|
+
stage: 'locate',
|
|
71
|
+
startedAt,
|
|
72
|
+
locator,
|
|
73
|
+
context,
|
|
74
|
+
});
|
|
65
75
|
}
|
|
66
|
-
return textResultWithPrimaryElementId(elementId, `Successfully found element ${args.selector} with strategy ${args.strategy}.`)
|
|
76
|
+
return withEvidence(textResultWithPrimaryElementId(elementId, `Successfully found element ${args.selector} with strategy ${args.strategy}.`), {
|
|
77
|
+
name: 'appium_find_element',
|
|
78
|
+
stage: 'locate',
|
|
79
|
+
startedAt,
|
|
80
|
+
locator,
|
|
81
|
+
element: { webdriverId: elementId },
|
|
82
|
+
context,
|
|
83
|
+
});
|
|
67
84
|
}
|
|
68
85
|
catch (err) {
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
return withEvidence(errorResult(`Failed to find element. Error: ${toolErrorMessage(err)}`), {
|
|
87
|
+
name: 'appium_find_element',
|
|
88
|
+
stage: 'locate',
|
|
89
|
+
startedAt,
|
|
90
|
+
locator,
|
|
91
|
+
context,
|
|
92
|
+
error: err,
|
|
93
|
+
});
|
|
71
94
|
}
|
|
72
95
|
},
|
|
73
96
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find.js","sourceRoot":"","sources":["../../../src/tools/interactions/find.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,aAAa,EACb,8BAA8B,EAC9B,WAAW,EACX,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"find.js","sourceRoot":"","sources":["../../../src/tools/interactions/find.ts"],"names":[],"mappings":"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"}
|
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.
|
|
4
|
+
"version": "1.84.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -27,8 +27,7 @@
|
|
|
27
27
|
"appium-mcp": "dist/index.js"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
|
-
"build": "rimraf dist tsconfig.tsbuildinfo && tsc -b && chmod +x dist/index.js
|
|
31
|
-
"copy-docs": "mkdir -p dist/tools/documentation/uploads dist/scripts && cp -f src/tools/documentation/uploads/documents.json dist/tools/documentation/uploads/documents.json 2>/dev/null || true && cp -f src/tools/documentation/uploads/embeddings-*.json dist/tools/documentation/uploads/ 2>/dev/null || true && cp -f src/scripts/rag-eval-dataset.json dist/scripts/rag-eval-dataset.json 2>/dev/null || true",
|
|
30
|
+
"build": "rimraf dist tsconfig.tsbuildinfo && tsc -b && chmod +x dist/index.js",
|
|
32
31
|
"start": "node dist/index.js",
|
|
33
32
|
"start:stdio": "node dist/index.js",
|
|
34
33
|
"start:httpStream": "node dist/index.js --httpStream",
|
|
@@ -45,27 +44,17 @@
|
|
|
45
44
|
"format": "prettier --write \"src/**/*.{ts,js,json}\"",
|
|
46
45
|
"format:check": "prettier --check \"src/**/*.{ts,js,json}\"",
|
|
47
46
|
"check": "npm run lint && npm run format:check",
|
|
48
|
-
"
|
|
49
|
-
"query-docs": "node dist/scripts/simple-query-documentation.js",
|
|
50
|
-
"eval-docs": "node dist/scripts/eval-documentation-rag.js",
|
|
51
|
-
"generate-cache": "node dist/scripts/generate-embeddings-cache.js",
|
|
52
|
-
"prepublishOnly": "npm run build && npm run generate-cache",
|
|
47
|
+
"prepublishOnly": "npm run build",
|
|
53
48
|
"sync-version": "node scripts/sync-version.mjs",
|
|
54
|
-
"version": "npm run sync-version"
|
|
55
|
-
"zip-assets": "node scripts/zip-assets.mjs zip",
|
|
56
|
-
"unzip-assets": "node scripts/zip-assets.mjs unzip",
|
|
57
|
-
"preinstall": "node scripts/zip-assets.mjs unzip"
|
|
49
|
+
"version": "npm run sync-version"
|
|
58
50
|
},
|
|
59
51
|
"author": "",
|
|
60
52
|
"license": "Apache-2.0",
|
|
61
53
|
"description": "Intelligent MCP server providing AI assistants with powerful tools and resources for Appium mobile automation",
|
|
62
54
|
"dependencies": {
|
|
55
|
+
"@appium/mcp-documentation": "^1.0.1",
|
|
63
56
|
"@appium/support": "^7.0.2",
|
|
64
|
-
"@langchain/classic": "^1.0.10",
|
|
65
|
-
"@langchain/core": "^1.1.17",
|
|
66
|
-
"@langchain/textsplitters": "^1.0.1",
|
|
67
57
|
"@modelcontextprotocol/sdk": "^1.22.0",
|
|
68
|
-
"@xenova/transformers": "^2.17.2",
|
|
69
58
|
"@xmldom/xmldom": "^0.9.8",
|
|
70
59
|
"appium-adb": "^15.0.0",
|
|
71
60
|
"appium-ios-device": "^3.1.0",
|
|
@@ -73,18 +62,17 @@
|
|
|
73
62
|
"appium-webdriveragent": "^12.1.1",
|
|
74
63
|
"appium-xcuitest-driver": "^11.0.0",
|
|
75
64
|
"applesign": "^5.0.0",
|
|
76
|
-
"fast-xml-parser": "^5.2.3",
|
|
77
65
|
"fastmcp": "^4.0.0",
|
|
78
66
|
"ios-mobileprovision-finder": "^1.2.1",
|
|
79
|
-
"langchain": "^1.1.6",
|
|
80
67
|
"node-simctl": "^8.0.4",
|
|
81
|
-
"
|
|
68
|
+
"lru-cache": "^11.5.0",
|
|
82
69
|
"webdriver": "^9.23.0",
|
|
83
70
|
"xpath": "^0.0.34",
|
|
84
71
|
"zod": "^4.3.6"
|
|
85
72
|
},
|
|
86
73
|
"devDependencies": {
|
|
87
74
|
"@appium/eslint-config-appium-ts": "^3.0.0",
|
|
75
|
+
"@appium/tsconfig": "^1.1.2",
|
|
88
76
|
"@jest/globals": "^30.2.0",
|
|
89
77
|
"@semantic-release/changelog": "^6.0.3",
|
|
90
78
|
"@semantic-release/git": "^10.0.1",
|
|
@@ -95,6 +83,7 @@
|
|
|
95
83
|
"jest": "^30.2.0",
|
|
96
84
|
"lint-staged": "^17.0.2",
|
|
97
85
|
"prettier": "^3.5.3",
|
|
86
|
+
"rimraf": "^6.0.1",
|
|
98
87
|
"semantic-release": "^25.0.2",
|
|
99
88
|
"ts-node": "^10.9.2",
|
|
100
89
|
"typescript": "^6.0.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.
|
|
6
|
+
"version": "1.84.0",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "appium-mcp",
|
|
11
|
-
"version": "1.
|
|
11
|
+
"version": "1.84.0",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|
package/src/create-server.ts
CHANGED
|
@@ -103,6 +103,10 @@ export function createAppiumMcpServer(
|
|
|
103
103
|
name: serverName,
|
|
104
104
|
version: serverVersion,
|
|
105
105
|
instructions,
|
|
106
|
+
// Appium MCP will not allow client to ask the local workspace/project folders.
|
|
107
|
+
roots: {
|
|
108
|
+
enabled: false,
|
|
109
|
+
},
|
|
106
110
|
});
|
|
107
111
|
|
|
108
112
|
installPolicy(server, policy);
|
package/src/server.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import type { AppiumMcpPlugin } from './core.js';
|
|
1
2
|
import { createAppiumMcpServer } from './create-server.js';
|
|
2
|
-
import { AppiumDocumentation } from './tools/documentation/plugin.js';
|
|
3
3
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const plugins: AppiumMcpPlugin[] = [];
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
const { AppiumDocumentation } = await import('@appium/mcp-documentation');
|
|
8
|
+
plugins.push(new AppiumDocumentation());
|
|
9
|
+
} catch (_err) {}
|
|
10
|
+
|
|
11
|
+
const server = createAppiumMcpServer({ plugins });
|
|
7
12
|
export default server;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { ContentResult } from 'fastmcp';
|
|
3
|
+
import pkg from '../../package.json' with { type: 'json' };
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Structured, machine-readable trace of a single tool action, so CI and agents
|
|
7
|
+
* can diagnose failures without parsing prose.
|
|
8
|
+
*
|
|
9
|
+
* This is an *action* record. A future run-level bundle may reference many of
|
|
10
|
+
* these plus heavier artifacts (screenshots, page source).
|
|
11
|
+
*/
|
|
12
|
+
export interface ActionEvidenceRecord {
|
|
13
|
+
schemaVersion: 1;
|
|
14
|
+
producer: { name: 'appium-mcp'; version: string };
|
|
15
|
+
evidenceId: string;
|
|
16
|
+
status: 'success' | 'error';
|
|
17
|
+
/**
|
|
18
|
+
* Action-specific details. `stage`/`locator`/`element` are optional because
|
|
19
|
+
* they only apply to tools that resolve or interact with elements (find,
|
|
20
|
+
* gesture); tools like screenshot carry just `name`.
|
|
21
|
+
*/
|
|
22
|
+
action: {
|
|
23
|
+
name: string;
|
|
24
|
+
stage?: EvidenceStage;
|
|
25
|
+
locator?: { strategy: string; selector: string };
|
|
26
|
+
element?: { webdriverId: string };
|
|
27
|
+
};
|
|
28
|
+
context?: {
|
|
29
|
+
platform?: string;
|
|
30
|
+
contextName?: string;
|
|
31
|
+
};
|
|
32
|
+
timing: { startedAt: string; finishedAt: string; durationMs: number };
|
|
33
|
+
error?: { code: EvidenceErrorCode; message: string };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type EvidenceStage = 'locate' | 'interact' | 'capture';
|
|
37
|
+
|
|
38
|
+
export type EvidenceErrorCode =
|
|
39
|
+
| 'ELEMENT_NOT_FOUND'
|
|
40
|
+
| 'TIMEOUT'
|
|
41
|
+
| 'STALE_ELEMENT'
|
|
42
|
+
| 'CONTEXT_NOT_AVAILABLE'
|
|
43
|
+
| 'INVALID_SELECTOR'
|
|
44
|
+
| 'ACTION_FAILED';
|
|
45
|
+
|
|
46
|
+
const EVIDENCE_MIME_TYPE = 'application/vnd.appium.evidence+json';
|
|
47
|
+
|
|
48
|
+
/** Inputs a handler supplies to describe the action it performed. */
|
|
49
|
+
export interface EvidenceInput {
|
|
50
|
+
name: string;
|
|
51
|
+
startedAt: number;
|
|
52
|
+
stage?: EvidenceStage;
|
|
53
|
+
locator?: ActionEvidenceRecord['action']['locator'];
|
|
54
|
+
element?: ActionEvidenceRecord['action']['element'];
|
|
55
|
+
context?: ActionEvidenceRecord['context'];
|
|
56
|
+
error?: unknown;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* When disabled, evidence is never built or attached and tool
|
|
61
|
+
* responses are byte-for-byte unchanged.
|
|
62
|
+
*/
|
|
63
|
+
export function isEvidenceEnabled(): boolean {
|
|
64
|
+
const raw = process.env.APPIUM_MCP_EVIDENCE?.trim().toLowerCase();
|
|
65
|
+
return raw === '1' || raw === 'true';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Attach an evidence record to an existing tool result as a `resource` content
|
|
70
|
+
* block, leaving the original text untouched. No-op when evidence is disabled.
|
|
71
|
+
*/
|
|
72
|
+
export function withEvidence(
|
|
73
|
+
result: ContentResult,
|
|
74
|
+
input: EvidenceInput
|
|
75
|
+
): ContentResult {
|
|
76
|
+
if (!isEvidenceEnabled()) {
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
const record = buildRecord(result, input);
|
|
80
|
+
return {
|
|
81
|
+
...result,
|
|
82
|
+
content: [
|
|
83
|
+
...result.content,
|
|
84
|
+
{
|
|
85
|
+
type: 'resource',
|
|
86
|
+
resource: {
|
|
87
|
+
uri: `evidence://${record.evidenceId}`,
|
|
88
|
+
mimeType: EVIDENCE_MIME_TYPE,
|
|
89
|
+
text: JSON.stringify(record),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Snapshot the active session's platform and current context for the evidence
|
|
98
|
+
* record. Returns undefined when there is no session to read, so the field is
|
|
99
|
+
* simply omitted. The session-store import is deferred until evidence is
|
|
100
|
+
* enabled so the disabled path pulls in no driver dependencies.
|
|
101
|
+
*/
|
|
102
|
+
export async function evidenceContext(
|
|
103
|
+
sessionId?: string
|
|
104
|
+
): Promise<ActionEvidenceRecord['context'] | undefined> {
|
|
105
|
+
if (!isEvidenceEnabled()) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
const { getSessionInfo } = await import('../session-store.js');
|
|
109
|
+
const info = getSessionInfo(sessionId);
|
|
110
|
+
if (!info) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
...(info.metadata.platform ? { platform: info.metadata.platform } : {}),
|
|
115
|
+
...(info.currentContext ? { contextName: info.currentContext } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Map a raw Appium/WebDriver error to a stable, normalized code. The codes are
|
|
121
|
+
* also the foundation for failure-reason hypotheses. Inspects the error name
|
|
122
|
+
* first, then the message, falling back to ACTION_FAILED.
|
|
123
|
+
*/
|
|
124
|
+
export function classifyError(err: unknown): EvidenceErrorCode {
|
|
125
|
+
const name = err instanceof Error ? err.name : '';
|
|
126
|
+
const message = (err instanceof Error ? err.message : String(err)) ?? '';
|
|
127
|
+
const haystack = `${name} ${message}`.toLowerCase();
|
|
128
|
+
|
|
129
|
+
if (/no such element|could not be located|element not found/.test(haystack)) {
|
|
130
|
+
return 'ELEMENT_NOT_FOUND';
|
|
131
|
+
}
|
|
132
|
+
if (/stale element/.test(haystack)) {
|
|
133
|
+
return 'STALE_ELEMENT';
|
|
134
|
+
}
|
|
135
|
+
if (/timed? ?out|timeout/.test(haystack)) {
|
|
136
|
+
return 'TIMEOUT';
|
|
137
|
+
}
|
|
138
|
+
if (/no such context|context.*not.*(found|available)/.test(haystack)) {
|
|
139
|
+
return 'CONTEXT_NOT_AVAILABLE';
|
|
140
|
+
}
|
|
141
|
+
if (/invalid selector|invalid.*(locator|xpath)/.test(haystack)) {
|
|
142
|
+
return 'INVALID_SELECTOR';
|
|
143
|
+
}
|
|
144
|
+
return 'ACTION_FAILED';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function buildRecord(
|
|
148
|
+
result: ContentResult,
|
|
149
|
+
input: EvidenceInput
|
|
150
|
+
): ActionEvidenceRecord {
|
|
151
|
+
const finished = Date.now();
|
|
152
|
+
const status: ActionEvidenceRecord['status'] =
|
|
153
|
+
result.isError || input.error !== undefined ? 'error' : 'success';
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
schemaVersion: 1,
|
|
157
|
+
producer: { name: 'appium-mcp', version: pkg.version },
|
|
158
|
+
evidenceId: randomUUID(),
|
|
159
|
+
status,
|
|
160
|
+
action: {
|
|
161
|
+
name: input.name,
|
|
162
|
+
...(input.stage ? { stage: input.stage } : {}),
|
|
163
|
+
...(input.locator ? { locator: input.locator } : {}),
|
|
164
|
+
...(input.element ? { element: input.element } : {}),
|
|
165
|
+
},
|
|
166
|
+
...(input.context ? { context: input.context } : {}),
|
|
167
|
+
timing: {
|
|
168
|
+
startedAt: new Date(input.startedAt).toISOString(),
|
|
169
|
+
finishedAt: new Date(finished).toISOString(),
|
|
170
|
+
durationMs: finished - input.startedAt,
|
|
171
|
+
},
|
|
172
|
+
...(status === 'error' ? { error: buildError(input.error, result) } : {}),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Resolve the error message, then classify from the same source so the code
|
|
178
|
+
* and message always agree — handlers that swallow errors only expose the
|
|
179
|
+
* result text, so fall back to that when no error object is passed.
|
|
180
|
+
*/
|
|
181
|
+
function buildError(
|
|
182
|
+
err: unknown,
|
|
183
|
+
result: ContentResult
|
|
184
|
+
): NonNullable<ActionEvidenceRecord['error']> {
|
|
185
|
+
const message = errorMessage(err, result);
|
|
186
|
+
return { code: classifyError(err ?? message), message };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function errorMessage(err: unknown, result: ContentResult): string {
|
|
190
|
+
if (err instanceof Error) {
|
|
191
|
+
return err.message;
|
|
192
|
+
}
|
|
193
|
+
if (err !== undefined) {
|
|
194
|
+
return String(err);
|
|
195
|
+
}
|
|
196
|
+
const text = result.content.find((c) => c.type === 'text');
|
|
197
|
+
return text && 'text' in text ? text.text : 'Unknown error';
|
|
198
|
+
}
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import type { ContentResult, FastMCP } from 'fastmcp';
|
|
2
|
+
import type { DriverInstance } from '../../session-store.js';
|
|
2
3
|
import {
|
|
3
4
|
errorResult,
|
|
4
5
|
resolveDriver,
|
|
5
6
|
textResult,
|
|
6
7
|
toolErrorMessage,
|
|
7
8
|
} from '../tool-response.js';
|
|
8
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
GESTURE_ACTIONS,
|
|
11
|
+
gestureSchema,
|
|
12
|
+
type GestureAction,
|
|
13
|
+
type GestureArgs,
|
|
14
|
+
} from './schema.js';
|
|
9
15
|
import { handleTap, handleDoubleTap, handleLongPress } from './handlers/tap.js';
|
|
10
16
|
import { handleScroll, handleSwipe } from './handlers/swipe-scroll.js';
|
|
11
17
|
import { handlePinchZoom } from './handlers/pinch.js';
|
|
12
18
|
import { handleScrollToElement } from './handlers/scroll-to-element.js';
|
|
13
19
|
import { back } from '../../command.js';
|
|
20
|
+
import {
|
|
21
|
+
withEvidence,
|
|
22
|
+
evidenceContext,
|
|
23
|
+
type EvidenceStage,
|
|
24
|
+
} from '../evidence.js';
|
|
25
|
+
import { isAiElementUUID } from './handlers/ai-element.js';
|
|
14
26
|
|
|
15
27
|
export default function gesture(server: FastMCP): void {
|
|
16
28
|
server.addTool({
|
|
@@ -35,31 +47,56 @@ export default function gesture(server: FastMCP): void {
|
|
|
35
47
|
}
|
|
36
48
|
const { driver } = resolved;
|
|
37
49
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
return handleLongPress(driver, args);
|
|
54
|
-
case 'scroll':
|
|
55
|
-
return handleScroll(driver, args);
|
|
56
|
-
case 'swipe':
|
|
57
|
-
return handleSwipe(driver, args);
|
|
58
|
-
case 'pinch_zoom':
|
|
59
|
-
return handlePinchZoom(driver, args);
|
|
60
|
-
case 'scroll_to_element':
|
|
61
|
-
return handleScrollToElement(driver, args);
|
|
62
|
-
}
|
|
50
|
+
const startedAt = Date.now();
|
|
51
|
+
const context = await evidenceContext(args.sessionId);
|
|
52
|
+
const result = await dispatchGesture(driver, args);
|
|
53
|
+
return withEvidence(result, {
|
|
54
|
+
name: 'appium_gesture',
|
|
55
|
+
stage: gestureStage(args.action),
|
|
56
|
+
startedAt,
|
|
57
|
+
context,
|
|
58
|
+
...(args.strategy && args.selector
|
|
59
|
+
? { locator: { strategy: args.strategy, selector: args.selector } }
|
|
60
|
+
: {}),
|
|
61
|
+
...(args.elementUUID && !isAiElementUUID(args.elementUUID)
|
|
62
|
+
? { element: { webdriverId: args.elementUUID } }
|
|
63
|
+
: {}),
|
|
64
|
+
});
|
|
63
65
|
},
|
|
64
66
|
});
|
|
65
67
|
}
|
|
68
|
+
|
|
69
|
+
async function dispatchGesture(
|
|
70
|
+
driver: DriverInstance,
|
|
71
|
+
args: GestureArgs
|
|
72
|
+
): Promise<ContentResult> {
|
|
73
|
+
switch (args.action) {
|
|
74
|
+
case 'back':
|
|
75
|
+
try {
|
|
76
|
+
await back(driver);
|
|
77
|
+
return textResult('Successfully performed back action.');
|
|
78
|
+
} catch (err: unknown) {
|
|
79
|
+
return errorResult(
|
|
80
|
+
`Failed to perform back action. ${toolErrorMessage(err)}`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
case 'tap':
|
|
84
|
+
return handleTap(driver, args);
|
|
85
|
+
case 'double_tap':
|
|
86
|
+
return handleDoubleTap(driver, args);
|
|
87
|
+
case 'long_press':
|
|
88
|
+
return handleLongPress(driver, args);
|
|
89
|
+
case 'scroll':
|
|
90
|
+
return handleScroll(driver, args);
|
|
91
|
+
case 'swipe':
|
|
92
|
+
return handleSwipe(driver, args);
|
|
93
|
+
case 'pinch_zoom':
|
|
94
|
+
return handlePinchZoom(driver, args);
|
|
95
|
+
case 'scroll_to_element':
|
|
96
|
+
return handleScrollToElement(driver, args);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function gestureStage(action: GestureAction): EvidenceStage {
|
|
101
|
+
return action === 'scroll_to_element' ? 'locate' : 'interact';
|
|
102
|
+
}
|