Skip to main content
Glama
dearlordylord

D&D 5e SRD Oracle

README.md
# D&D 5e Rules SDK

An executable, formally specified implementation of D&D 5e SRD 5.2.1 rules
for character creation, progression, character sheets, and combat.

Supports every SRD class and its abilities through
[level 10](plans/unit-profile-coverage/LEVEL1_10_FULL_SUPPORT.md), within the
documented runtime and table-adjudication boundaries.

The [shipped SRD catalog](plans/unit-profile-coverage/README.md#collection-boundaries)
contains only SRD 5.2.1 content. Closed-license content is not included; the
architecture separates authored content from reusable rule procedures so that
separately supplied closed-license content can build on the same foundation.
The current public catalog builders accept SRD collections; additional mechanics
must pass the owning runtime's admission boundary or receive new procedure
support. See [content architecture](ARCHITECTURE.md#content-scope-and-licensing).

## How it works

Rules have formal models in [Quint](https://quint.sh/) and are executed by
[TypeScript reducers](ARCHITECTURE.md).

The engine tells your application what it needs next: character choices,
available Battle Acts, or a missing input such as a target or roll result.
When a rule depends on a player choice or table observation, the runtime returns
a question to fill. Answering one question can reveal the next.

The core runtime does not roll dice or infer battlefield geometry. Callers provide
those facts as witnesses through the API.

A required input is a **Hole**; its answer is a **Fill**. In Battle, callers
discover **Acts**, select one, and answer the questions needed to resolve it.
See the [creation workflow](packages/character-creation-runtime/README.md#runtime-flow)
and [Battle protocol](packages/battle-runtime/README.md#runtime-protocol).

## Use it

Use the SDK as the rules engine for a game, a character builder, or tools for
running encounters. Your application supplies player decisions and table facts;
the runtime applies supported mechanics and returns the next state and inputs.
Work toward implementations in other languages is in progress through the
[conformance tooling](packages/opaque-oracle/README.md), with language-neutral contracts,
formal models, and conformance tools. Availability in arbitrary languages is a
goal, not a shipped SDK promise.

The [MCP server](packages/mcp/README.md) exposes these workflows to an agent,
including Play Sessions that can recover across HTTP server restarts. For
example, an illustrative interaction during an existing battle:

> **Player:** What can my fighter do?
>
> **Agent:** You can attack with your weapon. Which target?
>
> **Player:** The goblin. I'll roll at the table.
>
> **Agent:** Tell me the attack result.
>
> **Player:** 17.
>
> **Agent:** That hits. What did you roll for damage?
>
> **Player:** 8.
>
> **Agent:** The hit deals 8 damage. Here is what you can do next.

The agent discovers Acts and answers the runtime's questions through tools;
the particular choices and outcomes depend on the battle state. See
[MCP usage](packages/mcp/README.md#play-through-an-agent) for the tool-level flow.
For a browser view, run the [character creation UI and battle visualizer](packages/app/README.md#run-locally).

## npm packages

The SDK and stdio MCP distribution targets are `@dearlordylord/dnd-sdk` and
`@dearlordylord/dnd-mcp`. See [distribution and release instructions](scripts/distribution/README.md)
for package builds, verification, publication, and registry-status checks;
[release notes](distribution/CHANGELOG.md) identify unreleased changes.
Consumer instructions: [SDK](distribution/sdk/README.md) and [MCP](distribution/mcp/README.md).

## SDK example

_Programmatic usage_

Conceptual pseudocode; the public API exposes each discovery and fill step:

```ts
let creation = beginCharacter();

creation = fill(creation, {
  class: "fighter",
  background: "soldier",
  species: "human",
  size: "medium",
  humanSkill: "perception",
  originFeat: "alert",
  fighterSkills: ["acrobatics", "survival"],
  fightingStyle: "defense",
});

const fighter1 = finishCharacter(creation);
const fighter2 = levelUp(fighter1);
const fighter3 = levelUp(fighter2, { subclass: "champion" });
const fighterSheet = createCharacterSheet(fighter3);

const encounter = startBattle(fighterSheet, srd.monsters.goblinWarrior, {
  witnesses: { fighterInitiative: 17, goblinInitiative: 12 },
});
let battle = encounter.battle;
const fighter = encounter.character;
const goblin = encounter.opponent;

battle = attack(battle, {
  target: goblin,
  witnesses: { distance: 5, attackRoll: 17, damageRoll: 8 },
});

battle = takeDamage(battle, {
  target: fighter,
  witnesses: { damage: 7 },
});

battle = secondWind(battle, {
  witnesses: { healingRoll: 6 },
});

battle = actionSurge(battle);
battle = attack(battle, {
  target: goblin,
  witnesses: { distance: 5, attackRoll: 16, damageRoll: 7 },
});

const fighterAfterBattle = handoff(battle, fighterSheet);
```

## Inspect the content

Abilities are authored as Dhall data. Ice Knife, an SRD spell, composes an
attack phase and a saving-throw phase. This dependency trace is generated from
its [compiled JSON](packages/surface/content/ice_knife.json), authored in
[Dhall](packages/surface/content/ice_knife.dhall). Colors distinguish costs,
input holes, resolution, effects, and scaling:

```mermaid
flowchart TD
  classDef source fill:#1f77b4,color:#fff,stroke:#0d3c61
  classDef procedure fill:#2ca02c,color:#fff,stroke:#185018
  classDef window fill:#9467bd,color:#fff,stroke:#4a2b66
  classDef hole fill:#f4a261,color:#000,stroke:#8a4f12
  classDef attachment fill:#ffcc00,color:#000,stroke:#8a6d00
  classDef resolution fill:#ff7f0e,color:#fff,stroke:#8a4308
  classDef lifecycle fill:#7f7f7f,color:#fff,stroke:#333
  classDef resource fill:#e377c2,color:#000,stroke:#8a457a
  classDef scaling fill:#17becf,color:#000,stroke:#0a5f6a
  classDef effect fill:#d62728,color:#fff,stroke:#6a1414
  classDef statBlock fill:#111827,color:#fff,stroke:#f59e0b,stroke-width:4px
  root1["spell_root<br/>Ice Knife"]:::source
  act2["activate"]:::procedure
  q3["action_quota<br/>(Casting Time: Action)"]:::resource
  slot4["spell_slot<br/>≥ level 1"]:::resource
  att5["hole<br/>target<br/>target<br/>one<br/>range 60 ft"]:::hole
  res6["attack_roll [phase 1]<br/>ranged spell attack"]:::resolution
  dmg7["damage: 1d10 piercing"]:::effect
  win8["on_hit_window"]:::window
  att9["hole<br/>burst origin<br/>area<br/>emanation r=5 ft<br/>origin: primary target"]:::hole
  res10["save_gate [phase 2]<br/>DEX save<br/>DC: caster spell save DC"]:::resolution
  dmg11["damage: 2d6 (linear per slot level) cold"]:::effect
  sc12["scale_die_size<br/>axis=slot<br/>+1d6 per level above 1"]:::scaling
  act2 -- consumes --> q3
  act2 -- consumes --> slot4
  act2 -- attaches_to --> att5
  act2 -- grants --> res6
  res6 -- attaches_to --> att5
  res6 -- opens_window --> win8
  win8 -- grants --> dmg7
  dmg7 -- attaches_to --> att5
  act2 -- attaches_to --> att9
  act2 -- grants --> res10
  res10 -- attaches_to --> att9
  res10 -- branches_on_save --> dmg11
  dmg11 -- attaches_to --> att9
  sc12 -- modifies --> dmg11
  slot4 -- modifies --> sc12
  res6 -- branches_on_completion --> res10
  root1 -- roots --> act2
```

Another authored record can reuse implemented mechanical procedures without a
handler keyed to its name. New procedure shapes need runtime support. The
[Surface authoring guide](packages/surface/README.md#authoring-format-dhall--json)
shows how to compile, validate, and generate a full review trace;
[architecture](ARCHITECTURE.md#authored-content) explains the content boundary.

## Explore

| Build or inspect                               | Start here                                                            |
| ---------------------------------------------- | --------------------------------------------------------------------- |
| Character creation and progression             | [Creation runtime](packages/character-creation-runtime/README.md)     |
| Persistent character state and rests           | [Character Sheet runtime](packages/character-sheet-runtime/README.md) |
| Battle Acts, fills, and interrupts             | [Battle runtime](packages/battle-runtime/README.md)                   |
| Tool-driven play with recoverable sessions     | [MCP](packages/mcp/README.md)                                         |
| Character creation UI and battle visualization | [Website](packages/app/README.md)                                     |
| Package ownership and verification design      | [Architecture](ARCHITECTURE.md)                                       |

## License

Code is licensed under [Apache 2.0](LICENSE). SRD 5.2.1 content is available
under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/); see
[NOTICE](NOTICE) for attribution.

TDQS

A4.1/5.0

Scored across 24 tools

Disambiguation5/5

Each tool has a distinct purpose, clearly separating character creation, battle management, catalog inspection, and utility operations. Even overlapping concepts like 'holes' are split across creation vs. battle contexts, and state readers (read_battle_state) are differentiated from act discovery (discover_battle_acts).

Naming Consistency4/5

The overwhelming majority use a verb_noun pattern (create_character_draft, list_stat_blocks, fill_battle_hole, end_turn). One tool, battle_lifecycle, deviates from this convention, but it's a minor outlier and the rest are highly consistent.

Tool Count4/5

24 tools sits at the upper end of the ideal range, but the complexity of a D&D 5e SRD system justifies the breadth. Each tool addresses a specific aspect of character management, battle progression, or catalog access, so none feel redundant.

Completeness4/5

The surface covers character lifecycle (create, fill, finalize, query, update), battle lifecycle (start, act, turn, end), catalog inspection, and utility (dice, workflow). Minor gaps exist, such as no explicit tool to list ongoing battles or delete sessions, but these are workaroundable through existing tools.

Maintenance

ActivityNo data
ResponsivenessResponsive