An AI-powered Dungeon Master for D&D 5e, running entirely in Discord.
DnDnAi replaces the human DM with Claude AI while keeping human players at the table (in a Discord channel) alongside AI-controlled party members. The bot manages narration, combat, dice, characters, and turn flow — you just play.
- Create a game in a Discord channel with
/new-game - Human players join with
/joinand upload a markdown character sheet - Add AI companions with
/add-agent grimbold - Start the campaign with
/start— the AI DM narrates an opening scene - Type
> I search the room for trapsto act in character - AI agents respond in character, then the DM resolves everything and narrates what happens
- Dice are real — the DM requests rolls, the engine rolls actual random dice
- AI Dungeon Master — Claude Opus narrates, adjudicates rules, and drives the story (escalates effort on guardrail retry)
- AI Party Members — Claude Sonnet-powered NPCs with distinct personalities and voices (low effort; escalates on guardrail retry)
- Honest Dice — real random rolls, never AI-simulated
- Full Combat System — initiative, turn order, damage, healing, conditions, concentration tracking, automated death saves
- Spell Slots & Resources — auto-tracked and deducted; the DM can't accidentally let you cast beyond your limits
- XP & Leveling — earn XP from combat and milestones, level up with
/level-up - Rest System —
/rest shortand/rest longto recover spell slots, feature charges, and HP - Condition Tracking — prone, frightened, stunned, etc. with mechanical effects noted on rolls
- Webhook Identities — each AI character appears as a separate Discord user with their own name and avatar
- Auto-Persistence — game state saves after every turn; crash-safe, always resumable
- Narrative Compression — AI summarizes the story every 10 turns to manage context length
- Private Messages —
/whisperfor secret in-character communication (only recipient + DM see it)
- Bun runtime
- A Discord bot token (Discord Developer Portal)
- Claude CLI installed and logged in (uses your Pro/Max plan — no API key needed)
# Clone and install
git clone git@github.com:ericosg/DnDnAi.git
cd DnDnAi
bun install
# Configure
cp .env.example .env
# Edit .env with your tokens:
# DISCORD_TOKEN=your_bot_token
# GUILD_ID=your_server_id
# NARRATIVE_STYLE=concise # concise | standard | elaborate (default: concise)
# Run
bun run src/index.tsWhen creating your bot in the Discord Developer Portal, enable these:
- Bot permissions: Send Messages, Manage Webhooks, Use Slash Commands, Embed Links, Attach Files, Read Message History, Add Reactions, View Channels
- Privileged intents: Message Content Intent
Use the OAuth2 URL Generator in the Developer Portal with the bot and applications.commands scopes plus the permissions above.
| Command | Description |
|---|---|
/new-game |
Create a new campaign in this channel |
/join |
Join with a character sheet (markdown file attachment) |
/add-agent <name> |
Add an AI party member from agents/<name>.md |
/start |
Begin the adventure — AI generates backstories and opening scene |
/status |
Show party HP, conditions, combat turn order |
/roll <notation> |
Roll dice — 2d6+3, d20, 4d6kh3 |
/look [target] |
Ask the DM to describe the environment or something specific |
/whisper @player <msg> |
Private in-character message |
/recap |
DM summarizes the story so far |
/ask <question> |
Ask the DM an out-of-character question about your current game |
/help <question> |
Ask about D&D rules, bot commands, or how things work (reads docs + SRD) |
/how-to-play |
Quick-start reference card for new players |
/character [section] |
Show your character sheet (all, abilities, skills, features, spells, backstory) |
/inventory |
Show your character's equipment |
/rest <short|long> |
Take a rest to recover spell slots, features, and HP |
/level-up [hp:Roll|Fixed] |
Level up when you have enough XP |
/pass |
Skip your turn |
/pause |
Gracefully pause — DM saves full context for seamless resume |
/resume |
Resume a paused game — DM reloads context and continues |
/end |
End the campaign and save final state |
New player? Read docs/how-to-play.md — a 2-minute guide covering everything you need to start playing. No D&D experience required.
Prefix your message with > to act in character:
> I draw my sword and cautiously approach the door
> "Who goes there?" I call out into the darkness
> I check the chest for traps before opening it
Plain messages (no > prefix) are treated as out-of-character and don't affect the game.
- The DM narrates a scene
- Human players act (using
>messages or/pass) - AI agents respond in character automatically
- Once everyone has acted, the DM resolves all actions and narrates the outcome
- If dice are needed, the DM requests them and the engine rolls real dice
- Repeat
In combat, turns follow initiative order. The game never auto-advances without human input — if it's your turn, the bot waits for you.
When the DM starts combat, the engine rolls initiative and enforces turn order. On your turn:
- Describe what you do:
> I attack the goblin with my shortsword - The DM rolls dice, applies damage/healing, and narrates the result
- Your spell slots and feature charges are tracked automatically — you'll see a warning if you try to use something you've already spent
If you drop to 0 HP, the engine auto-rolls death saves at the start of your turn. You don't need to do anything — just hope for good rolls.
/rest short— recover short-rest features (Action Surge, Second Wind, Warlock slots)/rest long— full recovery: HP to max, all spell slots and features reset/level-up— when you have enough XP, level up your character (choose Roll or Fixed for HP)
/character— see your full sheet (ability scores, combat stats, XP progress, spell slots, feature charges)/character spells— just your spells and remaining slots/inventory— your equipment
- You can't break anything. The engine enforces the rules — if you try to cast a spell without slots, it'll tell you.
- Just describe what you want to do. You don't need to know the mechanics. Say
> I try to sneak past the guardsand the DM handles the rest. - Use
/askfreely. Ask the DM anything: "What can I do here?", "How does sneak attack work?", "What happened to the merchant?" It doesn't use your turn. - The DM AI reads your backstory. The more personality you put in your character sheet, the more personal the story gets.
Upload a markdown (.md) file with your character's stats, skills, equipment, and backstory. The bot's parser is flexible — **Key:** Value, Key: Value, and - Key: Value all work.
See the full guide: docs/creating-characters.md — covers the format, what every field means, how to use AI to generate a character if you're new to D&D, and links to learning resources.
A ready-to-use sample is included at characters/sample-character.md — copy it and make it your own, or use it as-is.
AI agents run agentically — they have persistent first-person memory across sessions (saved to data/games/<id>/agent-notes/<slug>.md) and the same abilities human players do (pass, ask the DM, look, whisper).
Add any of these to your game with /add-agent <name>:
| Agent File | Name | Race | Class | Level | Personality |
|---|---|---|---|---|---|
grimbold |
Grimbold Ironforge | Mountain Dwarf | Fighter | 3 | Gruff veteran who protects the party while complaining about everything |
criella |
Criella Arkalis | Tiefling | Warlock | 2 | Treats her fiendish pact like a tedious business contract |
merric |
Merric Tosscobble | Lightfoot Halfling | Bard | 1 | Cheerful traveling cook whose secret ingredient is magic |
torinn |
Torinn Stormfang | Dragonborn | Paladin | 3 | Earnest honor-bound knight who cannot detect sarcasm |
nyx |
Nyx Namfoodle | Forest Gnome | Wizard | 2 | Compulsive tinkerer whose contraptions "probably won't explode" |
vola |
Vola Keth | Half-Orc | Barbarian | 1 | Gentle former shepherd who rages only to protect the helpless |
caelynn |
Caelynn Galanodel | Wood Elf | Ranger | 2 | 237-year-old elf who adventures because she's bored |
damakos |
Damakos "Sorrow" | Tiefling | Monk | 1 | Awkward monk stuck with an embarrassing teenage virtue name |
seraphina |
Seraphina Goodbarrel | Lightfoot Halfling | Cleric | 3 | Militant soup-healer who mothers the entire party |
soveliss |
Soveliss Ilphelkiir | High Elf | Sorcerer | 2 | Aristocrat whose wild magic got him politely exiled |
pumpernickle |
Pumpernickle | Slate (Homebrew) | Bard | 3 | Theatrical slate humanoid whose every threat becomes a performance |
Agent personality files live in agents/ as markdown with YAML frontmatter. See agents/grimbold.md for a complete example.
---
name: Agent Name
race: Dwarf
class: Fighter
level: 3
description: One-line description
voice: How they speak
traits:
- Trait one
- Trait two
flaws:
- Flaw one
goals:
- Goal one
avatarUrl: https://example.com/avatar.png # optional
model: claude-sonnet-4-6 # optional override
characterSpec: |
**Name:** Agent Name
**Strength:** 16
... (full character sheet in markdown)
---
Detailed personality description, combat style, and roleplay notes go here.
The AI reads this entire file to stay in character.The characterSpec field contains the mechanical character sheet that gets parsed into game stats. The markdown body below the frontmatter defines personality, voice, combat behavior, and roleplay guidelines.
See docs/architecture.md for full technical details.
TL;DR: Discord events → game engine → AI orchestrator decides who acts → agents/DM generate responses via Claude CLI → webhooks post as character identities → state persists to JSON.
- Runtime: Bun
- Language: TypeScript (strict mode)
- Discord: discord.js v14
- AI: Claude CLI (non-interactive mode) — Opus (DM), Sonnet (agents, low effort), Haiku (orchestrator); effort escalates on guardrail retry
- Config Parsing: gray-matter for agent frontmatter
- Persistence: Flat JSON files (no database)
bun test # unit + integration tests
bunx tsc --noEmit # type-check
bunx biome check src/ # lint + format checkMIT