MCP Server
The Eustress MCP server, eustress-mcp, connects an AI client such as Claude Code, Claude Desktop or Cursor to a Universe through the Model Context Protocol. It reads and writes the Universe's files directly, and reaches a running Eustress Engine through its local bridge for anything live.
01Overview
What It Is
An MCP server is a program an AI client starts and talks to over a standard
protocol, so the model can call its tools and read its resources. eustress-mcp is Eustress's server: one native binary that the
client launches as a child process and speaks to over standard input and
output, one JSON-RPC 2.0 message per line. It answers MCP protocol versions 2025-06-18, 2025-03-26 and 2024-11-05, echoing whichever the client asks for.
It exposes about 120 tools and six kinds of resources. Most tools are the
same handlers the in-engine Workshop assistant uses, from the shared eustress-tools crate; the others, most of them live tools,
exist only in the server. Each request runs on its own task, so a long call such as await_simulation never blocks the others, and a client can
cancel it. In Studio, Help > Setup MCP opens this page.
Disk and Live
Every tool reaches a Space in one of two ways. Disk tools read and write the
Universe folder, and a running engine picks the change up through its file
watcher, so they work with Studio closed. Live tools connect to the engine's
bridge, a JSON-RPC endpoint on 127.0.0.1, and act on the
world the engine has loaded right now.
| Disk tools | Live tools | |
|---|---|---|
| Needs an engine running | No | Yes: Studio or eustress-headless |
| Sees | Files on disk | The loaded world, including parts that live only in the world database |
| A change shows | When the engine's watcher reloads the file | On the engine's next frame |
| Examples | read_file, create_script, git_status | inspect_scene, scene_raycast, sim_step |
The entity tools do both: they try the bridge first and fall back to the files when no engine answers. The simulation tools are a third case, covered in Simulation and Physics.
02Setup
Get the Binary
Build the server from a source checkout, in the repository's eustress folder:
cargo build --release -p eustress-mcp-server
# Windows: target/release/eustress-mcp.exe
# macOS, Linux: target/release/eustress-mcpThe Windows installer copies eustress-mcp.exe into the
install folder only when the build that precedes it has produced the file. The
release pipeline builds only the eustress-engine package, so
installers it produces do not include the server: build it as above and point
your client at the file.
The binary is named eustress-mcp, but its package is
eustress-mcp-server. The workspace also holds a package called
eustress-mcp: an older library that builds no program, so cargo build -p eustress-mcp finishes without producing
a server.
Register It with Your AI Client
The server needs no arguments. Name the Universe it should work in with an
environment variable and register it under any name. For Claude Code, add it
to .mcp.json in your project:
{
"mcpServers": {
"eustress": {
"type": "stdio",
"command": "C:\\Eustress\\eustress\\target\\release\\eustress-mcp.exe",
"args": [],
"env": {
"EUSTRESS_UNIVERSE": "C:\\Users\\you\\Documents\\Eustress\\Universe1"
}
}
}
}Other MCP clients take an equivalent entry, with the same command, arguments
and environment, in their own configuration file. Claude Code shows the tools
with its server prefix, for example mcp__eustress__inspect_scene. Everything the server reads at startup:
| Setting | Effect |
|---|---|
--universe <path> | The Universe to open at startup. Checked before the variable below. |
EUSTRESS_UNIVERSE | The same, as an environment variable: an absolute path to a folder that holds Spaces. |
EUSTRESS_UNIVERSES_PATH | Folders to search for Universes and for a running engine, separated by ; on Windows and : elsewhere. Default: ~/Eustress, ~/Documents/Eustress and your home folder. |
RUST_LOG | Level of the server's own log, which goes to stderr. Default: info. |
EUSTRESS_MODERATOR_TOKEN | For Gallery moderators only: grants the Network capability (see Access). |
Which Universe and Space
At startup the server picks its Universe from the first of these that gives one:
- The
--universeargument. - The
EUSTRESS_UNIVERSEvariable. - The nearest folder above the working directory that contains a
Spacesfolder.
If none does, the first resource request adopts the first Universe found in
the search folders. Resources always come from this Universe, and the set_active_universe tool switches it mid-session.
When Studio has run on this computer, every tool call works in the
Space Studio last had open, read from last_space_path in ~/.eustress_engine/settings.json, and in that
Space's Universe, even when EUSTRESS_UNIVERSE names
another. Only without that setting does a tool fall back to the
configured Universe and its first Space. Open the Space you want in
Studio before you point an agent at it.
A live tool connects to the port in .eustress/engine.port of
the Universe it works in, and falls back to the copy of that file in the folder
above. Without Studio's setting, the server first searches the search folders
for a Universe whose port file names a port that accepts a connection within
250 ms, and remembers the answer for 3 seconds, so closing one engine and
opening another needs no restart.
Check the Connection
On start the server logs its version, the Universe it resolved, its search
folders and its tool count to stderr, which your client may show in its MCP
log. Then ask the assistant to call list_spaces: it names
the Spaces of the Universe it is working in. With no Universe resolved, resources/list offers a single resource, eustress://help/setup, that explains how to set one.
To test the live path, open a Space in Studio and call inspect_scene. It returns the loaded entities and the current
frame rate. With no engine running, a live tool answers that the engine is not
running and suggests opening Studio or starting eustress-headless.
03Access & Safety
What a Client May Call
Every tool belongs to one capability class, set in a single table in the eustress-tools crate, and the dispatcher checks the class
before the tool runs. An MCP client gets the standard grant, Read and Write.
Tools in the other classes still appear in tools/list, and a
call returns a permission error that names the missing capability.
| Class | Over MCP | Tools |
|---|---|---|
| Read | Allowed | Queries, such as read_file, inspect_scene, git_log |
| Write | Allowed | Changes inside the Universe, such as create_entity, write_file, run_simulation |
| Destructive | Refused | delete_entity, git_commit, git_branch, website_remove_reference |
| Execute | Refused | run_bash, execute_luau, execute_rune |
| Network | Only with EUSTRESS_MODERATOR_TOKEN | http_request, image_to_code, image_to_geometry, document_to_code, the four moderation_ tools |
A tool missing from the capability table is refused even with full
permissions. Twelve of the server's own live tools are missing from it: equip_tool, select_entity, invoke_action, capture_viewport, the four ai_camera_ tools, export_instances_toml, data_bind, data_bindings and data_unbind. They are listed, and every call returns a
permission error. The bridge methods behind them work from the eustress CLI.
The Universe Sandbox
- File tools:
read_file,list_directoryandwrite_filetake paths relative to the Universe root. A path containing..is refused, and so is any path that resolves outside the Universe. - Entity files:
write_filewill not overwrite an_instance.toml; entities change throughcreate_entityandupdate_entity. - Size caps:
read_filereturns the first 50,000 bytes of a file, resource reads stop at 256 KB, and a tool's structured result is cut at 120 KB with an explicit truncation note. - The protocol stream carries only protocol messages. The server's own log goes to stderr.
Review and Audit
Each tool in tools/list carries MCP annotations taken from its
own code: readOnlyHint on tools that only observe, destructiveHint on tools that ask for approval in the Workshop,
and openWorldHint on http_request. Clients
use them to decide which calls to confirm with you.
stage_file_change writes nothing. It returns the proposed
create, modify or delete, with the file's current text for a modify, so the
client can show it to you before anything is written.
Two logs show what happened in a Space. query_audit_log reads
the engine's record of its own Claude calls, one .log.toml file per call under SoulService/Logs. oplog_tail reads the engine's op-log of entity creates and
deletes, in order.
The engine bridge listens only on 127.0.0.1 but checks
no identity: any program on your computer that reads engine.port can connect and call any bridge method,
including entity.delete and engine.shutdown. Only its tools.call method is limited, to the same
Read and Write set as the MCP server.
04World Tools
Universes and Spaces
| Tool | What it does |
|---|---|
set_active_universe | Switch the Universe this session resolves against. Handled by the server itself. |
list_universes | List the Universe folders beside the current one. |
list_spaces | List the Spaces of a Universe, the current one by default. |
new_universe | Create a Universe folder beside the current one, with Spaces and .eustress folders. Fails if it exists. |
new_space | Create a Space from the standard service templates. The engine builds its database on first open. |
rename_space | Rename a Space folder. Fails cleanly while a running engine holds it. |
rename_universe | Rename a Universe folder and update the next-launch marker if it pointed there. |
set_next_launch_universe | Write the marker the engine reads at startup to choose its Universe. |
list_space_contents | A Space's services and top-level entities, or the children of one folder or Model. |
generate_docs | Write a README.md describing the Space: services, entities, scripts and materials. |
get_conversation | Read a saved Workshop conversation by session id. |
Files and Scripts
| Tool | What it does |
|---|---|
read_file | Read a text file by its path from the Universe root. |
list_directory | Every file and subfolder of a folder as it is on disk, with sizes. |
write_file | Write a file under the Universe, creating folders as needed. |
stage_file_change | Return a proposed create, modify or delete for review. Writes nothing. |
search_universe | Search every Space's .toml, .rune, .lua and .md files; returns paths and line numbers. |
list_assets | Meshes, textures and materials in the Space's MaterialService and Workspace. |
list_scripts | Every script source (.rune, .lua, .luau, .soul) in the Space's services, as Space-relative paths. Workspace is left out. |
read_script | A script's source, by name or by the path list_scripts returns. |
create_script | Create a script folder, Rune by default or Luau. A Luau kind of server, client or module picks where it runs and its default service; parent places it in any Space folder. The engine's watcher loads it. |
execute_rune | Write a Rune script into SoulService for the engine to run. Refused over MCP (Execute). |
execute_luau | Write a Luau script into SoulService for the engine to run. Refused over MCP (Execute). |
run_bash | Run a shell command in the Universe root. Refused over MCP (Execute). |
Scripting itself is covered in Scripting.
Entities
Tools marked live first call the engine's bridge, where new parts go into the
world database, and fall back to writing _instance.toml folders
when no engine answers. An engine that is up but rejects the call reports the
error instead of a silent disk write.
| Tool | What it does |
|---|---|
create_entity | Create a part or other instance with its size, color and material. Live first. |
update_entity | Change an entity's properties. Live first. |
delete_entity | Remove an entity. Refused over MCP (Destructive). |
query_entities | List entities, optionally of one class. Live first. |
find_entity | Find entities whose name contains a text. Live first. |
add_tag | Add a CollectionService tag to an entity. Live first. |
remove_tag | Remove a CollectionService tag. Live first. |
get_tagged_entities | Entities that carry a tag, read from the Workspace files. |
insert_gaussian_splats | Import a .ply Gaussian-splat cloud as a GaussianSplats instance. |
particle_simulation | Describe, create, read or change a ParticleSimulation and its species. |
promote_entity | Write a database-only part out as an _instance.toml folder, keeping its uuid. Needs a running engine. |
demote_entity | Fold a bare part folder back into the database and delete the folder. Needs a running engine. |
Git, Memory and Logs
| Tool | What it does |
|---|---|
git_status | Modified, staged and untracked files in the Universe's git repository. |
git_log | Recent commits with hash, author, date and message. |
git_diff | Uncommitted changes as a unified diff, optionally for one path. |
feedback_diff | A structured diff between two git refs or two file paths. |
git_commit | Stage everything and commit. Refused over MCP (Destructive). |
git_branch | List, create, switch, delete or merge branches. Refused over MCP (Destructive). |
list_rules | Workshop rules: .eustress/rules/*.md for the Universe and .rules/*.md in the Space. |
list_workflows | Workshop workflows, the .md files behind /run commands. |
query_audit_log | The engine's Claude call log, newest first, up to 50 entries. |
remember | Returns the memory as a request. Nothing is stored out of process. |
recall | Returns the query as a request. The server holds no memories to search. |
query_stream_events | Returns the query as a request. The event stream lives inside the engine. |
How a Universe's history is kept is covered in Universes.
05Live Tools
The Engine Bridge
Every running engine, the Studio window and eustress-headless alike, hosts the
Engine Bridge: a JSON-RPC 2.0 endpoint on 127.0.0.1 at a port
the operating system picks. The engine writes that port to .eustress/engine.port in the Universe it has open, and a copy to
the same path in the folder above, the workspace root. Requests are handled on
the engine's main thread, up to 64 per frame, so a handler sees exactly the
world on screen.
The server connects with a 2 second limit and gives most calls 2 seconds to
answer; sim_step waits longer in proportion to the ticks it
asked for. When several engines share one Universe, engine.port names only one of them; CLI & Headless covers the instance registry that lists them all.
| Needs | Tools |
|---|---|
| A running engine | Everything in Scene and Editor and AI Camera and Capture, plus promote_entity and demote_entity |
| An engine if one runs, else the files | create_entity, update_entity, query_entities, find_entity, add_tag, remove_tag |
| An engine with the Universe open, reached through files | The simulation tools in Simulation and Physics |
| Nothing but the files | Every other tool |
Scene and Editor
| Tool | What it does |
|---|---|
inspect_scene | Per-entity class, mesh, material, color, transform, visibility, physics flags, parent and source file, plus the frame rate. Filter by class, name, cell or region; 200 per page by default, 5,000 at most. |
scene_overview | Entities grouped into 256 m Morton cells, densest first, each with bounds, a count and a class histogram. |
partition_scene | Split the scene into balanced, spatially contiguous work units for parallel agents, 4 by default and 64 at most. |
scene_raycast | Cast a ray against the live Avian colliders; hits nearest first, up to 1,000 m and 8 hits by default. |
oplog_tail | Recent entity creates and deletes from the engine's op-log, 50 by default and 1,000 at most. |
sim_step | Advance physics by exact 1/60 s ticks, up to 10,000 per call and one call at a time. Pause the simulation first. |
get_editor_state | The active editor tool and the current selection. |
read_output | The newest Output panel lines: script prints, warnings and runtime errors, 50 by default and 500 at most, filtered by lowest level or text. |
sim_bindings | Forge placement records. Needs an engine built with the sim-orchestration feature. |
equip_tool | Set the active tool: select, move, scale or rotate. Refused today. |
select_entity | Replace the selection with entities by id. Refused today. |
invoke_action | Run an editor action by name, as its shortcut would. Refused today. |
export_instances_toml | Dump the world database to readable TOML under the Space's .eustress/exports. Refused today. |
data_bind | Drive a simulation parameter from a Dataset column. Refused today. |
data_bindings | List the active Dataset bindings. Refused today. |
data_unbind | Remove a Dataset binding. Refused today. |
Call scene_overview to see where things are, partition_scene to cut the world into one unit per
agent, then page each unit's cells with inspect_scene and its cell argument, instead of paging one flat
list of every entity.
AI Camera and Capture
The AI camera is a second, off-screen camera inside the engine, separate from your view, that renders only when asked. All five tools here are refused over MCP today, because they are missing from the capability table.
| Tool | What it does |
|---|---|
capture_viewport | Screenshot the Studio window and return the PNG inline, up to 2,800,000 bytes. |
ai_camera_set_pose | Place the AI camera by position plus a look-at point or a rotation. |
ai_camera_orbit | Orbit the AI camera around a point: distance 15, yaw 45 degrees and pitch 30 degrees by default. |
ai_camera_frame | Aim the AI camera at a named entity from a distance suited to its size. |
ai_camera_capture | Render the AI camera and return the image inline. |
Simulation and Physics
The simulation tools reach an engine through files rather than the bridge,
because they also run inside the engine, where a call to its own bridge would
wait on itself. They queue a command in a file the engine reads every frame and
read the runtime snapshot it rewrites 4 times a second, so results need an
engine with the Universe open. With several engines on one Universe, pass pid to choose one. The waiting tools stop early when the client
cancels. The clock and watchpoints themselves are covered in Simulation.
| Tool | What it does |
|---|---|
run_simulation | Enter Play, like the Play button, with optional time_scale and duration_s; returns a ticket. |
pause_simulation | Pause the run, keeping its state. |
stop_simulation | Stop the run and return to Edit, like the Stop button. |
get_simulation_state | Play state, watchpoint values, snapshot age, the current run and the last finished one. |
await_simulation | Wait for a run to end, 300 s at most by default, and return its final values. |
get_sim_value | Read one watchpoint value. |
set_sim_value | Write a value into the simulation, such as an initial condition. |
list_sim_values | All watchpoints, compactly, optionally under one prefix. |
tail_telemetry | Recent watchpoint samples from the Universe's telemetry log. |
run_experiment | Optionally create a git branch, then apply overrides, run for a duration, wait, and save the result under .eustress/experiments. |
compare_runs | Metric deltas between two saved experiments; latest and latest-1 work as names. |
list_experiments | Saved experiment results, newest first. |
datastore_get | Read a key from a named DataStore file under the Universe's .eustress/datastore. |
datastore_set | Write a key to a named DataStore file. |
query_material | Rendering and mechanical properties of a material preset. |
calculate_physics | Evaluate a Realism equation, such as ideal gas pressure or drag force. |
measure_distance | Straight-line distance between two world points, in meters. |
raycast | Always returns an error pointing to live physics; use scene_raycast. |
06Specialist Tools
CAD
The CAD tools author and inspect parametric CadPart feature trees on disk, with no engine needed. Each authoring call reports the part's state back, so an agent sees a defect as soon as it makes one. The parts themselves are covered in CAD.
| Tool | What it does |
|---|---|
cad_list_templates | Built-in part templates with their variables, and the feature operations the kernel supports. |
cad_create_part | Create a CadPart from a template (plate, box, cylinder) or as a placement of a published part. |
cad_set_variable | Set a feature-tree variable, such as a height of 0.02 m. |
cad_describe_part | The feature tree: variables in meters, per-feature status, sketch solve state and mesh statistics. |
cad_validate_part | Pass or fail checks for empty bodies, open surfaces, non-manifold edges, degenerate triangles and bad volume. |
cad_measure | Volume, surface area, center of mass and bounds; mass from a density; exact minimum distance to a second part. |
cad_add_feature | Add a feature: extrude, revolve, hole, mirror, pattern, boolean, split, sweep, fillet, chamfer or shell. |
cad_edit_feature | Suppress, unsuppress, rename or patch the feature at a tree index. |
cad_delete_feature | Remove a feature; refused while later features reference it, unless forced. |
cad_create_sketch | Add a named 2D sketch on the xy, xz or yz plane or on a face. |
cad_add_sketch_entity | Add a point, line, circle, arc, rectangle or construction geometry, in meters. |
cad_add_constraint | Add a geometric constraint and get the solver's verdict. |
cad_dimension | Add a driving linear, radial or angular dimension from a value, a variable or an expression. |
cad_solve_sketch | Run the 2D solver and report status, residual and remaining degrees of freedom. |
cad_offset_sketch | Make a new sketch offset from a profile: negative shrinks it, positive grows it. |
cad_publish_part | Publish a part to the Universe's shared CAD library so it can be placed many times. |
cad_list_sources | The shared library's published parts and whether each still evaluates. |
cad_export_glb | Export a CadPart to a binary glTF file with its parameters. |
Website and Moderation
The Website tools edit a Space's Website service as TOML, so they work without
an engine; see Website Service. The moderation
tools serve Eustress's Gallery moderators and act on eustress.dev, so they need
the Network capability that EUSTRESS_MODERATOR_TOKEN grants.
| Tool | What it does |
|---|---|
website_status | The Website service's namespace, schema version and every Reference. |
website_setup | Create or update the service's namespace and schema version. |
website_add_reference | Add or replace a Reference, a pointer that each publish resolves live. |
website_remove_reference | Remove a Reference. Refused over MCP (Destructive). |
website_manifest_url | The manifest URL a site fetches, with the markup it needs. |
moderation_queue | Gallery moderation cases by status. |
moderation_case | One moderation case in full. |
moderation_act | Approve, reject, hold or request changes, as the signed-in moderator. |
moderation_backfill | Pass older Gallery listings through the moderation gate, a few per call. |
Generation and Network
| Tool | What it does |
|---|---|
image_to_code | Turn an image in the Universe into Rune code through the Claude vision API. Refused over MCP (Network). |
image_to_geometry | Rebuild a reference image as scene geometry with VIGA, a generate, render and verify loop. Refused over MCP (Network). |
document_to_code | Turn a design document into Rune or Luau code. Refused over MCP (Network). |
http_request | GET or POST to an external URL. Refused over MCP (Network). |
find_similar_entities | Entities most like a reference entity. Returns the request only. |
suggest_swap_template | Toolbox templates ranked for a part. Returns the request only. |
suggest_contextual_edits | A few edits that would improve a scene. Returns the request only. |
suggest_tool_defaults | Options Bar defaults for a tool. Returns the request only. |
The last four hand their request to an engine-side lookup. Over MCP there is no engine behind the call, so they answer with what was asked rather than results.
07Resources
Resource URIs
A resource is a document the client can read and pin. The server publishes six
URI templates; {+path} keeps its slashes, so nested folders
round-trip.
| URI | Returns |
|---|---|
eustress://space/{space} | A Markdown overview of a Space: its service folders and scripts. |
eustress://script/{space}/{+path} | A script folder's class, summary and source in one Markdown document. |
eustress://entity/{space}/{+path} | An entity's _instance.toml with its name and class. |
eustress://file/{space}/{+path} | Any text file under a Space. Binary files such as .png and .glb are refused. |
eustress://conversation/{session_id} | A saved Workshop session from .eustress/knowledge/sessions. |
eustress://brief/{product} | An ideation_brief.toml found under the Spaces, by product name. |
resources/list returns the Spaces, their scripts (up to 500), the
20 newest conversations and the briefs, 100 per page with a cursor, up to 2,000
entries.
Subscriptions
Subscribe to a resource and the server sends notifications/resources/updated when its file changes. A file
watcher starts with the first subscription and stops with the last. It watches
the Universe's Spaces folder and its saved sessions, lets a
burst of writes settle for 120 ms, and reacts to .rune, .luau, .soul, .md, .toml and .json files outside .git, target and node_modules.
Switching Universe with set_active_universe moves the watcher
and sends notifications/resources/list_changed, so the client
drops a list that belongs to the Universe it left.
08What's Next
Editor Control over MCP
The twelve refused live tools will become callable once they have entries in the capability table: the AI camera, viewport capture, tool and selection control, editor actions, Dataset bindings and database export. The engine side of each already answers on the bridge, so an agent will see and steer Studio through MCP the way the eustress CLI can today.
Packaging and Pass-Through Tools
The installer already has a place for eustress-mcp; a release build step for
its package will put the server beside Eustress Engine on every install. The
disk raycast tool will route to the bridge's live physics, and
the request-only tools will return results once they have a store or an engine
behind them out of process.
Point an agent at your Universe and let it read, build and test.