Skip to main content
Glama
KyodanCFG
by KyodanCFG
README.md
# trenchbroom-mcp

MCP server for editing Quake-format `.map` files, the format TrenchBroom
reads and writes. TrenchBroom has no scripting API, so this server treats the
map file itself as the API: an AI client edits the file, and TrenchBroom picks
the change up.

With the [patched TrenchBroom](#live-editing) the change appears in the editor
as it happens, keeping your camera, selection and undo history, and it can be
undone like any other edit. Without it, everything still works; you press
reload.

## What it can do

- **Read**: `map_overview`, `list_entities`, `get_entity`: counts, bounds,
  TrenchBroom layers/groups (`_tb_*` metadata), textures, per-entity detail.
- **Edit**: `add_entity`, `set_entity_properties`, `delete_entity`,
  `add_brush_box`, `delete_brush`, `translate_entity`. Every edit snapshots
  the file first; `undo_last_edit` / `list_backups` roll back.
- **See**: `render_preview` draws top/front/side orthographic SVG views;
  brush footprints are computed by real half-space intersection, so the model can
  reason about layout instead of editing blind.
- **Know the game**: `fgd_classes` / `fgd_class` parse an FGD entity
  definition file, with base-class inheritance resolved.
- **Compile**: `compile_map` runs [ericw-tools](https://ericwa.github.io/ericw-tools/)
  and reports what the compiler said: errors, grouped warnings, and leaks,
  with the coordinate the leak escaped from.
- **Play**: `launch_map` installs the compiled `.bsp` into the game's maps
  directory and starts an engine on it, reporting what the engine said if it
  refused.

Supports Valve 220 and standard (idTech) face formats, plus Quake 2 surface
flags. Unmodified lines are written back byte-for-byte, so diffs only touch
what actually changed.

## Setup

### Build

You need [Node.js](https://nodejs.org/) 20 or newer (`node --version` to
check). The build step is not optional: the server runs from `dist/`, which is
not checked in, so a fresh clone that skips it registers a server that cannot
start.

```
git clone <repo-url> trenchbroom-mcp
cd trenchbroom-mcp
npm install
npm run build
```

Note the absolute path to `dist/index.js`; every client below needs it. On
Windows it looks like `C:\Users\you\src\trenchbroom-mcp\dist\index.js`.

### Or: a single file, no clone, no build

`npm run bundle` produces `dist/trenchbroom-mcp.mjs`: the whole server and its
dependencies in one ~760 kb file that runs anywhere Node 20+ is installed, with
no `node_modules` beside it:

```
node /path/to/trenchbroom-mcp.mjs
```

This is the easiest way to move the server to another machine or hand it to
someone else: copy one file and point the client at it. Everywhere below that
says `dist/index.js`, you can say `trenchbroom-mcp.mjs` instead.

This is a plain [MCP](https://modelcontextprotocol.io/) server speaking JSON-RPC
over stdio, with nothing vendor-specific in it, so any MCP client can drive
it, including local models. Three setups follow; they all say the same thing
in different files.

> **Whatever the client, restart it after `npm run build`.** MCP servers are
> started when the client starts, so until you do you are running the old code.
> This is the single most common thing to trip over.

### Claude Code

```
claude mcp add --scope user trenchbroom -- node /absolute/path/to/trenchbroom-mcp/dist/index.js
```

`--scope user` makes it available in every project rather than only the one you
are in. Relative paths are stored verbatim and break as soon as you work
elsewhere, so use an absolute one. Check it with `claude mcp list`; you want
`trenchbroom: ... - ✔ Connected`.

### Claude Desktop

Edit `claude_desktop_config.json` (on Windows
`%APPDATA%\Claude\`, on macOS `~/Library/Application Support/Claude/`) and add
an entry under `mcpServers`:

```json
{
  "mcpServers": {
    "trenchbroom": {
      "command": "node",
      "args": ["C:/Users/you/src/trenchbroom-mcp/dist/index.js"]
    }
  }
}
```

Forward slashes work on Windows and avoid escaping every backslash. If the file
already has other servers, add this alongside them rather than replacing them.

### A local model

Any client that supports MCP and lets you point at a local backend works:
[LM Studio](https://lmstudio.ai/), [Goose](https://block.github.io/goose/),
[Cline](https://cline.bot/) or [Continue](https://continue.dev/) in VS Code, or
[LibreChat](https://www.librechat.ai/), among others. They differ in where the
config file lives, but the entry is the same `command` plus `args` shape as
above, because that is the MCP standard rather than anything Claude-specific.

The plumbing is the easy part; the model is not. These tools need real
multi-step tool use: read the map, reason about coordinates in 3D, emit exact
numeric arguments, then check the result. A model that is weak at tool calling
will pick the right tool with wrong numbers, which is worse than failing,
because the map still changes. In practice a 30B-class coding model is about
the floor.

Start a new model on the read-only tools (`map_overview`, `list_entities`,
`get_entity`, `fgd_class`, `render_preview`) and see how it does before letting
it write. The snapshots and `undo_last_edit` are there either way.

## Using it

Point the client at a map by absolute path: *"give me an overview of
C:\quake\mymod\maps\e1m1.map"*. From there, things like *"dim every light by
30%"*, *"list any entity nothing targets"*, or *"add a 512 unit room at the
origin with 16 unit walls"*.

## Live editing

By default you reload the map in TrenchBroom yourself after the server edits
it; TrenchBroom does not watch the file.

There is a patched build that does. It notices when another program changes
the open map and applies the change in place, keeping your camera, selection,
hidden and locked objects, and undo history. The change itself becomes
undoable, so <kbd>Ctrl</kbd>+<kbd>Z</kbd> undoes an edit made by the AI. With
it, edits simply appear in the editor as they happen. A version of this is
proposed upstream in
[TrenchBroom#5453](https://github.com/TrenchBroom/TrenchBroom/issues/5453).

Without the patch everything still works; you just press reload.

## Compiling

`compile_map` runs [ericw-tools](https://ericwa.github.io/ericw-tools/) and
returns what the compiler said, rather than a wall of text. Point it at the
tools with `tools_dir`, or set `ERICW_TOOLS_DIR`, or put them on `PATH`.

It runs `qbsp` by default, which is the fast structural check; pass
`stages: ["qbsp", "vis", "light"]` for a full build.

Two things it takes care of, both of which otherwise make the output useless
to a model:

- The tools print hundreds of percent counters, often several to a line with
  real output stuck on the end. Those are stripped, and repeated warnings are
  grouped with a count instead of listed one by one.
- **A leak is reported as a warning and the compiler still exits 0.** So exit
  status is not a useful signal, and `compile_map` reports `success: false`
  for a leak, along with the coordinate the compiler escaped from and the
  pointfile. That coordinate names an entity inside your level that can see
  the void, which is the thing you actually need to know.

```
"compile the map and tell me if it leaks"
  -> qbsp ok, 6 warning(s), 0.01s | LEAK at 0 0 64
```

## Playing

`launch_map` copies the compiled `.bsp` (and its `.lit`, if there is one)
into `<game_dir>/<mod>/maps/`, then starts the engine on that map with
`-basedir`, `-game` and `+map`, which QuakeSpasm, vkQuake and ironwail all
understand. Anything engine-specific goes in `extra_args`.

The engine is started detached, so it outlives the request and you can go and
play. Its output is written to a file rather than a pipe, because a pipe dies
with the server and takes the engine with it. If the engine gives up during
the first few seconds (a missing `pak0.pak`, a map it cannot find), that is
reported back with whatever it said, taken from its output and from
`qconsole.log`, which `-condebug` asks it to write.

```
"compile it and run it"
  -> qbsp ok, vis ok, light ok | launched e1m1 (pid 15588)
```

The tools drop a `.log` beside the map: qbsp names it after the map, vis and
light write a fixed `vis.log` and `light.log`. Everything in them has already
been parsed out of the output, so they are removed afterwards; pass
`keep_logs` to leave them. A log that was already there before the compile ran
is left alone.

## Workflow notes

- Save in TrenchBroom before asking for edits; the server reads whatever is
  on disk, not what is in the editor.
- Do not make changes in TrenchBroom and via the server at the same time. With
  the patched build the editor asks before discarding unsaved work; without
  it, last save wins.
- Writes are atomic (temp file + rename, retried briefly if the OS has the
  file locked). Snapshots live in `<mapdir>/.tb-mcp/backups/`, capped at 20,
  and that directory ignores itself in git.
- The comments at the top of a map file (`// Game: Quake`) are preserved;
  TrenchBroom picks the game and format from them.

## What this is actually for

Separately, each half is modest. This server is a `.map` file editor; the
patch is a file watcher. Together they remove the thing that made AI level
editing useless: **the round trip.** You don't export, or reload, or context
switch. You say something, and the map changes in front of you, in the editor
you were already using, with your camera where you left it and your selection
intact. And because the change arrives as an ordinary undoable edit,
<kbd>Ctrl</kbd>+<kbd>Z</kbd> is the escape hatch. That is what makes it safe
to try things instead of carefully vetting each one.

The subtler shift: the model can *see* the map. Between `map_overview`,
`list_entities` and the SVG previews, it reasons about your actual level (its
bounds, its entity graph, its layout) instead of generating plausible-looking
map text blind.

### Typical use

The bread and butter is **tedium at scale**. Dim every light by 30%.
Retarget `door1` to `bridge_door` everywhere. Delete every monster for a
no-combat build. Bump all health pickups up 8 units because they're clipping
the floor. These are minutes of clicking, or a script you'd have to write, and
now they're a sentence.

Second is **interrogation**. "Which entities does nothing target?" "What
textures does this actually use?" (trim the WAD) "Is anything outside the
world bounds?" TrenchBroom has an Issues panel, but it can't answer arbitrary
questions about *your* map's logic.

Third is **blockout**. Rooms, corridors, stairs, pillar rows, lights at
intervals. Axis-aligned boxes are exactly what greyboxing is, so the current
limitation bites much less here than it would for detail work.

Fourth, and underrated: **learning a game's entity vocabulary.** The FGD tools
mean the model can tell you what `func_door` actually supports in *your* game
(including a custom FGD for a mod) instead of half-remembering Quake trivia.

### Where it gets genuinely interesting

**Parametric level design.** "Make the courtyard 25% bigger and move everything
that was against the north wall." Iterating on layout at the speed of
conversation, with the editor as live preview. The map
stops being a static artifact and becomes something you can nudge.

**Closing the compile loop.** `compile_map` and `launch_map` make the compiler
part of the
conversation rather than a separate ritual. Leaks are the case that matters:
the compiler reports one as a warning, exits successfully, and quietly
produces a broken build, so the tool treats a leak as failure and hands back
the coordinate it escaped from. "You leaked at 0 0 64" is a fixable sentence,
and fix, recompile, verify is a loop a machine is good at running. Quake
mapping's most painful chore becomes a dialogue, and "compile it and run it"
ends with the map open in front of you.

**Whole-map refactors that nobody attempts by hand.** Convert every `func_wall`
to `func_detail`. Renumber a broken trigger chain. Port entity names from Quake
to a mod's conventions. These are "I'd rather rebuild the level" jobs today.

**Your own games.** TrenchBroom's generic game config plus custom FGDs means
this stack points at any brush-based game, not Quake alone.

**Two people, one map.** Nothing about the design assumes the other editor is
an AI. It's "another program changed the file." Two mappers with a shared file,
or a generator running continuously while you hand-detail, both work.

### The honest ceiling

It can't do rotated or non-box geometry, so it greyboxes rather than finishes.
It has no aesthetic judgement: it'll place lights at perfect intervals, which
is exactly what a good mapper wouldn't do. And it can't play the level, so
nothing it makes has been evaluated for whether it's *fun*.

The right mental model is a fast, tireless assistant who knows the file format
cold and has no taste. You bring the taste.

## Testing

```
npm test
```

## Limitations

- Brushes can only be created as axis-aligned boxes. No wedges, cylinders, or
  rotation, so this builds blockouts rather than finished geometry.
- No texture alignment control beyond what is preserved on untouched faces.
- Editing a map by hand while the server is also editing it is not supported.
- Compiling needs ericw-tools installed separately; it is not bundled, and
  launching needs an engine and the game data it expects.
- Launching has only been proven against a stand-in engine, not a real Quake
  engine on a real install.

## Roadmap

- More brush primitives (wedge, cylinder), rotation, texture retargeting.
- Upstreaming the editor patch: proposed in
  [TrenchBroom#5453](https://github.com/TrenchBroom/TrenchBroom/issues/5453),
  starting with a simplified reload dialog per maintainer feedback. (#3505 asked
  for external reload back in 2020.)

TDQS

A4.1/5.0

Scored across 17 tools

Disambiguation5/5

Each tool targets a distinct operation: map info, undo, preview, two separate compilers, FGD queries, entity CRUD, brush add/delete, and translation. No two tools have overlapping purposes; even compile_map and compile_shogo_map are clearly differentiated by toolchain.

Naming Consistency4/5

Most tools follow a verb_noun pattern in snake_case (list_entities, add_entity, delete_brush, translate_entity). Two tools (fgd_classes, fgd_class) use a noun-first structure, but the pattern is still predictable and all names are lowercase with underscores, so minor deviation only.

Tool Count4/5

With 17 tools, the count is slightly above the typical 3-15 range, but each tool serves a distinct need for map editing, compilation, and FGD introspection. The scope is broad but well-justified, making the count reasonable.

Completeness5/5

The tool surface covers the full lifecycle: map analysis, editing (add/update/delete entities and brushes), transformation (translate), undo/backups, preview rendering, compilation for two engines, and FGD entity lookups. No significant gaps for the stated purpose of TrenchBroom map manipulation.

Maintenance

ActivityMaintained
ResponsivenessNo issues