docker-flutter-ios-simulator-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CLAUDE.md +74 -0
  2. package/LICENSE +21 -0
  3. package/README.md +370 -0
  4. package/dist/flutter/log-buffer.d.ts +14 -0
  5. package/dist/flutter/log-buffer.d.ts.map +1 -0
  6. package/dist/flutter/log-buffer.js +49 -0
  7. package/dist/flutter/log-buffer.js.map +1 -0
  8. package/dist/flutter/process.d.ts +30 -0
  9. package/dist/flutter/process.d.ts.map +1 -0
  10. package/dist/flutter/process.js +241 -0
  11. package/dist/flutter/process.js.map +1 -0
  12. package/dist/flutter/types.d.ts +31 -0
  13. package/dist/flutter/types.d.ts.map +1 -0
  14. package/dist/flutter/types.js +2 -0
  15. package/dist/flutter/types.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +207 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/server.d.ts +3 -0
  21. package/dist/server.d.ts.map +1 -0
  22. package/dist/server.js +18 -0
  23. package/dist/server.js.map +1 -0
  24. package/dist/session/manager.d.ts +74 -0
  25. package/dist/session/manager.d.ts.map +1 -0
  26. package/dist/session/manager.js +209 -0
  27. package/dist/session/manager.js.map +1 -0
  28. package/dist/session/state.d.ts +14 -0
  29. package/dist/session/state.d.ts.map +1 -0
  30. package/dist/session/state.js +42 -0
  31. package/dist/session/state.js.map +1 -0
  32. package/dist/session/types.d.ts +26 -0
  33. package/dist/session/types.d.ts.map +1 -0
  34. package/dist/session/types.js +2 -0
  35. package/dist/session/types.js.map +1 -0
  36. package/dist/simulator/idb.d.ts +42 -0
  37. package/dist/simulator/idb.d.ts.map +1 -0
  38. package/dist/simulator/idb.js +127 -0
  39. package/dist/simulator/idb.js.map +1 -0
  40. package/dist/simulator/simctl.d.ts +9 -0
  41. package/dist/simulator/simctl.d.ts.map +1 -0
  42. package/dist/simulator/simctl.js +96 -0
  43. package/dist/simulator/simctl.js.map +1 -0
  44. package/dist/simulator/types.d.ts +25 -0
  45. package/dist/simulator/types.d.ts.map +1 -0
  46. package/dist/simulator/types.js +2 -0
  47. package/dist/simulator/types.js.map +1 -0
  48. package/dist/tools/flutter-commands.d.ts +128 -0
  49. package/dist/tools/flutter-commands.d.ts.map +1 -0
  50. package/dist/tools/flutter-commands.js +275 -0
  51. package/dist/tools/flutter-commands.js.map +1 -0
  52. package/dist/tools/index.d.ts +3 -0
  53. package/dist/tools/index.d.ts.map +1 -0
  54. package/dist/tools/index.js +499 -0
  55. package/dist/tools/index.js.map +1 -0
  56. package/dist/tools/session.d.ts +38 -0
  57. package/dist/tools/session.d.ts.map +1 -0
  58. package/dist/tools/session.js +40 -0
  59. package/dist/tools/session.js.map +1 -0
  60. package/dist/tools/simulator-ui.d.ts +102 -0
  61. package/dist/tools/simulator-ui.d.ts.map +1 -0
  62. package/dist/tools/simulator-ui.js +117 -0
  63. package/dist/tools/simulator-ui.js.map +1 -0
  64. package/dist/tools/simulator.d.ts +7 -0
  65. package/dist/tools/simulator.d.ts.map +1 -0
  66. package/dist/tools/simulator.js +8 -0
  67. package/dist/tools/simulator.js.map +1 -0
  68. package/dist/transport.d.ts +4 -0
  69. package/dist/transport.d.ts.map +1 -0
  70. package/dist/transport.js +20 -0
  71. package/dist/transport.js.map +1 -0
  72. package/dist/utils/exec.d.ts +29 -0
  73. package/dist/utils/exec.d.ts.map +1 -0
  74. package/dist/utils/exec.js +109 -0
  75. package/dist/utils/exec.js.map +1 -0
  76. package/dist/utils/logger.d.ts +17 -0
  77. package/dist/utils/logger.d.ts.map +1 -0
  78. package/dist/utils/logger.js +60 -0
  79. package/dist/utils/logger.js.map +1 -0
  80. package/package.json +76 -0
