@mgcrea/mcp-ios-simulator 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-D0JgmT_U.js","names":["IosError","IosError","IosError","IosError","IosError","IosError","IosError"],"sources":["../src/build-info.ts","../src/client/errors.ts","../src/client/display.ts","../src/client/logs.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/client/crash.ts","../src/tools/logs.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\n/**\n * Where Xcode 27 moved the screen geometry: its `profile.plist` no longer carries\n * any `mainScreen*` key — for every device type, old ones included — and the\n * same three numbers live under `capabilities.ScreenDimensionsCapability`.\n */\nexport const capabilitiesPlistPath = (bundlePath: string): string =>\n join(bundlePath, \"Contents\", \"Resources\", \"capabilities.plist\");\n\ntype CapabilitiesPlist = {\n capabilities?: {\n ScreenDimensionsCapability?: {\n \"main-screen-width\"?: number;\n \"main-screen-height\"?: number;\n \"main-screen-scale\"?: number;\n };\n };\n};\n\ntype PlistOpts = { plutilPath: string; exec: ExecImpl; timeoutMs: number };\n\nconst readPlistJson = async <T>(path: string, opts: PlistOpts): Promise<T | undefined> => {\n try {\n const { stdout } = await opts.exec(\n opts.plutilPath,\n [\"-convert\", \"json\", \"-o\", \"-\", path],\n opts.timeoutMs,\n );\n return JSON.parse(stdout) as T;\n } catch {\n return undefined;\n }\n};\n\nexport const readScreenProfile = async (\n deviceType: RawDeviceType,\n opts: PlistOpts,\n): Promise<ScreenProfile> => {\n if (!deviceType.bundlePath) return {};\n const profile =\n (await readPlistJson<ScreenProfile>(profilePlistPath(deviceType.bundlePath), opts)) ?? {};\n if (profile.mainScreenScale !== undefined) return profile;\n\n const dims = (\n await readPlistJson<CapabilitiesPlist>(capabilitiesPlistPath(deviceType.bundlePath), opts)\n )?.capabilities?.ScreenDimensionsCapability;\n if (!dims) return profile;\n return {\n mainScreenWidth: dims[\"main-screen-width\"],\n mainScreenHeight: dims[\"main-screen-height\"],\n mainScreenScale: dims[\"main-screen-scale\"],\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","import { stat } from \"node:fs/promises\";\n\n/**\n * The log captures this server has started, newest per simulator and app.\n *\n * Much less machinery than the device server's `ConsoleCaptures`, because on a\n * simulator nothing has to stay attached. `simctl launch --stdout/--stderr`\n * hands the redirect to the simulator's launchd and returns as soon as the app\n * is spawned; the app then writes its own file for as long as it runs. So there\n * is no child process to watch, to keep out of this server's signal group, or\n * to kill on exit — a capture is a path and a pid, and it outlives this server\n * without leaking anything.\n *\n * Whether it is still running is a question about the app's process, which on a\n * simulator is an ordinary process on this Mac: measured on an iOS 27.0\n * runtime, the pid `simctl launch` prints is a host pid owned by the user, and\n * `kill(pid, 0)` answers for it like for any other.\n */\nexport type Capture = {\n udid: string;\n simulatorName: string;\n bundleId: string;\n /** Where the file actually is on this Mac, which is not always where it was asked for. */\n path: string;\n pid: number | undefined;\n startedAt: string;\n};\n\n/** The liveness probe, as a seam: tests cannot rely on whichever pid is free on the machine. */\nexport type ProcessAlive = (pid: number) => boolean;\n\nexport const defaultProcessAlive: ProcessAlive = (pid) => {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n // EPERM is a process that exists and belongs to someone else. Not one a\n // simulator app of this user's should ever be, but \"alive\" is the truthful\n // reading of it; only ESRCH means gone.\n return (err as NodeJS.ErrnoException).code === \"EPERM\";\n }\n};\n\nexport class LaunchCaptures {\n private readonly captures = new Map<string, Capture>();\n private latest: Capture | undefined;\n\n constructor(private readonly alive: ProcessAlive = defaultProcessAlive) {}\n\n private key(udid: string, bundleId: string): string {\n return `${udid}\\u0000${bundleId}`;\n }\n\n /**\n * One capture per simulator and app. A relaunch replaces the record rather\n * than adding a second, so a read never has to choose between two files of\n * the same app — the older file stays on disk, it is just no longer \"the\" log.\n */\n record(capture: Capture): void {\n this.captures.set(this.key(capture.udid, capture.bundleId), capture);\n this.latest = capture;\n }\n\n find(udid: string | undefined, bundleId: string | undefined): Capture | undefined {\n if (udid && bundleId) return this.captures.get(this.key(udid, bundleId));\n const all = [...this.captures.values()].filter(\n (c) => (!udid || c.udid === udid) && (!bundleId || c.bundleId === bundleId),\n );\n if (all.length === 0) return undefined;\n return this.latest && all.includes(this.latest)\n ? this.latest\n : all.toSorted((a, b) => b.startedAt.localeCompare(a.startedAt))[0];\n }\n\n /**\n * Whether the app is still writing. A pid can be reused once the app exits,\n * and that is accepted: the cost of a false \"running\" is only that a final\n * half-written line waits for the next read.\n */\n running(capture: Capture): boolean {\n return capture.pid !== undefined && this.alive(capture.pid);\n }\n}\n\n/** True when `path` exists on this Mac. */\nexport const exists = async (path: string): Promise<boolean> =>\n stat(path).then(\n () => true,\n () => false,\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 * The simulator's own filesystem root on this Mac. Never shown in a list row;\n * it is how a launch log that the simulator wrote under its own `/tmp` is\n * found again — see `SimulatorClient.locateRedirect`.\n */\n dataPath?: 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 ...(sim.dataPath ? { dataPath: sim.dataPath } : {}),\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 /** Only `launch` with environment variables goes through it; see there. */\n envPath: 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 /** The simulator's own filesystem root on this Mac; see `locateRedirect`. */\n dataPath?: 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(\"the request to open\")) {\n // What `launch` says for an app that is not installed — measured right\n // after an uninstall — and for one that would not start.\n return (\n \"Check the app is installed with ios_simulator_list_apps: a bundle id that is not there \" +\n \"fails exactly like this, with nothing in the message saying so.\"\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 /**\n * Put a window on a booted simulator. A `boot` alone is headless.\n *\n * Xcode 27 ships no Simulator.app — the window belongs to DeviceHub — while\n * Xcode 26 and earlier only have Simulator.app. Try the old app first, since\n * on a Mac with both the one the user already has open is the one they\n * expect; fall back to DeviceHub.\n *\n * DeviceHub is opened by bundle id, never by URL. Measured on Xcode 27.0: no\n * `devices://` form selects a device — `device/<UDID>`, a bare UDID and\n * `simulator/<UDID>` all leave the window on whatever it showed — and a query\n * form it did not recognise made DeviceHub quit, which shuts down every\n * booted simulator on the Mac. So it cannot be told which device to show;\n * the caller is told that instead.\n */\n async openApp(openPath: string): Promise<\"Simulator\" | \"DeviceHub\"> {\n try {\n await this.exec(openPath, [\"-a\", \"Simulator\"], this.opts.timeoutMs);\n return \"Simulator\";\n } catch {\n await this.exec(openPath, [\"-b\", \"com.apple.dt.Devices\"], this.opts.timeoutMs);\n return \"DeviceHub\";\n }\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` takes\n * argv and pins a minimal environment on purpose, so the variables reach\n * simctl through `/usr/bin/env` as `SIMCTL_CHILD_NAME=value` argv entries in\n * front of xcrun — still no shell, and still one argv entry per value. Names\n * are checked here because `env` reads everything up to the first entry with\n * no `=` as an assignment, so a name holding one would shift the command.\n *\n * `stdout` and `stderr` may be the same path, and that is how a log capture\n * uses them: measured on Xcode 27.0 with an iOS 27.0 runtime, a probe app\n * writing to `print`, to stderr and to `Logger` in turn left one file with all\n * three interleaved in order, nothing overwritten. The redirect *appends* — a\n * second launch into the same file kept the first one's lines — and where it\n * lands is not always the path given; see `SimulatorClient.locateRedirect`.\n */\n async launch(\n udid: string,\n bundleId: string,\n opts: {\n args?: string[];\n terminateExisting?: boolean;\n stdout?: string;\n stderr?: string;\n env?: Record<string, string>;\n } = {},\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 const argv = [\"simctl\", \"launch\", ...flags, udid, bundleId, ...(opts.args ?? [])];\n const env = Object.entries(opts.env ?? {});\n if (env.length === 0) return this.run(argv.slice(1));\n\n for (const [name] of env) {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {\n throw new IosError(`Refusing environment variable name \"${name}\".`, {\n remedy: \"Use letters, digits and underscores only, e.g. OS_ACTIVITY_DT_MODE.\",\n });\n }\n }\n this.opts.logger?.debug?.(\n \"simctl\",\n ...argv.slice(1),\n `(env: ${env.map(([k]) => k).join(\" \")})`,\n );\n const { stdout } = await this.exec(\n this.opts.envPath,\n [\n ...env.map(([name, value]) => `SIMCTL_CHILD_${name}=${value}`),\n this.opts.xcrunPath,\n ...argv,\n ],\n this.opts.timeoutMs,\n );\n return stdout;\n }\n\n /**\n * Remove an app and its data container. Irreversible, which is why the tool\n * in front of this takes `confirm`.\n */\n async uninstall(udid: string, bundleId: string): Promise<void> {\n assertNotBulkTarget(udid);\n await this.run([\"uninstall\", udid, bundleId]);\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 { join } from \"node:path\";\n\nimport {\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 { exists, LaunchCaptures, type ProcessAlive } from \"#/client/logs\";\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 /** How launch environment variables reach simctl; see `Simctl.launch`. */\n envPath?: string | undefined;\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 /** Override the launched app's liveness probe (tests). */\n processAlive?: ProcessAlive | 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 readonly captures: LaunchCaptures;\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.captures = new LaunchCaptures(opts.processAlive);\n this.simctl = new Simctl({\n xcrunPath: opts.xcrunPath,\n plutilPath: opts.plutilPath,\n envPath: opts.envPath ?? \"/usr/bin/env\",\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 /**\n * Where a `simctl launch --stdout/--stderr` file actually landed.\n *\n * Not necessarily where it was asked to. The redirect is opened by the\n * simulator's own launchd, which sees the simulator's filesystem, and that\n * has its own `/tmp` and `/var/folders`. Measured on an iOS 27.0 runtime:\n * `--stdout=/tmp/x/a.out` created `<dataPath>/tmp/x/a.out`, and a path under\n * `/var/folders/…/T` — which is where `os.tmpdir()`, and so this server's\n * default output directory, points — landed under `<dataPath>/var/folders/…`,\n * the `/private` prefix of either making no difference. A path under `/Users`\n * was written exactly where it said, missing directories created.\n *\n * Rather than encode which prefixes are remapped, which is a property of a\n * runtime and can change with the next one, this looks: the path as given\n * first, then the same path under the simulator's data root. Neither existing\n * means the redirect went somewhere unforeseen, and the path as given is\n * returned with `found: false` so the caller can say so.\n */\n async locateRedirect(\n target: SimulatorSummary,\n path: string,\n ): Promise<{ path: string; found: boolean }> {\n if (await exists(path)) return { path, found: true };\n if (target.dataPath) {\n const inside = join(target.dataPath, path.replace(/^\\/private(?=\\/)/, \"\"));\n if (await exists(inside)) return { path: inside, found: true };\n }\n return { path, found: false };\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 { join } from \"node:path\";\n\nimport type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport { IosError } from \"#/client/errors\";\nimport { summarizeApps, type SimulatorSummary } from \"#/client/shape\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport { bundleIdArg, deviceArg, wrap } from \"#/tools/util\";\n\n/** `io.mgcrea.Canopy: 12345` is the whole of what `simctl launch` prints. */\nconst parsePid = (output: string): number | undefined => {\n const pid = /:\\s*(\\d+)\\s*$/.exec(output.trim())?.[1];\n return pid ? Number(pid) : undefined;\n};\n\nconst NOT_FOUND_WARNING =\n \"The log file is neither where it was asked for nor under the simulator's data directory. \" +\n \"Set IOS_SIMULATOR_OUTPUT_DIR to a directory under your home folder, which the simulator \" +\n \"writes to as-is.\";\n\n/**\n * Find the redirect, giving the simulator a moment to create it. `simctl\n * launch` returns once the app is spawned, and the file is opened as part of\n * that spawn, so the first look nearly always finds it; the retries only cover\n * a slow spawn rather than a known race.\n */\nconst locate = async (\n client: SimulatorClient,\n target: SimulatorSummary,\n path: string,\n): Promise<{ path: string; found: boolean }> => {\n for (let attempt = 0; ; attempt += 1) {\n const found = await client.locateRedirect(target, path);\n if (found.found || attempt >= 4) return found;\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n};\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. Pass `capture_logs: \" +\n \"true` to also get its Logger/os_log output, in one file read with \" +\n \"ios_simulator_read_logs.\",\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 capture_logs: z\n .boolean()\n .default(false)\n .describe(\n \"Write stdout, stderr and every Logger/os_log message to one fresh file, for \" +\n \"ios_simulator_read_logs. Sets OS_ACTIVITY_DT_MODE=YES in the app's environment, \" +\n \"which is what Xcode sets to mirror unified logging onto stderr; without it an \" +\n \"app's Logger output reaches no file at all.\",\n ),\n }),\n annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false },\n },\n async ({ device, bundle_id, arguments: args, terminate_first, capture_logs }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n const effective = args ?? launchArgs;\n const said = {\n ...(effective.length > 0 ? { arguments: effective } : {}),\n ...(args === undefined && launchArgs.length > 0\n ? { argumentsFrom: \"IOS_SIMULATOR_LAUNCH_ARGS\" }\n : {}),\n };\n\n if (capture_logs) {\n // One file for both streams, fresh per launch: the redirect appends,\n // so reusing a path would hand the reader the previous run's lines\n // first. Interleaving in one file is what lets one cursor follow\n // print(), stderr and Logger in the order they happened.\n const stamp = new Date().toISOString().replaceAll(\":\", \"-\");\n const requested = join(outputDir, \"logs\", `${bundle_id}-${stamp}.log`);\n const output = await client.simctl.launch(target.id, bundle_id, {\n args: effective,\n terminateExisting: terminate_first,\n stdout: requested,\n stderr: requested,\n env: { OS_ACTIVITY_DT_MODE: \"YES\" },\n });\n const pid = parsePid(output);\n const log = await locate(client, target, requested);\n client.captures.record({\n udid: target.id,\n simulatorName: target.name,\n bundleId: bundle_id,\n path: log.path,\n pid,\n startedAt: new Date().toISOString(),\n });\n return {\n launched: bundle_id,\n ...(pid !== undefined ? { pid } : {}),\n ...said,\n capturingLogs: true,\n log: log.path,\n ...(log.found ? {} : { warning: NOT_FOUND_WARNING }),\n };\n }\n\n const stdout = join(outputDir, `${bundle_id}.out.log`);\n const stderr = join(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 = parsePid(output);\n const [out, err] = await Promise.all([\n locate(client, target, stdout),\n locate(client, target, stderr),\n ]);\n return {\n launched: bundle_id,\n ...(pid !== undefined ? { pid } : {}),\n ...said,\n // Appended to across launches, unlike a `capture_logs` file.\n logs: { stdout: out.path, stderr: err.path },\n ...(out.found && err.found ? {} : { warning: NOT_FOUND_WARNING }),\n };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_uninstall\",\n {\n title: \"iOS Simulator: Uninstall\",\n description:\n \"Remove an app from the simulator, **with its data container** — documents, databases \" +\n \"and preferences all go, and nothing brings them back. To get back to a cold start while \" +\n \"keeping the data, use ios_simulator_terminate. To test a genuine first launch of one \" +\n \"app, this is narrower than ios_simulator_erase, which wipes every app on the simulator. \" +\n \"Uninstalling an app that is not installed also succeeds, so success does not prove it \" +\n \"was there.\",\n inputSchema: z.object({\n device: deviceArg,\n bundle_id: bundleIdArg,\n confirm: z\n .literal(true)\n .describe(\"Must be true. Explicit acknowledgement that the app's data is deleted.\"),\n }),\n annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true },\n },\n async ({ device, bundle_id }) =>\n wrap(async () => {\n const target = client.requireBooted(await client.resolveTarget(device));\n await client.simctl.uninstall(target.id, bundle_id);\n return { uninstalled: bundle_id, udid: target.id };\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 a window on the device — Simulator.app, or DeviceHub on Xcode 27 — so the \" +\n \"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 // A window that will not open is a warning, not a failed boot: the boot\n // has already been accepted, and reporting an error over a simulator\n // that is coming up sends the caller to retry something that worked.\n let windowWarning: string | undefined;\n let windowNote: string | undefined;\n if (open_window) {\n await client.simctl\n .openApp(client.openPath)\n .then((app) => {\n // DeviceHub shows one device per window and keeps showing the\n // last one picked; nothing outside it can switch that.\n if (app === \"DeviceHub\") {\n windowNote =\n `Opened DeviceHub. It may still be showing another device: select ` +\n `${target.name} in its sidebar to watch this one.`;\n }\n })\n .catch((err: unknown) => {\n windowWarning = `Booted, but no window could be opened: ${\n err instanceof Error ? err.message : String(err)\n }`;\n });\n }\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 ...(windowNote ? { note: windowNote } : {}),\n ...(observed === \"Booted\"\n ? windowWarning\n ? { warning: windowWarning }\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. Irreversible, hence `confirm`; ios_simulator_uninstall is the \" +\n \"narrower way to a first launch of one app. A booted simulator is shut down first and \" +\n \"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 { open, readdir, readFile } from \"node:fs/promises\";\nimport { isAbsolute, join, relative, resolve, sep } from \"node:path\";\n\nimport { crashKind, crashProcessName } from \"@mgcrea/mcp-ios-core\";\n\nimport { IosError } from \"#/client/errors\";\n\n/**\n * Simulator crash reports, out of this Mac's own DiagnosticReports.\n *\n * A simulator app is a Mac process, so when it crashes the Mac's ReportCrash\n * writes the `.ips` — into `~/Library/Logs/DiagnosticReports`, older ones under\n * `Retired/`, mixed in with every Mac app's and daemon's. On the machine this\n * was written against that was 593 `.ips` files, 464 of them from simulators,\n * and nearly all of those were the simulators' own daemons rather than an app\n * anyone was debugging.\n *\n * Telling the two apart, as measured on Xcode 27.0 with an iOS 27.0 runtime:\n *\n * - The header line's `platform` is the Mach-O platform of the crashed binary.\n * Every simulator report had 7 (`PLATFORM_IOSSIMULATOR` in\n * `<mach-o/loader.h>`) and every Mac report had 1, with no exceptions either\n * way. That is one line of a few hundred bytes, so it is the filter.\n * - The header's `is_simulated` is **not** the marker, despite the name: it\n * was set on 13 reports, every one a Mac process with platform 1 — Safari,\n * Mail and two of their helpers.\n * - `procPath` does not say either: it is anonymised to `/Volumes/VOLUME/…` or\n * `/Users/USER/…` with the middle starred out, so no device path survives.\n * - Which simulator is in the report body, as `coalitionName`:\n * `com.apple.CoreSimulator.SimDevice.<UDID>`. Its key ended by byte 1,731 in\n * every one of the 464, so reading the first `HEAD_BYTES` finds it without\n * parsing reports that run to 80 KB.\n */\n\n/** `PLATFORM_*SIMULATOR` in `<mach-o/loader.h>`: iOS, tvOS, watchOS, visionOS. Only 7 was seen. */\nconst SIMULATOR_PLATFORMS = new Set([7, 8, 9, 12]);\n\n/** Enough for the header and the body's `coalitionName`, with room to spare. */\nconst HEAD_BYTES = 8192;\n\nconst COALITION =\n /\"coalitionName\"\\s*:\\s*\"com\\.apple\\.CoreSimulator\\.SimDevice\\.([0-9A-Fa-f-]{36})\"/;\n\n/** Opened at once. The directory holds hundreds of files; this keeps clear of EMFILE. */\nconst CONCURRENCY = 32;\n\nexport type CrashReportRow = {\n /** Relative to the reports directory, e.g. `Retired/Canopy-2026-10-02-213409.ips`. */\n name: string;\n process: string;\n bundleId: string | undefined;\n kind: string;\n /** As the report writes it, e.g. `2026-10-08 23:28:26.00 +0200`. */\n timestamp: string | undefined;\n /** The simulator it came from, when the body named one. */\n udid: string | undefined;\n};\n\ntype Header = {\n platform?: number;\n bug_type?: string;\n app_name?: string;\n name?: string;\n bundleID?: string;\n timestamp?: string;\n};\n\nconst readHead = async (path: string): Promise<string> => {\n const handle = await open(path, \"r\");\n try {\n const buffer = Buffer.alloc(HEAD_BYTES);\n const { bytesRead } = await handle.read(buffer, 0, HEAD_BYTES, 0);\n return buffer.subarray(0, bytesRead).toString(\"utf8\");\n } finally {\n await handle.close();\n }\n};\n\nconst parseHeader = (text: string): Header | undefined => {\n const newline = text.indexOf(\"\\n\");\n try {\n return JSON.parse(newline === -1 ? text : text.slice(0, newline)) as Header;\n } catch {\n return undefined;\n }\n};\n\n/** `2026-10-08 23:28:26.00 +0200` → epoch ms, for ordering. File mtimes are not reliable here. */\nconst timestampMs = (value: string | undefined): number => {\n const m = /^(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?) ([+-]\\d{2})(\\d{2})$/.exec(\n value ?? \"\",\n );\n return m ? Date.parse(`${m[1]}T${m[2]}${m[3]}:${m[4]}`) : 0;\n};\n\nconst listIps = async (dir: string): Promise<string[]> => {\n const top = await readdir(dir).catch((err: NodeJS.ErrnoException) => {\n if (err.code === \"ENOENT\") return [];\n throw err;\n });\n const retired = await readdir(join(dir, \"Retired\")).catch(() => []);\n return [\n ...top.filter((name) => name.endsWith(\".ips\")),\n ...retired.filter((name) => name.endsWith(\".ips\")).map((name) => `Retired/${name}`),\n ];\n};\n\n/**\n * Every simulator report in `dir`, newest first. Reads the first 8 KB of each\n * `.ips`, never the whole file, except to find the UDID in the rare report\n * whose `coalitionName` did not fit in that.\n */\nexport const listSimulatorCrashReports = async (dir: string): Promise<CrashReportRow[]> => {\n const names = await listIps(dir);\n const rows: CrashReportRow[] = [];\n for (let i = 0; i < names.length; i += CONCURRENCY) {\n const batch = await Promise.all(\n names.slice(i, i + CONCURRENCY).map(async (name): Promise<CrashReportRow | undefined> => {\n const path = join(dir, name);\n const head = await readHead(path).catch(() => \"\");\n const header = parseHeader(head);\n if (!header || !SIMULATOR_PLATFORMS.has(Number(header.platform))) return undefined;\n let udid = COALITION.exec(head)?.[1];\n if (!udid) udid = COALITION.exec(await readFile(path, \"utf8\").catch(() => \"\"))?.[1];\n return {\n name,\n process: header.app_name || header.name || crashProcessName(name),\n bundleId: header.bundleID,\n kind: crashKind(header.bug_type),\n timestamp: header.timestamp,\n udid: udid?.toUpperCase(),\n };\n }),\n );\n for (const row of batch) if (row) rows.push(row);\n }\n return rows.toSorted((a, b) => timestampMs(b.timestamp) - timestampMs(a.timestamp));\n};\n\n/**\n * The absolute path of a report named by `ios_simulator_list_crash_logs`, or a\n * refusal. The schema already rejects `..` and absolute names; this is the\n * check that cannot be argued with, made on the resolved path.\n */\nexport const crashReportPath = (dir: string, name: string): string => {\n const root = resolve(dir);\n const path = resolve(root, name);\n const rel = relative(root, path);\n if (rel === \"\" || rel === \"..\" || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {\n throw new IosError(`\"${name}\" is not inside the crash reports directory.`, {\n remedy: \"Pass a `name` exactly as ios_simulator_list_crash_logs returned it.\",\n });\n }\n return path;\n};\n\n/** Whether a report's header says it came from a simulator. */\nexport const isSimulatorReport = (text: string): boolean => {\n const header = parseHeader(text);\n return header !== undefined && SIMULATOR_PLATFORMS.has(Number(header.platform));\n};\n\n/** The simulator a full report came from, when its body says. */\nexport const reportUdid = (text: string): string | undefined =>\n COALITION.exec(text)?.[1]?.toUpperCase();\n","import { readFile } from \"node:fs/promises\";\n\nimport { DEFAULT_CRASH_KINDS, readLogFile, summarizeCrashReport } from \"@mgcrea/mcp-ios-core\";\nimport type { McpServer } from \"@modelcontextprotocol/server\";\nimport { z } from \"zod\";\n\nimport {\n crashReportPath,\n isSimulatorReport,\n listSimulatorCrashReports,\n reportUdid,\n} from \"#/client/crash\";\nimport { IosError } from \"#/client/errors\";\nimport type { SimulatorClient } from \"#/client/simulator\";\nimport type { ToolContext } from \"#/tools/index\";\nimport { deviceArg, wrap } from \"#/tools/util\";\n\n/** UDID → name, for labelling reports. Best effort: a Mac with no Xcode still has reports. */\nconst simulatorNames = async (client: SimulatorClient): Promise<Map<string, string>> => {\n try {\n return new Map((await client.listSimulators()).map((sim) => [sim.id.toUpperCase(), sim.name]));\n } catch {\n return new Map();\n }\n};\n\n/**\n * Crash reports: the half of \"logs\" that needs nothing launched. Observing, so\n * registered whatever the write gate says — reading a report changes nothing\n * on the simulator, the same way a screenshot does not.\n */\nexport const registerCrashLogTools = (\n server: McpServer,\n client: SimulatorClient,\n ctx: ToolContext,\n): void => {\n const dir = ctx.config.crashReportsDir;\n\n server.registerTool(\n \"ios_simulator_list_crash_logs\",\n {\n title: \"iOS Simulator: List Crash Logs\",\n description:\n \"List crash reports from simulator processes, newest first. A simulator app's crash is \" +\n \"written by this Mac into ~/Library/Logs/DiagnosticReports alongside the Mac's own \" +\n \"crashes; this keeps only the simulators' and says which simulator each came from. Most \" +\n \"of them are the simulators' own system daemons, so pass `process` to find your app's. \" +\n \"Read one with ios_simulator_get_crash_log. Nothing needs to be booted.\",\n inputSchema: z.object({\n device: deviceArg.describe(\n \"Only reports from this simulator, by UDID or name as shown by ios_simulator_list. \" +\n \"Omit for every simulator's — unlike the other tools, this does not default to the \" +\n \"booted one, since a crash often outlives the boot it happened in.\",\n ),\n process: z\n .string()\n .optional()\n .describe(\n 'Case-insensitive substring of the process name or bundle id, e.g. \"Canopy\" or ' +\n '\"io.mgcrea\". The process name is the app\\'s executable name, usually but not ' +\n \"always its display name.\",\n ),\n kinds: z\n .array(z.string())\n .optional()\n .describe(\n 'Report kinds to include, e.g. [\"crash\"]. Defaults to [\"crash\", \"jetsam\", ' +\n '\"user_fault\"]; kinds without a name come back as \"bug_type_<n>\". Pass [\"all\"] ' +\n \"for everything.\",\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(200)\n .default(20)\n .describe(\"Maximum reports to return, newest first (1-200). Defaults to 20.\"),\n }),\n annotations: { readOnlyHint: true },\n },\n async ({ device, process, kinds, limit }) =>\n wrap(async () => {\n const target = device ? await client.resolveTarget(device) : undefined;\n const wanted = kinds ?? DEFAULT_CRASH_KINDS;\n const needle = process?.toLowerCase();\n const all = await listSimulatorCrashReports(dir);\n const matching = all.filter(\n (report) =>\n (!target || report.udid === target.id.toUpperCase()) &&\n (wanted.includes(\"all\") || wanted.includes(report.kind)) &&\n (!needle ||\n report.process.toLowerCase().includes(needle) ||\n (report.bundleId?.toLowerCase().includes(needle) ?? false)),\n );\n const names = target ? undefined : await simulatorNames(client);\n return {\n ...(target ? { simulator: target.name, udid: target.id } : {}),\n reports: matching.slice(0, limit).map(({ udid, bundleId, ...row }) => ({\n ...row,\n ...(bundleId ? { bundleId } : {}),\n // Per row only when the list spans simulators; otherwise it is the\n // same value on every row and already said once above.\n ...(names && udid\n ? { udid, ...(names.get(udid) ? { simulator: names.get(udid) } : {}) }\n : {}),\n })),\n total: matching.length,\n };\n }),\n );\n\n server.registerTool(\n \"ios_simulator_get_crash_log\",\n {\n title: \"iOS Simulator: Get Crash Log\",\n description:\n \"Read one simulator crash report and return what matters in it: the exception, why the \" +\n \"process was terminated, any application-specific message, and the faulting thread's \" +\n \"top frames with library names resolved. The full report's path is returned too; it is \" +\n \"often tens of kilobytes, so read it only when the summary is not enough. A Swift fatalError's \" +\n \"message may not be in the report at all — on a simulator it goes to the app's stderr.\",\n inputSchema: z.object({\n name: z\n .string()\n .regex(\n /^[^/][^\\0]*\\.ips$/,\n \"Pass a `name` exactly as ios_simulator_list_crash_logs returned it.\",\n )\n .refine((value) => !value.split(\"/\").includes(\"..\"), \"`name` cannot contain `..`.\")\n .describe(\n \"The report's `name` from ios_simulator_list_crash_logs, e.g. \\\"Canopy-2026-10-08-\" +\n '101500.ips\" or \"Retired/Canopy-2026-10-02-213409.ips\". Older reports move under ' +\n \"Retired/, so a name that worked yesterday may need listing again.\",\n ),\n }),\n annotations: { readOnlyHint: true },\n },\n async ({ name }) =>\n wrap(async () => {\n const path = crashReportPath(dir, name);\n let text: string;\n try {\n text = await readFile(path, \"utf8\");\n } catch {\n throw new IosError(`No crash report named ${name}.`, {\n remedy:\n \"Run ios_simulator_list_crash_logs again: reports move to Retired/ as they age, \" +\n \"and the system deletes old ones.\",\n });\n }\n if (!isSimulatorReport(text)) {\n throw new IosError(`${name} is a crash report from this Mac, not from a simulator.`, {\n remedy:\n \"Pick a report from ios_simulator_list_crash_logs, which lists only simulators'.\",\n });\n }\n const summary = summarizeCrashReport(text);\n const udid = reportUdid(text);\n const simulator = udid ? (await simulatorNames(client)).get(udid) : undefined;\n const asi = summary.crash?.[\"applicationSpecificInfo\"];\n return {\n name,\n path,\n ...(udid ? { udid } : {}),\n ...(simulator ? { simulator } : {}),\n ...summary,\n ...(summary.crash\n ? asi\n ? {}\n : {\n note:\n \"The report carries no application-specific message. A Swift fatalError's \" +\n \"text goes to the app's stderr on a simulator\" +\n (ctx.allowWrites\n ? \", so relaunch with ios_simulator_launch `capture_logs: true` and read \" +\n \"it with ios_simulator_read_logs.\"\n : \".\"),\n }\n : { note: \"Not a crash report with a faulting thread; read the file at `path`.\" }),\n };\n }),\n );\n};\n\n/**\n * Reading an app's captured log. Registered with the launch tool it depends on,\n * behind the write gate: without `ios_simulator_launch` there is never anything\n * to read.\n */\nexport const registerLogReadTools = (server: McpServer, client: SimulatorClient): void => {\n server.registerTool(\n \"ios_simulator_read_logs\",\n {\n title: \"iOS Simulator: Read Logs\",\n description:\n \"Read an app's log output: stdout, stderr and every Logger/os_log message the process \" +\n \"emits, interleaved in one file. Only available for an app started with \" +\n \"ios_simulator_launch `capture_logs: true`. Returns the newest `limit` lines plus a \" +\n \"`next` cursor; pass it back to get only what arrived since, which is how to see what \" +\n \"one tap logged. `capturing` turns false once the app's process has exited, and what it \" +\n \"wrote stays readable. A crash's backtrace is not in here — see \" +\n \"ios_simulator_list_crash_logs — but its fatalError message is.\",\n inputSchema: z.object({\n device: deviceArg,\n bundle_id: z\n .string()\n .optional()\n .describe(\n 'Which app\\'s capture, e.g. \"io.mgcrea.Canopy\". Defaults to the most recent capture.',\n ),\n cursor: z\n .number()\n .int()\n .min(0)\n .optional()\n .describe(\n \"The `next` value from the previous read, to get only newer lines. Omit to read \" +\n \"from the start of the capture.\",\n ),\n filter: z\n .string()\n .optional()\n .describe(\n 'Case-insensitive regular expression a line must match, e.g. \"error|fail\" or ' +\n '\"\\\\[Sync\\\\]\". Applied before `limit`, so it finds matches across the whole range.',\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(2000)\n .default(200)\n .describe(\n \"Most lines to return; the newest are kept and the rest counted in `omitted`. \" +\n \"Defaults to 200 — filter rather than raising this.\",\n ),\n }),\n annotations: { readOnlyHint: true },\n },\n async ({ device, bundle_id, cursor, filter, limit }) =>\n wrap(async () => {\n // Resolved only when named: the file is on this Mac and outlives the\n // boot, so reading it should not fail because the simulator shut down.\n const target = device ? await client.resolveTarget(device) : undefined;\n const capture = client.captures.find(target?.id, bundle_id);\n if (!capture) {\n throw new IosError(\n bundle_id ? `No log capture for ${bundle_id}.` : \"No log capture has been started.\",\n {\n remedy:\n 'Launch the app with ios_simulator_launch {\"bundle_id\":\"…\",\"capture_logs\":true}. ' +\n \"A capture belongs to this server process, so one started before it restarted \" +\n \"is not known here, though its file is still under the output directory.\",\n },\n );\n }\n const running = client.captures.running(capture);\n let result: Awaited<ReturnType<typeof readLogFile>>;\n try {\n result = await readLogFile(capture.path, { cursor, filter, limit, complete: !running });\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n throw new IosError(`The log file for ${capture.bundleId} is gone: ${capture.path}`, {\n remedy:\n \"Erasing the simulator deletes it when it was written under the simulator's data \" +\n \"directory. Launch again with `capture_logs: true`.\",\n });\n }\n return {\n bundleId: capture.bundleId,\n simulator: capture.simulatorName,\n capturing: running,\n ...(capture.pid !== undefined ? { pid: capture.pid } : {}),\n ...result,\n path: capture.path,\n ...(result.skippedBytes > 0\n ? {\n note:\n `${result.skippedBytes} bytes before this window were not read. Pass the ` +\n \"returned `next` sooner, or read the file at `path`.\",\n }\n : {}),\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 { registerCrashLogTools, registerLogReadTools } from \"#/tools/logs\";\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 * Reading logs splits across the line. Crash reports are files this Mac\n * already wrote, so listing and reading them is observing and survives the\n * gate. `ios_simulator_read_logs` is read-only too, but it only ever reads what\n * `ios_simulator_launch` started capturing, so it goes where launch goes — a\n * tool that can only answer \"no capture\" is noise in the list.\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 registerCrashLogTools(server, client, ctx);\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 registerLogReadTools(server, client);\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 type { ProcessAlive } from \"#/client/logs\";\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 /** Override the launched-app liveness probe (tests): no real pid is signalled. */\n processAlive?: ProcessAlive;\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`, `spawnRunner` and\n * `processAlive`, plus `crashReportsDir` in the config — are the whole reason\n * the test suite can drive real tools through the real SDK with no simulator,\n * 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.processAlive ? { processAlive: opts.processAlive } : {}),\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` and `ios_simulator_uninstall` still\n * require an explicit `confirm`, because they are the irreversible ones.\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 /**\n * Saved screenshots, launch logs and the runner log. A launch log is opened\n * by the simulator rather than by this server, and a simulator has its own\n * `/tmp` and `/var/folders` — under this default it lands in the\n * simulator's data directory, and `launch` reports where it really is.\n */\n outputDir: z.string().min(1).default(join(tmpdir(), \"mcp-ios-simulator\")),\n /**\n * Where this Mac's ReportCrash writes `.ips` files, simulator apps' included.\n * Configurable for tests and for a report folder copied off another Mac.\n */\n crashReportsDir: z\n .string()\n .min(1)\n .default(join(homedir(), \"Library\", \"Logs\", \"DiagnosticReports\")),\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 crashReportsDir: pick(trimmed(env.IOS_SIMULATOR_CRASH_REPORTS_DIR), \"crashReportsDir\"),\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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"}