Skip to content

Repository files navigation

acore-data

MCP server providing unified query access to ~462 AzerothCore game datastores — DBC binary files, SQL tables, SQL overlays, and auxiliary stores. Plus terrain/pathfinding queries against MMap navmesh data, creature spawn analysis, database state audit, encounter rollups, record digests, mod configuration lookup, and C++ enum decoding. Exposes twelve tools (query, lookup, list, spawns, dbversion, encounter, travel, explain, config, enums, sql, terrain) over the JSON-RPC based Model Context Protocol.

Overview

AzerothCore stores game data in four categories of datastores. This server gives any MCP-compatible client (LLM, IDE plugin, CLI) a single entry point to query all of them by name, with automatic name resolution, type-aware field annotation, and smart SQL routing.

Category Count Description
dbc_backed ~112 Binary .dbc files loaded into packed C structs, with optional SQL overlay (*_dbc tables)
sql_objectmgr ~95 ObjectMgr SQL tables — creature templates, gameobjects, items, quests, gossip, etc.
sql_manager ~48 Tables loaded by other singleton managers (SpellMgr, PoolMgr, GameEventMgr, LootStore, …)
sql_auxiliary ~171 Discovered SQL tables not in the static registry

For the full technical reference on how each category is loaded in AzerothCore, see docs/datastores/README.md.

Tools

query

Query any datastore by name. Accepts a DBC file name ("Spell"), SQL table name ("quest_template"), or C++ struct name ("SpellEntry").

Parameter Type Description
name string Required. Datastore name. Use lookup to find valid names.
id number Primary key for O(1) lookup. If a filter is also given, both must match (AND).
filter object Named field filters. Supports $like / $ilike patterns.
fields array Select fields by index [38, 39] or name ["BaseLevel", "SpellLevel"]. Strict: unknown names are errors with suggestions.
limit number Max records to return (default 100).
compact boolean Strip null/zero fields (default true).
annotate boolean DBC rows as legacy per-field arrays with index/type/sql_column/source (default false = flat {name: value} rows). On SQL tables, attaches column types.
hints boolean Include field_references / referenced_by cross-reference metadata (default false).
links boolean Add metadata.links: one-hop relation map (related NPC/item/spell names) for the returned rows (default false; capped at 25 rows × 10 fields).
resolve boolean or array Resolve type-specific data fields. true = all, or ["dbc", "sql", "loot"].
resolve_max number Max items per loot table resolution (default 10). Use 0 for unlimited.

Result shape: rows are flat {field: value} objects. id/row_index lookups return a single object (or a specific error if the id is absent / does not match the filter); filter and unconstrained queries return a list. Locale arrays (e.g. name[0..15]) collapse to a scalar (or a list of non-empty values).

Examples:

query(name="Spell", id=118)
→ O(1) lookup for Polymorph, ~1 KB flat object

query(name="quest_template", filter={"Title": {"$ilike": "%murloc%"}})
→ SQL $ilike search across all quest titles

query(name="gameobject_template", id=180013, resolve=true)
→ Returns gameobject data with data[0-19] annotated (lockId, lootId, spellId, etc.)

query(name="quest_template", id=46, links=true)
→ metadata.links: quest NPC (Guard Thomas), required item (Torn Murloc Fin), reward items

query(name="Spell", id=118, fields=[38, 39])
→ Only BaseLevel and SpellLevel fields

For DBC-backed stores that also have an SQL overlay table, query merges both sources — SQL overlay data replaces or supplements the binary DBC data, and errors report which source failed.

lookup

Get schema and metadata for any datastore. Resolves by C++ struct name, SQL table, DBC file, or store variable (e.g. sSpellStore).

Parameter Type Description
query string Required. Name to resolve.
detail string "schema" (default) — full field list. "summary" — 10 sample fields.

Returns the per-field mapping (index, name, type, sql_column, plus notes/references where available), referenced_by cross-references, and access hints (e.g. sSpellStore.LookupEntry(id) -> SpellEntry const*). SQL-native tables additionally list live columns as name:type [PK].

