Sauerbraten MCP
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Sauerbraten MCP fill a 4x4x2 block at the current selection with the selected texture"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Sauerbraten MCP
A local MCP server for editing levels in a running Sauerbraten (Cube 2) game. It exposes 22 typed tools over stdio and connects to an opt-in native bridge through a private Unix socket. Linux and single-player editing are supported. This is an unofficial extension; it is not part of an official Sauerbraten release.
The Python server lives here. Native engine changes are prepared separately for official Sauerbraten SVN r6891 and included in engine-patches. The repository contains no game assets, personal maps, profiles or credentials. An unmodified game does not provide the required socket protocol.
Build the engine
Use a complete Sauerbraten checkout, including current data/ and packages/:
svn checkout -r 6891 https://svn.code.sf.net/p/sauerbraten/code/ sauerbraten
cd sauerbraten
# Use an absolute path to this repository:
while IFS= read -r name; do
patch -p1 < "/absolute/sauerbraten-mcp/engine-patches/$name"
done < /absolute/sauerbraten-mcp/engine-patches/series
make -C src -j2 MCP=1 clientBuild dependencies: C/C++ compiler, make, SDL2, SDL2_image, SDL2_mixer, zlib,
OpenGL and OpenSSL development headers/libcrypto. The engine keeps its bundled
ENet. The default build and standalone server/master do not include MCP or its
JSON/crypto dependencies. Enabled client objects use a separate src/.mcp/
directory, so switching build modes does not require cleaning.
Related MCP server: dsh-godot-mcp
Install and connect
Install Python >=3.11 and uv, then:
git clone https://github.com/pastukhov/sauerbraten-mcp.git
cd sauerbraten-mcp
uv sync --lockedMap validation also needs Xvfb and xauth. Validators use a separate SDL/GL
client; use the same patched executable and asset installation as the live game.
Start the patched game in a terminal, from its full resource checkout:
cd /absolute/sauerbraten
umask 077
sauer_runtime=$(mktemp -d "${XDG_RUNTIME_DIR:-/tmp}/sauer-mcp.XXXXXX")
printf 'Socket: %s/editor.sock\n' "$sauer_runtime"
tools/run-editor.sh \
--client "$PWD/src/sauer_client" \
--data-root "$PWD" \
--profile "$HOME/.local/share/sauerbraten-mcp" \
--socket "$sauer_runtime/editor.sock"The launcher writes game output to the dedicated profile's client.log.
Create/open a map in the UI and enter edit mode. Configure your MCP host with
these stdio command/arguments, replacing paths with absolute paths on your machine:
{
"command": "uv",
"args": [
"run", "--locked", "--project", "/absolute/sauerbraten-mcp", "sauer-mcp",
"--client", "/absolute/sauerbraten/src/sauer_client",
"--socket", "/absolute/private-directory/editor.sock"
]
}Use the socket path printed by the launcher. The server attaches to an existing
game and does not start or stop it. It can reconnect after restarting. Only one
peer is allowed; another receives BUSY. The socket has mode 0600, its parent
must have mode 0700 and the peer must have the same UID. The bridge is disabled
unless the game is started with -mcpbridge<absolute-path>.
Always pass --client explicitly for a source build. Otherwise the server checks
SAUER_MCP_CLIENT, then sauer_client on PATH, then the Debian installation path
/usr/lib/cube2/cube2_client. An unpatched or different executable can make
validation fail even when the live connection works. MCP stdout carries only
the protocol.
Tools and editing rules
Purpose | Tools |
Observe | editor_state, region_read, entity_list, texture_catalog, mapmodel_catalog |
Geometry | geometry_fill, geometry_face_set, geometry_vertex_set, texture_apply, material_apply |
Entities | entity_create, entity_update, entity_delete |
History | history_undo, history_redo |
View | camera_set, view_capture |
Maps and recovery | map_catalog, map_new, map_open, map_save_as, operation_status |
Read editor_state first. Each mutation requires a fresh UUID request_id,
current map_epoch and expected_revision. Revisions and generations are
canonical decimal strings. Use catalog resource references rather than guessing
IDs. Schemas are advertised through tools/list and recorded in
contracts/v1.json.
If a reply is lost, retain the UUID and exact arguments and query
operation_status. An unknown outcome is not permission to repeat an edit. UUID
reuse with different arguments is rejected. Native manual edits and MCP edits
share undo/redo, with one history step per RPC. Pending native drag/onrelease
commands return BUSY.
Geometry supports aligned solid/empty fills, face deformation and vertex
movement. geometry_face_set sets four absolute depths 0..8 along the selected
face axis in grid/8 units; even negative faces use absolute coordinates. The
order is (R,C)=00,10,01,11: X faces use Y/Z, Y faces use Z/X, Z faces use X/Y.
geometry_vertex_set selects a logical corner with XYZ bits and sets its local
XYZ position 0..8. Both affect every selected cell and require existing nonempty
leaf cubes exactly matching the grid; fill explicitly first when establishing
new geometry. They preserve textures/materials. Neighbors and coincident corners
are not moved implicitly. Inverted, nonconvex and zero-volume shapes are rejected
before any change. Each successful change is one shared undo step; no-ops do not
consume history or clear redo. Textures can target one exact face or all faces. Entities support light, playerstart and mapmodel; attached entities/history
are rejected. Reads preserve the native selection and focus. camera_set selects
first-person and disables zoom; view_capture requires view_revision and
returns PNG MCP ImageContent with state tokens.
Persistence and limits
map_new and map_open refuse to replace an unsaved world. Opening pins and
validates the source in a separate process before committing in the game.
Native loading is destructive: a failure after reset reports the new state and
recovery information; ordinary edits are blocked while recovery_required is set.
map_save_as creates an immutable bundle in the dedicated profile:
packages/mcp/<slug>-<request_uuid>/{level.ogz,level.cfg,manifest.json}. It does not
overwrite source maps. Exports compare semantic/config fingerprints after reload
and depend on this installation's trusted assets/cfg. They are not standalone
media packages. Changed or shadowed dependencies block reopening before reset.
A stale clean acknowledgement leaves the live map dirty. The persistent export
journal records outcomes across transport failures and server restarts; keep it
and published bundles for recovery.
Custom shader declarations/variants/alternate/fast/deferred definitions are not
supported. Manual custom shader changes disable export/open/new for that process
until a game restart; editor_state explains the reason. Standard lazy shaders
remain supported. Other unreconstructible or name-dependent cfg may disable
export. Native save/load is synchronous and can pause frames. Baking lighting,
coop, batch, terrain/blend editing are outside this version.
Mutations allow 4096 cells, 65536 visited nodes, 8 MiB snapshot and 16 MiB inverse preparation. Reads return at most 256 leaves / 4096 nodes per page. Captures are at most 1280x720 with a 10-minute TTL and 64 MiB owned cache. There is no arbitrary CubeScript, shell or file-operation tool.
Tests
uv run --locked pytest -q
# Full integration: explicitly select the patched engine with complete assets.
SAUER_MCP_ENGINE=/absolute/sauerbraten uv run --locked pytest -q
# Native-only suite, from the engine checkout:
make -C /absolute/sauerbraten/src MCP=1 test-mcpThe first command runs isolated Python tests and explicitly skips live/native
cases. The second runs the complete suite, including initialized engine cases,
stdio/SDK calls, native history, camera/PNG, validated saves/reopen, persistence
and recovery. Integration needs Xvfb/xauth and actual assets. Tests use temporary
profiles/sockets and never attach to an existing game. To test an installed
binary, set both SAUER_MCP_CLIENT and SAUER_MCP_DATA instead; the source fixture
comparison is then skipped. Explicit invalid paths are errors rather than silent
skips. The native suite needs only Python 3 stdlib, not the MCP SDK.
The Python dependencies are locked in uv.lock; the MCP SDK is pinned to
mcp==1.26.0. Extension code uses the zlib license. The engine patch
series preserves Sauerbraten's notices and the MIT license of vendored cJSON.
Available Tools
22 toolscamera_setB
Set edit camera coordinates, yaw/pitch and FOV; returns a view_revision for capture.
Inputs: yaw (required), pitch (required), position (required), map_epoch (required), +3 more.
editor_stateARead-onlyIdempotent
Read current map/session/revision, camera and editor capabilities. Read before mutations.
Inputs: no named parameters.
entity_createA
Create light/playerstart/mapmodel at explicit coordinates with five attrs; no camera/drop fixups.
Inputs: type (required), attrs (required), position (required), map_epoch (required), +3 more.
entity_deleteCDestructive
Delete a referenced entity as one undoable step.
Inputs: map_epoch (required), entity_ref (required), request_id (required), expected_revision (required).
entity_listCRead-onlyIdempotent
List entities and generation-bound references, optionally in world bounds.
Inputs: type, limit, bounds, offset, +2 more.
entity_updateC
Replace position/attrs of a referenced entity. Attached entities are unsupported.
Inputs: attrs (required), position (required), map_epoch (required), entity_ref (required), +3 more.
geometry_face_setBDestructive
Set four absolute face-axis depths (0..8 in grid/8 units) on each exact nonempty leaf. Face=selection.orient; order (R,C)=00,10,01,11; X uses Y/Z, Y uses Z/X, Z uses X/Y. Atomic and undoable; invalid/zero-volume shapes rejected.
Inputs: depths (required), map_epoch (required), selection (required), request_id (required), +1 more.
geometry_fillCDestructive
Replace an aligned selection with solid or empty cubes. count is in cells, grid in world units.
Inputs: mode (required), map_epoch (required), selection (required), request_id (required), +2 more.
geometry_vertex_setBDestructive
Set one logical corner (XYZ bits) to absolute local XYZ coordinates 0..8 in grid/8 units on each exact nonempty leaf. Neighbors/coincident corners are not moved implicitly. Atomic and undoable; invalid shapes rejected.
Inputs: corner (required), position (required), map_epoch (required), selection (required), +2 more.
history_redoC
Redo the top shared manual/MCP history step; read state afterwards.
Inputs: map_epoch (required), request_id (required), expected_revision (required).
history_undoB
Undo the top shared manual/MCP history step; read state afterwards.
Inputs: map_epoch (required), request_id (required), expected_revision (required).
map_catalogBRead-onlyIdempotent
List trusted local maps. Use opaque map_id values with map_open.
Inputs: limit, offset, search.
mapmodel_catalogBRead-onlyIdempotent
List mapmodel registry references for entity_create/update.
Inputs: limit, offset, map_epoch, generation.
map_newBDestructive
Create a native map (bottom half solid). Scale 10..16; refuses unsaved changes.
Inputs: scale (required), map_epoch (required), request_id (required), expected_revision (required).
map_openBDestructive
Validate and open a catalog map or saved artifact; refuses an unsaved map.
Inputs: map_epoch (required), request_id (required), expected_revision (required), map_id, +1 more.
map_save_asC
Validate reload and publish an immutable map snapshot; returns revision_saved and conditional clean_ack.
Inputs: slug (required), map_epoch (required), request_id (required), expected_revision (required).
material_applyC
Apply a built-in volume/clip material to an aligned selection.
Inputs: material (required), map_epoch (required), selection (required), request_id (required), +1 more.
operation_statusARead-onlyIdempotent
Look up a mutation UUID; unknown means do not blindly repeat it.
Inputs: request_id (required).
region_readCRead-onlyIdempotent
Read octree leaves in half-open world bounds, without subdividing geometry.
Inputs: bounds (required), cursor.
texture_applyC
Set exactly one orientation (0..5) or all six faces of an aligned selection.
Inputs: faces (required), map_epoch (required), selection (required), request_id (required), +2 more.
texture_catalogBRead-onlyIdempotent
List texture references. Preserve map_epoch and vslot_generation when applying.
Inputs: limit, offset, map_epoch, generation.
view_captureCRead-onlyIdempotent
Capture the requested map/view revision as PNG image content.
Inputs: map_epoch (required), view_revision (required), expected_revision (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 22 tools
Each tool targets a distinct resource+action: entities (create/update/delete/list), geometry (fill/face_set/vertex_set), textures (catalog/apply), maps (new/open/save_as/catalog), and history (undo/redo). Minor possible confusion between material_apply vs texture_apply and between the three *_catalog tools, but descriptions clearly scope them to different domains. The three geometry mutation tools are clearly separated by the type of shape manipulation.
Dominant pattern is noun_verb (entity_create, geometry_fill, texture_apply, map_open), applied consistently across domains. A few outliers like editor_state, operation_status, and map_save_as are noun_noun or verb-phrase forms rather than the strict verb at end, but still readable and predictable.
22 tools is above the ideal 3-15 band but justified by a rich 3D map editor spanning entities, geometry, textures, maps, camera, and history. Each domain contributes a small, purposeful cluster rather than redundant tools, so the count feels earned though slightly heavy.
Covers a strong lifecycle: map new/open/save/catalog, entity CRUD+list, geometry mutation, texture catalog/apply, material apply, camera, history undo/redo, operation_status, and region_read for readback. Minor gaps exist (no material catalog listing, no explicit entity get-by-ref beyond entity_list), but agents can work around these.
Maintenance
Related MCP Connectors
Build and inspect browser-based 3D games: projects, scenes, Lua scripts, assets and publishing.
Build 3D web apps: physics, audio, music, Blender import. Live edits, undo, versions, publishing.
Drive a live Cinevva game session: edit game files, import CC0 assets, preview changes.
Create, inspect, validate, and save editable 3D building scenes with Pascal's MCP server.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI coding tools to connect and authorize local game projects, then drive source edits, validation, preview, version saving, and mode-based creation workflows.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables full control of the Godot engine from any MCP client through three layers: headless project, scene, script, settings and export operations, live editor edits that remain undoable, and inspection of a running game with input injection, frame-stepped digests and screenshots. Each layer also exposes an arbitrary-GDScript escape hatch so unanticipated tasks can still be performed.MIT
- AlicenseNot gradedqualityCmaintenanceEnables any MCP client to drive the Fortnite map editor through typed, safety-validated tools — placing and tagging props, renaming assets with Verse-reference pre-flight, authoring prefabs, exporting textures for visual identification, and searching the project's Verse API digests. An in-editor listener executes the operations on the editor's main thread over HTTP.MIT
- AlicenseAqualityCmaintenanceConnects an MCP client to a running Serious Editor 2017 / Serious Sam Fusion instance through a local Lua bridge, enabling package and entry inspection, log reading, editor diagnostics, and previewed/undoable entity property edits on a captured selection.14MIT