@mgcrea/mcp-ios-simulator 0.2.2 → 0.3.1
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/README.md +79 -24
- package/dist/cli.js +1 -1
- package/dist/{config-qc0zwYZr.js → config-D0JgmT_U.js} +491 -36
- package/dist/config-D0JgmT_U.js.map +1 -0
- package/dist/index.d.ts +112 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/dist/config-qc0zwYZr.js.map +0 -1
package/README.md
CHANGED
|
@@ -26,9 +26,17 @@ unlock. And **screenshots work with nothing installed at all**.
|
|
|
26
26
|
- **Push without APNs.** Deliver a real remote notification with no certificate
|
|
27
27
|
and no server.
|
|
28
28
|
- **Manage the app.** Install a build, launch it with fixture arguments,
|
|
29
|
-
terminate it, open a deep link. An app's data container is an
|
|
30
|
-
this Mac, so its database or logs are read directly — there
|
|
31
|
-
off a device.
|
|
29
|
+
terminate it, uninstall it, open a deep link. An app's data container is an
|
|
30
|
+
ordinary path on this Mac, so its database or logs are read directly — there
|
|
31
|
+
is nothing to copy off a device.
|
|
32
|
+
- **Read its logs.** `launch` with `capture_logs: true` writes the app's stdout,
|
|
33
|
+
stderr and every `Logger`/`os_log` message into one file, and `read_logs`
|
|
34
|
+
follows it with a cursor, a regex filter and Xcode's thirty-character line
|
|
35
|
+
prefix cut down to the time.
|
|
36
|
+
- **Read its crashes.** Simulator crash reports land among this Mac's own in
|
|
37
|
+
`~/Library/Logs/DiagnosticReports`; `list_crash_logs` keeps only the
|
|
38
|
+
simulators', says which simulator each came from, and `get_crash_log` returns
|
|
39
|
+
the exception and the faulting thread with library names resolved.
|
|
32
40
|
|
|
33
41
|
## Security
|
|
34
42
|
|
|
@@ -42,9 +50,13 @@ What that still costs you, stated honestly:
|
|
|
42
50
|
- `install` and `launch` run code on your Mac. A simulated process is a host
|
|
43
51
|
process.
|
|
44
52
|
- The app's data container is a plain host path, readable by anything.
|
|
45
|
-
- `erase`
|
|
53
|
+
- `erase` and `uninstall` are irreversible — they are the two tools behind an
|
|
54
|
+
explicit `confirm`.
|
|
55
|
+
- `list_crash_logs` reads this Mac's DiagnosticReports folder. It keeps only
|
|
56
|
+
simulator reports and `get_crash_log` refuses a Mac app's, but the folder is
|
|
57
|
+
yours and the server reads it as you.
|
|
46
58
|
- `IOS_SIMULATOR_ALLOW_WRITES=0` restores the device server's posture in one
|
|
47
|
-
variable, and then the
|
|
59
|
+
variable, and then the seventeen driving tools are **absent** from `tools/list`
|
|
48
60
|
rather than refused, because a refusal still lets a model try, retry and reason
|
|
49
61
|
about a way around it.
|
|
50
62
|
|
|
@@ -67,8 +79,9 @@ image library nor a plist parser has to be installed.
|
|
|
67
79
|
The asymmetry with `mcp-ios-device` is worth stating plainly: there, _every_
|
|
68
80
|
pixel comes through WebDriverAgent, so nothing about the screen works until a
|
|
69
81
|
signed runner is installed and trusted on the phone. Here `simctl` captures the
|
|
70
|
-
screen itself, so **
|
|
71
|
-
— only `ios_simulator_ui_tree` and the five
|
|
82
|
+
screen itself, so **eighteen of the twenty-five tools work with no runner at all**
|
|
83
|
+
— only `ios_simulator_ui_tree`, `ios_simulator_wait_for_element` and the five
|
|
84
|
+
input tools need one, and building it
|
|
72
85
|
takes about two minutes with no Apple account.
|
|
73
86
|
|
|
74
87
|
`ios_simulator_diagnostics` reports both lanes separately, so "the simulator is
|
|
@@ -77,18 +90,19 @@ failure.
|
|
|
77
90
|
|
|
78
91
|
## Configure
|
|
79
92
|
|
|
80
|
-
| Variable
|
|
81
|
-
|
|
|
82
|
-
| `IOS_SIMULATOR_ID`
|
|
83
|
-
| `IOS_SIMULATOR_ALLOW_WRITES`
|
|
84
|
-
| `IOS_SIMULATOR_LAUNCH_ARGS`
|
|
85
|
-
| `IOS_SIMULATOR_WDA_PORT`
|
|
86
|
-
| `IOS_SIMULATOR_WDA_URL`
|
|
87
|
-
| `IOS_SIMULATOR_MAX_TREE_BYTES`
|
|
88
|
-
| `IOS_SIMULATOR_TIMEOUT_MS`
|
|
89
|
-
| `IOS_SIMULATOR_WDA_TIMEOUT_MS`
|
|
90
|
-
| `IOS_SIMULATOR_OUTPUT_DIR`
|
|
91
|
-
| `
|
|
93
|
+
| Variable | Default | What it does |
|
|
94
|
+
| --------------------------------- | ---------------------------------- | ----------------------------------------------------------------------------- |
|
|
95
|
+
| `IOS_SIMULATOR_ID` | the only booted simulator | UDID, name, or `booted`. Two booted and no value is an error that names them. |
|
|
96
|
+
| `IOS_SIMULATOR_ALLOW_WRITES` | **on** | `0` drops the seventeen driving tools from `tools/list`. |
|
|
97
|
+
| `IOS_SIMULATOR_LAUNCH_ARGS` | none | Launch arguments applied when `launch` passes none of its own. |
|
|
98
|
+
| `IOS_SIMULATOR_WDA_PORT` | `8100` | Must match the runner's `USE_PORT`. One port per simulator. |
|
|
99
|
+
| `IOS_SIMULATOR_WDA_URL` | `http://127.0.0.1:<port>` | Explicit override; rarely needed. |
|
|
100
|
+
| `IOS_SIMULATOR_MAX_TREE_BYTES` | `24000` | Byte cap on a `ui_tree` payload. |
|
|
101
|
+
| `IOS_SIMULATOR_TIMEOUT_MS` | `120000` | Budget for one `simctl` call; a boot is slow. |
|
|
102
|
+
| `IOS_SIMULATOR_WDA_TIMEOUT_MS` | `30000` | Budget for one WebDriverAgent call. |
|
|
103
|
+
| `IOS_SIMULATOR_OUTPUT_DIR` | `$TMPDIR/mcp-ios-simulator` | Screenshots, launch logs, the runner log. |
|
|
104
|
+
| `IOS_SIMULATOR_CRASH_REPORTS_DIR` | `~/Library/Logs/DiagnosticReports` | Where `list_crash_logs` looks. |
|
|
105
|
+
| `IOS_SIMULATOR_DEBUG` | off | Log every `simctl` and WebDriverAgent call to stderr. |
|
|
92
106
|
|
|
93
107
|
The same keys in camelCase can go in `~/.config/ios-simulator-mcp/config.json`
|
|
94
108
|
(`IOS_SIMULATOR_CONFIG` to move it). The environment wins **per field**, so a
|
|
@@ -136,8 +150,10 @@ See [.mcp.json.example](.mcp.json.example).
|
|
|
136
150
|
|
|
137
151
|
## Tools
|
|
138
152
|
|
|
139
|
-
Twenty-
|
|
140
|
-
`IOS_SIMULATOR_ALLOW_WRITES=0`.
|
|
153
|
+
Twenty-five. Eight are read-only; the other seventeen disappear with
|
|
154
|
+
`IOS_SIMULATOR_ALLOW_WRITES=0`. `read_logs` is among those seventeen although it
|
|
155
|
+
changes nothing: it only reads what `launch` started capturing, so it goes where
|
|
156
|
+
`launch` goes.
|
|
141
157
|
|
|
142
158
|
| Tool | Writes? | What it does |
|
|
143
159
|
| -------------------------------- | ------------- | ------------------------------------------------------------------------ |
|
|
@@ -147,15 +163,19 @@ Twenty-one. Six are read-only; the other fifteen disappear with
|
|
|
147
163
|
| `ios_simulator_screenshot` | | The screen, in point space — **no runner needed** |
|
|
148
164
|
| `ios_simulator_ui_tree` | | Addressable elements with precomputed tap points |
|
|
149
165
|
| `ios_simulator_wait_for_element` | | Poll until something appears, or goes away |
|
|
166
|
+
| `ios_simulator_list_crash_logs` | | Simulator crash reports, newest first, by simulator and process |
|
|
167
|
+
| `ios_simulator_get_crash_log` | | One report: exception, termination, faulting thread |
|
|
150
168
|
| `ios_simulator_tap` | ✓ | Tap a point |
|
|
151
169
|
| `ios_simulator_tap_element` | ✓ | Tap by identifier, label or predicate |
|
|
152
170
|
| `ios_simulator_swipe` | ✓ | Drag between two points |
|
|
153
171
|
| `ios_simulator_type` | ✓ | Type into the focused field, or a named one |
|
|
154
172
|
| `ios_simulator_press_button` | ✓ | Home |
|
|
155
173
|
| `ios_simulator_power` | ✓ | Boot or shut down; never implicit |
|
|
156
|
-
| `ios_simulator_erase` | ✓ **confirm** | Wipe to factory
|
|
174
|
+
| `ios_simulator_erase` | ✓ **confirm** | Wipe to factory |
|
|
157
175
|
| `ios_simulator_install` | ✓ | Install a simulator `.app` |
|
|
158
176
|
| `ios_simulator_launch` | ✓ | Launch, with fixture arguments and captured output |
|
|
177
|
+
| `ios_simulator_read_logs` | ✓ (gate only) | Follow a `capture_logs` launch: stdout, stderr and os_log, with a cursor |
|
|
178
|
+
| `ios_simulator_uninstall` | ✓ **confirm** | Remove one app and its data |
|
|
159
179
|
| `ios_simulator_terminate` | ✓ | Kill a running app |
|
|
160
180
|
| `ios_simulator_open_url` | ✓ | Deep links and universal links |
|
|
161
181
|
| `ios_simulator_set_environment` | ✓ | Appearance, Dynamic Type, contrast, status bar, location, permissions |
|
|
@@ -165,8 +185,7 @@ Twenty-one. Six are read-only; the other fifteen disappear with
|
|
|
165
185
|
|
|
166
186
|
Deliberately absent: `create`, `clone`, `delete`, `rename`, `upgrade` and `pair`
|
|
167
187
|
(fleet management, not driving, and `delete all` is a footgun with no upside);
|
|
168
|
-
`get_app_container` (`list_apps` already returns the path); `
|
|
169
|
-
(`install` overwrites, and `erase` covers first-run properly); `keychain`,
|
|
188
|
+
`get_app_container` (`list_apps` already returns the path); `keychain`,
|
|
170
189
|
`pbcopy`, `spawn`, `diagnose` and `recordVideo` (real capabilities that an agent
|
|
171
190
|
would use approximately never, and every tool costs listing bytes on every
|
|
172
191
|
connect).
|
|
@@ -177,6 +196,11 @@ only way an app whose first step is "choose a photo" can be driven here at all.
|
|
|
177
196
|
Anyone hitting that had to drop out to a shell, which is exactly what these
|
|
178
197
|
tools exist to avoid.
|
|
179
198
|
|
|
199
|
+
`uninstall` was on it too, on the grounds that `install` overwrites and `erase`
|
|
200
|
+
covers a first run. But `erase` takes every other app's data and a reboot with
|
|
201
|
+
it, and "this app's first launch, everything else as it was" is the ordinary
|
|
202
|
+
case. It sits behind `confirm` like `erase`.
|
|
203
|
+
|
|
180
204
|
## Traps worth knowing
|
|
181
205
|
|
|
182
206
|
Measured on Xcode 26.6 (17F113) unless marked Xcode 27, which was checked on
|
|
@@ -207,6 +231,37 @@ Xcode 27.0 (27A266a) with an iOS 27.0 runtime.
|
|
|
207
231
|
every later screenshot.
|
|
208
232
|
- **`simctl launch` environment variables need a `SIMCTL_CHILD_` prefix** on the
|
|
209
233
|
calling process, not a flag.
|
|
234
|
+
- **An app's `Logger` output reaches no file unless `OS_ACTIVITY_DT_MODE` is
|
|
235
|
+
set** (Xcode 27). Settings launched plainly wrote 8 lines to stderr; launched
|
|
236
|
+
with `SIMCTL_CHILD_OS_ACTIVITY_DT_MODE=YES` it wrote 77 in the same eight
|
|
237
|
+
seconds, the unified log mirrored with Xcode's prefix. `capture_logs` sets it.
|
|
238
|
+
- **`--stdout` and `--stderr` may be the same file** (Xcode 27). A probe app
|
|
239
|
+
alternating `print`, stderr and `Logger` produced one file with all fifteen
|
|
240
|
+
lines in order and nothing overwritten. The redirect **appends**, so a
|
|
241
|
+
relaunch into the same path keeps the previous run's lines — every
|
|
242
|
+
`capture_logs` launch gets a fresh file.
|
|
243
|
+
- **A launch log under `/tmp` or `$TMPDIR` is not where you asked** (Xcode 27).
|
|
244
|
+
The simulator opens the redirect itself, and it has its own `/tmp` and
|
|
245
|
+
`/var/folders`: `--stdout=/tmp/x/a.out` created
|
|
246
|
+
`<device>/data/tmp/x/a.out`, and the path given never existed — which is
|
|
247
|
+
where this server's default output directory is. `launch` now looks in both
|
|
248
|
+
places and returns the real one. A path under `/Users` is written as given.
|
|
249
|
+
- **A simulator crash report says it is one in `platform`, not
|
|
250
|
+
`is_simulated`** (Xcode 27). Of 593 reports on one Mac, the 464 from
|
|
251
|
+
simulators all had header `platform: 7` (`PLATFORM_IOSSIMULATOR`), the Mac's
|
|
252
|
+
all had 1, and `is_simulated: 1` was on 13 _Mac_ reports — Safari, Mail and two
|
|
253
|
+
helpers. `procPath` is anonymised, so the simulator's UDID comes from the
|
|
254
|
+
body's `coalitionName` (`com.apple.CoreSimulator.SimDevice.<UDID>`), which sat
|
|
255
|
+
in the first 1.8 KB of every one.
|
|
256
|
+
- **A Swift `fatalError` message is in the log, not the crash report**
|
|
257
|
+
(Xcode 27). The probe's report had the `_assertionFailure` frame and no
|
|
258
|
+
application-specific text; the message was on stderr. `get_crash_log` says so
|
|
259
|
+
when it happens.
|
|
260
|
+
- **`simctl uninstall` of an app that is not installed succeeds** (Xcode 27),
|
|
261
|
+
silently, exit 0. Success does not prove it was there.
|
|
262
|
+
- **Launching an app that is not installed fails as "The request to open …
|
|
263
|
+
failed"** (Xcode 27), `FBSOpenApplicationServiceErrorDomain` code 4, with nothing about installation.
|
|
264
|
+
The error now carries that remedy.
|
|
210
265
|
- **A label belongs to the control _and_ to every container around it.**
|
|
211
266
|
WebDriverAgent answers depth-first, so an unqualified label match lands on the
|
|
212
267
|
navigation bar as readily as on the button — and a tap on a container does
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { N as BUILD_INFO, a as createServer, n as loadConfig, t as defaultConfigPath } from "./config-
|
|
2
|
+
import { N as BUILD_INFO, a as createServer, n as loadConfig, t as defaultConfigPath } from "./config-D0JgmT_U.js";
|
|
3
3
|
import { ZodError } from "zod";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
5
5
|
//#region src/cli.ts
|