mcp-terminal-share
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