# 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.

Source: https://war3ai.com/en/docs/mcp/

`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

```bash
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:

```json
{"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](https://war3ai.com/en/docs/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_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

```text
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](https://war3ai.com/en/docs/gateway/).
- Logs go to stderr; stdout carries only the protocol.
