@wa008/ui-audit-mcp 1.0.2

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 (130) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +75 -0
  3. package/dist/device/adapter.d.ts +45 -0
  4. package/dist/device/adapter.d.ts.map +1 -0
  5. package/dist/device/adapter.js +137 -0
  6. package/dist/device/adapter.js.map +1 -0
  7. package/dist/evaluation/checklist.d.ts +14 -0
  8. package/dist/evaluation/checklist.d.ts.map +1 -0
  9. package/dist/evaluation/checklist.js +57 -0
  10. package/dist/evaluation/checklist.js.map +1 -0
  11. package/dist/evaluation/scorer.d.ts +7 -0
  12. package/dist/evaluation/scorer.d.ts.map +1 -0
  13. package/dist/evaluation/scorer.js +35 -0
  14. package/dist/evaluation/scorer.js.map +1 -0
  15. package/dist/examples/agent-demo.d.ts +2 -0
  16. package/dist/examples/agent-demo.d.ts.map +1 -0
  17. package/dist/examples/agent-demo.js +79 -0
  18. package/dist/examples/agent-demo.js.map +1 -0
  19. package/dist/index.d.ts +23 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +75 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/logger/evaluation-logger.d.ts +20 -0
  24. package/dist/logger/evaluation-logger.d.ts.map +1 -0
  25. package/dist/logger/evaluation-logger.js +93 -0
  26. package/dist/logger/evaluation-logger.js.map +1 -0
  27. package/dist/maestro/adapter.d.ts +37 -0
  28. package/dist/maestro/adapter.d.ts.map +1 -0
  29. package/dist/maestro/adapter.js +139 -0
  30. package/dist/maestro/adapter.js.map +1 -0
  31. package/dist/src/device/adapter.d.ts +45 -0
  32. package/dist/src/device/adapter.d.ts.map +1 -0
  33. package/dist/src/device/adapter.js +137 -0
  34. package/dist/src/device/adapter.js.map +1 -0
  35. package/dist/src/evaluation/checklist.d.ts +14 -0
  36. package/dist/src/evaluation/checklist.d.ts.map +1 -0
  37. package/dist/src/evaluation/checklist.js +57 -0
  38. package/dist/src/evaluation/checklist.js.map +1 -0
  39. package/dist/src/evaluation/scorer.d.ts +7 -0
  40. package/dist/src/evaluation/scorer.d.ts.map +1 -0
  41. package/dist/src/evaluation/scorer.js +35 -0
  42. package/dist/src/evaluation/scorer.js.map +1 -0
  43. package/dist/src/index.d.ts +23 -0
  44. package/dist/src/index.d.ts.map +1 -0
  45. package/dist/src/index.js +78 -0
  46. package/dist/src/index.js.map +1 -0
  47. package/dist/src/logger/evaluation-logger.d.ts +20 -0
  48. package/dist/src/logger/evaluation-logger.d.ts.map +1 -0
  49. package/dist/src/logger/evaluation-logger.js +93 -0
  50. package/dist/src/logger/evaluation-logger.js.map +1 -0
  51. package/dist/src/tools/evaluate-style.d.ts +30 -0
  52. package/dist/src/tools/evaluate-style.d.ts.map +1 -0
  53. package/dist/src/tools/evaluate-style.js +57 -0
  54. package/dist/src/tools/evaluate-style.js.map +1 -0
  55. package/dist/src/tools/get-checklist.d.ts +27 -0
  56. package/dist/src/tools/get-checklist.d.ts.map +1 -0
  57. package/dist/src/tools/get-checklist.js +57 -0
  58. package/dist/src/tools/get-checklist.js.map +1 -0
  59. package/dist/src/tools/get-log.d.ts +25 -0
  60. package/dist/src/tools/get-log.d.ts.map +1 -0
  61. package/dist/src/tools/get-log.js +33 -0
  62. package/dist/src/tools/get-log.js.map +1 -0
  63. package/dist/src/tools/launch-app.d.ts +19 -0
  64. package/dist/src/tools/launch-app.d.ts.map +1 -0
  65. package/dist/src/tools/launch-app.js +24 -0
  66. package/dist/src/tools/launch-app.js.map +1 -0
  67. package/dist/src/tools/list-apps.d.ts +28 -0
  68. package/dist/src/tools/list-apps.d.ts.map +1 -0
  69. package/dist/src/tools/list-apps.js +75 -0
  70. package/dist/src/tools/list-apps.js.map +1 -0
  71. package/dist/src/tools/submit-evaluation.d.ts +57 -0
  72. package/dist/src/tools/submit-evaluation.d.ts.map +1 -0
  73. package/dist/src/tools/submit-evaluation.js +107 -0
  74. package/dist/src/tools/submit-evaluation.js.map +1 -0
  75. package/dist/src/tools/swipe.d.ts +28 -0
  76. package/dist/src/tools/swipe.d.ts.map +1 -0
  77. package/dist/src/tools/swipe.js +26 -0
  78. package/dist/src/tools/swipe.js.map +1 -0
  79. package/dist/src/tools/take-screenshot.d.ts +26 -0
  80. package/dist/src/tools/take-screenshot.d.ts.map +1 -0
  81. package/dist/src/tools/take-screenshot.js +32 -0
  82. package/dist/src/tools/take-screenshot.js.map +1 -0
  83. package/dist/src/tools/tap.d.ts +22 -0
  84. package/dist/src/tools/tap.d.ts.map +1 -0
  85. package/dist/src/tools/tap.js +24 -0
  86. package/dist/src/tools/tap.js.map +1 -0
  87. package/dist/src/types.d.ts +57 -0
  88. package/dist/src/types.d.ts.map +1 -0
  89. package/dist/src/types.js +5 -0
  90. package/dist/src/types.js.map +1 -0
  91. package/dist/test-agent.d.ts +2 -0
  92. package/dist/test-agent.d.ts.map +1 -0
  93. package/dist/test-agent.js.map +1 -0
  94. package/dist/tools/evaluate-style.d.ts +30 -0
  95. package/dist/tools/evaluate-style.d.ts.map +1 -0
  96. package/dist/tools/evaluate-style.js +57 -0
  97. package/dist/tools/evaluate-style.js.map +1 -0
  98. package/dist/tools/get-checklist.d.ts +27 -0
  99. package/dist/tools/get-checklist.d.ts.map +1 -0
  100. package/dist/tools/get-checklist.js +57 -0
  101. package/dist/tools/get-checklist.js.map +1 -0
  102. package/dist/tools/get-log.d.ts +25 -0
  103. package/dist/tools/get-log.d.ts.map +1 -0
  104. package/dist/tools/get-log.js +33 -0
  105. package/dist/tools/get-log.js.map +1 -0
  106. package/dist/tools/launch-app.d.ts +19 -0
  107. package/dist/tools/launch-app.d.ts.map +1 -0
  108. package/dist/tools/launch-app.js +24 -0
  109. package/dist/tools/launch-app.js.map +1 -0
  110. package/dist/tools/submit-evaluation.d.ts +57 -0
  111. package/dist/tools/submit-evaluation.d.ts.map +1 -0
  112. package/dist/tools/submit-evaluation.js +107 -0
  113. package/dist/tools/submit-evaluation.js.map +1 -0
  114. package/dist/tools/swipe.d.ts +28 -0
  115. package/dist/tools/swipe.d.ts.map +1 -0
  116. package/dist/tools/swipe.js +26 -0
  117. package/dist/tools/swipe.js.map +1 -0
  118. package/dist/tools/take-screenshot.d.ts +26 -0
  119. package/dist/tools/take-screenshot.d.ts.map +1 -0
  120. package/dist/tools/take-screenshot.js +32 -0
  121. package/dist/tools/take-screenshot.js.map +1 -0
  122. package/dist/tools/tap.d.ts +22 -0
  123. package/dist/tools/tap.d.ts.map +1 -0
  124. package/dist/tools/tap.js +24 -0
  125. package/dist/tools/tap.js.map +1 -0
  126. package/dist/types.d.ts +57 -0
  127. package/dist/types.d.ts.map +1 -0
  128. package/dist/types.js +5 -0
  129. package/dist/types.js.map +1 -0
  130. package/package.json +44 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wa008
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # UI Audit MCP
2
+
3
+ An MCP server for iOS app UI evaluation and testing.
4
+
5
+ ## Features
6
+ - **Device Control**: Launch apps, take screenshots, tap, and swipe on iOS Simulators.
7
+ - **UI Evaluation**: Structured checklists for quality and style consistency.
8
+
9
+ ## Prerequisites
10
+ - **Node.js 18+**
11
+ - **Xcode CLI Tools**: `xcode-select --install` (for `xcrun simctl`)
12
+ - **idb**: Required for tap/swipe.
13
+ ```bash
14
+ brew install idb-companion
15
+ pip3 install fb-idb
16
+ ```
17
+
18
+ ## Quick Install (via npx)
19
+ Add this to your MCP client config (e.g., `claude_desktop_config.json`):
20
+ ```json
21
+ {
22
+ "mcpServers": {
23
+ "ui-audit": {
24
+ "command": "npx",
25
+ "args": ["-y", "@wa007/ui-audit-mcp"]
26
+ }
27
+ }
28
+ }
29
+ ```
30
+ *(Note: Requires the package to be published to NPM. For local development, use the Setup steps below.)*
31
+
32
+ ## Manual Setup
33
+ ```bash
34
+ git clone https://github.com/wa008/ui-audit-mcp.git
35
+ cd ui-audit-mcp
36
+ npm install
37
+ ```
38
+
39
+ ### Manual Config
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "ui-audit": {
44
+ "command": "node",
45
+ "args": ["/absolute/path/to/ui-audit-mcp/dist/src/index.js"]
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## Tools
52
+ | Tool | Purpose |
53
+ |---|---|
54
+ | `launch_app` | Launch app by Bundle ID |
55
+ | `list_apps` | keys to find Bundle IDs |
56
+ | `take_screenshot` | Capture screen & metadata |
57
+ | `tap` / `swipe` | Interact with UI (ratio coordinates 0-1) |
58
+ | `get_checklist` | Start evaluation session (screen/style) |
59
+ | `submit_evaluation` | Submit scores (1-5) & get pass/fail result |
60
+ | `evaluate_style_consistency` | Compare multiple screens |
61
+ | `get_evaluation_log` | Review past results |
62
+
63
+ ## Typical Workflow
64
+ 1. `list_apps(all: false)` → Find Bundle ID
65
+ 2. `launch_app("com.example.app")`
66
+ 3. `take_screenshot()`
67
+ 4. `get_checklist(type: "screen")` → Get session ID
68
+ 5. Agent analyzes UI → `submit_evaluation(sessionId, scores)`
69
+
70
+ ## Data
71
+ Logs: `~/.ui-audit-mcp/logs/`
72
+ Screenshots: `~/.ui-audit-mcp/screenshots/`
73
+
74
+ ## License
75
+ MIT
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Device adapter using idb + xcrun simctl.
3
+ *
4
+ * Dependencies:
5
+ * - xcrun simctl (ships with Xcode CLI tools) → launch app, screenshot
6
+ * - idb (Facebook's iOS Development Bridge) → tap, swipe
7
+ *
8
+ * Install idb:
9
+ * brew install idb-companion
10
+ * pip3 install fb-idb
11
+ *
12
+ * Coordinates use the iOS points system. The adapter accepts ratio
13
+ * coordinates (0-1) and converts them using the screen dimensions
14
+ * obtained from the screenshot.
15
+ */
16
+ /** Check that required CLI tools are available. */
17
+ export declare function preflight(): Promise<{
18
+ ok: boolean;
19
+ missing: string[];
20
+ }>;
21
+ /** Launch an app by bundle ID on the booted simulator. */
22
+ export declare function launchApp(appId: string): Promise<{
23
+ success: boolean;
24
+ error?: string;
25
+ }>;
26
+ /** Take a screenshot and return base64 PNG + dimensions. */
27
+ export declare function takeScreenshot(): Promise<{
28
+ imageBase64: string;
29
+ width: number;
30
+ height: number;
31
+ filePath: string;
32
+ }>;
33
+ /** Tap at ratio coordinates (0-1). Converts to points using screen size. */
34
+ export declare function tap(ratioX: number, ratioY: number): Promise<{
35
+ success: boolean;
36
+ pointX: number;
37
+ pointY: number;
38
+ error?: string;
39
+ }>;
40
+ /** Swipe using ratio coordinates (0-1). */
41
+ export declare function swipe(startX: number, startY: number, endX: number, endY: number): Promise<{
42
+ success: boolean;
43
+ error?: string;
44
+ }>;
45
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/device/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA+BH,mDAAmD;AACnD,wBAAsB,SAAS,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAa7E;AAID,0DAA0D;AAC1D,wBAAsB,SAAS,CAC3B,KAAK,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAQ/C;AAED,4DAA4D;AAC5D,wBAAsB,cAAc,IAAI,OAAO,CAAC;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC,CAcD;AAED,4EAA4E;AAC5E,wBAAsB,GAAG,CACrB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAe/E;AAED,2CAA2C;AAC3C,wBAAsB,KAAK,CACvB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmB/C"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Device adapter using idb + xcrun simctl.
3
+ *
4
+ * Dependencies:
5
+ * - xcrun simctl (ships with Xcode CLI tools) → launch app, screenshot
6
+ * - idb (Facebook's iOS Development Bridge) → tap, swipe
7
+ *
8
+ * Install idb:
9
+ * brew install idb-companion
10
+ * pip3 install fb-idb
11
+ *
12
+ * Coordinates use the iOS points system. The adapter accepts ratio
13
+ * coordinates (0-1) and converts them using the screen dimensions
14
+ * obtained from the screenshot.
15
+ */
16
+ import * as fs from "node:fs";
17
+ import * as path from "node:path";
18
+ import * as os from "node:os";
19
+ import { execFile } from "node:child_process";
20
+ import { promisify } from "node:util";
21
+ import { randomUUID } from "node:crypto";
22
+ const execFileAsync = promisify(execFile);
23
+ const SCREENSHOT_DIR = path.join(os.homedir(), ".ui-critic-mcp", "screenshots");
24
+ function ensureDir(dir) {
25
+ if (!fs.existsSync(dir))
26
+ fs.mkdirSync(dir, { recursive: true });
27
+ }
28
+ /** Read PNG width and height from the IHDR chunk (bytes 16-23). */
29
+ function readPngDimensions(filePath) {
30
+ const buf = Buffer.alloc(24);
31
+ const fd = fs.openSync(filePath, "r");
32
+ fs.readSync(fd, buf, 0, 24, 0);
33
+ fs.closeSync(fd);
34
+ return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
35
+ }
36
+ const IDB_INSTALL_HINT = "idb is required for tap/swipe. Install it with:\n" +
37
+ " brew install idb-companion\n" +
38
+ " pip3 install fb-idb\n" +
39
+ "See https://fbidb.io for details.";
40
+ /** Check that required CLI tools are available. */
41
+ export async function preflight() {
42
+ const missing = [];
43
+ try {
44
+ await execFileAsync("xcrun", ["simctl", "list", "devices", "booted"]);
45
+ }
46
+ catch {
47
+ missing.push("xcrun simctl (install Xcode Command Line Tools)");
48
+ }
49
+ try {
50
+ await execFileAsync("idb", ["--help"]);
51
+ }
52
+ catch {
53
+ missing.push(`idb (${IDB_INSTALL_HINT})`);
54
+ }
55
+ return { ok: missing.length === 0, missing };
56
+ }
57
+ // ─── Public API ──────────────────────────────────────────
58
+ /** Launch an app by bundle ID on the booted simulator. */
59
+ export async function launchApp(appId) {
60
+ try {
61
+ await execFileAsync("xcrun", ["simctl", "launch", "booted", appId]);
62
+ return { success: true };
63
+ }
64
+ catch (err) {
65
+ const e = err;
66
+ return { success: false, error: e.stderr ?? e.message ?? String(err) };
67
+ }
68
+ }
69
+ /** Take a screenshot and return base64 PNG + dimensions. */
70
+ export async function takeScreenshot() {
71
+ ensureDir(SCREENSHOT_DIR);
72
+ const filePath = path.join(SCREENSHOT_DIR, `${randomUUID()}.png`);
73
+ try {
74
+ await execFileAsync("xcrun", ["simctl", "io", "booted", "screenshot", filePath]);
75
+ }
76
+ catch (err) {
77
+ const e = err;
78
+ throw new Error(`Screenshot failed: ${e.stderr ?? e.message}`);
79
+ }
80
+ const { width, height } = readPngDimensions(filePath);
81
+ const imageBase64 = fs.readFileSync(filePath).toString("base64");
82
+ return { imageBase64, width, height, filePath };
83
+ }
84
+ /** Tap at ratio coordinates (0-1). Converts to points using screen size. */
85
+ export async function tap(ratioX, ratioY) {
86
+ const { width, height } = await getScreenSize();
87
+ const pointX = Math.round(ratioX * width);
88
+ const pointY = Math.round(ratioY * height);
89
+ try {
90
+ await execFileAsync("idb", ["ui", "tap", String(pointX), String(pointY)]);
91
+ return { success: true, pointX, pointY };
92
+ }
93
+ catch (err) {
94
+ const e = err;
95
+ if (e.code === "ENOENT") {
96
+ return { success: false, pointX, pointY, error: IDB_INSTALL_HINT };
97
+ }
98
+ return { success: false, pointX, pointY, error: e.stderr ?? e.message ?? String(err) };
99
+ }
100
+ }
101
+ /** Swipe using ratio coordinates (0-1). */
102
+ export async function swipe(startX, startY, endX, endY) {
103
+ const { width, height } = await getScreenSize();
104
+ const x1 = Math.round(startX * width);
105
+ const y1 = Math.round(startY * height);
106
+ const x2 = Math.round(endX * width);
107
+ const y2 = Math.round(endY * height);
108
+ try {
109
+ await execFileAsync("idb", [
110
+ "ui", "swipe", String(x1), String(y1), String(x2), String(y2),
111
+ ]);
112
+ return { success: true };
113
+ }
114
+ catch (err) {
115
+ const e = err;
116
+ if (e.code === "ENOENT") {
117
+ return { success: false, error: IDB_INSTALL_HINT };
118
+ }
119
+ return { success: false, error: e.stderr ?? e.message ?? String(err) };
120
+ }
121
+ }
122
+ // ─── Internal ────────────────────────────────────────────
123
+ let cachedScreenSize = null;
124
+ /** Get screen size by taking a quick screenshot and reading its dimensions. */
125
+ async function getScreenSize() {
126
+ if (cachedScreenSize)
127
+ return cachedScreenSize;
128
+ const result = await takeScreenshot();
129
+ cachedScreenSize = { width: result.width, height: result.height };
130
+ // Clean up the temp screenshot — it was only needed for dimensions
131
+ try {
132
+ fs.unlinkSync(result.filePath);
133
+ }
134
+ catch { /* ignore */ }
135
+ return cachedScreenSize;
136
+ }
137
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/device/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC;AAEhF,SAAS,SAAS,CAAC,GAAW;IAC1B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,mEAAmE;AACnE,SAAS,iBAAiB,CAAC,QAAgB;IACvC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACtC,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC/B,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC;AACzE,CAAC;AAED,MAAM,gBAAgB,GAClB,mDAAmD;IACnD,gCAAgC;IAChC,yBAAyB;IACzB,mCAAmC,CAAC;AAExC,mDAAmD;AACnD,MAAM,CAAC,KAAK,UAAU,SAAS;IAC3B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC;QACD,MAAM,aAAa,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,CAAC;QACD,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC3C,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,CAAC,IAAI,CAAC,QAAQ,gBAAgB,GAAG,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;AACjD,CAAC;AAED,4DAA4D;AAE5D,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,SAAS,CAC3B,KAAa;IAEb,IAAI,CAAC;QACD,MAAM,aAAa,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QACpE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,GAA4C,CAAC;QACvD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3E,CAAC;AACL,CAAC;AAED,4DAA4D;AAC5D,MAAM,CAAC,KAAK,UAAU,cAAc;IAMhC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,UAAU,EAAE,MAAM,CAAC,CAAC;IAElE,IAAI,CAAC;QACD,MAAM,aAAa,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrF,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,GAA4C,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACjE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AACpD,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,KAAK,UAAU,GAAG,CACrB,MAAc,EACd,MAAc;IAEd,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,EAAE,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IAE3C,IAAI,CAAC;QACD,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,GAA2D,CAAC;QACtE,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;QACvE,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3F,CAAC;AACL,CAAC;AAED,2CAA2C;AAC3C,MAAM,CAAC,KAAK,UAAU,KAAK,CACvB,MAAc,EACd,MAAc,EACd,IAAY,EACZ,IAAY;IAEZ,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,EAAE,CAAC;IAChD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;IACtC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IAErC,IAAI,CAAC;QACD,MAAM,aAAa,CAAC,KAAK,EAAE;YACvB,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,CAAC;SAChE,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,CAAC,GAAG,GAA2D,CAAC;QACtE,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC;QACvD,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3E,CAAC;AACL,CAAC;AAED,4DAA4D;AAE5D,IAAI,gBAAgB,GAA6C,IAAI,CAAC;AAEtE,+EAA+E;AAC/E,KAAK,UAAU,aAAa;IACxB,IAAI,gBAAgB;QAAE,OAAO,gBAAgB,CAAC;IAC9C,MAAM,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;IACtC,gBAAgB,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAClE,mEAAmE;IACnE,IAAI,CAAC;QAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAC9D,OAAO,gBAAgB,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Evaluation checklist definitions.
3
+ *
4
+ * Screen evaluation: 4 items (overlap, layout, info clarity, ambiguity)
5
+ * Style consistency: 3 items (color, component, typography)
6
+ */
7
+ import { ChecklistItem } from "../types.js";
8
+ /** Single-screen UI quality checklist */
9
+ export declare const SCREEN_CHECKLIST: ChecklistItem[];
10
+ /** Multi-screen style-consistency checklist */
11
+ export declare const STYLE_CHECKLIST: ChecklistItem[];
12
+ /** Default passing score for each checklist item (on a 1-5 scale) */
13
+ export declare const DEFAULT_PASSING_SCORE = 3;
14
+ //# sourceMappingURL=checklist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checklist.d.ts","sourceRoot":"","sources":["../../src/evaluation/checklist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,EAAE,aAAa,EAiC3C,CAAC;AAEF,+CAA+C;AAC/C,eAAO,MAAM,eAAe,EAAE,aAAa,EAyB1C,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,IAAI,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Evaluation checklist definitions.
3
+ *
4
+ * Screen evaluation: 4 items (overlap, layout, info clarity, ambiguity)
5
+ * Style consistency: 3 items (color, component, typography)
6
+ */
7
+ /** Single-screen UI quality checklist */
8
+ export const SCREEN_CHECKLIST = [
9
+ {
10
+ id: "overlap",
11
+ name: "Element Overlap Detection",
12
+ description: "Check whether any UI elements (text, buttons, images, icons) overlap or obscure each other on the current screen.",
13
+ scoringGuide: "1 = severe overlap affecting usability, 2 = noticeable overlap, 3 = minor overlap, 4 = almost no overlap, 5 = no overlap at all",
14
+ },
15
+ {
16
+ id: "layout",
17
+ name: "Layout & Alignment",
18
+ description: "Check whether button sizes are reasonable, elements are properly aligned, spacing is consistent, and the overall layout is harmonious.",
19
+ scoringGuide: "1 = chaotic layout, 2 = obvious layout issues, 3 = acceptable with minor flaws, 4 = good layout, 5 = polished layout",
20
+ },
21
+ {
22
+ id: "info_clarity",
23
+ name: "Information Clarity",
24
+ description: "Check whether the user can quickly identify key information on this screen, whether the information hierarchy is clear, and whether the primary call-to-action (CTA) is prominent.",
25
+ scoringGuide: "1 = key information is impossible to find, 2 = confusing, 3 = readable but not ideal, 4 = clear, 5 = excellent hierarchy",
26
+ },
27
+ {
28
+ id: "ambiguity",
29
+ name: "Expression Ambiguity",
30
+ description: "Check whether any copy, icons, or button labels are ambiguous or misleading, and whether the intended action is clearly communicated to the user.",
31
+ scoringGuide: "1 = severely ambiguous / misleading, 2 = multiple ambiguities, 3 = minor ambiguity, 4 = clear, 5 = precise and unambiguous",
32
+ },
33
+ ];
34
+ /** Multi-screen style-consistency checklist */
35
+ export const STYLE_CHECKLIST = [
36
+ {
37
+ id: "color_consistency",
38
+ name: "Color Scheme Consistency",
39
+ description: "Compare all provided screenshots and check whether the primary colors, accent colors, and background colors are consistent across screens.",
40
+ scoringGuide: "1 = completely different styles, 2 = noticeable inconsistencies, 3 = mostly consistent, 4 = well unified, 5 = perfectly unified",
41
+ },
42
+ {
43
+ id: "component_consistency",
44
+ name: "Component Style Consistency",
45
+ description: "Compare buttons, navigation bars, cards, and other reusable components across all screenshots to verify they share the same visual style (corner radius, shadow, spacing, etc.).",
46
+ scoringGuide: "1 = every screen looks different, 2 = noticeable differences, 3 = mostly consistent, 4 = well unified, 5 = strict design system",
47
+ },
48
+ {
49
+ id: "typography_consistency",
50
+ name: "Typography Consistency",
51
+ description: "Compare heading sizes, body text sizes, font weights, and line spacing across all screenshots to verify consistency.",
52
+ scoringGuide: "1 = chaotic typography, 2 = noticeable differences, 3 = mostly consistent, 4 = well unified, 5 = strictly unified",
53
+ },
54
+ ];
55
+ /** Default passing score for each checklist item (on a 1-5 scale) */
56
+ export const DEFAULT_PASSING_SCORE = 3;
57
+ //# sourceMappingURL=checklist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checklist.js","sourceRoot":"","sources":["../../src/evaluation/checklist.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,yCAAyC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAoB;IAC7C;QACI,EAAE,EAAE,SAAS;QACb,IAAI,EAAE,2BAA2B;QACjC,WAAW,EACP,mHAAmH;QACvH,YAAY,EACR,iIAAiI;KACxI;IACD;QACI,EAAE,EAAE,QAAQ;QACZ,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EACP,wIAAwI;QAC5I,YAAY,EACR,sHAAsH;KAC7H;IACD;QACI,EAAE,EAAE,cAAc;QAClB,IAAI,EAAE,qBAAqB;QAC3B,WAAW,EACP,oLAAoL;QACxL,YAAY,EACR,0HAA0H;KACjI;IACD;QACI,EAAE,EAAE,WAAW;QACf,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EACP,mJAAmJ;QACvJ,YAAY,EACR,4HAA4H;KACnI;CACJ,CAAC;AAEF,+CAA+C;AAC/C,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC5C;QACI,EAAE,EAAE,mBAAmB;QACvB,IAAI,EAAE,0BAA0B;QAChC,WAAW,EACP,4IAA4I;QAChJ,YAAY,EACR,iIAAiI;KACxI;IACD;QACI,EAAE,EAAE,uBAAuB;QAC3B,IAAI,EAAE,6BAA6B;QACnC,WAAW,EACP,kLAAkL;QACtL,YAAY,EACR,iIAAiI;KACxI;IACD;QACI,EAAE,EAAE,wBAAwB;QAC5B,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EACP,sHAAsH;QAC1H,YAAY,EACR,mHAAmH;KAC1H;CACJ,CAAC;AAEF,qEAAqE;AACrE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Scoring logic — determines pass / fail for each checklist item
3
+ * and computes the overall evaluation outcome.
4
+ */
5
+ import { ChecklistItem, EvaluationOutcome, EvaluationType, ScoreEntry } from "../types.js";
6
+ export declare function evaluateScores(sessionId: string, type: EvaluationType, screenName: string, checklist: ChecklistItem[], scores: ScoreEntry[], passingScore: number, attemptNumber: number): EvaluationOutcome;
7
+ //# sourceMappingURL=scorer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scorer.d.ts","sourceRoot":"","sources":["../../src/evaluation/scorer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACH,aAAa,EACb,iBAAiB,EAEjB,cAAc,EACd,UAAU,EACb,MAAM,aAAa,CAAC;AAErB,wBAAgB,cAAc,CAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,aAAa,EAAE,EAC1B,MAAM,EAAE,UAAU,EAAE,EACpB,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GACtB,iBAAiB,CAgCnB"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Scoring logic — determines pass / fail for each checklist item
3
+ * and computes the overall evaluation outcome.
4
+ */
5
+ export function evaluateScores(sessionId, type, screenName, checklist, scores, passingScore, attemptNumber) {
6
+ const results = checklist.map((item) => {
7
+ const entry = scores.find((s) => s.id === item.id);
8
+ const score = entry?.score ?? 0;
9
+ const passed = score >= passingScore;
10
+ return {
11
+ id: item.id,
12
+ score,
13
+ passed,
14
+ reason: entry?.reason,
15
+ suggestion: entry?.suggestion,
16
+ };
17
+ });
18
+ const failedItems = results.filter((r) => !r.passed).map((r) => r.id);
19
+ const totalScore = results.reduce((sum, r) => sum + r.score, 0);
20
+ const averageScore = results.length > 0
21
+ ? parseFloat((totalScore / results.length).toFixed(2))
22
+ : 0;
23
+ return {
24
+ sessionId,
25
+ type,
26
+ screenName,
27
+ overallPassed: failedItems.length === 0,
28
+ averageScore,
29
+ results,
30
+ failedItems,
31
+ timestamp: new Date().toISOString(),
32
+ attemptNumber,
33
+ };
34
+ }
35
+ //# sourceMappingURL=scorer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scorer.js","sourceRoot":"","sources":["../../src/evaluation/scorer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,MAAM,UAAU,cAAc,CAC1B,SAAiB,EACjB,IAAoB,EACpB,UAAkB,EAClB,SAA0B,EAC1B,MAAoB,EACpB,YAAoB,EACpB,aAAqB;IAErB,MAAM,OAAO,GAAuB,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACvD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,KAAK,IAAI,YAAY,CAAC;QACrC,OAAO;YACH,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK;YACL,MAAM;YACN,MAAM,EAAE,KAAK,EAAE,MAAM;YACrB,UAAU,EAAE,KAAK,EAAE,UAAU;SAChC,CAAC;IACN,CAAC,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAChE,MAAM,YAAY,GACd,OAAO,CAAC,MAAM,GAAG,CAAC;QACd,CAAC,CAAC,UAAU,CAAC,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC,CAAC;IAEZ,OAAO;QACH,SAAS;QACT,IAAI;QACJ,UAAU;QACV,aAAa,EAAE,WAAW,CAAC,MAAM,KAAK,CAAC;QACvC,YAAY;QACZ,OAAO;QACP,WAAW;QACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,aAAa;KAChB,CAAC;AACN,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=agent-demo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-demo.d.ts","sourceRoot":"","sources":["../../examples/agent-demo.ts"],"names":[],"mappings":""}
@@ -0,0 +1,79 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
3
+ import * as path from "path";
4
+ // Connect to the local MCP server
5
+ async function run() {
6
+ console.log("Starting MCP Client...");
7
+ const transport = new StdioClientTransport({
8
+ command: "node",
9
+ args: [path.join(process.cwd(), "dist/src/index.js")],
10
+ });
11
+ const client = new Client({
12
+ name: "test-agent",
13
+ version: "1.0.0",
14
+ }, {
15
+ capabilities: {},
16
+ });
17
+ try {
18
+ await client.connect(transport);
19
+ console.log("Connected to MCP server.");
20
+ // 1. Launch App
21
+ console.log("Launching Preferences app...");
22
+ await client.callTool({
23
+ name: "launch_app",
24
+ arguments: { appId: "com.apple.Preferences" },
25
+ });
26
+ // 2. Take Screenshot
27
+ console.log("Taking screenshot...");
28
+ const screenshotResult = await client.callTool({
29
+ name: "take_screenshot",
30
+ arguments: {},
31
+ });
32
+ if (screenshotResult.imageBase64) {
33
+ console.log(`Screenshot captured (${screenshotResult.width}x${screenshotResult.height})`);
34
+ }
35
+ // 3. Get Checklist (Screen)
36
+ console.log("Getting checklist for 'SettingsScreen'...");
37
+ const checklistResult = await client.callTool({
38
+ name: "get_checklist",
39
+ arguments: { type: "screen", screenName: "SettingsScreen" },
40
+ });
41
+ const checklistData = JSON.parse(checklistResult.content[0].text);
42
+ const sessionId = checklistData.sessionId;
43
+ const items = checklistData.checklist;
44
+ console.log(`Session ID: ${sessionId}`);
45
+ console.log(`Checklist items: ${items.map((i) => i.id).join(", ")}`);
46
+ // 4. Submit Evaluation (Simulate passing)
47
+ console.log("Submitting passing evaluation...");
48
+ const scores = items.map((item) => ({
49
+ id: item.id,
50
+ score: 5,
51
+ }));
52
+ const evalResult = await client.callTool({
53
+ name: "submit_evaluation",
54
+ arguments: {
55
+ sessionId: sessionId,
56
+ scores: scores,
57
+ },
58
+ });
59
+ console.log("Evaluation result:", JSON.parse(evalResult.content[0].text));
60
+ // 5. Get Logs
61
+ console.log("Fetching logs...");
62
+ const logResult = await client.callTool({
63
+ name: "get_evaluation_log",
64
+ arguments: { limit: 1 },
65
+ });
66
+ console.log("Logs retrieved.");
67
+ }
68
+ catch (error) {
69
+ console.error("Error running test agent:", error);
70
+ }
71
+ finally {
72
+ // transport.close() is not exposed directly on StdioClientTransport in some versions,
73
+ // but client.close() should handle it?
74
+ // Actually, just exit process.
75
+ process.exit(0);
76
+ }
77
+ }
78
+ run();
79
+ //# sourceMappingURL=agent-demo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-demo.js","sourceRoot":"","sources":["../../examples/agent-demo.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,kCAAkC;AAClC,KAAK,UAAU,GAAG;IACd,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IAEtC,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC;QACvC,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,mBAAmB,CAAC,CAAC;KACxD,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,IAAI,MAAM,CACrB;QACI,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,OAAO;KACnB,EACD;QACI,YAAY,EAAE,EAAE;KACnB,CACJ,CAAC;IAEF,IAAI,CAAC;QACD,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QAExC,gBAAgB;QAChB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC5C,MAAM,MAAM,CAAC,QAAQ,CAAC;YAClB,IAAI,EAAE,YAAY;YAClB,SAAS,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE;SAChD,CAAC,CAAC;QAEH,qBAAqB;QACrB,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACpC,MAAM,gBAAgB,GAAQ,MAAM,MAAM,CAAC,QAAQ,CAAC;YAChD,IAAI,EAAE,iBAAiB;YACvB,SAAS,EAAE,EAAE;SAChB,CAAC,CAAC;QAEH,IAAI,gBAAgB,CAAC,WAAW,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,wBAAwB,gBAAgB,CAAC,KAAK,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9F,CAAC;QAED,4BAA4B;QAC5B,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QACzD,MAAM,eAAe,GAAQ,MAAM,MAAM,CAAC,QAAQ,CAAC;YAC/C,IAAI,EAAE,eAAe;YACrB,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE;SAC9D,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAClE,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,CAAC;QAC1C,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC;QAEtC,OAAO,CAAC,GAAG,CAAC,eAAe,SAAS,EAAE,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAE1E,0CAA0C;QAC1C,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;YACrC,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,KAAK,EAAE,CAAC;SACX,CAAC,CAAC,CAAC;QAEJ,MAAM,UAAU,GAAQ,MAAM,MAAM,CAAC,QAAQ,CAAC;YAC1C,IAAI,EAAE,mBAAmB;YACzB,SAAS,EAAE;gBACP,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,MAAM;aACjB;SACJ,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAE1E,cAAc;QACd,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QAChC,MAAM,SAAS,GAAQ,MAAM,MAAM,CAAC,QAAQ,CAAC;YACzC,IAAI,EAAE,oBAAoB;YAC1B,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;SAC1B,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAEnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;IACtD,CAAC;YAAS,CAAC;QACP,sFAAsF;QACtF,uCAAuC;QACvC,+BAA+B;QAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC;AAED,GAAG,EAAE,CAAC"}
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * UI Critic MCP Server
4
+ *
5
+ * An MCP server for iOS app UI evaluation and testing.
6
+ * Uses idb + xcrun simctl for device operations (launch, screenshot, tap, swipe)
7
+ * and provides a structured checklist-based evaluation workflow.
8
+ *
9
+ * Tools:
10
+ * Device operations:
11
+ * - launch_app Launch an iOS app by bundle ID
12
+ * - take_screenshot Capture the current screen as base64 PNG
13
+ * - tap Tap at ratio coordinates (0-1)
14
+ * - swipe Swipe between ratio coordinates (0-1)
15
+ *
16
+ * Evaluation:
17
+ * - get_checklist Get the evaluation checklist and create a session
18
+ * - submit_evaluation Submit scores and get pass/fail result
19
+ * - evaluate_style_consistency Compare multiple screenshots for style consistency
20
+ * - get_evaluation_log Query past evaluation results
21
+ */
22
+ export {};
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;GAmBG"}
package/dist/index.js ADDED
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * UI Critic MCP Server
4
+ *
5
+ * An MCP server for iOS app UI evaluation and testing.
6
+ * Uses idb + xcrun simctl for device operations (launch, screenshot, tap, swipe)
7
+ * and provides a structured checklist-based evaluation workflow.
8
+ *
9
+ * Tools:
10
+ * Device operations:
11
+ * - launch_app Launch an iOS app by bundle ID
12
+ * - take_screenshot Capture the current screen as base64 PNG
13
+ * - tap Tap at ratio coordinates (0-1)
14
+ * - swipe Swipe between ratio coordinates (0-1)
15
+ *
16
+ * Evaluation:
17
+ * - get_checklist Get the evaluation checklist and create a session
18
+ * - submit_evaluation Submit scores and get pass/fail result
19
+ * - evaluate_style_consistency Compare multiple screenshots for style consistency
20
+ * - get_evaluation_log Query past evaluation results
21
+ */
22
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
23
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
24
+ import { launchAppSchema, launchApp } from "./tools/launch-app.js";
25
+ import { takeScreenshotSchema, takeScreenshot } from "./tools/take-screenshot.js";
26
+ import { tapSchema, tap } from "./tools/tap.js";
27
+ import { swipeSchema, swipe } from "./tools/swipe.js";
28
+ import { getChecklistSchema, getChecklist } from "./tools/get-checklist.js";
29
+ import { submitEvaluationSchema, submitEvaluation } from "./tools/submit-evaluation.js";
30
+ import { evaluateStyleSchema, evaluateStyleConsistency } from "./tools/evaluate-style.js";
31
+ import { getEvaluationLogSchema, getEvaluationLog } from "./tools/get-log.js";
32
+ const server = new McpServer({
33
+ name: "ui-critic-mcp",
34
+ version: "1.0.0",
35
+ });
36
+ // ─── Device operation tools ────────────────────────────────
37
+ server.tool("launch_app", "Launch an iOS app on the simulator by its bundle ID.", launchAppSchema.shape, async (args) => launchApp(args));
38
+ server.tool("take_screenshot", "Capture the current simulator screen as a base64 PNG image. " +
39
+ "Returns the image for visual inspection along with screen metadata.", takeScreenshotSchema.shape, async (args) => takeScreenshot(args));
40
+ server.tool("tap", "Tap at a specific position on the screen. " +
41
+ "Uses ratio coordinates (0-1): x=0 is left edge, x=1 is right edge, y=0 is top, y=1 is bottom.", tapSchema.shape, async (args) => tap(args));
42
+ server.tool("swipe", "Swipe from one point to another on the screen. " +
43
+ "Uses ratio coordinates (0-1) for both start and end positions.", swipeSchema.shape, async (args) => swipe(args));
44
+ // ─── Evaluation tools ──────────────────────────────────────
45
+ server.tool("get_checklist", "Get the UI evaluation checklist and create an evaluation session. " +
46
+ "Use type='screen' for single-screen quality evaluation (4 items: overlap, layout, info clarity, ambiguity). " +
47
+ "Use type='style' for multi-screen style consistency evaluation (3 items: color, component, typography).", getChecklistSchema.shape, async (args) => getChecklist(args));
48
+ server.tool("submit_evaluation", "Submit scores for each checklist item. Determines pass/fail (passing score >= 3) " +
49
+ "and persists the result. Failed items include reasons and suggestions for improvement.", submitEvaluationSchema.shape, async (args) => submitEvaluation(args));
50
+ server.tool("evaluate_style_consistency", "Compare multiple screenshots for visual style consistency. " +
51
+ "Provide at least 2 screenshots. Returns all images alongside a style consistency checklist. " +
52
+ "After analyzing, call submit_evaluation with the returned sessionId and your scores.", evaluateStyleSchema.shape, async (args) => evaluateStyleConsistency(args));
53
+ server.tool("get_evaluation_log", "Query past evaluation results and summary statistics. " +
54
+ "Use to review improvement across attempts or generate a final report.", getEvaluationLogSchema.shape, async (args) => getEvaluationLog(args));
55
+ import { preflight } from "./device/adapter.js";
56
+ // ─── Start server ──────────────────────────────────────────
57
+ async function main() {
58
+ // Check for required CLI tools (non-blocking — warn only)
59
+ const check = await preflight();
60
+ if (!check.ok) {
61
+ console.error("[ui-critic-mcp] ⚠️ Missing dependencies:");
62
+ for (const m of check.missing) {
63
+ console.error(` - ${m}`);
64
+ }
65
+ console.error("[ui-critic-mcp] Some tools (tap, swipe) may not work.");
66
+ }
67
+ const transport = new StdioServerTransport();
68
+ await server.connect(transport);
69
+ console.error("[ui-critic-mcp] Server started on stdio transport.");
70
+ }
71
+ main().catch((err) => {
72
+ console.error("[ui-critic-mcp] Fatal error:", err);
73
+ process.exit(1);
74
+ });
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACxF,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1F,OAAO,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IACzB,IAAI,EAAE,eAAe;IACrB,OAAO,EAAE,OAAO;CACnB,CAAC,CAAC;AAEH,8DAA8D;AAE9D,MAAM,CAAC,IAAI,CACP,YAAY,EACZ,sDAAsD,EACtD,eAAe,CAAC,KAAK,EACrB,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAClC,CAAC;AAEF,MAAM,CAAC,IAAI,CACP,iBAAiB,EACjB,8DAA8D;IAC9D,qEAAqE,EACrE,oBAAoB,CAAC,KAAK,EAC1B,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CACvC,CAAC;AAEF,MAAM,CAAC,IAAI,CACP,KAAK,EACL,4CAA4C;IAC5C,+FAA+F,EAC/F,SAAS,CAAC,KAAK,EACf,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAC5B,CAAC;AAEF,MAAM,CAAC,IAAI,CACP,OAAO,EACP,iDAAiD;IACjD,gEAAgE,EAChE,WAAW,CAAC,KAAK,EACjB,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAC9B,CAAC;AAEF,8DAA8D;AAE9D,MAAM,CAAC,IAAI,CACP,eAAe,EACf,oEAAoE;IACpE,8GAA8G;IAC9G,yGAAyG,EACzG,kBAAkB,CAAC,KAAK,EACxB,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CACrC,CAAC;AAEF,MAAM,CAAC,IAAI,CACP,mBAAmB,EACnB,mFAAmF;IACnF,wFAAwF,EACxF,sBAAsB,CAAC,KAAK,EAC5B,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CACzC,CAAC;AAEF,MAAM,CAAC,IAAI,CACP,4BAA4B,EAC5B,6DAA6D;IAC7D,8FAA8F;IAC9F,sFAAsF,EACtF,mBAAmB,CAAC,KAAK,EACzB,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,wBAAwB,CAAC,IAAI,CAAC,CACjD,CAAC;AAEF,MAAM,CAAC,IAAI,CACP,oBAAoB,EACpB,wDAAwD;IACxD,uEAAuE,EACvE,sBAAsB,CAAC,KAAK,EAC5B,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,CACzC,CAAC;AAEF,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,8DAA8D;AAE9D,KAAK,UAAU,IAAI;IACf,0DAA0D;IAC1D,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACZ,OAAO,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC3D,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;AACxE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}