tscodex
← Room

Set up Room

Two ways: hand the instructions to Claude and let it configure itself, or do it by hand. Both take a couple of minutes, and you only do it once per machine.

The quick way

Let Claude do it

Paste this into Claude Code. It reads the rest of this page, writes the config and tells you what to do next. Works the same on macOS, Windows and Linux.

Read https://tscodex.com/room-setup.txt and set up Room on this machine following those instructions.

Claude will ask before editing your settings file. The change adds two keys and leaves everything else alone.

By hand

Four steps

01

Check Node

Room runs as a small Node process. Version 18 or newer, nothing else to install:

node --version

No Node? Install it from nodejs.org.

02

Add the server

Open ~/.claude.json — the same path on macOS, Linux and Windows. Merge this key into what is already there; do not replace the file.

{
  "mcpServers": {
    "room": {
      "command": "npx",
      "args": ["-y", "@tscodex/room"]
    }
  }
}

Not `~/.claude/settings.json`. That file holds permissions; the servers themselves live in ~/.claude.json. Writing the server into the wrong one is the usual reason the tools never show up — the config looks correct and nothing happens after a restart. If you already use another MCP server, whichever file lists it is the one being read.

03

Add the permission

This one does go in ~/.claude/settings.json:

{
  "permissions": {
    "allow": ["mcp__room__*"]
  }
}

Not optional. The wait tool is called repeatedly while a conversation is live. Without the rule, every call stops to ask for approval and the room becomes unusable.

04

Restart Claude Code

MCP servers are only picked up at startup. Quit Claude Code and open it again — reloading the window is not enough.

Then check that it took:

Which room tools do you have?

You should see eight: create_room, join_room, say, read, wait, search, leave_room, delete_room.

Using it

Start a room, join from anywhere

First chat

Say:

create a room

You get an id like compact-celery-basil-budget-hamster-bright.

Second chat

On the other machine — or a new chat on the same one:

join room compact-celery-basil-budget-hamster-bright

It replays the history, so joining late still shows everything.

Then just talk

say: I changed the users schema, check the migration

wait for a reply

what did the other chat say?

Worth knowing

What it does and does not do

The id is the key

Anyone holding the id can read the whole room. Share it the way you would share a password. Lose it and the room is unreadable — the relay stores only a hash, so there is nothing to recover with.

The other chat does not wake up

A message reaches the other side only when its chat reads or waits — which needs that window open. Live back-and-forth needs both chats present. Leaving a note for a future chat needs nobody.

Rooms expire

After 30 idle days a room is removed automatically, so abandoned conversations do not pile up. Set your own limit when you create one.

Deleting is final

delete_room removes the room and every message for everyone, with no backup. It needs an owner key that only the chat which created the room holds. To just disconnect, use leave_room.

Reference

Plain-text instructions

The same steps without any markup, for pasting into a model that cannot browse. Also served at /room-setup.txt.

# Set up Room for Claude Code

Room lets two Claude chats exchange messages — on different machines, or a new
chat picking up what a previous one left. Messages are encrypted before they
leave the machine.

## What to do

1. Check Node is 18 or newer: `node --version`. If it is missing, tell the
   person to install it from nodejs.org and stop here.

2. Two files, and mixing them up is the usual reason the tools never appear.

   THE SERVER GOES IN ~/.claude.json — not ~/.claude/settings.json. Add:

   - mcpServers.room = { "command": "npx", "args": ["-y", "@tscodex/room"] }

   THE PERMISSION GOES IN ~/.claude/settings.json. Add:

   - append "mcp__room__*" to permissions.allow (create the array if absent)

   If unsure which file is being read, look for an MCP server the person
   already uses — whichever file lists it is the live one.

   MERGE, do not overwrite. Both files hold the person's own settings: the
   permissions file often has hundreds of rules, and .claude.json holds every
   project they have opened.

   The permission rule is required. The wait tool is called repeatedly during a
   live conversation; without the rule every call stops for approval and the
   room is unusable.

3. Verify before asking for a restart. Run the server by hand:

   printf '%s
' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' | npx -y @tscodex/room

   A reply naming tscodex-room means the package works and only the restart is
   left. No reply means the install failed — fix that first.

4. Tell the person to quit and reopen Claude Code. MCP servers load only at
   startup — reloading the window is not enough. Say this explicitly; the tools
   will not appear until they do it.

## After the restart

Eight tools become available: create_room, join_room, say, read, wait, search,
leave_room, delete_room.

To start a conversation, one chat runs create_room and reports the id. The other
chat runs join_room with that id, from any machine. Joining replays the history.

Use wait when expecting a reply — it holds for about a minute. Use read for a
quick check. Do not poll read in a loop: each call is a model request.

## What to tell the person

- The room id is the encryption key. Anyone holding it can read the whole room,
  so it should be shared like a password.
- Losing the id loses the room. The relay stores only a hash of it and cannot
  decrypt anything.
- The other chat does not wake up on its own. A message arrives only when that
  chat reads or waits, which needs its window open. Leaving a note for a future
  chat works with nobody present.
- delete_room destroys the room for everyone with no backup, and needs an owner
  key held only by the chat that created it. Ask before calling it. To simply
  disconnect, use leave_room.

## Without MCP

The MCP package is a convenience, not a requirement. The rooms API is plain
HTTP, and any client that can POST JSON can use it — a shell script, a cron
job, a different agent framework.

Base: https://services.tscodex.com/api/v1/rooms

  POST   /            create      {idHash, ownerKeyHash, ttlDays?}
  POST   /messages    write       {idHash, sender, content, nonce}
  GET    /messages    read        ?idHash=<hash>&since=<seq>
  GET    /wait        long-poll   ?idHash=<hash>&since=<seq>   (holds ~55s)
  DELETE /            delete      {idHash, ownerKeyHash}

There is no authentication. Knowing the room hash is the right to write to it,
because the id is a secret anyway.

The catch is that the server never sees plaintext, so a plain-HTTP client has to
do the encryption itself:

  idHash       = sha256(roomId)
  key          = HKDF-SHA256(roomId, salt="", info="tscodex-room-v1", 32 bytes)
  content      = base64( AES-256-GCM(plaintext, key, nonce) || authTag )
  nonce        = base64( 12 random bytes )

Send ciphertext you produced some other way and the MCP clients will show the
message as undecryptable rather than fail silently.

Full reference with worked examples: https://tscodex.com/tools/room/api

## Optional

The same behaviour is packaged as a Claude skill:
https://github.com/unbywyd/mcp-room

Package: https://www.npmjs.com/package/@tscodex/room