Examples:

lookup(query="SpellEntry")
→ Full schema with all 183 fields, SQL columns, and cross-references

lookup(query="creature_template", detail="summary")
→ Compact 10-field sample + live SQL columns from the database

lookup(query="sSpellStore")
→ Resolves store variable to SpellEntry with access pattern hints

list

List available datastores with optional search and category filtering.

Parameter Type Description
search string Filter by struct name, table, DBC file, or store variable.
category string "all" (default), "dbc_backed", "sql_objectmgr", "sql_manager", "sql_auxiliary".
limit number Max entries to return (default 50). When truncated, metadata.total reports the full count.

Examples:

list(category="dbc_backed")
→ First 50 DBC-backed stores with field counts (total reported in metadata)

list(search="Quest")
→ Stores matching "Quest" in struct name, table, or DBC file

spawns

Creature spawn analysis for a creature_template entry — total world spawns, per-map breakdown (with map names resolved from DBC), and sample positions. Read-only; uses the creature, creature_template and map/Map.dbc tables. No mod required — works on any azerothcore-wotlk install.

Parameter Type Description
entry number Required. creature_template entry (the creature id).
map number Restrict the analysis to a single map ID.
limit number Sample positions to return (default 3, max 20).

Examples:

spawns(entry=32820)
→ Wild Turkey: 3125 spawns, map 0 (Eastern Kingdoms), sample positions

spawns(entry=1)
→ template exists but has_world_spawn=false (Waypoint, GM-only)

dbversion

Database state audit for this install — no arguments. Reports the core/DB version row (acore_world.version), per-database SQL update state (RELEASED/ARCHIVED/CUSTOM/MODULE/PENDING counts plus applied/pending totals), pending update names, and which of the four AzerothCore databases are present. acore_playerbots only exists with mod-playerbots; its absence is reported as installed: false, not an error.

Examples:

dbversion()
→ core_version (with fork/branch marker), db_version, per-DB tables/applied/pending,
  mod_playerbots_installed=true|false

enums

C++ enum decoder — indexes every named enum in the AzerothCore source tree (~1,044 enums, ~17.7k members, one cached scan) and resolves magic numbers. Answers "what does this value mean?" (enums(enum='Mechanics', value=17)MECHANIC_POLYMORPH) without the agent grepping the source. The source tree is optional (override with ACORE_SRC_ROOT); absence is a clean error.

Parameter Type Description
enum string Exact enum name.
value number Value to decode (with enum=) or scan across all enums.
member string Member-name substring for reverse lookup (with enum=).
search string Case-insensitive substring match on enum names.

Examples:

enums()
→ 1044 enums, 17713 members, largest enums by size

enums(enum="Mechanics", value=17)
→ MECHANIC_POLYMORPH (src/server/shared/SharedDefines.h)

enums(enum="SpellEffects")          full value table (capped)
enums(value=17)                     which enums contain 17 (ambiguity note)

config

mod-playerbots configuration index — indexes playerbots.conf.dist (setting → default + line) and the C++ GetOption call sites that read each key in one cached pass. Answers "what does this setting do / what's its default / where is it used" without reading the 900-line conf or grepping the mod. The mod source tree is optional (override its location with PLAYERBOTS_ROOT); absence is a clean error.

Parameter Type Description
key string Exact setting key → full detail incl. code refs.
search string Case-insensitive substring match on key names (capped 50).
rebuild boolean Force re-scanning the mod conf + source.

Examples:

config()
→ 888 settings, 384 code refs, counts by prefix

config(search="strategy")
→ EnableNewRpgStrategy, Max/MinRandomBotChangeStrategyTime, ...

config(key="AiPlayerbot.RandomBotCombatStrategies")
→ default "", conf line 1370, code ref: PlayerbotAIConfig.cpp:459

explain

