EmirCobanOfficial

roblox-studio-mcp

Community EmirCobanOfficial
Updated

Open-source MCP server that lets AI assistants drive Roblox Studio — run Luau, edit the DataModel, read the scene tree.

roblox-studio-mcp

An open-source Model Context Protocol (MCP) server that lets AI assistants — Claude Desktop, Claude Code, or any MCP client — drive Roblox Studio: run Luau, build and edit the DataModel, read the scene tree, and manage scripts.

Status: early (v0.1). Works, but expect rough edges. Contributions welcome!

How it works

Roblox Studio cannot open a listening socket, so the plugin drives the loop bylong-polling a tiny local HTTP bridge that ships inside the MCP server:

┌────────────┐   MCP (stdio)   ┌──────────────────┐   HTTP long-poll   ┌──────────────┐
│ Claude /   │ ◀─────────────▶ │ roblox-studio-mcp │ ◀────────────────▶ │ Studio plugin │
│ MCP client │   JSON-RPC      │  (server+bridge)  │   /poll  /result   │   (Luau)      │
└────────────┘                 └──────────────────┘                    └──────────────┘
  1. Your MCP client calls a tool (e.g. create_instance).
  2. The server queues a command; the plugin picks it up via GET /poll.
  3. The plugin runs it in Studio and posts back via POST /result.
  4. The server returns the result to the client.

Everything stays on 127.0.0.1 — nothing is exposed to the network.

Tools

Tool What it does
run_luau Execute an arbitrary Luau snippet; captures return values and print/warn output.
create_instance Create an Instance and parent it under a path.
set_properties Set properties on an existing instance.
delete_instance Delete an instance (undoable — re-parents to nil, not Destroy).
get_tree Return the DataModel hierarchy as nested JSON.
get_properties Read properties from an instance.
get_selection Return the instances the user has selected in Studio.
read_script Read the Source of a Script/LocalScript/ModuleScript.
edit_script Replace a script's Source (undoable).
batch Run several commands as one all-or-nothing transaction (single undo).

Mutating tools are wrapped in a ChangeHistoryService recording, so every AIaction is a single Ctrl+Z in Studio.

Install

1. Build the server

git clone https://github.com/EmirCobanOfficial/roblox-studio-mcp.git
cd roblox-studio-mcp
npm install
npm run build

2. Install the Studio plugin

Copy plugin/RobloxStudioMCP.server.lua into your local Plugins folder:

  • In Studio: Plugins tab ▸ Plugins Folder — drop the file in there.
  • Restart Studio (or right-click the Plugins pane ▸ Rescan).

You'll get an MCP toolbar with a Connect button.

Updating the plugin later: Studio does not hot-reload local pluginfile changes. After overwriting the file, fully restart Studio to pick upthe new version (a Rescan is not always enough).

3. Point your MCP client at the server

Claude Desktop — add to claude_desktop_config.json:

{
  "mcpServers": {
    "roblox-studio": {
      "command": "node",
      "args": ["/absolute/path/to/roblox-studio-mcp/dist/index.js"]
    }
  }
}

Claude Code:

claude mcp add roblox-studio -- node /absolute/path/to/roblox-studio-mcp/dist/index.js

4. Connect

Open a place in Studio, click MCP ▸ Connect, then ask your assistant to dosomething like "create a red neon part 10 studs above the baseplate."

HTTP requests must be allowed. The plugin tries to setHttpService.HttpEnabled = true for you. If requests are blocked, enableGame Settings ▸ Security ▸ Allow HTTP Requests and reconnect.

Encoding Roblox datatypes

JSON has no Vector3, so property values may be plain JSON, or a taggedobject { "$type": ..., "value": ... }:

Roblox type JSON
number/string/bool 5, "hi", true
Vector3 { "$type": "Vector3", "value": [0, 10, 0] }
Vector2 { "$type": "Vector2", "value": [1, 2] }
Color3 (0–1) { "$type": "Color3", "value": [1, 0, 0] }
UDim { "$type": "UDim", "value": [0.5, 20] }
UDim2 { "$type": "UDim2", "value": [0.5, 20, 0.5, 0] }
CFrame { "$type": "CFrame", "value": [x,y,z, ...12 components] }
BrickColor { "$type": "BrickColor", "value": "Bright red" }
EnumItem { "$type": "EnumItem", "enum": "Material", "name": "Neon" }
Instance ref { "$type": "Instance", "path": "game.Workspace.Part" }

Reads (get_properties, get_tree, run_luau return values) come back in thesame tagged form.

Example

// create_instance
{
  "className": "Part",
  "parent": "game.Workspace",
  "name": "GlowBlock",
  "properties": {
    "Anchored": true,
    "Position": { "$type": "Vector3", "value": [0, 10, 0] },
    "Color":    { "$type": "Color3", "value": [1, 0, 0] },
    "Material": { "$type": "EnumItem", "enum": "Material", "name": "Neon" }
  }
}

Transactions (batch)