@@ -0,0 +1,241 @@
1
+ import { spawnStreaming } from '../utils/exec.js';
2
+ import { logger } from '../utils/logger.js';
3
+ import { LogBuffer } from './log-buffer.js';
4
+ import { existsSync } from 'fs';
5
+ import { resolve } from 'path';
6
+ export class FlutterProcessManager {
7
+ process;
8
+ flutterProcess;
9
+ logBuffer;
10
+ logSubscribers = new Set();
11
+ constructor(maxLogLines = 1000) {
12
+ this.logBuffer = new LogBuffer(maxLogLines);
13
+ }
14
+ // eslint-disable-next-line @typescript-eslint/require-await
15
+ async start(options) {
16
+ if (this.process) {
17
+ throw new Error('Flutter process already running');
18
+ }
19
+ logger.info('Starting Flutter process', {
20
+ worktreePath: options.worktreePath,
21
+ deviceId: options.deviceId,
22
+ });
23
+ // Security: Validate target file
24
+ // Prevents malicious clients from accessing arbitrary files on the system
25
+ if (options.target) {
26
+ const targetPath = resolve(options.worktreePath, options.target);
27
+ // Prevent path traversal attacks (e.g., "../../../etc/passwd")
28
+ // Ensures the target file is within the project directory boundary
29
+ if (!targetPath.startsWith(resolve(options.worktreePath))) {
30
+ throw new Error(`Security: Target file must be within project directory. ` +
31
+ `Target: ${options.target}`);
32
+ }
33
+ // Validate file exists to provide early feedback
34
+ if (!existsSync(targetPath)) {
35
+ throw new Error(`Target file does not exist: ${options.target}`);
36
+ }
37
+ // Only .dart files are valid Flutter entry points
38
+ // Prevents attempts to execute arbitrary file types
39
+ if (!targetPath.endsWith('.dart')) {
40
+ throw new Error(`Target must be a Dart file (.dart extension). ` +
41
+ `Provided: ${options.target}`);
42
+ }
43
+ }
44
+ // Security: Validate flavor name
45
+ // Prevents command injection through flavor parameter
46
+ if (options.flavor) {
47
+ // Only allow safe characters that cannot break shell command parsing
48
+ if (!/^[a-zA-Z0-9_-]+$/.test(options.flavor)) {
49
+ throw new Error(`Invalid flavor name: ${options.flavor}. ` +
50
+ `Flavor must contain only letters, numbers, hyphens, and underscores.`);
51
+ }
52
+ }
53
+ // Security: Validate additionalArgs against allowlist
54
+ // Prevents command injection by only allowing known-safe Flutter arguments
55
+ // This protects against malicious arguments like "--dart-define=KEY=$(rm -rf /)"
56
+ if (options.additionalArgs) {
57
+ const ALLOWED_ARGS = new Set([
58
+ '--debug',
59
+ '--release',
60
+ '--profile',
61
+ '--no-sound-null-safety',
62
+ '--enable-software-rendering',
63
+ '--verbose',
64
+ '-v',
65
+ ]);
66
+ for (const arg of options.additionalArgs) {
67
+ // Check if argument is in the allowlist
68
+ if (ALLOWED_ARGS.has(arg)) {
69
+ continue;
70
+ }
71
+ // Validate --dart-define format more strictly to prevent injection
72
+ // Example attack prevented: --dart-define=X=$(malicious_command)
73
+ if (arg.startsWith('--dart-define=')) {
74
+ const defineValue = arg.substring('--dart-define='.length);
75
+ // Enforce KEY=VALUE where KEY follows identifier rules (alphanumeric/underscore)
76
+ // This prevents shell metacharacters and command substitution
77
+ if (!/^[A-Za-z_][A-Za-z0-9_]*=.*$/.test(defineValue)) {
78
+ throw new Error(`Invalid --dart-define format: ${arg}. ` +
79
+ `Expected --dart-define=KEY=VALUE where KEY is alphanumeric/underscore starting with letter or underscore`);
80
+ }
81
+ continue;
82
+ }
83
+ // If we get here, argument is not allowed - reject it
84
+ throw new Error(`Invalid Flutter argument: ${arg}. ` +
85
+ `Allowed arguments: ${Array.from(ALLOWED_ARGS).join(', ')}, --dart-define=KEY=VALUE`);
86
+ }
87
+ }
88
+ const args = ['run', '-d', options.deviceId];
89
+ if (options.target) {
90
+ args.push('-t', options.target);
91
+ }
92
+ if (options.flavor) {
93
+ args.push('--flavor', options.flavor);
94
+ }
95
+ if (options.additionalArgs) {
96
+ args.push(...options.additionalArgs);
97
+ }
98
+ this.flutterProcess = {
99
+ pid: 0,
100
+ status: 'starting',
101
+ startedAt: new Date(),
102
+ };
103
+ this.process = spawnStreaming('flutter', args, {
104
+ cwd: options.worktreePath,
105
+ onStdout: (data) => {
106
+ this.handleOutput(data);
107
+ },
108
+ onStderr: (data) => {
109
+ this.handleOutput(data);
110
+ },
111
+ onExit: (code, signal) => {
112
+ this.handleExit(code, signal);
113
+ },
114
+ });
115
+ if (this.process.pid) {
116
+ this.flutterProcess.pid = this.process.pid;
117
+ this.flutterProcess.status = 'running';
118
+ logger.info('Flutter process started', { pid: this.process.pid });
119
+ }
120
+ return this.flutterProcess;
121
+ }
122
+ handleOutput(data) {
123
+ const lines = data.split('\n');
124
+ for (const line of lines) {
125
+ if (line.trim()) {
126
+ this.logBuffer.append(line);
127
+ this.logSubscribers.forEach((subscriber) => {
128
+ try {
129
+ subscriber(line);
130
+ }
131
+ catch (error) {
132
+ logger.error('Error in log subscriber', { error: String(error) });
133
+ }
134
+ });
135
+ this.detectStatusChanges(line);
136
+ }
137
+ }
138
+ }
139
+ detectStatusChanges(line) {
140
+ if (!this.flutterProcess)
141
+ return;
142
+ if (line.includes('Hot reload') || line.includes('Reloaded')) {
143
+ this.flutterProcess.status = 'hot-reloading';
144
+ setTimeout(() => {
145
+ if (this.flutterProcess) {
146
+ this.flutterProcess.status = 'running';
147
+ }
148
+ }, 1000);
149
+ }
150
+ }
151
+ handleExit(code, signal) {
152
+ logger.info('Flutter process exited', { code, signal });
153
+ if (this.flutterProcess) {
154
+ this.flutterProcess.status = code === 0 ? 'stopped' : 'failed';
155
+ this.flutterProcess.stoppedAt = new Date();
156
+ this.flutterProcess.exitCode = code ?? undefined;
157
+ }
158
+ this.process = undefined;
159
+ }
160
+ stop() {
161
+ if (!this.process) {
162
+ logger.warn('No Flutter process to stop');
163
+ return false;
164
+ }
165
+ logger.info('Stopping Flutter process', { pid: this.process.pid });
166
+ this.process.stdin.write('q\n');
167
+ this.process.stdin.end();
168
+ return true;
169
+ }
170
+ hotReload() {
171
+ if (!this.process) {
172
+ logger.warn('No Flutter process for hot reload');
173
+ return false;
174
+ }
175
+ logger.info('Triggering hot reload', { pid: this.process.pid });
176
+ this.process.stdin.write('r\n');
177
+ return true;
178
+ }
179
+ hotRestart() {
180
+ if (!this.process) {
181
+ logger.warn('No Flutter process for hot restart');
182
+ return false;
183
+ }
184
+ logger.info('Triggering hot restart', { pid: this.process.pid });
185
+ this.process.stdin.write('R\n');
186
+ return true;
187
+ }
188
+ kill(signal = 'SIGTERM') {
189
+ if (!this.process) {
190
+ return false;
191
+ }
192
+ logger.info('Killing Flutter process', { pid: this.process.pid, signal });
193
+ return this.process.kill(signal);
194
+ }
195
+ getStatus() {
196
+ return this.flutterProcess;
197
+ }
198
+ getLogs(fromIndex, limit) {
199
+ const logs = this.logBuffer.getLogs(fromIndex, limit);
200
+ return {
201
+ logs,
202
+ nextIndex: this.logBuffer.getNextIndex(),
203
+ totalLines: this.logBuffer.getTotalLines(),
204
+ };
205
+ }
206
+ subscribeToLogs(callback) {
207
+ this.logSubscribers.add(callback);
208
+ return () => {
209
+ this.logSubscribers.delete(callback);
210
+ };
211
+ }
212
+ clearLogs() {
213
+ this.logBuffer.clear();
214
+ }
215
+ async cleanup() {
216
+ logger.debug('Cleaning up Flutter process manager');
217
+ this.logSubscribers.clear();
218
+ // Capture process reference to avoid race condition
219
+ const currentProcess = this.process;
220
+ if (currentProcess) {
221
+ this.stop();
222
+ await new Promise((resolve) => {
223
+ const timeout = setTimeout(() => {
224
+ logger.warn('Flutter process did not stop gracefully, killing');
225
+ this.kill('SIGKILL');
226
+ resolve();
227
+ }, 5000);
228
+ currentProcess.wait().then(() => {
229
+ clearTimeout(timeout);
230
+ resolve();
231
+ }).catch((error) => {
232
+ logger.error('Error waiting for process', { error: String(error) });
233
+ clearTimeout(timeout);
234
+ resolve();
235
+ });
236
+ });
237
+ }
238
+ this.clearLogs();
239
+ }
240
+ }
241
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/flutter/process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ export type FlutterProcessStatus = 'starting' | 'running' | 'hot-reloading' | 'stopped' | 'failed';
2
+ export interface FlutterProcess {
3
+ pid: number;
4
+ status: FlutterProcessStatus;
5
+ startedAt: Date;
6
+ stoppedAt?: Date;
7
+ exitCode?: number;
8
+ }
9
+ export interface LogEntry {
10
+ line: string;
11
+ timestamp: Date;
12
+ index: number;
13
+ }
14
+ export interface FlutterRunOptions {
15
+ worktreePath: string;
16
+ deviceId: string;
17
+ flavor?: string;
18
+ target?: string;
19
+ additionalArgs?: string[];
20
+ }
21
+ export interface FlutterLogsQuery {
22
+ sessionId: string;
23
+ fromIndex?: number;
24
+ limit?: number;
25
+ }
26
+ export interface FlutterLogsResult {
27
+ logs: LogEntry[];
28
+ nextIndex: number;
29
+ totalLines: number;
30
+ }
31
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/flutter/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAC5B,UAAU,GACV,SAAS,GACT,eAAe,GACf,SAAS,GACT,QAAQ,CAAC;AAEb,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/flutter/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env node
2
+ import express from 'express';
3
+ import { createMCPServer } from './server.js';
4
+ import { setupTransport } from './transport.js';
5
+ import { sessionManager } from './session/manager.js';
6
+ import { logger } from './utils/logger.js';
7
+ function parseArgs() {
8
+ const args = process.argv.slice(2);
9
+ let port = parseInt(process.env.PORT || '3000', 10);
10
+ let host = process.env.HOST || '127.0.0.1';
11
+ let help = false;
12
+ let allowOnly = process.env.ALLOW_ONLY || '/Users/';
13
+ let maxSessions = parseInt(process.env.MAX_SESSIONS || '10', 10);
14
+ let preBuildScript = process.env.PRE_BUILD_SCRIPT;
15
+ let postBuildScript = process.env.POST_BUILD_SCRIPT;
16
+ for (let i = 0; i < args.length; i++) {
17
+ const arg = args[i];
18
+ if (arg === '--help' || arg === '-h') {
19
+ help = true;
20
+ }
21
+ else if (arg === '--port' || arg === '-p') {
22
+ const portValue = args[++i];
23
+ if (!portValue || isNaN(parseInt(portValue, 10))) {
24
+ console.error('Error: --port requires a numeric value');
25
+ process.exit(1);
26
+ }
27
+ port = parseInt(portValue, 10);
28
+ }
29
+ else if (arg === '--host') {
30
+ const hostValue = args[++i];
31
+ if (!hostValue) {
32
+ console.error('Error: --host requires a value');
33
+ process.exit(1);
34
+ }
35
+ host = hostValue;
36
+ }
37
+ else if (arg === '--allow-only') {
38
+ const pathValue = args[++i];
39
+ if (!pathValue) {
40
+ console.error('Error: --allow-only requires a path value');
41
+ process.exit(1);
42
+ }
43
+ allowOnly = pathValue;
44
+ }
45
+ else if (arg === '--max-sessions') {
46
+ const maxValue = args[++i];
47
+ if (!maxValue || isNaN(parseInt(maxValue, 10))) {
48
+ console.error('Error: --max-sessions requires a numeric value');
49
+ process.exit(1);
50
+ }
51
+ maxSessions = parseInt(maxValue, 10);
52
+ if (maxSessions < 1) {
53
+ console.error('Error: --max-sessions must be at least 1');
54
+ process.exit(1);
55
+ }
56
+ }
57
+ else if (arg === '--pre-build-script') {
58
+ const scriptValue = args[++i];
59
+ if (!scriptValue) {
60
+ console.error('Error: --pre-build-script requires a command value');
61
+ process.exit(1);
62
+ }
63
+ preBuildScript = scriptValue;
64
+ }
65
+ else if (arg === '--post-build-script') {
66
+ const scriptValue = args[++i];
67
+ if (!scriptValue) {
68
+ console.error('Error: --post-build-script requires a command value');
69
+ process.exit(1);
70
+ }
71
+ postBuildScript = scriptValue;
72
+ }
73
+ else {
74
+ console.error(`Error: Unknown argument: ${arg}`);
75
+ console.error('Use --help to see available options');
76
+ process.exit(1);
77
+ }
78
+ }
79
+ return { port, host, help, allowOnly, maxSessions, preBuildScript, postBuildScript };
80
+ }
81
+ function showHelp() {
82
+ console.log(`
83
+ docker-flutter-ios-simulator-mcp - MCP server for containerized Flutter iOS development
84
+
85
+ USAGE:
86
+ docker-flutter-ios-simulator-mcp [OPTIONS]
87
+
88
+ OPTIONS:
89
+ -p, --port <port> Port to listen on (default: 3000)
90
+ --host <host> Host address to bind to (default: 127.0.0.1)
91
+ --allow-only <path> Only allow Flutter projects under this path (default: /Users/)
92
+ --max-sessions <number> Maximum number of concurrent sessions (default: 10)
93
+ --pre-build-script <cmd> Command to run before flutter build/run (e.g., "git pull")
94
+ --post-build-script <cmd> Command to run after flutter build/run completes
95
+ -h, --help Show this help message
96
+
97
+ ENVIRONMENT VARIABLES:
98
+ PORT Port to listen on (overridden by --port)
99
+ HOST Host address to bind to (overridden by --host)
100
+ ALLOW_ONLY Path prefix for allowed projects (overridden by --allow-only)
101
+ MAX_SESSIONS Maximum concurrent sessions (overridden by --max-sessions)
102
+ PRE_BUILD_SCRIPT Command to run before builds (overridden by --pre-build-script)
103
+ POST_BUILD_SCRIPT Command to run after builds (overridden by --post-build-script)
104
+ LOG_LEVEL Logging level (debug, info, warn, error)
105
+
106
+ EXAMPLES:
107
+ docker-flutter-ios-simulator-mcp
108
+ docker-flutter-ios-simulator-mcp --port 8080
109
+ docker-flutter-ios-simulator-mcp --port 3000 --host localhost
110
+ docker-flutter-ios-simulator-mcp --allow-only /Users/alice/projects
111
+ docker-flutter-ios-simulator-mcp --max-sessions 20
112
+ docker-flutter-ios-simulator-mcp --pre-build-script "git pull" --post-build-script "echo Done"
113
+ PORT=8080 docker-flutter-ios-simulator-mcp
114
+
115
+ SECURITY:
116
+ By default, only Flutter projects under /Users/ are allowed to prevent
117
+ malicious MCP clients from accessing system directories like /etc/, /usr/, etc.
118
+
119
+ For more information, visit: https://github.com/zafnz/docker-flutter-ios-simulator-mcp
120
+ `);
121
+ }
122
+ async function main() {
123
+ const { port, host, help, allowOnly, maxSessions, preBuildScript, postBuildScript } = parseArgs();
124
+ if (help) {
125
+ showHelp();
126
+ process.exit(0);
127
+ }
128
+ // Configure session manager with allowed path prefix and session limit
129
+ sessionManager.configure(allowOnly, maxSessions, preBuildScript, postBuildScript);
130
+ const PORT = port;
131
+ const HOST = host;
132
+ const app = express();
133
+ // Middleware
134
+ app.use(express.json());
135
+ app.use(express.urlencoded({ extended: true }));
136
+ // Error handling middleware for JSON parse errors
137
+ app.use((err, req, res, next) => {
138
+ if (err instanceof SyntaxError && 'body' in err) {
139
+ logger.warn('Invalid JSON in request', { error: err.message, path: req.path });
140
+ res.status(400).json({ error: 'Invalid JSON in request body' });
141
+ return;
142
+ }
143
+ next(err);
144
+ });
145
+ const mcpServer = createMCPServer();
146
+ const transport = setupTransport(app);
147
+ await mcpServer.connect(transport);
148
+ app.get('/health', (_req, res) => {
149
+ res.json({ status: 'ok', timestamp: new Date().toISOString() });
150
+ });
151
+ const server = app.listen(PORT, HOST, () => {
152
+ logger.info('Server started', {
153
+ host: HOST,
154
+ port: String(PORT),
155
+ mcpEndpoint: `http://${HOST}:${String(PORT)}/mcp`,
156
+ });
157
+ });
158
+ server.on('error', (error) => {
159
+ if (error.code === 'EADDRINUSE') {
160
+ logger.error(`Port ${String(PORT)} is already in use`);
161
+ process.exit(1);
162
+ }
163
+ else {
164
+ logger.error('Server error', { error: error.message });
165
+ process.exit(1);
166
+ }
167
+ });
168
+ const shutdown = async () => {
169
+ logger.info('Shutting down server');
170
+ // Forcefully exit after timeout to prevent hanging
171
+ const forceExitTimeout = setTimeout(() => {
172
+ logger.warn('Forced shutdown after timeout');
173
+ process.exit(0);
174
+ }, 10000);
175
+ try {
176
+ await sessionManager.cleanup();
177
+ await transport.close();
178
+ server.close(() => {
179
+ clearTimeout(forceExitTimeout);
180
+ logger.info('Server closed');
181
+ process.exit(0);
182
+ });
183
+ }
184
+ catch (error) {
185
+ logger.error('Error during shutdown', { error: String(error) });
186
+ clearTimeout(forceExitTimeout);
187
+ process.exit(1);
188
+ }
189
+ };
190
+ process.on('SIGTERM', () => {
191
+ shutdown().catch((error) => {
192
+ logger.error('Fatal error during shutdown', { error: String(error) });
193
+ process.exit(1);
194
+ });
195
+ });
196
+ process.on('SIGINT', () => {
197
+ shutdown().catch((error) => {
198
+ logger.error('Fatal error during shutdown', { error: String(error) });
199
+ process.exit(1);
200
+ });
201
+ });
202
+ }
203
+ main().catch((error) => {
204
+ logger.error('Fatal error', { error: String(error) });
205
+ process.exit(1);
206
+ });
207
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function createMCPServer(): McpServer;
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,eAAe,IAAI,SAAS,CAmB3C"}
package/dist/server.js ADDED
@@ -0,0 +1,18 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { registerTools } from './tools/index.js';
3
+ import { logger } from './utils/logger.js';
4
+ export function createMCPServer() {
5
+ logger.info('Creating MCP server');
6
+ const server = new McpServer({
7
+ name: 'docker-flutter-ios-simulator-mcp',
8
+ version: '0.1.0',
9
+ }, {
10
+ capabilities: {
11
+ tools: {},
12
+ },
13
+ });
14
+ registerTools(server);
15
+ logger.info('MCP server created');
16
+ return server;
17
+ }
18
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,UAAU,eAAe;IAC7B,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAEnC,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;QACE,IAAI,EAAE,kCAAkC;QACxC,OAAO,EAAE,OAAO;KACjB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;SACV;KACF,CACF,CAAC;IAEF,aAAa,CAAC,MAAM,CAAC,CAAC;IAEtB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAClC,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { CreateSessionParams, Session, SessionInfo } from './types.js';
2
+ export declare class SessionManager {
3
+ private allowedPathPrefix;
4
+ private maxSessions;
5
+ private preBuildScript?;
6
+ private postBuildScript?;
7
+ constructor(allowedPathPrefix?: string, maxSessions?: number);
8
+ /**
9
+ * Configure the session manager with security settings.
10
+ *
11
+ * @param allowedPathPrefix - Absolute path prefix for allowed Flutter projects (e.g., "/Users/")
12
+ * @param maxSessions - Optional maximum number of concurrent sessions
13
+ * @param preBuildScript - Optional command to run before flutter build/run
14
+ * @param postBuildScript - Optional command to run after flutter build/run
15
+ *
16
+ * @example
17
+ * sessionManager.configure('/Users/alice/projects', 20, 'git pull', 'echo Done');
18
+ */
19
+ configure(allowedPathPrefix: string, maxSessions?: number, preBuildScript?: string, postBuildScript?: string): void;
20
+ /**
21
+ * Create a new development session with an iOS Simulator.
22
+ *
23
+ * Creates a fresh iOS Simulator, boots it, and associates it with the Flutter project directory.
24
+ * Validates that the project path is within allowed directories and contains a valid Flutter project.
25
+ *
26
+ * @param params - Session creation parameters
27
+ * @param params.worktreePath - Absolute path to Flutter project directory (must contain pubspec.yaml)
28
+ * @param params.deviceType - iOS device type to simulate (default: "iPhone 16 Pro")
29
+ *
30
+ * @returns Session information including unique ID and simulator UDID
31
+ *
32
+ * @throws {Error} If session limit is reached
33
+ * @throws {Error} If path is outside allowed prefix
34
+ * @throws {Error} If project directory doesn't exist or isn't valid
35
+ * @throws {Error} If simulator creation or boot fails
36
+ *
37
+ * @example
38
+ * const session = await sessionManager.createSession({
39
+ * worktreePath: '/Users/alice/my-flutter-app',
40
+ * deviceType: 'iPhone 16 Pro'
41
+ * });
42
+ * // Returns: { id: 'uuid...', worktreePath: '...', simulatorUdid: '...', deviceType: '...' }
43
+ */
44
+ createSession(params: CreateSessionParams): Promise<SessionInfo>;
45
+ endSession(sessionId: string): Promise<void>;
46
+ /**
47
+ * List all active sessions.
48
+ *
49
+ * @returns Array of session information for all active sessions
50
+ */
51
+ listSessions(): SessionInfo[];
52
+ /**
53
+ * Get detailed information about a specific session.
54
+ *
55
+ * @param sessionId - Unique session identifier from createSession()
56
+ * @returns Session object with full state, or undefined if not found
57
+ */
58
+ getSession(sessionId: string): Session | undefined;
59
+ /**
60
+ * Get the pre-build script command if configured.
61
+ *
62
+ * @returns Pre-build script command, or undefined if not set
63
+ */
64
+ getPreBuildScript(): string | undefined;
65
+ /**
66
+ * Get the post-build script command if configured.
67
+ *
68
+ * @returns Post-build script command, or undefined if not set
69
+ */
70
+ getPostBuildScript(): string | undefined;
71
+ cleanup(): Promise<void>;
72
+ }
73
+ export declare const sessionManager: SessionManager;
74
+ //# sourceMappingURL=manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIvE,qBAAa,cAAc;IACzB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,eAAe,CAAC,CAAS;gBAErB,iBAAiB,SAAY,EAAE,WAAW,SAAK;IAK3D;;;;;;;;;;OAUG;IACH,SAAS,CACP,iBAAiB,EAAE,MAAM,EACzB,WAAW,CAAC,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,MAAM,EACvB,eAAe,CAAC,EAAE,MAAM,GACvB,IAAI;IAeP;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAC;IA8EhE,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4ClD;;;;OAIG;IACH,YAAY,IAAI,WAAW,EAAE;IAI7B;;;;;OAKG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS;IAIlD;;;;OAIG;IACH,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAIvC;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,GAAG,SAAS;IAIlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAe/B;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"}