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.
Files changed (55) hide show
  1. package/CLAUDE.md +14 -5
  2. package/README.md +34 -10
  3. package/dist/config.d.ts +4 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +22 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/flutter/test-manager.d.ts +15 -0
  8. package/dist/flutter/test-manager.d.ts.map +1 -0
  9. package/dist/flutter/test-manager.js +270 -0
  10. package/dist/flutter/test-manager.js.map +1 -0
  11. package/dist/flutter/test-types.d.ts +94 -0
  12. package/dist/flutter/test-types.d.ts.map +1 -0
  13. package/dist/flutter/test-types.js +3 -0
  14. package/dist/flutter/test-types.js.map +1 -0
  15. package/dist/index.js +24 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/session/manager.d.ts +13 -3
  18. package/dist/session/manager.d.ts.map +1 -1
  19. package/dist/session/manager.js +60 -35
  20. package/dist/session/manager.js.map +1 -1
  21. package/dist/session/state.d.ts +1 -0
  22. package/dist/session/state.d.ts.map +1 -1
  23. package/dist/session/state.js +3 -0
  24. package/dist/session/state.js.map +1 -1
  25. package/dist/session/types.d.ts +4 -2
  26. package/dist/session/types.d.ts.map +1 -1
  27. package/dist/simulator/idb.d.ts +1 -0
  28. package/dist/simulator/idb.d.ts.map +1 -1
  29. package/dist/simulator/idb.js +25 -6
  30. package/dist/simulator/idb.js.map +1 -1
  31. package/dist/simulator/simctl.d.ts.map +1 -1
  32. package/dist/simulator/simctl.js +6 -3
  33. package/dist/simulator/simctl.js.map +1 -1
  34. package/dist/tools/flutter-commands.d.ts.map +1 -1
  35. package/dist/tools/flutter-commands.js +8 -1
  36. package/dist/tools/flutter-commands.js.map +1 -1
  37. package/dist/tools/flutter-test.d.ts +70 -0
  38. package/dist/tools/flutter-test.d.ts.map +1 -0
  39. package/dist/tools/flutter-test.js +154 -0
  40. package/dist/tools/flutter-test.js.map +1 -0
  41. package/dist/tools/index.d.ts.map +1 -1
  42. package/dist/tools/index.js +111 -12
  43. package/dist/tools/index.js.map +1 -1
  44. package/dist/tools/session.d.ts +15 -4
  45. package/dist/tools/session.d.ts.map +1 -1
  46. package/dist/tools/session.js +14 -3
  47. package/dist/tools/session.js.map +1 -1
  48. package/dist/tools/simulator-ui.d.ts +1 -0
  49. package/dist/tools/simulator-ui.d.ts.map +1 -1
  50. package/dist/tools/simulator-ui.js +48 -0
  51. package/dist/tools/simulator-ui.js.map +1 -1
  52. package/dist/transport.d.ts.map +1 -1
  53. package/dist/transport.js +6 -0
  54. package/dist/transport.js.map +1 -1
  55. 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 with a simulator
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 and return results
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", simulatorUdid: "..." }
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 (Docker-compatible)
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 with a simulator
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 return results
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", simulatorUdid: "..." }
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. Clean up when done
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
- - They work automatically with Docker containers (no filesystem needed)
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. **Image Transport**: Screenshots are returned as base64 PNG in MCP responses
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
@@ -0,0 +1,4 @@
1
+ export declare function setServerConfig(host: string, port: number): void;
2
+ export declare function updateHostHeader(hostHeader: string): void;
3
+ export declare function getServerBaseUrl(): string;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -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,3 @@
1
+ // Types for Flutter test functionality
2
+ export {};
3
+ //# sourceMappingURL=test-types.js.map
@@ -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
  });