Skip to main content
Glama
wu-yu-pei
by wu-yu-pei
README.md
# mcp-terminal-share

An MCP server that lets two or more Claude Code terminals on the same machine talk to each other. Register each terminal with a short name, then send messages between them.

```
┌──────────────┐                      ┌──────────────┐
│  terminal A  │ ── send_message ──►  │  terminal B  │
│              │ ◄── send_message ──  │              │
└──────────────┘                      └──────────────┘
        │              file-based              │
        └──────► ~/.claude/terminal-messages ──┘
```

## Why

Pair-running two Claude Code sessions and want one to hand off a long log or a task summary to the other? That's it.

## Install

Wire it into Claude Code's MCP config (Claude Code → `~/.claude.json` or via the CLI):

```bash
claude mcp add terminal-share -- npx -y mcp-terminal-share
```

Or by hand in your MCP config:

```json
{
  "mcpServers": {
    "terminal-share": {
      "command": "npx",
      "args": ["-y", "mcp-terminal-share"]
    }
  }
}
```

Requires Node.js 18+.

## Tools

| Tool | Purpose |
| --- | --- |
| `register` | Give this terminal a name (e.g. `A1`, `dev`). Required before others can address it. |
| `list_terminals` | Show all live terminals. Stale records are cleaned up automatically. |
| `send_message` | Send `{from, to, summary, content}` to another terminal. |
| `get_messages` | Read messages addressed to you. Deletes them on read by default. |
| `watch_messages` | Block until a message arrives or `timeout` (default 300s) expires. |

Names must match `^[A-Za-z0-9_-]{1,32}$`. Message content is capped at 1 MB, summary at 500 B.

## Suggested slash commands

If you already use the bundled `t-rg` / `t-list` / `t-send` / `t-get` / `t-watch` skills, they map 1:1 onto the tools above.

## Show the registered name in your statusline

After a successful `register`, the server writes a tiny session record keyed by a hash of the working directory:

```
~/.claude/terminal-messages/sessions/<sha256(cwd)[:16]>.json
```

The MCP server, your statusline, and any helper subprocess it spawns all inherit `cwd` from the same Claude Code session, so they can find each other without any extra plumbing.

### claude-hud

Add `--extra-cmd "mcp-terminal-share-label"` to your statusline command:

```json
{
  "statusLine": {
    "type": "command",
    "command": "claude-hud --extra-cmd \"mcp-terminal-share-label\""
  }
}
```

The bundled `mcp-terminal-share-label` bin reads the record for the current cwd and outputs claude-hud's expected `{"label": "📟 <name>"}` (or `{}` when nothing is registered). Override the emoji/prefix with `MCP_TERMINAL_SHARE_LABEL_PREFIX`.

### Other statuslines

Roll your own. The minimal Node version:

```js
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
import { createHash } from "node:crypto";

const key = createHash("sha256").update(process.cwd()).digest("hex").slice(0, 16);
try {
  const { name } = JSON.parse(
    readFileSync(
      join(homedir(), ".claude", "terminal-messages", "sessions", `${key}.json`),
      "utf-8",
    ),
  );
  process.stdout.write(`📟 ${name}`);
} catch {
  // not registered
}
```

The file is removed automatically when the MCP server shuts down.

**Known limitation:** if you open two Claude Code sessions in the same directory, the later one's `register` overwrites the earlier one's session record. This only affects the statusline label — message routing between terminals is unaffected.

## How it works

- Each terminal writes its registration to `~/.claude/terminal-messages/terminals/<name>.json`.
- Messages are dropped into `~/.claude/terminal-messages/messages/to_<recipient>_<ts>_<rand>.json`.
- A session record for statusline consumers lives at `~/.claude/terminal-messages/sessions/<sha256(cwd)[:16]>.json`.
- Liveness uses a 5 s heartbeat; records older than 30 s whose owning PID is also dead are reaped on read.
- Messages older than 24 h are reaped on read, and a per-recipient queue cap of 100 prevents runaway buildup.
- All file writes go through a temp-file + rename for atomicity.

Override the storage root with `MCP_TERMINAL_SHARE_DIR` (mostly useful for tests).

## Security model

- **Local, single-user, single-host.** Anyone with read access to `~/.claude/terminal-messages` can read or spoof messages. This is fine on a personal dev machine and explicitly out of scope to defend otherwise.
- **No network surface.** Communication is purely through the local filesystem; nothing listens on a port.
- **Input validation.** Names are regex-validated to prevent path traversal; content size is capped.

If you need cross-machine sharing or untrusted multi-tenant isolation, this is the wrong tool — reach for a real message bus.

## Development

```bash
npm install
npm test
```

Tests use Node's built-in `node:test` runner with isolated temp dirs (no extra deps).

## License

[MIT](LICENSE)

TDQS

A3.7/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clear, non-overlapping purpose: registration, sending, listing, and two distinct reading methods (polling and blocking). No ambiguity.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern (get_messages, list_terminals, register, send_message, watch_messages), making them predictable.

Tool Count5/5

With 5 tools covering the core operations of a terminal messaging service, the count is well-scoped and each tool earns its place.

Completeness4/5

The tool surface covers registration, sending, and two receive modes. A minor gap is the lack of an explicit unregister or delete functionality, but the core workflow is complete.

Maintenance

ActivityInactive
ResponsivenessNo issues