DOOM
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
An experiment in how far MCP Apps can go. DOOM runs headless on a server as WebAssembly, exposed as a remote MCP server with an inline MCP App viewer. The model sees the game (a screenshot, structured state, or both) and plays it through tool calls. The game is frozen between calls, so the model can take as long as it likes to think.
The goal: play it inside ChatGPT, by voice.
Autonomous: "Start a game of DOOM and clear E1M1."
Directed: "Go through the door on the left and kill the imp."
Voice: say it out loud in ChatGPT and let the model turn it into game actions (in progress).
Human: play it yourself in the browser (in progress).
Live MCP endpoint: https://doom-player.vercel.app/api/mcp, see Install.
Status: the engine and the MCP server work and are live. Playing inside ChatGPT, and by voice, is what we're building and testing now. The image above is a mock-up around a real frame from the engine.
Contents
Related MCP server: space-invaders-tui
Install
It's a normal remote MCP server: add it to your client by URL. No sign-in needed.
https://doom-player.vercel.app/api/mcpTransport: Streamable HTTP (not SSE).
ChatGPT
Open ChatGPT Plugins (chatgpt.com/plugins), select +, then Add custom MCP server.
Give it a name (e.g. DOOM) and paste the URL above under Connection.
Leave authentication off, confirm the warning, and select Create as a plugin.
Start a new chat, type
@, pick DOOM and ask: "Start a game of DOOM and play E1M1. Caption each move."
Adding custom MCP servers depends on your ChatGPT plan and workspace settings.
Claude (web or desktop)
Open Settings → Connectors → Add custom connector.
Paste the URL above and save.
In a new chat, ask: "Start a game of DOOM and play E1M1. Caption each move."
Claude Code
claude mcp add --transport http doom https://doom-player.vercel.app/api/mcpAny other MCP client that supports Streamable HTTP works the same way. To poke at the tools directly, run npx @modelcontextprotocol/inspector@latest and point it at the URL.
What the model sees
Real frames from the engine in this repo (Freedoom E1M1). The state says a zombie is 315 units away, 4.8° to the left. One doom_turn by exactly that angle and a doom_act fire later, it's down, and the next one walks in.
How it works
flowchart LR
subgraph app["Claude app"]
C["Claude"]
V["Inline viewer<br/>(MCP App iframe)"]
end
subgraph fn["Vercel function · /api/mcp"]
M["MCP endpoint<br/>(Next.js + mcp-handler)"]
G["Game service<br/>(cache · replay · observe)"]
E["DOOM engine<br/>(doomgeneric → wasm)"]
end
R[("Upstash Redis<br/>session = input log")]
C -- "doom_act / doom_turn" --> M
M -- "PNG + JSON state" --> C
V -- "doom_frame (app-only, polled)" --> M
M --> G
G <--> E
G <--> RFour ideas make it work:
Idea | What it means |
Virtual time | The engine's clock only moves when Claude acts. Each call advances an exact number of tics (35 tics = 1 s), then the game freezes. |
Deterministic replay | Serverless functions don't keep a game in memory. A session is just the list of inputs, stored in Redis; any instance rebuilds the exact game by replaying it (~14 ms for 5 minutes of play). A warm cache skips replay most of the time. |
Exact controls | Inputs go straight into DOOM's per-tic command ( |
Perception modes | Each action returns a 640×400 screenshot ( |
There's an interactive walkthrough of one action, end to end, in the architecture explainer.
Run it locally
Requirements
Tool | Version | Needed for |
Node.js | 24+ ( | everything |
pnpm | 12 (via | everything |
curl, unzip (or python3) | any | downloading the Freedoom WAD |
Emscripten | 7.0.0 ( | only if you change the C engine |
You don't need Emscripten to run the project: the compiled engine (engine/dist/) is committed.
1. Install and fetch the game data
git clone --recursive https://github.com/ionut85/doom-mcp-app.git
cd doom-mcp-app
pnpm install
pnpm get-wadpnpm get-wad downloads Freedoom Phase 1 (free, BSD-licensed) to wads/freedoom1.wad and verifies its checksum. To use your own IWAD instead, set DOOM_IWAD=/path/to/doom.wad.
2. Check the engine works
pnpm test
pnpm play-clipnpm testruns determinism, replay and game-service tests.pnpm play-cliplays a short scripted run of E1M1 and writes frames toout/*.png.
3. Start the MCP server
pnpm devThis builds the inline viewer and starts Next.js on http://localhost:3000. The MCP endpoint is http://localhost:3000/api/mcp.
Redis is optional locally: without it, sessions are kept in memory (lost on restart). To use Redis, put these in apps/web/.env.local, or run vercel env pull apps/web/.env.local if you have access to the Vercel project:
KV_REST_API_URL=...
KV_REST_API_TOKEN=...4. Try the inline viewer without Claude
Open http://localhost:3000/dev/host. It's a minimal MCP Apps host for development: it shows the viewer in a sandboxed iframe, starts a game, and can send scripted "Claude" moves. Sandboxed frames can't load from localhost, so to test Play yourself locally, add DOOM_PUBLIC_ORIGIN=https://doom-player.vercel.app to apps/web/.env.local. The viewer then loads the engine from production.
5. Watch a bot play
In a second terminal, while pnpm dev is running:
pnpm botscripts/mcp-bot.ts is a simple scripted player (no AI). It plays 40 actions through the real MCP tools and prints health, kills, events and latency. Arguments: pnpm bot [url] [actions] [vision|state|both].
Connect your local server to Claude
Claude Code can reach localhost directly:
claude mcp add --transport http doom-local http://localhost:3000/api/mcpclaude.ai needs a public URL. Either deploy (see Deploying) or tunnel your dev server, for example with cloudflared tunnel --url http://localhost:3000. Then add <tunnel-url>/api/mcp as a custom connector.
The tools
Tool | Who calls it | What it does |
| Claude | Starts a game: |
| Claude | Holds actions for a number of tics, up to 8 steps and 105 tics per call. Actions: |
| Claude | Turns by exact degrees: negative = left, positive = right, the same convention as enemy angles. |
| Claude | Returns the current observation without advancing time. |
| Viewer only | Returns the latest frame, HUD and captions for the inline viewer. Hidden from Claude via |
| Viewer only | Returns the session's input log, so Play yourself can rebuild Claude's game in the browser and hand you the controls from Claude's exact position. |
An observation in state or both mode looks like this, shortened:
{
"status": "alive", "health": 87, "armor": 0, "weapon": "pistol", "ammo": 42, "kills": "3/29",
"enemies": [{ "type": "imp", "distance": 412, "angle": -12.5, "on_screen": true, "in_crosshair": false, "health": 60 }],
"nearby_items": [{ "type": "stimpack", "distance": 289, "angle": -59.6 }],
"events": ["took 8 damage from 30° left", "killed 1 enemy", "moved 96 units"],
"hint": "Nearest enemy is 12° left; doom_turn by its angle to aim."
}Limits per session: 30,000 tics (about 14 minutes of game time), a 24 h lifetime, and 300 requests per minute per IP.
Development
Command | What it does |
| All tests (Vitest) |
| Replay speed benchmark (1k / 5k / 10k tics) |
| Typecheck |
| Run a scripted step list headlessly and write PNGs to |
| Run the web app and MCP server locally |
| Scripted bot against an MCP endpoint |
| Rebuild |
Changing the C engine
Install emsdk at the pinned version, then rebuild:
source ~/emsdk/emsdk_env.sh
pnpm build:engine
pnpm testCommit the updated engine/dist/ with your change. CI rebuilds the engine and fails if the committed output is stale.
Never edit
engine/doomgeneric/(a git submodule). Changes to upstream code go inengine/patches/*.patch; our own C lives inengine/dg_headless.candengine/dg_exports.c.The state and object layouts in
dg_exports.care mirrored inpackages/doom-core/src/core.ts. Change both together.Determinism is a hard requirement: no wall clock and no randomness in anything that feeds the engine.
Useful environment variables:
Variable | Effect |
| Path to a different IWAD |
| Max warm engines per server instance (default 6; |
| Upstash Redis for sessions and rate limiting |
| Origin the inline viewer loads the engine and WAD from (default: the production domain on Vercel, |
Deploying
The app deploys to Vercel as a Next.js project:
Root directory:
apps/webNode.js: 24.x, with Fluid compute on
Environment variables:
ENABLE_EXPERIMENTAL_COREPACK=1, so Vercel uses the pinned pnpm 12;KV_REST_API_URLandKV_REST_API_TOKEN, which the Upstash for Redis Marketplace integration adds automatically.
Build command: the default
pnpm build. It downloads Freedoom, builds the viewer, then runsnext build.
vercel link
vercel integration add upstash/upstash-kv
vercel deploy --prodProject layout
engine/ C engine → WebAssembly (Emscripten)
doomgeneric/ upstream portable DOOM (git submodule, never edited)
patches/ ticcmd injection hook (3 lines)
dg_headless.c virtual clock, framebuffer, key queue
dg_exports.c JS API: boot, step, render, player state, nearby objects
dist/ committed build output (doom.mjs + doom.wasm)
packages/doom-core/ TypeScript engine wrapper (Node + browser) and tests
apps/web/ Next.js app on Vercel
app/api/mcp/ MCP endpoint and tools
lib/ sessions (Redis), engine cache and replay, observations
ui/viewer/ inline MCP App viewer (single-file Vite build)
scripts/ get-wad.sh, play-cli.ts, mcp-bot.ts
docs/PLAN.md plan, progress, deviations and measurementsLicense
GPL-2.0 (see LICENSE and NOTICE). Built on doomgeneric (GPL-2.0) and Freedoom (BSD-3-Clause). DOOM is a trademark of id Software; this project is not affiliated with or endorsed by id Software, ZeniMax or Bethesda. No id Software WAD files are included.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Make games with AI: one MCP server that connects your agent to Unity, Unreal and Godot editors.
Build, run and publish 3D games in the Zero engine from Claude Code, Cursor or Codex, over MCP.
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to control Unreal E…
Generate game-ready 3D models, textures, and audio from natural language, over MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to control a Minecraft bot for movement, building, crafting, and instant schematic-based structure spawning via MCP tools.92 npm2Apache 2.0
- AlicenseNot gradedqualityDmaintenanceMCP server that lets LLMs play a terminal Space Invaders game with the SUPERHOT rule: time only moves when the agent moves. Exposes tools to start a game, inspect the board state, and act by holding left/right/fire for N frames.9 npm1MIT
- AlicenseNot gradedqualityDmaintenanceMCP server that lets AI coding assistants play DOOM in compatible clients by embedding the real DOOM engine, providing game state, enemy intel, and screenshots.13MIT
- FlicenseNot gradedqualityBmaintenanceEnables LLMs to control a Minecraft bot and interact with the game through MCP tools, including observing state, moving, mining, crafting, building, farming, managing inventory, enchanting, and chatting in-game.1-