PsychoSmiley/mcp-to-mcp
minimal proof-of-concept where two LLMs play Tic-Tac-Toe against each other through a single MCP tool, make_move. Supports local stdio/HTTP play and a Cloudflare Durable Object remote deployment, with server-side rule validation and no external database.
Platform-specific configuration:
{
"mcpServers": {
"mcp-to-mcp": {
"command": "npx",
"args": [
"-y",
"mcp-to-mcp"
]
}
}
}Add the config above to .claude/settings.json under the mcpServers key.
Two LLMs play Tic-Tac-Toe against each other through a single MCP tool make_move. No human input, AIs autonomously take turns via ping-pong SSE relay.
# (starts local server on :8787 via stdio, or auto-joins if another instance is already hosting)
claude mcp add tictactoe -- npx -y github:PsychoSmiley/mcp-to-mcp
# Or using Cloudflare MCP Remote
claude mcp add tictactoe --transport http https://mcp-tictactoe.edge-relay-9x.workers.dev/mcpOr simply from claude.ai web in Settings -> Connectors URL: https://mcp-tictactoe.edge-relay-9x.workers.dev/mcp
# Optionally, to self-host on Cloudflare Workers (free)
Set CLOUDFLARE_API_TOKEN=your-token-here && Set CLOUDFLARE_ACCOUNT_ID=your-account-id && git clone https://github.com/PsychoSmiley/mcp-to-mcp && cd mcp-to-mcp && npm install && npx wrangler deploy # Auto-deploys on push via GitHub Actions (add secrets in repo Settings -> Secrets).Then open two separate Claude chats. In each ask: use make_move to play tic-tac-toe
The idea isn't Tic-Tac-Toe - it's the architecture. Using MCP itself as a ping-pong turn relay, where each tool call is the AI's response back-and-forth like a baton relay. This achieves synchronous P2P agent communication over a stateless REST protocol, with zero database reads/writes. The same pattern could be used for chat between two or more AI agents via MCP ;)
Each make_move(move) call both submits a move and waits for the opponent's reply - send + block + receive in one MCP call. The server is authoritative: LLMs only send moves, never the board state. No database - game state lives in RAM. Local MCP Session-Id identifies each player (X vs O).
-> time -> Each MCP closes only to receive opponent's move result, then immediately answers
Agent A: <mcp make_move("B2")>{LLM thinLoading reviews...