Agent-friendly digest of a single record from any datastore — wraps the query pipeline (annotate + links) and distills it to: a one-line summary, the non-trivial fields (capped at 20, name pinned first), one-hop cross-references with resolved target names, and source provenance. For DBC-backed stores it reports which fields the live SQL overlay changed vs the vanilla DBC (overlay_overrides), and flags overlay-only records (ids that exist only in the *_dbc overlay tables, as in this reduced build).

Parameter Type Description
name string Required. Datastore name.
id number Required. The record's primary key (0 allowed).

Examples:

explain(name="Spell", id=118)
→ Polymorph (dbc) — 46 fields with values, 20 shown — 4 cross-reference(s)

explain(name="Spell", id=19)
→ SWORDSPECIAL (DND) — overlay-only record (no vanilla DBC row)

explain(name="quest_template", id=46)
→ Bounty on Murlocs — starters/enders: Guard Thomas, reward items resolved

encounter

Map/instance encounter rollup — what is in this place. Top creatures by spawn count with level ranges, rank (0=normal 1=rare 2=elite 3=worldboss) and loot item names for the top 5; top game objects (ores, doors, chests); instance metadata (script, allow_mount) when the map is an instance; and the mod-playerbots travel graph size when installed. Read-only, acore_world based — works on any azerothcore-wotlk install.

Parameter Type Description
map number Required. Map id.
limit number Max creatures to list (default 20, max 100).

Examples:

encounter(map=43)
→ Wailing Caverns (instance_wailing_caverns): Druid of the Fang (19, rare),
  Deviate Lasher (19), ... with loot; travel graph: 10 nodes / 1968 points

travel

Inspect the mod-playerbots travel graph and verify paths against the MMap navmesh — no running server needed. Three modes (all read-only; requires acore_playerbots, its absence is a clean error):

Parameter Type Description
map number Required. Map id.
node number Node mode: node details + named neighbours (capped 50 each).
from / to number Path mode: decode the stored path between two nodes (requires both).
verify boolean Path mode: check each point against MMap ground (default true; degrades to a note where the install has no .map data for the map).

Examples:

travel(map=0)
→ 644 nodes, 3010 edges, 295623 path points, sample named nodes

travel(map=0, node=0)
→ "Human start" + neighbours (Goldshire innkeeper, Northshire Valley spirithealer, ...)

travel(map=0, from=0, to=2776)
→ "Human start" → "Elwynn Forest Goldshire": 103 path points + navmesh_verification

sql

Execute raw SQL queries with automatic database routing and typo suggestions.

Parameter Type Description
query string Required. SQL query (SELECT, INSERT, UPDATE, DELETE only).

Features:

  • Smart routing — automatically directs queries to acore_world, acore_characters, or acore_auth based on table name
  • Typo suggestions — suggests correct table and column names on errors
  • Safety — blocks DROP, TRUNCATE, ALTER, GRANT, REVOKE
  • Context hints — e.g. empty loot_template results suggest trying questitem tables

Examples:

sql(query="SELECT entry, name FROM creature_template WHERE entry = 1")
→ Routes to acore_world

sql(query="SELECT id, username FROM account LIMIT 5")
→ Auto-routes to acore_auth

sql(query="SELECT * FROM creature_templat LIMIT 1")
→ Error with suggestion: "Did you mean: creature_template?"

terrain

Query map, VMap, and MMap terrain data — terrain height, liquid, area IDs, navmesh tiles, and cross-tile pathfinding on the Detour navmesh.

Parameter Type Description
subcommand string Required. One of: list_maps, list_tiles, height, liquid, area, coord, tile_info, vmap_info, tile_stats, map_info, pathfind.
mapId string or number Map ID (numeric) or name (e.g. "Eastern Kingdoms", "571").
x, y, z number World coordinates (required by subcommand).
tileX, tileY number Tile coordinates 0-63 (for tile-level queries).
data_type string "maps", "vmaps", or "mmaps" (for list_tiles, tile_info).
x1, y1, z1, x2, y2, z2 number Start/end coordinates (for pathfind).
flying boolean Ignore height constraints (for pathfind, default false).

Subcommands:

