Skip to main content
Glama

OpenDota MCP

A dependency-free OpenDota API client for MCP-compatible assistants and the terminal. It runs locally over stdio, uses Node.js 20 or later, and needs no API key for public endpoints.

  • Four MCP tools: endpoint discovery, endpoint details, API calls, and Steam ID conversion.

  • CLI commands for matches, players, heroes, and generic GET requests.

  • A bundled endpoint catalog for offline discovery.

  • Request pacing, timeouts, bounded GET retries, and API-key redaction in errors.

  • Explicit POST operations for replay parsing and player refresh.

This is an independent community client. It is not an official OpenDota or Valve product.

Install from GitHub

Clone the repository:

git clone https://github.com/HFUBlackRabbit/opendota-mcp.git
cd opendota-mcp
node server.mjs --help
node server.mjs --version

There are no runtime or development npm dependencies to install. npm start launches the MCP server; npm test runs the local test suite. The package is marked private to prevent accidental npm registry publication; GitHub cloning and local use work normally.

Related MCP server: opendota-mcp-server

Connect an MCP client

Use an absolute path to the cloned server.mjs. If a desktop client cannot find node, use an absolute path to your Node.js executable as command.

For clients using JSON configuration, see examples/mcp.json:

{
  "mcpServers": {
    "opendota": {
      "command": "node",
      "args": ["/absolute/path/to/opendota-mcp/server.mjs"]
    }
  }
}

For Codex, merge the following section into your user configuration. Do not replace unrelated settings. See examples/codex.toml.

[mcp_servers.opendota]
command = "node"
args = ["/absolute/path/to/opendota-mcp/server.mjs"]

Use Windows paths in the form supported by your client, such as C:/tools/opendota-mcp/server.mjs. The server does not bind an HTTP port. Without CLI arguments it waits for newline-delimited JSON-RPC messages on stdin and writes protocol messages to stdout.

Tools

Tool

Purpose

opendota_find_endpoints

Search the bundled catalog with query and limit

opendota_endpoint_info

Read an endpoint's description and parameters using path and optional method

opendota_api_call

Make a GET or an allowed POST using path, params, optional fields, and max_chars

opendota_account_id

Convert a Steam32 or SteamID64 value to OpenDota account_id

Start by finding an endpoint, then inspect its parameters. A compact API call looks like this:

{
  "path": "/players/1/matches",
  "params": {
    "limit": 5,
    "project": ["kills", "deaths"]
  },
  "fields": ["match_id", "kills", "deaths"],
  "max_chars": 40000
}

fields selects top-level properties of an object or of every element in an array after the API response arrives. It does not project fields inside players. Use upstream project/limit when the endpoint supports them.

MCP output defaults to 40,000 characters, with a configurable limit of 1,000–200,000. Larger results become a JSON object with truncated, total_chars, hint, and preview; that preview is not a complete match. The CLI emits the full JSON response, which is useful for replay analysis that requires all ten players.

Use the CLI

Commands that contact the API require network access. IDs below illustrate the syntax; choose a player or match whose public data is available.

node server.mjs heroes
node server.mjs heroes stats
node server.mjs player 1
node server.mjs player 1 recentMatches
node server.mjs player 1 heroes -p having=10
node server.mjs match 271145478
node server.mjs search Dendi
node server.mjs endpoints "players matches"
node server.mjs info "/players/{account_id}/matches"
node server.mjs account-id "76561197960265729"

Generic GET requests accept paths relative to the API base: /heroes, not a full URL, /api prefix, or query string. Repeat a parameter for arrays:

node server.mjs get /players/1/matches -p limit=5 -p project=kills -p project=deaths --compact

project=kills,deaths is not equivalent to two project parameters. SteamID64 values must be passed as strings in JSON to preserve their precision. The player, refresh, and account-id commands accept Steam32 or SteamID64.

CLI API results are JSON on stdout; failures are written to stderr with a nonzero exit code. --help and --version return plain text. The package exposes both opendota and opendota-mcp executable aliases when installed as a local package.

Operations that change upstream state

These commands send POST requests and are never automatically retried:

# Request replay parsing. This creates an upstream job.
node server.mjs parse 271145478

# Use job.jobId from the previous response, not the match ID.
node server.mjs job 12345

