[BUG] Desktop SSH session + Remote Control: while the laptop sleeps, claude.ai connectors are unusable from the phone because they execute inside the sleeping Desktop app — answering the PreToolUse permission hook is not enough

Status Open
Reported on v2.1.260
Maintainer reply None cached
Activity 1 comment · opened Sep 16, 2026

Preflight Checklist

  • [x] I have searched existing issues (closest open: #35637, #78784, #49790; #67840 was bot-closed — see Related issues)
  • [x] This is a single bug report
  • [x] Reproduced on the Claude Code build Claude Desktop ships for SSH sessions, ccd-cli 2.1.260 (live-tested 2026-09-16; also seen on 2.1.255/2.1.258)

What's Wrong?

A Claude Code session hosted by Claude Desktop over SSH, with Remote Control enabled, stays drivable from the phone after the laptop sleeps — prompts, Bash, Read, and AskUserQuestion all work, because the CLI itself hosts Remote Control on the remote host. But claude.ai connector calls (Google Drive, Notion, Gmail, calendar, Linear, etc.) cannot complete, and this is structural, not just a timeout:

These connectors execute inside the Desktop app, not on the remote host. Desktop delivers them to the SSH session as SDK-hosted MCP servers (they appear in the session's initialize frame under sdkMcpServers). The remote CLI does not run them; it calls back into the Desktop app to run each tool. When the laptop sleeps, that app is unreachable, so the connector's actual work cannot run — regardless of Remote Control being alive and the remote CLI being healthy.

There are two layers, and fixing only the first does not fix the bug:

  1. The PreToolUse permission hook times out. Every tool Desktop registers a PreToolUse hook for (on 2.1.260: all MCP tools via a mcp__.* matcher, plus Workflow, SendMessage, and a set of mcp__ccd_* session tools) fires a hook_callback the sleeping laptop can't answer. Desktop supplies no per-hook timeout, so the CLI's 600 000 ms default applies and the call fails at exactly 600 s with PreToolUse hook did not respond before its timeout (host client may be unreachable). No prompt reaches the phone, because the failure is one stage before the permission prompt.
  1. Even with the hook answered, the connector still can't run. I verified this directly (see below): I answered the stuck PreToolUse hook in ~20 s with a local shim. The call then proceeded, a permission prompt did reach the phone, I approved it — and the connector call still hung, because executing it requires the sleeping Desktop app. So the missing per-host-unreachable hook timeout is real, but it is not the whole bug. The connector-hosting layer is.

Evidence that answering the hook is insufficient

To isolate the two layers I built a small stdio shim between the claude-ssh daemon and the remote CLI that watches the SDK stream and injects an empty "no opinion" control_response for a Desktop PreToolUse hook_callback once it has been pending 20 s (it never touches can_use_tool, device hooks, timed matchers, or non-PreToolUse events). Live test, 2026-09-16, laptop asleep, driven from the phone, calling Google Drive list_recent_files:

14:41:02  pending      <id> cb=hook_2 (PreToolUse mcp__…__list_recent_files)   ← hook fired; sleeping host can't answer
14:41:23  answered by shim <id>                                                ← shim answered it after 21 s
          → permission prompt then appeared on the phone; I approved it
          → the connector call still hung: its execution needs the awake Desktop app

So: the hook can be answered server-side in seconds instead of 600 s, and that is enough to surface the permission prompt on the phone — but it is not enough to run a Desktop-hosted connector. The session's initialize confirms the connectors are SDK-hosted: sdkMcpServers lists the connector server ids (Drive, Notion, calendar, Gmail, Linear, etc.), i.e. Desktop runs them, not the remote CLI.

Mechanism (from the shipped binaries, logs, and live SDK stream)

  1. The claude-ssh daemon idle-closes a bridge connection after ~5 min with no traffic (internal/server.(*Server).closeWhenIdle; log [Server] closing connection idle %s in both directions). Awake, Desktop reopens it lazily; asleep, the open-bridge count sits at 0 for the whole sleep.
  2. Desktop registers hooks via the SDK initialize request (hooks: { PreToolUse: [{ matcher, hookCallbackIds, timeout? }] }). A matching tool makes the CLI send a hook_callback control request back over stdio → daemon → bridge → SSH → Desktop. With no bridge, nothing answers.
  3. No per-hook timeout is supplied, so the CLI default applies (ccd-cli 2.1.260): let ut = Se.timeout ? Se.timeout*1000 : bf; var bf = 600000; on timeout it stops the tool with PreToolUse hook did not respond before its timeout (host client may be unreachable).
  4. The observed PreToolUse matchers on 2.1.260 (from a live initialize): mcp__.* (all MCP tools); a list of specific mcp__ccd_directory/session_mgmt/scheduled-tasks/sidebar/pr/* tools plus ^SendMessage$/^ListAgents$/^ListPeers$; and ^Workflow$. Edit, Write, and Bash matched no PreToolUse hook on this version, so they are not gated by it and complete normally while asleep (earlier host transcripts did show Write/Edit failing at 600 s, so the registered matcher set has changed across versions; the connector facet is version-independent because it is about where the connector runs).

What Should Happen?

With Remote Control connected, a connector call should either run to completion or fail fast with a clear "Desktop client asleep/unreachable" state — not hang. More fundamentally: a session advertised as usable from the phone should not depend on the laptop staying awake to run its connectors. Either host the connectors somewhere durable (server-side / CLI-connected for SSH sessions), or make it explicit that connectors require the awake host.

Steps to Reproduce

  1. In Claude Desktop, start a session on an SSH host. Enable Remote Control; confirm the session appears in the iOS app.
  2. Close the laptop; wait ≥5 min so the daemon idle-closes the bridge.
  3. From the phone, send a prompt (works), ask for a Bash command (works), trigger AskUserQuestion (renders, answerable).
  4. From the phone, call a claude.ai connector (Drive/Notion/Gmail/calendar/Linear). It hangs; no prompt appears for 600 s, then it fails with the error below (or, if the permission hook is somehow answered, it surfaces a prompt and then still hangs on execution).
  5. Open the laptop; the bridge reconnects within seconds and the call completes.

Error Messages/Logs

PreToolUse hook did not respond before its timeout (host client may be unreachable).
The tool call was not executed; other configured hooks may not have completed.

Daemon log ~/.claude/remote/run/<id>/remote-server.log (open-bridge count in brackets):

2026/09/08 15:01:14 [Server] New connection from: @                            [1]
2026/09/08 15:37:14 [Server] closing connection idle 5m4s in both directions: @  [0]
        <- 1 h 40 min with zero bridge connections
2026/09/08 17:17:55 [Server] New connection from: @                            [1]  laptop woken

Across host transcripts, 23 genuine entrypoint: claude-desktop events failed at ~600 s (17 MCP/connector calls; earlier versions also 4 Write + 2 Edit). Bash/Read/ToolSearch/AskUserQuestion: 0 failures.

Claude Model

Opus / Fable (both observed).

Is this a regression?

No — the connector-hosting dependency and the 600 s hook timeout both predate current versions (#67840 shows the same 600 s failure on 2.1.170).

Claude Code Version

ccd-cli 2.1.260 shipped by Claude Desktop for SSH sessions (also 2.1.255/2.1.258). Latest CLI at time of writing is 2.1.273; nothing in the 2.1.257→2.1.273 changelog addresses hook-callback timeouts, bridge idle-close, or connector hosting.

Platform / OS

Anthropic API. Remote host: Ubuntu 22.04, Linux 5.15 (SLURM compute node reached via an SSH login proxy). Client: Claude Desktop on macOS, steered from the Claude iOS app.

Related issues

  • #35637 (open) — permission prompts not rendering on mobile; later comments report the same ~600 s phantom-timeout signature on Desktop/remote sessions. No maintainer reply; most recent comment: "Remote control is unusable because of this."
  • #67840 (bot-closed, not by a maintainer; flagged as an incorrect closure per #87647) — same 600 000 ms mechanism on 2.1.170.
  • #78784, #49790 (open feature requests) — durable/reattachable remote sessions. These would keep the CLI process alive across disconnects but would not relocate connector execution off the Desktop app, so they would not fix this facet on their own.
  • #71875 (bot-closed) — keep the host awake via OS power APIs; a sidestep, not a fix.
  • #92687, #91034 — partial reattach and a per-reconnect process leak; adjacent, not this.

Suggested fixes (in priority order for the connector facet)

  1. Host claude.ai connectors server-side for SSH sessions (CLI-connected rather than SDK-hosted in Desktop), so a connector's execution does not depend on the laptop being awake. This is the only change that makes connectors usable from the phone while the laptop sleeps.
  2. When the SDK host is unreachable, fail (or hold) the PreToolUse hook callback in seconds instead of 600 s, and route the permission decision to connected Remote Control clients. (Necessary, but insufficient alone — see the shim evidence.)
  3. Surface a clear "Desktop client asleep/unreachable" state on the mobile client instead of a silent 600 s hang.
  4. Don't idle-close the bridge (or make the 5-minute daemon timeout configurable) for sessions with Remote Control on, and reconnect proactively on wake.

Workaround (partial)

Hosting the session with claude remote-control inside tmux on the remote host avoids the Desktop hook and the laptop dependency for server-side work (edits, Bash, and any CLI-connected MCP servers), and stayed up 7+ hours across a sleep. It does not give you the Desktop-hosted claude.ai connectors, because those are only delivered by the Desktop app — the same reason they fail while asleep. So there is currently no way to use the claude.ai connectors from the phone while the laptop sleeps.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