Skip to main content
Glama
README.md
# Goxel-MCP

A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for
the [Goxel](https://goxel.xyz) voxel editor.  It lets any MCP client (Claude
Code, Claude Desktop, Gemini CLI, Codex CLI, and others) create and edit voxel
models with tools such as "draw a box", "recolor these voxels", "add a layer",
"save" and "export".  You can work on files, or edit the scene that is open in
Goxel and watch the model change in the editor as you go.

![Supercar built with Goxel-MCP, rendered by Goxel](docs/images/supercar.png)

- 17 tools: scenes, voxels, primitives, regions, colors, layers, preview,
  save, export, validation, live editing and undo.
- Reads and writes Goxel's native `.gox` format, and MagicaVoxel's `.vox`.
  Exports `.obj`/`.mtl`, `.txt` and `.png` natively, plus `.gltf`, `.glb`,
  `.ply`, `.qb`, `.kv6`, `.kvx`, `.vxl` and `.pov` through Goxel's own
  exporters.
- Built-in preview renderer: tools return PNG images, so the client can look
  at the model it is building.
- Every file written is validated with Goxel's own parser, voxel by voxel,
  when Goxel is available.
- Safe by default: all paths stay inside a workspace folder, nothing is
  overwritten without asking, and backups are made before anything is
  replaced.
- Local only: stdio transport, no network services, no accounts.

---

## Contents

