anthropics / anthropics/claude-code

iOS Simulator MCP/panel: attach reports a device it does not route actions to — taps land on the wrong simulator, and shut-down or deleted devices are reported as attached

Offen
#88,217 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
area:desktop area:mcp bug has repro platform:macos
Vorherrschende Sprache
Python
Sterne
145k
Forks
23.1k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

### Description

This report covers three likely related device-state and targeting defects in the Desktop app.s iOS Simulator panel / `mcp__Claude_Code_iOS_Simulator__control`. The observed common symptom is that **the device `attach` reports is not always the device that subsequent `udid`-less actions resolve to**. Separately, `attach` neither validates the target.s boot state nor reliably replaces or clears a previous target.

The practical consequence is that an agent can be told "the panel is on device X", then send taps and swipes to device Y without any error — including a simulator currently being used by a *different* Claude Desktop session, since simulator state is machine-global. The tool does not expose per-session device ownership, so this is an observed cross-session risk rather than a claim about an internal ownership model.

All three were reproduced from a clean state on throwaway devices created with `simctl create`, verbatim tool output below.

---

#### 1. `attach {udid: X}` claims success but does not become the default target

```
> control { action: "attach", udid: "4D388256-…" } # ReproB, booted
< Simulator panel opened for ReproB (4D388256-190C-41FD-8156-004EEE2A69AE).
Coordinate space for tap/swipe: 402x874 points (origin top-left).

> control { action: "screenshot" } # no udid
< Screenshot of iPhone 17 Pro (5800AF4B-90D7-4F28-A8EC-80C8E2AE4B75). # ← different device
```

Passing `udid` on the action itself resolves correctly, which isolates this to default-device resolution rather than the capture path:

```
> control { action: "screenshot", udid: "149E7DAB-…" }
< Screenshot of 149E7DAB-BA80-462E-9D0E-E5E8840997D4. # ← correct
```

Input actions follow the same wrong route. Immediately after `attach` reported `ReproC`:

```
> control { action: "tap", x: 157, y: 843 }
< Tapped at (157, 843) on iPhone 17 Pro (5800AF4B-90D7-4F28-A8EC-80C8E2AE4B75).
```

`attach` appears to *append* to a set rather than switch, which `detach` confirms — one call tears down every device attached so far, including ones the session never asked for:

```
> control { action: "detach" }
< Detached iPhone 17 Pro (5800AF4B-…), ReproB (4D388256-…), ReproC (59CA0F86-…).
```

With exactly one device in that set the resolution is correct; with more than one it drifts to another member. So the bug only shows up once a session has attached twice — which is the normal flow when switching devices.

#### 2. `attach` on an existing-but-shut-down device reports success and does not boot it

```
$ xcrun simctl shutdown 149E7DAB-… # device is Shutdown, verified via simctl

> control { action: "attach", udid: "149E7DAB-…" }
< Simulator panel already attached to ClaudeReproDevice (149E7DAB-…).
Coordinate space for tap/swipe: 402x874 points (origin top-left).

$ xcrun simctl list devices | grep 149E7DAB
ClaudeReproDevice (149E7DAB-…) (Shutdown) # ← attach did not boot it

> control { action: "screenshot" }
< screenshot failed: Error Domain=com.facebook.FBSimulatorControl Code=0
"No Image available to encode"
```

The success message carries a coordinate space, so it reads as a fully working attach. Another device (`iPhone 17 Pro`) was booted the whole time and was never offered as a fallback.

This also contradicts the panel's own onboarding copy, which states verbatim that **"shut-down devices are booted automatically"** (ko: `종료된 기기는 자동으로 부팅됩니다`). In the reproduced MCP `attach` path, this automatic boot does not occur.

Note that a genuinely nonexistent UDID *is* rejected properly, so the existence check runs and the boot-state check simply doesn't:

```
> control { action: "attach", udid: "00000000-DEAD-BEEF-0000-000000000000" }
< Could not attach simulator panel: Failed to read the device's state:
No simulator with UDID 00000000-DEAD-BEEF-0000-000000000000
```

#### 3. A deleted device stays pinned for the lifetime of the panel, and an explicit re-attach does not clear it

```
$ xcrun simctl delete 149E7DAB-… # another device remains booted

> control { action: "attach" }
< Simulator panel already attached to ClaudeReproDevice (149E7DAB-…) # ← device no longer exists

> control { action: "screenshot" }
< screenshot failed: No simulator with UDID 149E7DAB-BA80-462E-9D0E-E5E8840997D4

> control { action: "attach", udid: "5800AF4B-…" } # valid, booted
< Simulator panel opened for iPhone 17 Pro (5800AF4B-…) # reports success

> control { action: "screenshot" }
< screenshot failed: No simulator with UDID 149E7DAB-BA80-462E-9D0E-E5E8840997D4 # ← still pinned
```

