Content
# Disco Engine — MCP AI Plays Disco Elysium
A general-purpose text game engine that stores the complete script/rules/state of Disco Elysium in a SQLite ROM, allowing AI to play through the MCP protocol, while providing a browser-based real-time observation page.
## Architecture
```
ROM (disco.db, read-only)
├── temp_table 111166 dialogue nodes (lines + conditionstring + userscript)
├── dialogues 1428 scenes
├── dlinks 134491 transition edges
├── checks 235 dice checks
├── modifiers 921 modifiers
├── alternates 2843 alternate lines
├── variables 10513 global variables
└── actors 421 actors
Engine Layer
├── RomDb SQLite wrapper (node:sqlite, zero dependencies)
├── parser.ts Recursive descent lexer + parser (conditionstring + userscript)
├── eval.ts Condition evaluator + script runner + function registry
├── CheckResolver 2d6 + skills + modifiers vs difficulty
├── Engine Main loop: node → condition → choices → dlinks → script → dice → save
└── WorldState variables + inventory + party + time + reputation + skills
Adapter Layer
└── DiscoFunctions 85 game function registrations (IsKimHere/CheckItem/SetVariableValue...)
Service Layer
├── MCP server disco.start/play/status/history/save/load/scenes (remote HTTP transport)
└── WebSocket Real-time push of lines/dice/choices/variable changes to browser observation page
```
## Quick Start
```bash
npm install
npm run demo # End-to-end verification of engine (console output)
npm run play # Start complete service (MCP + observation page)
```
After service starts:
- **Observation Page**: http://localhost:3000/
- **MCP Endpoint**: http://localhost:3000/mcp
## Connecting AI Players
### Claude Desktop / Cursor / Other MCP Hosts
Add remote HTTP server to MCP configuration:
```json
{
"mcpServers": {
"disco": {
"url": "http://localhost:3000/mcp"
}
}
}
```
### VPS Deployment
```bash
# On VPS
git clone <repo> && cd disco-engine
npm install
# Put disco.db in data/ directory
DISCO_PORT=3000 DISCO_MODE=both npm run play
# Use pm2 to daemon
pm2 start "npx tsx src/index.ts" --name disco
```
Local AI host connects to remote MCP:
```json
{ "mcpServers": { "disco": { "url": "https://your-vps:3000/mcp" } } }
```
Open `https://your-vps:3000/` in browser for real-time observation.
## MCP Tools
| Tool | Description |
|------|-------------|
| `disco.start` | Start new game or jump to specified scene |
| `disco.play` | **Main Loop**: auto-advance through nodes without choices, stop at decision points. Pass `choices:[N]` to make a choice and continue |
| `disco.status` | Current state snapshot (scene/money/party/quest/skills) |
| `disco.history` | Recent game trajectory |
| `disco.save` / `disco.load` / `disco.saves` | Save management |
| `disco.scenes` | List/search scenes |
### Game Loop (AI Perspective)
```
disco.start({sceneId: 142}) → See opening lines + 2 choices
disco.play({choices: [0]}) → Choose "Try to pick lock", see subsequent plot + 4 choices
disco.play({choices: [2]}) → Choose "Break door", see dice result + subsequent plot
disco.play({}) → Don't choose, continue auto-advance to next decision point
disco.status() → View current state
disco.save({slot: "ch1"}) → Save
```
**Why not call every step?** 80% of nodes are NPC monologues/connectors, no AI decision needed. `disco.play` automatically skips these, only stops at real choices. A scene reduces from 20-50 calls to 5-10 calls.
**Why only one choice at a time?** Disco Elysium choices affect each other - choosing A changes B's visible choices. Blindly choosing multiple will choose non-existent options. Pass multiple `choices` only for known path replay.
## Tech Stack
- TypeScript + Node.js 24 (`node:sqlite` built-in, zero native dependencies)
- `@modelcontextprotocol/sdk` — MCP server + client
- `ws` — WebSocket observation page
- `zod` — Tool parameter validation
## Generality
Engine core doesn't recognize any game functions. `FunctionRegistry` is pluggable - switch games by:
1. Preparing SQLite ROM that fits this table structure
2. Writing an adapter layer to register game functions
3. Reusing the rest (engine/interpreter/MCP/observation page)
## Data Source
- `disco.db` from [msyavuz/disco-api](https://github.com/msyavuz/disco-api) (community-curated for personal use)
- For personal use only, not for public distribution or commercial use.