opendota-mcp-server
Provides access to Dota 2 player statistics, match data, hero information, item builds, and matchup data through the OpenDota API, enabling AI agents to query player profiles, recent matches, hero performance, and more.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@opendota-mcp-serverget recent matches for player 86745991"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
OpenDota MCP Server
A Model Context Protocol (MCP) server that provides seamless access to the OpenDota API for querying Dota 2 player statistics, match data, and hero information through AI assistants like Claude.
Features
20+ Tools across 5 categories for comprehensive Dota 2 data access
Natural Language Support - Fuzzy matching for hero names, lane positions, and stat fields
Dual Transport - Supports both stdio (local) and HTTP (remote) modes
Rate Limiting - Built-in protection respecting OpenDota API limits
Reference Data Caching - Fast lookups with local hero/item data
Related MCP server: LolByte MCP Server
Available Tools
Player Tools (6 tools)
Query player statistics, win/loss records, hero preferences, and performance metrics:
get_player_info- Get player profile, win rate, and favorite heroesget_player_win_loss- Win/loss stats with advanced filteringget_heroes_played- Detailed statistics for all heroes playedget_player_peers- Find frequent teammatesget_player_totals- Aggregated player statisticsget_player_histograms- Distribution of matches across statistical fields
Hero Tools (4 tools)
Access hero information, matchups, and item builds:
get_heroes- List all heroes with attributesget_hero_matchups- Matchup data and win ratesget_hero_item_popularity- Popular item builds by game phaseget_hero_stats- Aggregated hero statistics and pick rates
Match Tools (3 tools)
Retrieve detailed match information and parsed data:
get_match_details- Comprehensive match informationget_parsed_match_details- Parsed match data with advanced analyticsget_player_recent_matches- Recent matches for a player
Lookup Tools (4 tools)
Convert names to IDs with fuzzy matching:
get_hero_id_by_name- Hero name → ID (handles typos)get_hero_by_id- Get hero details by IDconvert_lane_name_to_id- Lane/position → lane_role IDresolve_time_name- Time period names → OpenDota filter parameters
Misc Tools (3 tools)
Search and utility functions:
search_players- Search for players by nameget_constants- Get OpenDota constants and reference dataget_player_counts- Get player count by rank tier
Installation
Prerequisites
Python 3.10 or higher
pip package manager
Install from Source
# Clone the repository
git clone https://github.com/hkaanengin/opendota-mcp-server.git
cd opendota-mcp-server
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install with dev dependencies
pip install -e ".[dev]"
# Or install runtime only
pip install -e .Usage
Option 1: Local Server (stdio mode)
Best for Claude Desktop integration on your local machine.
Using Claude Desktop
Config file location:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.jsonLinux:
~/.config/Claude/claude_desktop_config.json
Recommended: Using the installed command
After running pip install -e ., add to your claude_desktop_config.json:
{
"mcpServers": {
"opendota": {
"command": "/path/to/your/opendota-mcp-server/.venv/bin/opendota-mcp"
}
}
}Note: Replace
/path/to/your/opendota-mcp-serverwith the actual path where you cloned this repository. On macOS/Linux, runpwdin the project directory to find this path. On Windows, runcdto see the full path.
Alternative: Using Python module directly
{
"mcpServers": {
"opendota": {
"command": "/path/to/your/opendota-mcp-server/.venv/bin/python",
"args": ["-m", "opendota_mcp.server"]
}
}
}Alternative: Using the shell script
{
"mcpServers": {
"opendota": {
"command": "/path/to/your/opendota-mcp-server/start_mcp.sh"
}
}
}Using Docker:
{
"mcpServers": {
"opendota": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e", "MCP_TRANSPORT=stdio",
"hkaanengin/opendota-mcp-server:latest"
]
}
}
}After updating the config:
Save the file
Restart Claude Desktop completely
Start a new conversation
Your OpenDota tools should now be available!
Manual Testing
# Run the server directly
opendota-mcp
# Or using Python module
python -m opendota_mcp.server
# Or using the shell script
./start_mcp.shOption 2: Remote Server (HTTP mode)
Access a deployed server from anywhere.
Public Server
Use the publicly deployed server at:
https://opendota-mcp-server-jylza6gata-ew.a.run.app/mcpClaude Desktop (Remote Connection)
Add to your claude_desktop_config.json:
{
"mcpServers": {
"opendota": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-remote-http",
"https://opendota-mcp-server-jylza6gata-ew.a.run.app/mcp"
]
}
}
}Claude.ai Web Interface
Go to Claude.ai → Settings → MCP Servers
Click "Add Server"
Enter Server URL:
https://opendota-mcp-server-jylza6gata-ew.a.run.app/mcpSave and start chatting!
Option 3: Deploy Your Own
Deploy to Google Cloud Run or any container platform:
# Build Docker image
docker build -t opendota-mcp-server .
# Run in HTTP mode
docker run -p 8080:8080 \
-e MCP_TRANSPORT=http \
-e PORT=8080 \
opendota-mcp-serverConfiguration
Environment Variables
Create a .env file in the project root (or copy from .env.example):
# OpenDota API Key (optional)
# Get your API key from: https://www.opendota.com/api-keys
OPENDOTA_API_KEY=
# Logging level (DEBUG, INFO, WARNING, ERROR)
LOG_LEVEL=INFO
# Transport mode (stdio or http)
MCP_TRANSPORT=stdio
# HTTP server port (for HTTP mode)
PORT=8080OpenDota API Key (Optional)
An API key is optional but recommended for higher rate limits and better performance.
Rate Limits:
Without API key: 60 requests/minute (anonymous)
With API key: Higher limits for registered users
How to get an API key:
Sign in with your Steam account
Click "Create API Key"
Copy the generated API key
Add it to your
.envfile:OPENDOTA_API_KEY=your-api-key-hereRestart the server
The server will automatically detect and use the API key when configured. You'll see a confirmation message in the logs:
✅ OpenDota API key configured (higher rate limits enabled)Contributing
Contributions are welcome! Please:
Fork the repository
Create a feature branch (
git checkout -b feature/amazing-feature)Commit your changes (
git commit -m 'Add amazing feature')Push to the branch (
git push origin feature/amazing-feature)Open a Pull Request
Acknowledgments
OpenDota for providing the excellent Dota 2 API
FastMCP for the MCP framework
The Model Context Protocol team at Anthropic
The Dota 2 community
Support
For issues, questions, or contributions:
Open an issue on GitHub
Check existing issues for solutions
Review the OpenDota API documentation
Made with ❤️ for the Dota 2 community
Available Tools
18 toolsget_aghs_detailsA
Get Aghanim's Scepter and Shard upgrade details for a hero with fuzzy matching.
Use this when users ask about Aghanim's upgrades:
"What does Aghanim's Scepter do for Pudge?"
"Show me Anti-Mage's shard upgrade"
"What are Invoker's Aghs upgrades?"
"Does Rubick have a shard?"
Supports fuzzy matching for hero names:
"Pudge", "pudge", "Anti-Mage", "antimage" all work
"Shadow Fiend", "shadowfiend", "sf" all work (if alias exists)
Args: hero: Hero name (display name, internal name, fuzzy match) or hero ID Examples: "Pudge", "antimage", "Shadow Fiend", 1
Returns: Dictionary with Aghanim's upgrade information: - hero_id: Hero ID - hero_name: Internal hero name (e.g., "npc_dota_hero_pudge") - has_scepter: Whether the hero has a scepter upgrade (bool) - scepter_desc: Description of scepter upgrade (if has_scepter) - scepter_skill_name: Name of skill affected by scepter (if has_scepter) - scepter_new_skill: Whether scepter adds a new skill (bool, if has_scepter) - has_shard: Whether the hero has a shard upgrade (bool) - shard_desc: Description of shard upgrade (if has_shard) - shard_skill_name: Name of skill affected by shard (if has_shard) - shard_new_skill: Whether shard adds a new skill (bool, if has_shard)
Examples: get_aghs_details("Pudge") → Full Aghanim's info for Pudge get_aghs_details("Anti-Mage") → Scepter and Shard details for Anti-Mage get_aghs_details(1) → Aghanim's details by hero ID
| Name | Required | Description | Default |
|---|---|---|---|
| hero | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It transparently explains fuzzy matching behavior, accepts both names and IDs, and details the exact return dictionary fields. It does not mention error handling or edge cases (e.g., unknown hero), but the provided information gives a solid behavioral model.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (use cases, fuzzy matching, args, returns, examples). Every sentence provides useful information or an example, with no unnecessary filler. While longer than minimal descriptions, the added complexity of fuzzy matching and return format justifies the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with a single parameter and an output schema, the description is exceptionally complete. It covers when to use it, input formats, matching behavior, and provides a detailed breakdown of the return dictionary and examples. It leaves no significant gaps for an agent to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only the parameter name 'hero' with a loose anyOf type, and schema description coverage is 0%. The description fully compensates by explaining acceptable forms (display name, internal name, fuzzy match, hero ID) with concrete examples like 'Pudge', 'antimage', and 1. This adds deep semantic meaning beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool gets Aghanim's Scepter and Shard upgrade details for a hero, and explicitly distinguishes it from sibling tools like get_hero_details or get_item_details by focusing specifically on Aghanim's upgrades. The verb 'Get' and resource 'Aghanim's upgrade details' are specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage guidance: 'Use this when users ask about Aghanim's upgrades' and gives concrete example queries. It does not explicitly mention when not to use it or name alternative tools, but the context is clear enough to steer an agent correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_benchmarksA
Get statistical benchmarks for a hero (average performance metrics across all skill levels).
Use this when users ask:
"What are the average stats for [hero]?"
"What's a good GPM/XPM for [hero]?"
"Show me benchmark stats for [hero]"
"What's the typical performance for [hero]?"
"How do I compare to other [hero] players?"
Returns percentile-based benchmarks showing what constitutes below-average, average, good, and exceptional performance for various metrics on this hero. Useful for comparing a player's performance to the general population.
Supports both hero IDs and natural language hero names.
Args: hero_id: Hero to get benchmarks for. Accepts: - Integer: Hero ID (e.g., 86 for Rubick) - String: Hero name (e.g., "Rubick", "Anti-Mage")
Returns: Dictionary containing benchmark statistics organized by metric. Each metric shows percentile breakdowns (e.g., 0.1, 0.25, 0.5, 0.75, 0.9) representing the 10th, 25th, 50th (median), 75th, and 90th percentiles.
Common metrics included:
- gold_per_min: GPM benchmarks by percentile
- xp_per_min: XPM benchmarks by percentile
- kills_per_min, deaths_per_min, assists_per_min: KDA-related benchmarks
- last_hits_per_min: Farming efficiency benchmarks
- hero_damage_per_min: Combat effectiveness benchmarks
- hero_healing_per_min: Support/healing benchmarks
- tower_damage: Objective damage benchmarks
- stuns_per_min: Crowd control benchmarks
- And many more...Common queries: - General benchmarks: get_benchmarks("Rubick") - Compare player stats: First get player stats with get_player_totals(), then compare to benchmarks from this function
Example: get_benchmarks("Rubick") -> { "gold_per_min": { "0.1": 250.5, # 10th percentile - below average "0.25": 310.2, # 25th percentile - low average "0.5": 380.7, # 50th percentile (median) - average "0.75": 450.3, # 75th percentile - above average "0.9": 520.8 # 90th percentile - excellent }, "kills_per_min": { "0.1": 0.15, "0.5": 0.35, "0.9": 0.65 }, ... }
Interpretation: If a player has 460 GPM on Rubick, they're performing around the 75th percentile (better than 75% of Rubick players).
| Name | Required | Description | Default |
|---|---|---|---|
| hero_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It explains the return is percentile-based, shows a detailed example output with percentiles, and describes how to interpret results (e.g., 'If a player has 460 GPM on Rubick, they're performing around the 75th percentile'). This is exemplary transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections and front-loaded purpose, but it is somewhat lengthy. There is minor redundancy between the initial bullet list of queries and the later 'Common queries' section. Still, every section contributes useful information, and the example output earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter tool with no annotations, the description is remarkably complete. It covers input formats, output structure, common metrics, example usage, and interpretation. Even though an output schema exists, the description's Returns section adds clarity about percentile semantics and metric names.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema only defines hero_id as integer or string with zero description. The tool description compensates fully by stating it accepts hero IDs (e.g., 86) or hero names (e.g., 'Rubick', 'Anti-Mage'), giving examples and clarifying the flexible input format. This adds substantial meaning beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Get statistical benchmarks for a hero' and clarifies it provides 'average performance metrics across all skill levels.' This clearly distinguishes it from sibling tools like get_player_histograms or get_player_totals by focusing on percentile-based benchmarks for a single hero.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly lists example user queries (e.g., 'What are the average stats for [hero]?') and even recommends a workflow: 'First get player stats with get_player_totals(), then compare to benchmarks from this function.' This names an alternative tool and provides sequencing, exceeding the basic 'when to use' requirement.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_hero_detailsA
Get detailed hero information by hero name or ID with fuzzy matching.
Use this when users ask about heroes:
"What are Anti-Mage's stats?"
"Tell me about Pudge"
"Show me Invoker details"
Supports fuzzy matching for typos and variations:
"Anti-Mage", "antimage", "anti mage" all work
"Pudge", "pudge", "PUDGE" all work
"Shadow Fiend", "shadowfiend", "nevermore" all work
Args: hero: Hero name (display name, internal name, fuzzy match) or hero ID Examples: "Anti-Mage", "antimage", "pudge", 1
Returns: Dictionary with complete hero information including: - id: Hero ID - name: Internal name (e.g., "npc_dota_hero_antimage") - localized_name: Display name (e.g., "Anti-Mage") - primary_attr: Primary attribute ("str", "agi", "int", "all") - attack_type: "Melee" or "Ranged" - roles: List of roles (e.g., ["Carry", "Escape", "Nuker"]) - Base stats: base_health, base_mana, base_armor, etc. - Attribute gains: str_gain, agi_gain, int_gain - Combat stats: attack_range, attack_rate, move_speed, vision - All other hero statistics
Examples: get_hero_details("Anti-Mage") → Full Anti-Mage data get_hero_details("antimage") → Same result (fuzzy match) get_hero_details("Pudge") → Full Pudge data get_hero_details(1) → Anti-Mage data (by ID)
| Name | Required | Description | Default |
|---|---|---|---|
| hero | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It discloses fuzzy matching behavior, case-insensitivity, and accepted input forms, and details the return structure. It does not mention error behavior or rate limits, but for a simple read-only lookup this is fairly transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with 'Use this when', 'Supports', 'Args', 'Returns', and 'Examples' sections. It is appropriately detailed (about 200 words) with no redundant fluff; every sentence adds value.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity and the presence of an output schema, the description is complete for typical use. It covers usage, parameter semantics, examples, and return fields. It doesn't address edge cases like unknown heroes or partial matches, but fuzzy matching is explained, so this is a minor gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and the schema only declares 'hero' as anyOf integer/string with no description. The description fully compensates by explaining accepted formats (display name, internal name, fuzzy match, ID) with concrete examples, making parameter semantics crystal clear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Get detailed hero information by hero name or ID with fuzzy matching', which is a specific verb+resource+method. It distinguishes itself from sibling tools like get_item_details and get_aghs_details by focusing on hero data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit 'when to use' guidance with example user queries ('Use this when users ask about heroes: ...'). However, it does not mention explicit exclusions or alternative tools for hero-specific queries (e.g., get_hero_matchups), so it lacks the full 'when-not-to-use' component.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_heroes_playedA
Get detailed statistics for all heroes a player has played, with performance metrics.
Use this when users ask about HERO POOLS or HERO PERFORMANCE:
"What heroes does [player] play?"
"Show me [player]'s hero pool"
"Which heroes is [player] best at?"
"What's [player]'s most played hero?"
"How good is [player] at Rubick?"
"What heroes does [player] play in mid lane?"
"When did [player] last play Invoker?"
This returns detailed stats for EACH hero (win rate, games played, last played date). For simple win/loss totals without hero breakdown, use get_player_win_loss() instead.
Supports both IDs and natural language for flexible querying.
Args: player_name: The Dota 2 player name to search for limit: Maximum number of heroes to return (default: all heroes) offset: Skip first N heroes in results (for pagination) lane_role: Filter by lane (accepts "mid", "carry", "offlane", "pos 1-4", etc.) hero_id: Focus on specific hero only (accepts name or ID) included_account_id: Only include matches with these teammates (accepts names or IDs) excluded_account_id: Exclude matches with these players (accepts names or IDs) with_hero_id: Require these heroes on player's team (accepts IDs or names, single or list) against_hero_id: Require these heroes on enemy team (accepts IDs or names, single or list) having: Minimum games played threshold to include a hero in results
Returns: List of hero statistics (sorted by games played, descending), each containing: - hero_id (str): Hero name (localized, e.g., "Rubick") - last_played (str): Date string (e.g., "December 09, 2024") - wins (int): Number of wins with this hero - games_played (int): Total games with this hero - win_rate (str): Win percentage as string (e.g., "65.3")
Common queries: - Most played heroes: get_heroes_played("kürlo") - Top 5 heroes: get_heroes_played("kürlo", limit=5) - Mid lane heroes: get_heroes_played("kürlo", lane_role="mid") - Heroes with 10+ games: get_heroes_played("kürlo", having=10) - Performance vs Pudge: get_heroes_played("kürlo", against_hero_id="Pudge") - Specific hero stats: get_heroes_played("kürlo", hero_id="Rubick")
Example: get_heroes_played("kürlo", lane_role="mid", having=5) -> [ {"hero_id": "Rubick", "last_played": "December 09, 2024", "wins": 42, "games_played": 80, "win_rate": "52.5"}, {"hero_id": "Invoker", "last_played": "December 05, 2024", "wins": 15, "games_played": 25, "win_rate": "60.0"}, ... ]
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| having | No | ||
| offset | No | ||
| hero_id | No | ||
| lane_role | No | ||
| player_name | Yes | ||
| with_hero_id | No | ||
| against_hero_id | No | ||
| excluded_account_id | No | ||
| included_account_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses sorting order ('sorted by games played, descending'), pagination behavior (offset), filtering effects, and the exact return structure. However, it does not explicitly state that the operation is read-only or mention potential rate limits, but these are implicitly obvious for a stats query.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured with clear headings (Args, Returns, Common queries, Example). Every sentence provides useful information—including example queries and a concrete JSON output—without fluff. It is front-loaded with the core purpose, so agents can quickly grasp functionality.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 10 parameters, no annotations, and an output schema whose presence is indicated, the description is remarkably complete. It covers all parameter semantics, return fields, common usage patterns, and a full example output. It leaves no significant gaps for an agent to make incorrect assumptions.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description's Args section compensates fully by explaining every parameter's meaning, acceptable types, and examples (e.g., lane_role accepts 'mid', 'carry', offlane', 'pos 1-4'; with_hero_id accepts names or IDs). It adds substantial value beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Get detailed statistics for all heroes a player has played'. It clearly distinguishes this tool from sibling get_player_win_loss by stating 'For simple win/loss totals without hero breakdown, use get_player_win_loss() instead.'
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage context with example user queries (e.g., 'What heroes does [player] play?') and demonstrates when to use this tool. It also names the alternative tool for simpler queries, fulfilling the when-to-use vs. alternatives criterion.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_hero_item_popularityA
Get item popularity statistics for a hero organized by game phase (start, early, mid, late).
Use this when users ask about:
"What items should I buy on Pudge?"
"Show me Anti-Mage's item build"
"What's the most popular starting items for Invoker?"
"What do people build late game on Phantom Assassin?"
"Show me Rubick's core items"
Returns aggregated item statistics from thousands of matches, useful for:
Understanding optimal item progression for a hero
Identifying core items vs situational items
Learning meta item builds and timings
Seeing which items are most popular at each game stage
Supports fuzzy matching for hero names:
"Pudge", "pudge", "PUDGE" all work
"Anti-Mage", "antimage", "anti mage" all work
"Shadow Fiend", "shadowfiend", "sf" all work
Args: hero: Hero name (display name, internal name, fuzzy match) or hero ID Examples: "Pudge", "antimage", "Shadow Fiend", 86
Returns: Dictionary with game phases as keys, each containing: - start_game_items: Items purchased at game start (0-10 min) - early_game_items: Items purchased early (0-25 min) - mid_game_items: Items purchased mid game (25-40 min) - late_game_items: Items purchased late game (40+ min)
Each phase contains item names mapped to their statistics:
- wins (int): Times this item was in winning matches
- games (int): Total matches where this item was purchasedExamples: get_hero_item_popularity("Anti-Mage") -> { "start_game_items": { "Quelling Blade": {"wins": 15234, "games": 25123}, "Tango": {"wins": 18234, "games": 26234}, "Slippers of Agility": {"wins": 12345, "games": 21234}, ... }, "early_game_items": { "Power Treads": {"wins": 8234, "games": 15234}, "Battle Fury": {"wins": 12234, "games": 18234}, ... }, "mid_game_items": { "Manta Style": {"wins": 9234, "games": 14234}, "Black King Bar": {"wins": 5234, "games": 8234}, ... }, "late_game_items": { "Butterfly": {"wins": 4234, "games": 6234}, "Abyssal Blade": {"wins": 3234, "games": 5234}, ... } }
get_hero_item_popularity(1) # Anti-Mage by ID
-> Same format as above| Name | Required | Description | Default |
|---|---|---|---|
| hero | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It details the return structure (game phases, wins/games), explains fuzzy matching for hero names, and notes that data comes from 'thousands of matches.' It does not explicitly state read-only nature or potential errors, but for a read-only 'get' tool this is fairly transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with sections: purpose, use cases, return format, args, and examples. Every sentence adds value, and the content is directly relevant to invoking the tool correctly. Though long, the organization and front-loaded purpose make it easy to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with rich output, the description is comprehensive. It fully documents the return structure, phase time ranges, and per-item statistics with examples. It also demonstrates the call format via example invocations. No important context is missing for an agent to use this tool effectively.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema only provides a type (integer or string) for the hero parameter with no enum, and schema coverage is 0%. The description compensates thoroughly by explaining 'Hero name (display name, internal name, fuzzy match) or hero ID' with concrete examples including alias handling. This fully disambiguates the parameter beyond what the schema offers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource+scope: 'Get item popularity statistics for a hero organized by game phase.' It clearly distinguishes from siblings like get_item_details (item info) or get_hero_matchups (hero matchups) by focusing on item popularity per hero. The example queries further reinforce the tool's purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit 'Use this when users ask about:' with multiple concrete example queries, making the intended usage clear. It also lists 'useful for' scenarios that help the agent decide when to invoke this tool. However, it does not mention when not to use or explicitly name alternative tools, so it falls short of a full 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_hero_matchupsA
Get matchup statistics showing how a hero performs against all other heroes.
Use this when users ask about:
"Which heroes counter Pudge?"
"What are Anti-Mage's best matchups?"
"Show me heroes that Invoker struggles against"
"Who should I pick against Phantom Assassin?"
"What's Rubick's win rate against Storm Spirit?"
Returns win/loss statistics for every hero matchup, useful for:
Identifying counter-picks (heroes with high win rates against your hero)
Finding favorable matchups (heroes your hero performs well against)
Draft analysis and hero selection strategy
Supports fuzzy matching for hero names:
"Pudge", "pudge", "PUDGE" all work
"Anti-Mage", "antimage", "anti mage" all work
"Shadow Fiend", "shadowfiend", "sf" all work
Args: hero: Hero name (display name, internal name, fuzzy match) or hero ID Examples: "Pudge", "antimage", "Shadow Fiend", 86
Returns: List of matchup dictionaries, each containing: - hero_name (str): Name of the opponent hero - games (int): Total games played in this matchup - win (int): Games won against this hero - loss (int): Games lost against this hero - win_rate (float): Win percentage (0-100) against this hero
Sorted by game count (most common matchups first)Examples: get_hero_matchups("Pudge") -> [ { "hero_name": "Anti-Mage", "games": 15234, "win": 7123, "loss": 8111, "win_rate": 46.75 }, { "hero_name": "Invoker", "games": 14521, "win": 8234, "loss": 6287, "win_rate": 56.70 }, ... ]
get_hero_matchups(86) # Rubick by ID
-> Same format as above| Name | Required | Description | Default |
|---|---|---|---|
| hero | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It discloses return format, sorting behavior, fuzzy-matching behavior, and input flexibility. It does not mention potential errors, rate limits, or authentication, but for a read-only lookup tool the described behavior is transparent and sufficient.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (purpose, use cases, fuzzy matching, args, returns, example). It is somewhat verbose but each section adds value; the example output is instructive rather than redundant. It could be tightened slightly without losing clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter lookup tool, the description provides comprehensive context: when to use, input semantics, output format, sorting, and a full example. The presence of an output schema is noted, but the description itself already details the return structure thoroughly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must fully compensate. It does: it explains the 'hero' parameter accepts display names, internal names, fuzzy matches, and IDs, with concrete examples like 'Pudge', 'antimage', and 86. This far exceeds the bare schema definition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Get matchup statistics showing how a hero performs against all other heroes.' It clearly distinguishes itself from sibling tools like get_hero_details or get_hero_item_popularity by focusing on head-to-head matchup statistics. The use-case examples further reinforce the specific purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly lists natural-language triggers ('Which heroes counter Pudge?') and practical applications (counter-picks, draft analysis). It does not name alternative sibling tools directly, but the guidance is clear enough to know when to invoke this tool. It lacks explicit negative guidance ('do not use for X'), so it misses a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_item_detailsA
Get detailed item information by item name with fuzzy matching.
Use this when users ask about items:
"What does Diffusal Blade do?"
"Show me Octarine Core"
"Tell me about BKB"
Supports fuzzy matching for typos and variations:
"Diffusal", "diffusal blade", "Diffusal Blade" all work
"Octarine", "octarine core", "Octarine Core" all work
"bkb", "Black King Bar", "black king bar" all work
Args: item_name: Item name (display name, internal name, or fuzzy match) Examples: "Diffusal Blade", "diffusal", "octarine core"
Returns: Dictionary with complete item information including: - dname: Display name (e.g., "Diffusal Blade") - cost: Gold cost - abilities: List of active/passive abilities with descriptions - hint: Usage hints and tips - stats: Attribute bonuses (damage, mana, health, etc.) - notes: Additional information
Examples: get_item_details("Diffusal Blade") → Full Diffusal Blade data get_item_details("diffusal") → Same result (fuzzy match) get_item_details("Octarine Core") → Full Octarine Core data get_item_details("octarine") → Same result (fuzzy match)
| Name | Required | Description | Default |
|---|---|---|---|
| item_name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full transparency burden. It discloses fuzzy matching behavior, the argument format, and the return structure with field names. However, it doesn't specify error handling for unknown items, which would be helpful but not critical.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with sections for usage, fuzzy matching, args, returns, and examples. It's concise for the information provided—every section adds value without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple single-parameter input and the detailed explanation of behavior and return fields, the description is complete for an AI agent to select and use the tool. It covers what the tool does, how to invoke it, and what to expect in response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only a type string with no description (0% coverage). The description thoroughly explains item_name with accepted formats (display name, internal name, fuzzy), provides examples, and clarifies case-insensitivity. It fully compensates for the schema gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses 'Get' as a specific verb with the resource 'detailed item information by item name.' It clearly distinguishes from sibling tools focused on players, matches, and heroes. No ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly states 'Use this when users ask about items' and provides three query examples. It doesn't list exclusions or alternative tools, but the scope is clear and no other sibling handles item details.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_match_detailsA
Get comprehensive details for a specific match, with automatic detection of parse status.
Use this when users ask:
"Show me match [match_id]"
"Analyze match [match_id]"
"What happened in match [match_id]?"
"Show me the teamfights in match [match_id]"
"Who won match [match_id]?"
"What were the item builds in match [match_id]?"
"Show me the gold graph for match [match_id]"
This tool automatically detects whether a match has been parsed (has detailed replay data) or not:
Parsed matches: Returns organized summary with teamfights, objectives, gold/XP advantage graphs, chat logs, and detailed player performance benchmarks
Unparsed matches: Returns basic match data with player KDA, GPM/XPM, and game outcome
For matches that aren't parsed yet, use request_parse_match() first, wait a few minutes, then call this function to get the detailed data.
Args: match_id: The match ID to retrieve (e.g., 8123456789)
Returns: Dictionary with structure depending on parse status:
If parsed=True (detailed replay data available):
- parsed (bool): True
- metadata (dict): Match info (duration, game mode, winner, etc.)
- teamfights_summary (dict):
- count (int): Number of teamfights detected
- teamfights (list): Detailed teamfight breakdowns with:
- start/end time, location, deaths, gold swing
- objectives (list): Timeline of objectives (towers, barracks, Roshan)
- chat (list): In-game chat messages with timestamps
- picks_bans (list): Draft phase picks and bans
- players_summary (dict):
- count (int): Number of players (always 10)
- players (list): Per-player statistics including:
- account_id, hero_name, team, KDA
- gold_per_min, xp_per_min, net_worth
- hero_damage, tower_damage, hero_healing
- last_hits, denies
- benchmarks: Performance percentiles for 7 key metrics
- gold_advantage (list): Gold advantage over time (array of values)
- xp_advantage (list): XP advantage over time (array of values)
If parsed=False (basic match data only):
- parsed (bool): False
- data (dict):
- players (list): Basic player stats (KDA, GPM, damage, benchmarks)
- radiant_win (bool): True if Radiant won, False if Dire won
- duration (str): Game length in MM:SS format
- match_id (int): The match IDCommon queries: - Basic match info: get_match_details(8123456789) - Teamfight analysis: After parsing, check teamfights_summary - Performance comparison: Compare benchmarks between players - Gold advantage: Check gold_advantage array for momentum swings
Example (parsed match): get_match_details(8123456789) -> { "parsed": true, "metadata": { "match_id": 8123456789, "duration": "45:23", "radiant_win": true, "game_mode": 22 }, "teamfights_summary": { "count": 12, "teamfights": [ { "start": 1245, "end": 1267, "deaths": 6, "gold_delta": 3500, ... } ] }, "players_summary": { "count": 10, "players": [ { "hero_name": "Rubick", "team": "radiant", "kills": 8, "deaths": 5, "assists": 25, "gold_per_min": 380, "benchmarks": { "gold_per_min": {"raw": 380, "pct": 65.3}, "xp_per_min": {"raw": 425, "pct": 58.2}, ... } }, ... ] }, "gold_advantage": [0, 200, 450, 800, ...], ... }
Example (unparsed match): get_match_details(8123456789) -> { "parsed": false, "data": { "players": [...], "radiant_win": true, "duration": "45:23", "match_id": 8123456789 } }
| Name | Required | Description | Default |
|---|---|---|---|
| match_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden and excels. It discloses the automatic parse-status detection, explains the two distinct return structures (parsed/unparsed), and notes the prerequisite for unparsed matches. The behavior is fully transparent and includes practical details like 'wait a few minutes.'
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear headers, bullet lists, and examples, and the purpose is front-loaded. However, it is notably long, and the extensive return-structure documentation may be redundant given an output schema exists. Still, the organization keeps it scannable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description addresses the core complexity: conditional behavior based on parse status, the prerequisite parse workflow, and detailed semantics of both response shapes. It also includes common queries and examples, leaving minimal ambiguity for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% for match_id, so the description must compensate. It does so by defining match_id as 'The match ID to retrieve' with a concrete example (8123456789). For a single integer parameter, this is sufficient, though it doesn't add constraints (e.g., format/range).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with a clear, specific verb+resource combination: 'Get comprehensive details for a specific match.' It further distinguishes itself from siblings like request_parse_match and get_hero_details by focusing on match-level data and directly listing user query examples that trigger its use.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance through example user queries, and clearly differentiates from the alternative request_parse_match: 'For matches that aren't parsed yet, use request_parse_match() first, wait a few minutes, then call this function.' This is a concrete workflow with an alternative named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_player_histogramsA
Get the DISTRIBUTION of a specific statistic across matches (performance consistency analysis).
Use this when users ask about PERFORMANCE RANGES or CONSISTENCY:
"How often does [player] get 10+ kills?"
"What's [player]'s typical GPM range?"
"How consistent is [player]'s farming?"
"Show me [player]'s kill distribution"
"What GPM does [player] usually achieve?"
"How often does [player] get a rampage (5+ kills)?"
This shows HOW OFTEN different values occur (e.g., "30 games with 5-6 kills, 20 games with 7-8 kills").
DO NOT use this for simple averages - use get_player_totals() instead. DO NOT use this for win/loss counts - use get_player_win_loss() instead.
Supports both IDs and natural language for flexible querying.
Args: player_name: The Dota 2 player name to search for field: Statistical field to analyze. Accepts variations like: - "kills", "deaths", "assists" - "gpm" or "gold_per_min" or "gold per min" - "xpm" or "xp_per_min" - "cs" or "last_hits" - "damage" or "hero_damage" - "healing" or "hero_healing" - "duration" - "comeback", "stomp", "loss" limit: Number of matches to analyze (default: all available) offset: Skip first N matches (for pagination) lane_role: Filter by lane (accepts "mid", "carry", "pos 1-4", etc.) hero_id: Filter to specific hero (accepts name or ID) included_account_id: Only matches with these teammates (accepts names or IDs) excluded_account_id: Exclude matches with these players (accepts names or IDs) with_hero_id: Require these heroes on player's team (accepts IDs or names) against_hero_id: Require these heroes on enemy team (accepts IDs or names) having: Minimum games played threshold
Returns: List of histogram buckets showing frequency distribution, each containing: - count (int): The value or range (e.g., 10 for "10 kills") - games_played (int): Number of games with this value - win (int): Games won at this performance level - win_rate (str): Win rate at this value (0.0 to 1.0)
The buckets are typically in ranges (e.g., 0-1, 1-2, 2-3 kills) or exact values depending on the field. Higher performance levels often correlate with higher win rates.
Common queries: - Kill distribution: get_player_histograms("kürlo", field="kills") - GPM consistency: get_player_histograms("kürlo", field="gpm") - Hero-specific: get_player_histograms("kürlo", field="last_hits", hero_id="Anti-Mage")
Example: get_player_histograms("kürlo", field="kills", hero_id="Rubick") -> [ {"count": 0, "games_played": 2, "win": 0, "win_rate": 0.0}, {"count": 1, "games_played": 5, "win": 1, "win_rate": 0.2}, {"count": 2, "games_played": 8, "win": 3, "win_rate": 0.375}, {"count": 3, "games_played": 12, "win": 7, "win_rate": 0.583}, {"count": 4, "games_played": 15, "win": 10, "win_rate": 0.667}, {"count": 5, "games_played": 20, "win": 16, "win_rate": 0.8}, ... ]
This tells you: player had 20 games with 5 kills, winning 16 of them (80% win rate at that performance level).
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes | ||
| limit | No | ||
| having | No | ||
| offset | No | ||
| hero_id | No | ||
| lane_role | No | ||
| player_name | Yes | ||
| with_hero_id | No | ||
| against_hero_id | No | ||
| excluded_account_id | No | ||
| included_account_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description fully carries the behavioral transparency burden. It explains what the tool returns (histogram buckets), how values are grouped (ranges or exact values), the inclusion of win_rate, and nuances like 'Higher performance levels often correlate with higher win rates.' It also mentions flexible querying ('Supports both IDs and natural language'), pagination via offset, and the having threshold.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although long, the description is well-structured with clear sections: purpose, usage, exclusions, arguments, return values, and examples. Every section earns its place given 11 parameters and a nuanced output format. The example output is illustrative and not fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is complete for a complex tool: it covers all 11 parameters, explains the return value semantics despite having an output schema, and provides concrete examples. It leaves no obvious gaps for the agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the description's Args section compensates fully. It explains every parameter, gives alternative names for fields (e.g., 'gpm' or 'gold_per_min'), and clarifies that hero/account parameters accept both IDs and names. This goes far beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Get the DISTRIBUTION of a specific statistic across matches (performance consistency analysis).' It clearly distinguishes itself from siblings like get_player_totals and get_player_win_loss by focusing on distributions, ranges, and consistency.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit when-to-use criteria with real user queries (e.g., 'How often does [player] get 10+ kills?') and explicit when-not-to-use instructions with named alternatives ('use get_player_totals() instead', 'use get_player_win_loss() instead'). This is model-level guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_player_infoA
Get complete Dota 2 player profile with overview statistics.
Use this for initial player lookups when users ask:
"Who is [player]?"
"Tell me about [player]"
"What's [player]'s profile?"
"Show me [player]'s stats"
General player information requests
This is the FIRST tool to use when a user mentions a player - it provides a comprehensive overview. For detailed analysis of specific aspects, use the specialized tools afterward.
Retrieves:
Player profile (name, avatar, profile URL)
Overall win/loss statistics and win rate
Top 10 most played heroes with individual performance metrics
Args: player_name: The Dota 2 player name to search for
Returns: Dictionary containing: - personaname (str): Player's display name - avatarfull (str): URL to player's avatar image - profileurl (str): Steam profile URL - win_count (int): Total wins - lose_count (int): Total losses - win_rate (float): Overall win percentage - fav_heroes (list): Top 10 heroes, each with: - hero_name (str): Hero's display name - games_played (int): Games played with this hero - win_count (int): Wins with this hero - win_rate (float): Win percentage with this hero
Example: get_player_info("kürlo") -> Full profile with top heroes like Rubick, Invoker, etc.
| Name | Required | Description | Default |
|---|---|---|---|
| player_name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It comprehensively discloses the output shape, including nested hero data, and implies read-only behavior. However, it leaves minor gaps such as exact-match vs fuzzy search behavior for player_name and error handling when a player is not found. Still, the disclosure is far more detailed than typical.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although the description is long, it is well-structured with clear sections and every part earns its place: purpose, usage context, return format, and an example. The example brings concrete relevance. No word is wasted on tautology.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers purpose, usage, parameter semantics, return schema in detail, and an example. It differentiates from a large set of siblings and gives a complete picture of a straightforward read-only lookup tool. There is no missing critical context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It provides an Args section: 'player_name: The Dota 2 player name to search for.' This adds meaning beyond the bare string type, though it could specify match behavior (e.g., exact, partial). Sufficient for a single simple parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb+resource: 'Get complete Dota 2 player profile with overview statistics.' It clearly distinguishes from siblings by positioning itself as the FIRST tool for player lookups and enumerating exactly what is retrieved (profile, win/loss, top heroes). This contrasts with the many specialized sibling tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use guidance is provided: 'initial player lookups', example user queries, and the directive 'This is the FIRST tool to use when a user mentions a player.' It also references specialized tools afterward, effectively naming the alternative behavior. This is a textbook example of usage differentiation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_player_peersA
Get players who frequently play WITH the specified player (teammates, not opponents).
Use this when users ask about TEAMMATES or PARTY MEMBERS:
"Who does [player] play with?"
"Who are [player]'s teammates?"
"What's [player]'s win rate with [teammate]?"
"Show me [player]'s most common party members"
"Who does [player] duo with?"
"Find [player]'s frequent teammates"
This returns players who have been ON THE SAME TEAM as the specified player. Results are sorted by number of games played together (most frequent first).
Supports both IDs and natural language for flexible querying.
Args: player_name: The Dota 2 player name to search for limit: Number of matches to analyze (default: all recent matches) offset: Skip first N matches (for pagination) lane_role: Filter to games where player was in specific lane hero_id: Filter to games where player played specific hero included_account_id: Get stats for SPECIFIC teammate(s) only (accepts names or IDs) excluded_account_id: Exclude specific players from results (accepts names or IDs) with_hero_id: Filter to games with these heroes on player's team against_hero_id: Filter to games against these enemy heroes peers_count: Number of teammates to return (default 5, increase for more results)
Returns: List of teammate statistics (sorted by games together, descending), each containing: - account_id (int): Teammate's account ID - personaname (str): Teammate's display name - last_played (str): Date of most recent game together (e.g., "December 09, 2024") - wins (int): Games won together - games_played (int): Total games played together - win_rate (str): Win rate as percentage string (e.g., "67.5") - average_gpm (float): Player's average GPM when playing with this teammate - average_xpm (float): Player's average XPM when playing with this teammate
Common queries: - Top teammates: get_player_peers("kürlo", peers_count=10) - Specific teammate stats: get_player_peers("kürlo", included_account_id="hotpocalypse") - Teammates on Rubick: get_player_peers("kürlo", hero_id="Rubick") - Recent duos: get_player_peers("kürlo", limit=100, peers_count=5)
Example: get_player_peers("kürlo", peers_count=3) -> [ {"account_id": 123456, "personaname": "hotpocalypse", "last_played": "December 09, 2024", "wins": 45, "games_played": 78, "win_rate": "57.7", "average_gpm": 456.3, "average_xpm": 523.1}, ... ]
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No | ||
| hero_id | No | ||
| lane_role | No | ||
| peers_count | No | ||
| player_name | Yes | ||
| with_hero_id | No | ||
| against_hero_id | No | ||
| excluded_account_id | No | ||
| included_account_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses sorting ('Results are sorted by number of games played together'), pagination behavior ('offset: Skip first N matches'), defaults (peers_count default 5), and returns a detailed field-by-field breakdown. It doesn't cover possible errors or rate limits, but for a read-only teammate stats tool it provides substantial behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is lengthy but well-structured into an intro, example queries, Args, Returns, Common queries, and an Example. Each section serves a purpose for a 10-parameter tool. It could be slightly tightened, but no part feels like filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having 10 parameters and no annotations or schema descriptions, the description covers all parameters, defaults, return output, and provides concrete examples. It explains sorting and pagination behavior, and the example output field list makes the result structure fully transparent. Minor ambiguity about what counts as 'recent matches' is the only gap, but overall it's highly complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, making the description essential. The 'Args:' section adds meaning beyond the raw schema for every parameter, clarifying things like 'included_account_id: Get stats for SPECIFIC teammate(s) only (accepts names or IDs)' and 'limit: Number of matches to analyze (default: all recent matches).' The 'Common queries' examples show how to combine parameters, giving rich semantic grounding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Get players who frequently play WITH the specified player (teammates, not opponents)' and further reinforces the scope with 'This returns players who have been ON THE SAME TEAM as the specified player.' It distinguishes from sibling tools by focusing on teammates/party members, and the example user queries ('Who does [player] play with?') make the purpose immediately obvious.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Use this when users ask about TEAMMATES or PARTY MEMBERS' and provides example queries. It also includes an exclusion ('not opponents'). However, it does not name alternative tools for opponent-focused queries or general stats, so it lacks explicit alternative tool guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_player_totalsA
Get aggregated performance statistics across ALL tracked metrics (GPM, KDA, damage, etc.).
Use this when users ask about AVERAGE PERFORMANCE or AGGREGATE STATS:
"What's [player]'s average GPM?"
"How much damage does [player] do per game?"
"What's [player]'s average kills per game?"
"Show me [player]'s average stats"
"What's [player]'s average last hits?"
"How much XPM does [player] average?"
Returns comprehensive statistics for ALL fields tracked by OpenDota including:
Combat: kills, deaths, assists, kda, hero_kills, hero_damage, hero_healing
Economy: gold, gold_per_min, xp_per_min, gold_spent
Farming: last_hits, denies, neutral_kills, lane_kills
Objectives: tower_damage, tower_kills, courier_kills, observer_uses, sentry_uses
And many more...
For simple win/loss counts, use get_player_win_loss() instead. For performance distribution analysis, use get_player_histograms() instead.
Supports both IDs and natural language for flexible querying.
Args: player_name: The Dota 2 player name to search for limit: Number of matches to analyze (default: all available matches) offset: Skip first N matches (for pagination) lane_role: Filter by lane (accepts "mid", "carry", "pos 1-4", etc.) hero_id: Filter to specific hero (accepts name or ID like "Rubick") included_account_id: Only include matches with these teammates (accepts names or IDs) excluded_account_id: Exclude matches with these players (accepts names or IDs) with_hero_id: Require these heroes on player's team (accepts IDs or names) against_hero_id: Require these heroes on enemy team (accepts IDs or names) having: Minimum games played threshold for included data
Returns: List of statistical totals (one object per metric), each containing: - field (str): Name of the statistic (e.g., "kills", "gold_per_min", "hero_damage") - games_played (int): Number of games in this dataset - count (float): Total sum across all games - average (float): Mean value per game (count / games_played)
Common queries: - All average stats: get_player_totals("kürlo") - Mid lane averages: get_player_totals("kürlo", lane_role="mid") - Rubick averages: get_player_totals("kürlo", hero_id="Rubick") - With teammate: get_player_totals("kürlo", included_account_id="hotpocalypse")
Example: get_player_totals("kürlo", hero_id="Rubick", lane_role="mid") -> [ {"field": "kills", "games_played": 80, "count": 418, "average": 5.225}, {"field": "deaths", "games_played": 80, "count": 520, "average": 6.5}, {"field": "gold_per_min", "games_played": 80, "count": 33840, "average": 423.0}, {"field": "hero_damage", "games_played": 80, "count": 1640000, "average": 20500.0}, ... ]
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| having | No | ||
| offset | No | ||
| hero_id | No | ||
| lane_role | No | ||
| player_name | Yes | ||
| with_hero_id | No | ||
| against_hero_id | No | ||
| excluded_account_id | No | ||
| included_account_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the transparency burden and does well: it explains the return format ('List of statistical totals...each containing field, games_played, count, average'), flexible ID/natural language support, and parameter behaviors like default limit. However, it omits edge-case behaviors such as empty results, rate limiting, or the performance cost of aggregating over all available matches.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with headers, bullet lists, and examples, making it easy to scan. It is slightly verbose due to the extensive metric list and the 'Common queries' section partially duplicating the Example section, but the extra detail is informative rather than wasteful.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 10-parameter tool with no annotations, the description is exceptionally complete. It covers when to use, parameter semantics, output schema details, common query patterns, and a concrete example, leaving the agent fully equipped to invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, but the Args section compensates by documenting all 10 parameters with meaningful semantics. For example, 'hero_id: Filter to specific hero (accepts name or ID like "Rubick")' and 'lane_role: Filter by lane (accepts "mid", "carry", "pos 1-4", etc.)' add crucial usage information beyond the schema types.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Get aggregated performance statistics across ALL tracked metrics (GPM, KDA, damage, etc.)', which clearly identifies the tool's action and scope. It further differentiates from siblings by explicitly naming get_player_win_loss and get_player_histograms as alternatives for different question types.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage guidance with 'Use this when users ask about AVERAGE PERFORMANCE or AGGREGATE STATS' and lists concrete example queries. It also states when NOT to use it, directing users to get_player_win_loss() and get_player_histograms() for other analyses.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_player_win_lossA
Get simple win/loss counts for a player with optional filters.
Use this when users ask about WIN RATES or WIN/LOSS RECORDS:
"What's [player]'s win rate?"
"How many games has [player] won?"
"What's [player]'s win rate with Rubick?"
"How does [player] perform in mid lane?"
"What's [player]'s win rate when playing with [teammate]?"
"How does [player] do against Pudge?"
Returns ONLY win and loss counts. For detailed hero statistics with dates and performance metrics, use get_heroes_played() instead. For aggregate statistics like GPM/XPM/KDA, use get_player_totals() instead.
Supports both IDs and natural language for flexible querying.
Args: player_name: The Dota 2 player name to search for limit: Number of matches to limit analysis to offset: Skip first N matches (for pagination) lane_role: Lane filter. Accepts: - Integer: 1 (Safe), 2 (Mid), 3 (Off), 4 (Jungle) - String: "mid", "safe lane", "offlane", "jungle", "carry", "pos 1", etc. hero_id: Hero filter. Accepts: - Integer: Hero ID (e.g., 86 for Rubick) - String: Hero name (e.g., "Rubick", "Anti-Mage") included_account_id: Filter by teammate. Accepts: - String: Player name (e.g., "hotpocalypse") - List[String]: Multiple player names (games with ANY of these players) excluded_account_id: Exclude matches with these players (accepts names or IDs) with_hero_id: Require these heroes on player's team (accepts IDs or names) against_hero_id: Require these heroes on enemy team. Accepts: - Integer/String: Single hero ID or name - List: Multiple hero IDs or names
Returns: Dictionary with exactly two fields: - win (int): Number of wins matching the filters - lose (int): Number of losses matching the filters
Common queries: - Overall record: get_player_win_loss("kürlo") - Hero-specific: get_player_win_loss("kürlo", hero_id="Rubick") - Lane-specific: get_player_win_loss("kürlo", lane_role="mid") - With teammate: get_player_win_loss("kürlo", included_account_id="hotpocalypse") - Vs counter: get_player_win_loss("kürlo", hero_id="Rubick", against_hero_id="Pudge")
Example: get_player_win_loss("kürlo", lane_role="mid", hero_id="Rubick") -> {"win": 42, "lose": 38}
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| offset | No | ||
| hero_id | No | ||
| lane_role | No | ||
| player_name | Yes | ||
| with_hero_id | No | ||
| against_hero_id | No | ||
| excluded_account_id | No | ||
| included_account_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden, and it does well: it states the return format ('Dictionary with exactly two fields: win and lose'), mentions it returns ONLY win/loss counts, and notes support for both IDs and natural language. It could add edge-case behavior (e.g., no matches found, invalid hero name), but it is already highly transparent for a read-only query tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Though lengthy, the description is well-structured with clear sections: purpose, when-to-use, parameter details, return values, and examples. Every paragraph earns its place; the length is justified by the tool's nine flexible parameters and the need to distinguish between similar sibling tools. It is front-loaded with the core purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers all nine parameters, return format, common usage scenarios, and explicit alternatives. It is complete enough for an agent to select and invoke the tool correctly without needing external documentation. The output schema is simple, and the description fully complements it.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has no parameter descriptions, but the description thoroughly explains every parameter, including accepted types and values (e.g., lane_role integer-to-string mapping, hero_id integer or name, included_account_id as string or list). It also provides common query examples for each meaningful filter, fully compensating for the 0% schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Get simple win/loss counts for a player with optional filters.' It explicitly distinguishes itself from sibling tools like get_heroes_played and get_player_totals, and the tool name itself is clear. The purpose is unambiguous and well-differentiated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool ('Use this when users ask about WIN RATES or WIN/LOSS RECORDS') and provides concrete example queries. It also names alternatives ('use get_heroes_played() instead', 'use get_player_totals() instead'), making the decision boundary crystal clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recent_matchesA
Get a player's 20 most recent Dota 2 matches with performance statistics.
Use this when users ask:
"Show me [player]'s recent matches"
"What are [player]'s last games?"
"How has [player] been performing lately?"
"Show me [player]'s match history"
"What heroes has [player] been playing?"
"How did [player] do in their last game?"
Returns detailed statistics for each match including hero played, KDA, farm efficiency (GPM/XPM), damage dealt, and more. Useful for analyzing recent performance trends and hero picks.
Args: player_name: The Dota 2 player name to search for
Returns: List of 20 most recent matches (sorted newest first), each containing: - match_id (int): Unique match identifier - match_date (str): Date match was played (e.g., "December 09, 2024") - duration (str): Game length in MM:SS format (e.g., "45:23") - game_mode (int): Game mode ID (e.g., 22 for All Pick, 2 for Captain's Mode) - hero_name (str): Hero played (e.g., "Rubick", "Anti-Mage") - match_rank_tier (str): Skill bracket (e.g., "Ancient [5]", "Divine [3]") - kills (int): Number of kills - deaths (int): Number of deaths - assists (int): Number of assists - xp_per_min (int): Experience gained per minute - gold_per_min (int): Gold earned per minute - hero_damage (int): Total damage dealt to enemy heroes - tower_damage (int): Total damage dealt to towers - hero_healing (int): Total healing provided to allies - last_hits (int): Creeps killed (farming stat)
Common queries: - Recent performance: get_recent_matches("kürlo") - Check specific player: get_recent_matches("hotpocalypse")
Example: get_recent_matches("kürlo") -> [ { "match_id": 8123456789, "match_date": "December 09, 2024", "duration": "45:23", "game_mode": 22, "hero_name": "Rubick", "match_rank_tier": "Divine [3]", "kills": 8, "deaths": 5, "assists": 25, "xp_per_min": 425, "gold_per_min": 380, "hero_damage": 15234, "tower_damage": 1250, "hero_healing": 2340, "last_hits": 45 }, ... ]
| Name | Required | Description | Default |
|---|---|---|---|
| player_name | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full transparency burden. It discloses the exact number of matches (20), the sorting order (newest first), and the detailed return fields. It also includes an example output, making the behavior highly predictable. It does not cover edge cases like player-not-found errors, but for a read-only query tool, this is reasonably transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although the description is long, it is well-structured with clear sections (summary, use cases, returns, args, example). Every sentence provides useful information, and the front-loaded first sentence immediately states the core purpose. The length is justified by the complexity of the return data.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is highly complete for a tool with one parameter and no annotations. It covers the tool's purpose, when to use it, the full return schema with type descriptions, common query examples, and a worked example. The only missing piece might be error handling, but the provided context is sufficient for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema only defines player_name as a string with no description, so the description must compensate. It does so thoroughly: the 'Args' section explains player_name is 'The Dota 2 player name to search for,' and the 'Common queries' section provides concrete usage examples with actual player names, adding significant meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves a player's 20 most recent Dota 2 matches with performance statistics, which is a specific verb-resource pairing. It distinguishes itself from siblings by explicitly focusing on 'recent matches' and including performance metrics, while sibling tools like get_match_details or get_player_win_loss serve different purposes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides an explicit 'Use this when users ask' section with concrete example queries, which is strong usage guidance. However, it does not mention when NOT to use this tool or explicitly point to alternative sibling tools, so it lacks explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recordsA
Get top world record performances in a specific statistical field.
Use this when users ask:
"What's the world record for most kills in a game?"
"Show me the highest GPM ever achieved"
"What's the longest Dota 2 game ever?"
"Who has the world record for [stat]?"
"What are the top performances for [stat]?"
"Has anyone ever gotten 50+ kills?"
Returns the all-time best performances globally for the specified metric, including match details, hero used, and player information. These are the absolute highest values ever recorded in tracked matches.
Supports natural language field names with fuzzy matching and variations.
Args: field: Statistical field to get records for. Accepts variations like: - Combat: "kills", "deaths", "assists", "kda" - Economy: "gold_per_min", "xp_per_min", - Farming: "last_hits", "denies" - Damage: "hero_damage", "hero_healing", "tower_damage" - Other: "duration"
Returns: List of top record performances (sorted by field value, descending), each containing: - match_id (int): Match ID for the record game - start_time (str): Date of the match (e.g., "December 09, 2024") - hero_id (int): Hero ID used in the record - hero_name (str): Hero's display name (e.g., "Rubick", "Anti-Mage") - score (float): The record value achieved (e.g., 45 kills, 1245 GPM) - Additional match details may be included depending on the field
Common queries: - Kill record: get_records("kills") - GPM record: get_records("gpm") or get_records("gold_per_min") - Longest game: get_records("duration")
Example: get_records("kills") -> [ { "match_id": 1234567890, "start_time": "March 15, 2024", "hero_id": 1, "hero_name": "Anti-Mage", "score": 45.0, ... }, { "match_id": 9876543210, "start_time": "January 08, 2024", "hero_id": 86, "hero_name": "Rubick", "score": 43.0, ... }, ... ]
| Name | Required | Description | Default |
|---|---|---|---|
| field | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden of behavioral disclosure. It transparently states that it 'Supports natural language field names with fuzzy matching and variations', and that 'Additional match details may be included depending on the field', revealing behavioral nuances. It also clarifies the scope as 'the absolute highest values ever recorded in tracked matches'. However, it does not explicitly state that the operation is read-only or safe, which a fully transparent description would include.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but well-structured: it front-loads the purpose, then gives usage examples, parameter details, return format, common queries, and a full example. Every section adds value, though it could be slightly more concise. The example output is helpful but not essential.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is highly complete for a single-parameter lookup tool: it thoroughly explains the parameter, describes the return object with field names and types, and provides a realistic example. It does not cover error cases (e.g., unknown field) or specify a limit on the number of records returned, which are minor gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides only the parameter name 'field' with a generic string type and 0% description coverage. The description fully compensates by listing accepted field categories (Combat, Economy, Farming, Damage, Other) with specific values like kills, gold_per_min, last_hits, and duration, and by explaining that variations like 'gpm' and 'gold_per_min' are accepted. This goes far beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description begins with 'Get top world record performances in a specific statistical field', which is a specific verb+resource combination that clearly defines the tool's function. It further distinguishes the tool from siblings by focusing on 'world record' and 'all-time best performances', and it provides multiple example user queries that uniquely map to this tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit usage context with 'Use this when users ask:' followed by six concrete example questions. It also includes a 'Common queries' section with specific calls, providing clear guidance on when to invoke the tool. However, it does not explicitly mention when not to use it or name alternative tools, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_scenarios_item_timingsA
Get win rates for heroes based on item purchase timing (when key items are completed).
Use this when users ask:
"When should I buy [item] on [hero]?"
"What's the optimal [item] timing?"
"How does [item] timing affect win rate?"
"Show me item timing statistics for [hero]"
"Which heroes benefit most from early [item]?"
"What's a good [item] timing?"
"Is [hero] better with early or late [item]?"
"When do pros buy [item]?"
Provides time-segmented data showing how item purchase timing correlates with win rates. Generally, earlier timings have higher win rates for core items, helping identify optimal farming targets and build orders.
Supports natural language item and hero names.
Args: item_name: Item to analyze. Accepts item names like: - Core items: "bfury" (Battle Fury), "radiance", "midas" (Hand of Midas) - Mobility: "blink" (Blink Dagger), "force" (Force Staff) - Defense: "bkb" (Black King Bar), "linkens" (Linken's Sphere) - Boots: "travels" (Boots of Travel), "phase" (Phase Boots) - Support: "wards", "mek" (Mekansm) - And many more item names/abbreviations hero_name: Hero to analyze. Accepts: - Integer: Hero ID (e.g., 86 for Rubick) - String: Hero name (e.g., "Rubick", "Anti-Mage")
Note: At least one parameter must be provided. You can provide:
- Only item_name: See which heroes buy this item and when
- Only hero_name: See all item timings for this hero
- Both: See specific item timing for specific heroReturns: Dictionary organized by timing brackets (formatted as "MM:SS"). Structure depends on parameters provided:
If only item_name provided:
- item_name (str): The item being analyzed
- [time brackets]: Keys like "12:30", "15:00", etc., each containing list of:
- hero_name (str): Hero name
- games (int): Games with this timing
- wins (int): Wins with this timing
- win_rate (str): Win rate percentage (e.g., "65.3")
If only hero_name provided:
- hero_name (str): The hero being analyzed
- [time brackets]: Keys like "12:30", "15:00", etc., each containing list of:
- item_name (str): Item name
- games (int): Games with this timing
- wins (int): Wins with this timing
- win_rate (str): Win rate percentage
If both provided:
- hero_name (str): The hero
- item_name (str): The item
- [time brackets]: Keys like "12:30", "15:00", etc., each containing list of:
- games (int): Games with this timing
- wins (int): Wins with this timing
- win_rate (str): Win rate percentageCommon queries: - Optimal item timing: get_scenarios_item_timings(item_name="bfury", hero_name="Anti-Mage") - Best heroes for item: get_scenarios_item_timings(item_name="blink") - Hero's item timings: get_scenarios_item_timings(hero_name="Anti-Mage")
Example: get_scenarios_item_timings(item_name="bfury", hero_name="Anti-Mage") -> { "hero_name": "Anti-Mage", "item_name": "bfury", "10:00": [{"games": 12, "wins": 10, "win_rate": "83.3"}], "12:00": [{"games": 45, "wins": 32, "win_rate": "71.1"}], "15:00": [{"games": 234, "wins": 145, "win_rate": "62.0"}], "18:00": [{"games": 189, "wins": 98, "win_rate": "51.9"}], "21:00": [{"games": 87, "wins": 38, "win_rate": "43.7"}], ... }
This clearly shows that Anti-Mage's win rate with Battle Fury decreases significantly as the timing gets later. A 12-minute Battle Fury has 71% win rate, while an 18-minute Battle Fury only has 52% win rate, suggesting you should aim for Battle Fury before 15 minutes for optimal results.
| Name | Required | Description | Default |
|---|---|---|---|
| hero_name | No | ||
| item_name | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It clearly discloses the requirement to provide at least one parameter, supports natural language names, explains the three return modes, and provides a detailed example showing the data structure and how to interpret win rate trends. This is strong transparency for a tool with no annotations, though it does not mention potential edge cases like omitted parameters causing errors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is lengthy but well-organized into clear sections (purpose, use cases, args, returns, common queries, example). It is front-loaded with the primary purpose and usage examples. While some sections are verbose, every part earns its place given the tool's complexity and the need to document three modes of operation.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the complexity (2 optional parameters, 3 modes, no annotations, output schema present), the description is exceptionally complete. It fully documents the return structure for each parameter combination, provides a worked example, and interprets the data to help the agent understand the semantic meaning of win rates over time. It is more than sufficient for an agent to use the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema is minimal (anyOf integer/string/null with default null) and has 0% description coverage. The description's Args section fully compensates by explaining accepted item abbreviations, hero ID/name formats, and the three combination scenarios. The note about 'at least one parameter must be provided' and the 'Common queries' examples add semantic meaning far beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Get win rates for heroes based on item purchase timing' with a specific verb and resource. It is distinguished from sibling tools like get_hero_item_popularity and get_scenarios_lane_roles by focusing on timing-based win rates. Numerous example queries reinforce the tool's unique purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description includes a 'Use this when users ask' section with 8 explicit example queries and a 'Common queries' section with concrete parameter combinations. However, it does not explicitly mention when not to use this tool or name alternative tools for exclusions, which prevents a score of 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_scenarios_lane_rolesA
Get win rates for heroes in specific lane roles segmented by game duration.
Use this when users ask:
"What's [hero]'s win rate in mid lane by game time?"
"How does [hero] perform in offlane at different game lengths?"
"Show me lane role statistics for [hero]"
"Which heroes are best in [lane] for long games?"
"What's the win rate for mid laners at 30 minutes?"
"Does [hero] scale better in late game when played as carry?"
"Which lane should I play [hero] in for short games?"
Provides time-segmented win rate data showing how heroes perform in specific lanes at different stages of the game. This helps understand which heroes thrive in early/mid/late game scenarios and optimal lane choices.
Supports both IDs and natural language for flexible querying.
Args: lane_role: Lane to analyze. Accepts: - Integer: 1 (Carry-Position 1/Hard Support-Position 5), 2 (Mid), 3 (Offlane-Position 3/Soft Support-Position 4), 4 (Jungle/Support) - String: "mid", "safe lane", "offlane", "jungle", "carry", "support" "pos 1-5", etc. hero_name: Hero to analyze. Accepts: - Integer: Hero ID (e.g., 86 for Rubick) - String: Hero name (e.g., "Rubick", "Anti-Mage")
Note: At least one parameter must be provided. You can provide:
- Only hero_name: See all lanes for this hero by game time
- Only lane_role: See all heroes in this lane by game time
- Both: See specific hero in specific lane by game timeReturns: Dictionary with structure depending on parameters provided:
If only hero_name provided:
- hero_name (str): The hero being analyzed
- [lane_role keys]: One key per lane (e.g., "Safe Lane", "Mid Lane"), each containing:
- List of timing data with time, games, wins, win_rate
If only lane_role provided:
- lane_role (str): The lane being analyzed (e.g., "Mid Lane")
- [hero_name keys]: One key per hero, each containing:
- List of timing data with time, games, wins, win_rate
If both provided:
- hero_name (str): The hero
- lane_role (str): The lane
- timings (list): List of timing data with time, games, wins, win_rate
Each timing entry contains:
- time (str): Game duration in MM:SS format (e.g., "25:30")
- games (int): Number of games at this duration
- wins (int): Number of wins at this duration
- win_rate (str): Win percentage as string (e.g., "56.7")Common queries: - Hero in specific lane: get_scenarios_lane_roles(lane_role="mid", hero_name="Rubick") - All heroes in lane: get_scenarios_lane_roles(lane_role="mid") - Hero in all lanes: get_scenarios_lane_roles(hero_name="Anti-Mage")
Example: get_scenarios_lane_roles(lane_role="mid", hero_name="Rubick") -> { "hero_name": "Rubick", "lane_role": "Mid Lane", "timings": [ {"time": "20:00", "games": 145, "wins": 78, "win_rate": "53.8"}, {"time": "30:00", "games": 234, "wins": 125, "win_rate": "53.4"}, {"time": "40:00", "games": 189, "wins": 98, "win_rate": "51.9"}, {"time": "50:00", "games": 87, "wins": 42, "win_rate": "48.3"}, ... ] }
This shows Rubick's mid lane win rate decreases slightly as games go longer, suggesting he's stronger in early-mid game than late game.
| Name | Required | Description | Default |
|---|---|---|---|
| hero_name | No | ||
| lane_role | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full responsibility. It fully discloses the varying output structure based on which parameters are provided, lists each timing entry field with types, and gives a rich example that even shows interpretation. It also notes that at least one parameter is required, adding important behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long, but well-structured with clear sections (Args, Returns, Common queries, Example) and a front-loaded purpose sentence. Some example queries are repetitive, but each section adds distinct value for an agent, so the length is mostly warranted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given only two parameters, no annotations, and no schema descriptions, this description fully compensates. It covers all input combinations, return shapes for each case, field definitions, an example output, and even a sample interpretation. The agent is left with no ambiguity about invocation or results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, but the description thoroughly documents both parameters. It explains accepted integer codes and string aliases for lane_role, hero_name ID and name formats, and provides concrete examples like '86 for Rubick' and 'Anti-Mage'. It also explains the effect of providing each parameter alone or together.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Get win rates for heroes in specific lane roles segmented by game duration.' It clearly distinguishes this from sibling tools like get_scenarios_item_timings by focusing on lane roles and game duration, and it lists concrete user query examples.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides extensive usage guidance, including exact user questions, common query patterns, and parameter combination rules. It does not explicitly name an alternative tool for exclusion, but it makes the intended use case unmistakable through examples such as 'Which lane should I play [hero] in for short games?'
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
request_parse_matchA
Submit a request to parse a specific match for detailed replay analysis.
Use this when users ask:
"Parse match [match_id]"
Parsing a match extracts detailed information from the replay including:
Teamfight breakdowns (who killed who, where, when)
Objectives timeline (tower kills, Roshan, etc.)
Gold/XP advantage graphs over time
Chat logs
Player movement and positioning data
Note: Parsing takes time (usually 1-5 minutes). After requesting parse, wait a bit before calling get_match_details() to retrieve the parsed data.
Not all matches can be parsed - very old matches or matches from private lobbies may not have replay data available.
Args: match_id: The match ID to parse (e.g., 8123456789)
Returns: Dictionary containing parse request status: - job: Information about the parse job (if queued) - status: Current status of the parse request - May include error information if parse cannot be queued
Common workflow: 1. Request parse: request_parse_match(8123456789) 2. Wait 1-5 minutes 3. Get parsed data: get_match_details(8123456789)
Example: request_parse_match(8123456789) -> { "job": { "jobId": "12345" } }
| Name | Required | Description | Default |
|---|---|---|---|
| match_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully carries the behavioral disclosure burden. It reveals that parsing is asynchronous ('takes time'), non-instant ('wait 1-5 minutes'), and conditional ('Not all matches can be parsed'). It also explains the return structure (status, job, error info) and warns against immediately calling get_match_details. This is exemplary transparency about latency, failure modes, and response semantics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although the description is long, it is well-structured with clear sections (use case, extracted data, notes, Args, Returns, workflow, example). Every section earns its place: the time warning is critical, the return example clarifies the async behavior, and the workflow avoids misuse. No filler or redundancy; the length is proportionate to the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's async nature and the presence of an output schema, the description covers all necessary context: how to trigger, what to expect in the response, how long to wait, potential errors, and the next step in the workflow. It even provides a concrete example output. The description is complete enough for an AI agent to select and invoke the tool correctly without further clarification.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema only specifies match_id as an integer with no description (coverage 0%). The description compensates with 'match_id: The match ID to parse (e.g., 8123456789)', adding an example and the semantic concept of 'match ID'. This adds meaning beyond the raw schema, though the parameter is simple and the extra explanation is minimal.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific action: 'Submit a request to parse a specific match for detailed replay analysis.' It uses the verb 'submit' and the resource 'match', distinguishing this from the sibling get_match_details by positioning it as the request initiation step. The trigger phrase 'Parse match [match_id]' also directly maps to user intent.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit when-to-use guidance: 'Use this when users ask: Parse match...'. It also details a common workflow (request, wait 1-5 minutes, then get_match_details), clearly indicating this tool is a prerequisite for the sibling tool. However, it does not explicitly state when NOT to use this tool (e.g., when match data is already parsed), though the workflow implicitly covers that.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
18 tool updates
v1.0.0- First observed
get_aghs_details - First observed
get_benchmarks - First observed
get_hero_details - First observed
get_hero_item_popularity - First observed
get_hero_matchups - First observed
get_heroes_played - First observed
get_item_details - First observed
get_match_details - First observed
get_player_histograms - First observed
get_player_info - First observed
get_player_peers - First observed
get_player_totals - First observed
get_player_win_loss - First observed
get_recent_matches - First observed
get_records - First observed
get_scenarios_item_timings - First observed
get_scenarios_lane_roles - First observed
request_parse_match
TDQS
Scored across 18 tools
Each tool targets a distinct resource: player profile, win/loss, heroes, totals, histograms, peers, match details, heroes, items, aghs upgrades, matchups, item popularity, benchmarks, records, and scenario timings. The descriptions explicitly cross-reference similar tools to prevent confusion, e.g., 'For simple win/loss counts, use get_player_win_loss() instead'.
All tools use a 'get_' prefix except for request_parse_match, which breaks the pattern. While most names are clear and consistent (get_player_*, get_hero_*, get_scenarios_*), minor grammatical variations like get_heroes_played (past participle) and get_player_win_loss (compound noun) create slight inconsistency.
18 tools is slightly above the typical well-scoped range of 3-15, but justified given the broad Dota 2 domain covering players, matches, heroes, items, records, and scenarios. The count is manageable and each tool has a unique purpose, though it borders on heavy.
The toolset covers the core lifecycle: player overview, detailed match info (including parse request), hero reference, item reference, and various analytics. Minor gaps exist (no list-all-heroes/items tool, no opponent-only stats, no global hero tier list), but agents can work around these using the provided tools.
Maintenance
Related MCP Connectors
Wynncraft MMO player + guild + leaderboard + item DB lookups.
Live competitive-game predictions, meta data and decision tools for AI agents.
Related MCP Servers
- AlicenseBqualityCmaintenanceModel Context Protocol server that enables LLMs and AI assistants to retrieve real-time Dota 2 statistics, match data, player information, and game metrics through a standardized interface.178MIT
- FlicenseNot gradedqualityDmaintenanceEnables interaction with League of Legends game data through the Riot Games API. Allows users to query player statistics, match history, and game information using natural language.-
- FlicenseNot gradedqualityDmaintenanceEnables querying RGL.gg competitive TF2 data through natural language, resolving player, team, season, division, and match information via a series of API calls.-
- FlicenseNot gradedqualityDmaintenanceEnables querying Mobile Legends: Bang Bang hero data including win rates, counters, synergies, and builds via natural language.-