Only `detach` followed by `attach` recovers:

```
> control { action: "detach" }
> control { action: "attach", udid: "5800AF4B-…" }
> control { action: "screenshot" }
< Screenshot of iPhone 17 Pro (5800AF4B-…) # ← recovered
```

The same failure is visible in the panel UI, not just over MCP. During this investigation a second Desktop session's panel silently latched onto one of the throwaway devices as soon as it booted — the session never asked for it — and when that device was deleted the panel froze on:

```
기기 상태를 읽지 못했습니다: UDID 59CA0F86-898D-4FEF-A820-86DB9F0800EC에 해당하는 시뮬레이터가 없습니다.
(Failed to read the device's state: no simulator matching UDID 59CA0F86-…)
```

with only a "Connect simulator" button, which re-runs the same doomed attach. Meanwhile the adjacent panel in the same window was streaming a healthy `iPhone 17 Pro` at 60 FPS. Two panels, same machine, same moment: one fine, one stuck on a deleted UDID the user has no way to recognise.

Worse, that dead panel is **orphaned from the backend**. After a later `detach`, the tool reports only the live device:

```
> control { action: "detach" }
< Detached iPhone 17 Pro (5800AF4B-…) # the dead panel is not listed
```

yet the dead panel is still on screen, still pinned to `59CA0F86-…`, and its "Connect simulator" button still retries that UDID. The backend has forgotten it, so no MCP call — `detach`, `attach`, or otherwise — can clear it. The only remaining action is closing the panel by hand with the X.

This is how a panel instance gets stuck: an attached device can disappear through `simctl delete`, a runtime reinstall, or another session cleaning up. After that, the panel remains unusable for the lifetime of that panel instance, with an error that names a UDID the user cannot map to anything, while the documented escape hatch — attaching by UDID — silently fails to help.

### Repro steps

1. `xcrun simctl create A "iPhone 17"` and `xcrun simctl create B "iPhone 17"`, boot both.
2. `control { action: "attach", udid: A }` → reports panel opened for A.
3. `control { action: "attach", udid: B }` → reports panel opened for B.
4. `control { action: "screenshot" }` (no `udid`) → returns **A**, not B. Same for `tap`.
5. `xcrun simctl shutdown B`, then `control { action: "attach", udid: B }` → reports "already attached", B stays `Shutdown`, `screenshot` fails with `No Image available to encode`.
6. `xcrun simctl delete B`, then `control { action: "attach" }` → still reports "already attached" to the deleted B; `attach` with a valid booted UDID does not clear it; only `detach` + `attach` does.

### Expected

- `attach {udid: X}` should make X the target for subsequent `udid`-less actions, or fail loudly. It should never report success for a device it is not going to route actions to.
- `attach` on a `Shutdown` device should either boot it or return an error naming the boot state, not "already attached" with a coordinate space.
- A panel should not auto-adopt a device the session never asked for, since that is what silently hands a session a device that some other workflow is about to delete.
- When the attached device disappears, the panel should drop the stale UDID and either fall back to a booted device or return an actionable error (e.g. list the available devices). At minimum, an explicit `attach {udid: }` must clear the dead pin without requiring `detach` first.
- `screenshot`'s `No Image available to encode` should be replaced by a message that names the cause (device is shut down / device no longer exists), since today the same opaque string appears for unrelated failures — see #81864.

### Environment

- Claude Desktop 1.32885.1
- macOS 27.0 beta (26A5416b), MacBook Air M4 (Mac16,12)
- Xcode 26.6 (17F113), `xcode-select` → `/Applications/Xcode.app/Contents/Developer`
- iOS 26.5 (23F77) simulator runtime
- No `claude-ios-sim` crash reports during any of this — the helper stayed alive throughout, so this is unrelated to the Metal crash-loop family (#80177 / #80472 / #83011).

### Related

- #81864 — panel does not recover after host reboot, also surfaces `No Image available to encode`. Same "panel does not renegotiate state" family, different trigger (host reboot vs. device shutdown/deletion) and, unlike that report, here the underlying capture path is provably healthy for other devices at the same moment.

Beitragsleitfaden

Für dieses Repository ist kein Beitragsleitfaden indexiert

Rechercherichtung

Start at the iOS Simulator panel and the MCP `control` entry point, focusing on `attach`, `detach`, and `udid`-less action resolution. Reproduce the state transitions with `xcrun simctl create`, boot, shutdown, and delete, then exercise screenshot and tap. Done means explicit attachment targets subsequent actions, invalid or shut-down devices fail clearly or recover, and deleted-device pins can be replaced without a separate detach.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
ios, macos
Bereich
desktop-dev, devtools, mobile-dev
Issue-Typ
Bug
Schwierigkeit
5/5
Geschätzter Aufwand
Über eine Woche
Aktivitätsstatus
Aktiv
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
38/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.