Tools#
To keep things fair and interesting, Claude calls the following Python scripts over MCP. They’re grouped by what they touch.
Campaign / session#
Lifecycle commands — spinning up campaigns, archiving them, and feeding the dashboard.
campaign_mgmt.py— create, list, switch, inspect campaigns; add characterscampaign_archive.py— export/import a campaign as a single tarballdm_session.py— headlessclaude -pdriver for the dashboard’s/playsurfaceexport_session.py— export a Claude Code session JSONL transcript as markdownaudit.py— scanevents.jsonfor procedural lapses (missed reactions/morale/surprise)log.py— chronicle notes, calendar/time advancement
Combat & dice#
Mechanics — initiative, dice, conditions, and the DungML-driven tactical grid.
combat.py— initiative, attacks, HP, spell declaration, conditions, effectscombat_map.py— DungML renderer, grid state, combatant positioningdice.py— roll, reaction, morale, encounter, saves, skill checks
Characters / party / NPCs#
Character and party state — vitals, loyalty, factions, and player holdings.
party.py— HP, XP, spells, rest, coinhirelings.py— CHA-driven loyalty checks for hirelings/henchmenhomebrew_classes.py— loader forglobal/homebrew/classes/*.jsonfactions.py— factions and world-clock countdownsholdings.py— player-owned houses and mounts
World / locations / maps#
World simulation — places, weather, travel days, and overland geography.
world.py— NPCs, locations, areas (markdown-backed)world_map.py— Structurizr-style overland DSL → GeoJSON viewslore.py— search, timelines, quest threads, session primerstravel.py— overland day-loop chaining weather, encounters, clock ticks, rationsweather.py— seasonal weather tablessurvival.py— consumable inventory, light sources, encumbrance, rations
Reference lookups#
Read-only lookup over rulebooks, monster/spell/class data, and setting databases.
lookup.py— monster/spell/class lookup, treasure generationrules.py— FTS5 search over PHB/DMG/MM textgreyhawk.py— wrapssettings/greyhawk/greyhawk.db(pages, FTS, typed entries)build_2e_db.py— buildglobal/2e.db(spells + classes)build_phb_ref.py— buildability_scores+proficienciestablesbuild_rules_db.py— build the FTS5 rules index
Items / quests / rumors#
Plot-adjacent data — quest fairness, gossip with truth tiers, and shared item weights.
quests.py— quests with scope/stakes and level-fairness warningsrumors.py— gossip table tracking truth tier separately from what NPCs sayitem_weights.py— shared item-weight resolution (used by dashboard + survival)
Media / misc#
Audio, imagery, generators, and DM-only secret tracking.
images.py— Replicate scene + portrait generationtts.py— OpenAI TTS with per-character voice mappingwordlist.py— fantasy-themed random word generatorsecrets.py— DM-only hidden facts (dm_note,dm_secrets)
Campaign state is stored as structured JSON on the file-system. Tools change state; the web UI reflects current state.
Instructions#
Claude tends towards a certain style of adventure — world-spanning stakes and long-term adventure hooks where everything connects. At lower levels, I prefer adventures to be more straightforward. My model instructions specify:
- Focus on simulating a world, rather than telling a story where every adventure thread is connected.
- Fairness, avoiding the rule of cool and fudging dice-rolls in favour of the party.
- Playing characters and monsters in line with their intelligence. Animals don’t devise clever tactics; dragons do.
- Respecting player statistics. Characters with low charisma shouldn’t have supermodel portraits; characters with low intelligence should use simple speech.
- Not breaking the fourth wall by sharing information player characters couldn’t know (statistics, dungeon master information…).
Sometimes it’s necessary to ask a question or nudge the engine in a certain direction. In that case, I use the ooc: (out of character) prefix. The prefix tells the engine to treat the line as meta-instruction rather than character speech — for example, ooc: skip the tavern small talk and jump to the road, or ooc: what’s the DC for this lock?. Without it, the model would route the input through whichever character I’m currently playing.