batch runs a list of commands in order as one transaction: all theirmutations collapse into a single Ctrl+Z, and if any command fails, everychange made earlier in the batch is reverted (via a cancelledChangeHistoryService recording) and an error is returned. On success you getback each command's result, in order.

// batch
{
  "commands": [
    { "type": "create_instance", "args": { "className": "Folder", "parent": "game.Workspace", "name": "Arena" } },
    { "type": "create_instance", "args": { "className": "Part", "parent": "game.Workspace.Arena", "name": "Floor",
        "properties": { "Anchored": true, "Size": { "$type": "Vector3", "value": [64, 1, 64] } } } },
    { "type": "set_properties", "args": { "path": "Workspace.Arena.Floor",
        "properties": { "Color": { "$type": "Color3", "value": [0.2, 0.2, 0.2] } } } }
  ]
}

If, say, the third command referenced a bad path, the Arena folder andFloor part from the first two would be rolled back — nothing is lefthalf-built. batch cannot be nested.

Paths

Paths are resolved from game. Both dots and slashes work, and the leadinggame is optional:

game.Workspace.Baseplate
Workspace/Baseplate
ServerScriptService.Main

The first hop off game is resolved with game:GetService(...) when possible.

Configuration

Env var Default Description
ROBLOX_STUDIO_MCP_PORT 44755 Port for the local plugin bridge.
ROBLOX_STUDIO_MCP_READONLY (unset) When set to anything other than "", 0, or false, blocks all mutating tools. See Read-only mode.

If you change the port, also update SERVER_URL at the top of the plugin.

Read-only mode

A safety switch that blocks every mutating tool — create_instance,set_properties, delete_instance, edit_script, and batch — while leavingthe read tools (get_tree, get_properties, get_selection, read_script,run_luau) available. Useful when you want the assistant to inspect a place butnot change it. It's enforced in two independent places — either one blocks amutation:

  • Server (launch-time lock). Start the server withROBLOX_STUDIO_MCP_READONLY=1. Mutating tools are refused before the requestever reaches Studio, and the server logs READ-ONLY mode on startup.
  • Plugin (runtime toggle). Click MCP ▸ Read-only in Studio. While active,the plugin refuses mutating commands no matter what the client sends. Readsstill work. This is authoritative — it's enforced at the point of execution,under the control of whoever is at the Studio session.

Note: run_luau is a read tool for gating purposes, but it can executearbitrary Luau — including code that mutates the DataModel. Read-only mode doesnot sandbox run_luau; use the plugin toggle plus your own review if youneed a hard guarantee against changes.

Security notes

  • The bridge binds to 127.0.0.1 only.
  • run_luau executes arbitrary Luau in the Studio/plugin context. Onlyconnect Studio to an MCP client you trust, and review what the assistant does.
  • Mutations are undoable via Studio's history.
  • Read-only mode blocks the dedicated mutating tools when youwant look-but-don't-touch access.

Troubleshooting

  • "No Roblox Studio plugin is connected." The plugin does not auto-connectwhen Studio starts — click MCP ▸ Connect on the toolbar after opening yourplace. The button is a toggle; make sure it's active.
  • Edited the plugin but nothing changed? Studio doesn't hot-reload localplugins. Fully restart Studio, then click Connect again.
  • HTTP requests blocked. Enable Game Settings ▸ Security ▸ Allow HTTPRequests and reconnect (the plugin also tries to set this for you).
  • Deletes should be undoable. delete_instance re-parents to nil inside aChangeHistoryService recording, so a single Ctrl+Z restores the instance.(Instance:Destroy() would lock the instance and make the delete unrecoverable.)
  • Mutations keep getting refused ("Read-only mode is enabled")? Either theMCP ▸ Read-only toggle is active in Studio, or the server was started withROBLOX_STUDIO_MCP_READONLY set. See Read-only mode.

Roadmap

Done

  • Verified in real Studio
  • Selection-aware tools (operate on the current Studio selection)
  • Output/print capture for run_luau
  • Batch/transaction commands
  • Optional read-only mode

Planned

  • Rojo-friendly project layout for the plugin

Contributing

Issues and PRs welcome. Run npm run build before pushing; CI builds on Node 20.

License

MIT © Emir Coban

MCP Server · Populars

MCP Server · New

    ROCTUP

    1C Metacode MCP Server

    MCP сервер с встроенным AI агентом для поиска по графу метаданных и кода конфигураций 1С

    Community ROCTUP
    nhevers

    r0x-os

    Official SDK, Claude Code plugin and facilitator docs for r0x, the x402 facilitator for Robinhood Chain.

    Community nhevers
    scarletkc

    Vexor

    A semantic search engine for files and code.

    Community scarletkc
    marmutapp

    SuperBased Observer

    Local-first cost & token tracking for Claude Code, Cursor, Codex & 23 more AI coding agents — proxy-accurate per-model spend, an MCP server your agent can query, and an opt-in team rollup. 100% local, no telemetry.

    Community marmutapp
    Intuition-Lab

    Persome

    Local-first macOS Runtime that turns cross-app activity into an inspectable personal model for MCP agents.

    Community Intuition-Lab