crabsmadethis/d2r-horadric-tools
This server provides 23 tools for working with Diablo II: Resurrected save files, item data, character generation, and data mod pipelines.
Lookup & Search
Look up unique items, set items, base items, runewords, stats, and skills by name or ID
Search across all item types in a single query (up to 20 tagged results)
Save File Inspection & Validation
Scan
.d2ssave files for errors, warnings, checksum/size status, and item countsInspect and summarize a save file (class, level, stats, progression, mercenary info)
List items in a save, optionally filtered by location (equipped, inventory, stash, cube, merc)
Diff two
.d2sfiles to compare structural differences
Character Generation
List characters defined in YAML files
Validate character YAML configs with optional dry-run binary builds
Build and deploy characters from YAML to the live saves directory, with automatic backup and scanner gate
Import existing
.d2ssaves back into character YAML definitions
Data Mod Pipeline
Build mods from vanilla data, overlays, and scripts
Deploy mods to the game folder (including CASC injection) or undeploy to revert to vanilla
Diff vanilla vs. build tables to identify changes
Extract vanilla data from the CASC archive
Clean build artifacts
Run a full recovery pipeline (extract → build → deploy) in one step
Audit vanilla skills and items, generating markdown reports
Officially supported platform; the toolset auto-detects game path on Linux and provides full functionality including CASC read/write.
Supports running Diablo II: Resurrected modding tools under Proton on Steam Deck, including auto-detection of game path.
The MCP server is implemented in Python and requires Python 3.10+; integration is via pip install and python -m d2r_mcp.
Integrates with Steam to auto-detect Diablo II: Resurrected installation path and deploy mods to the game directory.
Provides support and auto-detection for Diablo II: Resurrected on Steam Deck (Proton), enabling modding and character building on the device.
Uses YAML for character definitions, data mod overlays, and JSON string patches; the MCP server exposes these YAML-driven workflows as tools.
Horadric Tools for Diablo II: Resurrected
Horadric Tools is a Python/MCP toolkit for offline Diablo II: Resurrected save and
data-mod workflows. It builds .d2s characters from YAML, scans save files
before they are loaded, builds data mods for single player, and exposes
the same capabilities through an MCP server.
The repository is Linux / Steam Deck first. Windows path detection exists but needs more testing. The repo does not ship Blizzard game data, private save files, or Battle.net automation.
Toolkit Scope
Horadric Tools provides:
d2r-chargen: YAML-to-.d2scharacter generation, import, diff, validation, and scanning.d2r-mod: local data extraction, overlay builds, JSON string patches, CASC deploy helpers, diffs, audits, and cleanup commands.d2r_mcp: 23 MCP tools for lookup, save inspection, character generation, and mod-pipeline automation.Public diagnostics in
tools/for corpus scans, follower payload inspection, model-row comparison, and repo hygiene checks.A shareable external-corpus scan wrapper at
scripts/merc_status_external_scan.shfor the merc-status open question (aggregate-only JSON; no paths or per-file examples).
Character generation currently covers equipment, runewords, charms, stats,
skills, mercenary gear, Iron Golems, and experimental template-derived bound
demons. Bound-demon template recipes are documented in
docs/bound-demon-template-recipes.md;
they keep source affixes separate from Bind Demon skill affixes and do not
treat template-derived support as arbitrary algorithmic synthesis. A narrow
registry-backed synthesis_validated surface is also available for exact
validated packages; list those ids with d2r-chargen bound-demon-packages.
Related MCP server: LoreKeeper MCP
Non-Goals
Online play, ladder play, or Battle.net automation.
Trainers, bots, maphacks, or live memory editors.
Committed extracted D2R data, raw save corpora, or machine-local fixtures.
Loading generated saves without first running the scanner.
For .d2s work, the expected loop is: write to staging, recompute size and
checksum, scan, then promote only scanner-clean output.
Quickstart
git clone https://github.com/crabsmadethis/d2r-horadric-tools.git
cd d2r-horadric-tools
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"Generate local lookup data from your D2R install:
d2r-mod extractIf the install is not auto-detected:
d2r-mod extract --game-dir "/path/to/Diablo II Resurrected"Validate the included example character:
d2r-chargen validate ExamplePaladinBuild it only when your save directory is configured and you are ready to write the generated save:
d2r-chargen build ExamplePaladin --force
d2r-chargen scan ExamplePaladinD2R caches saves at startup, so fully exit and relaunch the game after changing a local save file.
Character YAML
The tracked chars/ directory contains curated examples only. Keep disposable
or validation-specific character drafts outside the repo and point the tools at
them with D2R_CHARS=/path/to/chars.
schema_version: 1
name: ExamplePaladin
class: paladin
level: 85
progression: hell_complete
stats:
strength: 156
dexterity: 125
vitality: 250
energy: 25
skills:
Blessed Hammer: 20
Concentration: 20
Vigor: 20
equipment:
- slot: helm
unique: Harlequin Crest
- slot: body
runeword: Enigma
base: utp
- slot: weapon
runeword: Heart of the Oak
base: fla
inventory:
charms:
- magic_grand_charm:
count: 8
properties:
skill_tab: [1, 15]
life: 40See chars/ExamplePaladin.yaml for a fuller example.
Iron Golem payloads are authored with an iron_golem: block on Necromancers
that have IronGolem learned. The supported public surface includes normal,
magic, ethereal, set, rare, crafted, socketed-normal, and runeword payloads.
Runeword golems are written as a JM-less parent followed by generated rune
filler records inside the golem block. Unique payloads require explicit
canonicalization opt-in because D2R rewrites some bytes on save/exit. Manual
Iron Golem socket filler authoring and broad jewel-filler authoring remain
gated; character stash_items expose only the narrow live-positive normal
parent shape with one magic jew or validated unique cjw filler.
class: necromancer
skills:
IronGolem: 1
iron_golem:
item:
runeword: Insight
base: 7wcSupported class names:
amazon, sorceress, necromancer, paladin, barbarian, druid, assassin, warlockCommon equipment slots:
helm, body, weapon, shield, hands, belt, feet, neck, ring_right, ring_left,
weapon_switch, shield_switchCLI Reference
d2r-chargen
d2r-chargen list
d2r-chargen validate <name> [--yaml-only]
d2r-chargen build <name> [--phase N] [--force]
d2r-chargen scan <name>
d2r-chargen import <name> [--force]
d2r-chargen diff <file1> <file2>
d2r-chargen bound-demon-packages [--json] [--all]validate checks YAML and binary encoding without writing a save, including
bound-demon and Iron Golem payloads when the YAML requests them. build writes
through the safety pipeline and should be followed by scan before the result
is loaded in game.
For bound demons, normal reusable authoring should use template_path or a
listed synthesis_validated package id. Raw context slices, generated-name
requests, aura-choice requests, pcount/stat knobs, and algorithmic
monster: NAME synthesis fail before any save is written unless a validated
package explicitly owns that behavior.
Bound-demon template workflow:
python3 tools/d2s_demon_template_inspect.py <template.d2s>
python3 tools/d2s_demon_template_inspect.py <template.d2s> \
--extract-payload .local-demon-templates/<template>.bin \
--emit-yaml-snippetThe emitted YAML snippet is preserve-only. It names template_path and the
payload row index, but does not add source or skill affix edits. Put character
drafts and extracted templates outside the repo:
export D2R_CHARS=/path/to/local/chars
export D2R_SAVES=/path/to/offline-saves
d2r-chargen validate MyWarlock
d2r-chargen build MyWarlock --force
d2r-chargen scan MyWarlockThe public template catalog currently documents Black Lancer, Mauler, Lister-style/Baal Subject 5, and Hephasto-family entries in docs/bound-demon-template-recipes.md.
stash_items can also build the narrow socketed-normal parent shape that has
passed Offline validation: exactly one magic jew filler, or the validated
unique Guardian's Thunder cjw, under a normal parent. Broader jewel
families and socketed magic/rare parents are still research-gated.
stash_items:
- normal: true
base: flc
socketed: true
num_sockets: 1
socket_fillers:
- base: jew
magic: true
properties:
fire_res: 5Normal stash_items may also carry explicit stack quantities for misc and
quest-style bases that use the D2R quantity field. This is fixture-backed for
representative tokens, essences, keys, Worldstone shards, and tomes; live
validation should still be run before promising a new quest-item family.
stash_items:
- normal: true
base: toa
quantity: 1
- normal: true
base: pk1
quantity: 4
- normal: true
base: tbk
quantity: 20d2r-mod
d2r-mod extract [--game-dir PATH]
d2r-mod build [--no-regen]
d2r-mod deploy [--force] [--no-casc]
d2r-mod undeploy
d2r-mod diff [--summary]
d2r-mod inject [--from-dir PATH]
d2r-mod audit [--skills] [--items]
d2r-mod clean
d2r-mod updateCommands that read or write game data need a detected D2R install or an
explicit --game-dir / D2R_GAME_DIR.
Data Mod Overlays
Overlays modify D2R data tables declaratively. Place overlay files in
overlays/ and run d2r-mod build.
target: data/global/excel/UniqueItems.txt
changes:
- row: {index: "The Gnasher"}
set:
prop4: "dmg%"
min4: "50"
max4: "50"
comment: "Buff The Gnasher with +50% Enhanced Damage"If no overlays/ directory exists, the build proceeds with vanilla data only.
See examples/sample_overlay.yaml for a complete
example.
JSON String Patches
D2R reads most item, mercenary, and UI strings from JSON files under
data/local/lng/strings/. Put YAML specs in patches/json_strings/ to add or
override strings:
description: "Rename a few potions"
target: item-names.json
entries:
- key: "vps"
value: "Wild Rice Cake"
- key: "MyCustomItem"
value: "Heart of the Mountain"After d2r-mod build, patched JSON files are written to
build/data/local/lng/strings/. D2R caches strings at startup, so fully exit
and relaunch to see changes.
MCP Server
Horadric Tools ships an MCP server with 23 tools across lookup, save inspection, chargen, and mod-pipeline categories.
Install the package and the MCP SDK before launching the server:
pip install -e .
pip install mcpLaunch it with:
python3 -m d2r_mcpClaude Code:
claude mcp add d2r-tools --transport stdio --scope user -- python3 -m d2r_mcpGeneric MCP config:
{
"mcpServers": {
"d2r-tools": {
"command": "python3",
"args": ["-m", "d2r_mcp"],
"env": {}
}
}
}See d2r_mcp/README.md for the full tool catalog and safety notes.
Safety Model
The save-file workflow is intentionally conservative:
Back up the target save family before writing.
Start from an existing valid
.d2stemplate when possible.Write changes to a temp or staging file.
Recompute size and checksum.
Run
d2r-chargen scan <name>.Promote only scanner-clean files.
Scanner hard errors are deployment blockers unless there is bit-level evidence that the scanner is wrong. Detailed save-format notes live in docs/d2s_format.md.
Repository Map
d2r_chargen/ Character YAML parser, save writer, scanner, importer, diff
d2r_mod/ Data extraction, overlays, CASC read/write, deploy pipeline
d2r_mcp/ MCP server, tool wrappers, response envelope, MCP docs
chars/ Curated example character YAML
examples/ Public overlay examples
docs/ Save-format notes, validation docs, public research writeups
tools/ Standalone diagnostics, corpus tools, hygiene checks
plugin/ Optional Claude Code plugin commands
tests/ Unit, MCP, scanner, mod-pipeline, and fixture-gated testsGenerated data, build output, local evidence, and extracted game files should stay untracked.
Platform Support
Platform | Status |
Linux / Steam Deck (Proton) | Supported, tested |
Windows | Path detection included, needs more testing |
macOS | Unsupported for playing D2R; useful for docs and code work only |
Requirements
Python 3.11+
PyYAML, installed by the package
Diablo II: Resurrected for data extraction and live game use
MCP SDK for MCP server use (
pip install mcp)
Contributing
Start with CONTRIBUTING.md. Before opening a PR, run:
python tools/public_hygiene_check.py
ruff check .
pytest tests/ -v --timeout=60 \
-m "not integration and not slow and not e2e and not smoke" \
--ignore=tests/fixtures/ \
--ignore=tests/test_chargen.py \
--ignore=tests/test_decoder.py \
--ignore=tests/test_fixtures.py \
--ignore=tests/test_importer.pyLicense
MIT
Available Tools
23 toolsd2r_chargen_buildA
Build and deploy a character to the live saves directory.
Structurally enforces CLAUDE.md rules #3, #4, #10, #17: backs up the live save, builds to a temp file, runs the scanner on the temp, and only promotes the temp to the live save if the scanner passes. Live save is untouched on scanner failure.
Args: name: Character name (chars/.yaml must exist). phase: Deploy through phase (1-4). Default: full deploy (4). force: Skip the freshness gate (discards server progress). Avoid unless intentional.
Inputs: name (required), phase, force.
d2r_chargen_importA
Import a server-synced .d2s save into chars/.yaml.
Args: name: Character name (reads live save at /.d2s). force: Overwrite an existing chars/.yaml.
Inputs: name (required), force.
d2r_chargen_listA
List character names defined in chars/*.yaml.
Inputs: no named parameters.
d2r_chargen_validateB
Validate a character YAML and optionally run a dry-run binary build.
Args: name: Character name (resolves to chars/.yaml). yaml_only: Skip the dry-run binary build + scanner pass.
Inputs: name (required), yaml_only.
d2r_lookup_item_baseA
Look up a D2R base item by 3-char code or name.
Searches item_bases.py data. Returns dimensions, requirements, sockets. Supports substring matching on names.
Args: query: 3-character item code (e.g. "hax") or item name
Inputs: query (required). Returns: result
d2r_lookup_runewordA
Look up a D2R runeword by name or numeric ID.
Searches runewords.py data. Returns runes, valid bases, stats. Supports substring matching.
Args: query: Runeword name (or substring) or numeric runeword ID
Inputs: query (required). Returns: result
d2r_lookup_set_itemA
Look up a D2R set item by name or numeric ID.
Searches set_items.py data. Returns item info + set membership. Supports substring matching.
Args: query: Item name (or substring) or numeric set item ID
Inputs: query (required). Returns: result
d2r_lookup_skillA
Look up a D2R skill by name or numeric ID.
Searches skills.py data. Returns skill ID and class. Supports substring matching.
Args: query: Skill name (or substring) or numeric skill ID
Inputs: query (required). Returns: result
d2r_lookup_statA
Look up a D2R stat by ID, canonical name, or YAML alias.
Searches item_stat_cost.py data. Returns encoding info (save bits, save add, param bits, value shift) needed for binary stat encoding.
Args: query: Stat ID, canonical name (e.g. "fireresist"), or alias (e.g. "fcr")
Inputs: query (required). Returns: result
d2r_lookup_uniqueA
Look up a D2R unique item by name or numeric UID.
Searches unique_items.py data. Returns item info + stats. Supports substring matching (e.g. "harlequin" finds Harlequin Crest).
Args: query: Item name (or substring) or numeric UID
Inputs: query (required). Returns: result
d2r_mod_auditB
Audit vanilla skills and items; write markdown reports.
Args: skills: Run skills audit. items: Run items audit. audit_all: Run both audits (overrides skills/items). output_dir: Directory to write reports into. Default: docs/audit.
Inputs: skills, items, audit_all, output_dir.
d2r_mod_buildC
Build mod from vanilla + overlays + scripts into build/.
Inputs: warn_conflicts, no_regen, game_dir.
d2r_mod_cleanA
Wipe the local mod build artifacts to start from a clean slate.
Deletes the build/ directory and resets generated chargen data files. Does NOT touch the deployed D2R install; run d2r_mod_undeploy first if you also want to revert the running game.
Inputs: no named parameters.
d2r_mod_deployC
Build + deploy mod to the game mod folder (runs CASC injection by default).
Inputs: force, no_casc, no_build, warn_conflicts, +2 more.
d2r_mod_diffC
Compare vanilla/ vs build/ tables. Returns list of changed files.
Inputs: file, summary.
d2r_mod_extractC
Extract vanilla data from CASC archive into vanilla/.
Inputs: game_dir.
d2r_mod_undeployA
Revert the deployed D2R mod, restoring the vanilla game state.
Removes the mod directory from the D2R install (or keeps it on disk with keep_mod=True) and unregisters the launch options. Use this to roll back a deploy without rebuilding, or before switching to a different mod.
Args: keep_mod: If True, leave the built mod directory in place on disk. game_dir: Override the auto-detected D2R install path.
Inputs: keep_mod, game_dir.
d2r_mod_updateC
Full recovery pipeline: extract → build → deploy.
Inputs: warn_conflicts, no_regen, game_dir.
d2r_save_diffC
Compare two .d2s files and return structural differences.
Args: path_a: First save (treated as "before"). path_b: Second save (treated as "after").
Inputs: path_a (required), path_b (required).
d2r_save_inspectA
Summarize a .d2s save: class, level, stats, progression, merc.
No scanner pass — use d2r_save_scan for validation.
Args: path: Absolute path to .d2s file, or bare character name.
Inputs: path (required).
d2r_save_list_itemsA
List items on disk in a .d2s save, optionally filtered by location.
Args: path: Absolute path to .d2s file, or bare character name. location: Filter: equipped, inventory, cube, stash, merc_equipped, or None for all.
Inputs: path (required), location.
d2r_save_scanA
Run d2rdoctor scanner on a .d2s save file.
Returns a structured result with checksum/size status, item counts, and any scanner errors or warnings. status == "error" iff errors is non-empty. Deployment-blocker rule: never deploy a save that this tool flags as error (see CLAUDE.md Rule #17).
Args: path: Absolute path to .d2s file, or bare character name (resolved against Steam saves dir).
Inputs: path (required).
d2r_searchA
Search across ALL D2R item types: uniques, sets, runewords, bases.
Use this when you don't know what type of item you're looking for. Returns up to 20 results tagged by type.
Args: query: Search term (substring match across all item names)
Inputs: query (required). Returns: result
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 23 tools
Each tool has a clear, distinct purpose. The naming with category prefixes (chargen, lookup, mod, save) and specific actions prevents ambiguity. For example, d2r_chargen_build and d2r_save_inspect operate on different resources, and lookups are split by item type.
All tools follow the consistent pattern 'd2r_<category>_<action>', using snake_case uniformly. There are no mixed conventions (e.g., camelCase) or irregular formats.
23 tools cover the full modding lifecycle—character generation, lookups, mod building/deploying, and save file inspection—without being excessive. Each tool seems justified for its role.
The tool set covers the main workflows: chargen (build, import, validate), lookups (all item types), mod management (extract, build, deploy, clean, update, diff, audit), and save operations (inspect, scan, diff, list items). No obvious gaps for the intended domain.
Maintenance
Related MCP Connectors
Shared memory MCP server for Claude Code, Codex, Gemini CLI and Cursor. Cited, editable.
Build, run and publish 3D games in the Zero engine from Claude Code, Cursor or Codex, over MCP.
MCP Server for Slima - AI Writing IDE for Novel Authors with AI Beta Reader.
Build and publish Overskill apps from Cursor, Claude, ChatGPT, or any MCP client.
Related MCP Servers
- AlicenseBqualityNot gradedmaintenanceEnables complete RPG Maker MZ game development through MCP tools, including autonomous game creation from concepts, AI-generated assets using Gemini 2.5 Flash, and programmatic control over maps, events, characters, and databases. Supports full game creation workflow from a single command with automatic story, asset, and implementation generation.3225-
- AlicenseAqualityDmaintenanceProvides fast, cached access to comprehensive Dungeons & Dragons 5th Edition data including spells, monsters, classes, races, equipment, and rules through Open5e and D\&D 5e APIs.72MIT
- AlicenseAqualityFmaintenanceProvides real-time access to Path of Exile 2 game data including currency exchange rates, item prices, and ladder meta-build statistics. It also enables LLMs to search the community wiki and retrieve datamined game information from public APIs.285MIT
- AlicenseNot gradedqualityBmaintenanceA Linux-native MCP server that gives AI assistants direct access to DMA-based memory operations for memory inspection, process analysis, reverse engineering, and game engine SDK extraction via natural language.13AGPL 3.0