1. [How it works](#how-it-works)
2. [Requirements](#requirements)
3. [Step-by-step setup](#step-by-step-setup)
4. [Using it](#using-it)
5. [Tools](#tools)
6. [Conventions](#conventions)
7. [Examples](#examples)
8. [Safety](#safety)
9. [Troubleshooting](#troubleshooting)
10. [Development](#development)
11. [Limitations](#limitations)
12. [License](#license)

---

## How it works

```
 MCP client ──stdio──> goxel-mcp ──┬── file mode: scene in memory, read / written as .gox, .vox, ...
 (Claude Code,                     │
  Gemini CLI, ...)                 └── live mode: Unix socket ──> Goxel editor (command bridge)
```

**File mode** works everywhere and needs only Node.js.  The scene is kept in
memory, and the tools read and write voxel files directly.  The server has its
own readers and writers for `.gox` and `.vox`, written from Goxel's source
code.

**Live mode** edits the scene open in the Goxel editor.  It uses the Goxel
*command bridge*: a small local server built into Goxel that listens on a Unix
socket (see [docs/BRIDGE.md](docs/BRIDGE.md)).  Before each operation the
server reads the scene from Goxel, so edits you make by hand in the editor are
kept.  It then sends only the changes back, as a single undo step.

The bridge is not part of the official Goxel release yet.
[`scripts/build-goxel.sh`](scripts/build-goxel.sh) builds Goxel from the
official sources and adds the bridge from
[`bridge/goxel-mcp-bridge.patch`](bridge/goxel-mcp-bridge.patch).

---

## Requirements

| | File mode | Live mode and Goxel exporters |
|---|---|---|
| Node.js 20 or newer | required | required |
| Goxel built with the bridge | – | required (step 3) |
| macOS | ✓ | ✓ |
| Linux / BSD | ✓ | ✓ (needs `scons`, `pkg-config`, GLFW and GTK 3 development packages) |
| Windows | ✓ | – (the bridge uses Unix sockets) |

---

## Step-by-step setup

### 1. Get the code

```bash
git clone https://github.com/yberudev/Goxel-MCP.git
cd Goxel-MCP
```

### 2. Install and build the server

```bash
npm install
npm run build
```

This creates `dist/index.js`, the MCP server.  You can check it with:

```bash
npm test
```

### 3. Build Goxel with the command bridge (for live mode)

Skip this step if you only want to work with files.

```bash
npm run build:goxel
```

The script:

1. clones the official Goxel repository into `goxel-src/` (or uses
   `GOXEL_SRC=/path/to/goxel` if you already have the sources);
2. copies the sources to `goxel-build/` and applies the bridge patch if
   needed;
3. builds `goxel-build/goxel` in release mode.

Nothing is installed globally and `sudo` is never used.  On macOS, missing
build tools (SCons, CMake, GLFW) are installed locally in `.tools/`.  On
Debian / Ubuntu, install the dependencies first:

```bash
sudo apt install scons pkg-config libglfw3-dev libgtk-3-dev
```

On macOS you can also make a double-clickable application that starts Goxel
with the bridge for a given workspace:

```bash
sh scripts/make-app.sh /path/to/your/workspace
```

This creates `Goxel MCP.app` in the repository folder.  The first time you
open it, macOS may ask you to confirm that you want to run it.

### 4. Choose a workspace

The workspace is the only folder the server reads and writes.  Your models,
exports and previews go there, and the server keeps its backups and the
bridge socket in `<workspace>/.goxel-mcp/`.  Set it with the
`GOXEL_MCP_WORKSPACE` environment variable; when the variable is not set,
the current directory is used.

### 5. Register the server with your MCP client

Replace `/path/to/Goxel-MCP` and `/path/to/workspace` with your own folders.

**Claude Code**, for the current project:

```bash
claude mcp add --scope project goxel -e GOXEL_MCP_WORKSPACE=/path/to/workspace -- node /path/to/Goxel-MCP/dist/index.js
```

or add this to `.mcp.json` at the root of the project:

```json
{
  "mcpServers": {
    "goxel": {
      "type": "stdio",
      "command": "node",
      "args": ["/path/to/Goxel-MCP/dist/index.js"],
      "env": { "GOXEL_MCP_WORKSPACE": "/path/to/workspace" }
    }
  }
}
```

**Claude Desktop**: add the same `"goxel"` entry under `"mcpServers"` in
`claude_desktop_config.json`.

**Gemini CLI**: add it to `.gemini/settings.json`:

```json
{
  "mcpServers": {
    "goxel": {
      "command": "node",
      "args": ["/path/to/Goxel-MCP/dist/index.js"],
      "env": { "GOXEL_MCP_WORKSPACE": "/path/to/workspace" },
      "timeout": 120000
    }
  }
}
```

**Codex CLI**:

```bash
codex mcp add goxel --env GOXEL_MCP_WORKSPACE=/path/to/workspace -- node /path/to/Goxel-MCP/dist/index.js
```

or in `~/.codex/config.toml`:

```toml
[mcp_servers.goxel]
command = "node"
args = ["/path/to/Goxel-MCP/dist/index.js"]
env = { GOXEL_MCP_WORKSPACE = "/path/to/workspace" }
tool_timeout_sec = 180
```

**Any other client**: start `node /path/to/Goxel-MCP/dist/index.js` with the
stdio transport.

### 6. Check the connection

Start a new session of your client.  Approve the `goxel` server if the client
asks, then ask:

> Call goxel_info.

The answer lists the server version, the Goxel build found, the current
`mode` (`file` or `live`) and the supported formats.

---

## Using it

Just describe what you want.  For example:

> Create a red voxel race car with four black wheels, dark windows and a big
> rear wing.  Put each part on its own layer.

> Show me the car from the front and from the side.

> Make the rims black.

> Save the project as output/car/car.gox and export it for Blender.

### Live editing in Goxel

> Open output/car/car.gox in Goxel, live.

`goxel_open_in_app` starts the Goxel build with the bridge and connects to
it.  If `Goxel MCP.app` (or `goxel --mcp-socket <workspace>/.goxel-mcp/goxel.sock`)
is already running, the server connects to it automatically when it starts,
or when asked with `goxel_live {action: "connect"}`.

From then on:

- every editing tool changes the scene open in the editor, and you see the
  result right away;
- each tool call is one undo step in Goxel, so Ctrl+Z (⌘Z) reverts it;
- what you change by hand in the editor is kept, because the server reads
  the scene before each operation;
- `goxel_save_scene` is done by Goxel itself;
- `goxel_render_preview {engine: "goxel"}` returns an image rendered by
  Goxel, with its lighting and shadows.

`goxel_live {action: "disconnect"}` goes back to file mode and keeps the
current scene in memory.

### Exporting for Blender

- **OBJ:** `goxel_export {path: "output/car.obj", scale: 0.1}` writes
  `car.obj` and `car.mtl`, one material per color, with hidden faces removed.
  Blender imports it with *File › Import › Wavefront (.obj)*.
- **glTF:** `goxel_export {path: "output/car.gltf"}` uses Goxel's own glTF
  exporter, and needs the Goxel build.

---

## Tools

| Tool | What it does |
|---|---|
| `goxel_info` | Versions, mode, supported formats and capabilities, scene summary |
| `goxel_new_scene` | Start an empty scene (the previous one is auto-saved if it had unsaved changes) |
| `goxel_open_scene` | Open a `.gox`, `.vox` or `.txt` file (in live mode, in the editor) |
| `goxel_add_voxels` | Set up to 200,000 voxels in one call; best for geometry computed by a script |
| `goxel_draw_primitive` | Up to 500 shape operations per call: box, sphere / ellipsoid, cylinder (with inner radius), cone, line, points; modes add / remove / paint; clip planes; mirror copies; one layer per operation |
| `goxel_erase_region` | Erase by box / shape and / or by color |
| `goxel_recolor_region` | Recolor by box / shape and / or by source color, with a tolerance |
| `goxel_transform` | Move the voxels of a layer or region |
| `goxel_layers` | `list`, `create`, `delete`, `rename`, `set_visible`, `set_active`, `duplicate`, `clear`, `merge_into` |
| `goxel_get_voxels` | Scene summary (bounding boxes, colors per layer) and voxel lists |
| `goxel_render_preview` | PNG preview: `iso`, `iso_back`, `front`, `back`, `left`, `right`, `top`; built-in renderer or Goxel's |
| `goxel_save_scene` | Save as `.gox`, with backup and validation |
| `goxel_export` | Export to `gox`, `vox`, `obj`, `txt`, `png`, and with Goxel to `gltf`, `glb`, `ply`, `qubicle`, `kv6`, `kvx`, `vxl`, `povray` |
| `goxel_validate_scene` | Check a file and compare it, voxel by voxel, with what Goxel's own parser reads |
| `goxel_open_in_app` | Start Goxel with the bridge and switch to live mode (or open the file in the official app) |
| `goxel_live` | `status`, `connect`, `disconnect` |
| `goxel_undo` | Undo / redo (Goxel's own history in live mode) |

All the arguments are described in [docs/TOOLS.md](docs/TOOLS.md).  Tips for
building detailed models are in [docs/MODELING.md](docs/MODELING.md).

---

## Conventions

- Integer coordinates; voxel `(x, y, z)` fills the cube `[x, x+1] × [y, y+1] × [z, z+1]`.
- **Z is up**, like in Goxel.  For vehicles and buildings it helps to use +X
  as the front, Y as the width, and z = 0 as the ground.
- Colors: `"#rrggbb"`, `"#rrggbbaa"`, `[r, g, b]`, `[r, g, b, a]`, or names
  such as `red`, `black`, `silver`, `glass`.
- Symmetric models: draw one half (y ≥ 0) and add
  `mirror: [{axis: "y", center: -0.5}]` (y → −1 − y).
- Clip planes `{point, normal}` remove the voxels on the side the normal
  points to, for slopes, windshields and bevels.

---

## Examples

The [`examples/`](examples) folder has three scripts that build complete
models through the MCP tools, with the official MCP client: the same calls an
MCP client makes.

```bash
node examples/red-sports-car.mjs   # GT sports car built with primitives
node examples/stilt-house.mjs      # stilt house on the water, voxel by voxel
node examples/supercar.mjs         # supercar sculpted from height profiles
```

They write to `output/` inside the current directory, or inside
`GOXEL_MCP_WORKSPACE` when it is set.  If Goxel is running with the bridge,
the model is drawn live in the editor.

| | |
|---|---|
| ![Red sports car](docs/images/red-sports-car.png) | ![Stilt house](docs/images/stilt-house.png) |

---

## Safety

- Every path is resolved inside the workspace.  `..` and symbolic links that
  point outside of it are refused.
- Writing into the server's own folders (`src/`, `dist/`, `goxel-src/`,
  `goxel-build/`, …) and into `.git/` or the clients' configuration folders is
  refused.
- Existing files are never replaced without `overwrite: true`, except the
  scene's own file when you save it.  The previous version is copied to
  `.goxel-mcp/backups/` first.  Writes are atomic (temporary file + rename).
- `goxel_new_scene` and `goxel_open_scene` auto-save an unsaved scene before
  replacing it.  Destructive tools can be undone with `goxel_undo`.
- Logs go to stderr (`GOXEL_MCP_LOG_LEVEL=debug|info|warn|error`).  Stdout is
  only used by the MCP protocol.
- The bridge socket is only accessible by the current user (mode `0600`).
  Goxel opens it only when it is started with `--mcp-socket` or
  `GOXEL_MCP_SOCKET`.

---

## Troubleshooting

| Problem | Solution |
|---|---|
| The `goxel` tools do not show up | Start a new session of the client after registering the server, and approve it if asked. Check that `node /path/to/Goxel-MCP/dist/index.js` exists (run `npm run build`). |
| `goxel_open_in_app` times out the first time | macOS may be showing a security prompt for the new Goxel build. Accept it and try again. |
| A live call times out | A dialog is probably open in Goxel, or another client is connected to the bridge (only one client is served at a time). Close the dialog, or disconnect the other client. |
| A script cannot connect to the bridge while your client is connected | Same reason: call `goxel_live {action: "disconnect"}` first, and `connect` again afterwards. |
| The official Goxel app does not open the file | The official macOS build ignores files sent by Finder or `open`. Use *File › Open*, or the live mode. |
| glTF / PLY export is not available | Build Goxel with `npm run build:goxel`, or set `GOXEL_MCP_GOXEL_BIN` to a Goxel build with the bridge. |
| Where are the logs? | The server's stderr (your client's MCP log), and `<workspace>/.goxel-mcp/goxel-live.log` for Goxel. |

### Environment variables

| Variable | Default | Meaning |
|---|---|---|
| `GOXEL_MCP_WORKSPACE` | current directory | Folder for every file operation |
| `GOXEL_MCP_GOXEL_BIN` | `goxel-build/goxel` | Goxel build with the bridge |
| `GOXEL_APP` | `/Applications/goxel.app`, … | Official Goxel app (used with `live: false`) |
| `GOXEL_MCP_LOG_LEVEL` | `info` | `debug`, `info`, `warn` or `error` |
| `GOXEL_SRC`, `GOXEL_REPO`, `GOXEL_REF` | | Used by `scripts/build-goxel.sh` |

---

## Development

```bash
npm run build                 # compile TypeScript into dist/
npm test                      # unit tests + end-to-end MCP test over stdio
GOXEL_LIVE_TEST=1 npm test    # + live test (opens a Goxel window)
```

The tests that use the Goxel build (validation, glTF export, live editing)
open a Goxel window for a moment.  Set `GOXEL_MCP_SKIP_GOXEL=1` to skip them.

| Path | Content |
|---|---|
| `src/index.ts` | MCP server and tool definitions |
| `src/session.ts` | File / live modes (pull, edit, push the changes) |
| `src/bridge.ts` | Client of the Goxel command bridge |
| `src/operations.ts`, `src/shapes.ts` | Editing operations and shape rasterization |
| `src/formats/` | `.gox`, `.vox`, `.obj` and PNG readers / writers |
| `src/render.ts` | Software preview renderer |
| `src/workspace.ts` | Path sandbox, overwrite protection and backups |
| `bridge/` | The command bridge for Goxel (`mcp_bridge.c`, `mcp_bridge.h`) and the patch that adds it to Goxel |
| `scripts/` | Goxel build and macOS app packaging |

---

## Limitations

- When the server writes a `.gox` file itself (file mode), it keeps the
  layers, names, visibility and voxels.  Per-layer materials, cameras and
  lights are not kept.  In live mode Goxel saves the file itself, so
  everything is kept.
- Clone layers, shape layers and image layers are read as empty layers, with
  a warning.
- `.vox`: node rotations are ignored when reading.  Models larger than 256³
  are split automatically, and palettes are limited to 255 colors (quantized
  automatically).
- The bridge serves one client at a time and is not available on Windows.
- When Goxel exports through its command line (validation, glTF, …), a window
  opens for a moment.

---

## License

GPL-3.0-or-later, like Goxel.  See [LICENSE](LICENSE).

Goxel is developed by Guillaume Chereau: https://github.com/guillaumechereau/goxel