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

オープン
#88,217 コメント 1 件 リアクション 0 件 担当者 0 名 GitHub で見る
area:desktop area:mcp bug has repro platform:macos
主要言語
Python
スター
145k
フォーク
23.1k
PR マージ指標
PR 指標を取得中

説明

### 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.

コントリビューションガイド

このリポジトリのコントリビューションガイドは索引されていません

調査の方向性

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.

索引モデルが issue の本文から書いたものです。

評価

技術スタック
ios, macos
領域
desktop-dev, devtools, mobile-dev
issue の種類
バグ
難易度
5/5
見積もり時間
1週間以上
活発さ
活発
明瞭さ
おおむね明確
初心者へのやさしさ
38/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。