Subcommand Purpose
list_maps List all maps with file counts (ADT, VMap, MMap)
list_tiles List tiles for a map (maps, vmaps, or mmaps)
height Terrain height at (x, y)
liquid Liquid type/height at (x, y, z)
area Area table ID at (x, y)
coord Convert world coordinates to grid/tile
tile_info MMap/VMap tile header info
vmap_info VMap model info for a tile
tile_stats Navmesh statistics for a tile (poly count, vertex count)
map_info MMap navmesh parameters for a map
pathfind A* pathfinding between two points with cross-tile support

Pathfinding:

The pathfind subcommand runs A* on the Detour navmesh with on-demand tile loading, cross-tile external edge resolution, and funnel-algorithm corridor steering. Coordinates are in world space. The AC world→Detour transform (world_y, world_z, world_x) is applied automatically.

terrain(subcommand="height", mapId=0, x=1620, y=1530)
→ {"height": 52.34, "map_id": 0, "position": {"x": 1620, "y": 1530}}

terrain(subcommand="list_tiles", mapId=571, data_type="mmaps")
→ 433 MMap tiles for Wintergrasp

terrain(subcommand="tile_stats", mapId=571, tileX=23, tileY=24)
→ {"poly_count": 3639, "vert_count": 5832, "tile": [23, 24]}

terrain(subcommand="pathfind", mapId=571, x1=4683, y1=3824, z1=355, x2=4538, y2=3230, z2=403)
→ {"found": true, "distance": 649.0, "raw_path_length": 38, "smooth_path_length": 3,
   "smooth_path": [{"x": 4683, "y": 3824, "z": 355}, {"x": 4528, "y": 3244, "z": 357}, {"x": 4538, "y": 3230, "z": 403}]}

Cross-tile pathfinding works automatically: tiles are loaded on-demand as the A* search expands, and external edges (DT_EXT_LINK) are resolved by finding matching polygons in neighbor tiles via BV-tree search. Long-distance paths (2000+ yards) across many tiles are supported.

Type Resolver

The resolve parameter on query enables type-aware field resolution for tables whose fields change meaning based on a type column. Resolution types:

  • "dbc" — resolve to DBC entries (e.g. LockEntry, SpellEntry, MapEntry)
  • "sql" — resolve to SQL tables (e.g. quest_template, gossip_menu, page_text)
  • "loot" — expand loot templates into item lists with names

Registry-driven resolution

Any table with cross-reference metadata in datastore_registry.json gets automatic field resolution via _resolve_generic(). Fields like faction, lootId, spellId are resolved to their target entries by name.

Specialized table resolvers

In addition to generic registry-driven resolution, these tables have dedicated resolver modules that enrich results with custom data:

Table Resolver module What it resolves
gameobject_template resolvers/gameobject.py type-aware data[0-19] annotation (lockId, lootId, spellId …)
smart_scripts resolvers/smart_scripts.py EVENT_ID/ACTION_ID/TARGET_ID → enum names + value meaning
quest_template resolvers/quest.py starter/ender NPCs, POIs, quest chain (prev/next/breadcrumb)
conditions resolvers/condition.py polymorphic SourceType → entity name, ConditionType (~49 types: AURA, QUEST, ITEM, ALIVE, CLASS, etc.), TYPEID/GENDER/RACE enums
achievement_criteria_data resolvers/achievement_criteria.py CriterionType-specific field interpretation
item_template resolvers/item.py loot template for openable items (Flags & 0x04)
Spell (DBC) resolvers/spell.py cast conditions from conditions table with full enum resolution
query(name="quest_template", id=4512, resolve=true)
→ Start/ender NPCs, chain info, POIs, plus faction/spell/item refs

query(name="Spell", id=15698, resolve=true)
→ Cast conditions: "OBJECT_ENTRY_GUID(UNIT)=creature_template [Cursed Ooze], NOT_ALIVE"

query(name="item_template", id=11912, resolve=["loot"])
→ Openable item → 6x Empty Cursed Jar, 6x Empty Tainted Jar

query(name="conditions", filter={"SourceTypeOrReferenceId": 17, "SourceEntry": 15698}, resolve=true)
→ Polymorphic: source type name, condition type enum, resolved entity names

query(name="gameobject_template", id=12345, resolve=["loot"], resolve_max=20)
→ Expand loot template into up to 20 item names

Setup

Requirements

  • Python 3.9+
  • pymysql — installed via requirements.txt into .venv
  • Access to an AzerothCore MySQL instance (for SQL tools)
  • DBC binary files and DBCfmt.h from the AzerothCore source/build
  • MMap data files (.mmtile, .mm) for terrain/pathfinding queries

Environment Variables

Variable Default
ACORE_DBC_PATH /root/azerothcore-wotlk/env/dist/bin/dbc
ACORE_FORMAT_FILE /root/azerothcore-wotlk/src/server/shared/DataStores/DBCfmt.h
ACORE_MMAP_PATH /root/azerothcore-wotlk/env/dist/bin/mmaps
ACORE_VMAP_PATH /root/azerothcore-wotlk/env/dist/bin/vmaps
ACORE_MAP_PATH /root/azerothcore-wotlk/env/dist/bin/maps
DB_HOST Auto-detected
DB_PORT 3306
DB_USER Auto-detected
DB_PASSWORD Auto-detected
DB_NAME acore_world

When DB_HOST and DB_USER are empty, the server attempts auto-detection from common AzerothCore configuration files.

Running

python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python3 server.py

Recommended: launch with the .venv Python (.venv/bin/python3). Without pymysql the server falls back to a mysql CLI subprocess — it works (params are interpolated and MYSQL_PWD is honored) but is slower.

If run via an MCP client, point the command at the venv's Python:

"command": ["/path/to/acore-data/.venv/bin/python3", "server.py"]

To test manually:

echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | .venv/bin/python3 server.py

pi (coding agent)

This project ships a first-class pi bridge at .pi/extensions/acore-data.ts. pi loads it automatically and registers the server's tools (query, lookup, list, sql, terrain) as native pi tools. It hardcodes no paths or credentials — it inherits the ambient environment and the server self-configures (DBC path defaults + DB creds auto-detected from worldserver.conf). To use a non-default project root or a non-default AzerothCore layout, export the relevant vars (ACORE_DATA_ROOT, DB_*, DBC_PATH, …) in pi's environment.

Testing

# Regression suite — 21 tests, the output-shape/strictness/protocol contract
.venv/bin/python3 tests/test_regression.py

# Integration tests — 75 tests (requires running MySQL + DBC files)
.venv/bin/python3 tests/test_integration.py

# Fast unit tests — 20 tests (no DB)
.venv/bin/python3 tests/test_helpers.py

# Everything at once
.venv/bin/python3 -m pytest tests/ -q

Output-size baselines (for LLM context budgeting):

.venv/bin/python3 scripts/capture_output_sizes.py /tmp/after
.venv/bin/python3 scripts/capture_output_sizes.py --compare /tmp/before /tmp/after

Project Structure

acore-data/
├── server.py                    # MCP server entry point (JSON-RPC over stdio)
├── datastore_registry.json      # Static metadata for all ~467 datastores
├── requirements.txt             # pymysql >= 1.1, pytest >= 7.0
│
├── core/
    │   ├── annotation.py            # DBC field annotation, filter conversion, schema errors
    │   ├── database.py              # MySQL connection (pymysql), table discovery, smart routing
    │   ├── dbc.py                   # WDBC binary file reader
    │   ├── enums.py                 # Shared enum dicts: condition types, SOURCE_TYPE, TYPEID …
    │   ├── formats.py               # DBCfmt.h parser (format strings → field types)
    │   ├── registry.py              # Datastore registry: name resolution, fuzzy matching
    │   ├── type_resolver.py         # Dispatcher + generic registry-driven resolution engine
    │   ├── resolvers/               # Specialized table-specific resolver modules
    │   └── terrain/                 # Terrain data: map/vmap/mmap readers, navmesh pathfinding
    │       ├── coords.py            # World ↔ tile coordinate conversions
    │       ├── map_reader.py        # ADT map file reader (height, liquid, area)
    │       ├── vmap_reader.py       # VMap model file reader
    │       ├── mmap_reader.py       # MMap navmesh tile index reader
    │       ├── detour_parser.py     # Detour tile parser (polygons, BV tree, vertices)
    │       ├── tile_manager.py      # On-demand tile loading, cross-tile link resolution
    │       └── pathfinder.py        # A* search, corridor steering, cross-tile pathfinding
    │
