conductor-remote 1.41.0 → 1.42.0

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.
package/README.md CHANGED
@@ -198,6 +198,80 @@ RELAY_TOKEN=$(openssl rand -hex 16) yarn start
198
198
  - ✅ **Push notifications** when an agent finishes its turn or hits an error —
199
199
  see below.
200
200
 
201
+ ## MCP: let your agents drive Conductor
202
+
203
+ `conductor-remote mcp` is an MCP server on stdio. It gives a coding agent the same
204
+ control the phone has, over the same relay.
205
+
206
+ Two transports, same ten tools.
207
+
208
+ **stdio** — for an agent running on this Mac. The client spawns it as a child process;
209
+ there is no URL and nothing is exposed.
210
+
211
+ ```bash
212
+ claude mcp add conductor -- conductor-remote mcp
213
+ ```
214
+
215
+ **HTTP** — for an agent that can only reach a URL, at `POST /mcp` on the relay, gated by
216
+ the same token as `/api/*`.
217
+
218
+ ```bash
219
+ claude mcp add --transport http conductor https://<your-magicdns>/mcp \
220
+ --header "Authorization: Bearer $(cat ~/Library/Application\ Support/conductor-remote/token)"
221
+ ```
222
+
223
+ Either way the relay must be running (`conductor-remote service status`). The stdio
224
+ server reads the persisted token itself, so it needs no configuration at all.
225
+
226
+ **Who can reach the HTTP endpoint depends on `EXPOSE`**, and this is the decision worth
227
+ making deliberately:
228
+
229
+ | `EXPOSE` | fronted by | who reaches `/mcp` |
230
+ |---|---|---|
231
+ | `tailnet` | `tailscale serve` | any device signed into your tailnet, from any network |
232
+ | `public` (default) | `tailscale funnel` | anyone on the internet holding the token |
233
+
234
+ `tailnet` is not LAN-only — a laptop on hotel wi-fi reaches it fine, over WireGuard. But
235
+ a *hosted* client is not on your tailnet: an agent running on someone else's servers
236
+ needs `public`, where the token is the only thing between the internet and
237
+ `create_workspace` / `send_prompt`. Prefer stdio when the agent is local, which is most
238
+ of the time.
239
+
240
+ | tool | what it does |
241
+ |---|---|
242
+ | `search_chats` | full-text search every chat on the Mac, archived included |
243
+ | `read_chat` | a transcript by `session_id` — works for archived workspaces |
244
+ | `list_workspaces` | what is running right now, with status and model |
245
+ | `list_chats` | the chat tabs in a workspace |
246
+ | `workspace_diff` | a workspace's diff against its target branch |
247
+ | `list_repos` | repos a workspace can be created in |
248
+ | `create_workspace` | start a new workspace, optionally with a first prompt |
249
+ | `send_prompt` | send into an existing chat (drives the real UI) |
250
+ | `stop_turn` | cancel a running answer (drives the real UI) |
251
+ | `set_workspace_status` | set the sidebar status (drives the real UI) |
252
+
253
+ The first six touch nothing. `create_workspace` opens a Conductor deep link, so it
254
+ needs no Accessibility and steals no focus. The last three drive Conductor's real
255
+ window for a few seconds.
256
+
257
+ The HTTP transport is deliberately minimal: the server never initiates a message, so
258
+ there is no SSE stream and `GET /mcp` answers 405, which the spec allows. It keeps no
259
+ session either. A request carrying an `Origin` header is refused outright — a real MCP
260
+ client sends none and a browser cannot omit one, which closes the DNS-rebinding hole
261
+ without the relay having to know its own hostname behind Tailscale's TLS.
262
+
263
+ **Two agents cannot collide.** Conductor has one window, so every UI write in the
264
+ relay is serialized by a single process-local lock — which is exactly why these
265
+ tools call the relay instead of driving AppleScript themselves. Agents are marked
266
+ background priority, so they always yield to whoever is holding the phone, and past
267
+ four queued operations a caller is refused with "busy, retry shortly" rather than
268
+ joining a line it would only time out waiting in.
269
+
270
+ Worth knowing before you wire it up: `send_prompt` into a chat that is already
271
+ working **steers that agent** rather than starting a new turn, and `stop_turn`
272
+ destroys work in flight. The tool descriptions say so, and both ask the caller to
273
+ confirm with you first.
274
+
201
275
  ## Notifications
202
276
 
203
277
  Turn them on from the **Connect sheet** (the QR button in the workspace list
package/bin/cli.js CHANGED
@@ -58,6 +58,8 @@ function usage() {
58
58
  '',
59
59
  'Usage:',
60
60
  ' conductor-remote [start] run the relay (default)',
61
+ ' conductor-remote mcp MCP server on stdio — lets an agent search chats,',
62
+ ' read transcripts and drive workspaces via the relay',
61
63
  ' conductor-remote service <subcommand> manage the login LaunchAgent',
62
64
  ' install | uninstall | restart | status',
63
65
  ' conductor-remote logs [-n N] [--no-follow] tail the running relay’s logs (follows by default)',
@@ -89,6 +91,10 @@ switch (cmd) {
89
91
  case 'start':
90
92
  await import(resolveEntry('../dist-node/src/server.js', '../src/server.ts'))
91
93
  break
94
+ case 'mcp':
95
+ // MCP server on stdio: stdout is the JSON-RPC wire, so nothing else may print there.
96
+ await import(resolveEntry('../dist-node/src/mcp.js', '../src/mcp.ts'))
97
+ break
92
98
  case 'service':
93
99
  // service.ts reads its subcommand from argv[2]; re-shape argv so `service install` → `install`.
94
100
  process.argv = [process.argv[0], process.argv[1], ...rest]