docker-flutter-ios-simulator-mcp 0.1.2 → 0.1.3
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/CLAUDE.md +14 -5
- package/README.md +34 -10
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +22 -0
- package/dist/config.js.map +1 -0
- package/dist/flutter/test-manager.d.ts +15 -0
- package/dist/flutter/test-manager.d.ts.map +1 -0
- package/dist/flutter/test-manager.js +270 -0
- package/dist/flutter/test-manager.js.map +1 -0
- package/dist/flutter/test-types.d.ts +94 -0
- package/dist/flutter/test-types.d.ts.map +1 -0
- package/dist/flutter/test-types.js +3 -0
- package/dist/flutter/test-types.js.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -1
- package/dist/session/manager.d.ts +13 -3
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +60 -35
- package/dist/session/manager.js.map +1 -1
- package/dist/session/state.d.ts +1 -0
- package/dist/session/state.d.ts.map +1 -1
- package/dist/session/state.js +3 -0
- package/dist/session/state.js.map +1 -1
- package/dist/session/types.d.ts +4 -2
- package/dist/session/types.d.ts.map +1 -1
- package/dist/simulator/idb.d.ts +1 -0
- package/dist/simulator/idb.d.ts.map +1 -1
- package/dist/simulator/idb.js +25 -6
- package/dist/simulator/idb.js.map +1 -1
- package/dist/simulator/simctl.d.ts.map +1 -1
- package/dist/simulator/simctl.js +6 -3
- package/dist/simulator/simctl.js.map +1 -1
- package/dist/tools/flutter-commands.d.ts.map +1 -1
- package/dist/tools/flutter-commands.js +8 -1
- package/dist/tools/flutter-commands.js.map +1 -1
- package/dist/tools/flutter-test.d.ts +70 -0
- package/dist/tools/flutter-test.d.ts.map +1 -0
- package/dist/tools/flutter-test.js +154 -0
- package/dist/tools/flutter-test.js.map +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +111 -12
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/session.d.ts +15 -4
- package/dist/tools/session.d.ts.map +1 -1
- package/dist/tools/session.js +14 -3
- package/dist/tools/session.js.map +1 -1
- package/dist/tools/simulator-ui.d.ts +1 -0
- package/dist/tools/simulator-ui.d.ts.map +1 -1
- package/dist/tools/simulator-ui.js +48 -0
- package/dist/tools/simulator-ui.js.map +1 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +6 -0
- package/dist/transport.js.map +1 -1
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -11,14 +11,17 @@ This MCP provides access to and control of building flutter apps, launching the
|
|
|
11
11
|
The MCP server provides these tools to AI agents:
|
|
12
12
|
|
|
13
13
|
**Session Management:**
|
|
14
|
-
- `session_start` - Create a new development session
|
|
14
|
+
- `session_start` - Create a new development session (simulator starts lazily on first flutter_run or explicit start_simulator)
|
|
15
|
+
- `start_simulator` - Explicitly start an iOS simulator for a session (optional - flutter_run auto-starts)
|
|
15
16
|
- `session_end` - Clean up and delete the simulator
|
|
16
17
|
- `session_list` - View active sessions
|
|
17
18
|
|
|
18
19
|
**Flutter Development:**
|
|
19
20
|
- `flutter_run` - Build and launch your app
|
|
20
21
|
- `flutter_build` - Build iOS app without running (for CI/deployment)
|
|
21
|
-
- `flutter_test` - Run Flutter tests
|
|
22
|
+
- `flutter_test` - Run Flutter tests (supports `testTarget` for specific file/directory, `testNameMatch` for regex filtering, `tags` for tag filtering)
|
|
23
|
+
- `flutter_test_results` - Get test progress and results
|
|
24
|
+
- `flutter_test_logs` - Get detailed test output (failures by default)
|
|
22
25
|
- `flutter_clean` - Clean build cache and artifacts
|
|
23
26
|
- `flutter_logs` - Monitor build progress and app output
|
|
24
27
|
- `flutter_hot_reload` - Apply code changes instantly
|
|
@@ -26,7 +29,7 @@ The MCP server provides these tools to AI agents:
|
|
|
26
29
|
- `flutter_stop` - Stop the running app
|
|
27
30
|
|
|
28
31
|
**UI Interaction:**
|
|
29
|
-
- `screenshot` - Capture and view the simulator screen
|
|
32
|
+
- `screenshot` - Capture and view the simulator screen (returns image + HTTP URL)
|
|
30
33
|
- `ui_tap` - Tap at coordinates
|
|
31
34
|
- `ui_swipe` - Swipe gestures (scrolling, swiping)
|
|
32
35
|
- `ui_type` - Enter text into fields
|
|
@@ -46,10 +49,16 @@ session_start({
|
|
|
46
49
|
worktreePath: "/path/to/your/flutter/project",
|
|
47
50
|
deviceType: "iPhone 16 Pro"
|
|
48
51
|
})
|
|
49
|
-
// Returns: { sessionId: "abc-123",
|
|
52
|
+
// Returns: { sessionId: "abc-123", deviceType: "iPhone 16 Pro", worktreePath: "..." }
|
|
53
|
+
// NOTE: Simulator is NOT created yet - it starts automatically when you call flutter_run
|
|
50
54
|
|
|
51
|
-
// 2. Run the Flutter app
|
|
55
|
+
// 2. Run the Flutter app (auto-starts simulator)
|
|
52
56
|
flutter_run({ sessionId: "abc-123" })
|
|
57
|
+
// The simulator boots automatically on first flutter_run
|
|
58
|
+
|
|
59
|
+
// Optional: If you need to start the simulator before running Flutter, use:
|
|
60
|
+
// start_simulator({ sessionId: "abc-123" })
|
|
61
|
+
// Returns: { simulatorUdid: "...", deviceType: "iPhone 16 Pro", message: "..." }
|
|
53
62
|
|
|
54
63
|
// 3. Monitor build progress (poll every few seconds)
|
|
55
64
|
flutter_logs({
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Enables AI agents (like Claude) inside a Docker container to build, run, and int
|
|
|
9
9
|
- 🎯 **Session-based Development** - Isolated simulator and Flutter process per session
|
|
10
10
|
- 🔥 **Hot Reload & Restart** - Instant code updates without full rebuilds
|
|
11
11
|
- 📱 **UI Automation** - Tap, swipe, type, and interact with the simulator
|
|
12
|
-
- 📸 **Visual Feedback** - Screenshots returned as images (
|
|
12
|
+
- 📸 **Visual Feedback** - Screenshots returned as images + HTTP URLs (accessible from anywhere)
|
|
13
13
|
- 🔍 **Accessibility Tree** - Inspect UI elements and hierarchy
|
|
14
14
|
- 📊 **Live Logs** - Real-time Flutter build output and app logs
|
|
15
15
|
- 🌐 **HTTP Transport** - Works from Docker containers (no filesystem access needed)
|
|
@@ -167,14 +167,15 @@ claude mcp add docker-flutter-ios-simulator-mcp http://host.docker.internal:3000
|
|
|
167
167
|
The MCP server provides these tools to AI agents:
|
|
168
168
|
|
|
169
169
|
**Session Management:**
|
|
170
|
-
- `session_start` - Create a new development session
|
|
170
|
+
- `session_start` - Create a new development session (simulator starts on first flutter_run or explicit start_simulator)
|
|
171
|
+
- `start_simulator` - Explicitly start an iOS simulator for a session
|
|
171
172
|
- `session_end` - Clean up and delete the simulator
|
|
172
173
|
- `session_list` - View active sessions
|
|
173
174
|
|
|
174
175
|
**Flutter Development:**
|
|
175
176
|
- `flutter_run` - Build and launch your app
|
|
176
177
|
- `flutter_build` - Build iOS app without running (for CI/deployment)
|
|
177
|
-
- `flutter_test` - Run Flutter tests and
|
|
178
|
+
- `flutter_test` - Run Flutter tests (supports filtering by file/directory, test name, and tags)
|
|
178
179
|
- `flutter_clean` - Clean build cache and artifacts
|
|
179
180
|
- `flutter_logs` - Monitor build progress and app output
|
|
180
181
|
- `flutter_hot_reload` - Apply code changes instantly
|
|
@@ -182,7 +183,7 @@ The MCP server provides these tools to AI agents:
|
|
|
182
183
|
- `flutter_stop` - Stop the running app
|
|
183
184
|
|
|
184
185
|
**UI Interaction:**
|
|
185
|
-
- `screenshot` - Capture and view the simulator screen
|
|
186
|
+
- `screenshot` - Capture and view the simulator screen (returns image + HTTP URL)
|
|
186
187
|
- `ui_tap` - Tap at coordinates
|
|
187
188
|
- `ui_swipe` - Swipe gestures (scrolling, swiping)
|
|
188
189
|
- `ui_type` - Enter text into fields
|
|
@@ -202,10 +203,16 @@ session_start({
|
|
|
202
203
|
worktreePath: "/path/to/your/flutter/project",
|
|
203
204
|
deviceType: "iPhone 16 Pro"
|
|
204
205
|
})
|
|
205
|
-
// Returns: { sessionId: "abc-123",
|
|
206
|
+
// Returns: { sessionId: "abc-123", deviceType: "iPhone 16 Pro", worktreePath: "..." }
|
|
207
|
+
// Note: Simulator is NOT started yet - starts automatically when you call flutter_run
|
|
206
208
|
|
|
207
|
-
// 2. Run the Flutter app
|
|
209
|
+
// 2. Run the Flutter app (automatically starts simulator if not already started)
|
|
208
210
|
flutter_run({ sessionId: "abc-123" })
|
|
211
|
+
// Simulator boots automatically on first flutter_run
|
|
212
|
+
|
|
213
|
+
// Alternative: Explicitly start simulator before running Flutter
|
|
214
|
+
// start_simulator({ sessionId: "abc-123" })
|
|
215
|
+
// Returns: { simulatorUdid: "...", deviceType: "iPhone 16 Pro", message: "..." }
|
|
209
216
|
|
|
210
217
|
// 3. Monitor build progress (poll every few seconds)
|
|
211
218
|
flutter_logs({
|
|
@@ -216,7 +223,8 @@ flutter_logs({
|
|
|
216
223
|
|
|
217
224
|
// 4. Take a screenshot to see the app
|
|
218
225
|
screenshot({ sessionId: "abc-123" })
|
|
219
|
-
// Returns image directly in response!
|
|
226
|
+
// Returns image directly in response + HTTP URL!
|
|
227
|
+
// Example URL: http://localhost:3000/screenshot/abc-123-1234567890.png
|
|
220
228
|
|
|
221
229
|
// 5. Interact with the UI
|
|
222
230
|
ui_tap({ sessionId: "abc-123", x: 200, y: 400 })
|
|
@@ -224,7 +232,21 @@ ui_tap({ sessionId: "abc-123", x: 200, y: 400 })
|
|
|
224
232
|
// 6. Make code changes, then hot reload
|
|
225
233
|
flutter_hot_reload({ sessionId: "abc-123" })
|
|
226
234
|
|
|
227
|
-
// 7.
|
|
235
|
+
// 7. Run tests (all tests, or filter by file/directory)
|
|
236
|
+
flutter_test({ sessionId: "abc-123" })
|
|
237
|
+
// Or run specific tests:
|
|
238
|
+
flutter_test({
|
|
239
|
+
sessionId: "abc-123",
|
|
240
|
+
testTarget: "test/unit/", // Run only tests in this directory
|
|
241
|
+
testNameMatch: "login.*" // Further filter by test name
|
|
242
|
+
})
|
|
243
|
+
// Returns: { reference: 1 }
|
|
244
|
+
|
|
245
|
+
// 8. Check test progress
|
|
246
|
+
flutter_test_results({ reference: 1 })
|
|
247
|
+
// Returns: { tests_complete: 10, tests_total: 20, passes: 8, fails: 2, complete: false }
|
|
248
|
+
|
|
249
|
+
// 9. Clean up when done
|
|
228
250
|
session_end({ sessionId: "abc-123" })
|
|
229
251
|
```
|
|
230
252
|
|
|
@@ -325,7 +347,9 @@ By default, the server:
|
|
|
325
347
|
|
|
326
348
|
### Screenshots not appearing
|
|
327
349
|
- Screenshots are returned as images in the MCP response
|
|
328
|
-
-
|
|
350
|
+
- An HTTP URL is also provided to fetch the screenshot (e.g., http://localhost:3000/screenshot/session-123-1234567890.png)
|
|
351
|
+
- The URL works from anywhere - browsers, Docker containers, or HTTP clients
|
|
352
|
+
- Screenshots are saved to /tmp/mcp-screenshots/ on the server
|
|
329
353
|
|
|
330
354
|
### First Flutter build is slow
|
|
331
355
|
- First build can take 1-2 minutes (normal)
|
|
@@ -363,7 +387,7 @@ src/
|
|
|
363
387
|
1. **Session-based Isolation**: Each session creates a dedicated iOS Simulator and Flutter process
|
|
364
388
|
2. **HTTP Transport**: MCP protocol over HTTP (works from Docker containers)
|
|
365
389
|
3. **Log Buffering**: Flutter output is buffered in memory, retrieved via polling
|
|
366
|
-
4. **
|
|
390
|
+
4. **Screenshot Delivery**: Screenshots returned as base64 PNG in MCP responses AND saved to disk with HTTP URL for easy access
|
|
367
391
|
5. **UI Automation**: Uses Facebook IDB for simulator interaction
|
|
368
392
|
|
|
369
393
|
## Contributing
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAGhE;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAUzC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Global configuration for the server
|
|
2
|
+
let serverHost = 'localhost';
|
|
3
|
+
let serverPort = 3000;
|
|
4
|
+
let lastKnownHostHeader;
|
|
5
|
+
export function setServerConfig(host, port) {
|
|
6
|
+
serverHost = host;
|
|
7
|
+
serverPort = port;
|
|
8
|
+
}
|
|
9
|
+
export function updateHostHeader(hostHeader) {
|
|
10
|
+
lastKnownHostHeader = hostHeader;
|
|
11
|
+
}
|
|
12
|
+
export function getServerBaseUrl() {
|
|
13
|
+
// Prefer the last known Host header from an actual request
|
|
14
|
+
// This allows the URL to work correctly regardless of how clients access the server
|
|
15
|
+
// (localhost, host.docker.internal, IP address, etc.)
|
|
16
|
+
if (lastKnownHostHeader) {
|
|
17
|
+
return `http://${lastKnownHostHeader}`;
|
|
18
|
+
}
|
|
19
|
+
// Fallback to configured host and port
|
|
20
|
+
return `http://${serverHost}:${String(serverPort)}`;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,IAAI,UAAU,GAAG,WAAW,CAAC;AAC7B,IAAI,UAAU,GAAG,IAAI,CAAC;AACtB,IAAI,mBAAuC,CAAC;AAE5C,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,IAAY;IACxD,UAAU,GAAG,IAAI,CAAC;IAClB,UAAU,GAAG,IAAI,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,mBAAmB,GAAG,UAAU,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,2DAA2D;IAC3D,oFAAoF;IACpF,sDAAsD;IACtD,IAAI,mBAAmB,EAAE,CAAC;QACxB,OAAO,UAAU,mBAAmB,EAAE,CAAC;IACzC,CAAC;IAED,uCAAuC;IACvC,OAAO,UAAU,UAAU,IAAI,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FlutterTestOptions, FlutterTestProgress, FlutterTestLog } from './test-types.js';
|
|
2
|
+
export declare class FlutterTestManager {
|
|
3
|
+
private process?;
|
|
4
|
+
private testStates;
|
|
5
|
+
start(options: FlutterTestOptions): number;
|
|
6
|
+
private handleOutput;
|
|
7
|
+
private handleStderr;
|
|
8
|
+
private processEvent;
|
|
9
|
+
private handleExit;
|
|
10
|
+
getProgress(reference: number, showAllTestNames?: boolean, offset?: number, limit?: number): FlutterTestProgress | null;
|
|
11
|
+
getLogs(reference: number, showAll?: boolean, offset?: number, limit?: number): FlutterTestLog[];
|
|
12
|
+
cleanup(reference?: number): void;
|
|
13
|
+
getAllReferences(): number[];
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=test-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-manager.d.ts","sourceRoot":"","sources":["../../src/flutter/test-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAElB,mBAAmB,EACnB,cAAc,EAMf,MAAM,iBAAiB,CAAC;AAMzB,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,UAAU,CAA4C;IAE9D,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM;IAyE1C,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,YAAY;IA0GpB,OAAO,CAAC,UAAU;IAelB,WAAW,CACT,SAAS,EAAE,MAAM,EACjB,gBAAgB,UAAQ,EACxB,MAAM,SAAI,EACV,KAAK,SAAM,GACV,mBAAmB,GAAG,IAAI;IA2C7B,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,UAAQ,EAAE,MAAM,SAAI,EAAE,KAAK,SAAM,GAAG,cAAc,EAAE;IAqBtF,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAejC,gBAAgB,IAAI,MAAM,EAAE;CAG7B"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { spawnStreaming } from '../utils/exec.js';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
// Global counter for unique test references across all sessions
|
|
4
|
+
// This prevents reference collisions when multiple sessions have testManagers
|
|
5
|
+
let globalNextReference = 1;
|
|
6
|
+
export class FlutterTestManager {
|
|
7
|
+
process;
|
|
8
|
+
testStates = new Map();
|
|
9
|
+
start(options) {
|
|
10
|
+
logger.info('Starting Flutter test', {
|
|
11
|
+
worktreePath: options.worktreePath,
|
|
12
|
+
testTarget: options.testTarget,
|
|
13
|
+
testNameMatch: options.testNameMatch,
|
|
14
|
+
tags: options.tags,
|
|
15
|
+
timeout: options.timeout,
|
|
16
|
+
deviceId: options.deviceId,
|
|
17
|
+
});
|
|
18
|
+
const reference = globalNextReference++;
|
|
19
|
+
const state = {
|
|
20
|
+
reference,
|
|
21
|
+
startedAt: new Date(),
|
|
22
|
+
tests: new Map(),
|
|
23
|
+
testNames: new Map(),
|
|
24
|
+
totalTests: 0,
|
|
25
|
+
complete: false,
|
|
26
|
+
outputBuffer: [],
|
|
27
|
+
};
|
|
28
|
+
this.testStates.set(reference, state);
|
|
29
|
+
const args = ['test', '--reporter', 'json'];
|
|
30
|
+
// Add test name filter
|
|
31
|
+
if (options.testNameMatch) {
|
|
32
|
+
args.push('--name', options.testNameMatch);
|
|
33
|
+
}
|
|
34
|
+
// Add tags filter
|
|
35
|
+
if (options.tags && options.tags.length > 0) {
|
|
36
|
+
for (const tag of options.tags) {
|
|
37
|
+
args.push('--tags', tag);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Add timeout
|
|
41
|
+
if (options.timeout) {
|
|
42
|
+
// Convert minutes to seconds
|
|
43
|
+
const timeoutSeconds = options.timeout * 60;
|
|
44
|
+
args.push('--timeout', `${String(timeoutSeconds)}s`);
|
|
45
|
+
}
|
|
46
|
+
// Add device ID (simulator UDID)
|
|
47
|
+
if (options.deviceId) {
|
|
48
|
+
args.push('--device-id', options.deviceId);
|
|
49
|
+
}
|
|
50
|
+
// Add test target (file or directory)
|
|
51
|
+
if (options.testTarget) {
|
|
52
|
+
args.push(options.testTarget);
|
|
53
|
+
}
|
|
54
|
+
this.process = spawnStreaming('flutter', args, {
|
|
55
|
+
cwd: options.worktreePath,
|
|
56
|
+
onStdout: (data) => {
|
|
57
|
+
this.handleOutput(reference, data);
|
|
58
|
+
},
|
|
59
|
+
onStderr: (data) => {
|
|
60
|
+
// Also capture stderr for any error messages
|
|
61
|
+
this.handleStderr(reference, data);
|
|
62
|
+
},
|
|
63
|
+
onExit: (code, signal) => {
|
|
64
|
+
this.handleExit(reference, code, signal);
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
logger.info('Flutter test process started', { reference, pid: this.process.pid });
|
|
68
|
+
return reference;
|
|
69
|
+
}
|
|
70
|
+
handleOutput(reference, data) {
|
|
71
|
+
const state = this.testStates.get(reference);
|
|
72
|
+
if (!state)
|
|
73
|
+
return;
|
|
74
|
+
const lines = data.split('\n');
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
if (!line.trim())
|
|
77
|
+
continue;
|
|
78
|
+
// Store raw output
|
|
79
|
+
state.outputBuffer.push(line);
|
|
80
|
+
// Try to parse as JSON event
|
|
81
|
+
try {
|
|
82
|
+
const event = JSON.parse(line);
|
|
83
|
+
this.processEvent(reference, event);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Not JSON, just regular output
|
|
87
|
+
logger.debug('Non-JSON test output', { line });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
handleStderr(reference, data) {
|
|
92
|
+
const state = this.testStates.get(reference);
|
|
93
|
+
if (!state)
|
|
94
|
+
return;
|
|
95
|
+
const lines = data.split('\n');
|
|
96
|
+
for (const line of lines) {
|
|
97
|
+
if (line.trim()) {
|
|
98
|
+
state.outputBuffer.push(`[stderr] ${line}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
processEvent(reference, event) {
|
|
103
|
+
const state = this.testStates.get(reference);
|
|
104
|
+
if (!state)
|
|
105
|
+
return;
|
|
106
|
+
const eventType = event.type;
|
|
107
|
+
switch (eventType) {
|
|
108
|
+
case 'start':
|
|
109
|
+
logger.debug('Test run started', { reference });
|
|
110
|
+
break;
|
|
111
|
+
case 'allSuites':
|
|
112
|
+
state.totalTests = event.count || 0;
|
|
113
|
+
logger.debug('Total test suites', { count: state.totalTests });
|
|
114
|
+
break;
|
|
115
|
+
case 'testStart': {
|
|
116
|
+
const testEvent = event;
|
|
117
|
+
const testId = testEvent.test.id;
|
|
118
|
+
const testName = testEvent.test.name;
|
|
119
|
+
state.testNames.set(testId, testName);
|
|
120
|
+
// Initialize test result
|
|
121
|
+
if (!state.tests.has(testId)) {
|
|
122
|
+
state.tests.set(testId, {
|
|
123
|
+
testId,
|
|
124
|
+
testName,
|
|
125
|
+
result: 'success',
|
|
126
|
+
skipped: false,
|
|
127
|
+
hidden: testEvent.test.metadata.skip,
|
|
128
|
+
output: [],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
logger.debug('Test started', { testId, testName });
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case 'testDone': {
|
|
135
|
+
const testEvent = event;
|
|
136
|
+
const testId = testEvent.testID;
|
|
137
|
+
const test = state.tests.get(testId);
|
|
138
|
+
if (test) {
|
|
139
|
+
test.result = testEvent.result === 'success' ? 'success' : testEvent.result;
|
|
140
|
+
test.skipped = testEvent.skipped;
|
|
141
|
+
test.hidden = testEvent.hidden;
|
|
142
|
+
}
|
|
143
|
+
logger.debug('Test done', {
|
|
144
|
+
testId,
|
|
145
|
+
result: testEvent.result,
|
|
146
|
+
skipped: testEvent.skipped,
|
|
147
|
+
});
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case 'error': {
|
|
151
|
+
const errorEvent = event;
|
|
152
|
+
const testId = errorEvent.testID;
|
|
153
|
+
const test = state.tests.get(testId);
|
|
154
|
+
if (test) {
|
|
155
|
+
test.result = errorEvent.isFailure ? 'failure' : 'error';
|
|
156
|
+
test.output.push(errorEvent.error);
|
|
157
|
+
if (errorEvent.stackTrace) {
|
|
158
|
+
test.output.push(errorEvent.stackTrace);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
logger.debug('Test error', { testId, error: errorEvent.error });
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
case 'print': {
|
|
165
|
+
const printEvent = event;
|
|
166
|
+
const testId = printEvent.testID;
|
|
167
|
+
const test = state.tests.get(testId);
|
|
168
|
+
if (test) {
|
|
169
|
+
test.output.push(printEvent.message);
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case 'done': {
|
|
174
|
+
const doneEvent = event;
|
|
175
|
+
state.complete = true;
|
|
176
|
+
state.success = doneEvent.success;
|
|
177
|
+
state.completedAt = new Date();
|
|
178
|
+
logger.info('Test run complete', {
|
|
179
|
+
reference,
|
|
180
|
+
success: doneEvent.success,
|
|
181
|
+
totalTests: state.tests.size,
|
|
182
|
+
});
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
default:
|
|
186
|
+
// Ignore other event types (group, suite, etc.)
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
handleExit(reference, code, signal) {
|
|
191
|
+
const state = this.testStates.get(reference);
|
|
192
|
+
if (!state)
|
|
193
|
+
return;
|
|
194
|
+
logger.info('Flutter test process exited', { reference, code, signal });
|
|
195
|
+
if (!state.complete) {
|
|
196
|
+
state.complete = true;
|
|
197
|
+
state.completedAt = new Date();
|
|
198
|
+
state.success = code === 0;
|
|
199
|
+
}
|
|
200
|
+
this.process = undefined;
|
|
201
|
+
}
|
|
202
|
+
getProgress(reference, showAllTestNames = false, offset = 0, limit = 100) {
|
|
203
|
+
const state = this.testStates.get(reference);
|
|
204
|
+
if (!state) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
// Count tests (excluding hidden tests like suite loading tests)
|
|
208
|
+
const visibleTests = Array.from(state.tests.values()).filter((t) => !t.hidden);
|
|
209
|
+
const passes = visibleTests.filter((t) => t.result === 'success' && !t.skipped).length;
|
|
210
|
+
const fails = visibleTests.filter((t) => (t.result === 'failure' || t.result === 'error') && !t.skipped).length;
|
|
211
|
+
const progress = {
|
|
212
|
+
reference,
|
|
213
|
+
testsComplete: visibleTests.filter((t) => !t.skipped).length,
|
|
214
|
+
testsTotal: visibleTests.length,
|
|
215
|
+
passes,
|
|
216
|
+
fails,
|
|
217
|
+
complete: state.complete,
|
|
218
|
+
};
|
|
219
|
+
if (showAllTestNames) {
|
|
220
|
+
const passingTests = visibleTests
|
|
221
|
+
.filter((t) => t.result === 'success' && !t.skipped)
|
|
222
|
+
.map((t) => t.testName);
|
|
223
|
+
const failingTests = visibleTests
|
|
224
|
+
.filter((t) => (t.result === 'failure' || t.result === 'error') && !t.skipped)
|
|
225
|
+
.map((t) => t.testName);
|
|
226
|
+
// Apply pagination
|
|
227
|
+
progress.passingTests = passingTests.slice(offset, offset + limit);
|
|
228
|
+
progress.failingTests = failingTests.slice(offset, offset + limit);
|
|
229
|
+
progress.totalPassingTests = passingTests.length;
|
|
230
|
+
progress.totalFailingTests = failingTests.length;
|
|
231
|
+
progress.hasMorePassing = offset + limit < passingTests.length;
|
|
232
|
+
progress.hasMoreFailing = offset + limit < failingTests.length;
|
|
233
|
+
}
|
|
234
|
+
return progress;
|
|
235
|
+
}
|
|
236
|
+
getLogs(reference, showAll = false, offset = 0, limit = 100) {
|
|
237
|
+
const state = this.testStates.get(reference);
|
|
238
|
+
if (!state) {
|
|
239
|
+
return [];
|
|
240
|
+
}
|
|
241
|
+
const visibleTests = Array.from(state.tests.values()).filter((t) => !t.hidden);
|
|
242
|
+
const testsToShow = showAll
|
|
243
|
+
? visibleTests
|
|
244
|
+
: visibleTests.filter((t) => t.result === 'failure' || t.result === 'error');
|
|
245
|
+
const logs = testsToShow.map((test) => ({
|
|
246
|
+
testName: test.testName,
|
|
247
|
+
output: test.output.join('\n'),
|
|
248
|
+
}));
|
|
249
|
+
// Apply pagination
|
|
250
|
+
return logs.slice(offset, offset + limit);
|
|
251
|
+
}
|
|
252
|
+
cleanup(reference) {
|
|
253
|
+
if (reference !== undefined) {
|
|
254
|
+
this.testStates.delete(reference);
|
|
255
|
+
logger.debug('Cleaned up test state', { reference });
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
this.testStates.clear();
|
|
259
|
+
logger.debug('Cleaned up all test states');
|
|
260
|
+
}
|
|
261
|
+
if (this.process) {
|
|
262
|
+
this.process.kill('SIGTERM');
|
|
263
|
+
this.process = undefined;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
getAllReferences() {
|
|
267
|
+
return Array.from(this.testStates.keys());
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=test-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-manager.js","sourceRoot":"","sources":["../../src/flutter/test-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
export interface FlutterTestOptions {
|
|
2
|
+
worktreePath: string;
|
|
3
|
+
testTarget?: string;
|
|
4
|
+
testNameMatch?: string;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
tags?: string[];
|
|
7
|
+
deviceId?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface FlutterTestEvent {
|
|
10
|
+
type: string;
|
|
11
|
+
time: number;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface TestStartEvent extends FlutterTestEvent {
|
|
15
|
+
type: 'testStart';
|
|
16
|
+
test: {
|
|
17
|
+
id: number;
|
|
18
|
+
name: string;
|
|
19
|
+
suiteID: number;
|
|
20
|
+
groupIDs: number[];
|
|
21
|
+
metadata: {
|
|
22
|
+
skip: boolean;
|
|
23
|
+
skipReason: string | null;
|
|
24
|
+
};
|
|
25
|
+
line: number | null;
|
|
26
|
+
column: number | null;
|
|
27
|
+
url: string | null;
|
|
28
|
+
root_line?: number;
|
|
29
|
+
root_column?: number;
|
|
30
|
+
root_url?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface TestDoneEvent extends FlutterTestEvent {
|
|
34
|
+
type: 'testDone';
|
|
35
|
+
testID: number;
|
|
36
|
+
result: 'success' | 'failure' | 'error';
|
|
37
|
+
skipped: boolean;
|
|
38
|
+
hidden: boolean;
|
|
39
|
+
}
|
|
40
|
+
export interface ErrorEvent extends FlutterTestEvent {
|
|
41
|
+
type: 'error';
|
|
42
|
+
testID: number;
|
|
43
|
+
error: string;
|
|
44
|
+
stackTrace: string;
|
|
45
|
+
isFailure: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface PrintEvent extends FlutterTestEvent {
|
|
48
|
+
type: 'print';
|
|
49
|
+
testID: number;
|
|
50
|
+
message: string;
|
|
51
|
+
messageType: 'print' | 'skip';
|
|
52
|
+
}
|
|
53
|
+
export interface DoneEvent extends FlutterTestEvent {
|
|
54
|
+
type: 'done';
|
|
55
|
+
success: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface TestResult {
|
|
58
|
+
testId: number;
|
|
59
|
+
testName: string;
|
|
60
|
+
result: 'success' | 'failure' | 'error';
|
|
61
|
+
skipped: boolean;
|
|
62
|
+
hidden: boolean;
|
|
63
|
+
output: string[];
|
|
64
|
+
}
|
|
65
|
+
export interface FlutterTestProgress {
|
|
66
|
+
reference: number;
|
|
67
|
+
testsComplete: number;
|
|
68
|
+
testsTotal: number;
|
|
69
|
+
passes: number;
|
|
70
|
+
fails: number;
|
|
71
|
+
complete: boolean;
|
|
72
|
+
passingTests?: string[];
|
|
73
|
+
failingTests?: string[];
|
|
74
|
+
totalPassingTests?: number;
|
|
75
|
+
totalFailingTests?: number;
|
|
76
|
+
hasMorePassing?: boolean;
|
|
77
|
+
hasMoreFailing?: boolean;
|
|
78
|
+
}
|
|
79
|
+
export interface FlutterTestLog {
|
|
80
|
+
testName: string;
|
|
81
|
+
output: string;
|
|
82
|
+
}
|
|
83
|
+
export interface FlutterTestState {
|
|
84
|
+
reference: number;
|
|
85
|
+
startedAt: Date;
|
|
86
|
+
completedAt?: Date;
|
|
87
|
+
tests: Map<number, TestResult>;
|
|
88
|
+
testNames: Map<number, string>;
|
|
89
|
+
totalTests: number;
|
|
90
|
+
complete: boolean;
|
|
91
|
+
success?: boolean;
|
|
92
|
+
outputBuffer: string[];
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=test-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-types.d.ts","sourceRoot":"","sources":["../../src/flutter/test-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-types.js","sourceRoot":"","sources":["../../src/flutter/test-types.ts"],"names":[],"mappings":"AAAA,uCAAuC"}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,9 @@ import { createMCPServer } from './server.js';
|
|
|
4
4
|
import { setupTransport } from './transport.js';
|
|
5
5
|
import { sessionManager } from './session/manager.js';
|
|
6
6
|
import { logger } from './utils/logger.js';
|
|
7
|
+
import { setServerConfig } from './config.js';
|
|
8
|
+
import { tmpdir } from 'os';
|
|
9
|
+
import { join } from 'path';
|
|
7
10
|
function parseArgs() {
|
|
8
11
|
const args = process.argv.slice(2);
|
|
9
12
|
let port = parseInt(process.env.PORT || '3000', 10);
|
|
@@ -184,9 +187,30 @@ async function main() {
|
|
|
184
187
|
}
|
|
185
188
|
next(err);
|
|
186
189
|
});
|
|
190
|
+
// Set server configuration for screenshot URLs
|
|
191
|
+
setServerConfig(HOST, PORT);
|
|
187
192
|
const mcpServer = createMCPServer();
|
|
188
193
|
const transport = setupTransport(app);
|
|
189
194
|
await mcpServer.connect(transport);
|
|
195
|
+
// Screenshot endpoint - serve screenshot files
|
|
196
|
+
app.get('/screenshot/:filename', (req, res) => {
|
|
197
|
+
const { filename } = req.params;
|
|
198
|
+
// Security: only allow alphanumeric, hyphens, and image extensions (.png, .jpg, .jpeg)
|
|
199
|
+
if (!/^[a-zA-Z0-9-]+\.(png|jpe?g)$/.test(filename)) {
|
|
200
|
+
logger.warn('Invalid screenshot filename requested', { filename });
|
|
201
|
+
res.status(400).json({ error: 'Invalid filename' });
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const screenshotsDir = join(tmpdir(), 'mcp-screenshots');
|
|
205
|
+
const filepath = join(screenshotsDir, filename);
|
|
206
|
+
logger.info('Serving screenshot', { filename, filepath });
|
|
207
|
+
res.sendFile(filepath, (err) => {
|
|
208
|
+
if (err) {
|
|
209
|
+
logger.error('Error serving screenshot', { filename, error: String(err) });
|
|
210
|
+
res.status(404).json({ error: 'Screenshot not found' });
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
});
|
|
190
214
|
app.get('/health', (_req, res) => {
|
|
191
215
|
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
192
216
|
});
|