The bridge lets an agent (or any MCP client) drive the Map Editor of a running Smithbox: load maps, list and edit objects, place models, take screenshots.
What was checked for this page: the Python server and the C# bridge were read in full for names, parameters and defaults. No tool was called.
The two halves
| Part | Where | What it does |
|---|---|---|
| The bridge | inside Smithbox, src/Smithbox.Program/Utilities/McpBridge/ |
A socket on 127.0.0.1 only. One JSON request per line: {"tool": name, "args": {...}}. One JSON line back: {"ok": true, "result": ...} or {"ok": false, "error": ...}. |
| The MCP server | S:/Smithbox/Tools/mcp/smithbox_mcp.py |
A stdio MCP server named smithbox. It forwards each tool call to the socket. It adds screenshot and launch_smithbox itself. |
Requests are queued and run on Smithbox's UI thread, at most 16 per frame. So they change editor state the same way the GUI does. If the UI thread does not answer within 60 seconds the bridge returns "Timed out waiting for the editor's UI thread (is a modal dialog open?)".
Port and environment variables
| Variable | Read by | Meaning |
|---|---|---|
SMITHBOX_MCP_PORT |
Smithbox and the MCP server | The port. Default 47700. In Smithbox, 0 turns the bridge off. |
SMITHBOX_EXE |
MCP server | The exe launch_smithbox starts. Default: src/Smithbox.Vulkan/bin/Release/net10.0/win-x64/Smithbox.Vulkan.exe under the repo the script is in. |
SMITHBOX_PID |
MCP server | screenshot only captures the window of this process id. Use it when two Smithbox windows are open. |
SMITHBOX_SESSION_DIR |
Smithbox | Where "reopen where you left off" sessions are kept. Default %LOCALAPPDATA%\Smithbox\Sessions. Set it for a test copy so it leaves the real sessions alone. |
Smithbox logs "MCP bridge listening on 127.0.0.1:<port>" at start. If the port is taken it logs a warning and runs without the bridge.
How it is registered
The server needs Python with mcp and pillow (pip install mcp pillow). It is registered for Claude Code at user
scope under the name smithbox:
claude mcp add --scope user smithbox -- python S:\Smithbox\tools\mcp\smithbox_mcp.py
claude mcp list shows it. The team release also ships the script in the Smithbox folder under Tools/mcp/.
Safety rules
A running Smithbox is someone's live workspace. Stan edits maps between an agent's turns.
- Call
smithbox_statusfirst. Look atloaded_maps[].unsavedandcan_undo. If anything could be someone's work, ask before you unload, close or rebuild. Ask them to save, or save only with their OK. - Never pass
discard_changes=truetounload_mapon a map someone has touched. Never close Smithbox to rebuild it while it holds unsaved edits. This rule exists because edits were lost that way once. - Every edit is undoable. Edits go through the editor's undo stack, so
undoand Ctrl+Z in the editor both take them back. - Nothing reaches disk until
save_map. A save writes into the Smithbox project folder, never the game install. - These write to disk at once, with no undo:
create_map_copy(new files in the project folder),place_modelwithgame="er"andport_er_map(imported model files in the project folder),set_community_name(the project's alias file),nav_generatewithsave=True, andpreview_statuswithclear_disk. - Undo does not clear Smithbox's dirty flag. A map that was edited and fully undone still reports
unsaved. - Do not launch the game to check a map. Hand over to the person and say what to look for.
Running a test copy on another port
To try a build without touching the Smithbox someone is using:
- Start the second Smithbox with
SMITHBOX_MCP_PORTset to another port (the team has used 47711 and 47715) andSMITHBOX_SESSION_DIRset to a scratch folder. - The registered
smithboxMCP server reads its port once at start, so it keeps talking to 47700. Reach the test copy with a raw socket instead: connect to127.0.0.1:<port>, send one JSON line{"tool": ..., "args": {...}}, read one line back. The bridge's tool names are the same as the MCP ones, exceptstatus(MCP:smithbox_status). - With two Smithbox windows, set
SMITHBOX_PIDforscreenshot, or it captures the largest one. - From the team's notes, not re-checked here: a test copy opens the same project, so it writes preview caches and
map indexes into that project's
.smithboxfolder; build a test copy into its own output folder so the shared build is not locked. See build-smithbox.md.
Addressing
- Objects are addressed by
map_idplus MSB name (for exampleAEG099_090_9000).kind(Part,Region,Event) narrows a name that exists in more than one list. - A map must be loaded (
load_map) before its objects can be listed or edited. - Positions are map-local, as the MSB stores them. m60 tiles sit at a world offset;
smithbox_statusreports it asloaded_maps[].world_offset. Camera positions are world space. - Rotations are in degrees for ER and NR. Y is up.
- Property paths look like
ModelName,EntityGroupIDs[0],Unk1.DisplayGroups[2]. - A map loaded only as read-only context for the tiles around it refuses edits. Load it with
load_mapto edit it.
Tools
Every tool in smithbox_mcp.py, grouped by purpose. Defaults are in brackets.
Session
| Tool | What it does | Key parameters |
|---|---|---|
smithbox_status |
The open project (name, game, paths), loaded maps with unsaved state, maps still loading, the selection, the camera. | none |
launch_smithbox |
Starts the build at SMITHBOX_EXE if the bridge does not answer, and waits up to 60 s for it. |
none |
show_map_editor |
Brings the Map Editor tab to the front. | none |
screenshot |
Captures the Smithbox window. Works behind other windows, not when minimised. | max_width [1600], crop [left, top, right, bottom] |
Maps
| Tool | What it does | Key parameters |
|---|---|---|
list_maps |
Map ids in the project with their alias names. | filter, limit [300] |
load_map |
Loads a map and waits until it is loaded. | map_id, wait [true], timeout_s [180] |
unload_map |
Unloads a map. Refuses when it has unsaved changes. | map_id, discard_changes [false]: see the safety rules |
save_map |
Writes the map's changed files into the project folder. | map_id |
create_map_copy |
Makes a new map in the project as a copy of another map's MSB. Written at once. | source, new_id, with_navigation [false] |
Look at objects
| Tool | What it does | Key parameters |
|---|---|---|
list_objects |
Objects of a loaded map with model, alias, position, rotation, entity id, and counts per class. | map_id, kind, cls, name_contains, model_contains, near + radius, limit [200] |
get_object |
Every MSB field of one object, and what references it. | map_id, name, kind, depth [3] |
models_in_map |
Distinct part models of a loaded map by class, with counts and an example. | map_id |
get_selection |
What is selected in the viewport. | full [false] |
select |
Selects objects and frames the camera on them. | map_id, names, frame [true], kind |
Edit objects
| Tool | What it does | Key parameters |
|---|---|---|
set_property |
Sets fields on one or more objects as one undo step. | map_id, changes {path: value}, name or names, kind |
transform |
Moves, rotates or scales objects as one undo step. | map_id, name or names, position, offset, rotation, rotate_by, scale |
duplicate |
Copies an object; can swap the copies' model. | map_id, name, count [1], offset, positions, model, entity_id [clear / increment / keep], clear_entity_groups [true], rename [true] |
place_model |
Places a new part with a model, cloned from a fitting template part. Same code as the Object Palette. | map_id, model, position, snap_to_ground [false], rotation [template / zero / random], clear_ids [true], cls, game ["" or "er"], coordinates [local / world] |
delete |
Deletes objects as one undo step. | map_id, names, kind |
undo / redo |
Undo or redo editor actions in the active map view. | steps [1] |
modal_transform |
Runs the Blender-style G / R / S modal on the selection with synthetic input. | mode [grab / rotate / trackball / scale], axis, plane, numeric, from_px, to_px, finish [confirm / cancel / leave], local, push |
rotate_gizmo |
Reports or drags the Blender-style rotate gizmo on the selection. | point, drag [x / y / z / view / trackball], from_px, to_px, finish, rotate_mode [true] |
Find models
| Tool | What it does | Key parameters |
|---|---|---|
search_models |
Searches model ids, names and tags. | query, kind [asset / chr / mappiece / part], limit [50], only_in_game [true] |
model_maps |
Which maps use an asset, or which assets the maps matching a search use. | game [project / er], id, search, limit [20] |
list_files |
The project's file lists, with the layer each file comes from (project, mod[i], game). | kind [chr / asset / map / parts / mappiece / all], filter, limit [100], count_layers |
palette_search |
Opens the Object Palette on a tab with a search typed in. | search, tab [used_in_map / assets / characters / map_pieces / recent], game [nr / er], place, category, peek |
preview_status |
How far the Object Palette's previews have got. | clear_memory [false], clear_disk [false] |
set_community_name |
Gives a model a community name, saved at once to the project's alias file. | id, name, kind, game ["" or "er"] |
See place-an-object.md for the Object Palette.
Elden Ring assets and maps
| Tool | What it does | Key parameters |
|---|---|---|
er_assets |
The Elden Ring install a Nightreign project borrows from: matching assets, and what is already copied into the project. | search, limit [50], inspect (an ER asset id) |
port_er_map |
Bulk-ports an Elden Ring map's Asset parts into loaded Nightreign maps. | See port-er-map.md |
port_status |
Progress of the running or last port_er_map job. |
none |
See also place-er-asset.md.
Navigation
| Tool | What it does | Key parameters |
|---|---|---|
nav_info |
The navmesh models of a loaded map. | map_id |
nav_validate |
Runs the navigation validator on a loaded map, with the game's own counts to compare. | map_id, limit [30] |
nav_generate |
Generates a model's n and o navmeshes from the map's collision, as one undo. | See generate-navmesh.md |
Camera and view
| Tool | What it does | Key parameters |
|---|---|---|
look_at |
Points the camera at a position, framing a box of a radius. | position, radius [10], map_id (then the position is map-local) |
camera |
Sets the camera's world position, pitch and yaw in degrees. | position, pitch (90 = straight down), yaw (0 = +Z) |
render_filter |
Shows or hides kinds of things in the viewport. Returns what is shown. | hide, show (for example Collision, Navmesh, Region) |
See rendering.md and move-rotate-snap.md.
Params
| Tool | What it does | Key parameters |
|---|---|---|
param_info |
Whether params are loaded; a param's type and row count; row ids and names in a range. | name, id_min, id_max, limit [60] |
Bridge commands with no MCP tool
The C# bridge also answers these names, but smithbox_mcp.py does not expose them. They can only be reached with a
raw socket request. Their arguments were read from the code but are not documented further here:
set_visible (the Map Contents eye for objects; not a map edit), variations, focus_variation,
copy_variation, open_map, material_info, material_audit, model_materials, viewport_setting (reads or
sets a Viewport_* setting by name), extract_file (copies a file the project can see to a folder on disk),
snap_to_surface, surface_ray, snap_debug, snap_model_debug.
The tool list in Tools/mcp/README.md is older than the script and misses many tools. Trust the script.