Skip to main content
Glama
blizzard-wow-mcp

blizzard-wow-mcp

by hjoliveira

blizzard-wow-mcp

CI

An MCP server exposing World of Warcraft character data from the Blizzard API.

It targets the MCP 2026-07-28 specification via the Python SDK v2 (mcp>=2). Everything it does is read-only: it authenticates with the OAuth2 client-credentials flow, so it can reach any public character profile, but none of the /profile/user/* endpoints — those require the authorization-code flow with an end user in the loop.

Requirements

Related MCP server: wow-api-mcp

Setup

uv sync

Then set your credentials:

export BLIZZARD_CLIENT_ID=...
export BLIZZARD_CLIENT_SECRET=...

Variable

Required

Default

Notes

BLIZZARD_CLIENT_ID

yes

—

Server refuses to start without it

BLIZZARD_CLIENT_SECRET

yes

—

Server refuses to start without it

BLIZZARD_REGION

no

eu

One of us, eu, apac. Realms differ between regions

BLIZZARD_LOCALE

no

en_GB

Any locale the region supports, e.g. en_US

Running

uv run server.py           # stdio (default)
uv run server.py --http    # stateless streamable HTTP on :8000/mcp

The package also installs a blizzard-wow-mcp console script that does the same thing.

Claude Desktop / MCP client config

{
  "mcpServers": {
    "wow": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/wow-armory-mcp2", "server.py"],
      "env": {
        "BLIZZARD_CLIENT_ID": "...",
        "BLIZZARD_CLIENT_SECRET": "...",
        "BLIZZARD_REGION": "eu"
      }
    }
  }
}

Verifying it works

In --http mode the server is stateless and returns plain JSON, so curl is enough to smoke-test it — no session ID and no initialized notification needed.

Start it in one shell:

BLIZZARD_CLIENT_ID=... BLIZZARD_CLIENT_SECRET=... uv run server.py --http

Check it is up and negotiating:

curl -s -X POST http://localhost:8000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'

List the tools — the quickest single check that the server is wired up:

curl -s -X POST http://localhost:8000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

Call a tool, which exercises the credentials and the round-trip to Blizzard. wow_find_realm is the cheapest one — it needs no character name:

curl -s -X POST http://localhost:8000/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"wow_find_realm","arguments":{"query":"twisting"}}}'

A few gotchas:

  • The Accept header must list both application/json and text/event-stream. That is a streamable-HTTP requirement, not optional.

  • If your shell has HTTPS_PROXY or ALL_PROXY set, add --noproxy '*' or curl will try to proxy localhost and hang.

  • A tool call that comes back with "isError": true and a 403 usually means bad or unset credentials, or that outbound access to oauth.battle.net is blocked.

Tools

Tool

Purpose

wow_character_summary

Level, class, spec, race, faction, guild, item level, achievement points, last login, Armory URL

wow_character_equipment

Every equipped item with slot, name, item ID, item level, quality, enchantments, stats, sockets and tier set

wow_character_mythic_keystone

Mythic+ season rating and best runs, sorted by rating. Defaults to the character's most recent season

wow_item_search

Find items by name, filtered by quality and item level, to get the ID wow_item needs

wow_item

An item's own record by ID: stats, weapon damage, effects, binding, durability and vendor prices

wow_character_resource

Escape hatch for any other character sub-resource — raid progression, talents, professions, PvP, reputations, collections, and so on

wow_find_realm

Resolve a realm name to the slug the other tools need

All tools are annotated read-only, idempotent and non-destructive.

Everything except wow_character_resource and wow_find_realm returns structured Pydantic models rather than raw Blizzard JSON. wow_character_resource returns the raw payload with Blizzard's self-referential key/href link objects stripped, which typically removes 30–50% of the tokens without losing anything a model would use. Its resource argument is constrained to a fixed list: achievements, appearance, collections/mounts, collections/pets, collections/toys, encounters/dungeons, encounters/raids, hunter-pets, professions, pvp-summary, quests/completed, reputations, specializations, statistics, titles.

Item stats: base vs. worn

There are two sources of item stats, and they answer different questions.

wow_item reads /data/wow/item/{id}, whose preview_item block carries the item's base form — Blizzard renders it at the item's default bonus list. The endpoint takes no bonus-list or context parameter, so for gear that scales (most modern raid and Mythic+ drops, which ship at many item levels under one item ID) the stats it reports will not match any particular character's copy.

wow_character_equipment reports what a character is actually wearing, rendered with that instance's real bonus list — so its stats, sockets and item_level are authoritative for that character. Each equipped item also carries an item_id, which is what you feed to wow_item.

Neither can resolve arbitrary bonus IDs — an auction listing's bonus_lists cannot be turned into stats through any public endpoint.

Searching for items

wow_item_search is the only way into the item data by name; Blizzard exposes no endpoint that enumerates items. Matching is token-based rather than substring, so Thunderfury matches and fury does not. Results are sorted by item level descending and carry classification only — feed an id to wow_item for the stat block.

The search API filters on a locale-qualified field name (name.en_GB, not name), which makes BLIZZARD_LOCALE load-bearing in a way it is not for any other endpoint. A locale the region does not serve yields either an empty result set or a 400; both are reported with a message naming the locale. If you change BLIZZARD_REGION, change BLIZZARD_LOCALE to match.

The filter encoding — locale-qualified fields, [min,max] ranges, _page / _pageSize — is taken from Blizzard's documentation rather than from a captured response, and lives entirely in _item_search_params. If a live call disagrees, that one function is the only thing to change.

Notes on names and realms

Realm names are slugified automatically — Kil'jaeden becomes kiljaeden, Área 52 becomes area-52 — and character names are lowercased and percent-encoded, so you can pass them as a player would write them.

If a lookup 404s, the likely causes are: the character does not exist on that realm, the realm slug is wrong, the character is below level 10, or it has not logged in since the last expansion. Call wow_find_realm to confirm the slug before retrying, and check BLIZZARD_REGION.

Blizzard's quota is 100 requests/second and 36,000/hour per client. The server caches the access token for its lifetime (refreshing 60s before expiry) and retries once on a 401, so ordinary use stays well inside that.

Development

Linting is configured for ruff (line length 100, targeting py314):

uvx ruff check .
uvx ruff format .

Tests

uv sync          # installs the dev group
uv run pytest

The suite is offline — every Blizzard call is served by an httpx.MockTransport, so no credentials and no network access are needed:

  • tests/test_helpers.py — realm slugging, character normalisation, slim(), timestamp conversion, and the localised-name fallback search results need.

  • tests/test_client.py — token caching and refresh, the single 401 retry, extra query parameters merging without clobbering the namespace, and the mapping from HTTP status codes to readable BlizzardError messages.

  • tests/test_tools.py — all seven tools, including field mapping, missing optional fields, equipment stat/socket/set parsing, item detail from preview_item, item search filter encoding and locale handling, Mythic+ season defaulting and run ordering, and realm-search matching.

CI

.github/workflows/ci.yml runs ruff and the test suite on Python 3.14 for every pull request, plus weekly on Mondays at 06:00 UTC and on demand via workflow dispatch.

Pull request runs use uv sync --locked, so they install exactly what uv.lock pins and fail if the lock has drifted from pyproject.toml. The weekly run uses --upgrade instead, resolving dependencies fresh so it surfaces upstream releases that break the server — which a run pinned to the lockfile would never catch.

License

MIT

Available Tools

5 tools
wow_character_equipmentWoW Character EquipmentA
Read-onlyIdempotent

Get every equipped item with slot, name, item level, quality and enchants.

Args: realm: Realm name or slug. character: Character name, case-insensitive.

Returns: EquipmentSummary with one entry per filled slot.

Inputs: realm (required), character (required). Returns: items, realm, character, equipped_item_level

wow_character_mythic_keystoneWoW Mythic+ ProfileA
Read-onlyIdempotent

Get a character's Mythic+ score and best dungeon runs for a season.

Args: realm: Realm name or slug. character: Character name, case-insensitive. season_id: Mythic+ season. Defaults to the character's most recent season.

Returns: MythicKeystoneSummary. Returns an error if the character has not completed a Mythic Keystone dungeon in that season.

Inputs: realm (required), character (required), season_id. Returns: realm, best_runs, character, season_id, +1 more

wow_character_resourceWoW Character Resource (raw)A
Read-onlyIdempotent

Fetch any other character sub-resource that has no dedicated tool.

Use this for raid progression ('encounters/raids'), talents ('specializations'), 'professions', 'pvp-summary', 'reputations', 'statistics', 'titles', 'appearance', collections and so on.

Args: realm: Realm name or slug. character: Character name, case-insensitive. resource: One of the allowed sub-resource paths.

Returns: The Blizzard payload with key/href link objects stripped out. 'achievements' and 'statistics' are very large — prefer a narrower resource where one exists.

Inputs: realm (required), resource (required), character (required). Returns: object

wow_character_summaryWoW Character SummaryA
Read-onlyIdempotent

Get the headline profile for a character: level, class, spec, race, faction, guild, item level, achievement points and last login.

Args: realm: Realm name or slug, e.g. 'Twisting Nether' or 'twisting-nether'. character: Character name, case-insensitive.

Returns: CharacterSummary. Call wow_character_equipment or wow_character_mythic_keystone for gear and Mythic+ detail.

Inputs: realm (required), character (required). Returns: Condensed form of the character profile summary endpoint.

wow_find_realmFind WoW Realm SlugA
Read-onlyIdempotent

Resolve a realm name to the slug the other tools need.

Use this when a character lookup 404s, to rule out a wrong realm slug.

Args: query: Full or partial realm name, e.g. 'twisting' or 'Área 52'.

Returns: Up to 10 matching realms as {name, slug} pairs, for the configured region.

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

    A4.6/5.0

    Scored across 5 tools

    Disambiguation5/5

    Each tool targets a distinct aspect of WoW character data: summary, equipment, Mythic+ details, generic sub-resources, and realm resolution. The generic resource tool is explicitly for sub-resources without dedicated tools, so no overlap exists.

    Naming Consistency4/5

    All tool names use lowercase snake_case and the 'wow_' prefix, with four of five sharing 'wow_character_*'. The exception is 'wow_find_realm', which uses a verb and different resource prefix, creating a minor but noticeable pattern deviation.

    Tool Count5/5

    Five tools is a well-scoped count for a character-information server. It covers core workflows without overwhelming users, and the generic resource tool prevents unnecessary tool proliferation.

    Completeness5/5

    The dedicated tools handle the most common character lookups, while the generic resource tool provides access to many additional sub-resources (raids, talents, professions, etc.). This effectively covers the domain of WoW character data without significant gaps.

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      C
      maintenance
      A comprehensive MCP server that wraps the complete World of Warcraft retail API into 197 tools for Game Data and Profile information. It enables users to query character statistics, achievements, collections, and game mechanics across all global regions and locales.
      13
      -
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server that exposes structured World of Warcraft API data (functions, deprecated replacements, enums, events, widget methods) to AI agents, enabling querying and exploration of WoW API without wiki parsing.
      30 npm
      15
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server that gives LLMs live access to warcraft.wiki.gg API documentation with behavioral notes, restrictions, and patch history for World of Warcraft APIs.
      1
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server for World of Warcraft that provides 26 tools for accessing characters, guilds, realms, game data, and auction house info via Blizzard's Battle.net APIs. It enables natural-language queries for character audits, Mythic+ reviews, market analysis, and more.
      MIT