dreamingazeroth-mcp
by Pliskin92
README.md
# dreamingazeroth-mcp
MCP server for **Dreaming Azeroth**, a WotLK 3.3.5a private server running AzerothCore + mod-playerbots. It gives an AI client — Claude Code, Claude Desktop, or anything else that speaks [Model Context Protocol](https://modelcontextprotocol.io) — read access to the realm's databases and, behind explicit opt-in gates, the ability to run GM commands, edit config and code, and apply content changes.
Companion repos: [dreamingazeroth-gui](https://github.com/Pliskin92/dreamingazeroth-gui) (web panel and the AzerothCore install script) and [dreamingazeroth-addons](https://github.com/Pliskin92/dreamingazeroth-addons) (client addons).
Designed to run **on the server box itself**: MySQL and SOAP are both reached over loopback, so nothing new has to be exposed to the network.
## What it is not
This does not talk to Blizzard's Battle.net API. Everything comes from your own `acore_auth` / `acore_characters` / `acore_world` / `acore_playerbots` schemas and your live worldserver, so custom items, custom loot and bot activity all show up exactly as they are on your realm.
## Capabilities
Four tiers, each independently enabled. Reads always work; **everything else is off until you turn it on.**
| Tier | Env var | What it unlocks |
| --- | --- | --- |
| `read` | — always on | Every database query: characters, accounts, guilds, items, quests, loot, auctions, logs, server status |
| `soap` | `AC_ENABLE_SOAP` | GM commands through the running worldserver: announce, kick, teleport, mail, reload, account admin, shutdown |
| `write` | `AC_ENABLE_WRITE` | Editing files inside `AC_WRITABLE_PATHS`, and applying SQL with automatic backups |
| `exec` | `AC_ENABLE_EXEC` | `systemctl` service control, CMake builds, `mysqldump` backups |
A tool whose tier is disabled is still listed, so the model can see it exists — it just fails with the exact env var to set.
## Tools
<details>
<summary><b>Ops and monitoring</b> (read)</summary>
- `ac_server_status` — services, population, database reachability, realm list, live SOAP banner
- `ac_online_players` — who is connected, with real players separated from RNDBOT playerbots
- `ac_population_report` — accounts, characters by level/class/race/faction, gold totals
- `ac_server_logs` — tail `Server.log` / `Auth.log` or the systemd journal, with a `contains` filter
- `ac_db_health` — schema sizes, largest tables, MySQL connection state
- `ac_account_lookup` — accounts by name/email/id/IP with GM level, bans and characters
</details>
<details>
<summary><b>Players</b> (read)</summary>
- `ac_character_search` — partial-name search with level/class/online filters
- `ac_character_profile` — full profile, optionally with equipment, stats, talents, skills, social
- `ac_character_inventory` — equipped gear and bags joined to `item_template`
- `ac_guild_info` — guild details and roster, or every guild ranked by size
- `ac_character_mail` — inbox with money and item attachments
</details>
<details>
<summary><b>Game content</b> (read)</summary>
- `ac_item_search` / `ac_item_details` — items, plus what drops them, who sells them, which quests reward them
- `ac_creature_search` — NPCs with loot tables and spawn points
- `ac_quest_lookup` — quests with objectives and rewards
- `ac_spell_lookup` — server-side spell data (limited by design; see Known limits)
- `ac_db_describe` — tables and columns, for confirming schema before writing SQL
- `ac_sql_query` — read-only `SELECT` escape hatch for anything not covered
</details>
<details>
<summary><b>Economy</b> (read)</summary>
- `ac_auction_search` — AH price statistics per item, split by bot vs player sellers
- `ac_economy_report` — gold in circulation, wealthiest characters, guild banks, gold in mail
- `ac_playerbots_status` — bot population, level spread, and their share of AH supply
</details>
<details>
<summary><b>GM commands</b> (soap)</summary>
- `ac_gm_commands` — the catalogue of runnable commands and their parameters
- `ac_gm_command` — run one by key with typed arguments
- `ac_announce` — broadcast chat text or an on-screen banner
- `ac_player_action` — kick, revive, summon, teleport an online character
- `ac_send_mail` — mail gold and items (the safe way to grant anything)
- `ac_reload` — reload `worldserver.conf` or one world table
- `ac_server_shutdown` — graceful shutdown/restart with a player countdown
- `ac_account_admin` — create accounts, reset passwords, set GM level, ban/unban
- `ac_save_all` — flush online characters to MySQL before reading their rows
</details>
<details>
<summary><b>Code, config and content</b> (write / exec)</summary>
- `ac_server_layout` — paths, installed modules, enabled capabilities, and the workflow for each kind of change
- `ac_list_files` / `ac_read_file` — browse and read the source tree and configs
- `ac_write_file` — write inside the allowlist, keeping a timestamped `.bak`
- `ac_apply_sql` — transactional SQL with a `dryRun` mode and `mysqldump` backups
- `ac_read_config` — grep settings out of `.conf` files without dumping them whole
- `ac_service_control` — status always; start/stop/restart with `exec`
- `ac_build` — CMake rebuild after a C++ change
- `ac_git_status` — read-only git against the source or a module
</details>
Plus 5 prompts (`health-check`, `investigate-player`, `add-content`, `tune-rates`, `economy-review`) and two resources: `ac://guide` and `ac://schema`.
## Two things that will bite you otherwise
**Online characters are stale in the database.** Worldserver holds logged-in characters in memory and only flushes on a timer and at logout. Any character query for someone who is online can be minutes behind. Call `ac_save_all` first when the exact number matters. Every affected tool says so in its output rather than letting you assume the row is current.
**Never write to a live player's rows.** `UPDATE characters SET money = ...` for someone who is online gets silently overwritten at the next save — the in-memory copy wins. Grant items and gold with `ac_send_mail`, change accounts with `ac_account_admin`. Both go through the running worldserver, so the change sticks and the server's own bookkeeping stays correct.
## Which path for which change
| Change | How | Restart needed? |
| --- | --- | --- |
| Content — items, loot, vendors, NPCs, quests | `ac_apply_sql`, then `ac_reload` on the table | No |
| A setting in `worldserver.conf` | `ac_write_file`, then `ac_reload` target `config` | Only for settings not re-read live |
| Module config, e.g. `playerbots.conf` | `ac_write_file`, then restart worldserver | Usually yes |
| C++ in the core or a module | `ac_write_file`, `ac_build`, restart | Yes |
Reach for SQL before C++ — most gameplay changes on AzerothCore are data, and data changes apply live.
## Install
On the server, as the user that owns the AzerothCore checkout:
```bash
git clone https://github.com/Pliskin92/dreamingazeroth-mcp.git
cd dreamingazeroth-mcp
./install/install-mcp.sh --with-systemd --start
```
The script builds, deploys to `/opt/dreamingazeroth-mcp`, creates `.env` from `.env.example`, pulls `DB_PASS` out of `~/azerothcore/.db_credentials`, generates an `MCP_AUTH_TOKEN`, and installs the systemd unit. Re-running it is safe and never touches an existing `.env`.
Verify:
```bash
curl -s http://127.0.0.1:8080/readyz | jq
```
### Enabling the write and exec tiers
Edit `/opt/dreamingazeroth-mcp/.env`:
```bash
AC_ENABLE_SOAP=true
SOAP_USER=panel_soap # any GM account
SOAP_PASS=...
AC_ENABLE_WRITE=true
AC_WRITABLE_PATHS=/home/youruser/azerothcore/source/modules,/home/youruser/azerothcore/env/dist/etc
AC_ENABLE_EXEC=true
```
SOAP also needs `SOAP.Enabled = 1` in `worldserver.conf`.
`AC_ENABLE_WRITE` needs the systemd unit to allow those paths — `ProtectHome=read-only` is on by default, so uncomment and adjust `ReadWritePaths=` in the unit file.
`AC_ENABLE_EXEC` needs passwordless sudo for the service actions:
```
# /etc/sudoers.d/dreamingazeroth-mcp
youruser ALL=(root) NOPASSWD: /bin/systemctl start azerothcore-worldserver, \
/bin/systemctl stop azerothcore-worldserver, \
/bin/systemctl restart azerothcore-worldserver, \
/bin/systemctl start azerothcore-authserver, \
/bin/systemctl stop azerothcore-authserver, \
/bin/systemctl restart azerothcore-authserver
```
Scope it to exactly those units. A blanket `NOPASSWD: ALL` would make the MCP endpoint equivalent to root on the box.
## Connecting a client
**Claude Code**, over the LAN from your desktop:
```bash
claude mcp add --transport http dreamingazeroth \
http://azeroth.local:8080/mcp \
--header "Authorization: Bearer $MCP_AUTH_TOKEN"
```
That needs `HOST=0.0.0.0` and `MCP_AUTH_TOKEN` set in `.env`.
**stdio**, when the client runs on the server itself:
```json
{
"mcpServers": {
"dreamingazeroth": {
"command": "node",
"args": ["/opt/dreamingazeroth-mcp/dist/index.js", "--stdio"]
}
}
}
```
## Security
The endpoint can read your entire player database and, with the gates open, change the running realm. It binds to `127.0.0.1` by default.
- **`MCP_AUTH_TOKEN` is mandatory** for any non-loopback bind. The server logs a warning at startup if it is unset.
- **`azeroth` is LAN-only** (no public IP, no port forwarding — see the gui repo's TODO). Keep it that way; there is no reason to expose this to the internet.
- **SOAP commands are an allowlist, not a passthrough.** You pick a command by key and supply typed parameters — free-text command strings are never sent. This is deliberate: the previous version of this stack shipped a SOAP command-injection hole (see the `uncompliantexperience` audit referenced in the gui repo), and structuring the interface this way is what prevents that class of bug rather than trusting each call site to escape correctly.
- **AzerothCore's console parser has no quoting.** It splits arguments on whitespace, full stop — `account create "My Name" pw` really does create an account called `"My`. Arguments are typed `bare` / `quoted` / `rest` / `int` accordingly, and a value that cannot be expressed safely is rejected with an explanation instead of being mangled. Newlines and control characters are refused everywhere, since they could smuggle in a second command.
- **File access is a realpath-checked allowlist.** A symlink inside a writable root cannot be used to escape it.
- **`ac_sql_query` is read-only** — positive check for `SELECT`/`SHOW`/`DESCRIBE`/`EXPLAIN`, a keyword denylist for anything hidden in a subquery, and a row cap.
- **Destructive tools require `confirm: true`.** Not a substitute for the capability gates — a second, per-call deliberate step.
## Known limits
- **Spell data is mostly client-side.** Stock AzerothCore reads spells from the client's DBC files; `spell_dbc` only holds custom or overridden entries. `ac_spell_lookup` returns what the server actually knows and says so when a spell is not there, rather than inventing an answer.
- **Column names drift between forks.** This targets the [mod-playerbots/azerothcore-wotlk](https://github.com/mod-playerbots/azerothcore-wotlk) `Playerbot` branch. Where a schema difference is known — `creature.id1` vs `creature.id`, `talent_dbc` vs `Talent` — it is handled, but run `ac_db_describe` before trusting a hand-written query.
- **`ac_build` is slow.** A full rebuild is tens of minutes and saturates CPU. It will degrade a live realm.
- **`acore_playerbots` is module-owned.** Read it; do not write it. mod-playerbots rewrites that schema on its own schedule.
- **Multi-item mail sends one mail per item.** The console parser cannot express several `id:count` pairs in one argument, so `ac_send_mail` splits them and says so in its result.
## Development
```bash
npm install
npm run dev # tsx watch, HTTP transport
npm run stdio # stdio transport against a local .env
npm run typecheck
npm test
```
Layout:
```
src/
ac/ AzerothCore integration: db pools, SOAP, file sandbox, process runner, WotLK constants
tools/ MCP tools grouped by area (ops, characters, content, economy, admin, devops)
util/ Cache, formatting, errors
config.ts Environment parsing and capability gates
http.ts Streamable HTTP transport, sessions, auth, CORS, health
stdio.ts stdio transport
server.ts Wires backends into an McpServer
```
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues