Tic-Tac-Toe MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Tic-Tac-Toe MCP Serverjoin the tic-tac-toe game"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
tic-tac-toe over an MCP channel
Two Claude Code sessions play tic-tac-toe against each other, coordinating through a shared MCP server — no human relaying moves. Each session is woken on its turn by a Claude Code channel push, plays, and goes idle until the next push.
┌───────────────── the referee (one process, owns the game) ─────────────────┐
│ POST /join · POST /move · GET /state · GET /subscribe · GET /spectate │
└──────────▲──────────────────────────────────────────────────▲──────────────┘
│ HTTP + SSE │ HTTP + SSE
channel adapter (X) channel adapter (O)
stdio MCP server spawned by stdio MCP server spawned by
Claude session X Claude session O
tools: join_game/view_board/make_move (same) — pushes "your turn" inOpen http://127.0.0.1:8787/ to watch it live, or read the built-in Docs tab on that page for the architecture and turn-flow diagrams.
Project layout
Organised by feature, not by layer: everything about the game lives in game/,
everything about the channel in channel/, every prompt in prompts/.
src/
game/
engine.ts pure rules — win, draw, legal moves (unit-tested)
game.ts authoritative state + transitions, announces events
serialize.ts model → wire payloads (public + spectator shapes)
routes.ts the Express router: join / move / state / reset
streams.ts every open SSE stream; routes events to the right player
prompts/ what the agent is told, as Jinja (Nunjucks) templates
channel-instructions.j2 goes into the session's system prompt
your-turn.j2 · game-over.j2 the pushed channel notifications
seed.j2 paste this into a session to start it
render.ts the tiny loader
server.ts Express app: routes, templates, static assets, listen
templates/ EJS page, split into partials
index.ejs the shell — head + includes
partials/
header.ejs title + connection indicator
tabs.ejs Board / Docs switch
panel-board.ejs the live game
panel-docs.ejs the documentation sections
diagram-architecture.ejs inline SVG
diagram-sequence.ejs inline SVG
channel/
index.ts the MCP channel: capability, tools, stdio
refereeClient.ts HTTP + SSE link to the referee (self-healing)
tools.ts join_game / view_board / make_move
notifications.ts referee event → channel notification
public/
styles/ tokens · base · board · docs
scripts/
model.js MODEL: game state + which seat this browser holds
view.js VIEW: the only module touching the DOM
controller.js CONTROLLER: intent in, model updates out
api.js fetch + EventSource, nothing else
main.js entry pointgame.ts knows the rules but no transport — it announces events, and streams.ts
decides how to deliver them. Prompts are content, not code: edit a .j2 file to
change what the agent is told, without touching TypeScript.
Related MCP server: ttt-mcp
Requirements
Node 20+ and pnpm (
pnpm installonce).Channels enabled. Channels are a Claude Code research preview.
Pro / Max personal account: enabled by default. ✅ use this.
Team / Enterprise org (e.g. a work account): blocked until an Owner enables
channelsEnabled— you'll getblocked by org policy. Log in with a personal Pro/Max account instead, or have the Owner enable it.
Run it
cd ~/dev/tic-tac-toe
pnpm install # once
# 1. start the referee (leave running)
pnpm server # listens on http://127.0.0.1:8787Open the live board in a browser: http://127.0.0.1:8787/ — it shows each session joining, whose turn it is, every move, the winning line, and a live event feed, all pushed over SSE. Leave it open while the match runs.
Then open two more terminals, both in this directory. In each:
claude --dangerously-load-development-channels server:tictactoeAccept the dev-channels warning ("I am using this for local development").
Accept "New MCP server found in this project: tictactoe" → Use this MCP server.
Paste the contents of
src/prompts/seed.j2as your first message.The session calls
join_gameand goes idle. Do the same in the other terminal.
The first session to call join_game is X, the second is O. As soon as
both have joined, the referee wakes X — and from there the two sessions ping-pong
on their own until someone wins or it's a draw. You just watch.
A dim line under each session's banner confirms the channel is live:
Channels (experimental) messages from server:tictactoe inject directly in this session.
Play against Claude yourself (human vs AI)
You can take one seat. Start the server, open http://127.0.0.1:8787/, and click
🙋 Join as human — you get whichever mark is free. Launch one Claude session
(claude --dangerously-load-development-channels server:tictactoe, then the seed) to
fill the other seat. When it's your turn the board highlights and you click an empty
cell; your move flips the turn and the Claude opponent is woken by its channel to reply.
Only one human seat is allowed — the point of the demo is the Claude-channel side, so the second player is always a Claude session (a second human join is rejected).
Try it without Claude (sanity check)
pnpm test # engine unit tests
pnpm server # then, in another shell, drive it with curl:
curl -s -XPOST localhost:8787/join -d '{}' # -> {"playerId":...,"mark":"X",...}
curl -s -XPOST localhost:8787/join -d '{}' # -> mark O; X is now woken
curl -s localhost:8787/stateNotes
One game at a time.
POST /reset(or restart the server) starts a fresh match; a new pair joining after a finished game also resets automatically.Everything binds to
127.0.0.1only. Change the port withTTT_PORT=9000 pnpm server(the channel reads the sameTTT_PORT, or setTTT_SERVER=http://host:port).This is the same pattern as the rounders channel adapter, in miniature: a shared event source + a per-session stdio channel that wakes an idle session.
pnpm format/pnpm format:checkrun Prettier over the whole repo.
This server cannot be deployed
Maintenance
Related MCP Connectors
Real-time chat for AI agents. Claude Code, Cursor, Cline and Codex join channels over MCP.
Chess MCP for Claude: engine analysis, attack maps, game review. One URL, no install.
- bluffnetOAuthgg.bluffnet
Live Texas Hold'em for AI agents. The tools teach the rules; the bluffing is up to your model.
Agents play Connect 4, Battleship and duels for real USDC. Every move published. First match free.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables two LLMs to play Tic-Tac-Toe against each other autonomously using a shared tool and an SSE relay. The server facilitates agent-to-agent communication by holding tool responses until the opponent makes a move, managing the game state in real-time.1Apache 2.0
- FlicenseAqualityDmaintenanceExposes tic-tac-toe game tools (new_game, get_state, make_move) for Claude to play against a human via chat.35-
- AlicenseNot gradedqualityBmaintenanceEnables multiple Claude Code sessions to communicate and coordinate through broadcast and peer-to-peer messaging.2 npm1MIT
- FlicenseNot gradedqualityBmaintenanceEnables playing the board game Codenames cooperatively with an AI spymaster (Claude) through MCP tools. Players guess words on a live board while Claude gives clues.-