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.
- package/CLAUDE.md +74 -0
- package/LICENSE +21 -0
- package/README.md +370 -0
- package/dist/flutter/log-buffer.d.ts +14 -0
- package/dist/flutter/log-buffer.d.ts.map +1 -0
- package/dist/flutter/log-buffer.js +49 -0
- package/dist/flutter/log-buffer.js.map +1 -0
- package/dist/flutter/process.d.ts +30 -0
- package/dist/flutter/process.d.ts.map +1 -0
- package/dist/flutter/process.js +241 -0
- package/dist/flutter/process.js.map +1 -0
- package/dist/flutter/types.d.ts +31 -0
- package/dist/flutter/types.d.ts.map +1 -0
- package/dist/flutter/types.js +2 -0
- package/dist/flutter/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +207 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +18 -0
- package/dist/server.js.map +1 -0
- package/dist/session/manager.d.ts +74 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +209 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/session/state.d.ts +14 -0
- package/dist/session/state.d.ts.map +1 -0
- package/dist/session/state.js +42 -0
- package/dist/session/state.js.map +1 -0
- package/dist/session/types.d.ts +26 -0
- package/dist/session/types.d.ts.map +1 -0
- package/dist/session/types.js +2 -0
- package/dist/session/types.js.map +1 -0
- package/dist/simulator/idb.d.ts +42 -0
- package/dist/simulator/idb.d.ts.map +1 -0
- package/dist/simulator/idb.js +127 -0
- package/dist/simulator/idb.js.map +1 -0
- package/dist/simulator/simctl.d.ts +9 -0
- package/dist/simulator/simctl.d.ts.map +1 -0
- package/dist/simulator/simctl.js +96 -0
- package/dist/simulator/simctl.js.map +1 -0
- package/dist/simulator/types.d.ts +25 -0
- package/dist/simulator/types.d.ts.map +1 -0
- package/dist/simulator/types.js +2 -0
- package/dist/simulator/types.js.map +1 -0
- package/dist/tools/flutter-commands.d.ts +128 -0
- package/dist/tools/flutter-commands.d.ts.map +1 -0
- package/dist/tools/flutter-commands.js +275 -0
- package/dist/tools/flutter-commands.js.map +1 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +499 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/session.d.ts +38 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +40 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/simulator-ui.d.ts +102 -0
- package/dist/tools/simulator-ui.d.ts.map +1 -0
- package/dist/tools/simulator-ui.js +117 -0
- package/dist/tools/simulator-ui.js.map +1 -0
- package/dist/tools/simulator.d.ts +7 -0
- package/dist/tools/simulator.d.ts.map +1 -0
- package/dist/tools/simulator.js +8 -0
- package/dist/tools/simulator.js.map +1 -0
- package/dist/transport.d.ts +4 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +20 -0
- package/dist/transport.js.map +1 -0
- package/dist/utils/exec.d.ts +29 -0
- package/dist/utils/exec.d.ts.map +1 -0
- package/dist/utils/exec.js +109 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +60 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +76 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Information and instructions to AI agents using this MCP
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This MCP provides access to and control of building flutter apps, launching the iOS simulator, and interacting with it, including taking screenshots and getting accessibility info.
|
|
8
|
+
|
|
9
|
+
### 3. Use the Tools
|
|
10
|
+
|
|
11
|
+
The MCP server provides these tools to AI agents:
|
|
12
|
+
|
|
13
|
+
**Session Management:**
|
|
14
|
+
- `session_start` - Create a new development session with a simulator
|
|
15
|
+
- `session_end` - Clean up and delete the simulator
|
|
16
|
+
- `session_list` - View active sessions
|
|
17
|
+
|
|
18
|
+
**Flutter Development:**
|
|
19
|
+
- `flutter_run` - Build and launch your app
|
|
20
|
+
- `flutter_build` - Build iOS app without running (for CI/deployment)
|
|
21
|
+
- `flutter_test` - Run Flutter tests and return results
|
|
22
|
+
- `flutter_clean` - Clean build cache and artifacts
|
|
23
|
+
- `flutter_logs` - Monitor build progress and app output
|
|
24
|
+
- `flutter_hot_reload` - Apply code changes instantly
|
|
25
|
+
- `flutter_hot_restart` - Restart the app
|
|
26
|
+
- `flutter_stop` - Stop the running app
|
|
27
|
+
|
|
28
|
+
**UI Interaction:**
|
|
29
|
+
- `screenshot` - Capture and view the simulator screen
|
|
30
|
+
- `ui_tap` - Tap at coordinates
|
|
31
|
+
- `ui_swipe` - Swipe gestures (scrolling, swiping)
|
|
32
|
+
- `ui_type` - Enter text into fields
|
|
33
|
+
- `ui_describe_all` - Get accessibility tree
|
|
34
|
+
- `ui_describe_point` - Inspect element at coordinates
|
|
35
|
+
|
|
36
|
+
**Device Management:**
|
|
37
|
+
- `simulator_list` - See available iOS device types
|
|
38
|
+
|
|
39
|
+
## Example Workflow
|
|
40
|
+
|
|
41
|
+
Here's a typical AI agent workflow:
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
// 1. Start a session with your Flutter project
|
|
45
|
+
session_start({
|
|
46
|
+
worktreePath: "/path/to/your/flutter/project",
|
|
47
|
+
deviceType: "iPhone 16 Pro"
|
|
48
|
+
})
|
|
49
|
+
// Returns: { sessionId: "abc-123", simulatorUdid: "..." }
|
|
50
|
+
|
|
51
|
+
// 2. Run the Flutter app
|
|
52
|
+
flutter_run({ sessionId: "abc-123" })
|
|
53
|
+
|
|
54
|
+
// 3. Monitor build progress (poll every few seconds)
|
|
55
|
+
flutter_logs({
|
|
56
|
+
sessionId: "abc-123",
|
|
57
|
+
fromIndex: 0, // Start from beginning
|
|
58
|
+
limit: 100 // Get 100 lines
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// 4. Take a screenshot to see the app
|
|
62
|
+
screenshot({ sessionId: "abc-123" })
|
|
63
|
+
// Returns image directly in response!
|
|
64
|
+
|
|
65
|
+
// 5. Interact with the UI
|
|
66
|
+
ui_tap({ sessionId: "abc-123", x: 200, y: 400 })
|
|
67
|
+
|
|
68
|
+
// 6. Make code changes, then hot reload
|
|
69
|
+
flutter_hot_reload({ sessionId: "abc-123" })
|
|
70
|
+
|
|
71
|
+
// 7. Clean up when done
|
|
72
|
+
session_end({ sessionId: "abc-123" })
|
|
73
|
+
```
|
|
74
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nick Clifford
|
|
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,370 @@
|
|
|
1
|
+
# docker-flutter-ios-simulator-mcp
|
|
2
|
+
|
|
3
|
+
**Model Context Protocol server for Flutter iOS development with AI agents**
|
|
4
|
+
|
|
5
|
+
Enables AI agents (like Claude) inside a Docker container to build, run, and interact with Flutter iOS applications through an iOS Simulator running on the host. Perfect for running claude inside a secure container while allowing it to build and test iOS and macOS apps
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- 🎯 **Session-based Development** - Isolated simulator and Flutter process per session
|
|
10
|
+
- 🔥 **Hot Reload & Restart** - Instant code updates without full rebuilds
|
|
11
|
+
- 📱 **UI Automation** - Tap, swipe, type, and interact with the simulator
|
|
12
|
+
- 📸 **Visual Feedback** - Screenshots returned as images (Docker-compatible)
|
|
13
|
+
- 🔍 **Accessibility Tree** - Inspect UI elements and hierarchy
|
|
14
|
+
- 📊 **Live Logs** - Real-time Flutter build output and app logs
|
|
15
|
+
- 🌐 **HTTP Transport** - Works from Docker containers (no filesystem access needed)
|
|
16
|
+
|
|
17
|
+
## Architecture
|
|
18
|
+
|
|
19
|
+
Enables AI agents running in Docker containers to control iOS Simulators on the macOS host:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
23
|
+
│ macOS Host │
|
|
24
|
+
│ │
|
|
25
|
+
│ ┌──────────────────┐ ┌─────────────────────────────┐ │
|
|
26
|
+
│ │ iOS Simulators │ │ docker-flutter-ios- │ │
|
|
27
|
+
│ │ ┌─ Sim 1 ──────┐ │◄────────┤ simulator-mcp │ │
|
|
28
|
+
│ │ ┌─ Sim 2 ──────┐ │ │ │ │
|
|
29
|
+
│ │ │ Flutter App │ │ │ • Session Management │ │
|
|
30
|
+
│ │ │ │ │ │ • Flutter Build/Run/Test │ │
|
|
31
|
+
│ │ └──────────────┘ │ │ • Hot Reload/Restart │ │
|
|
32
|
+
│ │ │ │ • Screenshots (via IDB) │ │
|
|
33
|
+
│ │ Status: Booted │ │ • UI Automation │ │
|
|
34
|
+
│ └──────────────────┘ │ • Log Streaming │ │
|
|
35
|
+
│ └─────────────────────────────┘ │
|
|
36
|
+
│ ▲ │
|
|
37
|
+
│ │ HTTP/MCP │
|
|
38
|
+
└─────────────────────────────────────────┼───────────────────────┘
|
|
39
|
+
│
|
|
40
|
+
┌─────────────────────┴──────────────────────┐
|
|
41
|
+
│ │
|
|
42
|
+
┌─────────┴──────────┐ ┌──────────────────┴┐
|
|
43
|
+
│ Docker Container │ │ Docker Container │
|
|
44
|
+
│ │ │ │
|
|
45
|
+
│ ┌──────────────┐ │ │ ┌───────────────┐ │
|
|
46
|
+
│ │ AI Agent │ │ │ │ AI Agent │ │
|
|
47
|
+
│ │ (Claude) │──┼──────────────┼─│ (Claude) │ │
|
|
48
|
+
│ │ │ │ │ │ │ │
|
|
49
|
+
│ │ Builds & │ │ │ │ Tests & │ │
|
|
50
|
+
│ │ Develops │ │ │ │ Debugs │ │
|
|
51
|
+
│ └──────────────┘ │ │ └───────────────┘ │
|
|
52
|
+
│ │ │ │
|
|
53
|
+
└────────────────────┘ └───────────────────┘
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Flow:**
|
|
57
|
+
1. AI agents in containers connect to MCP server via HTTP
|
|
58
|
+
2. MCP server manages iOS Simulator instances and Flutter processes
|
|
59
|
+
3. Agents can build, run, test, and interact with Flutter apps
|
|
60
|
+
4. Screenshots and logs flow back to agents in real-time
|
|
61
|
+
|
|
62
|
+
## Prerequisites
|
|
63
|
+
|
|
64
|
+
**macOS only** - Requires Xcode and iOS Simulator
|
|
65
|
+
|
|
66
|
+
1. **Xcode** - Install from Mac App Store, then run:
|
|
67
|
+
```bash
|
|
68
|
+
sudo xcode-select --switch /Applications/Xcode.app
|
|
69
|
+
xcodebuild -runFirstLaunch
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
2. **Flutter SDK** - [Install Flutter](https://docs.flutter.dev/get-started/install/macos) and ensure it's in your PATH:
|
|
73
|
+
```bash
|
|
74
|
+
flutter --version
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
3. **Node.js 18+** - [Install Node.js](https://nodejs.org/)
|
|
78
|
+
|
|
79
|
+
4. **Facebook IDB** - iOS automation tool:
|
|
80
|
+
MacOS's Python environment is pretty borked at the moment, it's best to use brew and pipx to manage the installation:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# First, ensure you have a modern python installed via homebrew (if not already done)
|
|
84
|
+
brew install python@3.12
|
|
85
|
+
|
|
86
|
+
# Then, install pipx using that specific python's pip
|
|
87
|
+
python3.12 -m pip install pipx
|
|
88
|
+
|
|
89
|
+
# Ensure pipx is added to your PATH
|
|
90
|
+
pipx ensurepath
|
|
91
|
+
|
|
92
|
+
# Install IDB
|
|
93
|
+
pipx install fb-idb
|
|
94
|
+
|
|
95
|
+
# Verify installation
|
|
96
|
+
idb --help
|
|
97
|
+
|
|
98
|
+
# Then install idb-companion
|
|
99
|
+
brew tap facebook/fb
|
|
100
|
+
brew install idb-companion
|
|
101
|
+
|
|
102
|
+
# Verify it works
|
|
103
|
+
idb list-targets
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Installation
|
|
107
|
+
|
|
108
|
+
### Option 1: NPX (Recommended for Users)
|
|
109
|
+
|
|
110
|
+
No installation needed! Run directly with npx:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx docker-flutter-ios-simulator-mcp
|
|
114
|
+
# Server starts at http://localhost:3000/mcp
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Option 2: Local Development
|
|
118
|
+
|
|
119
|
+
Clone and run from source:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# Clone the repository
|
|
123
|
+
git clone https://github.com/zafnz/docker-flutter-ios-simulator-mcp.git
|
|
124
|
+
cd docker-flutter-ios-simulator-mcp
|
|
125
|
+
|
|
126
|
+
# Install dependencies
|
|
127
|
+
npm install
|
|
128
|
+
|
|
129
|
+
# Build TypeScript
|
|
130
|
+
npm run build
|
|
131
|
+
|
|
132
|
+
# Start the server
|
|
133
|
+
npm start
|
|
134
|
+
# Server starts at http://localhost:3000/mcp
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Quick Start
|
|
138
|
+
|
|
139
|
+
### 1. Start the MCP Server
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
npx docker-flutter-ios-simulator-mcp
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The server will start on port 3000 by default.
|
|
146
|
+
|
|
147
|
+
### 2. Configure Your MCP Client
|
|
148
|
+
|
|
149
|
+
For **Claude Desktop** or **Docker containers**, connect to:
|
|
150
|
+
```
|
|
151
|
+
http://localhost:3000/mcp
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
From **Docker**, use:
|
|
155
|
+
```
|
|
156
|
+
http://host.docker.internal:3000/mcp
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 3. Use the Tools
|
|
160
|
+
|
|
161
|
+
The MCP server provides these tools to AI agents:
|
|
162
|
+
|
|
163
|
+
**Session Management:**
|
|
164
|
+
- `session_start` - Create a new development session with a simulator
|
|
165
|
+
- `session_end` - Clean up and delete the simulator
|
|
166
|
+
- `session_list` - View active sessions
|
|
167
|
+
|
|
168
|
+
**Flutter Development:**
|
|
169
|
+
- `flutter_run` - Build and launch your app
|
|
170
|
+
- `flutter_build` - Build iOS app without running (for CI/deployment)
|
|
171
|
+
- `flutter_test` - Run Flutter tests and return results
|
|
172
|
+
- `flutter_clean` - Clean build cache and artifacts
|
|
173
|
+
- `flutter_logs` - Monitor build progress and app output
|
|
174
|
+
- `flutter_hot_reload` - Apply code changes instantly
|
|
175
|
+
- `flutter_hot_restart` - Restart the app
|
|
176
|
+
- `flutter_stop` - Stop the running app
|
|
177
|
+
|
|
178
|
+
**UI Interaction:**
|
|
179
|
+
- `screenshot` - Capture and view the simulator screen
|
|
180
|
+
- `ui_tap` - Tap at coordinates
|
|
181
|
+
- `ui_swipe` - Swipe gestures (scrolling, swiping)
|
|
182
|
+
- `ui_type` - Enter text into fields
|
|
183
|
+
- `ui_describe_all` - Get accessibility tree
|
|
184
|
+
- `ui_describe_point` - Inspect element at coordinates
|
|
185
|
+
|
|
186
|
+
**Device Management:**
|
|
187
|
+
- `simulator_list` - See available iOS device types
|
|
188
|
+
|
|
189
|
+
## Example Workflow
|
|
190
|
+
|
|
191
|
+
Here's a typical AI agent workflow:
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
// 1. Start a session with your Flutter project
|
|
195
|
+
session_start({
|
|
196
|
+
worktreePath: "/path/to/your/flutter/project",
|
|
197
|
+
deviceType: "iPhone 16 Pro"
|
|
198
|
+
})
|
|
199
|
+
// Returns: { sessionId: "abc-123", simulatorUdid: "..." }
|
|
200
|
+
|
|
201
|
+
// 2. Run the Flutter app
|
|
202
|
+
flutter_run({ sessionId: "abc-123" })
|
|
203
|
+
|
|
204
|
+
// 3. Monitor build progress (poll every few seconds)
|
|
205
|
+
flutter_logs({
|
|
206
|
+
sessionId: "abc-123",
|
|
207
|
+
fromIndex: 0, // Start from beginning
|
|
208
|
+
limit: 100 // Get 100 lines
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
// 4. Take a screenshot to see the app
|
|
212
|
+
screenshot({ sessionId: "abc-123" })
|
|
213
|
+
// Returns image directly in response!
|
|
214
|
+
|
|
215
|
+
// 5. Interact with the UI
|
|
216
|
+
ui_tap({ sessionId: "abc-123", x: 200, y: 400 })
|
|
217
|
+
|
|
218
|
+
// 6. Make code changes, then hot reload
|
|
219
|
+
flutter_hot_reload({ sessionId: "abc-123" })
|
|
220
|
+
|
|
221
|
+
// 7. Clean up when done
|
|
222
|
+
session_end({ sessionId: "abc-123" })
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Configuration
|
|
226
|
+
|
|
227
|
+
### Command-Line Options
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
docker-flutter-ios-simulator-mcp [OPTIONS]
|
|
231
|
+
|
|
232
|
+
OPTIONS:
|
|
233
|
+
-p, --port <port> Port to listen on (default: 3000)
|
|
234
|
+
--host <host> Host address to bind to (default: 127.0.0.1)
|
|
235
|
+
--allow-only <path> Only allow Flutter projects under this path (default: /Users/)
|
|
236
|
+
--max-sessions <number> Maximum number of concurrent sessions (default: 10)
|
|
237
|
+
--pre-build-script <cmd> Command to run before flutter build/run (e.g., "git pull")
|
|
238
|
+
--post-build-script <cmd> Command to run after flutter build/run completes
|
|
239
|
+
-h, --help Show this help message
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Environment Variables
|
|
243
|
+
|
|
244
|
+
| Variable | Description | Default |
|
|
245
|
+
|----------|-------------|---------|
|
|
246
|
+
| `PORT` | HTTP server port | `3000` |
|
|
247
|
+
| `HOST` | Server bind address (use `0.0.0.0` for Docker) | `127.0.0.1` |
|
|
248
|
+
| `ALLOW_ONLY` | Path prefix for allowed Flutter projects | `/Users/` |
|
|
249
|
+
| `MAX_SESSIONS` | Maximum number of concurrent sessions | `10` |
|
|
250
|
+
| `PRE_BUILD_SCRIPT` | Command to run before flutter build/run | (none) |
|
|
251
|
+
| `POST_BUILD_SCRIPT` | Command to run after flutter build/run | (none) |
|
|
252
|
+
| `LOG_LEVEL` | Logging verbosity (`debug`, `info`, `warn`, `error`) | `info` |
|
|
253
|
+
|
|
254
|
+
### Examples
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Default (localhost only, /Users/ projects)
|
|
258
|
+
npx docker-flutter-ios-simulator-mcp
|
|
259
|
+
|
|
260
|
+
# Custom port
|
|
261
|
+
npx docker-flutter-ios-simulator-mcp --port 8080
|
|
262
|
+
|
|
263
|
+
# Docker (bind to all interfaces)
|
|
264
|
+
npx docker-flutter-ios-simulator-mcp --host 0.0.0.0
|
|
265
|
+
|
|
266
|
+
# Restrict to specific directory
|
|
267
|
+
npx docker-flutter-ios-simulator-mcp --allow-only /Users/alice/flutter-projects
|
|
268
|
+
|
|
269
|
+
# Allow more concurrent sessions
|
|
270
|
+
npx docker-flutter-ios-simulator-mcp --max-sessions 20
|
|
271
|
+
|
|
272
|
+
# Run git pull before each build
|
|
273
|
+
npx docker-flutter-ios-simulator-mcp --pre-build-script "git pull"
|
|
274
|
+
|
|
275
|
+
# Run commands before and after builds
|
|
276
|
+
npx docker-flutter-ios-simulator-mcp --pre-build-script "git pull" --post-build-script "echo Build complete"
|
|
277
|
+
|
|
278
|
+
# Multiple options
|
|
279
|
+
npx docker-flutter-ios-simulator-mcp --port 8080 --host 0.0.0.0 --allow-only /Users/alice --max-sessions 15
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Security
|
|
283
|
+
|
|
284
|
+
By default, the server:
|
|
285
|
+
- Binds to `127.0.0.1` (localhost only) for security
|
|
286
|
+
- Only allows Flutter projects under `/Users/` to prevent access to system directories
|
|
287
|
+
- Validates all project paths have a `pubspec.yaml` file
|
|
288
|
+
- Limits concurrent sessions to 10 to prevent resource exhaustion
|
|
289
|
+
|
|
290
|
+
## Troubleshooting
|
|
291
|
+
|
|
292
|
+
### "Session not found" errors
|
|
293
|
+
- Sessions are in-memory and lost if the MCP server restarts
|
|
294
|
+
- Create a new session after restarting the server
|
|
295
|
+
|
|
296
|
+
### "Simulator failed to boot"
|
|
297
|
+
- Check Xcode is installed: `xcode-select -p`
|
|
298
|
+
- Verify simulators are available: `xcrun simctl list devices`
|
|
299
|
+
- Try rebooting: `sudo killall -9 com.apple.CoreSimulator.CoreSimulatorService`
|
|
300
|
+
|
|
301
|
+
### "Flutter not found"
|
|
302
|
+
- Ensure Flutter is in your PATH: `which flutter`
|
|
303
|
+
- Add to PATH in `~/.zshrc` or `~/.bash_profile`
|
|
304
|
+
|
|
305
|
+
### "IDB command failed"
|
|
306
|
+
- Verify IDB is installed: `which idb`
|
|
307
|
+
- Check IDB version: `idb --version`
|
|
308
|
+
- Reinstall if needed: `pipx install --force fb-idb`
|
|
309
|
+
|
|
310
|
+
### Screenshots not appearing
|
|
311
|
+
- Screenshots are returned as images in the MCP response
|
|
312
|
+
- They work automatically with Docker containers (no filesystem needed)
|
|
313
|
+
|
|
314
|
+
### First Flutter build is slow
|
|
315
|
+
- First build can take 1-2 minutes (normal)
|
|
316
|
+
- Use `flutter_logs` to monitor progress
|
|
317
|
+
- Subsequent builds are much faster with hot reload
|
|
318
|
+
|
|
319
|
+
## Development
|
|
320
|
+
|
|
321
|
+
### Run in Development Mode
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
npm run dev # Watch mode with auto-restart
|
|
325
|
+
npm run build # Build TypeScript
|
|
326
|
+
npm test # Run tests
|
|
327
|
+
npm run test:watch # Tests in watch mode
|
|
328
|
+
npm run lint # Check code style
|
|
329
|
+
npm run typecheck # Type checking
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Project Structure
|
|
333
|
+
|
|
334
|
+
```
|
|
335
|
+
src/
|
|
336
|
+
├── index.ts # Entry point & Express server
|
|
337
|
+
├── server.ts # MCP server setup
|
|
338
|
+
├── session/ # Session management
|
|
339
|
+
├── flutter/ # Flutter process control
|
|
340
|
+
├── simulator/ # iOS Simulator & IDB wrappers
|
|
341
|
+
├── tools/ # MCP tool definitions
|
|
342
|
+
└── utils/ # Helpers & utilities
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## How It Works
|
|
346
|
+
|
|
347
|
+
1. **Session-based Isolation**: Each session creates a dedicated iOS Simulator and Flutter process
|
|
348
|
+
2. **HTTP Transport**: MCP protocol over HTTP (works from Docker containers)
|
|
349
|
+
3. **Log Buffering**: Flutter output is buffered in memory, retrieved via polling
|
|
350
|
+
4. **Image Transport**: Screenshots are returned as base64 PNG in MCP responses
|
|
351
|
+
5. **UI Automation**: Uses Facebook IDB for simulator interaction
|
|
352
|
+
|
|
353
|
+
## Contributing
|
|
354
|
+
|
|
355
|
+
Contributions welcome! Please:
|
|
356
|
+
1. Fork the repository
|
|
357
|
+
2. Create a feature branch
|
|
358
|
+
3. Run tests: `npm test`
|
|
359
|
+
4. Submit a pull request
|
|
360
|
+
|
|
361
|
+
## License
|
|
362
|
+
|
|
363
|
+
MIT
|
|
364
|
+
|
|
365
|
+
## Links
|
|
366
|
+
|
|
367
|
+
- [MCP Protocol Specification](https://modelcontextprotocol.io/)
|
|
368
|
+
- [Flutter Documentation](https://docs.flutter.dev/)
|
|
369
|
+
- [Facebook IDB](https://fbidb.io/)
|
|
370
|
+
- [Xcode](https://developer.apple.com/xcode/)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { LogEntry } from './types.js';
|
|
2
|
+
export declare class LogBuffer {
|
|
3
|
+
private logs;
|
|
4
|
+
private currentIndex;
|
|
5
|
+
private readonly maxLines;
|
|
6
|
+
constructor(maxLines?: number);
|
|
7
|
+
append(line: string): void;
|
|
8
|
+
getLogs(fromIndex?: number, limit?: number): LogEntry[];
|
|
9
|
+
getNextIndex(): number;
|
|
10
|
+
getTotalLines(): number;
|
|
11
|
+
clear(): void;
|
|
12
|
+
getRecentLines(count: number): string[];
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=log-buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-buffer.d.ts","sourceRoot":"","sources":["../../src/flutter/log-buffer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAkB;IAC9B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,QAAQ,SAAO;IAI3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAc1B,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,QAAQ,EAAE;IAoBpD,YAAY,IAAI,MAAM;IAItB,aAAa,IAAI,MAAM;IAIvB,KAAK,IAAI,IAAI;IAKb,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE;CAIxC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export class LogBuffer {
|
|
2
|
+
logs = [];
|
|
3
|
+
currentIndex = 0;
|
|
4
|
+
maxLines;
|
|
5
|
+
constructor(maxLines = 1000) {
|
|
6
|
+
this.maxLines = maxLines;
|
|
7
|
+
}
|
|
8
|
+
append(line) {
|
|
9
|
+
const entry = {
|
|
10
|
+
line,
|
|
11
|
+
timestamp: new Date(),
|
|
12
|
+
index: this.currentIndex++,
|
|
13
|
+
};
|
|
14
|
+
this.logs.push(entry);
|
|
15
|
+
if (this.logs.length > this.maxLines) {
|
|
16
|
+
this.logs.shift();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
getLogs(fromIndex, limit = 100) {
|
|
20
|
+
// Validate fromIndex is non-negative
|
|
21
|
+
if (fromIndex !== undefined && fromIndex < 0) {
|
|
22
|
+
throw new Error(`fromIndex must be non-negative, got: ${String(fromIndex)}`);
|
|
23
|
+
}
|
|
24
|
+
// Validate limit is positive
|
|
25
|
+
if (limit <= 0) {
|
|
26
|
+
throw new Error(`limit must be positive, got: ${String(limit)}`);
|
|
27
|
+
}
|
|
28
|
+
let filtered = this.logs;
|
|
29
|
+
if (fromIndex !== undefined) {
|
|
30
|
+
filtered = this.logs.filter((entry) => entry.index >= fromIndex);
|
|
31
|
+
}
|
|
32
|
+
return filtered.slice(0, limit);
|
|
33
|
+
}
|
|
34
|
+
getNextIndex() {
|
|
35
|
+
return this.currentIndex;
|
|
36
|
+
}
|
|
37
|
+
getTotalLines() {
|
|
38
|
+
return this.logs.length;
|
|
39
|
+
}
|
|
40
|
+
clear() {
|
|
41
|
+
this.logs = [];
|
|
42
|
+
this.currentIndex = 0;
|
|
43
|
+
}
|
|
44
|
+
getRecentLines(count) {
|
|
45
|
+
const start = Math.max(0, this.logs.length - count);
|
|
46
|
+
return this.logs.slice(start).map((entry) => entry.line);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=log-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log-buffer.js","sourceRoot":"","sources":["../../src/flutter/log-buffer.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,SAAS;IACZ,IAAI,GAAe,EAAE,CAAC;IACtB,YAAY,GAAG,CAAC,CAAC;IACR,QAAQ,CAAS;IAElC,YAAY,QAAQ,GAAG,IAAI;QACzB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,IAAY;QACjB,MAAM,KAAK,GAAa;YACtB,IAAI;YACJ,SAAS,EAAE,IAAI,IAAI,EAAE;YACrB,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE;SAC3B,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEtB,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YACrC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,CAAC,SAAkB,EAAE,KAAK,GAAG,GAAG;QACrC,qCAAqC;QACrC,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,wCAAwC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC/E,CAAC;QAED,6BAA6B;QAC7B,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,gCAAgC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,IAAI,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;QAEzB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC;QACnE,CAAC;QAED,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,YAAY;QACV,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACf,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC;QACpD,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;CACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FlutterProcess, FlutterRunOptions } from './types.js';
|
|
2
|
+
export declare class FlutterProcessManager {
|
|
3
|
+
private process?;
|
|
4
|
+
private flutterProcess?;
|
|
5
|
+
private logBuffer;
|
|
6
|
+
private logSubscribers;
|
|
7
|
+
constructor(maxLogLines?: number);
|
|
8
|
+
start(options: FlutterRunOptions): Promise<FlutterProcess>;
|
|
9
|
+
private handleOutput;
|
|
10
|
+
private detectStatusChanges;
|
|
11
|
+
private handleExit;
|
|
12
|
+
stop(): boolean;
|
|
13
|
+
hotReload(): boolean;
|
|
14
|
+
hotRestart(): boolean;
|
|
15
|
+
kill(signal?: NodeJS.Signals): boolean;
|
|
16
|
+
getStatus(): FlutterProcess | undefined;
|
|
17
|
+
getLogs(fromIndex?: number, limit?: number): {
|
|
18
|
+
logs: Array<{
|
|
19
|
+
line: string;
|
|
20
|
+
timestamp: Date;
|
|
21
|
+
index: number;
|
|
22
|
+
}>;
|
|
23
|
+
nextIndex: number;
|
|
24
|
+
totalLines: number;
|
|
25
|
+
};
|
|
26
|
+
subscribeToLogs(callback: (line: string) => void): () => void;
|
|
27
|
+
clearLogs(): void;
|
|
28
|
+
cleanup(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/flutter/process.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAI/D,qBAAa,qBAAqB;IAChC,OAAO,CAAC,OAAO,CAAC,CAAiB;IACjC,OAAO,CAAC,cAAc,CAAC,CAAiB;IACxC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,cAAc,CAA0C;gBAEpD,WAAW,SAAO;IAKxB,KAAK,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC;IAwIhE,OAAO,CAAC,YAAY;IAoBpB,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,UAAU;IAYlB,IAAI,IAAI,OAAO;IAcf,SAAS,IAAI,OAAO;IAYpB,UAAU,IAAI,OAAO;IAYrB,IAAI,CAAC,MAAM,GAAE,MAAM,CAAC,OAAmB,GAAG,OAAO;IASjD,SAAS,IAAI,cAAc,GAAG,SAAS;IAIvC,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG;QAC3C,IAAI,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,IAAI,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC9D,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACpB;IASD,eAAe,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAQ7D,SAAS,IAAI,IAAI;IAIX,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CA8B/B"}
|