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
- Lingua principale
- Python
- Stelle
- 145k
- Fork
- 23.1k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
### 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.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Direzione di ricerca
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.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- ios, macos
- Ambito
- desktop-dev, devtools, mobile-dev
- Tipo di issue
- Bug
- Difficoltà
- 5/5
- Tempo stimato
- Più di una settimana
- Stato di attività
- Attiva
- Chiarezza
- Abbastanza chiara
- Idoneità per principianti
- 38/100