An MCP server that lets AI agents (Claude Code / Claude Desktop) drive OVERDARE Studio — browse the DataModel, create and edit instances, write Luau scripts, and start playtests.
OVERDARE Studio already hosts a local JSON-RPC server (the same one its built-in
"diligent" agent uses). This MCP server is a thin bridge to it — much like
unity-mcp's connector, except Studio hosts the local server itself, so
there's no plugin to install.
Claude ──stdio──▶ overdare-mcp ──raw TCP──▶ Studio RPC (127.0.0.1:13377)
──HTTP────▶ UE Remote Control (127.0.0.1:30010)
└─ OVERDARE Studio (must be running)
Port 13377 is not HTTP. It is newline-delimited JSON-RPC 2.0 straight over a
TCP socket, one connection per call — speaking HTTP at it yields a protocol
violation instead of a response. Remote Control on 30010 is HTTP, and is a
separate surface (the overdare_rc_* tools) that reaches the Unreal editor
underneath rather than the OVERDARE DataModel.
Studio must be open with a project for tools to work. If Studio isn't running, tools return a clear "cannot reach Studio" error instead of hanging.
npm install
npm run buildVerify the connection (open OVERDARE Studio first):
npm run probe # smoke test + level.browse
npm run probe -- instance.read '{"path":"Workspace.Baseplate"}' # inspect any methodAdd to .mcp.json in your project (an example is included in this repo):
{
"mcpServers": {
"overdare": {
"command": "node",
"args": ["/ABSOLUTE/PATH/TO/overdare-mcp/dist/index.js"]
}
}
}For Claude Desktop, add the same block under mcpServers in
claude_desktop_config.json.
| Var | Default | Purpose |
|---|---|---|
STUDIO_HOST |
localhost |
Studio RPC host (STUDIO_RPC_HOST also accepted) |
STUDIO_PORT |
13377 |
Studio RPC port (STUDIO_RPC_PORT also accepted) |
STUDIO_RPC_TIMEOUT_MS |
10000 |
Per-call timeout |
OVERDARE_PROJECT_DIR |
auto-detected | Fallback project directory for the file-backed tools |
OVERDARE_BLENDER |
auto-detected | Blender executable used by overdare_mesh_prepare |
Three backends sit behind these, and the difference matters when something
fails. RPC talks to Studio live over 13377. File edits the saved
.ovdrjm and asks Studio to reload, which is how the tools whose RPC method
does not exist are implemented — stop any playtest before using those. Each such
edit first copies the level into .ovdr-backups/ beside it, keeping the last
ten, and reports the path it wrote. RC is Unreal Remote Control on 30010.
| Tool | Via | Purpose |
|---|---|---|
overdare_browse |
RPC level.browse |
Browse the DataModel tree. Start here — everything else targets guid |
overdare_find |
File | Search by name substring and/or class; far more practical than browsing a large project |
overdare_read_instance |
File | Read one instance's properties as saved |
overdare_script_read |
File | Read a script's Luau source |
overdare_validate_lua |
luau-lsp | Type-check scripts against the OVERDARE type definitions |
overdare_status |
— | Report what is reachable and configured |
overdare_validate_lua runs the luau-lsp and overdare-types.d.lua that ship
beside Studio's bundled agent, so it knows the real API surface — it will tell
you that Key 'Bold' not found in external type 'TextLabel', which is exactly
the kind of break an engine update causes and that otherwise only shows up as a
runtime warning in Sandbox.log. It takes scripts already in the project (by
GUID or dotted path, as overdare_find reports them) or files on disk.
Its blind spot is worth knowing: a local from Instance.new("TextLabel") is not
narrowed to TextLabel, so property errors on that local go unreported. Annotate
the local, or check the value at runtime with overdare_observe.
| Tool | Via | Purpose |
|---|---|---|
overdare_create_part |
File | Create a Part |
overdare_create_instance |
File | Create one instance of any class |
overdare_create_instances |
File | Bulk-create under one parent in a single reload |
overdare_update_instance |
File | Change properties and/or rename |
overdare_move_instance |
File | Reparent |
overdare_duplicate_instance |
File | Copy a subtree with fresh GUIDs |
overdare_delete_instance |
File | Delete by GUID |
overdare_instance_create |
RPC instance.create |
Create live, under one parent, no reload |
overdare_instance_update |
RPC instance.update |
Change properties live, no reload |
overdare_instance_delete |
RPC instance.delete |
Delete live, without a reload |
overdare_script_add |
File | Create a Script / LocalScript / ModuleScript |
overdare_script_edit |
File | Replace a script's source, or toggle Enabled |
Prefer the RPC three when you are iterating: the file-backed tools rewrite the project and ask Studio to reload, so they need the playtest stopped, while these go to the running editor. Two things about them are worth knowing before you trust a result:
- Properties go flat. Nesting them under a
Propertieskey does not fail — the call succeeds,messagesaysProperties is not a valid property of Frame, and every value is dropped. Same for a misspelled property name: a[WARNING]inmessage, not an error. Readmessage. - A class this build lacks is not an error either. The call succeeds and
returns fewer GUIDs than you asked for.
overdare_instance_createpairs them back up and reportsguid: nullper instance.UICorner,UIGradient,UIPadding,UISizeConstraint,UITextSizeConstraint,ViewportFrame,TextBox,CanvasGroupandVideoFrameare all absent;UIStroke,UIListLayout,UIGridLayout,UIAspectRatioConstraint,ProgressBarandScrollingFrameare present.
Composite property values are tagged objects, and UDim2 spells its second axis
with a lower-case y — an upper-case Y is silently dropped. Rather than write
those by hand, pass the array short forms (Size: [0.5, 10, 0, 64],
BackgroundColor3: [20, 180, 90], AnchorPoint: [0.5, 1]); see src/props.ts.
| Tool | Via | Purpose |
|---|---|---|
overdare_save |
RPC level.save.file |
Save the project to disk |
overdare_apply |
RPC level.apply |
Reload pending level changes |
overdare_publish |
RPC level.publish |
Publish to the Hub (owner only) |
overdare_set_project |
— | Re-detect or pin the project directory |
| Tool | Via | Purpose |
|---|---|---|
overdare_play / overdare_stop |
RPC game.play / game.stop |
Start / stop a playtest |
overdare_screenshot |
RPC game.screenshot |
Capture the viewport and return the image. UI is included; locate projects instances into click coordinates |
overdare_camera |
RC | Aim the editor camera before a screenshot |
overdare_viewport |
RC | Read or change how the editor viewport renders |
Only meaningful while a playtest is running. A screenshot shows pixels and the
saved project shows what was authored; neither shows what the running game
currently holds, which is where UI bugs live — a label a script rewrote, a frame
a layout pushed off screen, a button buried under a higher ZIndex.
| Tool | Via | Purpose |
|---|---|---|
overdare_pie_status |
RPC game.pie.status |
Is a playtest running, and which clients take input |
overdare_ui_browse |
RPC game.ui.browse |
The live UI as flat elements: path, class, text, normalized rect, visibility |
overdare_observe |
RPC game.observe |
Character, UI, and live instance state in one game-thread read |
overdare_character_read |
RPC game.character.read |
CFrame, speed, facing, what it stands on |
overdare_input_inject |
RPC game.input.inject |
Keys, pointer, look, scroll, waits — up to 64 events |
Two things the underlying method does not tell you, which the tool handles:
pieSessionIdandclientIdare mandatory on the wire. Omitting either fails the batch with the sameInvalid input eventas a malformed event, sooverdare_input_injectresolves both fromgame.pie.statuswhen you leave them out.action: "press"is documented but rejected by this build; onlydown/upare accepted. The tool expandspressinto adown/wait/uptriple so the short form still works.
Pointer events only land on the part of the viewport that lies inside the Studio
window. When the window hangs off screen the reachable region shrinks — sometimes
to a sliver — and overdare_ui_browse reports it under viewport.reachable.
The game still renders the whole frame, so a screenshot looks fine while a click
at those coordinates goes nowhere.
| Tool | Via | Purpose |
|---|---|---|
overdare_assets |
— | List the curated Asset Drawer catalog |
overdare_asset_import |
RPC | Import an Asset Drawer model by id |
overdare_image_import |
RPC | Import a local PNG/JPG into the asset manager |
overdare_mesh_prepare |
Blender | Make a 3D file import-ready (see below) |
overdare_mesh_bulk_import |
GUI | Drive many prepared meshes through the import dialog |
overdare_engine_assets |
RC | Query the engine asset registry |
overdare_rc_search_assets |
RC | Search the Unreal asset library |
| Tool | Purpose |
|---|---|
overdare_rc_call |
Call a UFunction on an Unreal object |
overdare_rc_property |
Read or write a property |
overdare_rc_describe |
Describe an object's properties and functions |
overdare_rc_list_actors / overdare_actors |
Enumerate live actors and their object paths |
overdare_selection |
Read or change the editor's actor selection |
overdare_console |
Run a console command or read a cvar |
overdare_rc_batch |
Several RC requests in one round-trip |
overdare_rc_python |
Run an Unreal Python command |
overdare_rc_batch and overdare_rc_python do not work against a shipping
Studio build: PUT /remote/batch has crashed Studio outright, and
ExecutePythonCommand returns 400 because the Python plugin is not loaded.
They are kept for builds that do support them.
| Tool | Purpose |
|---|---|
overdare_rpc |
Call any Studio RPC method with raw params |
overdare_recipe |
Read a build recipe adapted from the built-in agent's skills |
OVERDARE caps imported meshes at 30,000 triangles per mesh and recommends
512px textures (large 4K textures OOM the importer). overdare_mesh_prepare
runs headless Blender to make any .fbx / .obj / .glb / .gltf / .blend
import-ready:
- decimates an over-budget mesh to fit the triangle limit,
- downscales & re-embeds textures to the target size,
- writes a self-contained
<name>_overdare.fbx.
The final Import into Studio (Home ▸ Import) is still a manual GUI step. Requires
Blender installed — Steam installs are auto-detected; set OVERDARE_BLENDER to
override the path.
The .ovdrjm file-edit tools now auto-follow whichever project is open in
Studio (recovered from its screenshot path). After switching projects in
Studio, call overdare_set_project with no args to re-detect, or pass a dir to
pin a specific project directory. OVERDARE_PROJECT_DIR remains a fallback.
Method names were originally read out of the Studio runtime, and several of them turned out not to be dispatchable. Probed against a live Studio (2026-09-07):
Present. level.browse · level.apply · level.save.file ·
level.publish · instance.create · instance.update · instance.read ·
instance.move · instance.delete · game.play · game.stop ·
game.screenshot · game.observe · game.ui.browse · game.pie.status ·
game.character.read · game.input.inject · viewport.camera.read ·
viewport.camera.set · hub.token.read
Absent — these return -32601, so anything routed to them fails: every
script.* method, instance.upsert, procedural.run, the asset-import
methods, and validatelua. They belong to the bundled agent, not to the RPC
surface. The script and instance tools here go through the project file instead,
which is why they carry the "stop the playtest first" caveat.
There is no discovery method — rpc.discover and system.listMethods are both
absent. To check whether a method exists, call it with empty params and read the
error: -32601 means no such method, anything else means it is there.
npm run probe -- <method> '<json>' does this, and overdare_rpc is the
fallback for a method with no dedicated tool.