Learn/MCP Server

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.

Time20 min readLevelIntermediateUpdatedUpdated Sep 2026

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.

AI clientClaude Code, Cursorstdioeustress-mcptools and resourcesUniverse filesSpaces, scripts, TOMLdisk toolsRunning engineStudio or headlessTCP bridgeload, watch, save
Disk tools work on the Universe folder with or without an engine running. Live tools go through the engine's bridge and act on the world it has loaded.
Disk toolsLive tools
Needs an engine runningNoYes: Studio or eustress-headless
SeesFiles on diskThe loaded world, including parts that live only in the world database
A change showsWhen the engine's watcher reloads the fileOn the engine's next frame
Examplesread_file, create_script, git_statusinspect_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:

Terminal
cargo build --release -p eustress-mcp-server

# Windows:        target/release/eustress-mcp.exe
# macOS, Linux:   target/release/eustress-mcp

The 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.

Advanced
Build eustress-mcp-server, not eustress-mcp

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:

.mcp.json
{
  "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:

SettingEffect
--universe <path>The Universe to open at startup. Checked before the variable below.
EUSTRESS_UNIVERSEThe same, as an environment variable: an absolute path to a folder that holds Spaces.
EUSTRESS_UNIVERSES_PATHFolders to search for Universes and for a running engine, separated by ; on Windows and : elsewhere. Default: ~/Eustress, ~/Documents/Eustress and your home folder.
RUST_LOGLevel of the server's own log, which goes to stderr. Default: info.
EUSTRESS_MODERATOR_TOKENFor 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:

  1. The --universe argument.
  2. The EUSTRESS_UNIVERSE variable.
  3. The nearest folder above the working directory that contains a Spaces folder.

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.

Advanced
Tools follow Studio's last Space

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.

ClassOver MCPTools
ReadAllowedQueries, such as read_file, inspect_scene, git_log
WriteAllowedChanges inside the Universe, such as create_entity, write_file, run_simulation
DestructiveRefuseddelete_entity, git_commit, git_branch, website_remove_reference
ExecuteRefusedrun_bash, execute_luau, execute_rune
NetworkOnly with EUSTRESS_MODERATOR_TOKENhttp_request, image_to_code, image_to_geometry, document_to_code, the four moderation_ tools
Advanced
Twelve live tools are refused today

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_directory and write_file take paths relative to the Universe root. A path containing .. is refused, and so is any path that resolves outside the Universe.
  • Entity files: write_file will not overwrite an _instance.toml; entities change through create_entity and update_entity.
  • Size caps: read_file returns 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.

Warning
The bridge trusts local programs

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

ToolWhat it does
set_active_universeSwitch the Universe this session resolves against. Handled by the server itself.
list_universesList the Universe folders beside the current one.
list_spacesList the Spaces of a Universe, the current one by default.
new_universeCreate a Universe folder beside the current one, with Spaces and .eustress folders. Fails if it exists.
new_spaceCreate a Space from the standard service templates. The engine builds its database on first open.
rename_spaceRename a Space folder. Fails cleanly while a running engine holds it.
rename_universeRename a Universe folder and update the next-launch marker if it pointed there.
set_next_launch_universeWrite the marker the engine reads at startup to choose its Universe.
list_space_contentsA Space's services and top-level entities, or the children of one folder or Model.
generate_docsWrite a README.md describing the Space: services, entities, scripts and materials.
get_conversationRead a saved Workshop conversation by session id.

Files and Scripts

ToolWhat it does
read_fileRead a text file by its path from the Universe root.
list_directoryEvery file and subfolder of a folder as it is on disk, with sizes.
write_fileWrite a file under the Universe, creating folders as needed.
stage_file_changeReturn a proposed create, modify or delete for review. Writes nothing.
search_universeSearch every Space's .toml, .rune, .lua and .md files; returns paths and line numbers.
list_assetsMeshes, textures and materials in the Space's MaterialService and Workspace.
list_scriptsEvery script source (.rune, .lua, .luau, .soul) in the Space's services, as Space-relative paths. Workspace is left out.
read_scriptA script's source, by name or by the path list_scripts returns.
create_scriptCreate 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_runeWrite a Rune script into SoulService for the engine to run. Refused over MCP (Execute).
execute_luauWrite a Luau script into SoulService for the engine to run. Refused over MCP (Execute).
run_bashRun 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.

ToolWhat it does
create_entityCreate a part or other instance with its size, color and material. Live first.
update_entityChange an entity's properties. Live first.
delete_entityRemove an entity. Refused over MCP (Destructive).
query_entitiesList entities, optionally of one class. Live first.
find_entityFind entities whose name contains a text. Live first.
add_tagAdd a CollectionService tag to an entity. Live first.
remove_tagRemove a CollectionService tag. Live first.
get_tagged_entitiesEntities that carry a tag, read from the Workspace files.
insert_gaussian_splatsImport a .ply Gaussian-splat cloud as a GaussianSplats instance.
particle_simulationDescribe, create, read or change a ParticleSimulation and its species.
promote_entityWrite a database-only part out as an _instance.toml folder, keeping its uuid. Needs a running engine.
demote_entityFold a bare part folder back into the database and delete the folder. Needs a running engine.

Git, Memory and Logs

ToolWhat it does
git_statusModified, staged and untracked files in the Universe's git repository.
git_logRecent commits with hash, author, date and message.
git_diffUncommitted changes as a unified diff, optionally for one path.
feedback_diffA structured diff between two git refs or two file paths.
git_commitStage everything and commit. Refused over MCP (Destructive).
git_branchList, create, switch, delete or merge branches. Refused over MCP (Destructive).
list_rulesWorkshop rules: .eustress/rules/*.md for the Universe and .rules/*.md in the Space.
list_workflowsWorkshop workflows, the .md files behind /run commands.
query_audit_logThe engine's Claude call log, newest first, up to 50 entries.
rememberReturns the memory as a request. Nothing is stored out of process.
recallReturns the query as a request. The server holds no memories to search.
query_stream_eventsReturns 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.

NeedsTools
A running engineEverything in Scene and Editor and AI Camera and Capture, plus promote_entity and demote_entity
An engine if one runs, else the filescreate_entity, update_entity, query_entities, find_entity, add_tag, remove_tag
An engine with the Universe open, reached through filesThe simulation tools in Simulation and Physics
Nothing but the filesEvery other tool

Scene and Editor

ToolWhat it does
inspect_scenePer-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_overviewEntities grouped into 256 m Morton cells, densest first, each with bounds, a count and a class histogram.
partition_sceneSplit the scene into balanced, spatially contiguous work units for parallel agents, 4 by default and 64 at most.
scene_raycastCast a ray against the live Avian colliders; hits nearest first, up to 1,000 m and 8 hits by default.
oplog_tailRecent entity creates and deletes from the engine's op-log, 50 by default and 1,000 at most.
sim_stepAdvance 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_stateThe active editor tool and the current selection.
read_outputThe newest Output panel lines: script prints, warnings and runtime errors, 50 by default and 500 at most, filtered by lowest level or text.
sim_bindingsForge placement records. Needs an engine built with the sim-orchestration feature.
equip_toolSet the active tool: select, move, scale or rotate. Refused today.
select_entityReplace the selection with entities by id. Refused today.
invoke_actionRun an editor action by name, as its shortcut would. Refused today.
export_instances_tomlDump the world database to readable TOML under the Space's .eustress/exports. Refused today.
data_bindDrive a simulation parameter from a Dataset column. Refused today.
data_bindingsList the active Dataset bindings. Refused today.
data_unbindRemove a Dataset binding. Refused today.
Tip
Large scenes: overview, partition, then page

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.

ToolWhat it does
capture_viewportScreenshot the Studio window and return the PNG inline, up to 2,800,000 bytes.
ai_camera_set_posePlace the AI camera by position plus a look-at point or a rotation.
ai_camera_orbitOrbit the AI camera around a point: distance 15, yaw 45 degrees and pitch 30 degrees by default.
ai_camera_frameAim the AI camera at a named entity from a distance suited to its size.
ai_camera_captureRender 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.

ToolWhat it does
run_simulationEnter Play, like the Play button, with optional time_scale and duration_s; returns a ticket.
pause_simulationPause the run, keeping its state.
stop_simulationStop the run and return to Edit, like the Stop button.
get_simulation_statePlay state, watchpoint values, snapshot age, the current run and the last finished one.
await_simulationWait for a run to end, 300 s at most by default, and return its final values.
get_sim_valueRead one watchpoint value.
set_sim_valueWrite a value into the simulation, such as an initial condition.
list_sim_valuesAll watchpoints, compactly, optionally under one prefix.
tail_telemetryRecent watchpoint samples from the Universe's telemetry log.
run_experimentOptionally create a git branch, then apply overrides, run for a duration, wait, and save the result under .eustress/experiments.
compare_runsMetric deltas between two saved experiments; latest and latest-1 work as names.
list_experimentsSaved experiment results, newest first.
datastore_getRead a key from a named DataStore file under the Universe's .eustress/datastore.
datastore_setWrite a key to a named DataStore file.
query_materialRendering and mechanical properties of a material preset.
calculate_physicsEvaluate a Realism equation, such as ideal gas pressure or drag force.
measure_distanceStraight-line distance between two world points, in meters.
raycastAlways 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.

ToolWhat it does
cad_list_templatesBuilt-in part templates with their variables, and the feature operations the kernel supports.
cad_create_partCreate a CadPart from a template (plate, box, cylinder) or as a placement of a published part.
cad_set_variableSet a feature-tree variable, such as a height of 0.02 m.
cad_describe_partThe feature tree: variables in meters, per-feature status, sketch solve state and mesh statistics.
cad_validate_partPass or fail checks for empty bodies, open surfaces, non-manifold edges, degenerate triangles and bad volume.
cad_measureVolume, surface area, center of mass and bounds; mass from a density; exact minimum distance to a second part.
cad_add_featureAdd a feature: extrude, revolve, hole, mirror, pattern, boolean, split, sweep, fillet, chamfer or shell.
cad_edit_featureSuppress, unsuppress, rename or patch the feature at a tree index.
cad_delete_featureRemove a feature; refused while later features reference it, unless forced.
cad_create_sketchAdd a named 2D sketch on the xy, xz or yz plane or on a face.
cad_add_sketch_entityAdd a point, line, circle, arc, rectangle or construction geometry, in meters.
cad_add_constraintAdd a geometric constraint and get the solver's verdict.
cad_dimensionAdd a driving linear, radial or angular dimension from a value, a variable or an expression.
cad_solve_sketchRun the 2D solver and report status, residual and remaining degrees of freedom.
cad_offset_sketchMake a new sketch offset from a profile: negative shrinks it, positive grows it.
cad_publish_partPublish a part to the Universe's shared CAD library so it can be placed many times.
cad_list_sourcesThe shared library's published parts and whether each still evaluates.
cad_export_glbExport 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.

ToolWhat it does
website_statusThe Website service's namespace, schema version and every Reference.
website_setupCreate or update the service's namespace and schema version.
website_add_referenceAdd or replace a Reference, a pointer that each publish resolves live.
website_remove_referenceRemove a Reference. Refused over MCP (Destructive).
website_manifest_urlThe manifest URL a site fetches, with the markup it needs.
moderation_queueGallery moderation cases by status.
moderation_caseOne moderation case in full.
moderation_actApprove, reject, hold or request changes, as the signed-in moderator.
moderation_backfillPass older Gallery listings through the moderation gate, a few per call.

Generation and Network

ToolWhat it does
image_to_codeTurn an image in the Universe into Rune code through the Claude vision API. Refused over MCP (Network).
image_to_geometryRebuild a reference image as scene geometry with VIGA, a generate, render and verify loop. Refused over MCP (Network).
document_to_codeTurn a design document into Rune or Luau code. Refused over MCP (Network).
http_requestGET or POST to an external URL. Refused over MCP (Network).
find_similar_entitiesEntities most like a reference entity. Returns the request only.
suggest_swap_templateToolbox templates ranked for a part. Returns the request only.
suggest_contextual_editsA few edits that would improve a scene. Returns the request only.
suggest_tool_defaultsOptions 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.

URIReturns
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.

Loading Eustress Engine...