readyroom 0.1.4 → 0.1.5

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.
Files changed (2) hide show
  1. package/README.md +154 -0
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -240,6 +240,160 @@ the stream is required and comes from the path segment or an explicit `stream` t
240
240
  If neither is present, the tool call fails with an error naming both ways to supply it. No
241
241
  default is invented.
242
242
 
243
+ ## MCP Configuration
244
+
245
+ An MCP client reaches readyroom over one of two transports. How you run the server decides
246
+ which one is available.
247
+
248
+ ### Entry points
249
+
250
+ | Command | Target | Transports | State |
251
+ |---|---|---|---|
252
+ | `npx readyroom` | Node, on the host | stdio + Streamable HTTP | `./.state` on disk |
253
+ | `npm start` (`node dist/server.mjs`) | Node, built bundle | stdio + Streamable HTTP | `./.state` on disk |
254
+ | `npm run dev` (`tsx watch --import ./scripts/html-loader-register.mjs src/index.ts`) | Node, development | stdio + Streamable HTTP | `./.state` on disk |
255
+ | `docker run … ghcr.io/therebelrobot/readyroom:latest` | Docker | stdio + Streamable HTTP | volume mount |
256
+ | `npm run dev:worker` (`wrangler dev`) | Cloudflare Worker, local | Streamable HTTP only | Durable Object |
257
+ | `npm run deploy:worker` (`wrangler deploy`) | Cloudflare Worker, edge | Streamable HTTP only | Durable Object |
258
+
259
+ On the Node targets a process resolves into a **daemon** or a **client** (see
260
+ [the singleton](#three-ways-aboard)): the first process becomes the daemon and serves both
261
+ transports on the port; later processes join as clients and serve stdio only, proxying their
262
+ tool calls to the daemon. The role is decided at start — there is nothing to configure.
263
+
264
+ ### Transports
265
+
266
+ - **stdio** — Node targets only. The MCP client spawns the process and speaks the protocol over
267
+ its file descriptors. The server writes nothing to stdout but MCP frames; all logs go to
268
+ stderr. Select it by configuring the client to run a `command`.
269
+ - **Streamable HTTP** — every target. On Node the daemon mounts it at `POST /mcp` (methods
270
+ `GET`, `POST`, `DELETE`) on the same port. On the Worker it is the only transport, mounted at
271
+ `/mcp` (stream supplied as the `/mcp/{stream}` path segment). Select it by pointing the client at the URL.
272
+
273
+ The two are not exclusive on Node: one daemon serves stdio and HTTP at once, so one agent can
274
+ run over stdio and another over HTTP against the same queue.
275
+
276
+ #### The `/mcp` endpoint is Origin-guarded
277
+
278
+ Both HTTP mounts reject a request whose `Origin` header is missing or not on the allowlist,
279
+ with `403`. On Node the allowlist is built from the server's own origins — `http://127.0.0.1:<port>`
280
+ and `http://localhost:<port>` — plus any `READYROOM_ALLOWED_ORIGINS` entries. On the Worker it
281
+ is the Worker's own origin plus the same extras. See `src/security.ts`.
282
+
283
+ > [!NOTE]
284
+ > An HTTP MCP client must send an `Origin` header that matches the allowlist. A client that
285
+ > sends no `Origin` at all is rejected. If your client sends a different origin (or none), add
286
+ > it to `READYROOM_ALLOWED_ORIGINS`.
287
+
288
+ #### Streams over HTTP
289
+
290
+ On Node the stream defaults to `READYROOM_STREAM`, else `basename(cwd)`, and can be overridden
291
+ per call with the `stream` tool argument. On the Worker there is no `cwd`: the stream comes from
292
+ the `POST /mcp/{stream}` path segment, or an explicit `stream` argument. If neither is present,
293
+ the call fails with an error naming both ways to supply it.
294
+
295
+ ### Configuration options
296
+
297
+ All six are read from the process environment on the Node targets, and, on the Worker, only
298
+ `READYROOM_TOKEN` and `READYROOM_ALLOWED_ORIGINS` are read from `[vars]` / `secrets` in
299
+ `wrangler.toml`. `src/config.ts` is the single source of truth.
300
+
301
+ | Variable | Default | Required? | Meaning |
302
+ |---|---|---|---|
303
+ | `READYROOM_STREAM` | `basename(cwd)` | optional | Default stream for tickets. Overridable per call via the `stream` tool argument. Not applicable on the Worker. |
304
+ | `READYROOM_TOKEN` | *(unset)* | **mandatory on Docker and the Worker**, optional on Node | Shared secret for `/api/*` and the Node `/internal/tool-call` proxy. |
305
+ | `READYROOM_BIND` | `127.0.0.1` | optional | Bind address. A non-loopback value refuses to start unless `READYROOM_TOKEN` is set. Not applicable on the Worker. |
306
+ | `READYROOM_STATE_DIR` | `./.state` | optional | Directory holding `tickets.json`, `audit.jsonl`, and `daemon.json`. Not applicable on the Worker. |
307
+ | `READYROOM_PORT` | `8971` | optional | TCP port. Must not collide with 3000-3899, which a worktree helper allocates. Not applicable on the Worker. |
308
+ | `READYROOM_ALLOWED_ORIGINS` | *(unset)* | optional | Comma-separated extra origins allowed on the `/mcp` Origin guard, appended to the server's own origin. |
309
+
310
+ The per-target Worker mapping is in [Environment variables](#environment-variables).
311
+
312
+ ### MCP client configuration
313
+
314
+ #### stdio (Claude Desktop and other MCP clients)
315
+
316
+ ```json
317
+ {
318
+ "mcpServers": {
319
+ "readyroom": {
320
+ "command": "npx",
321
+ "args": ["-y", "readyroom"],
322
+ "env": {
323
+ "READYROOM_STREAM": "my-agent",
324
+ "READYROOM_STATE_DIR": "/home/you/.readyroom"
325
+ }
326
+ }
327
+ }
328
+ }
329
+ ```
330
+
331
+ Every client that spawns readyroom becomes the daemon or a client automatically; you do not set
332
+ the role. Give each agent its own stream with `READYROOM_STREAM` (or the `stream` argument to
333
+ `ask_human`). If any process in a shared daemon group sets `READYROOM_TOKEN`, every process in
334
+ that group must set the same value — the client forwards it to the daemon.
335
+
336
+ For a built or installed copy, replace `command` with the absolute path to `dist/server.mjs`
337
+ (or the `readyroom` binary on `PATH`).
338
+
339
+ #### Streamable HTTP (Node daemon)
340
+
341
+ ```json
342
+ {
343
+ "mcpServers": {
344
+ "readyroom": {
345
+ "url": "http://127.0.0.1:8971/mcp",
346
+ "headers": {
347
+ "Origin": "http://127.0.0.1:8971"
348
+ }
349
+ }
350
+ }
351
+ }
352
+ ```
353
+
354
+ #### Streamable HTTP (Cloudflare Worker)
355
+
356
+ The stream is the path segment, so each agent gets its own URL.
357
+
358
+ ```json
359
+ {
360
+ "mcpServers": {
361
+ "readyroom": {
362
+ "url": "https://readyroom.example.com/mcp/my-agent",
363
+ "headers": {
364
+ "Origin": "https://readyroom.example.com"
365
+ }
366
+ }
367
+ }
368
+ }
369
+ ```
370
+
371
+ ### Auth and secrets
372
+
373
+ `src/security.ts` holds both guards.
374
+
375
+ - **`READYROOM_TOKEN`** protects `/api/*` (the page's API) and, on Node, the `/internal/tool-call`
376
+ proxy between a client process and its daemon. The comparison is constant-time via the shared
377
+ `src/core/constantTimeEqual.ts`, so Node and the Worker get the identical guarantee.
378
+ - **The `/mcp` MCP endpoint is guarded by the `Origin` header, not by the token.** A missing or
379
+ mismatched `Origin` is rejected with `403`; the MCP specification requires this of HTTP
380
+ transports. `READYROOM_ALLOWED_ORIGINS` extends that allowlist.
381
+ - On Docker and the Worker the token is **mandatory**. On Node it is optional for a loopback
382
+ bind and mandatory for any non-loopback bind — the refusal rule in `src/config.ts` enforces it.
383
+
384
+ > [!NOTE]
385
+ > `src/config.ts` and the [Environment variables](#environment-variables) table describe
386
+ > `READYROOM_TOKEN` as the secret for "the MCP endpoint". In the shipped code the token is
387
+ > enforced on `/api/*` (and the Node `/internal/tool-call` proxy) only; the `/mcp` endpoint is
388
+ > authenticated by its `Origin` guard instead. This section documents the code's actual behavior.
389
+
390
+ ##### A note on secrets
391
+
392
+ Treat `READYROOM_TOKEN` as a bearer secret. On the Worker, set it with
393
+ `wrangler secret put READYROOM_TOKEN` and never commit it. On Docker and npx, supply it through
394
+ an `--env-file` or your process manager, not a shell history. Never expose the server to a
395
+ public network directly — see [Never expose this to a public network directly](#never-expose-this-to-a-public-network-directly).
396
+
243
397
  ## Environment variables
244
398
 
245
399
  Five variables, plus one optional extra.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "readyroom",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "An MCP server that lets coding agents request a human decision without hijacking whichever window is focused. Agents enqueue a ticket; the human drains the queue from one page when they choose to.",
5
5
  "type": "module",
6
6
  "keywords": [