@mgcrea/mcp-ios-simulator 0.1.0 → 0.2.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/README.md +45 -26
- package/dist/cli.js +1 -1
- package/dist/{config-C1TH-p-c.js → config-HSDMfemT.js} +88 -22
- package/dist/config-HSDMfemT.js.map +1 -0
- package/dist/index.d.ts +34 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/package.json +4 -3
- package/dist/config-C1TH-p-c.js.map +0 -1
package/README.md
CHANGED
|
@@ -136,38 +136,46 @@ See [.mcp.json.example](.mcp.json.example).
|
|
|
136
136
|
|
|
137
137
|
## Tools
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
Twenty-one. Six are read-only; the other fifteen disappear with
|
|
140
140
|
`IOS_SIMULATOR_ALLOW_WRITES=0`.
|
|
141
141
|
|
|
142
|
-
| Tool
|
|
143
|
-
|
|
|
144
|
-
| `ios_simulator_list`
|
|
145
|
-
| `ios_simulator_diagnostics`
|
|
146
|
-
| `ios_simulator_list_apps`
|
|
147
|
-
| `ios_simulator_screenshot`
|
|
148
|
-
| `ios_simulator_ui_tree`
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
142
|
+
| Tool | Writes? | What it does |
|
|
143
|
+
| -------------------------------- | ------------- | ------------------------------------------------------------------------ |
|
|
144
|
+
| `ios_simulator_list` | | Every simulator, with state and whether its runtime is installed |
|
|
145
|
+
| `ios_simulator_diagnostics` | | Both lanes, the resolved target, the geometry, and who owns the WDA port |
|
|
146
|
+
| `ios_simulator_list_apps` | | Installed apps, with bundle id and host-path data container |
|
|
147
|
+
| `ios_simulator_screenshot` | | The screen, in point space — **no runner needed** |
|
|
148
|
+
| `ios_simulator_ui_tree` | | Addressable elements with precomputed tap points |
|
|
149
|
+
| `ios_simulator_wait_for_element` | | Poll until something appears, or goes away |
|
|
150
|
+
| `ios_simulator_tap` | ✓ | Tap a point |
|
|
151
|
+
| `ios_simulator_tap_element` | ✓ | Tap by identifier, label or predicate |
|
|
152
|
+
| `ios_simulator_swipe` | ✓ | Drag between two points |
|
|
153
|
+
| `ios_simulator_type` | ✓ | Type into the focused field, or a named one |
|
|
154
|
+
| `ios_simulator_press_button` | ✓ | Home |
|
|
155
|
+
| `ios_simulator_power` | ✓ | Boot or shut down; never implicit |
|
|
156
|
+
| `ios_simulator_erase` | ✓ **confirm** | Wipe to factory — the only irreversible tool |
|
|
157
|
+
| `ios_simulator_install` | ✓ | Install a simulator `.app` |
|
|
158
|
+
| `ios_simulator_launch` | ✓ | Launch, with fixture arguments and captured output |
|
|
159
|
+
| `ios_simulator_terminate` | ✓ | Kill a running app |
|
|
160
|
+
| `ios_simulator_open_url` | ✓ | Deep links and universal links |
|
|
161
|
+
| `ios_simulator_set_environment` | ✓ | Appearance, Dynamic Type, contrast, status bar, location, permissions |
|
|
162
|
+
| `ios_simulator_add_media` | ✓ | Seed the photo library — the way around the missing camera |
|
|
163
|
+
| `ios_simulator_push` | ✓ | A remote notification, with no APNs certificate |
|
|
164
|
+
| `ios_simulator_restart_wda` | ✓ | Start or restart the runner, detached |
|
|
163
165
|
|
|
164
166
|
Deliberately absent: `create`, `clone`, `delete`, `rename`, `upgrade` and `pair`
|
|
165
167
|
(fleet management, not driving, and `delete all` is a footgun with no upside);
|
|
166
168
|
`get_app_container` (`list_apps` already returns the path); `uninstall`
|
|
167
|
-
(`install` overwrites, and `erase` covers first-run properly); `
|
|
168
|
-
`
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
(`install` overwrites, and `erase` covers first-run properly); `keychain`,
|
|
170
|
+
`pbcopy`, `spawn`, `diagnose` and `recordVideo` (real capabilities that an agent
|
|
171
|
+
would use approximately never, and every tool costs listing bytes on every
|
|
172
|
+
connect).
|
|
173
|
+
|
|
174
|
+
`addmedia` was on that list until 0.2.0, and it was the wrong call. A simulator
|
|
175
|
+
has no camera, so seeding the photo library is not a nice-to-have — it is the
|
|
176
|
+
only way an app whose first step is "choose a photo" can be driven here at all.
|
|
177
|
+
Anyone hitting that had to drop out to a shell, which is exactly what these
|
|
178
|
+
tools exist to avoid.
|
|
171
179
|
|
|
172
180
|
## Traps worth knowing
|
|
173
181
|
|
|
@@ -198,6 +206,17 @@ All measured on Xcode 26.6 (17F113).
|
|
|
198
206
|
every later screenshot.
|
|
199
207
|
- **`simctl launch` environment variables need a `SIMCTL_CHILD_` prefix** on the
|
|
200
208
|
calling process, not a flag.
|
|
209
|
+
- **A label belongs to the control _and_ to every container around it.**
|
|
210
|
+
WebDriverAgent answers depth-first, so an unqualified label match lands on the
|
|
211
|
+
navigation bar as readily as on the button — and a tap on a container does
|
|
212
|
+
nothing while reporting success. `tap_element` narrows a label to the
|
|
213
|
+
interactive types first, and says `preferredControl` when it did.
|
|
214
|
+
- **`isVisible` is not always truthful.** A `PHPicker` presented over Safari
|
|
215
|
+
reports all nine of its asset cells `isVisible: "0"` while they are on screen
|
|
216
|
+
and tappable — a synthesised tap on one opens the preview. The default
|
|
217
|
+
`ui_tree` filter drops them, which is why the result carries a `filtered`
|
|
218
|
+
tally: a short list that has been filtered and a screen that is genuinely bare
|
|
219
|
+
are otherwise the same answer.
|
|
201
220
|
- **`simctl install` wants a `.app` bundle directory** built for the simulator —
|
|
202
221
|
an `.ipa` or a device build fails with "No such file or directory", which reads
|
|
203
222
|
like a path typo.
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { N as BUILD_INFO, a as createServer, n as loadConfig, t as defaultConfigPath } from "./config-HSDMfemT.js";
|
|
3
3
|
import { ZodError } from "zod";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
5
5
|
//#region src/cli.ts
|
|
@@ -23,8 +23,8 @@ const pkg = readPackageJson();
|
|
|
23
23
|
const BUILD_INFO = {
|
|
24
24
|
name: pkg.name,
|
|
25
25
|
version: pkg.version,
|
|
26
|
-
gitCommit: "
|
|
27
|
-
gitCommitDate: "2026-09-
|
|
26
|
+
gitCommit: "bf86d18",
|
|
27
|
+
gitCommitDate: "2026-09-06T14:09:07+02:00"
|
|
28
28
|
};
|
|
29
29
|
//#endregion
|
|
30
30
|
//#region src/client/errors.ts
|
|
@@ -32,13 +32,30 @@ const BUILD_INFO = {
|
|
|
32
32
|
const TIMEOUT_REMEDY = "Raise IOS_SIMULATOR_TIMEOUT_MS if this is a slow operation (a boot or a large `install` genuinely takes tens of seconds), or check the simulator is still booted.";
|
|
33
33
|
const TOOLCHAIN_REMEDY = "Install Xcode and run `xcode-select --install`, or point IOS_SIMULATOR_XCRUN_PATH at a different xcrun.";
|
|
34
34
|
/**
|
|
35
|
-
* How to get the runner up
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
* How to get the runner up, from a shell.
|
|
36
|
+
*
|
|
37
|
+
* Much shorter than the device server's equivalent, and that is the headline
|
|
38
|
+
* difference between the two: a simulator runner needs no Apple team, no
|
|
39
|
+
* signing, no trust prompt and no tunnel.
|
|
40
|
+
*
|
|
41
|
+
* This is the *fallback*. It stays because it is the only route when writes are
|
|
42
|
+
* off, and because it is what a person setting the server up for the first time
|
|
43
|
+
* needs — but a remedy that sends a caller holding ios_simulator_restart_wda out
|
|
44
|
+
* to a terminal is a remedy that wastes the tool it already has.
|
|
45
|
+
*/
|
|
46
|
+
const START_RUNNER_SCRIPT_REMEDY = "Build and start the runner with `scripts/wda.sh setup` then `scripts/wda.sh run` (or `npx -p @mgcrea/mcp-ios-simulator ios-simulator-wda run` from an npm install) and leave it open — the HTTP server is the XCTest process, so it stops when that command does. It needs no Apple Developer team: a simulator build is not signed.";
|
|
47
|
+
/** The same thing, done with this server's own tool. */
|
|
48
|
+
const START_RUNNER_TOOL_REMEDY = "Start it with ios_simulator_restart_wda, which spawns the runner detached so it outlives this conversation. Give it about fifteen seconds, then check ios_simulator_diagnostics for `wda.reachable`.";
|
|
49
|
+
/**
|
|
50
|
+
* How to get the runner up, in whichever way the caller can actually reach.
|
|
51
|
+
*
|
|
52
|
+
* `ios_simulator_restart_wda` is registered only when writes are on, so naming
|
|
53
|
+
* it unconditionally would be the same mistake this fixes, pointed the other
|
|
54
|
+
* way: a remedy naming a tool that is not in the list.
|
|
38
55
|
*/
|
|
39
|
-
const
|
|
56
|
+
const startRunnerRemedy = (allowWrites) => allowWrites ? START_RUNNER_TOOL_REMEDY : START_RUNNER_SCRIPT_REMEDY;
|
|
40
57
|
/** What to do when WebDriverAgent does not answer at all. */
|
|
41
|
-
const
|
|
58
|
+
const wdaUnavailableRemedy = (allowWrites) => `${startRunnerRemedy(allowWrites)} Everything except ios_simulator_ui_tree and the input tools works without it — screenshots go through simctl. If something else already forwards the port, set IOS_SIMULATOR_WDA_URL.`;
|
|
42
59
|
/** No simulator matched, or several did and none was named. */
|
|
43
60
|
var SimulatorNotFoundError = class extends IosError {
|
|
44
61
|
name = "SimulatorNotFoundError";
|
|
@@ -380,6 +397,23 @@ var Simctl = class {
|
|
|
380
397
|
]);
|
|
381
398
|
}
|
|
382
399
|
/**
|
|
400
|
+
* Put photos and videos in the simulator's library.
|
|
401
|
+
*
|
|
402
|
+
* `addmedia` is variadic and takes the paths as plain argv, so unlike `push`
|
|
403
|
+
* this needs no temp file. It is the one route around the simulator's missing
|
|
404
|
+
* camera: an app that reads from the photo library can be exercised end to
|
|
405
|
+
* end, while one that insists on a live capture still cannot.
|
|
406
|
+
*/
|
|
407
|
+
async addMedia(udid, paths) {
|
|
408
|
+
assertNotBulkTarget(udid);
|
|
409
|
+
for (const path of paths) assertNoShellMetachars("path", path);
|
|
410
|
+
await this.run([
|
|
411
|
+
"addmedia",
|
|
412
|
+
udid,
|
|
413
|
+
...paths
|
|
414
|
+
]);
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
383
417
|
* Launch, optionally replacing a running copy.
|
|
384
418
|
*
|
|
385
419
|
* Environment variables do not go on the command line: simctl reads them from
|
|
@@ -553,13 +587,20 @@ var Simctl = class {
|
|
|
553
587
|
};
|
|
554
588
|
//#endregion
|
|
555
589
|
//#region src/client/simulator.ts
|
|
556
|
-
/**
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
590
|
+
/**
|
|
591
|
+
* What to say when WebDriverAgent fails, on a simulator.
|
|
592
|
+
*
|
|
593
|
+
* A function of the write gate rather than a constant: three of these four
|
|
594
|
+
* remedies name ios_simulator_restart_wda, and that tool is not registered when
|
|
595
|
+
* writes are off. Sending a caller to a tool it cannot see is the same failure
|
|
596
|
+
* as sending it to a shell it did not need.
|
|
597
|
+
*/
|
|
598
|
+
const simulatorWdaRemedies = (allowWrites) => ({
|
|
599
|
+
unavailable: wdaUnavailableRemedy(allowWrites),
|
|
600
|
+
notAuthorized: "WebDriverAgent is running but is refusing to drive the UI. On a simulator there is no Enable UI Automation toggle to blame, so this is nearly always a stale runner. " + startRunnerRemedy(allowWrites),
|
|
601
|
+
noForegroundApp: `WebDriverAgent can see no foreground application on a session it has just created. Launch the app with ios_simulator_launch, or restart the runner. ${startRunnerRemedy(allowWrites)}`,
|
|
561
602
|
noSuchElement: "Call ios_simulator_ui_tree to see what is actually on screen — the element may not have appeared yet."
|
|
562
|
-
};
|
|
603
|
+
});
|
|
563
604
|
var SimulatorClient = class {
|
|
564
605
|
simctl;
|
|
565
606
|
exec;
|
|
@@ -708,7 +749,7 @@ var SimulatorClient = class {
|
|
|
708
749
|
const client = new WdaClient({
|
|
709
750
|
baseUrl: async () => url,
|
|
710
751
|
timeoutMs: this.opts.wdaTimeoutMs,
|
|
711
|
-
remedies:
|
|
752
|
+
remedies: simulatorWdaRemedies(this.opts.allowWrites !== false),
|
|
712
753
|
...this.opts.fetch ? { fetch: this.opts.fetch } : {},
|
|
713
754
|
...this.opts.logger ? { logger: this.opts.logger } : {}
|
|
714
755
|
});
|
|
@@ -943,6 +984,7 @@ const diagnose = async (client, ctx, deviceHint) => {
|
|
|
943
984
|
problems.push("No simulator is booted.");
|
|
944
985
|
nextSteps.push("Boot one with ios_simulator_power {\"state\":\"booted\"} — this server never boots implicitly.");
|
|
945
986
|
}
|
|
987
|
+
const nothingBooted = simulators.length > 0 && booted.length === 0;
|
|
946
988
|
let target;
|
|
947
989
|
try {
|
|
948
990
|
target = await client.resolveTarget(deviceHint);
|
|
@@ -971,7 +1013,7 @@ const diagnose = async (client, ctx, deviceHint) => {
|
|
|
971
1013
|
} catch (err) {
|
|
972
1014
|
wda.error = err instanceof Error ? err.message : String(err);
|
|
973
1015
|
problems.push("WebDriverAgent is not answering, so the UI tree and the input tools will fail.");
|
|
974
|
-
nextSteps.push(
|
|
1016
|
+
nextSteps.push((nothingBooted ? "Then, once it is booted: " : "") + startRunnerRemedy(ctx.allowWrites) + " Everything else — including screenshots — works without it.");
|
|
975
1017
|
}
|
|
976
1018
|
if (wda.reachable) {
|
|
977
1019
|
const owner = await portOwner(client, client.wdaPort);
|
|
@@ -1157,6 +1199,27 @@ const registerEnvironmentTools = (server, client) => {
|
|
|
1157
1199
|
}
|
|
1158
1200
|
};
|
|
1159
1201
|
}));
|
|
1202
|
+
server.registerTool("ios_simulator_add_media", {
|
|
1203
|
+
title: "iOS Simulator: Add Media",
|
|
1204
|
+
description: "Put photos or videos into the simulator's photo library. A simulator has no camera, and this is how you get around that: seed the library first, then drive the app's picker as normal. Without it, every flow that starts with an image is untestable here. Files are copied in, so the originals are untouched and the simulator keeps them until it is erased. Pair it with `permission` on ios_simulator_set_environment to skip the access prompt entirely.",
|
|
1205
|
+
inputSchema: z.object({
|
|
1206
|
+
device: deviceArg,
|
|
1207
|
+
paths: z.array(z.string()).min(1).describe("Absolute paths to the images or videos, e.g. [\"/Users/me/fixtures/monstera.jpg\"]. Formats are the ones Photos itself accepts — JPEG, PNG, HEIC, MOV, MP4.")
|
|
1208
|
+
}),
|
|
1209
|
+
annotations: {
|
|
1210
|
+
readOnlyHint: false,
|
|
1211
|
+
destructiveHint: false,
|
|
1212
|
+
idempotentHint: false
|
|
1213
|
+
}
|
|
1214
|
+
}, async ({ device, paths }) => wrap(async () => {
|
|
1215
|
+
for (const path of paths) if (!path.startsWith("/")) throw new IosError$1(`Every path must be absolute, got "${path}".`, { remedy: "Pass the full path to each file." });
|
|
1216
|
+
const target = client.requireBooted(await client.resolveTarget(device));
|
|
1217
|
+
await client.simctl.addMedia(target.id, paths);
|
|
1218
|
+
return {
|
|
1219
|
+
added: paths,
|
|
1220
|
+
udid: target.id
|
|
1221
|
+
};
|
|
1222
|
+
}));
|
|
1160
1223
|
server.registerTool("ios_simulator_push", {
|
|
1161
1224
|
title: "iOS Simulator: Push",
|
|
1162
1225
|
description: "Deliver a push notification, with no APNs certificate and no server. The payload is a normal remote-notification body and must contain an `aps` key. Only remote pushes are simulated — not VoIP, complications or file-provider. The app must be installed; it does not have to be running.",
|
|
@@ -1190,16 +1253,17 @@ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
1190
1253
|
const registerListTool = (server, client) => {
|
|
1191
1254
|
server.registerTool("ios_simulator_list", {
|
|
1192
1255
|
title: "iOS Simulator: List",
|
|
1193
|
-
description: "List the simulators on this machine, with the UDID every other tool takes.
|
|
1256
|
+
description: "List the simulators on this machine, with the UDID every other tool takes. Shows the usable ones: a typical machine carries thirty-odd simulators and a good fraction of them are orphans whose runtime is no longer installed, which cannot be booted, driven or screenshotted at all. The result still says how many were left out and how to delete them, and `include_unavailable` brings them back with `available: false` and the reason, so \"why can't I use that one\" is still answerable here.",
|
|
1194
1257
|
inputSchema: z.object({
|
|
1195
1258
|
booted_only: z.boolean().default(false).describe("Only simulators that are currently running."),
|
|
1259
|
+
include_unavailable: z.boolean().default(false).describe("Also list simulators whose runtime is not installed. Off by default because they are not usable for anything; turn it on to see why a specific one is missing."),
|
|
1196
1260
|
name_contains: z.string().optional().describe("Case-insensitive substring filter on the name, e.g. \"iPhone 17\".")
|
|
1197
1261
|
}),
|
|
1198
1262
|
annotations: { readOnlyHint: true }
|
|
1199
|
-
}, async ({ booted_only, name_contains }) => wrap(async () => {
|
|
1263
|
+
}, async ({ booted_only, include_unavailable, name_contains }) => wrap(async () => {
|
|
1200
1264
|
const all = await client.listSimulators({ fresh: true });
|
|
1201
1265
|
const needle = name_contains?.toLowerCase();
|
|
1202
|
-
const matched = all.filter((sim) => (!booted_only || sim.state === "Booted") && (needle === void 0 || sim.name.toLowerCase().includes(needle)));
|
|
1266
|
+
const matched = all.filter((sim) => (include_unavailable || sim.available) && (!booted_only || sim.state === "Booted") && (needle === void 0 || sim.name.toLowerCase().includes(needle)));
|
|
1203
1267
|
const unavailable = all.filter((sim) => !sim.available).length;
|
|
1204
1268
|
return {
|
|
1205
1269
|
simulators: matched.map(toListRow),
|
|
@@ -1207,7 +1271,7 @@ const registerListTool = (server, client) => {
|
|
|
1207
1271
|
total: all.length,
|
|
1208
1272
|
...unavailable > 0 ? {
|
|
1209
1273
|
unavailable,
|
|
1210
|
-
note: `${unavailable} simulators have no installed runtime and cannot be used.
|
|
1274
|
+
note: `${unavailable} simulators have no installed runtime and cannot be used` + (include_unavailable ? "" : ", and are not listed above") + ". `xcrun simctl delete unavailable` clears them, or pass `include_unavailable: true` to see them here."
|
|
1211
1275
|
} : {}
|
|
1212
1276
|
};
|
|
1213
1277
|
}));
|
|
@@ -1338,7 +1402,7 @@ const registerRunnerTools = (server, client, outputDir, spawnRunner = defaultSpa
|
|
|
1338
1402
|
},
|
|
1339
1403
|
logPath
|
|
1340
1404
|
});
|
|
1341
|
-
if (pid === void 0) throw new IosError$1("Could not start the runner — the process had no pid.", { remedy:
|
|
1405
|
+
if (pid === void 0) throw new IosError$1("Could not start the runner — the process had no pid.", { remedy: START_RUNNER_SCRIPT_REMEDY });
|
|
1342
1406
|
return {
|
|
1343
1407
|
started: true,
|
|
1344
1408
|
pid,
|
|
@@ -1379,6 +1443,7 @@ const registerTools = (server, client, ctx, spawnRunner) => {
|
|
|
1379
1443
|
capHint: "raise IOS_SIMULATOR_MAX_TREE_BYTES",
|
|
1380
1444
|
buttons: ["home"],
|
|
1381
1445
|
emptyScreenshotRemedy: EMPTY_SCREENSHOT_REMEDY,
|
|
1446
|
+
alertHint: "If this is a permission prompt, it can be answered before it ever appears: ios_simulator_set_environment {\"permission\":{\"action\":\"grant\",\"service\":\"photos\",\"bundle_id\":\"…\"}}. `reset` puts the prompt back for testing the other branch.",
|
|
1382
1447
|
includeDisplayInfo: false
|
|
1383
1448
|
};
|
|
1384
1449
|
registerDiagnosticsTools(server, client, ctx);
|
|
@@ -1416,6 +1481,7 @@ const createServer = (opts) => {
|
|
|
1416
1481
|
execTimeoutMs: config.execTimeoutMs,
|
|
1417
1482
|
wdaTimeoutMs: config.wdaTimeoutMs,
|
|
1418
1483
|
wdaPort: config.wdaPort,
|
|
1484
|
+
allowWrites: config.allowWrites,
|
|
1419
1485
|
...config.wdaUrl ? { wdaUrl: config.wdaUrl } : {},
|
|
1420
1486
|
...config.simulatorId ? { defaultSimulatorId: config.simulatorId } : {},
|
|
1421
1487
|
...opts.exec ? { exec: opts.exec } : {},
|
|
@@ -1548,6 +1614,6 @@ const loadConfig = (env = process.env, configPath = defaultConfigPath(env)) => {
|
|
|
1548
1614
|
});
|
|
1549
1615
|
};
|
|
1550
1616
|
//#endregion
|
|
1551
|
-
export { WritesDisabledError as A,
|
|
1617
|
+
export { WritesDisabledError as A, SimulatorNotBootedError as C, ToolchainError as D, TOOLCHAIN_REMEDY as E, wdaUnavailableRemedy as M, BUILD_INFO as N, WdaError as O, START_RUNNER_TOOL_REMEDY as S, TIMEOUT_REMEDY as T, CommandTimeoutError as _, createServer as a, RuntimeUnavailableError as b, assertNotBulkTarget as c, summarizeDevices as d, toListRow as f, CommandError as g, toDisplayInfo as h, SERVER_VERSION as i, startRunnerRemedy as j, WdaUnavailableError as k, parseSimctlError as l, readScreenProfile as m, loadConfig as n, SimulatorClient as o, pngDimensions as p, SERVER_NAME as r, Simctl as s, defaultConfigPath as t, summarizeApps as u, EMPTY_SCREENSHOT_REMEDY as v, SimulatorNotFoundError as w, START_RUNNER_SCRIPT_REMEDY as x, IosError$1 as y };
|
|
1552
1618
|
|
|
1553
|
-
//# sourceMappingURL=config-
|
|
1619
|
+
//# sourceMappingURL=config-HSDMfemT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config-HSDMfemT.js","names":["IosError","IosError","IosError","IosError","IosError"],"sources":["../src/build-info.ts","../src/client/errors.ts","../src/client/display.ts","../src/client/shape.ts","../src/client/simctl.ts","../src/client/simulator.ts","../src/tools/util.ts","../src/tools/apps.ts","../src/tools/diagnostics.ts","../src/tools/environment.ts","../src/tools/lifecycle.ts","../src/tools/runner.ts","../src/tools/index.ts","../src/server.ts","../src/config.ts"],"sourcesContent":["// Build-time / runtime identity for the running server. `name`/`version` are\n// read from package.json at startup (always accurate); `gitCommit` /\n// `gitCommitDate` are injected by tsdown's `define` substitution at build time\n// and fall back to \"unknown\" when running from source (e.g. vitest).\n\nimport { readFileSync } from \"node:fs\";\n\n// oxlint-disable no-underscore-dangle -- bundler-injected build-time constants.\ndeclare const __GIT_COMMIT__: string;\ndeclare const __GIT_COMMIT_DATE__: string;\n\ntype PackageJson = { name: string; version: string };\n\nconst readPackageJson = (): PackageJson => {\n try {\n const pkgUrl = new URL(\"../package.json\", import.meta.url);\n return JSON.parse(readFileSync(pkgUrl, \"utf8\")) as PackageJson;\n } catch {\n return { name: \"@mgcrea/mcp-ios-device\", version: \"0.0.0\" };\n }\n};\n\nconst pkg = readPackageJson();\n\nexport type BuildInfo = {\n name: string;\n version: string;\n gitCommit: string;\n gitCommitDate: string;\n};\n\nexport const BUILD_INFO: BuildInfo = {\n name: pkg.name,\n version: pkg.version,\n gitCommit: typeof __GIT_COMMIT__ === \"string\" ? __GIT_COMMIT__ : \"unknown\",\n gitCommitDate: typeof __GIT_COMMIT_DATE__ === \"string\" ? __GIT_COMMIT_DATE__ : \"unknown\",\n};\n","import { IosError } from \"@mgcrea/mcp-ios-core\";\n\nexport {\n CommandError,\n CommandTimeoutError,\n IosError,\n ToolchainError,\n WdaError,\n WdaUnavailableError,\n} from \"@mgcrea/mcp-ios-core\";\n\n/** Raising the budget, or noticing the simulator died mid-call. */\nexport const TIMEOUT_REMEDY =\n \"Raise IOS_SIMULATOR_TIMEOUT_MS if this is a slow operation (a boot or a large `install` \" +\n \"genuinely takes tens of seconds), or check the simulator is still booted.\";\n\nexport const TOOLCHAIN_REMEDY =\n \"Install Xcode and run `xcode-select --install`, or point IOS_SIMULATOR_XCRUN_PATH at a \" +\n \"different xcrun.\";\n\n/**\n * How to get the runner up, from a shell.\n *\n * Much shorter than the device server's equivalent, and that is the headline\n * difference between the two: a simulator runner needs no Apple team, no\n * signing, no trust prompt and no tunnel.\n *\n * This is the *fallback*. It stays because it is the only route when writes are\n * off, and because it is what a person setting the server up for the first time\n * needs — but a remedy that sends a caller holding ios_simulator_restart_wda out\n * to a terminal is a remedy that wastes the tool it already has.\n */\nexport const START_RUNNER_SCRIPT_REMEDY =\n \"Build and start the runner with `scripts/wda.sh setup` then `scripts/wda.sh run` (or \" +\n \"`npx -p @mgcrea/mcp-ios-simulator ios-simulator-wda run` from an npm install) and leave it \" +\n \"open — the HTTP server is the XCTest process, so it stops when that command does. It needs no \" +\n \"Apple Developer team: a simulator build is not signed.\";\n\n/** The same thing, done with this server's own tool. */\nexport const START_RUNNER_TOOL_REMEDY =\n \"Start it with ios_simulator_restart_wda, which spawns the runner detached so it outlives this \" +\n \"conversation. Give it about fifteen seconds, then check ios_simulator_diagnostics for \" +\n \"`wda.reachable`.\";\n\n/**\n * How to get the runner up, in whichever way the caller can actually reach.\n *\n * `ios_simulator_restart_wda` is registered only when writes are on, so naming\n * it unconditionally would be the same mistake this fixes, pointed the other\n * way: a remedy naming a tool that is not in the list.\n */\nexport const startRunnerRemedy = (allowWrites: boolean): string =>\n allowWrites ? START_RUNNER_TOOL_REMEDY : START_RUNNER_SCRIPT_REMEDY;\n\n/** What to do when WebDriverAgent does not answer at all. */\nexport const wdaUnavailableRemedy = (allowWrites: boolean): string =>\n `${startRunnerRemedy(allowWrites)} Everything except ios_simulator_ui_tree and the input ` +\n \"tools works without it — screenshots go through simctl. If something else already forwards \" +\n \"the port, set IOS_SIMULATOR_WDA_URL.\";\n\n/** No simulator matched, or several did and none was named. */\nexport class SimulatorNotFoundError extends IosError {\n override readonly name = \"SimulatorNotFoundError\";\n\n constructor(message: string, remedy: string) {\n super(message, { remedy });\n }\n}\n\n/**\n * The simulator exists but is shut down.\n *\n * Its own error class because the fix is a single specific call and nothing\n * else: almost every simctl subcommand fails this way, `listapps` included, so\n * \"it is shut down\" would otherwise be buried in a CoreSimulator envelope that\n * reads like a lookup bug.\n */\nexport class SimulatorNotBootedError extends IosError {\n override readonly name = \"SimulatorNotBootedError\";\n\n constructor(name: string, udid: string) {\n super(`${name} is shut down.`, {\n remedy:\n `Boot it with ios_simulator_power {\"simulator\":\"${udid}\",\"state\":\"booted\"} — this server ` +\n \"never boots a simulator implicitly, because a cold boot takes tens of seconds and puts a \" +\n \"window on the user's screen.\",\n });\n }\n}\n\n/**\n * The simulator's runtime is not installed, so CoreSimulator cannot service it.\n *\n * Worth its own class because the failure it prevents is not an error message\n * at all: `simctl io` against one of these aborts with an uncaught\n * NSInternalInconsistencyException and a thirty-line stack trace.\n */\nexport class RuntimeUnavailableError extends IosError {\n override readonly name = \"RuntimeUnavailableError\";\n\n constructor(name: string, reason: string | undefined) {\n super(`${name} is unavailable${reason ? `: ${reason}` : \".\"}`, {\n remedy:\n \"The iOS runtime this simulator was created against is not installed. Install it in \" +\n \"Xcode > Settings > Components, or clear the orphans with `xcrun simctl delete unavailable`.\",\n });\n }\n}\n\n/** Thrown when a write path is reached while IOS_SIMULATOR_ALLOW_WRITES is off. */\nexport class WritesDisabledError extends IosError {\n override readonly name = \"WritesDisabledError\";\n\n constructor(what: string) {\n super(\n `${what} changes the simulator, but writes are disabled. ` +\n \"Unset IOS_SIMULATOR_ALLOW_WRITES (it defaults to on) to enable them again.\",\n );\n }\n}\n\n/** A shut-down simulator captures nothing, and that is the usual cause here. */\nexport const EMPTY_SCREENSHOT_REMEDY =\n \"The simulator produced an empty capture. Check it is actually booted with \" +\n \"ios_simulator_diagnostics, and that its runtime is installed.\";\n","import { join } from \"node:path\";\n\nimport type { DisplayInfo, ExecImpl } from \"@mgcrea/mcp-ios-core\";\n\nimport { IosError } from \"#/client/errors\";\nimport type { RawDeviceType } from \"#/client/simctl\";\n\n/**\n * Screen geometry for a simulator, which has no `devicectl device info displays`.\n *\n * Three sources were measured, and the split between them is the interesting\n * part:\n *\n * - **`profile.plist`, per device type.** `mainScreenWidth`, `mainScreenHeight`\n * and `mainScreenScale`, agreeing exactly with both `io enumerate` and the\n * captured PNG. A pure file read: no simctl call, nothing to boot, and no\n * crash surface on an unavailable simulator. This is where `pointScale`\n * comes from.\n * - **The captured PNG's own header.** This is where *pixels* come from\n * whenever there is a capture, because `simctl io … screenConfig geometry`\n * can resize a booted simulator's screen at runtime and the profile would\n * then be silently wrong — putting every tap coordinate in the wrong space\n * with nothing looking wrong.\n * - **`io enumerate`**, which also reports it, but is a text parse of a\n * 150-line dump and aborts on an unavailable device. Not used.\n */\nexport const profilePlistPath = (bundlePath: string): string =>\n join(bundlePath, \"Contents\", \"Resources\", \"profile.plist\");\n\nexport type ScreenProfile = {\n mainScreenWidth?: number;\n mainScreenHeight?: number;\n mainScreenScale?: number;\n};\n\nexport const readScreenProfile = async (\n deviceType: RawDeviceType,\n opts: { plutilPath: string; exec: ExecImpl; timeoutMs: number },\n): Promise<ScreenProfile> => {\n if (!deviceType.bundlePath) return {};\n const { stdout } = await opts.exec(\n opts.plutilPath,\n [\"-convert\", \"json\", \"-o\", \"-\", profilePlistPath(deviceType.bundlePath)],\n opts.timeoutMs,\n );\n try {\n return JSON.parse(stdout) as ScreenProfile;\n } catch {\n return {};\n }\n};\n\n/**\n * Width and height straight out of a PNG's IHDR.\n *\n * Cheaper than shelling to `sips` for the one number that must not be stale,\n * and the bytes are already in hand: the signature is fixed, `IHDR` is always\n * the first chunk, and the two dimensions are big-endian at offsets 16 and 20.\n */\nexport const pngDimensions = (png: Buffer): { width: number; height: number } | undefined => {\n const SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n if (png.length < 24 || !png.subarray(0, 8).equals(SIGNATURE)) return undefined;\n if (png.subarray(12, 16).toString(\"ascii\") !== \"IHDR\") return undefined;\n return { width: png.readUInt32BE(16), height: png.readUInt32BE(20) };\n};\n\n/**\n * Assemble a `DisplayInfo`.\n *\n * `orientation` is derived by comparing the capture's own aspect against the\n * profile's portrait dimensions, and is `\"unknown\"` when there is no capture to\n * compare — never guessed as `\"portrait\"`, because the whole point of the field\n * is telling a caller whether the screen they are about to read is rotated.\n *\n * `backlightState` is left undefined: a simulator has no backlight, and\n * `io enumerate`'s `Power state` is about the framebuffer port rather than the\n * screen, so mapping it would be a lie.\n */\nexport const toDisplayInfo = (\n profile: ScreenProfile,\n capture?: { width: number; height: number },\n): DisplayInfo => {\n const scale = profile.mainScreenScale ?? 1;\n const pixelWidth = capture?.width ?? profile.mainScreenWidth;\n const pixelHeight = capture?.height ?? profile.mainScreenHeight;\n\n if (!pixelWidth || !pixelHeight) {\n throw new IosError(\"Could not determine the simulator's screen geometry.\", {\n remedy:\n \"The device type's profile.plist could not be read and no screenshot was available. \" +\n \"Run ios_simulator_diagnostics — an unavailable runtime is the usual cause.\",\n });\n }\n\n const landscape =\n profile.mainScreenWidth !== undefined && profile.mainScreenHeight !== undefined\n ? pixelWidth > pixelHeight && profile.mainScreenWidth < profile.mainScreenHeight\n : pixelWidth > pixelHeight;\n\n return {\n pixelWidth,\n pixelHeight,\n pointWidth: Math.round(pixelWidth / scale),\n pointHeight: Math.round(pixelHeight / scale),\n pointScale: scale,\n orientation: capture ? (landscape ? \"landscape\" : \"portrait\") : \"unknown\",\n };\n};\n","// The context-window layer.\n//\n// `simctl listapps` spends ~38 KB on the 31 apps a stock simulator carries, 25\n// of which are Apple's own, and most of every entry is group-container UUIDs no\n// caller acts on. `list devices` is smaller but still reports five keys per\n// simulator that only matter to CoreSimulator.\n//\n// So: lists return the few fields the other tools consume, and nothing else.\n\nimport type { RawDeviceType, RawRuntime, RawSim, RawSimApp } from \"#/client/simctl\";\n\nexport type SimulatorSummary = {\n /**\n * The UDID. Called `id` because that is the name the shared `ScreenHost`\n * contract uses — a physical device has two identifiers and a simulator has\n * one, so there is nothing here to disambiguate. Rendered back as `udid` by\n * `toListRow`, which is the word anyone reading the output will know it by.\n */\n id: string;\n name: string;\n /** `Booted` or `Shutdown`, verbatim from CoreSimulator. */\n state: string;\n runtime: string;\n deviceType: string | undefined;\n deviceTypeIdentifier: string | undefined;\n /**\n * False when the runtime is missing. Kept rather than filtered: \"why can't I\n * use that one\" has to be answerable from the list, and an unavailable\n * simulator is the one input that makes `simctl io` abort rather than fail.\n */\n available: boolean;\n unavailableReason?: string;\n lastBootedAt?: string;\n};\n\n/**\n * Flatten the runtime-keyed map `list devices` returns.\n *\n * The key is a runtime *identifier* and identifiers are not unique — a machine\n * carrying both iOS 26.4 and 26.4.1 has two entries under\n * `com.apple.CoreSimulator.SimRuntime.iOS-26-4`. So the key is never used as a\n * primary key: it is joined against `list runtimes` for a display name, 0..n\n * matches tolerated, and carried through verbatim besides.\n */\nexport const summarizeDevices = (\n devices: Record<string, RawSim[]>,\n runtimes: RawRuntime[],\n deviceTypes: RawDeviceType[],\n): SimulatorSummary[] => {\n const runtimeName = new Map(\n runtimes.map((r) => [r.identifier, r.name ?? r.version ?? r.identifier]),\n );\n const typeName = new Map(deviceTypes.map((t) => [t.identifier, t.name]));\n\n return Object.entries(devices).flatMap(([runtimeId, sims]) =>\n (sims ?? []).map((sim) => ({\n id: sim.udid,\n name: sim.name,\n state: sim.state,\n runtime: runtimeName.get(runtimeId) ?? runtimeId.split(\".\").pop() ?? runtimeId,\n deviceType: sim.deviceTypeIdentifier ? typeName.get(sim.deviceTypeIdentifier) : undefined,\n deviceTypeIdentifier: sim.deviceTypeIdentifier,\n available: sim.isAvailable !== false,\n ...(sim.availabilityError ? { unavailableReason: sim.availabilityError } : {}),\n ...(sim.lastBootedAt ? { lastBootedAt: sim.lastBootedAt } : {}),\n })),\n );\n};\n\nexport type SimAppSummary = {\n bundleId: string;\n name: string | undefined;\n version: string | undefined;\n build: string | undefined;\n type: string | undefined;\n /**\n * Host filesystem paths, and the reason there is no `pull_container` tool: a\n * simulator's containers are already on this machine, so reading an app's\n * SwiftData store is an ordinary file read rather than a copy off a device.\n */\n bundlePath: string | undefined;\n dataContainer: string | undefined;\n};\n\n/** `file:///Users/…/Foo.app/` → `/Users/…/Foo.app`, which is what a person can use. */\nconst toPath = (url: string | undefined): string | undefined => {\n if (!url) return undefined;\n if (!url.startsWith(\"file://\")) return url;\n try {\n return decodeURIComponent(new URL(url).pathname).replace(/\\/$/, \"\");\n } catch {\n return url;\n }\n};\n\nexport const summarizeApps = (\n apps: Record<string, RawSimApp>,\n opts: { includeAll?: boolean } = {},\n): SimAppSummary[] =>\n Object.values(apps)\n .filter((app) => opts.includeAll === true || app.ApplicationType === \"User\")\n .map((app) => ({\n bundleId: app.CFBundleIdentifier ?? \"\",\n name: app.CFBundleDisplayName ?? app.CFBundleName,\n version: app.CFBundleShortVersionString,\n build: app.CFBundleVersion,\n type: app.ApplicationType,\n bundlePath: toPath(app.Bundle ?? app.Path),\n dataContainer: toPath(app.DataContainer),\n }))\n .filter((app) => app.bundleId !== \"\")\n .toSorted((a, b) => a.bundleId.localeCompare(b.bundleId));\n\n/** The public shape of one row of `ios_simulator_list`. */\nexport const toListRow = (sim: SimulatorSummary): Record<string, unknown> => ({\n udid: sim.id,\n name: sim.name,\n state: sim.state,\n runtime: sim.runtime,\n ...(sim.deviceType ? { deviceType: sim.deviceType } : {}),\n ...(sim.available ? {} : { available: false }),\n ...(sim.unavailableReason ? { unavailableReason: sim.unavailableReason } : {}),\n});\n","import { mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport {\n assertNoShellMetachars,\n createExec,\n type ExecImpl,\n type Logger,\n} from \"@mgcrea/mcp-ios-core\";\n\nimport { IosError, TIMEOUT_REMEDY, TOOLCHAIN_REMEDY } from \"#/client/errors\";\n\n/**\n * `xcrun simctl` — Apple's own simulator tool, and the half of this server that\n * needs nothing installed and nothing signed.\n *\n * It covers strictly more here than `devicectl` does on a physical device: app\n * lifecycle, but also the screen itself (`io screenshot`), the appearance, the\n * status bar, permissions, location and push. Only the accessibility tree and\n * synthetic touches need WebDriverAgent.\n *\n * Three output shapes, and knowing which is which explains the whole class:\n *\n * - `list` takes `-j` and writes clean JSON to **stdout**. It is the only\n * subcommand that does; `listapps --json` does not exist.\n * - `listapps` writes an old-style NeXTSTEP plist. `plutil` converts it, but\n * `ExecImpl` has no stdin, so it goes through a temp file and two processes.\n * - everything else answers in prose, including `(null)` and `unknown`, which\n * are passed through rather than normalised — see the readers below.\n */\nexport type SimctlOptions = {\n xcrunPath: string;\n plutilPath: string;\n timeoutMs: number;\n exec?: ExecImpl | undefined;\n logger?: Logger | undefined;\n};\n\nexport type RawSim = {\n udid: string;\n name: string;\n state: string;\n isAvailable?: boolean;\n availabilityError?: string;\n deviceTypeIdentifier?: string;\n lastBootedAt?: string;\n};\n\nexport type RawRuntime = {\n identifier: string;\n name?: string;\n version?: string;\n isAvailable?: boolean;\n};\n\nexport type RawDeviceType = {\n identifier: string;\n name?: string;\n bundlePath?: string;\n modelIdentifier?: string;\n};\n\n/** One app as `listapps` reports it, once plutil has turned it into JSON. */\nexport type RawSimApp = {\n CFBundleIdentifier?: string;\n CFBundleDisplayName?: string;\n CFBundleName?: string;\n CFBundleShortVersionString?: string;\n CFBundleVersion?: string;\n ApplicationType?: string;\n Bundle?: string;\n Path?: string;\n DataContainer?: string;\n};\n\n/**\n * The two exit codes simctl actually uses, and what they mean.\n *\n * `148` is a device it could not even parse as an identifier. `149` is\n * everything else, wrapped in an envelope whose first line is machine-readable\n * and whose second line is the only part worth showing a person.\n */\nconst INVALID_DEVICE = 148;\n\n/**\n * Pull the human sentence out of a CoreSimulator error envelope.\n *\n * An error was encountered processing the command (domain=…, code=405):\n * Unable to lookup in current state: Shutdown\n *\n * Without this the message a caller sees is the first line, which names a\n * numeric code and says nothing about what to do.\n */\nexport const parseSimctlError = (\n stderr: string,\n): { domain?: string; code?: number; message: string } => {\n const envelope = /\\(domain=([^,]+),\\s*code=(\\d+)\\)\\s*:?\\s*\\n?([\\s\\S]*)/.exec(stderr);\n if (envelope) {\n return {\n domain: envelope[1]?.trim(),\n code: Number(envelope[2]),\n message: (envelope[3] ?? \"\").trim().split(\"\\n\")[0]?.trim() ?? stderr.trim(),\n };\n }\n return { message: stderr.trim().split(\"\\n\")[0]?.trim() ?? stderr.trim() };\n};\n\n/**\n * simctl reports its interesting failures in prose, and each has a different\n * fix. Every string here describes something only a simulator has.\n */\nconst remedyFor = (stderr: string): string | undefined => {\n const { message } = parseSimctlError(stderr);\n const text = `${stderr} ${message}`.toLowerCase();\n if (text.includes(\"invalid device\")) {\n return \"That is not a UDID or a name simctl knows. Run ios_simulator_list to see what exists.\";\n }\n if (text.includes(\"unable to lookup in current state: shutdown\")) {\n return 'The simulator is shut down. Boot it with ios_simulator_power {\"state\":\"booted\"}.';\n }\n if (text.includes(\"current state: booted\")) {\n return \"The simulator is already booted, which for most operations means there is nothing to do.\";\n }\n if (text.includes(\"no such file or directory\")) {\n return (\n \"Check the path exists and is a `.app` **bundle directory** built for the simulator — an \" +\n \"`.ipa` or a device build will not install, and reports exactly this.\"\n );\n }\n if (text.includes(\"failed to initialize io ports\")) {\n return (\n \"The simulator's runtime is not installed, so CoreSimulator cannot service it. Install the \" +\n \"runtime, or clear the orphans with `xcrun simctl delete unavailable`.\"\n );\n }\n return undefined;\n};\n\n/**\n * The one argument that must never reach simctl.\n *\n * `erase`, `delete` and `shutdown` all accept the literal string `all`, and\n * `simctl erase all` wipes every simulator on the machine. A device hint that\n * happens to be the word \"all\" is the single worst thing this server could\n * forward, so it is refused here as well as in the resolver — the resolver is\n * where it should be caught, and this is where it cannot be missed.\n */\nexport const assertNotBulkTarget = (udid: string): void => {\n if (udid === \"all\" || udid === \"unavailable\" || udid === \"booted\") {\n throw new IosError(`Refusing to pass \"${udid}\" to simctl as a device.`, {\n remedy:\n \"simctl reads these as *every* matching simulator, so `erase all` would wipe the machine. \" +\n \"Resolve to one concrete UDID with ios_simulator_list first.\",\n });\n }\n};\n\nexport class Simctl {\n private readonly opts: SimctlOptions;\n private readonly exec: ExecImpl;\n /** Device types change only when Xcode is updated; re-reading is pure cost. */\n private deviceTypes: RawDeviceType[] | undefined;\n\n constructor(opts: SimctlOptions) {\n this.opts = opts;\n this.exec =\n opts.exec ?? createExec({ timeout: TIMEOUT_REMEDY, toolchain: TOOLCHAIN_REMEDY, remedyFor });\n }\n\n /** Raw stdout, for the subcommands that answer in prose. */\n private async run(args: string[]): Promise<string> {\n this.opts.logger?.debug?.(\"simctl\", ...args);\n const { stdout } = await this.exec(\n this.opts.xcrunPath,\n [\"simctl\", ...args],\n this.opts.timeoutMs,\n );\n return stdout;\n }\n\n /**\n * `list` and only `list`. `-e` stops `/` being escaped as `\\/`, which matters\n * only for fixture readability but costs nothing.\n */\n private async runJson<T>(args: string[]): Promise<T> {\n const stdout = await this.run([...args, \"-j\", \"-e\"]);\n try {\n return JSON.parse(stdout) as T;\n } catch {\n throw new IosError(`simctl ${args.join(\" \")} did not return JSON.`, {\n details: stdout.slice(0, 400),\n });\n }\n }\n\n /**\n * A NeXTSTEP plist, via `plutil` and a temp file.\n *\n * The obvious `simctl listapps … | plutil -convert json -o - -` is not\n * expressible: `ExecImpl` takes argv and no stdin, deliberately, because that\n * is what keeps every caller value data rather than shell syntax. So simctl's\n * stdout is written out and handed to plutil by path. A fresh directory per\n * call rather than a shared one, so two concurrent tools cannot read each\n * other's answer with nothing looking wrong.\n */\n private async runPlist<T>(args: string[]): Promise<T> {\n const stdout = await this.run(args);\n const dir = await mkdtemp(join(tmpdir(), \"simctl-\"));\n const path = join(dir, \"out.plist\");\n try {\n await writeFile(path, stdout);\n const { stdout: json } = await this.exec(\n this.opts.plutilPath,\n [\"-convert\", \"json\", \"-o\", \"-\", path],\n this.opts.timeoutMs,\n );\n return JSON.parse(json) as T;\n } catch (err) {\n if (err instanceof IosError) throw err;\n throw new IosError(`Could not read simctl ${args[0]} output as a property list.`, {\n details: stdout.slice(0, 400),\n });\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n }\n\n // ------------------------------------------------------------------ list --\n\n async listDevices(): Promise<Record<string, RawSim[]>> {\n const result = await this.runJson<{ devices?: Record<string, RawSim[]> }>([\"list\", \"devices\"]);\n return result.devices ?? {};\n }\n\n async listRuntimes(): Promise<RawRuntime[]> {\n return (await this.runJson<{ runtimes?: RawRuntime[] }>([\"list\", \"runtimes\"])).runtimes ?? [];\n }\n\n async listDeviceTypes(): Promise<RawDeviceType[]> {\n this.deviceTypes ??=\n (await this.runJson<{ devicetypes?: RawDeviceType[] }>([\"list\", \"devicetypes\"]))\n .devicetypes ?? [];\n return this.deviceTypes;\n }\n\n // ------------------------------------------------------------- lifecycle --\n\n async boot(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"boot\", udid]);\n }\n\n async shutdown(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"shutdown\", udid]);\n }\n\n async erase(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"erase\", udid]);\n }\n\n /** Attach the Simulator UI to whatever is booted. A `boot` alone is headless. */\n async openApp(openPath: string): Promise<void> {\n await this.exec(openPath, [\"-a\", \"Simulator\"], this.opts.timeoutMs);\n }\n\n // ------------------------------------------------------------------ apps --\n\n async listApps(udid: string): Promise<Record<string, RawSimApp>> {\n assertNotBulkTarget(udid);\n return this.runPlist<Record<string, RawSimApp>>([\"listapps\", udid]);\n }\n\n async install(udid: string, appPath: string): Promise<void> {\n assertNotBulkTarget(udid);\n assertNoShellMetachars(\"path\", appPath);\n await this.run([\"install\", udid, appPath]);\n }\n\n /**\n * Put photos and videos in the simulator's library.\n *\n * `addmedia` is variadic and takes the paths as plain argv, so unlike `push`\n * this needs no temp file. It is the one route around the simulator's missing\n * camera: an app that reads from the photo library can be exercised end to\n * end, while one that insists on a live capture still cannot.\n */\n async addMedia(udid: string, paths: string[]): Promise<void> {\n assertNotBulkTarget(udid);\n for (const path of paths) assertNoShellMetachars(\"path\", path);\n await this.run([\"addmedia\", udid, ...paths]);\n }\n\n /**\n * Launch, optionally replacing a running copy.\n *\n * Environment variables do not go on the command line: simctl reads them from\n * its **own** environment under a `SIMCTL_CHILD_` prefix. `ExecImpl` pins a\n * minimal environment on purpose, so a caller wanting them has to say so, and\n * this is where that would be threaded through.\n */\n async launch(\n udid: string,\n bundleId: string,\n opts: { args?: string[]; terminateExisting?: boolean; stdout?: string; stderr?: string } = {},\n ): Promise<string> {\n assertNotBulkTarget(udid);\n const flags = [\n ...(opts.terminateExisting ? [\"--terminate-running-process\"] : []),\n ...(opts.stdout ? [`--stdout=${opts.stdout}`] : []),\n ...(opts.stderr ? [`--stderr=${opts.stderr}`] : []),\n ];\n return this.run([\"launch\", ...flags, udid, bundleId, ...(opts.args ?? [])]);\n }\n\n async terminate(udid: string, bundleId: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"terminate\", udid, bundleId]);\n }\n\n async openUrl(udid: string, url: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"openurl\", udid, url]);\n }\n\n // ---------------------------------------------------------------- screen --\n\n /**\n * A PNG of the screen, base64, with no WebDriverAgent involved.\n *\n * The documented `-` for stdout **does not work**: measured on Xcode 26.6, it\n * writes a file literally named `-` into the current working directory and\n * leaves stdout empty, exit code 0. So this passes a real path, like the\n * device lane's `--json-output` does, and for the same reason uses a fresh\n * directory per call.\n *\n * A successful capture also writes a `Note: No display specified…` line to\n * stderr, so non-empty stderr is not a failure here.\n */\n async screenshotPng(udid: string): Promise<string> {\n assertNotBulkTarget(udid);\n const dir = await mkdtemp(join(tmpdir(), \"simshot-\"));\n const path = join(dir, \"shot.png\");\n try {\n await this.run([\"io\", udid, \"screenshot\", \"--type=png\", path]);\n return (await readFile(path)).toString(\"base64\");\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n }\n\n // ----------------------------------------------------------- environment --\n\n /**\n * Read a `simctl ui` option.\n *\n * `unknown` and `unsupported` are passed through rather than normalised: they\n * mean genuinely different things — the runtime is too old, versus it failed\n * to read — and collapsing them throws away a distinction a caller can act on.\n */\n async uiGet(udid: string, option: string): Promise<string> {\n assertNotBulkTarget(udid);\n return (await this.run([\"ui\", udid, option])).trim();\n }\n\n async uiSet(udid: string, option: string, value: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"ui\", udid, option, value]);\n }\n\n async statusBarOverride(udid: string, flags: string[]): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"status_bar\", udid, \"override\", ...flags]);\n }\n\n async statusBarClear(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"status_bar\", udid, \"clear\"]);\n }\n\n async statusBarList(udid: string): Promise<string> {\n assertNotBulkTarget(udid);\n return (await this.run([\"status_bar\", udid, \"list\"])).trim();\n }\n\n async locationSet(udid: string, latitude: number, longitude: number): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"location\", udid, \"set\", `${latitude},${longitude}`]);\n }\n\n async locationClear(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"location\", udid, \"clear\"]);\n }\n\n /** `grant` and `revoke` need a bundle id; `reset` does not. */\n async privacy(udid: string, action: string, service: string, bundleId?: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"privacy\", udid, action, service, ...(bundleId ? [bundleId] : [])]);\n }\n\n /**\n * A simulated remote push. The payload goes through a temp file rather than\n * stdin, for the same argv-only reason as the plist lane.\n */\n async push(udid: string, bundleId: string, payload: unknown): Promise<void> {\n assertNotBulkTarget(udid);\n const dir = await mkdtemp(join(tmpdir(), \"simpush-\"));\n const path = join(dir, \"payload.json\");\n try {\n await writeFile(path, JSON.stringify(payload));\n await this.run([\"push\", udid, bundleId, path]);\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n }\n}\n\nexport { INVALID_DEVICE };\n","import {\n createExec,\n WdaClient,\n type DisplayInfo,\n type ExecImpl,\n type Logger,\n type ScreenHost,\n} from \"@mgcrea/mcp-ios-core\";\n\nimport { pngDimensions, readScreenProfile, toDisplayInfo } from \"#/client/display\";\nimport {\n RuntimeUnavailableError,\n SimulatorNotBootedError,\n SimulatorNotFoundError,\n startRunnerRemedy,\n TIMEOUT_REMEDY,\n TOOLCHAIN_REMEDY,\n wdaUnavailableRemedy,\n} from \"#/client/errors\";\nimport { summarizeDevices, type SimulatorSummary } from \"#/client/shape\";\nimport { Simctl } from \"#/client/simctl\";\n\n/**\n * What to say when WebDriverAgent fails, on a simulator.\n *\n * A function of the write gate rather than a constant: three of these four\n * remedies name ios_simulator_restart_wda, and that tool is not registered when\n * writes are off. Sending a caller to a tool it cannot see is the same failure\n * as sending it to a shell it did not need.\n */\nconst simulatorWdaRemedies = (allowWrites: boolean) => ({\n unavailable: wdaUnavailableRemedy(allowWrites),\n notAuthorized:\n \"WebDriverAgent is running but is refusing to drive the UI. On a simulator there is no \" +\n \"Enable UI Automation toggle to blame, so this is nearly always a stale runner. \" +\n startRunnerRemedy(allowWrites),\n noForegroundApp:\n \"WebDriverAgent can see no foreground application on a session it has just created. Launch \" +\n `the app with ios_simulator_launch, or restart the runner. ${startRunnerRemedy(allowWrites)}`,\n noSuchElement:\n \"Call ios_simulator_ui_tree to see what is actually on screen — the element may not have \" +\n \"appeared yet.\",\n});\n\nexport type SimulatorClientOptions = {\n xcrunPath: string;\n sipsPath: string;\n plutilPath: string;\n openPath: string;\n execTimeoutMs: number;\n wdaTimeoutMs: number;\n wdaPort: number;\n wdaUrl?: string | undefined;\n /** Only so the WebDriverAgent remedies can name a tool this server registers. */\n allowWrites?: boolean | undefined;\n defaultSimulatorId?: string | undefined;\n exec?: ExecImpl | undefined;\n fetch?: typeof fetch | undefined;\n logger?: Logger | undefined;\n};\n\nexport class SimulatorClient implements ScreenHost<SimulatorSummary> {\n readonly simctl: Simctl;\n readonly exec: ExecImpl;\n readonly sipsPath: string;\n readonly plutilPath: string;\n readonly openPath: string;\n readonly execTimeoutMs: number;\n private readonly opts: SimulatorClientOptions;\n private readonly wdaClients = new Map<string, WdaClient>();\n /** `simctl list` is ~80ms and most tools want it twice; 2s collapses that. */\n private cache: { at: number; simulators: SimulatorSummary[] } | undefined;\n private profiles = new Map<string, Awaited<ReturnType<typeof readScreenProfile>>>();\n\n constructor(opts: SimulatorClientOptions) {\n this.opts = opts;\n this.exec = opts.exec ?? createExec({ timeout: TIMEOUT_REMEDY, toolchain: TOOLCHAIN_REMEDY });\n this.sipsPath = opts.sipsPath;\n this.plutilPath = opts.plutilPath;\n this.openPath = opts.openPath;\n this.execTimeoutMs = opts.execTimeoutMs;\n this.simctl = new Simctl({\n xcrunPath: opts.xcrunPath,\n plutilPath: opts.plutilPath,\n timeoutMs: opts.execTimeoutMs,\n ...(opts.exec ? { exec: opts.exec } : {}),\n ...(opts.logger ? { logger: opts.logger } : {}),\n });\n }\n\n async listSimulators(opts: { fresh?: boolean } = {}): Promise<SimulatorSummary[]> {\n const cached = this.cache;\n if (!opts.fresh && cached && Date.now() - cached.at < 2000) return cached.simulators;\n const [devices, runtimes, deviceTypes] = await Promise.all([\n this.simctl.listDevices(),\n this.simctl.listRuntimes(),\n this.simctl.listDeviceTypes(),\n ]);\n const simulators = summarizeDevices(devices, runtimes, deviceTypes);\n this.cache = { at: Date.now(), simulators };\n return simulators;\n }\n\n /**\n * Resolve a hint to exactly one simulator, or refuse.\n *\n * The device server's rule — never guess between two — transfers, but\n * \"connected\" has to become the right predicate. A typical machine carries\n * dozens of simulators, most of them shut down and some of them unavailable,\n * so the population is mostly non-candidates.\n *\n * Three things happen here that cannot happen further down:\n *\n * - `all` and `unavailable` are refused outright. `simctl erase all` wipes\n * every simulator on the machine, and a hint is a string from a model.\n * - `booted` is resolved *here* rather than passed through. simctl's own\n * documentation says that with several booted it \"will choose one of\n * them\", and a coin flip that drives the wrong screen most of the time is\n * exactly what the refuse-to-guess rule exists to prevent.\n * - availability is checked before the target is returned, because `simctl\n * io` against an unavailable simulator aborts with an uncaught\n * NSInternalInconsistencyException rather than failing.\n *\n * State is deliberately *not* checked: `ios_simulator_list` and\n * `ios_simulator_diagnostics` are useful against a shut-down simulator, so\n * requiring booted is each tool's decision, via `requireBooted`.\n */\n async resolveTarget(hint?: string): Promise<SimulatorSummary> {\n const wanted = hint ?? this.opts.defaultSimulatorId;\n const simulators = await this.listSimulators();\n\n if (wanted === \"all\" || wanted === \"unavailable\") {\n throw new SimulatorNotFoundError(\n `\"${wanted}\" is not a simulator, it is how simctl says *every* simulator.`,\n \"Name one by UDID or by name. `simctl erase all` would wipe the machine, so this server \" +\n \"refuses to forward it.\",\n );\n }\n\n const usable = simulators.filter((sim) => sim.available);\n if (wanted !== undefined && wanted !== \"booted\") {\n const byUdid = simulators.filter((sim) => sim.id.toLowerCase() === wanted.toLowerCase());\n // A name is ambiguous far more often than a UDID: the same device name\n // exists once per runtime, and the orphans of an uninstalled runtime keep\n // theirs. Prefer the ones that can actually be used, so \"iPhone 17 Pro\"\n // resolves to the real one rather than erroring against a dead 26.3 twin.\n const named = simulators.filter((sim) => sim.name === wanted);\n const byName = named.some((sim) => sim.available)\n ? named.filter((sim) => sim.available)\n : named;\n const matches = byUdid.length > 0 ? byUdid : byName;\n if (matches.length === 0) {\n throw new SimulatorNotFoundError(\n `No simulator matches \"${wanted}\".`,\n simulators.length === 0\n ? \"simctl knows about no simulators at all. Install a runtime in Xcode > Settings > Components.\"\n : `Run ios_simulator_list to see them. ${usable.length} of ${simulators.length} are available.`,\n );\n }\n if (matches.length > 1) {\n throw new SimulatorNotFoundError(\n `${matches.length} simulators are named \"${wanted}\".`,\n `Pass a UDID instead: ${matches.map((sim) => `${sim.name} = ${sim.id}`).join(\", \")}.`,\n );\n }\n return this.assertAvailable(matches[0] as SimulatorSummary);\n }\n\n const booted = usable.filter((sim) => sim.state === \"Booted\");\n if (booted.length === 1) return this.assertAvailable(booted[0] as SimulatorSummary);\n if (booted.length === 0) {\n const candidates = usable\n .slice(0, 6)\n .map((sim) => `${sim.name} = ${sim.id}`)\n .join(\", \");\n throw new SimulatorNotFoundError(\n \"No simulator is booted.\",\n usable.length === 0\n ? \"No simulator on this machine has an installed runtime. Install one in Xcode > Settings > Components.\"\n : `Boot one with ios_simulator_power, e.g. ${candidates}.`,\n );\n }\n throw new SimulatorNotFoundError(\n `${booted.length} simulators are booted, so there is no obvious default.`,\n `Pass \\`simulator\\` (or set IOS_SIMULATOR_ID) to one of: ${booted\n .map((sim) => `${sim.name} = ${sim.id}`)\n .join(\", \")}.`,\n );\n }\n\n private assertAvailable(sim: SimulatorSummary): SimulatorSummary {\n if (!sim.available) throw new RuntimeUnavailableError(sim.name, sim.unavailableReason);\n return sim;\n }\n\n /** Every tool that needs a running simulator calls this; the resolver does not. */\n requireBooted(sim: SimulatorSummary): SimulatorSummary {\n if (sim.state !== \"Booted\") throw new SimulatorNotBootedError(sim.name, sim.id);\n return sim;\n }\n\n /**\n * Geometry. Cached per device *type* rather than per simulator, since that is\n * what the profile describes and it changes only when Xcode does.\n */\n async display(target: SimulatorSummary, capturePng?: string): Promise<DisplayInfo> {\n const id = target.deviceTypeIdentifier;\n let profile = id ? this.profiles.get(id) : undefined;\n if (!profile) {\n const deviceType = (await this.simctl.listDeviceTypes()).find((t) => t.identifier === id);\n profile = deviceType\n ? await readScreenProfile(deviceType, {\n plutilPath: this.plutilPath,\n exec: this.exec,\n timeoutMs: this.execTimeoutMs,\n })\n : {};\n if (id) this.profiles.set(id, profile);\n }\n const capture = capturePng ? pngDimensions(Buffer.from(capturePng, \"base64\")) : undefined;\n return toDisplayInfo(profile, capture);\n }\n\n /** The capture lane that needs no runner — the whole reason this server is cheap to start. */\n async screenshotPng(target: SimulatorSummary): Promise<string> {\n this.requireBooted(target);\n return this.simctl.screenshotPng(target.id);\n }\n\n /**\n * Where WebDriverAgent is.\n *\n * Unlike the device lane there is no tunnel address to derive: a simulator\n * shares the host's network stack, so the runner's port *is* a host loopback\n * port. That is also the trap — several booted simulators contend for the\n * same one, and WebDriverAgent scans 8100-8199 when `USE_PORT` is unset, so a\n * second runner comes up healthy on 8101 and a server pointed at 8100 drives\n * the first simulator while reporting success. `ios_simulator_diagnostics`\n * reports which simulator actually owns the port.\n */\n wdaUrl(): string {\n return this.opts.wdaUrl ?? `http://127.0.0.1:${this.opts.wdaPort}`;\n }\n\n get wdaPort(): number {\n return this.opts.wdaPort;\n }\n\n wda(_target: SimulatorSummary): WdaClient {\n const url = this.wdaUrl();\n const existing = this.wdaClients.get(url);\n if (existing) return existing;\n const client = new WdaClient({\n baseUrl: async () => url,\n timeoutMs: this.opts.wdaTimeoutMs,\n remedies: simulatorWdaRemedies(this.opts.allowWrites !== false),\n ...(this.opts.fetch ? { fetch: this.opts.fetch } : {}),\n ...(this.opts.logger ? { logger: this.opts.logger } : {}),\n });\n this.wdaClients.set(url, client);\n return client;\n }\n}\n","import { createArgs, type ScreenNaming } from \"@mgcrea/mcp-ios-core\";\n\nexport {\n compact,\n fail,\n ok,\n okImage,\n okText,\n toFailure,\n wrap,\n wrapResult,\n} from \"@mgcrea/mcp-ios-core\";\nexport type { ImageContent, TextContent, ToolResult } from \"@mgcrea/mcp-ios-core\";\n\n/**\n * How the shared tools name themselves and each other in this server.\n *\n * `copy.target` is written out rather than derived: a simulator is picked by\n * UDID, by name, or by being the only booted one, and none of those is what the\n * device server's version of this sentence says. `names.listTargets` is\n * overridden for the same reason — `ios_simulator_list_devices` would be a\n * mouthful for a tool that lists simulators.\n */\nexport const SIMULATOR_NAMING: ScreenNaming = {\n prefix: \"ios_simulator\",\n title: \"iOS Simulator\",\n noun: \"simulator\",\n envPrefix: \"IOS_SIMULATOR\",\n names: { listTargets: \"ios_simulator_list\" },\n copy: {\n target:\n \"Which simulator: its UDID or its name as shown by ios_simulator_list. Omit it when exactly \" +\n \"one is booted — that is the normal case, and IOS_SIMULATOR_ID pins it when it is not. \" +\n \"Unlike simctl's own `booted`, this never picks arbitrarily between two.\",\n },\n};\n\nconst args = createArgs(SIMULATOR_NAMING);\n\nexport const { bundleIdArg, confirmArg, detailArg, screenshotArg, settleArg, xArg, yArg } = args;\n\n/**\n * The schema field is `device` in every tool here, shared and local alike.\n *\n * It looks odd next to a server called \"simulator\" and it is still the right\n * name: `simctl`'s own interface calls a simulator a device — `simctl list\n * devices`, and `<device>` in the usage line of every subcommand — so this is\n * Apple's word for the thing, and the `ios_simulator_` prefix already says\n * which kind. Mixing `simulator` here with the shared tools' `device` would be\n * worse than either alone.\n */\nexport const deviceArg = args.targetArg;\n","import type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { IosError } from \"#/client/errors\";\nimport { summarizeApps } from \"#/client/shape\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { bundleIdArg, deviceArg, wrap } from \"#/tools/util\";\n\nexport const registerListAppsTool = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_list_apps\",\n {\n title: \"iOS Simulator: List Apps\",\n description:\n \"List the apps installed on a simulator, with the bundle id every other tool takes. \" +\n \"Defaults to your own apps: a stock simulator carries about thirty, and twenty-five of \" +\n \"them are Apple's. Each entry also carries `dataContainer` — an ordinary path on this \" +\n \"Mac, so an app's database or logs can be read directly with no copy step.\",\n inputSchema: z.object({\n device: deviceArg,\n include_all: z\n .boolean()\n .default(false)\n .describe(\"Include Apple's built-in apps as well. Off by default; the list is long.\"),\n }),\n annotations: { readOnlyHint: true },\n },\n async ({ device, include_all }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const apps = await client.simctl.listApps(target.id);\n return { apps: summarizeApps(apps, { includeAll: include_all }) };\n }),\n );\n};\n\nexport const registerAppTools = (\n server: McpServer,\n client: SimulatorClient,\n launchArgs: string[],\n outputDir: string,\n): void => {\n server.registerTool(\n \"ios_simulator_install\",\n {\n title: \"iOS Simulator: Install\",\n description:\n \"Install a build. The path must be a `.app` **bundle directory** built for the simulator \" +\n '— not an `.ipa`, and not a device build, both of which fail with a bare \"No such file or ' +\n 'directory\" that reads like a typo. Installing over an existing copy replaces it and ' +\n \"keeps its data.\",\n inputSchema: z.object({\n device: deviceArg,\n path: z\n .string()\n .describe(\n \"Absolute path to the .app bundle, e.g. \" +\n \"~/Library/Developer/Xcode/DerivedData/…/Build/Products/Debug-iphonesimulator/Foo.app\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n },\n async ({ device, path }) =>\n wrap(async () => {\n if (!path.startsWith(\"/\")) {\n throw new IosError(`path must be absolute, got \"${path}\".`, {\n remedy: \"Pass the full path to the .app bundle.\",\n });\n }\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.install(target.id, path);\n return { installed: path, udid: target.id };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_launch\",\n {\n title: \"iOS Simulator: Launch\",\n description:\n \"Launch an installed app. Pass `arguments` to put it into a fixture or demo mode — that \" +\n \"is what IOS_SIMULATOR_LAUNCH_ARGS sets as the default for every launch that does not \" +\n \"override it. Standard output and error are captured to files under the output directory, \" +\n \"so an app that dies on launch leaves something readable behind.\",\n inputSchema: z.object({\n device: deviceArg,\n bundle_id: bundleIdArg,\n arguments: z\n .array(z.string())\n .optional()\n .describe(\n 'Launch arguments, e.g. [\"-DemoMode\", \"YES\"]. Overrides IOS_SIMULATOR_LAUNCH_ARGS ' +\n \"rather than adding to it.\",\n ),\n terminate_first: z\n .boolean()\n .default(true)\n .describe(\n \"Replace a running copy rather than attaching to it. On by default so a launch means \" +\n \"a fresh process and a predictable first screen.\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, bundle_id, arguments: args, terminate_first }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const effective = args ?? launchArgs;\n const stdout = `${outputDir}/${bundle_id}.out.log`;\n const stderr = `${outputDir}/${bundle_id}.err.log`;\n const output = await client.simctl.launch(target.id, bundle_id, {\n args: effective,\n terminateExisting: terminate_first,\n stdout,\n stderr,\n });\n const pid = /:\\s*(\\d+)\\s*$/.exec(output.trim())?.[1];\n return {\n launched: bundle_id,\n ...(pid ? { pid: Number(pid) } : {}),\n ...(effective.length > 0 ? { arguments: effective } : {}),\n ...(args === undefined && launchArgs.length > 0\n ? { argumentsFrom: \"IOS_SIMULATOR_LAUNCH_ARGS\" }\n : {}),\n logs: { stdout, stderr },\n };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_terminate\",\n {\n title: \"iOS Simulator: Terminate\",\n description:\n \"Kill a running app. The app is not uninstalled and its data is untouched — this is how \" +\n \"you get back to a cold start without erasing anything.\",\n inputSchema: z.object({ device: deviceArg, bundle_id: bundleIdArg }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n },\n async ({ device, bundle_id }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.terminate(target.id, bundle_id);\n return { terminated: bundle_id };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_open_url\",\n {\n title: \"iOS Simulator: Open URL\",\n description:\n \"Open a URL on the simulator, which is how you exercise a deep link or a universal link \" +\n \"without finding a way to tap one. An `https://` link opens in Safari unless the app \" +\n \"claims it; a custom scheme goes straight to whichever app registered it.\",\n inputSchema: z.object({\n device: deviceArg,\n url: z.url().describe('e.g. \"myapp://garden/42\" or \"https://example.com/garden/42\".'),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, url }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.openUrl(target.id, url);\n return { opened: url };\n }),\n );\n};\n","import type { DisplayInfo } from \"@mgcrea/mcp-ios-core\";\nimport type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { startRunnerRemedy } from \"#/client/errors\";\nimport { toListRow, type SimulatorSummary } from \"#/client/shape\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport type { ToolContext } from \"#/tools/index\";\nimport { deviceArg, wrap } from \"#/tools/util\";\n\nexport type Diagnosis = {\n ok: boolean;\n writes: \"enabled\" | \"disabled\";\n /**\n * The booted ones only, plus the target. A typical machine carries thirty-odd\n * simulators and listing them all here spent more of the context window than\n * the whole rest of the report — and none of them is what a diagnosis is\n * about. `counts` carries the shape of the rest; ios_simulator_list is there\n * for the full picture.\n */\n simulators: Record<string, unknown>[];\n counts: { total: number; booted: number; unavailable: number };\n target?: { udid: string; name: string; state: string; runtime: string };\n display?: DisplayInfo;\n wda: {\n url: string;\n port: number;\n reachable: boolean;\n /** Which simulator actually owns the port — the failure unique to this server. */\n boundTo?: string;\n state?: unknown;\n error?: string;\n };\n problems: string[];\n nextSteps: string[];\n};\n\n/**\n * Which simulator owns the WebDriverAgent port.\n *\n * This check has no device-server equivalent and it is the most valuable one\n * here. A simulator shares the host's network stack, so a runner's port is a\n * *host* port, and WebDriverAgent scans 8100-8199 when `USE_PORT` is unset. Two\n * booted simulators therefore give you a second runner answering healthily on\n * 8101 while a server pointed at 8100 drives the first one and reports success\n * on every call.\n *\n * Every process inside a simulator carries `SIMULATOR_UDID` in its environment,\n * and `ps -Eww` prints it, so the port's real owner is knowable rather than\n * assumed.\n */\nconst portOwner = async (client: SimulatorClient, port: number): Promise<string | undefined> => {\n try {\n const { stdout: pids } = await client.exec(\n \"/usr/sbin/lsof\",\n [\"-nP\", `-iTCP:${port}`, \"-sTCP:LISTEN\", \"-t\"],\n 5000,\n );\n const pid = pids.trim().split(\"\\n\")[0];\n if (!pid) return undefined;\n const { stdout: env } = await client.exec(\"/bin/ps\", [\"-Eww\", \"-p\", pid], 5000);\n return /SIMULATOR_UDID=([0-9A-Fa-f-]{36})/.exec(env)?.[1];\n } catch {\n // lsof is not always installed and needs no permissions we can assume.\n // Not knowing the owner is a missing field, not a failure.\n return undefined;\n }\n};\n\n/**\n * The whole point of this tool is that **it never throws**. Every other tool\n * fails when nothing is booted, the runtime is missing or the runner is not up,\n * and each of those looks much like the others from the outside. This one\n * collects all of it as data and names which half is missing.\n */\nexport const diagnose = async (\n client: SimulatorClient,\n ctx: ToolContext,\n deviceHint?: string,\n): Promise<Diagnosis> => {\n const problems: string[] = [];\n const nextSteps: string[] = [];\n const writes = ctx.allowWrites ? \"enabled\" : \"disabled\";\n\n let simulators: SimulatorSummary[] = [];\n try {\n simulators = await client.listSimulators({ fresh: true });\n } catch (err) {\n problems.push(`Could not list simulators: ${err instanceof Error ? err.message : String(err)}`);\n nextSteps.push(\n \"Install Xcode and its command line tools, then run `xcrun simctl list devices`.\",\n );\n return {\n ok: false,\n writes,\n simulators: [],\n counts: { total: 0, booted: 0, unavailable: 0 },\n wda: { url: client.wdaUrl(), port: client.wdaPort, reachable: false },\n problems,\n nextSteps,\n };\n }\n\n const booted = simulators.filter((sim) => sim.state === \"Booted\");\n const unavailable = simulators.filter((sim) => !sim.available);\n const counts = {\n total: simulators.length,\n booted: booted.length,\n unavailable: unavailable.length,\n };\n\n if (simulators.length === 0) {\n problems.push(\"simctl knows about no simulators at all.\");\n nextSteps.push(\"Install a runtime: Xcode > Settings > Components.\");\n } else if (booted.length === 0) {\n problems.push(\"No simulator is booted.\");\n nextSteps.push(\n 'Boot one with ios_simulator_power {\"state\":\"booted\"} — this server never boots implicitly.',\n );\n }\n const nothingBooted = simulators.length > 0 && booted.length === 0;\n\n let target: SimulatorSummary | undefined;\n try {\n target = await client.resolveTarget(deviceHint);\n } catch (err) {\n if (booted.length > 0) {\n problems.push(err instanceof Error ? err.message : String(err));\n const remedy = (err as { remedy?: string }).remedy;\n if (remedy) nextSteps.push(remedy);\n }\n }\n\n let display: DisplayInfo | undefined;\n if (target) {\n try {\n display = await client.display(target);\n } catch {\n // Geometry comes from the device type's profile.plist, so failing here\n // means the profile could not be read — worth a missing field, not a\n // failed report.\n }\n }\n\n // WebDriverAgent. `reachable` and usable coincide on a simulator: there is no\n // Enable UI Automation toggle, so the device server's separate `authorized`\n // probe would be pure latency. What replaces it is `boundTo`.\n const url = client.wdaUrl();\n const wda: Diagnosis[\"wda\"] = { url, port: client.wdaPort, reachable: false };\n try {\n wda.state = await client.wda(target ?? ({ id: \"\", name: \"\" } as SimulatorSummary)).status();\n wda.reachable = true;\n } catch (err) {\n wda.error = err instanceof Error ? err.message : String(err);\n problems.push(\"WebDriverAgent is not answering, so the UI tree and the input tools will fail.\");\n // Ordered, not merely listed. The runner tool calls `requireBooted`, so on a\n // machine with nothing booted the two steps above and below are one\n // sequence and running them the other way round cannot work — which the\n // previous pair of independent bullets did not say anywhere.\n nextSteps.push(\n (nothingBooted ? \"Then, once it is booted: \" : \"\") +\n startRunnerRemedy(ctx.allowWrites) +\n \" Everything else — including screenshots — works without it.\",\n );\n }\n\n if (wda.reachable) {\n const owner = await portOwner(client, client.wdaPort);\n if (owner) wda.boundTo = owner;\n if (owner && target && owner.toLowerCase() !== target.id.toLowerCase()) {\n const other = simulators.find((sim) => sim.id.toLowerCase() === owner.toLowerCase());\n problems.push(\n `Port ${client.wdaPort} is owned by ${other?.name ?? owner}, not ${target.name}. ` +\n \"Every tap and every UI tree read would go to the wrong screen.\",\n );\n nextSteps.push(\n \"WebDriverAgent scans 8100-8199 when USE_PORT is unset, so a second simulator's runner \" +\n \"takes the next free port and nothing looks wrong. Give this one its own port with \" +\n \"IOS_SIMULATOR_WDA_PORT and start its runner with a matching USE_PORT.\",\n );\n }\n }\n\n if (unavailable.length > 0) {\n nextSteps.push(\n `${unavailable.length} simulators have no installed runtime and cannot be used at all. ` +\n \"`xcrun simctl delete unavailable` clears them.\",\n );\n }\n\n return {\n ok: problems.length === 0,\n writes,\n simulators: [...booted, ...(target && target.state !== \"Booted\" ? [target] : [])].map(\n toListRow,\n ),\n counts,\n ...(target\n ? {\n target: {\n udid: target.id,\n name: target.name,\n state: target.state,\n runtime: target.runtime,\n },\n }\n : {}),\n ...(display ? { display } : {}),\n wda,\n problems,\n nextSteps,\n };\n};\n\nexport const registerDiagnosticsTools = (\n server: McpServer,\n client: SimulatorClient,\n ctx: ToolContext,\n): void => {\n server.registerTool(\n \"ios_simulator_diagnostics\",\n {\n title: \"iOS Simulator: Diagnostics\",\n description:\n \"Report both lanes separately and never fail: which simulators exist and which are \" +\n \"usable, which one this server would drive, its screen geometry, and whether \" +\n \"WebDriverAgent is answering — including *which* simulator owns its port, which is the \" +\n \"one way to catch a second runner quietly taking your taps. Start here when anything \" +\n \"else misbehaves.\",\n inputSchema: z.object({ device: deviceArg }),\n annotations: { readOnlyHint: true },\n },\n async ({ device }) => wrap(async () => diagnose(client, ctx, device)),\n );\n\n server.registerResource(\n \"diagnostics\",\n \"ios-simulator://diagnostics\",\n {\n title: \"iOS Simulator diagnostics\",\n description: \"The same report as ios_simulator_diagnostics, as a resource.\",\n mimeType: \"application/json\",\n },\n async (uri) => ({\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(await diagnose(client, ctx)),\n },\n ],\n }),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { IosError } from \"#/client/errors\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { bundleIdArg, deviceArg, wrap } from \"#/tools/util\";\n\n/** simctl's own list, verbatim — a value it does not accept is not worth guessing at. */\nconst PRIVACY_SERVICES = [\n \"all\",\n \"calendar\",\n \"contacts-limited\",\n \"contacts\",\n \"location\",\n \"location-always\",\n \"photos-add\",\n \"photos\",\n \"media-library\",\n \"microphone\",\n \"motion\",\n \"reminders\",\n \"siri\",\n] as const;\n\n/** APNs refuses anything larger, and it is worth saying so before the round trip. */\nconst MAX_PUSH_BYTES = 4096;\n\nexport const registerEnvironmentTools = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_set_environment\",\n {\n title: \"iOS Simulator: Set Environment\",\n description:\n \"Stage the simulator before a screenshot or a test: dark mode, Dynamic Type size, \" +\n \"increased contrast, a frozen status bar, a simulated location, and app permissions. \" +\n \"None of this is possible on a physical device, and it is most of the reason to prefer a \" +\n \"simulator for UI work. Every field is optional; those given are applied in order and the \" +\n \"result reports the state afterwards. A status bar override survives a reboot, so clear \" +\n \"it when you are done or it quietly pollutes later screenshots.\",\n inputSchema: z.object({\n device: deviceArg,\n appearance: z.enum([\"light\", \"dark\"]).optional().describe(\"Interface style.\"),\n content_size: z\n .enum([\n \"extra-small\",\n \"small\",\n \"medium\",\n \"large\",\n \"extra-large\",\n \"extra-extra-large\",\n \"extra-extra-extra-large\",\n \"accessibility-medium\",\n \"accessibility-large\",\n \"accessibility-extra-large\",\n \"accessibility-extra-extra-large\",\n \"accessibility-extra-extra-extra-large\",\n ])\n .optional()\n .describe(\"Dynamic Type size. The accessibility- sizes are where layouts break.\"),\n increase_contrast: z.boolean().optional().describe(\"Increase Contrast accessibility mode.\"),\n status_bar: z\n .object({\n time: z\n .string()\n .optional()\n .describe('Fixed clock, e.g. \"9:41\" — what App Store screenshots use.'),\n wifi_bars: z.number().int().min(0).max(3).optional(),\n cellular_bars: z.number().int().min(0).max(4).optional(),\n operator_name: z.string().optional().describe('Carrier name; \"\" hides it.'),\n battery_level: z.number().int().min(0).max(100).optional(),\n battery_state: z.enum([\"charging\", \"charged\", \"discharging\"]).optional(),\n })\n .optional()\n .describe(\"Freeze the status bar. Overrides persist until cleared.\"),\n clear_status_bar: z\n .boolean()\n .default(false)\n .describe(\"Drop every status bar override, restoring the real one.\"),\n location: z\n .object({ latitude: z.number(), longitude: z.number() })\n .optional()\n .describe(\"Teleport the device, for location-gated screens.\"),\n clear_location: z.boolean().default(false).describe(\"Stop simulating a location.\"),\n permission: z\n .object({\n action: z\n .enum([\"grant\", \"revoke\", \"reset\"])\n .describe(\n \"`grant` and `revoke` answer the prompt without showing it; `reset` makes it \" +\n \"appear again on next use.\",\n ),\n service: z.enum(PRIVACY_SERVICES),\n bundle_id: bundleIdArg\n .optional()\n .describe(\"Required for grant and revoke; optional for reset.\"),\n })\n .optional()\n .describe(\n \"Set an app permission directly. This is how you reach the denied branch of a \" +\n \"permission check, which is otherwise a prompt nobody can tap.\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n },\n async ({\n device,\n appearance,\n content_size,\n increase_contrast,\n status_bar,\n clear_status_bar,\n location,\n clear_location,\n permission,\n }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const applied: string[] = [];\n\n if (appearance) {\n await client.simctl.uiSet(target.id, \"appearance\", appearance);\n applied.push(`appearance=${appearance}`);\n }\n if (content_size) {\n await client.simctl.uiSet(target.id, \"content_size\", content_size);\n applied.push(`content_size=${content_size}`);\n }\n if (increase_contrast !== undefined) {\n await client.simctl.uiSet(\n target.id,\n \"increase_contrast\",\n increase_contrast ? \"enabled\" : \"disabled\",\n );\n applied.push(`increase_contrast=${increase_contrast}`);\n }\n if (clear_status_bar) {\n await client.simctl.statusBarClear(target.id);\n applied.push(\"status_bar cleared\");\n }\n if (status_bar) {\n const flags = [\n ...(status_bar.time ? [\"--time\", status_bar.time] : []),\n ...(status_bar.wifi_bars !== undefined\n ? [\"--wifiMode\", \"active\", \"--wifiBars\", String(status_bar.wifi_bars)]\n : []),\n ...(status_bar.cellular_bars !== undefined\n ? [\"--cellularMode\", \"active\", \"--cellularBars\", String(status_bar.cellular_bars)]\n : []),\n ...(status_bar.operator_name !== undefined\n ? [\"--operatorName\", status_bar.operator_name]\n : []),\n ...(status_bar.battery_level !== undefined\n ? [\"--batteryLevel\", String(status_bar.battery_level)]\n : []),\n ...(status_bar.battery_state ? [\"--batteryState\", status_bar.battery_state] : []),\n ];\n if (flags.length === 0) {\n throw new IosError(\"`status_bar` was given with no fields to override.\", {\n remedy: \"Set at least one field, or use `clear_status_bar` to drop the overrides.\",\n });\n }\n await client.simctl.statusBarOverride(target.id, flags);\n applied.push(\"status_bar overridden\");\n }\n if (clear_location) {\n await client.simctl.locationClear(target.id);\n applied.push(\"location cleared\");\n }\n if (location) {\n await client.simctl.locationSet(target.id, location.latitude, location.longitude);\n applied.push(`location=${location.latitude},${location.longitude}`);\n }\n if (permission) {\n if (permission.action !== \"reset\" && !permission.bundle_id) {\n throw new IosError(`\\`${permission.action}\\` needs a bundle_id.`, {\n remedy: \"Pass the app's bundle id, or use `reset`, which does not need one.\",\n });\n }\n await client.simctl.privacy(\n target.id,\n permission.action,\n permission.service,\n permission.bundle_id,\n );\n applied.push(`permission ${permission.action} ${permission.service}`);\n }\n\n if (applied.length === 0) {\n throw new IosError(\"Nothing to do — no field was given.\", {\n remedy:\n \"Set at least one of appearance, content_size, status_bar, location, permission.\",\n });\n }\n\n // Read back rather than echo the request: `simctl ui` answers `unknown`\n // or `unsupported` on a runtime too old for a setting, and reporting the\n // value we asked for would hide that.\n return {\n applied,\n state: {\n appearance: await client.simctl.uiGet(target.id, \"appearance\"),\n contentSize: await client.simctl.uiGet(target.id, \"content_size\"),\n statusBar: await client.simctl.statusBarList(target.id),\n },\n };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_add_media\",\n {\n title: \"iOS Simulator: Add Media\",\n description:\n \"Put photos or videos into the simulator's photo library. A simulator has no camera, and \" +\n \"this is how you get around that: seed the library first, then drive the app's picker as \" +\n \"normal. Without it, every flow that starts with an image is untestable here. Files are \" +\n \"copied in, so the originals are untouched and the simulator keeps them until it is \" +\n \"erased. Pair it with `permission` on ios_simulator_set_environment to skip the access \" +\n \"prompt entirely.\",\n inputSchema: z.object({\n device: deviceArg,\n paths: z\n .array(z.string())\n .min(1)\n .describe(\n \"Absolute paths to the images or videos, e.g. \" +\n '[\"/Users/me/fixtures/monstera.jpg\"]. Formats are the ones Photos itself accepts — ' +\n \"JPEG, PNG, HEIC, MOV, MP4.\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, paths }) =>\n wrap(async () => {\n // Checked here rather than left to simctl, which answers a relative path\n // with a bare \"No such file or directory\" that reads like a typo in the\n // filename rather than a statement about the working directory.\n for (const path of paths) {\n if (!path.startsWith(\"/\")) {\n throw new IosError(`Every path must be absolute, got \"${path}\".`, {\n remedy: \"Pass the full path to each file.\",\n });\n }\n }\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.addMedia(target.id, paths);\n return { added: paths, udid: target.id };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_push\",\n {\n title: \"iOS Simulator: Push\",\n description:\n \"Deliver a push notification, with no APNs certificate and no server. The payload is a \" +\n \"normal remote-notification body and must contain an `aps` key. Only remote pushes are \" +\n \"simulated — not VoIP, complications or file-provider. The app must be installed; it does \" +\n \"not have to be running.\",\n inputSchema: z.object({\n device: deviceArg,\n bundle_id: bundleIdArg,\n payload: z\n .record(z.string(), z.unknown())\n .describe(\n 'The APNs payload, e.g. {\"aps\":{\"alert\":{\"title\":\"Watering due\",\"body\":\"Monstera\"},' +\n '\"sound\":\"default\",\"badge\":1}}. Max 4096 bytes.',\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, bundle_id, payload }) =>\n wrap(async () => {\n if (!(\"aps\" in payload)) {\n throw new IosError(\"A push payload must have a top-level `aps` key.\", {\n remedy: 'Wrap the notification, e.g. {\"aps\":{\"alert\":\"Hello\"}}.',\n });\n }\n const bytes = Buffer.byteLength(JSON.stringify(payload));\n if (bytes > MAX_PUSH_BYTES) {\n throw new IosError(`The payload is ${bytes} bytes; APNs allows ${MAX_PUSH_BYTES}.`, {\n remedy: \"Shorten it — this is refused here rather than by simctl, but the cap is real.\",\n });\n }\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.push(target.id, bundle_id, payload);\n return { pushed: bundle_id, bytes };\n }),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { toListRow } from \"#/client/shape\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { confirmArg, deviceArg, wrap } from \"#/tools/util\";\n\n/** Booting is slow and visible; this is how long `power` waits to see it land. */\nconst BOOT_POLL_MS = 500;\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport const registerListTool = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_list\",\n {\n title: \"iOS Simulator: List\",\n description:\n \"List the simulators on this machine, with the UDID every other tool takes. Shows the \" +\n \"usable ones: a typical machine carries thirty-odd simulators and a good fraction of them \" +\n \"are orphans whose runtime is no longer installed, which cannot be booted, driven or \" +\n \"screenshotted at all. The result still says how many were left out and how to delete \" +\n \"them, and `include_unavailable` brings them back with `available: false` and the reason, \" +\n 'so \"why can\\'t I use that one\" is still answerable here.',\n inputSchema: z.object({\n booted_only: z\n .boolean()\n .default(false)\n .describe(\"Only simulators that are currently running.\"),\n include_unavailable: z\n .boolean()\n .default(false)\n .describe(\n \"Also list simulators whose runtime is not installed. Off by default because they \" +\n \"are not usable for anything; turn it on to see why a specific one is missing.\",\n ),\n name_contains: z\n .string()\n .optional()\n .describe('Case-insensitive substring filter on the name, e.g. \"iPhone 17\".'),\n }),\n annotations: { readOnlyHint: true },\n },\n async ({ booted_only, include_unavailable, name_contains }) =>\n wrap(async () => {\n const all = await client.listSimulators({ fresh: true });\n const needle = name_contains?.toLowerCase();\n const matched = all.filter(\n (sim) =>\n (include_unavailable || sim.available) &&\n (!booted_only || sim.state === \"Booted\") &&\n (needle === undefined || sim.name.toLowerCase().includes(needle)),\n );\n const unavailable = all.filter((sim) => !sim.available).length;\n return {\n simulators: matched.map(toListRow),\n booted: all.filter((sim) => sim.state === \"Booted\").length,\n total: all.length,\n ...(unavailable > 0\n ? {\n unavailable,\n note:\n `${unavailable} simulators have no installed runtime and cannot be used` +\n (include_unavailable ? \"\" : \", and are not listed above\") +\n \". `xcrun simctl delete unavailable` clears them, or pass \" +\n \"`include_unavailable: true` to see them here.\",\n }\n : {}),\n };\n }),\n );\n};\n\nexport const registerLifecycleTools = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_power\",\n {\n title: \"iOS Simulator: Power\",\n description:\n \"Boot or shut down a simulator. Booting is the one thing this server will not do for you \" +\n \"implicitly — a cold boot takes tens of seconds and puts a window on the user's screen, \" +\n \"and it changes which simulator an unqualified call resolves to afterwards. Both \" +\n \"directions are idempotent: booting a booted simulator succeeds.\",\n inputSchema: z.object({\n device: deviceArg,\n state: z\n .enum([\"booted\", \"shutdown\"])\n .describe(\"The state to put it in. `booted` also waits for it to actually get there.\"),\n open_window: z\n .boolean()\n .default(true)\n .describe(\n \"Bring up the Simulator app so the screen is visible. A `boot` on its own is headless, \" +\n \"which is fine for an agent and confusing for a person watching.\",\n ),\n wait_ms: z\n .number()\n .int()\n .min(0)\n .max(300_000)\n .default(60_000)\n .describe(\"How long to wait for a boot to complete before reporting what it saw.\"),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n },\n async ({ device, state, open_window, wait_ms }) =>\n wrap(async () => {\n const target = await client.resolveTarget(device);\n if (state === \"shutdown\") {\n if (target.state !== \"Booted\") {\n return { udid: target.id, name: target.name, state: \"Shutdown\", alreadyThere: true };\n }\n await client.simctl.shutdown(target.id);\n return { udid: target.id, name: target.name, state: \"Shutdown\" };\n }\n\n const already = target.state === \"Booted\";\n if (!already) await client.simctl.boot(target.id);\n if (open_window) await client.simctl.openApp(client.openPath);\n\n // Poll rather than trust the exit code: `simctl boot` returns as soon as\n // CoreSimulator has accepted the request, well before the device is\n // usable, and every tool after this one would fail on a half-booted one.\n const deadline = Date.now() + wait_ms;\n let observed = target.state;\n while (Date.now() < deadline) {\n const now = (await client.listSimulators({ fresh: true })).find(\n (sim) => sim.id === target.id,\n );\n observed = now?.state ?? observed;\n if (observed === \"Booted\") break;\n await sleep(BOOT_POLL_MS);\n }\n return {\n udid: target.id,\n name: target.name,\n state: observed,\n ...(already ? { alreadyThere: true } : {}),\n ...(observed === \"Booted\"\n ? {}\n : { warning: `Still ${observed} after ${wait_ms}ms. Raise wait_ms, or check Xcode.` }),\n };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_erase\",\n {\n title: \"iOS Simulator: Erase\",\n description:\n \"Wipe a simulator back to a factory state: installed apps, their data, the keychain and \" +\n \"every granted permission. The only irreversible tool here, and the reason it is the only \" +\n \"one behind `confirm`. Use it to test a genuine first launch. A booted simulator is shut \" +\n \"down first and booted again afterwards, because simctl refuses to erase a running one.\",\n inputSchema: z.object({\n device: deviceArg,\n confirm: confirmArg,\n }),\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false },\n },\n async ({ device }) =>\n wrap(async () => {\n const target = await client.resolveTarget(device);\n const wasBooted = target.state === \"Booted\";\n if (wasBooted) await client.simctl.shutdown(target.id);\n await client.simctl.erase(target.id);\n if (wasBooted) await client.simctl.boot(target.id);\n return {\n udid: target.id,\n name: target.name,\n erased: true,\n rebooted: wasBooted,\n };\n }),\n );\n};\n","import { spawn } from \"node:child_process\";\nimport { mkdir, open } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { IosError, START_RUNNER_SCRIPT_REMEDY } from \"#/client/errors\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { deviceArg, wrap } from \"#/tools/util\";\n\n/** Injected in tests so no xcodebuild is ever started by the suite. */\nexport type SpawnRunner = (\n command: string,\n args: string[],\n opts: { env: Record<string, string>; logPath: string },\n) => Promise<number | undefined>;\n\nconst defaultSpawnRunner: SpawnRunner = async (command, args, { env, logPath }) => {\n await mkdir(dirname(logPath), { recursive: true });\n const log = await open(logPath, \"a\");\n const child = spawn(command, args, {\n // Detached, because the XCTest session *is* the HTTP server: it has to\n // outlive this MCP server, or the runner dies with the conversation.\n detached: true,\n stdio: [\"ignore\", log.fd, log.fd],\n env: { ...process.env, ...env },\n });\n child.unref();\n await log.close();\n return child.pid;\n};\n\nconst scriptPath = (): string => {\n // Beside the built module in dist/, or beside src/ in a checkout.\n const here = dirname(fileURLToPath(import.meta.url));\n for (const candidate of [\n join(here, \"..\", \"scripts\", \"wda.sh\"),\n join(here, \"..\", \"..\", \"scripts\", \"wda.sh\"),\n ]) {\n return candidate;\n }\n throw new IosError(\"Could not find `scripts/wda.sh` beside this module.\");\n};\n\nexport const registerRunnerTools = (\n server: McpServer,\n client: SimulatorClient,\n outputDir: string,\n spawnRunner: SpawnRunner = defaultSpawnRunner,\n): void => {\n server.registerTool(\n \"ios_simulator_restart_wda\",\n {\n title: \"iOS Simulator: Restart WebDriverAgent\",\n description:\n \"Start the WebDriverAgent runner, or restart it after it has died. Only the UI tree and \" +\n \"the input tools need it — screenshots and everything simctl does keep working without \" +\n \"it. The runner is started detached so it outlives this conversation, and its output goes \" +\n \"to a log file rather than to you. It does not wait: give it fifteen seconds or so, then \" +\n \"check ios_simulator_diagnostics.\",\n inputSchema: z.object({\n device: deviceArg,\n port: z\n .number()\n .int()\n .min(1)\n .max(65535)\n .optional()\n .describe(\n \"Port to bind, passed to the runner as USE_PORT. Defaults to IOS_SIMULATOR_WDA_PORT. \" +\n \"Two simulators need two ports: they share this Mac's loopback, and a runner with \" +\n \"no USE_PORT scans 8100-8199 and silently takes the next free one.\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, port }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const usePort = port ?? client.wdaPort;\n const logPath = join(outputDir, \"wda.log\");\n const pid = await spawnRunner(\"/bin/bash\", [scriptPath(), \"run\"], {\n env: {\n IOS_SIMULATOR_ID: target.id,\n IOS_SIMULATOR_WDA_PORT: String(usePort),\n USE_PORT: String(usePort),\n },\n logPath,\n });\n if (pid === undefined) {\n throw new IosError(\"Could not start the runner — the process had no pid.\", {\n remedy: START_RUNNER_SCRIPT_REMEDY,\n });\n }\n return {\n started: true,\n pid,\n port: usePort,\n udid: target.id,\n logPath,\n note: \"Give it ~15s, then check ios_simulator_diagnostics for `wda.reachable`.\",\n };\n }),\n );\n};\n","import { registerInputTools, registerScreenTools } from \"@mgcrea/mcp-ios-core\";\nimport type { McpServer } from \"@modelcontextprotocol/server\";\n\nimport { EMPTY_SCREENSHOT_REMEDY } from \"#/client/errors\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport type { Config } from \"#/config\";\nimport { registerAppTools, registerListAppsTool } from \"#/tools/apps\";\nimport { registerDiagnosticsTools } from \"#/tools/diagnostics\";\nimport { registerEnvironmentTools } from \"#/tools/environment\";\nimport { registerLifecycleTools, registerListTool } from \"#/tools/lifecycle\";\nimport { registerRunnerTools, type SpawnRunner } from \"#/tools/runner\";\nimport { SIMULATOR_NAMING } from \"#/tools/util\";\n\nexport type ToolContext = {\n config: Config;\n /** On by default here — see IOS_SIMULATOR_ALLOW_WRITES. */\n allowWrites: boolean;\n};\n\n/**\n * All capability decisions in one place, so \"why can't I call that\" is answered\n * by one file.\n *\n * The split is observe versus drive, exactly as in the device server. What\n * differs is the **default**: writes are on. The convention that keeps the rest\n * of the fleet read-only exists because a mutating tool acts on someone's real\n * account or someone's real phone, and a simulator is neither — it holds no\n * person's data and `ios_simulator_erase` puts it back to factory in seconds.\n * Turning writes off with IOS_SIMULATOR_ALLOW_WRITES=0 restores the device\n * server's posture, and then the driving tools are *absent* rather than\n * refused, because a refusal still lets a model try, retry and reason about a\n * way around it.\n *\n * The observe half is unusually capable here: `screenshot` goes through simctl,\n * so seeing the screen needs no WebDriverAgent and no setup of any kind. Only\n * `ui_tree` does, and that is a runtime fact reported by\n * ios_simulator_diagnostics rather than a registration decision — the runner\n * can start and stop while this server is connected.\n */\nexport const registerTools = (\n server: McpServer,\n client: SimulatorClient,\n ctx: ToolContext,\n spawnRunner?: SpawnRunner,\n): void => {\n const shared = {\n host: client,\n naming: SIMULATOR_NAMING,\n maxTreeBytes: ctx.config.maxTreeBytes,\n capHint: \"raise IOS_SIMULATOR_MAX_TREE_BYTES\",\n // `home` is the gesture and works; a simulator has no volume rocker worth\n // pressing, and offering one that does nothing is worse than offering none.\n buttons: [\"home\"] as [string, ...string[]],\n emptyScreenshotRemedy: EMPTY_SCREENSHOT_REMEDY,\n // An alert is usually a permission prompt, and a simulator is the one place\n // a permission prompt never had to appear. The moment one is on screen is\n // the moment that is worth saying so.\n alertHint:\n \"If this is a permission prompt, it can be answered before it ever appears: \" +\n 'ios_simulator_set_environment {\"permission\":{\"action\":\"grant\",\"service\":\"photos\",' +\n '\"bundle_id\":\"…\"}}. `reset` puts the prompt back for testing the other branch.',\n // Geometry here is a local plist read reported on every screenshot, not a\n // slow round trip worth its own tool as it is on a device.\n includeDisplayInfo: false,\n };\n\n registerDiagnosticsTools(server, client, ctx);\n registerListTool(server, client);\n registerListAppsTool(server, client);\n registerScreenTools(server, shared);\n\n if (!ctx.allowWrites) return;\n\n registerInputTools(server, shared);\n registerLifecycleTools(server, client);\n registerAppTools(server, client, ctx.config.launchArgs, ctx.config.outputDir);\n registerEnvironmentTools(server, client);\n registerRunnerTools(server, client, ctx.config.outputDir, spawnRunner);\n};\n","import type { ExecImpl, Logger } from \"@mgcrea/mcp-ios-core\";\nimport { McpServer } from \"@modelcontextprotocol/server\";\n\nimport { BUILD_INFO } from \"#/build-info\";\nimport { SimulatorClient } from \"#/client/simulator\";\nimport type { Config } from \"#/config\";\nimport { registerTools } from \"#/tools/index\";\nimport type { SpawnRunner } from \"#/tools/runner\";\n\nexport const SERVER_NAME = BUILD_INFO.name;\nexport const SERVER_VERSION = BUILD_INFO.version;\n\nexport type CreateServerOptions = {\n config: Config;\n /** Override the process boundary (tests): `xcrun`, `plutil` and `sips` never run. */\n exec?: ExecImpl;\n /** Override HTTP to WebDriverAgent (tests). */\n fetch?: typeof fetch;\n /** Override starting the runner (tests): no process is spawned. */\n spawnRunner?: SpawnRunner;\n logger?: Logger;\n};\n\nexport type CreatedServer = {\n server: McpServer;\n client: SimulatorClient;\n};\n\n/**\n * A pure factory. The injectable seams — `exec`, `fetch` and `spawnRunner` —\n * are the whole reason the test suite can drive real tools through the real SDK\n * with no simulator, no Xcode, no WebDriverAgent and no process left running.\n * Nothing below `config.ts` reads `process.env`.\n */\nexport const createServer = (opts: CreateServerOptions): CreatedServer => {\n const { config } = opts;\n const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });\n\n const client = new SimulatorClient({\n xcrunPath: config.xcrunPath,\n sipsPath: config.sipsPath,\n plutilPath: config.plutilPath,\n openPath: config.openPath,\n execTimeoutMs: config.execTimeoutMs,\n wdaTimeoutMs: config.wdaTimeoutMs,\n wdaPort: config.wdaPort,\n allowWrites: config.allowWrites,\n ...(config.wdaUrl ? { wdaUrl: config.wdaUrl } : {}),\n ...(config.simulatorId ? { defaultSimulatorId: config.simulatorId } : {}),\n ...(opts.exec ? { exec: opts.exec } : {}),\n ...(opts.fetch ? { fetch: opts.fetch } : {}),\n ...(opts.logger ? { logger: opts.logger } : {}),\n });\n\n registerTools(server, client, { config, allowWrites: config.allowWrites }, opts.spawnRunner);\n\n return { server, client };\n};\n","import { readFileSync } from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { z } from \"zod\";\n\n/**\n * There are no credentials here, and unlike the device server there is barely\n * any setup either. Two lanes reach a simulator and both are local:\n *\n * * `xcrun simctl` — Apple's own tool. It covers app lifecycle, the device's\n * whole staged environment, and the screen itself: `simctl io screenshot`\n * needs no runner at all.\n * * WebDriverAgent — an HTTP server inside the simulator, on the *host's*\n * loopback since a simulator shares the host network stack. Only the\n * accessibility tree and synthetic touches go through it.\n */\nconst ConfigSchema = z\n .object({\n /**\n * UDID, name, or the literal `booted`. Left unset, a single booted\n * simulator is used and two or more is an error that names them, rather\n * than the coin flip simctl's own `booted` performs.\n */\n simulatorId: z.string().min(1).optional(),\n wdaUrl: z.url().optional(),\n /**\n * Must match the runner's `USE_PORT`. One port per simulator: they all bind\n * the same host loopback, so two runners on 8100 is a silent wrong-target\n * bug rather than a collision.\n */\n wdaPort: z.number().int().min(1).max(65535).default(8100),\n wdaTimeoutMs: z.number().int().min(1000).max(600_000).default(30_000),\n /** A cold boot or a large `install` genuinely takes tens of seconds. */\n execTimeoutMs: z.number().int().min(1000).max(1_800_000).default(120_000),\n /**\n * **On by default**, which is the one place this server deliberately breaks\n * the fleet convention. What makes the device server read-only is that a\n * phone belongs to a real person; a simulator is disposable and holds\n * nobody's data. `ios_simulator_erase` still requires an explicit `confirm`\n * because it is the only irreversible tool here.\n */\n allowWrites: z.boolean().default(true),\n /** Applied by `launch` when the call passes no `arguments` of its own. */\n launchArgs: z.array(z.string()).default([]),\n /** Saved screenshots, launch logs and the runner log. */\n outputDir: z.string().min(1).default(join(tmpdir(), \"mcp-ios-simulator\")),\n maxTreeBytes: z.number().int().min(1000).max(500_000).default(24_000),\n xcrunPath: z.string().min(1).default(\"/usr/bin/xcrun\"),\n sipsPath: z.string().min(1).default(\"/usr/bin/sips\"),\n /** The `listapps` plist lane, and the device-type profile read. */\n plutilPath: z.string().min(1).default(\"/usr/bin/plutil\"),\n /** `simctl boot` is headless; this is what attaches a window. */\n openPath: z.string().min(1).default(\"/usr/bin/open\"),\n })\n .strict()\n .superRefine((_cfg, _ctx) => {\n // Deliberately NOT an error when there is no simulator and no Xcode. An MCP\n // server that exits on startup shows up in the client as a bare\n // \"MCP error -32000: Connection closed\" with stderr swallowed, so the one\n // message that would have explained the problem never reaches anyone.\n // ios_simulator_diagnostics reports all of it as data instead.\n });\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\nconst trimmed = (value: string | undefined): string | undefined => {\n const t = value?.trim();\n return t ? t : undefined;\n};\n\nconst parseBool = (value: string | undefined): boolean | undefined => {\n const t = trimmed(value);\n if (t === undefined) return undefined;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(t.toLowerCase());\n};\n\nconst parseIntOpt = (value: string | undefined): number | undefined => {\n const t = trimmed(value);\n if (t === undefined) return undefined;\n const n = Number(t);\n return Number.isInteger(n) ? n : undefined;\n};\n\n/** Whitespace-separated, so `IOS_SIMULATOR_LAUNCH_ARGS=\"-DemoMode -NoCloud\"` works. */\nconst parseArgs = (value: string | undefined): string[] | undefined => {\n const t = trimmed(value);\n return t === undefined ? undefined : t.split(/\\s+/);\n};\n\nexport const defaultConfigPath = (env: NodeJS.ProcessEnv = process.env): string =>\n trimmed(env.IOS_SIMULATOR_CONFIG) ??\n join(homedir(), \".config\", \"ios-simulator-mcp\", \"config.json\");\n\n/**\n * `.strict()` on purpose: a typo'd `wdaPort` spelled `wda_port` must be an\n * error. Silently ignoring an unknown key looks exactly like \"that setting had\n * no effect\", which is the worst possible way to learn where your config came\n * from.\n */\nconst readConfigFile = (path: string): Partial<Config> => {\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return {};\n }\n return ConfigSchema.partial().strict().parse(JSON.parse(raw)) as Partial<Config>;\n};\n\n/**\n * Environment first, config file second, **per field** — not whole-source.\n * A one-off `IOS_SIMULATOR_ALLOW_WRITES=0` has to beat a file that says `true`,\n * while a file that sets `simulatorId` keeps working when the environment says\n * nothing about it. Merging field by field is the only rule that gives both.\n */\nexport const loadConfig = (\n env: NodeJS.ProcessEnv = process.env,\n configPath: string = defaultConfigPath(env),\n): Config => {\n const file = readConfigFile(configPath);\n const pick = <K extends keyof Config>(\n fromEnv: Config[K] | undefined,\n key: K,\n ): Config[K] | undefined => fromEnv ?? file[key];\n\n return ConfigSchema.parse({\n simulatorId: pick(trimmed(env.IOS_SIMULATOR_ID), \"simulatorId\"),\n wdaUrl: pick(trimmed(env.IOS_SIMULATOR_WDA_URL), \"wdaUrl\"),\n wdaPort: pick(parseIntOpt(env.IOS_SIMULATOR_WDA_PORT), \"wdaPort\"),\n wdaTimeoutMs: pick(parseIntOpt(env.IOS_SIMULATOR_WDA_TIMEOUT_MS), \"wdaTimeoutMs\"),\n execTimeoutMs: pick(parseIntOpt(env.IOS_SIMULATOR_TIMEOUT_MS), \"execTimeoutMs\"),\n allowWrites: pick(parseBool(env.IOS_SIMULATOR_ALLOW_WRITES), \"allowWrites\"),\n launchArgs: pick(parseArgs(env.IOS_SIMULATOR_LAUNCH_ARGS), \"launchArgs\"),\n outputDir: pick(trimmed(env.IOS_SIMULATOR_OUTPUT_DIR), \"outputDir\"),\n maxTreeBytes: pick(parseIntOpt(env.IOS_SIMULATOR_MAX_TREE_BYTES), \"maxTreeBytes\"),\n xcrunPath: pick(trimmed(env.IOS_SIMULATOR_XCRUN_PATH), \"xcrunPath\"),\n sipsPath: pick(trimmed(env.IOS_SIMULATOR_SIPS_PATH), \"sipsPath\"),\n plutilPath: pick(trimmed(env.IOS_SIMULATOR_PLUTIL_PATH), \"plutilPath\"),\n openPath: pick(trimmed(env.IOS_SIMULATOR_OPEN_PATH), \"openPath\"),\n });\n};\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -118,6 +118,15 @@ declare class Simctl {
|
|
|
118
118
|
openApp(openPath: string): Promise<void>;
|
|
119
119
|
listApps(udid: string): Promise<Record<string, RawSimApp>>;
|
|
120
120
|
install(udid: string, appPath: string): Promise<void>;
|
|
121
|
+
/**
|
|
122
|
+
* Put photos and videos in the simulator's library.
|
|
123
|
+
*
|
|
124
|
+
* `addmedia` is variadic and takes the paths as plain argv, so unlike `push`
|
|
125
|
+
* this needs no temp file. It is the one route around the simulator's missing
|
|
126
|
+
* camera: an app that reads from the photo library can be exercised end to
|
|
127
|
+
* end, while one that insists on a live capture still cannot.
|
|
128
|
+
*/
|
|
129
|
+
addMedia(udid: string, paths: string[]): Promise<void>;
|
|
121
130
|
/**
|
|
122
131
|
* Launch, optionally replacing a running copy.
|
|
123
132
|
*
|
|
@@ -234,6 +243,8 @@ type SimulatorClientOptions = {
|
|
|
234
243
|
wdaTimeoutMs: number;
|
|
235
244
|
wdaPort: number;
|
|
236
245
|
wdaUrl?: string | undefined;
|
|
246
|
+
/** Only so the WebDriverAgent remedies can name a tool this server registers. */
|
|
247
|
+
allowWrites?: boolean | undefined;
|
|
237
248
|
defaultSimulatorId?: string | undefined;
|
|
238
249
|
exec?: ExecImpl | undefined;
|
|
239
250
|
fetch?: typeof fetch | undefined;
|
|
@@ -419,13 +430,30 @@ declare const toDisplayInfo: (profile: ScreenProfile, capture?: {
|
|
|
419
430
|
declare const TIMEOUT_REMEDY: string;
|
|
420
431
|
declare const TOOLCHAIN_REMEDY: string;
|
|
421
432
|
/**
|
|
422
|
-
* How to get the runner up
|
|
423
|
-
*
|
|
424
|
-
*
|
|
433
|
+
* How to get the runner up, from a shell.
|
|
434
|
+
*
|
|
435
|
+
* Much shorter than the device server's equivalent, and that is the headline
|
|
436
|
+
* difference between the two: a simulator runner needs no Apple team, no
|
|
437
|
+
* signing, no trust prompt and no tunnel.
|
|
438
|
+
*
|
|
439
|
+
* This is the *fallback*. It stays because it is the only route when writes are
|
|
440
|
+
* off, and because it is what a person setting the server up for the first time
|
|
441
|
+
* needs — but a remedy that sends a caller holding ios_simulator_restart_wda out
|
|
442
|
+
* to a terminal is a remedy that wastes the tool it already has.
|
|
443
|
+
*/
|
|
444
|
+
declare const START_RUNNER_SCRIPT_REMEDY: string;
|
|
445
|
+
/** The same thing, done with this server's own tool. */
|
|
446
|
+
declare const START_RUNNER_TOOL_REMEDY: string;
|
|
447
|
+
/**
|
|
448
|
+
* How to get the runner up, in whichever way the caller can actually reach.
|
|
449
|
+
*
|
|
450
|
+
* `ios_simulator_restart_wda` is registered only when writes are on, so naming
|
|
451
|
+
* it unconditionally would be the same mistake this fixes, pointed the other
|
|
452
|
+
* way: a remedy naming a tool that is not in the list.
|
|
425
453
|
*/
|
|
426
|
-
declare const
|
|
454
|
+
declare const startRunnerRemedy: (allowWrites: boolean) => string;
|
|
427
455
|
/** What to do when WebDriverAgent does not answer at all. */
|
|
428
|
-
declare const
|
|
456
|
+
declare const wdaUnavailableRemedy: (allowWrites: boolean) => string;
|
|
429
457
|
/** No simulator matched, or several did and none was named. */
|
|
430
458
|
declare class SimulatorNotFoundError extends IosError$1 {
|
|
431
459
|
readonly name = "SimulatorNotFoundError";
|
|
@@ -471,5 +499,5 @@ type BuildInfo = {
|
|
|
471
499
|
};
|
|
472
500
|
declare const BUILD_INFO: BuildInfo;
|
|
473
501
|
//#endregion
|
|
474
|
-
export { BUILD_INFO, CommandError, CommandTimeoutError, type Config, type CreateServerOptions, type CreatedServer, EMPTY_SCREENSHOT_REMEDY, IosError, type RawDeviceType, type RawRuntime, type RawSim, type RawSimApp, RuntimeUnavailableError, SERVER_NAME, SERVER_VERSION,
|
|
502
|
+
export { BUILD_INFO, CommandError, CommandTimeoutError, type Config, type CreateServerOptions, type CreatedServer, EMPTY_SCREENSHOT_REMEDY, IosError, type RawDeviceType, type RawRuntime, type RawSim, type RawSimApp, RuntimeUnavailableError, SERVER_NAME, SERVER_VERSION, START_RUNNER_SCRIPT_REMEDY, START_RUNNER_TOOL_REMEDY, type SimAppSummary, Simctl, SimulatorClient, SimulatorNotBootedError, SimulatorNotFoundError, type SimulatorSummary, TIMEOUT_REMEDY, TOOLCHAIN_REMEDY, ToolchainError, WdaError, WdaUnavailableError, WritesDisabledError, assertNotBulkTarget, createServer, defaultConfigPath, loadConfig, parseSimctlError, pngDimensions, readScreenProfile, startRunnerRemedy, summarizeApps, summarizeDevices, toDisplayInfo, toListRow, wdaUnavailableRemedy };
|
|
475
503
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/client/simctl.ts","../src/client/shape.ts","../src/client/simulator.ts","../src/config.ts","../src/tools/runner.ts","../src/server.ts","../src/client/display.ts","../src/client/errors.ts","../src/build-info.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;KA+BY;EACV;EACA;EACA;EACA,OAAO;EACP,SAAS;;KAGC;EACV;EACA;EACA;EACA;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;;KAGU;EACV;EACA;EACA;EACA;;;KAIU;EACV;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;;;;;cAqBW,mBAAgB;EAExB;EAAiB;EAAe;;;;;;;;;;;cAoDxB,sBAAmB;cAUnB;mBACM;mBACA;;UAET;EAER,YAAY,MAAM;;UAOJ;;;;;UAcA;;;;;;;;;;;UAqBA;EAwBR,eAAe,QAAQ,eAAe;EAKtC,gBAAgB,QAAQ;EAIxB,mBAAmB,QAAQ;EAS3B,KAAK,eAAe;EAKpB,SAAS,eAAe;EAKxB,MAAM,eAAe;;EAMrB,QAAQ,mBAAmB;EAM3B,SAAS,eAAe,QAAQ,eAAe;EAK/C,QAAQ,cAAc,kBAAkB;;;;;;;;;EAcxC,OACJ,cACA,kBACA;IAAQ;IAAiB;IAA6B;IAAiB;MACtE;EAUG,UAAU,cAAc,mBAAmB;EAK3C,QAAQ,cAAc,cAAc;;;;;;;;;;;;;EAmBpC,cAAc,eAAe;;;;;;;;EAqB7B,MAAM,cAAc,iBAAiB;EAKrC,MAAM,cAAc,gBAAgB,gBAAgB;EAKpD,kBAAkB,cAAc,kBAAkB;EAKlD,eAAe,eAAe;EAK9B,cAAc,eAAe;EAK7B,YAAY,cAAc,kBAAkB,oBAAoB;EAKhE,cAAc,eAAe;;EAM7B,QAAQ,cAAc,gBAAgB,iBAAiB,oBAAoB;;;;;EAS3E,KAAK,cAAc,kBAAkB,mBAAmB;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/client/simctl.ts","../src/client/shape.ts","../src/client/simulator.ts","../src/config.ts","../src/tools/runner.ts","../src/server.ts","../src/client/display.ts","../src/client/errors.ts","../src/build-info.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { A as WritesDisabledError, C as
|
|
2
|
-
export { BUILD_INFO, CommandError, CommandTimeoutError, EMPTY_SCREENSHOT_REMEDY, IosError, RuntimeUnavailableError, SERVER_NAME, SERVER_VERSION,
|
|
1
|
+
import { A as WritesDisabledError, C as SimulatorNotBootedError, D as ToolchainError, E as TOOLCHAIN_REMEDY, M as wdaUnavailableRemedy, N as BUILD_INFO, O as WdaError, S as START_RUNNER_TOOL_REMEDY, T as TIMEOUT_REMEDY, _ as CommandTimeoutError, a as createServer, b as RuntimeUnavailableError, c as assertNotBulkTarget, d as summarizeDevices, f as toListRow, g as CommandError, h as toDisplayInfo, i as SERVER_VERSION, j as startRunnerRemedy, k as WdaUnavailableError, l as parseSimctlError, m as readScreenProfile, n as loadConfig, o as SimulatorClient, p as pngDimensions, r as SERVER_NAME, s as Simctl, t as defaultConfigPath, u as summarizeApps, v as EMPTY_SCREENSHOT_REMEDY, w as SimulatorNotFoundError, x as START_RUNNER_SCRIPT_REMEDY, y as IosError } from "./config-HSDMfemT.js";
|
|
2
|
+
export { BUILD_INFO, CommandError, CommandTimeoutError, EMPTY_SCREENSHOT_REMEDY, IosError, RuntimeUnavailableError, SERVER_NAME, SERVER_VERSION, START_RUNNER_SCRIPT_REMEDY, START_RUNNER_TOOL_REMEDY, Simctl, SimulatorClient, SimulatorNotBootedError, SimulatorNotFoundError, TIMEOUT_REMEDY, TOOLCHAIN_REMEDY, ToolchainError, WdaError, WdaUnavailableError, WritesDisabledError, assertNotBulkTarget, createServer, defaultConfigPath, loadConfig, parseSimctlError, pngDimensions, readScreenProfile, startRunnerRemedy, summarizeApps, summarizeDevices, toDisplayInfo, toListRow, wdaUnavailableRemedy };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mgcrea/mcp-ios-simulator",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Model Context Protocol server for driving iOS Simulators: screenshots, UI tree, taps, app lifecycle and device staging",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ios",
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"bin": {
|
|
21
21
|
"ios-simulator-mcp": "dist/cli.js",
|
|
22
|
-
"ios-simulator-wda": "scripts/wda.sh"
|
|
22
|
+
"ios-simulator-wda": "scripts/wda.sh",
|
|
23
|
+
"mcp-ios-simulator": "dist/cli.js"
|
|
23
24
|
},
|
|
24
25
|
"files": [
|
|
25
26
|
"dist",
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
"prepublishOnly": "pnpm build"
|
|
60
61
|
},
|
|
61
62
|
"dependencies": {
|
|
62
|
-
"@mgcrea/mcp-ios-core": "^0.
|
|
63
|
+
"@mgcrea/mcp-ios-core": "^0.2.0",
|
|
63
64
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
64
65
|
"zod": "^4.4.3"
|
|
65
66
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"config-C1TH-p-c.js","names":["IosError","IosError","IosError","IosError","IosError"],"sources":["../src/build-info.ts","../src/client/errors.ts","../src/client/display.ts","../src/client/shape.ts","../src/client/simctl.ts","../src/client/simulator.ts","../src/tools/util.ts","../src/tools/apps.ts","../src/tools/diagnostics.ts","../src/tools/environment.ts","../src/tools/lifecycle.ts","../src/tools/runner.ts","../src/tools/index.ts","../src/server.ts","../src/config.ts"],"sourcesContent":["// Build-time / runtime identity for the running server. `name`/`version` are\n// read from package.json at startup (always accurate); `gitCommit` /\n// `gitCommitDate` are injected by tsdown's `define` substitution at build time\n// and fall back to \"unknown\" when running from source (e.g. vitest).\n\nimport { readFileSync } from \"node:fs\";\n\n// oxlint-disable no-underscore-dangle -- bundler-injected build-time constants.\ndeclare const __GIT_COMMIT__: string;\ndeclare const __GIT_COMMIT_DATE__: string;\n\ntype PackageJson = { name: string; version: string };\n\nconst readPackageJson = (): PackageJson => {\n try {\n const pkgUrl = new URL(\"../package.json\", import.meta.url);\n return JSON.parse(readFileSync(pkgUrl, \"utf8\")) as PackageJson;\n } catch {\n return { name: \"@mgcrea/mcp-ios-device\", version: \"0.0.0\" };\n }\n};\n\nconst pkg = readPackageJson();\n\nexport type BuildInfo = {\n name: string;\n version: string;\n gitCommit: string;\n gitCommitDate: string;\n};\n\nexport const BUILD_INFO: BuildInfo = {\n name: pkg.name,\n version: pkg.version,\n gitCommit: typeof __GIT_COMMIT__ === \"string\" ? __GIT_COMMIT__ : \"unknown\",\n gitCommitDate: typeof __GIT_COMMIT_DATE__ === \"string\" ? __GIT_COMMIT_DATE__ : \"unknown\",\n};\n","import { IosError } from \"@mgcrea/mcp-ios-core\";\n\nexport {\n CommandError,\n CommandTimeoutError,\n IosError,\n ToolchainError,\n WdaError,\n WdaUnavailableError,\n} from \"@mgcrea/mcp-ios-core\";\n\n/** Raising the budget, or noticing the simulator died mid-call. */\nexport const TIMEOUT_REMEDY =\n \"Raise IOS_SIMULATOR_TIMEOUT_MS if this is a slow operation (a boot or a large `install` \" +\n \"genuinely takes tens of seconds), or check the simulator is still booted.\";\n\nexport const TOOLCHAIN_REMEDY =\n \"Install Xcode and run `xcode-select --install`, or point IOS_SIMULATOR_XCRUN_PATH at a \" +\n \"different xcrun.\";\n\n/**\n * How to get the runner up. Much shorter than the device server's equivalent,\n * and that is the headline difference between the two: a simulator runner needs\n * no Apple team, no signing, no trust prompt and no tunnel.\n */\nexport const START_RUNNER_REMEDY =\n \"Build and start the runner with `scripts/wda.sh setup` then `scripts/wda.sh run` (or \" +\n \"`npx -p @mgcrea/mcp-ios-simulator ios-simulator-wda run` from an npm install) and leave it \" +\n \"open — the HTTP server is the XCTest process, so it stops when that command does. It needs no \" +\n \"Apple Developer team: a simulator build is not signed.\";\n\n/** What to do when WebDriverAgent does not answer at all. */\nexport const WDA_UNAVAILABLE_REMEDY =\n `${START_RUNNER_REMEDY} Everything except ios_simulator_ui_tree and the input tools works ` +\n \"without it — screenshots go through simctl. If something else already forwards the port, set \" +\n \"IOS_SIMULATOR_WDA_URL.\";\n\n/** No simulator matched, or several did and none was named. */\nexport class SimulatorNotFoundError extends IosError {\n override readonly name = \"SimulatorNotFoundError\";\n\n constructor(message: string, remedy: string) {\n super(message, { remedy });\n }\n}\n\n/**\n * The simulator exists but is shut down.\n *\n * Its own error class because the fix is a single specific call and nothing\n * else: almost every simctl subcommand fails this way, `listapps` included, so\n * \"it is shut down\" would otherwise be buried in a CoreSimulator envelope that\n * reads like a lookup bug.\n */\nexport class SimulatorNotBootedError extends IosError {\n override readonly name = \"SimulatorNotBootedError\";\n\n constructor(name: string, udid: string) {\n super(`${name} is shut down.`, {\n remedy:\n `Boot it with ios_simulator_power {\"simulator\":\"${udid}\",\"state\":\"booted\"} — this server ` +\n \"never boots a simulator implicitly, because a cold boot takes tens of seconds and puts a \" +\n \"window on the user's screen.\",\n });\n }\n}\n\n/**\n * The simulator's runtime is not installed, so CoreSimulator cannot service it.\n *\n * Worth its own class because the failure it prevents is not an error message\n * at all: `simctl io` against one of these aborts with an uncaught\n * NSInternalInconsistencyException and a thirty-line stack trace.\n */\nexport class RuntimeUnavailableError extends IosError {\n override readonly name = \"RuntimeUnavailableError\";\n\n constructor(name: string, reason: string | undefined) {\n super(`${name} is unavailable${reason ? `: ${reason}` : \".\"}`, {\n remedy:\n \"The iOS runtime this simulator was created against is not installed. Install it in \" +\n \"Xcode > Settings > Components, or clear the orphans with `xcrun simctl delete unavailable`.\",\n });\n }\n}\n\n/** Thrown when a write path is reached while IOS_SIMULATOR_ALLOW_WRITES is off. */\nexport class WritesDisabledError extends IosError {\n override readonly name = \"WritesDisabledError\";\n\n constructor(what: string) {\n super(\n `${what} changes the simulator, but writes are disabled. ` +\n \"Unset IOS_SIMULATOR_ALLOW_WRITES (it defaults to on) to enable them again.\",\n );\n }\n}\n\n/** A shut-down simulator captures nothing, and that is the usual cause here. */\nexport const EMPTY_SCREENSHOT_REMEDY =\n \"The simulator produced an empty capture. Check it is actually booted with \" +\n \"ios_simulator_diagnostics, and that its runtime is installed.\";\n","import { join } from \"node:path\";\n\nimport type { DisplayInfo, ExecImpl } from \"@mgcrea/mcp-ios-core\";\n\nimport { IosError } from \"#/client/errors\";\nimport type { RawDeviceType } from \"#/client/simctl\";\n\n/**\n * Screen geometry for a simulator, which has no `devicectl device info displays`.\n *\n * Three sources were measured, and the split between them is the interesting\n * part:\n *\n * - **`profile.plist`, per device type.** `mainScreenWidth`, `mainScreenHeight`\n * and `mainScreenScale`, agreeing exactly with both `io enumerate` and the\n * captured PNG. A pure file read: no simctl call, nothing to boot, and no\n * crash surface on an unavailable simulator. This is where `pointScale`\n * comes from.\n * - **The captured PNG's own header.** This is where *pixels* come from\n * whenever there is a capture, because `simctl io … screenConfig geometry`\n * can resize a booted simulator's screen at runtime and the profile would\n * then be silently wrong — putting every tap coordinate in the wrong space\n * with nothing looking wrong.\n * - **`io enumerate`**, which also reports it, but is a text parse of a\n * 150-line dump and aborts on an unavailable device. Not used.\n */\nexport const profilePlistPath = (bundlePath: string): string =>\n join(bundlePath, \"Contents\", \"Resources\", \"profile.plist\");\n\nexport type ScreenProfile = {\n mainScreenWidth?: number;\n mainScreenHeight?: number;\n mainScreenScale?: number;\n};\n\nexport const readScreenProfile = async (\n deviceType: RawDeviceType,\n opts: { plutilPath: string; exec: ExecImpl; timeoutMs: number },\n): Promise<ScreenProfile> => {\n if (!deviceType.bundlePath) return {};\n const { stdout } = await opts.exec(\n opts.plutilPath,\n [\"-convert\", \"json\", \"-o\", \"-\", profilePlistPath(deviceType.bundlePath)],\n opts.timeoutMs,\n );\n try {\n return JSON.parse(stdout) as ScreenProfile;\n } catch {\n return {};\n }\n};\n\n/**\n * Width and height straight out of a PNG's IHDR.\n *\n * Cheaper than shelling to `sips` for the one number that must not be stale,\n * and the bytes are already in hand: the signature is fixed, `IHDR` is always\n * the first chunk, and the two dimensions are big-endian at offsets 16 and 20.\n */\nexport const pngDimensions = (png: Buffer): { width: number; height: number } | undefined => {\n const SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n if (png.length < 24 || !png.subarray(0, 8).equals(SIGNATURE)) return undefined;\n if (png.subarray(12, 16).toString(\"ascii\") !== \"IHDR\") return undefined;\n return { width: png.readUInt32BE(16), height: png.readUInt32BE(20) };\n};\n\n/**\n * Assemble a `DisplayInfo`.\n *\n * `orientation` is derived by comparing the capture's own aspect against the\n * profile's portrait dimensions, and is `\"unknown\"` when there is no capture to\n * compare — never guessed as `\"portrait\"`, because the whole point of the field\n * is telling a caller whether the screen they are about to read is rotated.\n *\n * `backlightState` is left undefined: a simulator has no backlight, and\n * `io enumerate`'s `Power state` is about the framebuffer port rather than the\n * screen, so mapping it would be a lie.\n */\nexport const toDisplayInfo = (\n profile: ScreenProfile,\n capture?: { width: number; height: number },\n): DisplayInfo => {\n const scale = profile.mainScreenScale ?? 1;\n const pixelWidth = capture?.width ?? profile.mainScreenWidth;\n const pixelHeight = capture?.height ?? profile.mainScreenHeight;\n\n if (!pixelWidth || !pixelHeight) {\n throw new IosError(\"Could not determine the simulator's screen geometry.\", {\n remedy:\n \"The device type's profile.plist could not be read and no screenshot was available. \" +\n \"Run ios_simulator_diagnostics — an unavailable runtime is the usual cause.\",\n });\n }\n\n const landscape =\n profile.mainScreenWidth !== undefined && profile.mainScreenHeight !== undefined\n ? pixelWidth > pixelHeight && profile.mainScreenWidth < profile.mainScreenHeight\n : pixelWidth > pixelHeight;\n\n return {\n pixelWidth,\n pixelHeight,\n pointWidth: Math.round(pixelWidth / scale),\n pointHeight: Math.round(pixelHeight / scale),\n pointScale: scale,\n orientation: capture ? (landscape ? \"landscape\" : \"portrait\") : \"unknown\",\n };\n};\n","// The context-window layer.\n//\n// `simctl listapps` spends ~38 KB on the 31 apps a stock simulator carries, 25\n// of which are Apple's own, and most of every entry is group-container UUIDs no\n// caller acts on. `list devices` is smaller but still reports five keys per\n// simulator that only matter to CoreSimulator.\n//\n// So: lists return the few fields the other tools consume, and nothing else.\n\nimport type { RawDeviceType, RawRuntime, RawSim, RawSimApp } from \"#/client/simctl\";\n\nexport type SimulatorSummary = {\n /**\n * The UDID. Called `id` because that is the name the shared `ScreenHost`\n * contract uses — a physical device has two identifiers and a simulator has\n * one, so there is nothing here to disambiguate. Rendered back as `udid` by\n * `toListRow`, which is the word anyone reading the output will know it by.\n */\n id: string;\n name: string;\n /** `Booted` or `Shutdown`, verbatim from CoreSimulator. */\n state: string;\n runtime: string;\n deviceType: string | undefined;\n deviceTypeIdentifier: string | undefined;\n /**\n * False when the runtime is missing. Kept rather than filtered: \"why can't I\n * use that one\" has to be answerable from the list, and an unavailable\n * simulator is the one input that makes `simctl io` abort rather than fail.\n */\n available: boolean;\n unavailableReason?: string;\n lastBootedAt?: string;\n};\n\n/**\n * Flatten the runtime-keyed map `list devices` returns.\n *\n * The key is a runtime *identifier* and identifiers are not unique — a machine\n * carrying both iOS 26.4 and 26.4.1 has two entries under\n * `com.apple.CoreSimulator.SimRuntime.iOS-26-4`. So the key is never used as a\n * primary key: it is joined against `list runtimes` for a display name, 0..n\n * matches tolerated, and carried through verbatim besides.\n */\nexport const summarizeDevices = (\n devices: Record<string, RawSim[]>,\n runtimes: RawRuntime[],\n deviceTypes: RawDeviceType[],\n): SimulatorSummary[] => {\n const runtimeName = new Map(\n runtimes.map((r) => [r.identifier, r.name ?? r.version ?? r.identifier]),\n );\n const typeName = new Map(deviceTypes.map((t) => [t.identifier, t.name]));\n\n return Object.entries(devices).flatMap(([runtimeId, sims]) =>\n (sims ?? []).map((sim) => ({\n id: sim.udid,\n name: sim.name,\n state: sim.state,\n runtime: runtimeName.get(runtimeId) ?? runtimeId.split(\".\").pop() ?? runtimeId,\n deviceType: sim.deviceTypeIdentifier ? typeName.get(sim.deviceTypeIdentifier) : undefined,\n deviceTypeIdentifier: sim.deviceTypeIdentifier,\n available: sim.isAvailable !== false,\n ...(sim.availabilityError ? { unavailableReason: sim.availabilityError } : {}),\n ...(sim.lastBootedAt ? { lastBootedAt: sim.lastBootedAt } : {}),\n })),\n );\n};\n\nexport type SimAppSummary = {\n bundleId: string;\n name: string | undefined;\n version: string | undefined;\n build: string | undefined;\n type: string | undefined;\n /**\n * Host filesystem paths, and the reason there is no `pull_container` tool: a\n * simulator's containers are already on this machine, so reading an app's\n * SwiftData store is an ordinary file read rather than a copy off a device.\n */\n bundlePath: string | undefined;\n dataContainer: string | undefined;\n};\n\n/** `file:///Users/…/Foo.app/` → `/Users/…/Foo.app`, which is what a person can use. */\nconst toPath = (url: string | undefined): string | undefined => {\n if (!url) return undefined;\n if (!url.startsWith(\"file://\")) return url;\n try {\n return decodeURIComponent(new URL(url).pathname).replace(/\\/$/, \"\");\n } catch {\n return url;\n }\n};\n\nexport const summarizeApps = (\n apps: Record<string, RawSimApp>,\n opts: { includeAll?: boolean } = {},\n): SimAppSummary[] =>\n Object.values(apps)\n .filter((app) => opts.includeAll === true || app.ApplicationType === \"User\")\n .map((app) => ({\n bundleId: app.CFBundleIdentifier ?? \"\",\n name: app.CFBundleDisplayName ?? app.CFBundleName,\n version: app.CFBundleShortVersionString,\n build: app.CFBundleVersion,\n type: app.ApplicationType,\n bundlePath: toPath(app.Bundle ?? app.Path),\n dataContainer: toPath(app.DataContainer),\n }))\n .filter((app) => app.bundleId !== \"\")\n .toSorted((a, b) => a.bundleId.localeCompare(b.bundleId));\n\n/** The public shape of one row of `ios_simulator_list`. */\nexport const toListRow = (sim: SimulatorSummary): Record<string, unknown> => ({\n udid: sim.id,\n name: sim.name,\n state: sim.state,\n runtime: sim.runtime,\n ...(sim.deviceType ? { deviceType: sim.deviceType } : {}),\n ...(sim.available ? {} : { available: false }),\n ...(sim.unavailableReason ? { unavailableReason: sim.unavailableReason } : {}),\n});\n","import { mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport {\n assertNoShellMetachars,\n createExec,\n type ExecImpl,\n type Logger,\n} from \"@mgcrea/mcp-ios-core\";\n\nimport { IosError, TIMEOUT_REMEDY, TOOLCHAIN_REMEDY } from \"#/client/errors\";\n\n/**\n * `xcrun simctl` — Apple's own simulator tool, and the half of this server that\n * needs nothing installed and nothing signed.\n *\n * It covers strictly more here than `devicectl` does on a physical device: app\n * lifecycle, but also the screen itself (`io screenshot`), the appearance, the\n * status bar, permissions, location and push. Only the accessibility tree and\n * synthetic touches need WebDriverAgent.\n *\n * Three output shapes, and knowing which is which explains the whole class:\n *\n * - `list` takes `-j` and writes clean JSON to **stdout**. It is the only\n * subcommand that does; `listapps --json` does not exist.\n * - `listapps` writes an old-style NeXTSTEP plist. `plutil` converts it, but\n * `ExecImpl` has no stdin, so it goes through a temp file and two processes.\n * - everything else answers in prose, including `(null)` and `unknown`, which\n * are passed through rather than normalised — see the readers below.\n */\nexport type SimctlOptions = {\n xcrunPath: string;\n plutilPath: string;\n timeoutMs: number;\n exec?: ExecImpl | undefined;\n logger?: Logger | undefined;\n};\n\nexport type RawSim = {\n udid: string;\n name: string;\n state: string;\n isAvailable?: boolean;\n availabilityError?: string;\n deviceTypeIdentifier?: string;\n lastBootedAt?: string;\n};\n\nexport type RawRuntime = {\n identifier: string;\n name?: string;\n version?: string;\n isAvailable?: boolean;\n};\n\nexport type RawDeviceType = {\n identifier: string;\n name?: string;\n bundlePath?: string;\n modelIdentifier?: string;\n};\n\n/** One app as `listapps` reports it, once plutil has turned it into JSON. */\nexport type RawSimApp = {\n CFBundleIdentifier?: string;\n CFBundleDisplayName?: string;\n CFBundleName?: string;\n CFBundleShortVersionString?: string;\n CFBundleVersion?: string;\n ApplicationType?: string;\n Bundle?: string;\n Path?: string;\n DataContainer?: string;\n};\n\n/**\n * The two exit codes simctl actually uses, and what they mean.\n *\n * `148` is a device it could not even parse as an identifier. `149` is\n * everything else, wrapped in an envelope whose first line is machine-readable\n * and whose second line is the only part worth showing a person.\n */\nconst INVALID_DEVICE = 148;\n\n/**\n * Pull the human sentence out of a CoreSimulator error envelope.\n *\n * An error was encountered processing the command (domain=…, code=405):\n * Unable to lookup in current state: Shutdown\n *\n * Without this the message a caller sees is the first line, which names a\n * numeric code and says nothing about what to do.\n */\nexport const parseSimctlError = (\n stderr: string,\n): { domain?: string; code?: number; message: string } => {\n const envelope = /\\(domain=([^,]+),\\s*code=(\\d+)\\)\\s*:?\\s*\\n?([\\s\\S]*)/.exec(stderr);\n if (envelope) {\n return {\n domain: envelope[1]?.trim(),\n code: Number(envelope[2]),\n message: (envelope[3] ?? \"\").trim().split(\"\\n\")[0]?.trim() ?? stderr.trim(),\n };\n }\n return { message: stderr.trim().split(\"\\n\")[0]?.trim() ?? stderr.trim() };\n};\n\n/**\n * simctl reports its interesting failures in prose, and each has a different\n * fix. Every string here describes something only a simulator has.\n */\nconst remedyFor = (stderr: string): string | undefined => {\n const { message } = parseSimctlError(stderr);\n const text = `${stderr} ${message}`.toLowerCase();\n if (text.includes(\"invalid device\")) {\n return \"That is not a UDID or a name simctl knows. Run ios_simulator_list to see what exists.\";\n }\n if (text.includes(\"unable to lookup in current state: shutdown\")) {\n return 'The simulator is shut down. Boot it with ios_simulator_power {\"state\":\"booted\"}.';\n }\n if (text.includes(\"current state: booted\")) {\n return \"The simulator is already booted, which for most operations means there is nothing to do.\";\n }\n if (text.includes(\"no such file or directory\")) {\n return (\n \"Check the path exists and is a `.app` **bundle directory** built for the simulator — an \" +\n \"`.ipa` or a device build will not install, and reports exactly this.\"\n );\n }\n if (text.includes(\"failed to initialize io ports\")) {\n return (\n \"The simulator's runtime is not installed, so CoreSimulator cannot service it. Install the \" +\n \"runtime, or clear the orphans with `xcrun simctl delete unavailable`.\"\n );\n }\n return undefined;\n};\n\n/**\n * The one argument that must never reach simctl.\n *\n * `erase`, `delete` and `shutdown` all accept the literal string `all`, and\n * `simctl erase all` wipes every simulator on the machine. A device hint that\n * happens to be the word \"all\" is the single worst thing this server could\n * forward, so it is refused here as well as in the resolver — the resolver is\n * where it should be caught, and this is where it cannot be missed.\n */\nexport const assertNotBulkTarget = (udid: string): void => {\n if (udid === \"all\" || udid === \"unavailable\" || udid === \"booted\") {\n throw new IosError(`Refusing to pass \"${udid}\" to simctl as a device.`, {\n remedy:\n \"simctl reads these as *every* matching simulator, so `erase all` would wipe the machine. \" +\n \"Resolve to one concrete UDID with ios_simulator_list first.\",\n });\n }\n};\n\nexport class Simctl {\n private readonly opts: SimctlOptions;\n private readonly exec: ExecImpl;\n /** Device types change only when Xcode is updated; re-reading is pure cost. */\n private deviceTypes: RawDeviceType[] | undefined;\n\n constructor(opts: SimctlOptions) {\n this.opts = opts;\n this.exec =\n opts.exec ?? createExec({ timeout: TIMEOUT_REMEDY, toolchain: TOOLCHAIN_REMEDY, remedyFor });\n }\n\n /** Raw stdout, for the subcommands that answer in prose. */\n private async run(args: string[]): Promise<string> {\n this.opts.logger?.debug?.(\"simctl\", ...args);\n const { stdout } = await this.exec(\n this.opts.xcrunPath,\n [\"simctl\", ...args],\n this.opts.timeoutMs,\n );\n return stdout;\n }\n\n /**\n * `list` and only `list`. `-e` stops `/` being escaped as `\\/`, which matters\n * only for fixture readability but costs nothing.\n */\n private async runJson<T>(args: string[]): Promise<T> {\n const stdout = await this.run([...args, \"-j\", \"-e\"]);\n try {\n return JSON.parse(stdout) as T;\n } catch {\n throw new IosError(`simctl ${args.join(\" \")} did not return JSON.`, {\n details: stdout.slice(0, 400),\n });\n }\n }\n\n /**\n * A NeXTSTEP plist, via `plutil` and a temp file.\n *\n * The obvious `simctl listapps … | plutil -convert json -o - -` is not\n * expressible: `ExecImpl` takes argv and no stdin, deliberately, because that\n * is what keeps every caller value data rather than shell syntax. So simctl's\n * stdout is written out and handed to plutil by path. A fresh directory per\n * call rather than a shared one, so two concurrent tools cannot read each\n * other's answer with nothing looking wrong.\n */\n private async runPlist<T>(args: string[]): Promise<T> {\n const stdout = await this.run(args);\n const dir = await mkdtemp(join(tmpdir(), \"simctl-\"));\n const path = join(dir, \"out.plist\");\n try {\n await writeFile(path, stdout);\n const { stdout: json } = await this.exec(\n this.opts.plutilPath,\n [\"-convert\", \"json\", \"-o\", \"-\", path],\n this.opts.timeoutMs,\n );\n return JSON.parse(json) as T;\n } catch (err) {\n if (err instanceof IosError) throw err;\n throw new IosError(`Could not read simctl ${args[0]} output as a property list.`, {\n details: stdout.slice(0, 400),\n });\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n }\n\n // ------------------------------------------------------------------ list --\n\n async listDevices(): Promise<Record<string, RawSim[]>> {\n const result = await this.runJson<{ devices?: Record<string, RawSim[]> }>([\"list\", \"devices\"]);\n return result.devices ?? {};\n }\n\n async listRuntimes(): Promise<RawRuntime[]> {\n return (await this.runJson<{ runtimes?: RawRuntime[] }>([\"list\", \"runtimes\"])).runtimes ?? [];\n }\n\n async listDeviceTypes(): Promise<RawDeviceType[]> {\n this.deviceTypes ??=\n (await this.runJson<{ devicetypes?: RawDeviceType[] }>([\"list\", \"devicetypes\"]))\n .devicetypes ?? [];\n return this.deviceTypes;\n }\n\n // ------------------------------------------------------------- lifecycle --\n\n async boot(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"boot\", udid]);\n }\n\n async shutdown(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"shutdown\", udid]);\n }\n\n async erase(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"erase\", udid]);\n }\n\n /** Attach the Simulator UI to whatever is booted. A `boot` alone is headless. */\n async openApp(openPath: string): Promise<void> {\n await this.exec(openPath, [\"-a\", \"Simulator\"], this.opts.timeoutMs);\n }\n\n // ------------------------------------------------------------------ apps --\n\n async listApps(udid: string): Promise<Record<string, RawSimApp>> {\n assertNotBulkTarget(udid);\n return this.runPlist<Record<string, RawSimApp>>([\"listapps\", udid]);\n }\n\n async install(udid: string, appPath: string): Promise<void> {\n assertNotBulkTarget(udid);\n assertNoShellMetachars(\"path\", appPath);\n await this.run([\"install\", udid, appPath]);\n }\n\n /**\n * Launch, optionally replacing a running copy.\n *\n * Environment variables do not go on the command line: simctl reads them from\n * its **own** environment under a `SIMCTL_CHILD_` prefix. `ExecImpl` pins a\n * minimal environment on purpose, so a caller wanting them has to say so, and\n * this is where that would be threaded through.\n */\n async launch(\n udid: string,\n bundleId: string,\n opts: { args?: string[]; terminateExisting?: boolean; stdout?: string; stderr?: string } = {},\n ): Promise<string> {\n assertNotBulkTarget(udid);\n const flags = [\n ...(opts.terminateExisting ? [\"--terminate-running-process\"] : []),\n ...(opts.stdout ? [`--stdout=${opts.stdout}`] : []),\n ...(opts.stderr ? [`--stderr=${opts.stderr}`] : []),\n ];\n return this.run([\"launch\", ...flags, udid, bundleId, ...(opts.args ?? [])]);\n }\n\n async terminate(udid: string, bundleId: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"terminate\", udid, bundleId]);\n }\n\n async openUrl(udid: string, url: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"openurl\", udid, url]);\n }\n\n // ---------------------------------------------------------------- screen --\n\n /**\n * A PNG of the screen, base64, with no WebDriverAgent involved.\n *\n * The documented `-` for stdout **does not work**: measured on Xcode 26.6, it\n * writes a file literally named `-` into the current working directory and\n * leaves stdout empty, exit code 0. So this passes a real path, like the\n * device lane's `--json-output` does, and for the same reason uses a fresh\n * directory per call.\n *\n * A successful capture also writes a `Note: No display specified…` line to\n * stderr, so non-empty stderr is not a failure here.\n */\n async screenshotPng(udid: string): Promise<string> {\n assertNotBulkTarget(udid);\n const dir = await mkdtemp(join(tmpdir(), \"simshot-\"));\n const path = join(dir, \"shot.png\");\n try {\n await this.run([\"io\", udid, \"screenshot\", \"--type=png\", path]);\n return (await readFile(path)).toString(\"base64\");\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n }\n\n // ----------------------------------------------------------- environment --\n\n /**\n * Read a `simctl ui` option.\n *\n * `unknown` and `unsupported` are passed through rather than normalised: they\n * mean genuinely different things — the runtime is too old, versus it failed\n * to read — and collapsing them throws away a distinction a caller can act on.\n */\n async uiGet(udid: string, option: string): Promise<string> {\n assertNotBulkTarget(udid);\n return (await this.run([\"ui\", udid, option])).trim();\n }\n\n async uiSet(udid: string, option: string, value: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"ui\", udid, option, value]);\n }\n\n async statusBarOverride(udid: string, flags: string[]): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"status_bar\", udid, \"override\", ...flags]);\n }\n\n async statusBarClear(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"status_bar\", udid, \"clear\"]);\n }\n\n async statusBarList(udid: string): Promise<string> {\n assertNotBulkTarget(udid);\n return (await this.run([\"status_bar\", udid, \"list\"])).trim();\n }\n\n async locationSet(udid: string, latitude: number, longitude: number): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"location\", udid, \"set\", `${latitude},${longitude}`]);\n }\n\n async locationClear(udid: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"location\", udid, \"clear\"]);\n }\n\n /** `grant` and `revoke` need a bundle id; `reset` does not. */\n async privacy(udid: string, action: string, service: string, bundleId?: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"privacy\", udid, action, service, ...(bundleId ? [bundleId] : [])]);\n }\n\n /**\n * A simulated remote push. The payload goes through a temp file rather than\n * stdin, for the same argv-only reason as the plist lane.\n */\n async push(udid: string, bundleId: string, payload: unknown): Promise<void> {\n assertNotBulkTarget(udid);\n const dir = await mkdtemp(join(tmpdir(), \"simpush-\"));\n const path = join(dir, \"payload.json\");\n try {\n await writeFile(path, JSON.stringify(payload));\n await this.run([\"push\", udid, bundleId, path]);\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n }\n}\n\nexport { INVALID_DEVICE };\n","import {\n createExec,\n WdaClient,\n type DisplayInfo,\n type ExecImpl,\n type Logger,\n type ScreenHost,\n} from \"@mgcrea/mcp-ios-core\";\n\nimport { pngDimensions, readScreenProfile, toDisplayInfo } from \"#/client/display\";\nimport {\n RuntimeUnavailableError,\n SimulatorNotBootedError,\n SimulatorNotFoundError,\n START_RUNNER_REMEDY,\n TIMEOUT_REMEDY,\n TOOLCHAIN_REMEDY,\n WDA_UNAVAILABLE_REMEDY,\n} from \"#/client/errors\";\nimport { summarizeDevices, type SimulatorSummary } from \"#/client/shape\";\nimport { Simctl } from \"#/client/simctl\";\n\n/** What to say when WebDriverAgent fails, on a simulator. */\nconst SIMULATOR_WDA_REMEDIES = {\n unavailable: WDA_UNAVAILABLE_REMEDY,\n notAuthorized:\n \"WebDriverAgent is running but is refusing to drive the UI. On a simulator there is no \" +\n `Enable UI Automation toggle to blame, so this is nearly always a stale runner. ${START_RUNNER_REMEDY}`,\n noForegroundApp:\n \"WebDriverAgent can see no foreground application on a session it has just created. Launch \" +\n \"the app with ios_simulator_launch, or restart the runner with ios_simulator_restart_wda.\",\n noSuchElement:\n \"Call ios_simulator_ui_tree to see what is actually on screen — the element may not have \" +\n \"appeared yet.\",\n};\n\nexport type SimulatorClientOptions = {\n xcrunPath: string;\n sipsPath: string;\n plutilPath: string;\n openPath: string;\n execTimeoutMs: number;\n wdaTimeoutMs: number;\n wdaPort: number;\n wdaUrl?: string | undefined;\n defaultSimulatorId?: string | undefined;\n exec?: ExecImpl | undefined;\n fetch?: typeof fetch | undefined;\n logger?: Logger | undefined;\n};\n\nexport class SimulatorClient implements ScreenHost<SimulatorSummary> {\n readonly simctl: Simctl;\n readonly exec: ExecImpl;\n readonly sipsPath: string;\n readonly plutilPath: string;\n readonly openPath: string;\n readonly execTimeoutMs: number;\n private readonly opts: SimulatorClientOptions;\n private readonly wdaClients = new Map<string, WdaClient>();\n /** `simctl list` is ~80ms and most tools want it twice; 2s collapses that. */\n private cache: { at: number; simulators: SimulatorSummary[] } | undefined;\n private profiles = new Map<string, Awaited<ReturnType<typeof readScreenProfile>>>();\n\n constructor(opts: SimulatorClientOptions) {\n this.opts = opts;\n this.exec = opts.exec ?? createExec({ timeout: TIMEOUT_REMEDY, toolchain: TOOLCHAIN_REMEDY });\n this.sipsPath = opts.sipsPath;\n this.plutilPath = opts.plutilPath;\n this.openPath = opts.openPath;\n this.execTimeoutMs = opts.execTimeoutMs;\n this.simctl = new Simctl({\n xcrunPath: opts.xcrunPath,\n plutilPath: opts.plutilPath,\n timeoutMs: opts.execTimeoutMs,\n ...(opts.exec ? { exec: opts.exec } : {}),\n ...(opts.logger ? { logger: opts.logger } : {}),\n });\n }\n\n async listSimulators(opts: { fresh?: boolean } = {}): Promise<SimulatorSummary[]> {\n const cached = this.cache;\n if (!opts.fresh && cached && Date.now() - cached.at < 2000) return cached.simulators;\n const [devices, runtimes, deviceTypes] = await Promise.all([\n this.simctl.listDevices(),\n this.simctl.listRuntimes(),\n this.simctl.listDeviceTypes(),\n ]);\n const simulators = summarizeDevices(devices, runtimes, deviceTypes);\n this.cache = { at: Date.now(), simulators };\n return simulators;\n }\n\n /**\n * Resolve a hint to exactly one simulator, or refuse.\n *\n * The device server's rule — never guess between two — transfers, but\n * \"connected\" has to become the right predicate. A typical machine carries\n * dozens of simulators, most of them shut down and some of them unavailable,\n * so the population is mostly non-candidates.\n *\n * Three things happen here that cannot happen further down:\n *\n * - `all` and `unavailable` are refused outright. `simctl erase all` wipes\n * every simulator on the machine, and a hint is a string from a model.\n * - `booted` is resolved *here* rather than passed through. simctl's own\n * documentation says that with several booted it \"will choose one of\n * them\", and a coin flip that drives the wrong screen most of the time is\n * exactly what the refuse-to-guess rule exists to prevent.\n * - availability is checked before the target is returned, because `simctl\n * io` against an unavailable simulator aborts with an uncaught\n * NSInternalInconsistencyException rather than failing.\n *\n * State is deliberately *not* checked: `ios_simulator_list` and\n * `ios_simulator_diagnostics` are useful against a shut-down simulator, so\n * requiring booted is each tool's decision, via `requireBooted`.\n */\n async resolveTarget(hint?: string): Promise<SimulatorSummary> {\n const wanted = hint ?? this.opts.defaultSimulatorId;\n const simulators = await this.listSimulators();\n\n if (wanted === \"all\" || wanted === \"unavailable\") {\n throw new SimulatorNotFoundError(\n `\"${wanted}\" is not a simulator, it is how simctl says *every* simulator.`,\n \"Name one by UDID or by name. `simctl erase all` would wipe the machine, so this server \" +\n \"refuses to forward it.\",\n );\n }\n\n const usable = simulators.filter((sim) => sim.available);\n if (wanted !== undefined && wanted !== \"booted\") {\n const byUdid = simulators.filter((sim) => sim.id.toLowerCase() === wanted.toLowerCase());\n // A name is ambiguous far more often than a UDID: the same device name\n // exists once per runtime, and the orphans of an uninstalled runtime keep\n // theirs. Prefer the ones that can actually be used, so \"iPhone 17 Pro\"\n // resolves to the real one rather than erroring against a dead 26.3 twin.\n const named = simulators.filter((sim) => sim.name === wanted);\n const byName = named.some((sim) => sim.available)\n ? named.filter((sim) => sim.available)\n : named;\n const matches = byUdid.length > 0 ? byUdid : byName;\n if (matches.length === 0) {\n throw new SimulatorNotFoundError(\n `No simulator matches \"${wanted}\".`,\n simulators.length === 0\n ? \"simctl knows about no simulators at all. Install a runtime in Xcode > Settings > Components.\"\n : `Run ios_simulator_list to see them. ${usable.length} of ${simulators.length} are available.`,\n );\n }\n if (matches.length > 1) {\n throw new SimulatorNotFoundError(\n `${matches.length} simulators are named \"${wanted}\".`,\n `Pass a UDID instead: ${matches.map((sim) => `${sim.name} = ${sim.id}`).join(\", \")}.`,\n );\n }\n return this.assertAvailable(matches[0] as SimulatorSummary);\n }\n\n const booted = usable.filter((sim) => sim.state === \"Booted\");\n if (booted.length === 1) return this.assertAvailable(booted[0] as SimulatorSummary);\n if (booted.length === 0) {\n const candidates = usable\n .slice(0, 6)\n .map((sim) => `${sim.name} = ${sim.id}`)\n .join(\", \");\n throw new SimulatorNotFoundError(\n \"No simulator is booted.\",\n usable.length === 0\n ? \"No simulator on this machine has an installed runtime. Install one in Xcode > Settings > Components.\"\n : `Boot one with ios_simulator_power, e.g. ${candidates}.`,\n );\n }\n throw new SimulatorNotFoundError(\n `${booted.length} simulators are booted, so there is no obvious default.`,\n `Pass \\`simulator\\` (or set IOS_SIMULATOR_ID) to one of: ${booted\n .map((sim) => `${sim.name} = ${sim.id}`)\n .join(\", \")}.`,\n );\n }\n\n private assertAvailable(sim: SimulatorSummary): SimulatorSummary {\n if (!sim.available) throw new RuntimeUnavailableError(sim.name, sim.unavailableReason);\n return sim;\n }\n\n /** Every tool that needs a running simulator calls this; the resolver does not. */\n requireBooted(sim: SimulatorSummary): SimulatorSummary {\n if (sim.state !== \"Booted\") throw new SimulatorNotBootedError(sim.name, sim.id);\n return sim;\n }\n\n /**\n * Geometry. Cached per device *type* rather than per simulator, since that is\n * what the profile describes and it changes only when Xcode does.\n */\n async display(target: SimulatorSummary, capturePng?: string): Promise<DisplayInfo> {\n const id = target.deviceTypeIdentifier;\n let profile = id ? this.profiles.get(id) : undefined;\n if (!profile) {\n const deviceType = (await this.simctl.listDeviceTypes()).find((t) => t.identifier === id);\n profile = deviceType\n ? await readScreenProfile(deviceType, {\n plutilPath: this.plutilPath,\n exec: this.exec,\n timeoutMs: this.execTimeoutMs,\n })\n : {};\n if (id) this.profiles.set(id, profile);\n }\n const capture = capturePng ? pngDimensions(Buffer.from(capturePng, \"base64\")) : undefined;\n return toDisplayInfo(profile, capture);\n }\n\n /** The capture lane that needs no runner — the whole reason this server is cheap to start. */\n async screenshotPng(target: SimulatorSummary): Promise<string> {\n this.requireBooted(target);\n return this.simctl.screenshotPng(target.id);\n }\n\n /**\n * Where WebDriverAgent is.\n *\n * Unlike the device lane there is no tunnel address to derive: a simulator\n * shares the host's network stack, so the runner's port *is* a host loopback\n * port. That is also the trap — several booted simulators contend for the\n * same one, and WebDriverAgent scans 8100-8199 when `USE_PORT` is unset, so a\n * second runner comes up healthy on 8101 and a server pointed at 8100 drives\n * the first simulator while reporting success. `ios_simulator_diagnostics`\n * reports which simulator actually owns the port.\n */\n wdaUrl(): string {\n return this.opts.wdaUrl ?? `http://127.0.0.1:${this.opts.wdaPort}`;\n }\n\n get wdaPort(): number {\n return this.opts.wdaPort;\n }\n\n wda(_target: SimulatorSummary): WdaClient {\n const url = this.wdaUrl();\n const existing = this.wdaClients.get(url);\n if (existing) return existing;\n const client = new WdaClient({\n baseUrl: async () => url,\n timeoutMs: this.opts.wdaTimeoutMs,\n remedies: SIMULATOR_WDA_REMEDIES,\n ...(this.opts.fetch ? { fetch: this.opts.fetch } : {}),\n ...(this.opts.logger ? { logger: this.opts.logger } : {}),\n });\n this.wdaClients.set(url, client);\n return client;\n }\n}\n","import { createArgs, type ScreenNaming } from \"@mgcrea/mcp-ios-core\";\n\nexport {\n compact,\n fail,\n ok,\n okImage,\n okText,\n toFailure,\n wrap,\n wrapResult,\n} from \"@mgcrea/mcp-ios-core\";\nexport type { ImageContent, TextContent, ToolResult } from \"@mgcrea/mcp-ios-core\";\n\n/**\n * How the shared tools name themselves and each other in this server.\n *\n * `copy.target` is written out rather than derived: a simulator is picked by\n * UDID, by name, or by being the only booted one, and none of those is what the\n * device server's version of this sentence says. `names.listTargets` is\n * overridden for the same reason — `ios_simulator_list_devices` would be a\n * mouthful for a tool that lists simulators.\n */\nexport const SIMULATOR_NAMING: ScreenNaming = {\n prefix: \"ios_simulator\",\n title: \"iOS Simulator\",\n noun: \"simulator\",\n envPrefix: \"IOS_SIMULATOR\",\n names: { listTargets: \"ios_simulator_list\" },\n copy: {\n target:\n \"Which simulator: its UDID or its name as shown by ios_simulator_list. Omit it when exactly \" +\n \"one is booted — that is the normal case, and IOS_SIMULATOR_ID pins it when it is not. \" +\n \"Unlike simctl's own `booted`, this never picks arbitrarily between two.\",\n },\n};\n\nconst args = createArgs(SIMULATOR_NAMING);\n\nexport const { bundleIdArg, confirmArg, detailArg, screenshotArg, settleArg, xArg, yArg } = args;\n\n/**\n * The schema field is `device` in every tool here, shared and local alike.\n *\n * It looks odd next to a server called \"simulator\" and it is still the right\n * name: `simctl`'s own interface calls a simulator a device — `simctl list\n * devices`, and `<device>` in the usage line of every subcommand — so this is\n * Apple's word for the thing, and the `ios_simulator_` prefix already says\n * which kind. Mixing `simulator` here with the shared tools' `device` would be\n * worse than either alone.\n */\nexport const deviceArg = args.targetArg;\n","import type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { IosError } from \"#/client/errors\";\nimport { summarizeApps } from \"#/client/shape\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { bundleIdArg, deviceArg, wrap } from \"#/tools/util\";\n\nexport const registerListAppsTool = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_list_apps\",\n {\n title: \"iOS Simulator: List Apps\",\n description:\n \"List the apps installed on a simulator, with the bundle id every other tool takes. \" +\n \"Defaults to your own apps: a stock simulator carries about thirty, and twenty-five of \" +\n \"them are Apple's. Each entry also carries `dataContainer` — an ordinary path on this \" +\n \"Mac, so an app's database or logs can be read directly with no copy step.\",\n inputSchema: z.object({\n device: deviceArg,\n include_all: z\n .boolean()\n .default(false)\n .describe(\"Include Apple's built-in apps as well. Off by default; the list is long.\"),\n }),\n annotations: { readOnlyHint: true },\n },\n async ({ device, include_all }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const apps = await client.simctl.listApps(target.id);\n return { apps: summarizeApps(apps, { includeAll: include_all }) };\n }),\n );\n};\n\nexport const registerAppTools = (\n server: McpServer,\n client: SimulatorClient,\n launchArgs: string[],\n outputDir: string,\n): void => {\n server.registerTool(\n \"ios_simulator_install\",\n {\n title: \"iOS Simulator: Install\",\n description:\n \"Install a build. The path must be a `.app` **bundle directory** built for the simulator \" +\n '— not an `.ipa`, and not a device build, both of which fail with a bare \"No such file or ' +\n 'directory\" that reads like a typo. Installing over an existing copy replaces it and ' +\n \"keeps its data.\",\n inputSchema: z.object({\n device: deviceArg,\n path: z\n .string()\n .describe(\n \"Absolute path to the .app bundle, e.g. \" +\n \"~/Library/Developer/Xcode/DerivedData/…/Build/Products/Debug-iphonesimulator/Foo.app\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n },\n async ({ device, path }) =>\n wrap(async () => {\n if (!path.startsWith(\"/\")) {\n throw new IosError(`path must be absolute, got \"${path}\".`, {\n remedy: \"Pass the full path to the .app bundle.\",\n });\n }\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.install(target.id, path);\n return { installed: path, udid: target.id };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_launch\",\n {\n title: \"iOS Simulator: Launch\",\n description:\n \"Launch an installed app. Pass `arguments` to put it into a fixture or demo mode — that \" +\n \"is what IOS_SIMULATOR_LAUNCH_ARGS sets as the default for every launch that does not \" +\n \"override it. Standard output and error are captured to files under the output directory, \" +\n \"so an app that dies on launch leaves something readable behind.\",\n inputSchema: z.object({\n device: deviceArg,\n bundle_id: bundleIdArg,\n arguments: z\n .array(z.string())\n .optional()\n .describe(\n 'Launch arguments, e.g. [\"-DemoMode\", \"YES\"]. Overrides IOS_SIMULATOR_LAUNCH_ARGS ' +\n \"rather than adding to it.\",\n ),\n terminate_first: z\n .boolean()\n .default(true)\n .describe(\n \"Replace a running copy rather than attaching to it. On by default so a launch means \" +\n \"a fresh process and a predictable first screen.\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, bundle_id, arguments: args, terminate_first }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const effective = args ?? launchArgs;\n const stdout = `${outputDir}/${bundle_id}.out.log`;\n const stderr = `${outputDir}/${bundle_id}.err.log`;\n const output = await client.simctl.launch(target.id, bundle_id, {\n args: effective,\n terminateExisting: terminate_first,\n stdout,\n stderr,\n });\n const pid = /:\\s*(\\d+)\\s*$/.exec(output.trim())?.[1];\n return {\n launched: bundle_id,\n ...(pid ? { pid: Number(pid) } : {}),\n ...(effective.length > 0 ? { arguments: effective } : {}),\n ...(args === undefined && launchArgs.length > 0\n ? { argumentsFrom: \"IOS_SIMULATOR_LAUNCH_ARGS\" }\n : {}),\n logs: { stdout, stderr },\n };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_terminate\",\n {\n title: \"iOS Simulator: Terminate\",\n description:\n \"Kill a running app. The app is not uninstalled and its data is untouched — this is how \" +\n \"you get back to a cold start without erasing anything.\",\n inputSchema: z.object({ device: deviceArg, bundle_id: bundleIdArg }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n },\n async ({ device, bundle_id }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.terminate(target.id, bundle_id);\n return { terminated: bundle_id };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_open_url\",\n {\n title: \"iOS Simulator: Open URL\",\n description:\n \"Open a URL on the simulator, which is how you exercise a deep link or a universal link \" +\n \"without finding a way to tap one. An `https://` link opens in Safari unless the app \" +\n \"claims it; a custom scheme goes straight to whichever app registered it.\",\n inputSchema: z.object({\n device: deviceArg,\n url: z.url().describe('e.g. \"myapp://garden/42\" or \"https://example.com/garden/42\".'),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, url }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.openUrl(target.id, url);\n return { opened: url };\n }),\n );\n};\n","import type { DisplayInfo } from \"@mgcrea/mcp-ios-core\";\nimport type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { START_RUNNER_REMEDY } from \"#/client/errors\";\nimport { toListRow, type SimulatorSummary } from \"#/client/shape\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport type { ToolContext } from \"#/tools/index\";\nimport { deviceArg, wrap } from \"#/tools/util\";\n\nexport type Diagnosis = {\n ok: boolean;\n writes: \"enabled\" | \"disabled\";\n /**\n * The booted ones only, plus the target. A typical machine carries thirty-odd\n * simulators and listing them all here spent more of the context window than\n * the whole rest of the report — and none of them is what a diagnosis is\n * about. `counts` carries the shape of the rest; ios_simulator_list is there\n * for the full picture.\n */\n simulators: Record<string, unknown>[];\n counts: { total: number; booted: number; unavailable: number };\n target?: { udid: string; name: string; state: string; runtime: string };\n display?: DisplayInfo;\n wda: {\n url: string;\n port: number;\n reachable: boolean;\n /** Which simulator actually owns the port — the failure unique to this server. */\n boundTo?: string;\n state?: unknown;\n error?: string;\n };\n problems: string[];\n nextSteps: string[];\n};\n\n/**\n * Which simulator owns the WebDriverAgent port.\n *\n * This check has no device-server equivalent and it is the most valuable one\n * here. A simulator shares the host's network stack, so a runner's port is a\n * *host* port, and WebDriverAgent scans 8100-8199 when `USE_PORT` is unset. Two\n * booted simulators therefore give you a second runner answering healthily on\n * 8101 while a server pointed at 8100 drives the first one and reports success\n * on every call.\n *\n * Every process inside a simulator carries `SIMULATOR_UDID` in its environment,\n * and `ps -Eww` prints it, so the port's real owner is knowable rather than\n * assumed.\n */\nconst portOwner = async (client: SimulatorClient, port: number): Promise<string | undefined> => {\n try {\n const { stdout: pids } = await client.exec(\n \"/usr/sbin/lsof\",\n [\"-nP\", `-iTCP:${port}`, \"-sTCP:LISTEN\", \"-t\"],\n 5000,\n );\n const pid = pids.trim().split(\"\\n\")[0];\n if (!pid) return undefined;\n const { stdout: env } = await client.exec(\"/bin/ps\", [\"-Eww\", \"-p\", pid], 5000);\n return /SIMULATOR_UDID=([0-9A-Fa-f-]{36})/.exec(env)?.[1];\n } catch {\n // lsof is not always installed and needs no permissions we can assume.\n // Not knowing the owner is a missing field, not a failure.\n return undefined;\n }\n};\n\n/**\n * The whole point of this tool is that **it never throws**. Every other tool\n * fails when nothing is booted, the runtime is missing or the runner is not up,\n * and each of those looks much like the others from the outside. This one\n * collects all of it as data and names which half is missing.\n */\nexport const diagnose = async (\n client: SimulatorClient,\n ctx: ToolContext,\n deviceHint?: string,\n): Promise<Diagnosis> => {\n const problems: string[] = [];\n const nextSteps: string[] = [];\n const writes = ctx.allowWrites ? \"enabled\" : \"disabled\";\n\n let simulators: SimulatorSummary[] = [];\n try {\n simulators = await client.listSimulators({ fresh: true });\n } catch (err) {\n problems.push(`Could not list simulators: ${err instanceof Error ? err.message : String(err)}`);\n nextSteps.push(\n \"Install Xcode and its command line tools, then run `xcrun simctl list devices`.\",\n );\n return {\n ok: false,\n writes,\n simulators: [],\n counts: { total: 0, booted: 0, unavailable: 0 },\n wda: { url: client.wdaUrl(), port: client.wdaPort, reachable: false },\n problems,\n nextSteps,\n };\n }\n\n const booted = simulators.filter((sim) => sim.state === \"Booted\");\n const unavailable = simulators.filter((sim) => !sim.available);\n const counts = {\n total: simulators.length,\n booted: booted.length,\n unavailable: unavailable.length,\n };\n\n if (simulators.length === 0) {\n problems.push(\"simctl knows about no simulators at all.\");\n nextSteps.push(\"Install a runtime: Xcode > Settings > Components.\");\n } else if (booted.length === 0) {\n problems.push(\"No simulator is booted.\");\n nextSteps.push(\n 'Boot one with ios_simulator_power {\"state\":\"booted\"} — this server never boots implicitly.',\n );\n }\n\n let target: SimulatorSummary | undefined;\n try {\n target = await client.resolveTarget(deviceHint);\n } catch (err) {\n if (booted.length > 0) {\n problems.push(err instanceof Error ? err.message : String(err));\n const remedy = (err as { remedy?: string }).remedy;\n if (remedy) nextSteps.push(remedy);\n }\n }\n\n let display: DisplayInfo | undefined;\n if (target) {\n try {\n display = await client.display(target);\n } catch {\n // Geometry comes from the device type's profile.plist, so failing here\n // means the profile could not be read — worth a missing field, not a\n // failed report.\n }\n }\n\n // WebDriverAgent. `reachable` and usable coincide on a simulator: there is no\n // Enable UI Automation toggle, so the device server's separate `authorized`\n // probe would be pure latency. What replaces it is `boundTo`.\n const url = client.wdaUrl();\n const wda: Diagnosis[\"wda\"] = { url, port: client.wdaPort, reachable: false };\n try {\n wda.state = await client.wda(target ?? ({ id: \"\", name: \"\" } as SimulatorSummary)).status();\n wda.reachable = true;\n } catch (err) {\n wda.error = err instanceof Error ? err.message : String(err);\n problems.push(\"WebDriverAgent is not answering, so the UI tree and the input tools will fail.\");\n nextSteps.push(\n `${START_RUNNER_REMEDY} Everything else — including screenshots — works without it.`,\n );\n }\n\n if (wda.reachable) {\n const owner = await portOwner(client, client.wdaPort);\n if (owner) wda.boundTo = owner;\n if (owner && target && owner.toLowerCase() !== target.id.toLowerCase()) {\n const other = simulators.find((sim) => sim.id.toLowerCase() === owner.toLowerCase());\n problems.push(\n `Port ${client.wdaPort} is owned by ${other?.name ?? owner}, not ${target.name}. ` +\n \"Every tap and every UI tree read would go to the wrong screen.\",\n );\n nextSteps.push(\n \"WebDriverAgent scans 8100-8199 when USE_PORT is unset, so a second simulator's runner \" +\n \"takes the next free port and nothing looks wrong. Give this one its own port with \" +\n \"IOS_SIMULATOR_WDA_PORT and start its runner with a matching USE_PORT.\",\n );\n }\n }\n\n if (unavailable.length > 0) {\n nextSteps.push(\n `${unavailable.length} simulators have no installed runtime and cannot be used at all. ` +\n \"`xcrun simctl delete unavailable` clears them.\",\n );\n }\n\n return {\n ok: problems.length === 0,\n writes,\n simulators: [...booted, ...(target && target.state !== \"Booted\" ? [target] : [])].map(\n toListRow,\n ),\n counts,\n ...(target\n ? {\n target: {\n udid: target.id,\n name: target.name,\n state: target.state,\n runtime: target.runtime,\n },\n }\n : {}),\n ...(display ? { display } : {}),\n wda,\n problems,\n nextSteps,\n };\n};\n\nexport const registerDiagnosticsTools = (\n server: McpServer,\n client: SimulatorClient,\n ctx: ToolContext,\n): void => {\n server.registerTool(\n \"ios_simulator_diagnostics\",\n {\n title: \"iOS Simulator: Diagnostics\",\n description:\n \"Report both lanes separately and never fail: which simulators exist and which are \" +\n \"usable, which one this server would drive, its screen geometry, and whether \" +\n \"WebDriverAgent is answering — including *which* simulator owns its port, which is the \" +\n \"one way to catch a second runner quietly taking your taps. Start here when anything \" +\n \"else misbehaves.\",\n inputSchema: z.object({ device: deviceArg }),\n annotations: { readOnlyHint: true },\n },\n async ({ device }) => wrap(async () => diagnose(client, ctx, device)),\n );\n\n server.registerResource(\n \"diagnostics\",\n \"ios-simulator://diagnostics\",\n {\n title: \"iOS Simulator diagnostics\",\n description: \"The same report as ios_simulator_diagnostics, as a resource.\",\n mimeType: \"application/json\",\n },\n async (uri) => ({\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify(await diagnose(client, ctx)),\n },\n ],\n }),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { IosError } from \"#/client/errors\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { bundleIdArg, deviceArg, wrap } from \"#/tools/util\";\n\n/** simctl's own list, verbatim — a value it does not accept is not worth guessing at. */\nconst PRIVACY_SERVICES = [\n \"all\",\n \"calendar\",\n \"contacts-limited\",\n \"contacts\",\n \"location\",\n \"location-always\",\n \"photos-add\",\n \"photos\",\n \"media-library\",\n \"microphone\",\n \"motion\",\n \"reminders\",\n \"siri\",\n] as const;\n\n/** APNs refuses anything larger, and it is worth saying so before the round trip. */\nconst MAX_PUSH_BYTES = 4096;\n\nexport const registerEnvironmentTools = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_set_environment\",\n {\n title: \"iOS Simulator: Set Environment\",\n description:\n \"Stage the simulator before a screenshot or a test: dark mode, Dynamic Type size, \" +\n \"increased contrast, a frozen status bar, a simulated location, and app permissions. \" +\n \"None of this is possible on a physical device, and it is most of the reason to prefer a \" +\n \"simulator for UI work. Every field is optional; those given are applied in order and the \" +\n \"result reports the state afterwards. A status bar override survives a reboot, so clear \" +\n \"it when you are done or it quietly pollutes later screenshots.\",\n inputSchema: z.object({\n device: deviceArg,\n appearance: z.enum([\"light\", \"dark\"]).optional().describe(\"Interface style.\"),\n content_size: z\n .enum([\n \"extra-small\",\n \"small\",\n \"medium\",\n \"large\",\n \"extra-large\",\n \"extra-extra-large\",\n \"extra-extra-extra-large\",\n \"accessibility-medium\",\n \"accessibility-large\",\n \"accessibility-extra-large\",\n \"accessibility-extra-extra-large\",\n \"accessibility-extra-extra-extra-large\",\n ])\n .optional()\n .describe(\"Dynamic Type size. The accessibility- sizes are where layouts break.\"),\n increase_contrast: z.boolean().optional().describe(\"Increase Contrast accessibility mode.\"),\n status_bar: z\n .object({\n time: z\n .string()\n .optional()\n .describe('Fixed clock, e.g. \"9:41\" — what App Store screenshots use.'),\n wifi_bars: z.number().int().min(0).max(3).optional(),\n cellular_bars: z.number().int().min(0).max(4).optional(),\n operator_name: z.string().optional().describe('Carrier name; \"\" hides it.'),\n battery_level: z.number().int().min(0).max(100).optional(),\n battery_state: z.enum([\"charging\", \"charged\", \"discharging\"]).optional(),\n })\n .optional()\n .describe(\"Freeze the status bar. Overrides persist until cleared.\"),\n clear_status_bar: z\n .boolean()\n .default(false)\n .describe(\"Drop every status bar override, restoring the real one.\"),\n location: z\n .object({ latitude: z.number(), longitude: z.number() })\n .optional()\n .describe(\"Teleport the device, for location-gated screens.\"),\n clear_location: z.boolean().default(false).describe(\"Stop simulating a location.\"),\n permission: z\n .object({\n action: z\n .enum([\"grant\", \"revoke\", \"reset\"])\n .describe(\n \"`grant` and `revoke` answer the prompt without showing it; `reset` makes it \" +\n \"appear again on next use.\",\n ),\n service: z.enum(PRIVACY_SERVICES),\n bundle_id: bundleIdArg\n .optional()\n .describe(\"Required for grant and revoke; optional for reset.\"),\n })\n .optional()\n .describe(\n \"Set an app permission directly. This is how you reach the denied branch of a \" +\n \"permission check, which is otherwise a prompt nobody can tap.\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n },\n async ({\n device,\n appearance,\n content_size,\n increase_contrast,\n status_bar,\n clear_status_bar,\n location,\n clear_location,\n permission,\n }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const applied: string[] = [];\n\n if (appearance) {\n await client.simctl.uiSet(target.id, \"appearance\", appearance);\n applied.push(`appearance=${appearance}`);\n }\n if (content_size) {\n await client.simctl.uiSet(target.id, \"content_size\", content_size);\n applied.push(`content_size=${content_size}`);\n }\n if (increase_contrast !== undefined) {\n await client.simctl.uiSet(\n target.id,\n \"increase_contrast\",\n increase_contrast ? \"enabled\" : \"disabled\",\n );\n applied.push(`increase_contrast=${increase_contrast}`);\n }\n if (clear_status_bar) {\n await client.simctl.statusBarClear(target.id);\n applied.push(\"status_bar cleared\");\n }\n if (status_bar) {\n const flags = [\n ...(status_bar.time ? [\"--time\", status_bar.time] : []),\n ...(status_bar.wifi_bars !== undefined\n ? [\"--wifiMode\", \"active\", \"--wifiBars\", String(status_bar.wifi_bars)]\n : []),\n ...(status_bar.cellular_bars !== undefined\n ? [\"--cellularMode\", \"active\", \"--cellularBars\", String(status_bar.cellular_bars)]\n : []),\n ...(status_bar.operator_name !== undefined\n ? [\"--operatorName\", status_bar.operator_name]\n : []),\n ...(status_bar.battery_level !== undefined\n ? [\"--batteryLevel\", String(status_bar.battery_level)]\n : []),\n ...(status_bar.battery_state ? [\"--batteryState\", status_bar.battery_state] : []),\n ];\n if (flags.length === 0) {\n throw new IosError(\"`status_bar` was given with no fields to override.\", {\n remedy: \"Set at least one field, or use `clear_status_bar` to drop the overrides.\",\n });\n }\n await client.simctl.statusBarOverride(target.id, flags);\n applied.push(\"status_bar overridden\");\n }\n if (clear_location) {\n await client.simctl.locationClear(target.id);\n applied.push(\"location cleared\");\n }\n if (location) {\n await client.simctl.locationSet(target.id, location.latitude, location.longitude);\n applied.push(`location=${location.latitude},${location.longitude}`);\n }\n if (permission) {\n if (permission.action !== \"reset\" && !permission.bundle_id) {\n throw new IosError(`\\`${permission.action}\\` needs a bundle_id.`, {\n remedy: \"Pass the app's bundle id, or use `reset`, which does not need one.\",\n });\n }\n await client.simctl.privacy(\n target.id,\n permission.action,\n permission.service,\n permission.bundle_id,\n );\n applied.push(`permission ${permission.action} ${permission.service}`);\n }\n\n if (applied.length === 0) {\n throw new IosError(\"Nothing to do — no field was given.\", {\n remedy:\n \"Set at least one of appearance, content_size, status_bar, location, permission.\",\n });\n }\n\n // Read back rather than echo the request: `simctl ui` answers `unknown`\n // or `unsupported` on a runtime too old for a setting, and reporting the\n // value we asked for would hide that.\n return {\n applied,\n state: {\n appearance: await client.simctl.uiGet(target.id, \"appearance\"),\n contentSize: await client.simctl.uiGet(target.id, \"content_size\"),\n statusBar: await client.simctl.statusBarList(target.id),\n },\n };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_push\",\n {\n title: \"iOS Simulator: Push\",\n description:\n \"Deliver a push notification, with no APNs certificate and no server. The payload is a \" +\n \"normal remote-notification body and must contain an `aps` key. Only remote pushes are \" +\n \"simulated — not VoIP, complications or file-provider. The app must be installed; it does \" +\n \"not have to be running.\",\n inputSchema: z.object({\n device: deviceArg,\n bundle_id: bundleIdArg,\n payload: z\n .record(z.string(), z.unknown())\n .describe(\n 'The APNs payload, e.g. {\"aps\":{\"alert\":{\"title\":\"Watering due\",\"body\":\"Monstera\"},' +\n '\"sound\":\"default\",\"badge\":1}}. Max 4096 bytes.',\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, bundle_id, payload }) =>\n wrap(async () => {\n if (!(\"aps\" in payload)) {\n throw new IosError(\"A push payload must have a top-level `aps` key.\", {\n remedy: 'Wrap the notification, e.g. {\"aps\":{\"alert\":\"Hello\"}}.',\n });\n }\n const bytes = Buffer.byteLength(JSON.stringify(payload));\n if (bytes > MAX_PUSH_BYTES) {\n throw new IosError(`The payload is ${bytes} bytes; APNs allows ${MAX_PUSH_BYTES}.`, {\n remedy: \"Shorten it — this is refused here rather than by simctl, but the cap is real.\",\n });\n }\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.push(target.id, bundle_id, payload);\n return { pushed: bundle_id, bytes };\n }),\n );\n};\n","import type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { toListRow } from \"#/client/shape\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { confirmArg, deviceArg, wrap } from \"#/tools/util\";\n\n/** Booting is slow and visible; this is how long `power` waits to see it land. */\nconst BOOT_POLL_MS = 500;\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\nexport const registerListTool = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_list\",\n {\n title: \"iOS Simulator: List\",\n description:\n \"List the simulators on this machine, with the UDID every other tool takes. Returns all \" +\n \"of them by default — booted and shut down, available and not — because \\\"why can't I use \" +\n 'that one\" has to be answerable from this list: a simulator whose runtime is missing shows ' +\n \"`available: false` with the reason.\",\n inputSchema: z.object({\n booted_only: z\n .boolean()\n .default(false)\n .describe(\"Only simulators that are currently running.\"),\n name_contains: z\n .string()\n .optional()\n .describe('Case-insensitive substring filter on the name, e.g. \"iPhone 17\".'),\n }),\n annotations: { readOnlyHint: true },\n },\n async ({ booted_only, name_contains }) =>\n wrap(async () => {\n const all = await client.listSimulators({ fresh: true });\n const needle = name_contains?.toLowerCase();\n const matched = all.filter(\n (sim) =>\n (!booted_only || sim.state === \"Booted\") &&\n (needle === undefined || sim.name.toLowerCase().includes(needle)),\n );\n const unavailable = all.filter((sim) => !sim.available).length;\n return {\n simulators: matched.map(toListRow),\n booted: all.filter((sim) => sim.state === \"Booted\").length,\n total: all.length,\n ...(unavailable > 0\n ? {\n unavailable,\n note:\n `${unavailable} simulators have no installed runtime and cannot be used. ` +\n \"`xcrun simctl delete unavailable` clears them.\",\n }\n : {}),\n };\n }),\n );\n};\n\nexport const registerLifecycleTools = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_power\",\n {\n title: \"iOS Simulator: Power\",\n description:\n \"Boot or shut down a simulator. Booting is the one thing this server will not do for you \" +\n \"implicitly — a cold boot takes tens of seconds and puts a window on the user's screen, \" +\n \"and it changes which simulator an unqualified call resolves to afterwards. Both \" +\n \"directions are idempotent: booting a booted simulator succeeds.\",\n inputSchema: z.object({\n device: deviceArg,\n state: z\n .enum([\"booted\", \"shutdown\"])\n .describe(\"The state to put it in. `booted` also waits for it to actually get there.\"),\n open_window: z\n .boolean()\n .default(true)\n .describe(\n \"Bring up the Simulator app so the screen is visible. A `boot` on its own is headless, \" +\n \"which is fine for an agent and confusing for a person watching.\",\n ),\n wait_ms: z\n .number()\n .int()\n .min(0)\n .max(300_000)\n .default(60_000)\n .describe(\"How long to wait for a boot to complete before reporting what it saw.\"),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true },\n },\n async ({ device, state, open_window, wait_ms }) =>\n wrap(async () => {\n const target = await client.resolveTarget(device);\n if (state === \"shutdown\") {\n if (target.state !== \"Booted\") {\n return { udid: target.id, name: target.name, state: \"Shutdown\", alreadyThere: true };\n }\n await client.simctl.shutdown(target.id);\n return { udid: target.id, name: target.name, state: \"Shutdown\" };\n }\n\n const already = target.state === \"Booted\";\n if (!already) await client.simctl.boot(target.id);\n if (open_window) await client.simctl.openApp(client.openPath);\n\n // Poll rather than trust the exit code: `simctl boot` returns as soon as\n // CoreSimulator has accepted the request, well before the device is\n // usable, and every tool after this one would fail on a half-booted one.\n const deadline = Date.now() + wait_ms;\n let observed = target.state;\n while (Date.now() < deadline) {\n const now = (await client.listSimulators({ fresh: true })).find(\n (sim) => sim.id === target.id,\n );\n observed = now?.state ?? observed;\n if (observed === \"Booted\") break;\n await sleep(BOOT_POLL_MS);\n }\n return {\n udid: target.id,\n name: target.name,\n state: observed,\n ...(already ? { alreadyThere: true } : {}),\n ...(observed === \"Booted\"\n ? {}\n : { warning: `Still ${observed} after ${wait_ms}ms. Raise wait_ms, or check Xcode.` }),\n };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_erase\",\n {\n title: \"iOS Simulator: Erase\",\n description:\n \"Wipe a simulator back to a factory state: installed apps, their data, the keychain and \" +\n \"every granted permission. The only irreversible tool here, and the reason it is the only \" +\n \"one behind `confirm`. Use it to test a genuine first launch. A booted simulator is shut \" +\n \"down first and booted again afterwards, because simctl refuses to erase a running one.\",\n inputSchema: z.object({\n device: deviceArg,\n confirm: confirmArg,\n }),\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false },\n },\n async ({ device }) =>\n wrap(async () => {\n const target = await client.resolveTarget(device);\n const wasBooted = target.state === \"Booted\";\n if (wasBooted) await client.simctl.shutdown(target.id);\n await client.simctl.erase(target.id);\n if (wasBooted) await client.simctl.boot(target.id);\n return {\n udid: target.id,\n name: target.name,\n erased: true,\n rebooted: wasBooted,\n };\n }),\n );\n};\n","import { spawn } from \"node:child_process\";\nimport { mkdir, open } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { IosError, START_RUNNER_REMEDY } from \"#/client/errors\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { deviceArg, wrap } from \"#/tools/util\";\n\n/** Injected in tests so no xcodebuild is ever started by the suite. */\nexport type SpawnRunner = (\n command: string,\n args: string[],\n opts: { env: Record<string, string>; logPath: string },\n) => Promise<number | undefined>;\n\nconst defaultSpawnRunner: SpawnRunner = async (command, args, { env, logPath }) => {\n await mkdir(dirname(logPath), { recursive: true });\n const log = await open(logPath, \"a\");\n const child = spawn(command, args, {\n // Detached, because the XCTest session *is* the HTTP server: it has to\n // outlive this MCP server, or the runner dies with the conversation.\n detached: true,\n stdio: [\"ignore\", log.fd, log.fd],\n env: { ...process.env, ...env },\n });\n child.unref();\n await log.close();\n return child.pid;\n};\n\nconst scriptPath = (): string => {\n // Beside the built module in dist/, or beside src/ in a checkout.\n const here = dirname(fileURLToPath(import.meta.url));\n for (const candidate of [\n join(here, \"..\", \"scripts\", \"wda.sh\"),\n join(here, \"..\", \"..\", \"scripts\", \"wda.sh\"),\n ]) {\n return candidate;\n }\n throw new IosError(\"Could not find `scripts/wda.sh` beside this module.\");\n};\n\nexport const registerRunnerTools = (\n server: McpServer,\n client: SimulatorClient,\n outputDir: string,\n spawnRunner: SpawnRunner = defaultSpawnRunner,\n): void => {\n server.registerTool(\n \"ios_simulator_restart_wda\",\n {\n title: \"iOS Simulator: Restart WebDriverAgent\",\n description:\n \"Start the WebDriverAgent runner, or restart it after it has died. Only the UI tree and \" +\n \"the input tools need it — screenshots and everything simctl does keep working without \" +\n \"it. The runner is started detached so it outlives this conversation, and its output goes \" +\n \"to a log file rather than to you. It does not wait: give it fifteen seconds or so, then \" +\n \"check ios_simulator_diagnostics.\",\n inputSchema: z.object({\n device: deviceArg,\n port: z\n .number()\n .int()\n .min(1)\n .max(65535)\n .optional()\n .describe(\n \"Port to bind, passed to the runner as USE_PORT. Defaults to IOS_SIMULATOR_WDA_PORT. \" +\n \"Two simulators need two ports: they share this Mac's loopback, and a runner with \" +\n \"no USE_PORT scans 8100-8199 and silently takes the next free one.\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, port }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const usePort = port ?? client.wdaPort;\n const logPath = join(outputDir, \"wda.log\");\n const pid = await spawnRunner(\"/bin/bash\", [scriptPath(), \"run\"], {\n env: {\n IOS_SIMULATOR_ID: target.id,\n IOS_SIMULATOR_WDA_PORT: String(usePort),\n USE_PORT: String(usePort),\n },\n logPath,\n });\n if (pid === undefined) {\n throw new IosError(\"Could not start the runner — the process had no pid.\", {\n remedy: START_RUNNER_REMEDY,\n });\n }\n return {\n started: true,\n pid,\n port: usePort,\n udid: target.id,\n logPath,\n note: \"Give it ~15s, then check ios_simulator_diagnostics for `wda.reachable`.\",\n };\n }),\n );\n};\n","import { registerInputTools, registerScreenTools } from \"@mgcrea/mcp-ios-core\";\nimport type { McpServer } from \"@modelcontextprotocol/server\";\n\nimport { EMPTY_SCREENSHOT_REMEDY } from \"#/client/errors\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport type { Config } from \"#/config\";\nimport { registerAppTools, registerListAppsTool } from \"#/tools/apps\";\nimport { registerDiagnosticsTools } from \"#/tools/diagnostics\";\nimport { registerEnvironmentTools } from \"#/tools/environment\";\nimport { registerLifecycleTools, registerListTool } from \"#/tools/lifecycle\";\nimport { registerRunnerTools, type SpawnRunner } from \"#/tools/runner\";\nimport { SIMULATOR_NAMING } from \"#/tools/util\";\n\nexport type ToolContext = {\n config: Config;\n /** On by default here — see IOS_SIMULATOR_ALLOW_WRITES. */\n allowWrites: boolean;\n};\n\n/**\n * All capability decisions in one place, so \"why can't I call that\" is answered\n * by one file.\n *\n * The split is observe versus drive, exactly as in the device server. What\n * differs is the **default**: writes are on. The convention that keeps the rest\n * of the fleet read-only exists because a mutating tool acts on someone's real\n * account or someone's real phone, and a simulator is neither — it holds no\n * person's data and `ios_simulator_erase` puts it back to factory in seconds.\n * Turning writes off with IOS_SIMULATOR_ALLOW_WRITES=0 restores the device\n * server's posture, and then the driving tools are *absent* rather than\n * refused, because a refusal still lets a model try, retry and reason about a\n * way around it.\n *\n * The observe half is unusually capable here: `screenshot` goes through simctl,\n * so seeing the screen needs no WebDriverAgent and no setup of any kind. Only\n * `ui_tree` does, and that is a runtime fact reported by\n * ios_simulator_diagnostics rather than a registration decision — the runner\n * can start and stop while this server is connected.\n */\nexport const registerTools = (\n server: McpServer,\n client: SimulatorClient,\n ctx: ToolContext,\n spawnRunner?: SpawnRunner,\n): void => {\n const shared = {\n host: client,\n naming: SIMULATOR_NAMING,\n maxTreeBytes: ctx.config.maxTreeBytes,\n capHint: \"raise IOS_SIMULATOR_MAX_TREE_BYTES\",\n // `home` is the gesture and works; a simulator has no volume rocker worth\n // pressing, and offering one that does nothing is worse than offering none.\n buttons: [\"home\"] as [string, ...string[]],\n emptyScreenshotRemedy: EMPTY_SCREENSHOT_REMEDY,\n // Geometry here is a local plist read reported on every screenshot, not a\n // slow round trip worth its own tool as it is on a device.\n includeDisplayInfo: false,\n };\n\n registerDiagnosticsTools(server, client, ctx);\n registerListTool(server, client);\n registerListAppsTool(server, client);\n registerScreenTools(server, shared);\n\n if (!ctx.allowWrites) return;\n\n registerInputTools(server, shared);\n registerLifecycleTools(server, client);\n registerAppTools(server, client, ctx.config.launchArgs, ctx.config.outputDir);\n registerEnvironmentTools(server, client);\n registerRunnerTools(server, client, ctx.config.outputDir, spawnRunner);\n};\n","import type { ExecImpl, Logger } from \"@mgcrea/mcp-ios-core\";\nimport { McpServer } from \"@modelcontextprotocol/server\";\n\nimport { BUILD_INFO } from \"#/build-info\";\nimport { SimulatorClient } from \"#/client/simulator\";\nimport type { Config } from \"#/config\";\nimport { registerTools } from \"#/tools/index\";\nimport type { SpawnRunner } from \"#/tools/runner\";\n\nexport const SERVER_NAME = BUILD_INFO.name;\nexport const SERVER_VERSION = BUILD_INFO.version;\n\nexport type CreateServerOptions = {\n config: Config;\n /** Override the process boundary (tests): `xcrun`, `plutil` and `sips` never run. */\n exec?: ExecImpl;\n /** Override HTTP to WebDriverAgent (tests). */\n fetch?: typeof fetch;\n /** Override starting the runner (tests): no process is spawned. */\n spawnRunner?: SpawnRunner;\n logger?: Logger;\n};\n\nexport type CreatedServer = {\n server: McpServer;\n client: SimulatorClient;\n};\n\n/**\n * A pure factory. The injectable seams — `exec`, `fetch` and `spawnRunner` —\n * are the whole reason the test suite can drive real tools through the real SDK\n * with no simulator, no Xcode, no WebDriverAgent and no process left running.\n * Nothing below `config.ts` reads `process.env`.\n */\nexport const createServer = (opts: CreateServerOptions): CreatedServer => {\n const { config } = opts;\n const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });\n\n const client = new SimulatorClient({\n xcrunPath: config.xcrunPath,\n sipsPath: config.sipsPath,\n plutilPath: config.plutilPath,\n openPath: config.openPath,\n execTimeoutMs: config.execTimeoutMs,\n wdaTimeoutMs: config.wdaTimeoutMs,\n wdaPort: config.wdaPort,\n ...(config.wdaUrl ? { wdaUrl: config.wdaUrl } : {}),\n ...(config.simulatorId ? { defaultSimulatorId: config.simulatorId } : {}),\n ...(opts.exec ? { exec: opts.exec } : {}),\n ...(opts.fetch ? { fetch: opts.fetch } : {}),\n ...(opts.logger ? { logger: opts.logger } : {}),\n });\n\n registerTools(server, client, { config, allowWrites: config.allowWrites }, opts.spawnRunner);\n\n return { server, client };\n};\n","import { readFileSync } from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { z } from \"zod\";\n\n/**\n * There are no credentials here, and unlike the device server there is barely\n * any setup either. Two lanes reach a simulator and both are local:\n *\n * * `xcrun simctl` — Apple's own tool. It covers app lifecycle, the device's\n * whole staged environment, and the screen itself: `simctl io screenshot`\n * needs no runner at all.\n * * WebDriverAgent — an HTTP server inside the simulator, on the *host's*\n * loopback since a simulator shares the host network stack. Only the\n * accessibility tree and synthetic touches go through it.\n */\nconst ConfigSchema = z\n .object({\n /**\n * UDID, name, or the literal `booted`. Left unset, a single booted\n * simulator is used and two or more is an error that names them, rather\n * than the coin flip simctl's own `booted` performs.\n */\n simulatorId: z.string().min(1).optional(),\n wdaUrl: z.url().optional(),\n /**\n * Must match the runner's `USE_PORT`. One port per simulator: they all bind\n * the same host loopback, so two runners on 8100 is a silent wrong-target\n * bug rather than a collision.\n */\n wdaPort: z.number().int().min(1).max(65535).default(8100),\n wdaTimeoutMs: z.number().int().min(1000).max(600_000).default(30_000),\n /** A cold boot or a large `install` genuinely takes tens of seconds. */\n execTimeoutMs: z.number().int().min(1000).max(1_800_000).default(120_000),\n /**\n * **On by default**, which is the one place this server deliberately breaks\n * the fleet convention. What makes the device server read-only is that a\n * phone belongs to a real person; a simulator is disposable and holds\n * nobody's data. `ios_simulator_erase` still requires an explicit `confirm`\n * because it is the only irreversible tool here.\n */\n allowWrites: z.boolean().default(true),\n /** Applied by `launch` when the call passes no `arguments` of its own. */\n launchArgs: z.array(z.string()).default([]),\n /** Saved screenshots, launch logs and the runner log. */\n outputDir: z.string().min(1).default(join(tmpdir(), \"mcp-ios-simulator\")),\n maxTreeBytes: z.number().int().min(1000).max(500_000).default(24_000),\n xcrunPath: z.string().min(1).default(\"/usr/bin/xcrun\"),\n sipsPath: z.string().min(1).default(\"/usr/bin/sips\"),\n /** The `listapps` plist lane, and the device-type profile read. */\n plutilPath: z.string().min(1).default(\"/usr/bin/plutil\"),\n /** `simctl boot` is headless; this is what attaches a window. */\n openPath: z.string().min(1).default(\"/usr/bin/open\"),\n })\n .strict()\n .superRefine((_cfg, _ctx) => {\n // Deliberately NOT an error when there is no simulator and no Xcode. An MCP\n // server that exits on startup shows up in the client as a bare\n // \"MCP error -32000: Connection closed\" with stderr swallowed, so the one\n // message that would have explained the problem never reaches anyone.\n // ios_simulator_diagnostics reports all of it as data instead.\n });\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\nconst trimmed = (value: string | undefined): string | undefined => {\n const t = value?.trim();\n return t ? t : undefined;\n};\n\nconst parseBool = (value: string | undefined): boolean | undefined => {\n const t = trimmed(value);\n if (t === undefined) return undefined;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(t.toLowerCase());\n};\n\nconst parseIntOpt = (value: string | undefined): number | undefined => {\n const t = trimmed(value);\n if (t === undefined) return undefined;\n const n = Number(t);\n return Number.isInteger(n) ? n : undefined;\n};\n\n/** Whitespace-separated, so `IOS_SIMULATOR_LAUNCH_ARGS=\"-DemoMode -NoCloud\"` works. */\nconst parseArgs = (value: string | undefined): string[] | undefined => {\n const t = trimmed(value);\n return t === undefined ? undefined : t.split(/\\s+/);\n};\n\nexport const defaultConfigPath = (env: NodeJS.ProcessEnv = process.env): string =>\n trimmed(env.IOS_SIMULATOR_CONFIG) ??\n join(homedir(), \".config\", \"ios-simulator-mcp\", \"config.json\");\n\n/**\n * `.strict()` on purpose: a typo'd `wdaPort` spelled `wda_port` must be an\n * error. Silently ignoring an unknown key looks exactly like \"that setting had\n * no effect\", which is the worst possible way to learn where your config came\n * from.\n */\nconst readConfigFile = (path: string): Partial<Config> => {\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return {};\n }\n return ConfigSchema.partial().strict().parse(JSON.parse(raw)) as Partial<Config>;\n};\n\n/**\n * Environment first, config file second, **per field** — not whole-source.\n * A one-off `IOS_SIMULATOR_ALLOW_WRITES=0` has to beat a file that says `true`,\n * while a file that sets `simulatorId` keeps working when the environment says\n * nothing about it. Merging field by field is the only rule that gives both.\n */\nexport const loadConfig = (\n env: NodeJS.ProcessEnv = process.env,\n configPath: string = defaultConfigPath(env),\n): Config => {\n const file = readConfigFile(configPath);\n const pick = <K extends keyof Config>(\n fromEnv: Config[K] | undefined,\n key: K,\n ): Config[K] | undefined => fromEnv ?? file[key];\n\n return ConfigSchema.parse({\n simulatorId: pick(trimmed(env.IOS_SIMULATOR_ID), \"simulatorId\"),\n wdaUrl: pick(trimmed(env.IOS_SIMULATOR_WDA_URL), \"wdaUrl\"),\n wdaPort: pick(parseIntOpt(env.IOS_SIMULATOR_WDA_PORT), \"wdaPort\"),\n wdaTimeoutMs: pick(parseIntOpt(env.IOS_SIMULATOR_WDA_TIMEOUT_MS), \"wdaTimeoutMs\"),\n execTimeoutMs: pick(parseIntOpt(env.IOS_SIMULATOR_TIMEOUT_MS), \"execTimeoutMs\"),\n allowWrites: pick(parseBool(env.IOS_SIMULATOR_ALLOW_WRITES), \"allowWrites\"),\n launchArgs: pick(parseArgs(env.IOS_SIMULATOR_LAUNCH_ARGS), \"launchArgs\"),\n outputDir: pick(trimmed(env.IOS_SIMULATOR_OUTPUT_DIR), \"outputDir\"),\n maxTreeBytes: pick(parseIntOpt(env.IOS_SIMULATOR_MAX_TREE_BYTES), \"maxTreeBytes\"),\n xcrunPath: pick(trimmed(env.IOS_SIMULATOR_XCRUN_PATH), \"xcrunPath\"),\n sipsPath: pick(trimmed(env.IOS_SIMULATOR_SIPS_PATH), \"sipsPath\"),\n plutilPath: pick(trimmed(env.IOS_SIMULATOR_PLUTIL_PATH), \"plutilPath\"),\n openPath: pick(trimmed(env.IOS_SIMULATOR_OPEN_PATH), \"openPath\"),\n });\n};\n"],"mappings":"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"}
|