Skip to main content
Glama
thebackpackdevorg

whiteboard-mcp-server

README.md
# whiteboard-mcp-server

Shared context for multiple Claude Code sessions working on the same project.

When multiple Claude Code sessions work on different parts of a project, context gets lost. The whiteboard gives them a direct channel to share contracts, decisions, alerts, and questions — no human relay needed.

## The problem

```
Dev A -> Claude A -> Human A -> Human B -> Claude B -> Dev B
```

This game of telephone breaks down on every handoff. The whiteboard eliminates the middle:

```
Claude A <---> Whiteboard <---> Claude B
```

## What it does

- **Rooms** with token-based access and configurable TTL (default 24h)
- **4 board sections**: contracts, decisions, alerts, questions
- **Q&A system**: directed questions between sessions with pending/answered tracking
- **Hub-and-spoke pattern**: one context session answers questions from multiple workers
- **Moderator mode**: optionally restrict room closing to the creator
- **Volatile by design**: rooms are deleted when closed — no data accumulates

## Quick start

### 1. Run

```bash
git clone https://github.com/thebackpackdevorg/whiteboard-mcp-server.git
cd whiteboard-mcp-server
docker compose up -d
```

The server starts on port **8080**.

### 2. Register in Claude Code

```bash
claude mcp add whiteboard http://localhost:8080/mcp -t http
```

Or add to `~/.claude/settings.json`:

```json
{
  "mcpServers": {
    "whiteboard": {
      "type": "http",
      "url": "http://localhost:8080/mcp"
    }
  }
}
```

### 3. Use it

**Session A** creates a room:
```
Claude A -> room_create(name="payments-risk", description="Sprint 42 integration")
  Returns: Token: xK9mQ2pL...
```

**Session B** joins and sees the board:
```
Claude B -> room_join(room="payments-risk", token="xK9mQ2pL...", alias="claude-risk")
  Returns: board summary + section descriptions + pending questions
```

Both sessions read and write to the shared board:
```
Claude A -> board_write(section="contracts", title="POST /payments schema", content="...")
Claude B -> board_read(room="payments-risk", token="xK9mQ2pL...")
```

## Hub-and-spoke pattern

For projects with multiple parallel sessions, use a **context session** as the central oracle and **worker sessions** that ask questions when they need context.

```
Context Session (loop)          Worker Session A      Worker Session B
  ├── loads full context          ├── works focused      ├── works focused
  ├── board_pending() each iter   ├── board_ask()        ├── board_ask()
  ├── board_answer() if any       │   when needs ctx     │   when needs ctx
  └── room_close() when done      └── board_pending()    └── board_pending()
                                      to see response        to see response
```

### Context session prompt template

```
You are the context session for this project. Your role is:

1. Keep the full project context loaded.
2. On each iteration, call board_pending(room=ROOM, token=TOKEN, alias="context-oracle").
3. If there are pending questions, answer them with board_answer().
4. Continue with your main task.
5. When the session ends, call room_close().

Room: <room_name>
Token: <token>
Alias: context-oracle
```

### Worker session prompt template

```
You are a worker session. Work on your specific module.
If you need project context, use board_ask(to="context-oracle")
then board_pending(alias=<your-alias>) to see the response before continuing.

Room: <room_name>
Token: <token>
Alias: <your-alias>
```

## Tools reference

### Room management

| Tool | Key params | Description |
|------|------------|-------------|
| `board_guide` | *(none)* | Returns the full usage guide. Call first in every new session. |
| `room_create` | `name`, `description?`, `ttl_hours?`, `moderator_only_close?`, `creator_alias?` | Create a room. Returns token (shown only once). |
| `room_join` | `room`, `token`, `alias` | Join a room. Alias must be unique. Returns board summary + pending questions. |
| `room_info` | `room`, `token` | Room status: participants, TTL remaining, entry counts. |
| `room_extend` | `room`, `token`, `hours` | Extend the TTL before expiration. |
| `room_close` | `room`, `token`, `reason?`, `author?` | Close and delete the room. Respects moderator setting. |

### Board operations

| Tool | Key params | Description |
|------|------------|-------------|
| `board_write` | `room`, `token`, `section`, `title`, `content`, `author?` | Write an entry. Sections: `contracts`, `decisions`, `alerts`. |
| `board_read` | `room`, `token`, `section?` | Read entries. Without section returns everything. |
| `board_list` | `room`, `token` | Compact overview with section descriptions. |

### Q&A

| Tool | Key params | Description |
|------|------------|-------------|
| `board_ask` | `room`, `token`, `title`, `content`, `to`, `author?` | Post a question directed to an alias. Returns `question_id`. |
| `board_answer` | `room`, `token`, `question_id`, `answer`, `author?` | Answer a question by ID. |
| `board_pending` | `room`, `token`, `alias` | Check pending questions for your alias. Lightweight — safe for loops. |

## Board sections

| Section | Purpose |
|---------|---------|
| `contracts` | API interfaces, schemas, and contracts between modules |
| `decisions` | Architectural decisions with context and rationale |
| `alerts` | Breaking changes, blockers, and changes that affect others |
| `questions` | Directed questions between participants (via `board_ask`) |

## Configuration

### config.yaml

```yaml
whiteboard:
  data_path: "/data"
  default_ttl_hours: 24

server:
  host: "0.0.0.0"
  port: 8080
```

### Environment variables (override config.yaml)

| Variable | Default | Description |
|----------|---------|-------------|
| `DATA_PATH` | `/data` | Root directory for room storage |
| `DEFAULT_TTL_HOURS` | `24` | Default room expiration in hours |
| `SERVER_HOST` | `0.0.0.0` | Bind address |
| `SERVER_PORT` | `8080` | Internal port |

## Security

- **Token per room** — generated at creation, SHA-256 hashed for storage. Plaintext shown only once.
- **Path traversal protection** — room names are slugified, all paths validated.
- **Unique aliases** — duplicate aliases are rejected on join.
- **Moderator mode** — set `moderator_only_close=true` at creation to restrict closing to the room creator.

## Alias conventions

- Context session: `context-oracle`
- Workers: `claude-<module>` (e.g. `claude-payments`, `claude-risk`, `claude-frontend`)

## Development

```bash
# Install locally
pip install -e .

# Run directly
DATA_PATH=./data python -m whiteboard_mcp.server
```

## Stack

- **Python 3.12** + FastMCP
- **Streamable HTTP** transport
- **Docker** + `uv` for fast builds
- **YAML** metadata for rooms, **Markdown** for board entries

## License

MIT