- Pass the /ask question to claude on stdin so it can't be parsed as a CLI option. - Block run_lua in multiplayer unless an admin enables the new "Allow Lua queries in multiplayer" map setting. - Ignore UDP packets that don't come from the game's port, in both the MCP server and the ask helper. - Build the screenshot path from our own name instead of the game's reply, and restrict screenshot names in the mod. - Validate ask packets, cap question length and limit concurrent claude runs. - Replace hard-coded home paths in the README and systemd unit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
172 lines
8.7 KiB
Markdown
172 lines
8.7 KiB
Markdown
# Claude Co-Engineer
|
|
|
|
Ask Claude questions about your live Factorio game — "what should I do next?", "why is my
|
|
science slow?", "where's the nearest iron?" — from Claude Desktop or Claude Code, using your
|
|
subscription instead of API tokens. Works in singleplayer; no RCON needed.
|
|
|
|
It's read-only by design: Claude looks, explains and suggests; you play.
|
|
|
|
```
|
|
Claude Desktop / Claude Code ──stdio(MCP)──▶ server/ (Python) ──UDP localhost──▶ mod/ (in-game Lua)
|
|
```
|
|
|
|
The mod uses Factorio 2.x's Lua UDP API (`helpers.recv_udp` / `send_udp`), which is enabled
|
|
per game instance with `--enable-lua-udp <port>`.
|
|
|
|
## Setup
|
|
|
|
**1. Install the mod** (symlinked, so edits apply on the next save load):
|
|
|
|
```bash
|
|
ln -s "$PWD/mod/claude-co-engineer" ~/.factorio/mods/claude-co-engineer
|
|
```
|
|
|
|
Then enable "Claude Co-Engineer" in the in-game Mods menu.
|
|
|
|
**2. Launch Factorio with UDP enabled.** In Steam: Factorio → Properties → Launch Options, add:
|
|
|
|
```
|
|
--enable-lua-udp 34200
|
|
```
|
|
|
|
The game only listens on 127.0.0.1.
|
|
|
|
**3. Register the MCP server.** Requires [uv](https://docs.astral.sh/uv/).
|
|
|
|
Claude Code (run from this repo's root):
|
|
|
|
```bash
|
|
claude mcp add factorio --scope user -e FACTORIO_UDP_PORT=34200 -- uv run --directory "$PWD/server" claude-co-engineer-mcp
|
|
```
|
|
|
|
Claude Desktop — add to `claude_desktop_config.json` (Settings → Developer → Edit Config), with
|
|
`/path/to/claude-co-engineer` replaced by where you cloned this repo, then restart it:
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"factorio": {
|
|
"command": "uv",
|
|
"args": ["run", "--directory", "/path/to/claude-co-engineer/server", "claude-co-engineer-mcp"],
|
|
"env": { "FACTORIO_UDP_PORT": "34200" }
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Local (stdio) MCP servers only work in the desktop app and Claude Code, not claude.ai in a browser
|
|
or the mobile apps.
|
|
|
|
## Tools
|
|
|
|
| Tool | Answers |
|
|
|---|---|
|
|
| `get_overview` | Where am I, what am I hovering, what's researching, what's broken — start here |
|
|
| `get_player` | Inventory, cursor, hand-crafting queue, hovered/opened entity |
|
|
| `inspect_entity` | Everything about the hovered entity (or one at x,y): status, recipe, contents, fuel, power |
|
|
| `find_problems` | Machines not working, grouped by status (no_ingredients, full_output, low_power, …) with nearest examples |
|
|
| `find_entities` | What's around a point; ore patches with total amounts |
|
|
| `get_production` | Produced/consumed/net per minute per item and fluid |
|
|
| `get_power` | kW consumption and production per electric network |
|
|
| `trace_belt` | Follows a belt (the hovered one by default) through undergrounds, side-loads and splitters: fill and motion per section (backed up vs full and flowing), what inserters and drills put on or take off, and where it ends |
|
|
| `get_trends` | How production, consumption and power changed over the last 10 min / hour / 10 hours; finds the biggest movers by itself |
|
|
| `get_research` | Current/queued research, what's researchable now, the path to any technology |
|
|
| `get_recipe` | How to make something, in which machines, whether it's unlocked, what uses it |
|
|
| `calculate_ratios` | Machines, drills, belts and power for a target rate (or machine count), using your unlocked recipes and machines, research productivity, and what you've already built |
|
|
| `screenshot` | An image of part of the map (Alt mode on, always daylight) for layout questions |
|
|
| `highlight` / `clear_highlights` | Outline places in your game, with labels, guide lines from you, and clickable map links in chat |
|
|
| `run_lua` | Read-only Lua query for anything else |
|
|
|
|
"This" usually works: hover over a machine and ask "why isn't this working?".
|
|
|
|
When Claude points you at something, it outlines it in your world and posts a `[gps]` link in chat
|
|
(click it to open the map there). Highlights are visible only to you, expire after a minute by
|
|
default, and are purely visual.
|
|
|
|
`run_lua` is instructed to only read, but Lua can't be sandboxed read-only. To guarantee nothing in
|
|
your world changes, turn off **Allow Lua queries** in Settings → Mod settings → Map. Every query is
|
|
logged to `factorio-current.log`.
|
|
|
|
In multiplayer, `run_lua` is off unless an admin also turns on **Allow Lua queries in multiplayer**:
|
|
the Lua runs for everyone, and text other players put in the world (station names, labels) ends up
|
|
in what Claude reads, so it could be talked into changing things.
|
|
|
|
## Asking from inside the game
|
|
|
|
Type `/ask <question>` in the game chat (e.g. `/ask why is my green science slow?`). The answer
|
|
appears in chat a few seconds to a minute later, with clickable map links and item icons, and
|
|
the places it mentions get highlighted. Follow-up questions continue the same conversation;
|
|
`/ask-new` starts a fresh one.
|
|
|
|
```
|
|
/ask ──UDP──▶ claude-co-engineer-ask ──▶ claude -p (+ this MCP server) ──▶ answer ──UDP──▶ chat
|
|
```
|
|
|
|
This needs:
|
|
|
|
- the `claude` CLI logged in with your subscription (`claude auth login`, once);
|
|
- the helper running. Either start it by hand from this repo's root:
|
|
|
|
```bash
|
|
uv run --directory server claude-co-engineer-ask
|
|
```
|
|
|
|
or install it as a systemd user service that starts at login (from this repo's root; this fills in
|
|
its path):
|
|
|
|
```bash
|
|
uv sync --directory server && mkdir -p ~/.config/systemd/user && sed "s|/path/to/claude-co-engineer|$PWD|" server/claude-co-engineer-ask.service > ~/.config/systemd/user/claude-co-engineer-ask.service && systemctl --user daemon-reload && systemctl --user enable --now claude-co-engineer-ask
|
|
```
|
|
|
|
Its log shows each question and answer: `journalctl --user -u claude-co-engineer-ask -f`.
|
|
|
|
Pick the model under Settings → Mod settings → Per player → **/ask model**: Haiku, Sonnet
|
|
(default), Opus or Fable. Bigger models give more thorough answers but are slower and use more of
|
|
your plan's usage. It's per player, so in multiplayer everyone picks their own.
|
|
|
|
The dropdown entries are aliases, which the `claude` CLI resolves to the newest model of that name,
|
|
so a new Sonnet or Opus is used automatically. To force any other model, uncomment
|
|
`FACTORIO_ASK_MODEL_OVERRIDE` in `~/.config/systemd/user/claude-co-engineer-ask.service` (an alias or a full model ID)
|
|
and run `systemctl --user daemon-reload && systemctl --user restart claude-co-engineer-ask`. It wins
|
|
over the in-game setting for everyone.
|
|
|
|
Claude only gets the Factorio tools in these sessions: no shell, files or web. If the helper isn't
|
|
running, the game tells you after 3 seconds. The helper's port is a map setting (Ask helper port, default 34201).
|
|
Helper environment variables: `FACTORIO_ASK_PORT`, `FACTORIO_ASK_MODEL` (model when the game doesn't
|
|
send one, default `sonnet`), `FACTORIO_ASK_MODEL_OVERRIDE` (always use this model),
|
|
`FACTORIO_ASK_TIMEOUT` (300 s), `FACTORIO_ASK_MAX_CONCURRENT` (Claude runs at once across all players,
|
|
default 2), `FACTORIO_ASK_CLAUDE` (path to the CLI). The helper only accepts packets from the game's UDP port.
|
|
|
|
## Ratio calculator
|
|
|
|
Ask things like "how many assemblers for 1 green science per second?" or "what does 10 red
|
|
science assemblers need?". `calculate_ratios` works from your game, not a generic table:
|
|
|
|
- **Recipe:** the unlocked one for each item (never recycling). Mineable items count as mined, so
|
|
Space Age asteroid ore doesn't sneak in.
|
|
- **Machine:** the type you already use for that recipe, otherwise the fastest one you've unlocked.
|
|
- **Productivity:** research and machine bonuses are included. Modules and beacons aren't (yet).
|
|
- **Output:** per step, how many machines you need, how many you've already built on that recipe,
|
|
and how much you currently make. Ore becomes mining drill and belt counts.
|
|
|
|
Tell it what you already supply ("plates come from my main bus") to stop the breakdown there, or
|
|
override the recipe or machine for any item. Byproducts are listed but not subtracted from demand.
|
|
|
|
## Configuration
|
|
|
|
Environment variables for the server: `FACTORIO_UDP_PORT` (default 34200), `FACTORIO_UDP_HOST`
|
|
(127.0.0.1), `FACTORIO_TIMEOUT` seconds (5), `FACTORIO_SCRIPT_OUTPUT` (`~/.factorio/script-output`).
|
|
|
|
Screenshots are rendered off-screen with `game.take_screenshot` (your camera doesn't move), written
|
|
to `script-output/claude-co-engineer/`, and deleted once sent to Claude.
|
|
|
|
## Troubleshooting
|
|
|
|
- **"No reply from Factorio"**: the game must be running a save, launched with `--enable-lua-udp`
|
|
on the same port, with the mod enabled — and not paused. Singleplayer pauses while the Esc menu is open.
|
|
- **Headless servers** pause with no players connected unless `auto_pause` is false in
|
|
server settings. With nobody connected, queries use the `player` force and the first surface.
|
|
- **Screenshots** need a game client that renders; a headless server can't take them.
|
|
- Changes to `control.lua` take effect after reloading the save; changes to `settings.lua` need a
|
|
full game restart.
|