studio-plugin — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited studio-plugin (Plugin) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
<div align="center">
Claude · Gemini · Codex · OpenCode · any MCP client
Quick Start · Studio CLI · Features · Tools · Client Setup · Docs
</div>
An MCP server that connects AI assistants directly to a running Roblox Studio session. Read the instance tree, edit Luau scripts, set properties, manage attributes & tags, create objects, detect drift, and sync source files — all over a 100% local connection that never leaves your machine.
Your AI MCP Server Studio Plugin
┌──────────┐ stdio ┌──────────────┐ HTTP ┌──────────────┐
│ Claude │◄──────────►│ Node.js │◄─────────►│ Luau │
│ Gemini │ │ port 3002 │ localhost │ polls every │
│ Codex │ │ │ │ 500ms │
└──────────┘ └──────┬───────┘ └──────────────┘
│
┌──────┴───────┐
│ Blueprint V1 │
│ Rojo sync │
└──────────────┘## 🤖 Using an AI Agent? (Claude Code, Cursor, etc.)
>
Let your AI agent set everything up for you!
>
### Option 1: Copy-Paste This Prompt
>
Just copy and paste this to your AI agent:
>
`` Go to https://raw.githubusercontent.com/aaronaalmendarez/roblox-mcp/main/AGENT.md Read the entire file, then set up the Roblox Studio MCP on my machine. Follow all the installation steps in the "Complete Installation Guide" section. ``>
Your agent will handle cloning, building, configuring MCP, and setting up Blueprint V1 automatically.
>
### Option 2: Manual
>
- [📖 View AGENT.md on GitHub](https://github.com/aaronaalmendarez/roblox-mcp/blob/main/AGENT.md) - [📋 View Raw](https://raw.githubusercontent.com/aaronaalmendarez/roblox-mcp/main/AGENT.md) (copy all)
1 → Install the Studio plugin (download .rbxmx) into your plugins folder
| OS | Path |
|---|---|
| Windows | %LOCALAPPDATA%\Roblox\Plugins\ |
| macOS | ~/Documents/Roblox/Plugins/ |
2 → In Studio: Game Settings → Security → Allow HTTP Requests ✅
3 → Start the server:
# From this repo
npm install && npm run build
node dist/index.js4 → Configure your AI client (see Client Setup) — done!
<details> <summary><b>Build plugin from source</b></summary>
npm run build:plugin
# Copy studio-plugin/MCPPlugin.rbxmx → plugins folder → restart Studio</details>
| Category | What You Can Do | |
|---|---|---|
| 📂 | Instance Hierarchy | Browse game tree, search by name / class / content, list services |
| 📝 | Script Management | Read, write, line-edit Luau scripts with range support |
| ⚡ | Batch Editing | Atomic multi-op edits with SHA-256 hash checks + auto-rollback |
| 🔩 | Properties | Get/set any property, mass ops, formula & relative calculations |
| 🏗️ | Object Lifecycle | Create, delete, smart-duplicate with offset grids & variations |
| 🏷️ | Attributes & Tags | Full CRUD for attributes + CollectionService tags |
| 🩺 | Diagnostics | Drift detection, deprecated API lint, health endpoints, telemetry |
| 💾 | Snapshots | In-memory script snapshots with instant rollback |
X-Idempotency-Keyset_property on SourceOne command to rule them all. A zero-dependency, cross-terminal orchestrator for your entire Roblox workflow.
____ ____ ____________ ______________ _____
/ __ \/ __ )/ ____/ __ \/ _/ ____/ __ \/ ___/
/ /_/ / __ / / / / / // // / / / / /\__ \
/ _, _/ /_/ / /___/ /_/ // // /___/ /_/ /___/ /
/_/ |_/_____/\____/_____/___/\____/_____//____/# One command starts everything
npm run studio -- dev
# Or pick your services
npm run studio -- mcp # MCP server only
npm run studio -- serve # Rojo server only
npm run studio -- dev --verbose # See all process output| Feature | Description |
|---|---|
| Process Orchestration | Spawns MCP + Rojo + Watchers + Reverse Sync with a single command |
| PID Tracking | Every process gets a PID file in .studio-cli/pids/ — no orphaned node.exe processes |
| Cross-Platform Kill | taskkill /T on Windows, SIGTERM on Unix — tree-wide shutdown, no zombies |
| Log Capture | Background processes write timestamped logs to .studio-cli/logs/ |
| Health Checks | Auto-verifies MCP (HTTP) and Rojo (TCP socket) before reporting "ready" |
| Place Context | Auto-resolves active place, detected Studio place, or legacy fallback |
| Beautiful UI | ANSI boxes, spinners, tables, and status indicators — works in any terminal |
studio dev # Full environment (MCP + Rojo + Watch + Reverse)
studio serve # Rojo server only
studio mcp # MCP server only
studio sync # Blueprint property sync once
studio watch # Property file watcher
studio build # Build .rbxl via Rojo
studio lint # Luau lint
studio place list # Places table with active marker
studio place status # Resolved context box
studio place use <key> # Switch active place
studio place detect # Auto-register current Studio place
studio status # System dashboard with health checks
studio stop [name] # Kill tracked process(es)
studio transcribe # Whisper transcription
studio doctor # Blueprint doctor
studio version # Version + logo--place <key> # Target place (slug / id / name)
--verbose # Show process output
--json # Machine-readable output
--dry-run # Preview changes without applyingcheck_script_drift now normalizes line endings, BOM, trailing whitespace, and trailing final newlines by default.comparisonMode, formattingOnly, formattingDifferences, raw hashes/lengths, and normalized hashes/lengths.scripts/push-script-fast.mjs for large files; they commit through the plugin bridge and UpdateSourceAsync instead of set_property.set_property and mass_set_property now reject the Source property so escape sequences are not corrupted.3002 first and keep 58741 only as a legacy fallback.Example of the expected healthy case:
{
"status": "in-sync",
"comparisonMode": "canonical-text",
"formattingOnly": true,
"formattingDifferences": ["trailing-newline"],
"rawLocalLength": 74884,
"rawStudioLength": 74883,
"normalizedLocalLength": 74883,
"normalizedStudioLength": 74883
}This means the raw bytes differ, but the actual script content is the same.
A real healthy verification case now looks like this:
localLength: 74883, studioLength: 74883rawLocalLength: 74884, rawStudioLength: 74883in-synccomparisonMode, formattingOnly, formattingDifferences, and raw vs normalized hashes/lengths<details> <summary><b>📂 Instance Hierarchy</b> — 9 tools</summary>
| Tool | Description |
|---|---|
get_file_tree | Instance hierarchy as a tree |
search_files | Search by name, class, or script content |
get_services | List Roblox services and children |
search_objects | Find by name, class, or property |
get_project_structure | Full game hierarchy (configurable depth) |
get_instance_children | Children + class types |
get_class_info | Properties/methods for any class |
get_place_info | Place ID, name, game settings |
get_selection | Currently selected objects |
</details>
<details> <summary><b>📝 Script Management</b> — 15 tools</summary>
| Tool | Description |
|---|---|
get_script_source | Read source (optional line range, full reads safe for large scripts) |
get_script_snapshot | Source + SHA-256 hash with full-source recovery |
set_script_source | Full rewrite (editor-safe; use chunked upload for very large files) |
begin_script_source_upload | Start chunked upload session for large files |
append_script_source_upload_chunk | Append one chunk to an upload session |
commit_script_source_upload | Commit an uploaded script through the plugin bridge |
cancel_script_source_upload | Discard an upload session without writing |
set_script_source_checked | Write only if hash matches |
set_script_source_fast | Fast write with safe bridge fallback |
set_script_source_fast_gzip | Gzip-compressed fast write |
edit_script_lines | Replace line ranges |
insert_script_lines | Insert at position |
delete_script_lines | Delete line ranges |
batch_script_edits | Atomic multi-edit + rollback |
apply_and_verify_script_source | Apply → verify → rollback pipeline |
</details>
<details> <summary><b>💾 Snapshots & Safety</b> — 4 tools</summary>
| Tool | Description |
|---|---|
create_script_snapshot | In-memory rollback point |
list_script_snapshots | List session snapshots |
rollback_script_snapshot | Restore from snapshot |
cancel_pending_writes | Cancel queued writes |
</details>
<details> <summary><b>🔩 Properties & Objects</b> — 14 tools</summary>
| Tool | Description |
|---|---|
get_instance_properties | All properties of an instance |
set_property | Set any property except Source |
mass_set_property | Set on multiple instances except Source |
mass_get_property | Read from multiple instances |
search_by_property | Find by property value |
set_calculated_property | Formula-based property sets |
set_relative_property | Relative modifications |
create_object | Create instance |
create_object_with_properties | Create with initial props |
mass_create_objects | Batch create |
mass_create_objects_with_properties | Batch create with props |
delete_object | Delete instance |
smart_duplicate | Smart dup with offsets & variations |
mass_duplicate | Multiple smart dups at once |
</details>
<details> <summary><b>🏷️ Attributes & Tags</b> — 7 tools</summary>
| Tool | Description |
|---|---|
get_attribute / set_attribute | Read/write single attribute |
get_attributes | All attributes on instance |
delete_attribute | Remove attribute |
get_tags | CollectionService tags |
add_tag / remove_tag | Add or remove tag |
get_tagged | All instances with a tag |
</details>
<details> <summary><b>🩺 Diagnostics</b> — 4 tools</summary>
| Tool | Description |
|---|---|
get_runtime_state | Write queue + bridge telemetry |
get_diagnostics | Full diagnostic report |
check_script_drift | Local vs Studio drift check with formatting normalization and raw/normalized diagnostics |
lint_deprecated_apis | Deprecated API scanner |
</details>
All configs point to the local build. Replace the path with your actual install location.
<details open> <summary><b>Claude Code</b></summary>
claude mcp add robloxstudio -- node /path/to/roblox-mcp/dist/index.js</details>
<details> <summary><b>Gemini CLI</b></summary>
gemini mcp add robloxstudio node --trust -- /path/to/roblox-mcp/dist/index.js</details>
<details> <summary><b>Claude Desktop / Generic JSON</b></summary>
{
"mcpServers": {
"robloxstudio-mcp": {
"command": "node",
"args": ["/path/to/roblox-mcp/dist/index.js"]
}
}
}</details>
<details> <summary><b>Codex CLI</b></summary>
~/.codex/config.toml:
[mcp_servers.robloxstudio]
command = "node"
args = ["/path/to/roblox-mcp/dist/index.js"]</details>
<details> <summary><b>OpenCode</b></summary>
~/.config/opencode/opencode.json:
{
"mcp": {
"robloxstudio": {
"type": "local",
"enabled": true,
"command": ["node", "/path/to/roblox-mcp/dist/index.js"]
}
}
}</details>
<details> <summary><b>Published npm package</b></summary>
If using the published package instead of a local build:
npx -y @aaronalm19/roblox-mcp@latest</details>
Full reference with Windows fallbacks: docs/CLIENTS.md
IDE-first source control built on Rojo with multi-place support.
blueprint-v1/
├── places/
│ ├── registry.json # Place ID → slug
│ ├── .active-place.json # Active context
│ └── <slug>/
│ ├── default.project.json
│ ├── src/ # Luau source
│ └── properties/
│ └── instances.json # Non-script props
└── src/ # Legacy fallbacknpm run place:detect # Auto-detect Studio place
npm run place:list # List registered places
npm run blueprint:sync # Property sync → Studio
npm run blueprint:watch # Continuous sync
npm run blueprint:reverse-sync # Pull Studio → local
npm run drift:check # Detect file divergence
npm run luau:lint # Static analysis (requires luau-lsp, see below)npm run luau:lint uses luau-lsp which ships full Roblox type stubs — Player, BasePart, Vector3, RemoteEvent, etc. all resolve correctly under --!strict.
One-time setup:
Option A — use the bundled binary (already in this repo, v1.63.0 Windows x64):
Expand-Archive tools\luau-lsp\luau-lsp-win64.zip -DestinationPath .tools\luau-lsp -ForceOption B — download the latest release for your platform:
luau-lsp-win64.zip (Windows), luau-lsp-macos.zip (macOS), or luau-lsp-linux.zip (Linux)luau-lsp[.exe] → .tools/luau-lsp/luau-lsp.exeThen generate the Rojo sourcemap:
rojo sourcemap blueprint-v1/places/<slug>/default.project.json --output sourcemap.jsonRun:
npm run luau:lint # findings=0 is the goal
npm run luau:lint:strict # exits non-zero if any findings (use in CI)Expected clean output:
[context] Place1 (125175608517936) [place1-2]
[luau-lint] files=4 analyzer=.tools/luau-lsp/luau-lsp.exe
[luau-lint] sourcemap=sourcemap.json
[luau-lint] findings=0Deep dive: docs/BLUEPRINT_V1.md
Everything below is the strict operational guide for working with Blueprint V1. Follow these steps exactly.
Open a terminal in the repo root and run each step in order:
# Step 1 — Detect the place open in Studio and register it
npm run place:detect
# Step 2 — Confirm resolved paths
npm run place:status
# Step 3 — Start Rojo against the resolved project
rojo serve blueprint-v1/places/<slug>/default.project.json
# Step 4 — Start continuous property sync (separate terminal)
npm run blueprint:watch
# Step 5 — Start reverse sync guard (separate terminal)
npm run blueprint:reverse-syncOr use the one-command launcher that does steps 3-5 automatically:
npm run studio -- dev --place <slug>Or the legacy orchestrator:
npm run dev:studio -- --place <slug>This resolves the existing place context, then spawns the MCP server, Rojo, property watcher, and reverse-sync in parallel. It does not runplace:detect— you must register the place first. PressCtrl+Cto stop all.
| Tool | Required | Install (Windows) |
|---|---|---|
| Node.js ≥ 18 | ✅ Yes | winget install OpenJS.NodeJS.LTS or nodejs.org |
| Rojo | ✅ Yes (for script sync) | cargo install rojo — or download binary and add to PATH |
| Luau CLI | Optional (for lint) | npm run luau:install (auto-downloads from luau-lang releases) |
<details> <summary><b>No winget or cargo?</b></summary>
.msi installer from nodejs.org/en/downloadrojo.exe from GitHub releases, place in a folder on your PATHnpm run luau:install handles this — it downloads the correct binary for your OS into .tools/</details>
`npm run place:detect`
✔ Detected place: Place2 (136131439760483)
✔ Registered slug: place2
✔ Set as active place`npm run place:status`
Mode: place
Place: Place2 (136131439760483)
Slug: place2
Project: blueprint-v1/places/place2/default.project.json
Source: blueprint-v1/places/place2/src
Properties: blueprint-v1/places/place2/properties/instances.json`rojo serve ...`
Rojo server listening on port 34872`http://localhost:3002/health`
{
"pluginConnected": true,
"mcpServerActive": true,
"plugin": { "version": "1.10.0" }
}`npm run blueprint:reverse-sync`
Reverse sync active. Tracked scripts: 3
Polling every 2000ms...If any output differs from the above, stop and consult the Troubleshooting table.
| Situation | Who Wins | Action |
|---|---|---|
You edited a .luau file locally | Local wins | Rojo pushes to Studio automatically |
| You edited a script inside Studio | Studio wins | Run npm run blueprint:reverse-sync to pull changes back |
| Both sides changed the same script | Neither — conflict | A conflict snapshot folder is written; you manually merge |
| Non-script property changed in Studio | Studio wins | No automated pull — manually update instances.json to match |
Non-script property changed in instances.json | Local wins | Run npm run blueprint:sync to push to Studio |
| You aren't sure what changed | Check first | Run npm run drift:check to compare hashes |
Golden rule: Edit scripts in your IDE (Rojo syncs them). Edit non-script properties via instances.json. Only reverse-sync when you intentionally made Studio-side script changes.
Rojo uses file suffixes to determine the script type and instance name:
| File Suffix | Script Type | Instance Name |
|---|---|---|
.server.luau | Script (runs on server) | Filename without suffix |
.client.luau | LocalScript (runs on client) | Filename without suffix |
.module.luau | ModuleScript (shared) | Filename without suffix |
.luau (no suffix) | ModuleScript | Full filename |
Path resolution example:
File: blueprint-v1/places/place2/src/ServerScriptService/HorrorMain.server.luau
Instance: game.ServerScriptService.HorrorMain (Script)
File: blueprint-v1/places/place2/src/StarterPlayer/StarterPlayerScripts/HorrorClient.client.luau
Instance: game.StarterPlayer.StarterPlayerScripts.HorrorClient (LocalScript)
File: blueprint-v1/places/place2/src/ReplicatedStorage/HorrorConfig.module.luau
Instance: game.ReplicatedStorage.HorrorConfig (ModuleScript)The directory path undersrc/maps directly to the Roblox service hierarchy. Thedefault.project.jsondefines which directories map to which services.
When reverse-sync detects both local and Studio changed the same script, it writes a conflict snapshot folder instead of overwriting:
blueprint-v1/places/<slug>/.reverse-sync-conflicts/
└── ServerScriptService/
└── HorrorMain.server.luau/
├── local.luau # Your local version at time of conflict
├── studio.luau # The Studio version that diverged
└── meta.json # Timestamps, hashes, instance pathRecovery workflow:
.reverse-sync-conflicts/ServerScriptService/HorrorMain.server.luau/)local.luau (your version) vs studio.luau (Studio's version).luau file in src/npm run blueprint:reverse-sync again — it will re-baseline from the merged fileState tracking: Each tracked script's hashes are stored in:
blueprint-v1/places/<slug>/.reverse-sync-state.jsonThis file contains lastLocalHash and lastStudioHash per script. If you need to force a full re-sync, delete this file and restart reverse-sync.
| Failure | Cause | Fix |
|---|---|---|
| Place resolves wrong slug | .active-place.json points to old place | npm run place:detect (re-detects from Studio) |
rojo: command not found | Rojo not installed or not on PATH | Install via cargo install rojo or download binary |
| Module path mismatch | File in wrong src/ subdirectory | Match directory to Roblox service name exactly |
| HTTP 403 from plugin | HTTP requests disabled in Studio | Game Settings → Security → Allow HTTP Requests |
Stale .active-place.json | Switched Studio places without re-detecting | npm run place:detect |
ECONNREFUSED :3002 | MCP server not running | npm run studio -- dev or npm run dev:studio |
| Reverse-sync shows 0 tracked | No scripts match Rojo mappings | Verify files exist in resolved src/ path |
| Rojo sync not updating Studio | Rojo serving wrong project file | Check npm run place:status for correct project path |
| Lint says "luau-analyze not found" | Luau CLI not installed | npm run luau:install |
blueprint:doctor fails | Server or plugin not connected | Start server, open Studio, enable plugin |
The Studio CLI is the recommended daily driver:
npm run studio -- dev --place place2Or use the legacy orchestrator:
npm run dev:studio -- --place place2Both start all four services in parallel:
| Service | What It Does |
|---|---|
| MCP server | node dist/index.js |
| Rojo | rojo serve blueprint-v1/places/place2/default.project.json |
| Property watcher | Continuous blueprint:watch for non-script sync |
| Reverse sync | Guarded Studio → local pull |
The CLI adds PID tracking, health checks, log capture, and a beautiful terminal UI:
✓ Resolved: Place2 [place2]
✓ MCP server listening on port 3002
✓ Rojo server listening on port 34872
✓ Property watcher active
✓ Reverse sync active
┌─────────────────────────────────────────────────────────────────────────┐
│ Dev Environment │
│ Project: blueprint-v1\places\place2\default.project.json │
│ Mode: place │
│ Studio: ● Press Ctrl+C to stop all │
└─────────────────────────────────────────────────────────────────────────┘CLI flags:--no-rojo,--no-watch,--no-reverseto disable individual services.--verboseto see process output.
Blueprint separates concerns cleanly between two systems:
| What | Managed By | Files |
|---|---|---|
| Scripts (Luau code) | Rojo | .server.luau, .client.luau, .module.luau in src/ |
| Non-script properties (Position, Size, Color, etc.) | Sync scripts | properties/instances.json |
| Attributes | Sync scripts | properties/instances.json (attributes field) |
| Tags | Sync scripts | properties/instances.json (tags field) |
| Instance creation / hierarchy | Rojo (via default.project.json) | default.project.json tree |
Do NOT:
.luau files through instances.json — Rojo handles scriptsdefault.project.jsonblueprint-v1/src/ with place-specific blueprint-v1/places/<slug>/src/place2)A complete real-world place from this repository:
blueprint-v1/places/place2/
├── default.project.json # Rojo project mapping 12 services
├── .reverse-sync-state.json # Tracks 3 scripts with SHA-256 hashes
├── .reverse-sync-conflicts/ # Empty (no conflicts currently)
├── src/
│ ├── ServerScriptService/
│ │ └── HorrorMain.server.luau # → game.ServerScriptService.HorrorMain (Script)
│ ├── ReplicatedStorage/
│ │ └── HorrorConfig.module.luau # → game.ReplicatedStorage.HorrorConfig (ModuleScript)
│ └── StarterPlayer/
│ └── StarterPlayerScripts/
│ └── HorrorClient.client.luau # → game.StarterPlayer.StarterPlayerScripts.HorrorClient (LocalScript)
└── properties/
├── instances.json # Non-script property manifest (empty for now)
└── schema.json # Property schema definitionsRegistry entry (blueprint-v1/places/registry.json):
{
"136131439760483": {
"placeId": 136131439760483,
"gameId": 9708597637,
"slug": "place2",
"displayName": "Place2"
}
}Full workflow for this place:
# One-time: detect and register
npm run place:detect
# → ✔ Detected place: Place2 (136131439760483), slug: place2
# Daily: start everything
npm run studio -- dev --place place2
# → MCP server, Rojo, property watcher, and reverse sync all running
# Or the legacy orchestrator:
npm run dev:studio -- --place place2
# Or manually:
rojo serve blueprint-v1/places/place2/default.project.json
npm run blueprint:watch
npm run blueprint:reverse-syncnpm install # Dependencies
npm run build # TypeScript → dist/
npm run build:plugin # Build .rbxmx plugin
npm run dev # Dev server (tsx hot reload)
npm run typecheck # Type-check
npm test # Jest suite
npm run test:all # Jest + Luau E2Ecurl http://localhost:3002/health
curl http://localhost:3002/diagnostics├── src/ # TypeScript MCP server
│ ├── index.ts # Tool definitions + handler
│ ├── http-server.ts # Express bridge (:3002)
│ ├── bridge-service.ts # Plugin comms
│ └── tools/ # Tool implementations
├── studio-plugin/ # Luau Studio plugin
├── blueprint-v1/ # Rojo projects + sync state
├── scripts/ # 20+ CLI helpers
├── tests/ # Jest + Luau E2E
└── docs/ # Additional docs| Local-only | All traffic stays on localhost:3002 |
| No telemetry | Zero data collection — your projects are private |
| Explicit | Tools only run when your AI invokes them |
| Separated | Read and write operations are distinct |
| Problem | Fix |
|---|---|
| Plugin missing | .rbxmx in plugins folder → restart Studio |
| HTTP 403 | Game Settings → Security → Allow HTTP Requests |
| Disconnected | Start the MCP server — red is normal until then |
| No tools | Restart MCP client + Studio, check /health |
| Slow or large writes | Use chunked upload tools or push-script-fast.mjs |
| Firewall | Allow localhost:3002 |
| Client Configurations | Setup for every MCP client |
| Blueprint V1 Guide | Multi-place sync deep dive |
| Plugin Installation | Detailed plugin setup |
git clone https://github.com/aaronaalmendarez/roblox-mcp.git
cd roblox-mcp
npm install
npm run devIssues and PRs welcome on GitHub.
Original project: boshyxd/robloxstudio-mcp This fork extends that foundation for multi-agent workflows, local blueprint-first development, and enhanced tooling.
<div align="center">
[MIT License](LICENSE) © 2025
</div>
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.