Agents
The MCP server
The Unity MCP server and its one tool, satura_exec: how a script runs, what it can reach, how it fails and where the full API lives.
Other Unity bridges give the agent a catalog of tools: every tool's schema rides along in every request, and every pick is another round trip. Satura Space registers one tool, satura_exec. The agent writes the body of an async JavaScript function, and each call in it waits until its work is really done: Play entered, compile finished, video saved. One script can compile, play, drive the game, film it and open a report, and the agent gets one reply.
satura_exec, and a short script that runs line by line.Registration
The server is a Python program that ships inside the app. The app registers it into each project's .mcp.json and into the Claude Code, Codex and Cursor configs, so those three get it with no setup and no API key. You can point any other MCP client at it, and other CLI agents still work in a normal shell. Calls go to the app, so the app must be running.
The server runs locally. It reaches the app over local loopback and the editor through files in .sspace/.
What a script looks like
await unity.refresh()
await unity.play('Assets/Scenes/Pond.unity')
await handles.call('PrepareStageForFishTest')
const bite = await handles.call('WaitForBite', 10)
const state = await handles.call('GetFishState')
await unity.stop()
return { bite: bite.value, state: state.value }
When a value needs your eye, the same tool hands it to you and the agent ends its turn:
return await tuner.open({
name: 'arena-lights',
note: 'Balance the arena lights, then copy your changes to me',
objects: [{ path: '**', components: [{ type: 'Light', fields: ['m_Intensity', 'm_Color'] }] }]
})
Globals
| Global | What it reaches |
|---|---|
unity | Editor status, C# with unity.exec, refresh, Play and Stop, scenes, logs, screenshots, players, frame rate, stream resolution, run mode, dialogs, relaunch, trace, and Unity's source and docs at your version |
handles | Your [SaturaHandle] methods, on one player or all |
video | Recording the Unity panel, and cameras the agent places |
report | Opening and checking reports |
dashboard | The project dashboard |
tuner, drift | Opening and driving a Tuner, and Drift |
scratchpad | Creating and running scratchpads, importing and exporting assets |
env | The never-commit env files from the Git panel |
page, app | The agent's own app window, with a Playwright-style API, and the app's tabs and phone |
agents | Helper agents in other tabs |
logs | The app's main log and the window's console |
unity.exec reaches every Editor API and package in your project, with no recompile or domain reload.
Helpers: snapshot, shot, show, point, say, ask and sleep.
page sends input over Chromium's DevTools protocol, so it reaches the window without moving your cursor or taking focus. The script pauses while you use that window.
The reply
The agent gets back the return value, then Steps: (each call with its time, FAILED on the one that threw), Notes: (unsaved scenes, files written, compile errors), Console: and any images.
Errors
A failed call throws, so the agent can branch on it:
e.name | Means |
|---|---|
CompileError | The message lists the compile errors |
HandleError | A handle threw or doesn't exist; has its stack and log lines |
HandleTimeout | A coroutine outlived its timeout and still runs |
DialogBlocked | A modal dialog blocks the editor |
InputSystemRequired | The project isn't Input System only |
NoGraphicsError | A rendering call in Headless NoGraphics mode |
UnityTimeout, UnityError | The message says why |
TypeError | A wrong argument or option |
Limits
- A script runs in its own sandboxed worker with a time limit: 300 seconds by default, an hour at most. It stops when the MCP call is cancelled.
- No
require,import,process,fetchor file access. Agents use their own tools for files. - Editor calls queue behind each other.
logs,status,sourceanddocsdon't wait. - Scripts that don't come from the MCP server, such as report buttons, get only the app API: no
unity,handlesorvideo.
Blocking dialogs
A modal dialog halts Unity's main thread, so no queued command can run. Instead of timing out, a call returns in about a second with DialogBlocked and the dialog's contents. The agent reads it with unity.dialogs(), presses a button with unity.answer('Save') from outside the editor process, and retries.
Screenshots
unity.screenshot() renders the scene from a temporary camera in Edit or Play Mode, from 1 to 16 poses. Several poses give one grid image. It shows the world, not the player's screen, and nothing in the scene changes. The PNG comes back in the reply, and nothing stays on disk unless the agent saves it, by default under .sspace/screenshots/.
Video
video.record films what the Unity panel shows while part of the script runs, at 60 fps for up to 15 minutes, into an .mp4 under .sspace/videos/ by default. Video is muted unless the agent asks for audio, and the stream stays locked to the recording size, with a badge on the panel, until it finishes. Encoding runs in the app on the hardware H.264 encoder. video.cameras films up to 6 placed cameras on the same frames, each to its own file.
The full API
The tool description is a short sheet. The full API, with examples, is the agent guide in the engine package: Documentation~/agent-guide.md. Server internals are in packages/mcp/README.md.