Docs Let AI write your bot

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:

RoleCan use
dev (default)All tools, including war3_jass
player --player NCan command only player N’s units and sees only its vision (fair mode); no JASS
observerRead-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

ToolWhat it does
war3_overviewOne-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_unitsUnit list (owner is me / enemy / creep / all; filter with types); use addr to issue commands
war3_eventsWhat 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_callCall any public API (move, attack_move, train, build, cast, learn, ui.button, canvas.text…); write units as {"unit": addr}
war3_apiLook up APIs: search names and descriptions by keyword
war3_toast / war3_sayA line of text at the top of the screen / a line over a unit’s head
war3_ask_playerShows 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_screenshotScreenshot of the game (PNG; works even when the window is covered, and doesn’t steal focus)
war3_jassRun a JASS snippet (dev only; changes to the world only in single-player games)

What you can build with it:

  • Play buddy / coach: war3_overview to read the game, war3_toast to give advice on screen;
  • Ask the player mid-game: war3_ask_player pops up three cards, and whichever one the player clicks is what happens;
  • Commentary: war3_events reads what happened, and war3_say lets the units say it themselves;
  • Command a force directly: player role + war3_call, which can only move its own units;
  • Check the UI by looking: war3_screenshot takes 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 (htow 1, hpea 5) → 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 as mcp__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.