LLM calls tools directly (MCP)
tools/war3_mcp.py is an MCP server. Hook it up to Claude Code, Claude Desktop or any MCP-capable client, and the LLM can read the game, issue commands, talk to the player on screen, ask the player with pop-up cards and look at screenshots — no code to write first.
tools/war3_mcp.py is an MCP server (stdio). Claude Code, Claude Desktop, agent frameworks for local models — hook it up to any MCP-capable client, and the LLM can directly read the game, issue commands, talk to the player on the game screen, ask the player questions and look at screenshots, with no code to write first.
Besides writing bots, coaching, and making units talk, this is one more way to connect: the LLM itself is the one using the tools.
Hook it up
claude mcp add war3 -- python <repo>\tools\war3_mcp.py --inst 9 # Claude Code; replace <repo> with your openwar3 folder
For other clients, write the config in this format:
{"mcpServers": {"war3": {"command": "python", "args": ["<repo>\\tools\\war3_mcp.py", "--inst", "9"]}}}
It connects to the game only on the first tool call, so the game can be started later; if the game is closed and restarted, the next call reconnects automatically. Add --role to limit what the LLM can do:
| Role | Can use |
|---|---|
dev (default) | All tools, including war3_jass |
player --player N | Can command only player N’s units and sees only its vision (fair mode); no JASS |
observer | Read-only; can’t draw on the screen or make units talk; the runtime rejects its commands outright |
The player role has the same limits as on the gateway: no ending the game, changing the speed or pausing, no APIs that reveal other players’ hands, and queries that take a player number can only query its own player.
A few limits: a tool result is at most 200,000 characters, and anything beyond that is truncated with a hint on how to narrow the request; war3_ask_player waits at most 120 seconds; a screenshot’s scale is between 0.1 and 1.
Tools
| Tool | What it does |
|---|---|
war3_overview | One-page overview: time, resources, food, counts of each of our unit types, heroes (HP, mana, level, cooldowns), visible enemy unit types, production. Call this first |
war3_units | Unit list (owner is me / enemy / creep / all; filter with types); use addr to issue commands |
war3_events | What happened since the last call: deaths, level-ups, spell casts, production finished, chat, the player clicking a button… (the noisiest kinds are left out by default) |
war3_call | Call any public API (move, attack_move, train, build, cast, learn, ui.button, canvas.text…); write units as {"unit": addr} |
war3_api | Look up APIs: search names and descriptions by keyword |
war3_toast / war3_say | A line of text at the top of the screen / a line over a unit’s head |
war3_ask_player | Shows the player a few choice cards in the middle of the screen, waits for a click, and returns which one was picked (can pause the game) |
war3_screenshot | Screenshot of the game (PNG; works even when the window is covered, and doesn’t steal focus) |
war3_jass | Run a JASS snippet (dev only; changes to the world only in single-player games) |
What you can build with it:
- Play buddy / coach:
war3_overviewto read the game,war3_toastto give advice on screen; - Ask the player mid-game:
war3_ask_playerpops up three cards, and whichever one the player clicks is what happens; - Commentary:
war3_eventsreads what happened, andwar3_saylets the units say it themselves; - Command a force directly:
playerrole +war3_call, which can only move its own units; - Check the UI by looking:
war3_screenshottakes a shot to see whether the buttons you drew are laid out right.
What a conversation looks like
You: Take a look at the game, then ask me on screen: expand, build army, or tier up next?
→ war3_overview {}
← One-page overview: game time, gold 500, food 10/12, ours htow 1 · hpea 5 · Hpal 1, no enemies seen, nothing in production
→ war3_ask_player {"question": "Next?", "options": ["Build army", "Expand", "Tier up"], "pause": true}
← {"picked": 1, "option": "Expand"}
Model: You picked expand. First I'll use war3_units to find an idle peasant, then look for the nearest gold mine…
Measured
2026-09-25:
- Our own MCP client against a real game, 7/7: handshake → list tools (10) →
war3_overview(htow1,hpea5) →war3_units→war3_toast→war3_screenshot(PNG of about 200,000 bytes) →war3_ask_player(three cards, simulated click on the second →{"picked": 1, "option": "Expand"}). - Hooked up in Claude Code 2.1: it started the server and handshook on its own, status
connected, and all 10 tools showed up in its tool list asmcp__war3__*.
Implementation
- Newline-delimited JSON-RPC 2.0 (
initialize/tools/list/tools/call/ping), protocol version 2025-06-18, compatible with 2025-03-26 and 2024-11-05. - Tool errors go in the result per MCP convention (
isError: true); the connection stays open. - Shares the same role whitelists, unit argument format and “one-page overview” with the gateway.
- Logs go to stderr; stdout carries only the protocol.