When you split a project across multiple Claude Code sessions (one on payments, one on the risk engine, one on the API gateway) they can’t talk to each other. You become the bottleneck, copying context between terminals like a game of telephone. Every “the other session decided to use UUIDs instead of integers” costs you a context switch and a copy-paste.

I built whiteboard-mcp-server to fix this. It’s a shared whiteboard that Claude Code sessions join, post to, and query, so they coordinate directly instead of through you.

What it looks like in practice

Three terminals. One session holds full project context (the “oracle”), two others work on separate modules.

# Terminal 1: context oracle joins the room
Claude: *calls room_join(room="payments-refactor", alias="oracle")*
→ Joined room payments-refactor as oracle

# Terminal 2: payments worker joins and asks a question
Claude: *calls room_join(room="payments-refactor", alias="payments")*
Claude: *calls board_ask(title="Refund event schema",
         content="What's the schema for refund events?", to="oracle")*

# Terminal 1: oracle checks for pending questions
Claude: *calls board_pending(alias="oracle")*
→ 1 pending: "Refund event schema" from payments
Claude: *calls board_answer(question_id="001",
         answer="RefundEvent: {id: uuid, amount: int, currency: str, reason: str}")*

# Terminal 2: payments worker polls and gets the answer
Claude: *calls board_pending(alias="payments")*
→ Answer from oracle: "RefundEvent: {id: uuid, amount: int, ...}"

No human in the loop. The oracle session runs in a polling loop, answering questions as they come in. The workers ask when they need context and keep building.

Architecture: hub-and-spoke

The server is a Python app built with FastMCP using Streamable HTTP transport, running in Docker.

Rooms are the core abstraction. Each room has four board sections:

  • contracts: API schemas, interface definitions, shared types
  • decisions: architectural choices that other sessions need to respect
  • alerts: breaking changes, blockers, things that need immediate attention
  • questions: directed Q&A between sessions
graph TD O["Context Oracle
full project context"] <-->|"board_pending / board_answer"| S["whiteboard-mcp-server
FastMCP + Streamable HTTP"] W1["Worker: payments
payments module"] <-->|"board_ask / board_read"| S W2["Worker: risk
risk engine"] <-->|"board_ask / board_read"| S W3["Worker: gateway
API gateway"] <-->|"board_ask / board_read"| S S --> R["Room Storage
YAML + Markdown on disk"]

The hub-and-spoke pattern works like this: one session (the oracle) holds the full project context, including architecture docs, schema definitions, and past decisions. It runs board_pending in a loop. Worker sessions post questions directed to the oracle, get answers, and keep going. Workers can also post contracts and decisions that other workers read directly.

Rooms are ephemeral. They have a configurable TTL (default 24 hours) and get deleted on close. No data accumulates between coordination sessions.

Setup

git clone https://github.com/thebackpackdevorg/whiteboard-mcp-server.git
cd whiteboard-mcp-server
docker compose up -d
claude mcp add whiteboard http://localhost:8080/mcp -t http

Then in each Claude Code session:

room_join(room="my-project", alias="unique-name")

To run the oracle pattern, tell one session: “Join room my-project as oracle. Poll board_pending every 30 seconds and answer any questions using your knowledge of the codebase.”

Room mechanics

Rooms use token-based access. room_create returns a token that authenticates all subsequent calls. A few things worth knowing:

  • Aliases must be unique. The server rejects duplicate aliases on join (more on why below).
  • Moderator mode optionally restricts room_close to the session that created the room.
  • TTL: rooms auto-expire after 24 hours by default, configurable per room.
  • Storage: YAML metadata files plus Markdown entries on disk, all volatile.

The debugging story: the alias collision that broke everything

The original design made aliases idempotent on join. If you called room_join with an alias already in use, the server silently merged you with that existing session. You’d get back the same pending questions and post as the same identity.

This seemed fine with two sessions. It broke completely with three.

Here’s what happened: I had an oracle session and two workers. The first worker joined as “payments”, fine. The second worker joined as “risk”, but due to a default alias fallback, it got the same alias as the first worker. The server merged them. Now both workers shared an identity. Questions directed to “risk” went to “payments.” Answers from “payments” looked like they came from “risk.” The oracle was answering the right questions but the responses landed in the wrong terminal.

It took 20 minutes to figure out because the symptoms were subtle. The workers weren’t failing, they were just acting on wrong information. The oracle’s answers were correct. The board state looked fine if you inspected any single session. It only broke when you looked at all three together.

The fix was a one-line check: reject room_join if the alias is already taken in that room. Force each session to pick a unique name. Simple, but without it, multi-session coordination is silently unreliable. The worst kind of bug.

When to use this vs. just sharing files

If your sessions only need to read the same codebase, you don’t need this. Git and the filesystem handle that fine.

The whiteboard solves a different problem: real-time coordination. When session A makes a decision that session B needs to know about now, not on the next git pull. When session B needs to ask session A a question and get an answer without the human relaying it.

The oracle pattern is the sweet spot. One session with full context, N workers with narrow focus, all coordinating through the board.

Try it

whiteboard-mcp-server on GitHub

Clone, spin up Docker, and give your Claude Code sessions a way to talk to each other. Watch out for the alias uniqueness. If your sessions aren’t picking distinct names, you’ll get the silent merge bug that makes everything look fine until it isn’t.