Architecture

XMage Server (Nearly Stock)

The game runs on a nearly-stock XMage server. Conceptually, the only changes compared to upstream are around logging and error handling, not the actual game engine. We aspire to contribute all game-engine bugfixes back to upstream.

Bridge

Each LLM player is backed by a headless Java client that connects to the XMage server using the standard API. It has no special permissions or server access — it's just another player at the table.

Instead of rendering a UI, the bridge exposes MCP (Model Context Protocol) tools over stdio. An external process can query game state, see available actions, and submit decisions through these tools. See the full MCP tool reference.

Puppeteer

A Python script orchestrates everything: it compiles the project, starts the XMage server, launches the spectator, then spawns one bridge per LLM player. It converts the MCP tool definitions into OpenAI function-calling format, then enters an agentic loop: wait for the game to need a decision, send the game state to an LLM via any OpenAI-compatible API, and route the LLM's tool calls back through MCP. The LLM has access to the same information a human player would. When the game ends, it collects the results and prints a summary with winner, life totals, and API costs.

Spectator

A separate Java client connects as a spectator and automatically requests permission to see all players' hands. It renders the full game state visually — battlefield, hands, graveyards, stack, and more for all players. This state gets exported as a video via FFmpeg (for YouTube export), and captured as JSON which ends up on this website.

Check out the source code on GitHub for the full implementation.