roblox-studio-mcp
Allows AI assistants to control Roblox Studio, including running Luau scripts, creating and editing instances, reading the DataModel hierarchy, setting properties, and managing scripts via a plugin.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@roblox-studio-mcpcreate a red neon part 10 studs above the baseplate"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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: v0.5 — 25 tools, verified in real Studio. Live server-VM eval during playtests, multi-window routing, line-level script editing, tags/attributes, official docs + class reflection, a hardened bridge (auth + version handshake), and a Rojo-based plugin. Still young; contributions welcome!
How it works
Roblox Studio cannot open a listening socket, so the plugin drives the loop by long-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) │
└────────────┘ └──────────────────┘ └──────────────┘Your MCP client calls a tool (e.g.
create_instance).The server queues a command; the plugin picks it up via
GET /poll.The plugin runs it in Studio and posts back via
POST /result.The server returns the result to the client.
Everything stays on 127.0.0.1 — nothing is exposed to the network.
Related MCP server: Roblox Studio MCP Assistant
Tools
Tool | What it does |
| List connected Studio instances and their peers ( |
| Execute Luau in the edit VM; captures |
| Execute Luau in a running playtest's server VM (shared require cache). |
| Create an Instance and parent it under a path. |
| Set properties on an existing instance. |
| Delete an instance (undoable — re-parents to |
| Return the DataModel hierarchy as nested JSON. |
| Read properties from an instance. |
| Return the instances the user has selected in Studio. |
| Read a script's source with line numbers; supports line ranges + auto-truncation. |
| Replace a script's whole source via |
| Replace an inclusive line range with new text (editor-safe, undoable). |
| Insert text before a given line (append with |
| Delete an inclusive line range. |
| Search script sources under a path; returns |
| Add/remove a CollectionService tag on an instance (undoable). |
| List an instance's CollectionService tags. |
| Return every instance carrying a tag (optionally under a root). |
| Set/remove an instance attribute (undoable). |
| Return all attributes on an instance. |
| Fetch official engine reference docs as markdown (server-side; no Studio needed). |
| List a class's members (properties/methods/events) from the API dump, inheritance-aware. |
| Run several commands as one all-or-nothing transaction (single undo). |
Mutating tools are wrapped in a ChangeHistoryService recording, so every AI
action is a single Ctrl+Z in Studio.
Multiple Studio windows
Several open places can connect to the same server at once. Every plugin-backed
tool accepts an optional instance_id to pick which window it runs in:
One window open →
instance_idis optional; calls route to it.Several open, no
instance_id→ the call returns a structured error listing the connected windows, so the assistant picks one and retries (it never guesses).instance_idgiven → matched against each window'sinstanceIdor its rawsessionId.
Call get_instances to see what's connected. Each instance is a place
(instanceId is place:<placeId>, or anon:<uuid> for an unpublished place)
and lists its live peers by role: edit is the plugin, and server appears
during a playtest (see Runtime eval). A peer that
stops polling is dropped after ~30s.
Runtime eval (live debugging)
eval_server_runtime runs Luau inside the server VM of a running playtest,
sharing that VM's require cache — so you can read and change live runtime
state (a service's in-memory tables, a match in progress), which run_luau
(edit VM) cannot see. It returns { returned, output } just like run_luau.
How it works: the plugin injects a small peer script into ServerScriptService
when you Connect; during a playtest that script runs in the server VM, connects
to the bridge as a server peer, and evaluates what eval_server_runtime sends.
Check get_instances for a server peer to confirm a playtest is live.
Two one-time prerequisites (both are developer settings the plugin cannot set for you):
Allow HTTP Requests — Game Settings ▸ Security. The server VM needs this to reach the bridge.
ServerScriptService.LoadStringEnabled = true— selectServerScriptServicein the Explorer and tickLoadStringEnabledin the Properties panel. Runtime eval usesloadstring; this property is not script-settable.
eval_server_runtime executes arbitrary Luau in your live game — it is blocked
by read-only mode, and you should only enable it for trusted
clients.
Install
1. Build the server
git clone https://github.com/EmirCobanOfficial/roblox-studio-mcp.git
cd roblox-studio-mcp
npm install
npm run build2. Build & install the Studio plugin
The plugin is a Rojo project under plugin/ — its source
lives as separate modules in plugin/src/ (see Plugin layout).
Build it into a model file and drop that into your local Plugins folder:
# Install Rojo once: https://rojo.space/docs/v7/getting-started/installation/
rojo build plugin/default.project.json -o RobloxStudioMCP.rbxmIn Studio: Plugins tab ▸ Plugins Folder — drop
RobloxStudioMCP.rbxmthere.Restart Studio (or right-click the Plugins pane ▸ Rescan).
You'll get an MCP toolbar with Connect and Read-only buttons.
Working on the plugin? Edit the modules under
plugin/src/and rebuild — or runrojo servewith the Rojo Studio plugin for live sync during development. Studio does not hot-reload an installed plugin, so after rebuilding the.rbxmyou must fully restart Studio (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.js4. Connect
Open a place in Studio, click MCP ▸ Connect, then ask your assistant to do something like "create a red neon part 10 studs above the baseplate."
HTTP requests must be allowed. The plugin tries to set
HttpService.HttpEnabled = truefor you. If requests are blocked, enable Game Settings ▸ Security ▸ Allow HTTP Requests and reconnect.
Encoding Roblox datatypes
JSON has no Vector3, so property values may be plain JSON, or a tagged
object { "$type": ..., "value": ... }:
Roblox type | JSON |
number/string/bool |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Reads (get_properties, get_tree, run_luau return values) come back in the
same 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 their
mutations collapse into a single Ctrl+Z, and if any command fails, every
change made earlier in the batch is reverted (via a cancelled
ChangeHistoryService recording) and an error is returned. On success you get
back 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 and
Floor part from the first two would be rolled back — nothing is left
half-built. batch cannot be nested.
Script surgery
For anything beyond a whole-file rewrite, work by line — it's far cheaper than shipping entire scripts back and forth, and it keeps unrelated code untouched.
read_scriptreturnsnumberedSource(1: local x = …),lineCount, andsourceLength. PassstartLine/endLineto read a slice; without a range a large script is truncated to the first 400 lines with anoteon how to page.grep_scriptsfinds where something lives —{ path, line, text }matches across every script under a path (literal by default;plain=falsefor a Luau pattern).edit_script_lines/insert_script_lines/delete_script_lineschange a line range, insert before a line (lineCount+1appends), or remove a range. All are editor-safe (ScriptEditorService) and undoable, and are blocked by read-only mode.
Typical loop: grep_scripts to locate → read_script a range for context →
edit_script_lines the exact lines.
Tags & attributes
add_tag / remove_tag / get_tags / get_tagged wrap CollectionService,
and set_attribute / get_attributes / delete_attribute cover instance
attributes (values use the same datatype encoding
as properties). The mutating ones are undoable and blocked by read-only mode.
Reference data
Two tools pull official Roblox data server-side — they don't touch the Studio bridge, so they work even when nothing is connected:
get_roblox_docsreturns an engine reference page as markdown (fromcreate.roblox.com) so the agent can check real API semantics instead of guessing. Names are case-sensitive PascalCase; categories:classes(default),enums,datatypes,libraries,globals.get_class_infolists a class's properties (with value types), methods, events, and callbacks from the official API dump, walking the full inheritance chain — soget_class_info PartincludesAnchored(fromBasePart), not justPart's own members. Results are cached for a day.
// grep_scripts → read_script → edit_script_lines
{ "pattern": "PlayerAdded" } // find it
{ "path": "ServerScriptService.Main", "startLine": 40, "endLine": 60 } // read around it
{ "path": "ServerScriptService.Main", "startLine": 47, "endLine": 47,
"source": "\tprint(\"Welcome, \" .. player.DisplayName)" } // patch one linePaths
Paths are resolved from game. Both dots and slashes work, and the leading
game is optional:
game.Workspace.Baseplate
Workspace/Baseplate
ServerScriptService.MainThe first hop off game is resolved with game:GetService(...) when possible.
Plugin layout
The Studio plugin is a Rojo project. plugin/default.project.json maps
plugin/src/ into a single Script (the plugin) with ModuleScript children:
plugin/
├── default.project.json # Rojo project → builds RobloxStudioMCP.rbxm
└── src/
├── init.server.lua # entry: dispatch, poll loop, toolbar (Connect / Read-only)
├── Config.lua # server URL, reconnect delay, mutating-command set
├── Paths.lua # "game.Workspace.Part" → Instance resolution
├── Encoding.lua # Luau ⇄ JSON "$type" tagged datatypes
└── Handlers.lua # one function per command (run_luau, create_instance, …)init.server.lua owns the stateful/IO pieces (networking, undo recording,
read-only gating, toolbar), and Handlers are otherwise pure command logic.
Configuration
Env var | Default | Description |
|
| Port for the local plugin bridge. |
| (unset) | When set to anything other than |
| (unset) | Optional shared secret required on every plugin request. Also read from |
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 leaving
the read tools (get_tree, get_properties, get_selection, read_script,
run_luau) available. Useful when you want the assistant to inspect a place but
not change it. It's enforced in two independent places — either one blocks a
mutation:
Server (launch-time lock). Start the server with
ROBLOX_STUDIO_MCP_READONLY=1. Mutating tools are refused before the request ever reaches Studio, and the server logsREAD-ONLY modeon startup.Plugin (runtime toggle). Click MCP ▸ Read-only in Studio. While active, the plugin refuses mutating commands no matter what the client sends. Reads still work. This is authoritative — it's enforced at the point of execution, under the control of whoever is at the Studio session.
Note:
run_luauis a read tool for gating purposes, but it can execute arbitrary Luau — including code that mutates the DataModel. Read-only mode does not sandboxrun_luau; use the plugin toggle plus your own review if you need a hard guarantee against changes.
Security notes
The bridge binds to
127.0.0.1only.Origin/Host guard (always on). The bridge rejects any request carrying an
Originheader or a non-loopbackHost— so a web page in your browser can't drive Studio viafetch()(DNS-rebinding / CSRF), while the plugin's plain localhost requests pass.Optional shared-secret token. Set
ROBLOX_STUDIO_MCP_TOKEN(or create~/.roblox-studio-mcp/token) and every plugin request must present it (checked with a constant-time compare). Give the plugin the same value once, from the Studio command bar:plugin:SetSetting("MCPToken", "your-token-here")Leave it unset (the default) and the loopback + Origin/Host guard still apply.
run_luauexecutes arbitrary Luau in the Studio/plugin context. Only connect 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 you want look-but-don't-touch access.
Version handshake
On every poll the plugin sends its version and a per-session id; the server replies with its own version and whether it has seen this session before. The plugin prints a Studio warning if the two versions disagree (so a stale plugin or server is obvious), and automatically re-registers if it detects the server was restarted underneath it.
Troubleshooting
"No Roblox Studio plugin is connected." The plugin does not auto-connect when Studio starts — click MCP ▸ Connect on the toolbar after opening your place. The button is a toggle; make sure it's active.
Edited the plugin but nothing changed? Rebuild the
.rbxmfromplugin/src/(rojo build …) and note that Studio doesn't hot-reload local plugins — fully restart Studio, then click Connect again.HTTP requests blocked. Enable Game Settings ▸ Security ▸ Allow HTTP Requests and reconnect (the plugin also tries to set this for you).
Deletes should be undoable.
delete_instancere-parents tonilinside aChangeHistoryServicerecording, 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 the MCP ▸ Read-only toggle is active in Studio, or the server was started with
ROBLOX_STUDIO_MCP_READONLYset. See Read-only mode."Version mismatch" warning in Studio output? The plugin and server builds differ — rebuild/reinstall the plugin (
rojo build …, restart Studio) or restart the server so both are the same version."Server rejected the connection (HTTP 401/403)"? A token is configured on the server but the plugin's
MCPTokensetting is missing or wrong — set it to matchROBLOX_STUDIO_MCP_TOKEN(see Security).eval_server_runtimesays "No live server peer"? No playtest is running — press Play (F5) and checkget_instancesfor aserverpeer.eval_server_runtimefails to compile / "LoadStringEnabled"? TickServerScriptService.LoadStringEnabledin the Properties panel, and make sure Allow HTTP Requests is on. See Runtime eval.
Roadmap
Done
Verified in real Studio
Selection-aware tools (operate on the current Studio selection)
Output/
printcapture forrun_luauBatch/transaction commands
Optional read-only mode
Rojo-friendly project layout for the plugin
Multi-window instance routing
Live server-VM eval during playtests (
eval_server_runtime)
Planned
Client-VM eval + multiplayer peer roles
Runtime log capture per peer
Contributing
Issues and PRs welcome. Run npm run build (server) before pushing; CI builds on
Node 20. When changing the plugin, edit the modules in plugin/src/ and rebuild
the .rbxm with rojo build plugin/default.project.json -o RobloxStudioMCP.rbxm.
License
MIT © Emir Coban
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/EmirCobanOfficial/roblox-studio-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server