@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 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 ordinary path on
30
- this Mac, so its database or logs are read directly — there is nothing to copy
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` is irreversible — it is the only tool behind an explicit `confirm`.
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 fourteen driving tools are **absent** from `tools/list`
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 **fourteen of the nineteen tools work with no runner at all**
71
- — only `ios_simulator_ui_tree` and the five input tools need one, and building it
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 | Default | What it does |
81
- | ------------------------------ | --------------------------- | ----------------------------------------------------------------------------- |
82
- | `IOS_SIMULATOR_ID` | the only booted simulator | UDID, name, or `booted`. Two booted and no value is an error that names them. |
83
- | `IOS_SIMULATOR_ALLOW_WRITES` | **on** | `0` drops the fourteen driving tools from `tools/list`. |
84
- | `IOS_SIMULATOR_LAUNCH_ARGS` | none | Launch arguments applied when `launch` passes none of its own. |
85
- | `IOS_SIMULATOR_WDA_PORT` | `8100` | Must match the runner's `USE_PORT`. One port per simulator. |
86
- | `IOS_SIMULATOR_WDA_URL` | `http://127.0.0.1:<port>` | Explicit override; rarely needed. |
87
- | `IOS_SIMULATOR_MAX_TREE_BYTES` | `24000` | Byte cap on a `ui_tree` payload. |
88
- | `IOS_SIMULATOR_TIMEOUT_MS` | `120000` | Budget for one `simctl` call; a boot is slow. |
89
- | `IOS_SIMULATOR_WDA_TIMEOUT_MS` | `30000` | Budget for one WebDriverAgent call. |
90
- | `IOS_SIMULATOR_OUTPUT_DIR` | `$TMPDIR/mcp-ios-simulator` | Screenshots, launch logs, the runner log. |
91
- | `IOS_SIMULATOR_DEBUG` | off | Log every `simctl` and WebDriverAgent call to stderr. |
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-one. Six are read-only; the other fifteen disappear with
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 — the only irreversible tool |
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); `uninstall`
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-qc0zwYZr.js";
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