# Request an upstream player-data refresh.
node server.mjs refresh 1

Only POST /request/{match_id} and /players/{account_id}/refresh are allowed. A parse request counts as 10 calls toward OpenDota rate limits, but one call for billing. Check OpenDota's current limits and terms before large workloads.

GET /request/{jobId} may return a job or null. A disappeared job does not prove successful parsing: fetch the match again and inspect its parsed fields. Replay availability and public player history are controlled upstream.

Configuration and credentials

Environment variable

Default

Purpose

OPENDOTA_API_KEY

unset

Optional OpenDota key

OPENDOTA_BASE_URL

https://api.opendota.com/api

Trusted API base; CLI override: --base-url

OPENDOTA_TIMEOUT_MS

30000

Per-attempt timeout; --timeout-ms

OPENDOTA_INTERVAL_MS

1100

Interval per rate-limit budget unit in one process; --interval-ms

OPENDOTA_RETRIES

2

Additional GET attempts; --retries

Supply credentials using your process environment or MCP client's secret configuration. .env files are not loaded automatically. The key is sent as OpenDota's api_key query parameter; passing it through CLI arguments or tool params is rejected.

Set OPENDOTA_BASE_URL only to a trusted server: the configured API key is sent to that base URL. Error output redacts the configured key, but that does not make arbitrary upstream content or HTTP logs safe to publish. Remove credentials and private data before attaching reports.

The queue is local to one process. Each GET or player refresh uses one interval; a replay-parse POST reserves ten intervals before the next request. Multiple CLI processes or MCP instances do not share a rate-limit budget. GET retries cover HTTP 429/502/503/504 and network timeouts/errors. Retry-After is respected; requests are not retried early when the requested delay exceeds the retry budget. POST is not retried.

Data limitations and catalog provenance

  • Player IDs, match IDs, and parse-job IDs are different identifiers.

  • recentMatches returns a limited recent sample; use matches with filters and pagination for history.

  • Player aggregates usually apply significant=1. Use significant=0 when appropriate for nonstandard modes, and separate Turbo from ranked or tournament data.

  • Amateur league team summaries can be empty even when direct match IDs are available. Consult /leagues/{league_id}/matchIds for the league's catalog; a missing parse or absent field is not a zero statistic.

  • /health is an API endpoint; OpenDota's service /status is outside the API base and is not a relative CLI API route.

endpoints.json is a local OpenAPI-derived snapshot, retrieved 2026-10-19, API version 31.1.0. Endpoint discovery works offline and does not guarantee every route or field remains available on the live service. Sources: API specification, player parameters, and OpenDota documentation. See THIRD_PARTY_NOTICES.md for attribution.

Development and verification

npm test
npm run pack:check

The Node test suite uses local HTTP fixtures and child processes. It checks CLI behavior, MCP initialization/tool calls, filtering, output truncation, retries, and credential redaction without calling the public API. The GitHub Actions workflow is configured to run it on Node.js 22 and 24 on Linux and Windows.

The repository also contains a small Postman simulation. It is optional and is not a runtime dependency. Fixtures are synthetic contract examples, not recorded player data. To exercise the real HTTP client against it, install Postman CLI 1.75.0 or later and run:

postman simulation run postman/simulations/opendota.sim.yaml --port 4900 --no-history --output ndjson

After its listening event, run the checker in a separate terminal. It accepts only a loopback base URL and replaces any inherited API key with empty or synthetic test values:

node scripts/verify-simulation.mjs --base-url http://127.0.0.1:4900/opendota/api

Stop the simulation with Ctrl+C after checking. See docs/verification.md for the expected request counts and recorded evidence. Mocked checks establish behavior against those fixtures, not live OpenDota availability or completeness. No GitHub Actions step submits replay-parse or player-refresh requests to the public service.

License

MIT. See LICENSE. OpenDota's source material is attributed separately in THIRD_PARTY_NOTICES.md.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables querying Dota 2 data (hero stats, player matches, player heroes) via the STRATZ API, with support for arbitrary GraphQL queries.
      311 npm
      MIT
    • A
      license
      B
      quality
      B
      maintenance
      Enables LLMs and agents to query Dota 2 matches, players, heroes, teams, and pro scene data through the OpenDota API, with numeric IDs resolved to localized names and readable labels.
      42
      58 npm
      MIT