│       ├── __init__.py          # Resolver registry (table_name → func)
│       ├── gameobject.py        # data[0-19] annotation for GAMEOBJECT_TYPE subtypes
│       ├── smart_scripts.py     # EVENT_ID/ACTION_ID/TARGET_ID enum + value meaning
│       ├── quest.py             # Starter/ender NPCs, POIs, chain info (prev/next/breadcrumb)
│       ├── condition.py         # Polymorphic: SourceType → entity, ConditionType (~49 types), TYPEID/GENDER/RACE enums
│       ├── achievement_criteria.py  # CriterionType-specific field interpretation
│       ├── item.py              # Loot template for openable items (Flags & 0x04)
│       ├── spell.py             # Cast conditions from `conditions` table with full enum resolution
│       └── ref_utils.py         # Shared helpers: resolve_dbc_ref, resolve_sql_ref, batch_resolve_sql, resolve_loot_ref
│
├── tools/
    │   ├── query.py                 # Unified query tool (DBC + SQL + overlay merge)
    │   ├── lookup.py                # Schema/metadata lookup tool
    │   ├── list.py                  # Datastore listing tool
    │   ├── sql.py                   # Raw SQL execution with routing and suggestions
    │   └── terrain.py               # Terrain/pathfinding tool (map/vmap/mmap, A* navmesh)
    │
├── tests/
│   ├── test_integration.py      # Integration tests (live DB)
│   └── test_helpers.py          # Unit tests
│
├── docs/datastores/             # Technical reference for AzerothCore datastore internals
│   ├── README.md                # Overview of DBC pipeline, SQL overlay, format strings
│   ├── dbc-backed-stores.md
│   ├── sql-objectmgr-stores.md
│   ├── sql-manager-stores.md
│   ├── sql-auxiliary-stores.md
│   └── cross-reference.md
│
├── generators/                  # Registry tooling (docs/datastores is human reference)
│   ├── generate_registry.py     # docs->registry check (read-only; --write to regenerate)
│   └── generate_supplementary.py
│
└── scripts/                     # Utility scripts for cross-refs, column mappings, etc.
    ├── add_cross_references.py
    ├── update_sql_column_mappings.py
    ├── audit_registry.py        # registry health gate (structural drift checks)
    └── archive/                 # one-off registry migrations (phase_a–f, triage)

How It Works

   MCP Client (LLM / IDE / CLI)
         │
         │  JSON-RPC over stdio
         ▼
   server.py  ───────────────────────────────────────
         │
         ├─► registry.py        datastore_registry.json
         │     Name resolution, fuzzy matching        (462 entries)
         │
         ├─► tools/query.py     ┌──► dbc.py           .dbc binary files
         │     Unified query    │    WDBCReader        (Spell.dbc, Map.dbc, …)
         │                      │
         │                      ├──► database.py       MySQL
         │                      │    Smart routing     (acore_world, _characters, _auth)
         │                      │
         │                      ├──► annotation.py     Field annotation
         │                      │    DBC ↔ SQL merge   + type-aware resolution
         │                      │
         │                      └──► type_resolver.py  gameobject_template
         │                                           data[] → lockId/lootId/spellId
         │
         ├─► tools/lookup.py    Schema + live SQL columns + cross-refs
         │
         ├─► tools/list.py      Category filtering + search
         │
          ├─► tools/sql.py       Raw SQL with routing + typo suggestions
          │
          └─► tools/terrain.py   Terrain queries + pathfinding
                                   (map/vmap/mmap, A* navmesh)

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages