Skip to main content
Glama
README.md
# Roblox Compact MCP

sooo, i wanted Grok Bot to actually do stuff in Roblox Studio without burning my free credits counting 3 modulescripts ig

This is a local MCP server + a Studio plugin. It also uses [Roblox's built-in MCP](https://create.roblox.com/docs/studio/mcp) for playtests, runtime Luau, screenshots, inputs, and whatever else your Studio version exposes.

**The whole point:** do the work inside Studio, send the bot a small answer. Asking how many `ModuleScript`s named `pepitoelmascapito123` exist should give you `{"count":3}`, not a novel about your entire game.

No AI API key. No npm dependencies. Node 22+ and Studio. Your bot/provider can still charge for tokens and tool calls, so this reduces context; it doesn't magically make their billing disappear lol

## what u get

- Exact names/classes/tags, count-only queries, scoped searches, and pagination.
- Instance properties, attributes, tags, and child counts. You pick the properties.
- Create, change, move, clone, delete, tag, and set/remove attributes in batches.
- One Undo recording per edit batch. If an edit fails, its recording is cancelled.
- Script line ranges, literal text search, unique replacements, and guarded writes that check the old source first.
- Start/stop Play, run Luau in Edit/Server/Client, and get Studio state through the official MCP.
- On-demand access to the official tool catalog: console logs, screenshots, player inputs, assets, etc. Eight compact tools are exposed initially.
- A plugin toolbar button to connect/disconnect. Multiple Studio windows get separate IDs.
- Local stdio, or authenticated JSON Streamable HTTP for a cloud bot.
- Separate random keys for the Studio plugin and the remote bot. Nothing listens outside localhost by default.

The plugin handles the structured Edit operations. Runtime code, Play, and advanced tools use the official Studio connection. You can also use all structured tools through the official connection without installing the plugin.

## let the bot set it up

Send Grok this repo and [BOT_SETUP.md](BOT_SETUP.md). Tell it to do setup **on the Windows/Mac running Studio**, not just in its cloud VM. The VM can't reach your desktop's localhost by being very confident about it.

Your app needs to support a local MCP process, or a remote MCP URL with a Bearer token. This repo doesn't assume a particular free-plan entitlement. If your Grok Bot build doesn't expose either, the bot can still operate its MCP client from your local computer when local execution is supported and enabled. It must verify which option actually exists in your app.

## local setup, aka the easy path

```powershell
git clone https://github.com/Crazy-Or-Something/roblox-compact-mcp.git
cd roblox-compact-mcp
node scripts/setup.mjs --install-plugin
```

That generates private files in `.local/`, installs `GrokCompactBridge.rbxmx` into your local Roblox Plugins folder, and backs up an older copy before replacing it. **Restart Studio yourself when you're ready.** Open your place, go to **Plugins → Grok Bridge → Connect**, and allow its localhost HTTP permission if Studio asks.

Enable the official connection too: **Assistant → … → Manage MCP Servers → Enable Studio as MCP server**. For Windows the upstream launcher is `%LOCALAPPDATA%\Roblox\mcp.bat`; for macOS it's `/Applications/RobloxStudio.app/Contents/MacOS/StudioMCP`.

For a local MCP client, merge the `Roblox_Compact` entry from `.local/mcp.local.json` into its config. That file contains the correct absolute Node and server paths for your machine. Let the client start it. Don't also run the HTTP server on the same port.

Clicking Connect doesn't start the Node process. The MCP client starts it, or you start it with the remote command below. The plugin only polls while connected in idle Edit mode; it disconnects from Edit operations during Play. Click again to disconnect.

## Grok's cloud VM

Run this **on your Studio computer**:

```powershell
node src/server.mjs --http
```

The remote MCP endpoint is `http://127.0.0.1:28761/mcp`. A cloud bot needs a tunnel or your own HTTPS reverse proxy forwarding to that local port. If you already have Cloudflare's tunnel client:

```powershell
cloudflared tunnel --url http://127.0.0.1:28761
```

Use `https://your-generated-host/mcp` as the remote MCP URL. The connector must send:

```text
Authorization: Bearer <remoteToken from .local/config.json>
```

Use your client's secret/header field, not a public repo file or a normal chat message. `bridgeToken` belongs only to the local Studio plugin. A tunnel URL alone won't grant access. HTTP replies are JSON; GET/SSE isn't provided. See [xAI's tunnel guide](https://docs.x.ai/grok/connectors/custom-mcp-tunneling) for how their cloud connections reach local servers. A quick tunnel URL changes when restarted. Keep the bridge and tunnel running while using it.

If the app only offers OAuth and can't send a Bearer header, this version won't connect through that UI. Use its local execution path or a compatible MCP client; OAuth isn't implemented here.

**Don't commit `.local/`.** The generated plugin contains your private local bridge key. Share the source under `plugin/`; everyone generates their own plugin. Don't put that generated plugin in your game's Workspace/ReplicatedStorage, publish it to the Creator Store, or upload it as a public release.

## how to not waste credits

Paste [BOT_INSTRUCTIONS.md](BOT_INSTRUCTIONS.md) into your bot's project instructions. Example calls:

```json
{"name":"roblox_studio","arguments":{"action":"list"}}
```

Pick the actual Studio ID first. Plugin IDs start with `plugin:`; official IDs don't. If both represent the same place, use the plugin ID for Edit operations and the official ID for Play/runtime. Never guess when multiple windows are open.

```json
{"name":"roblox_query","arguments":{"studio_id":"ID_FROM_LIST","name":"pepitoelmascapito123","class":"ModuleScript","count_only":true}}
```

That's one count request and a tiny reply. Counts include descendants below `root`, not the root itself. Matching is exact and case-sensitive unless `contains:true`; class names are exact. Pagination traverses current children in depth-first order; if someone edits the hierarchy between pages, query again.

```json
{"name":"roblox_query","arguments":{"studio_id":"ID_FROM_LIST","root":["ServerScriptService"],"class":"ModuleScript","limit":10}}
```

Paths are arrays of exact instance names, so names containing dots are fine. `[]` means `game`. Duplicate sibling names make a path ambiguous: the server fails instead of silently picking the wrong object. Rename/select them using explicit runtime Luau when needed.

```json
{"name":"roblox_edit","arguments":{"studio_id":"ID_FROM_LIST","operations":[{"action":"create","class":"Part","parent":["Workspace"],"name":"hello cube","as":"cube","properties":{"Anchored":true,"Position":{"type":"Vector3","values":[0,5,0]}}},{"action":"attribute","ref":"cube","name":"MadeByGrok","value":true}]}}
```

Read 80 lines by default (200 max), or request a specific range:

```json
{"name":"roblox_script","arguments":{"studio_id":"ID_FROM_LIST","action":"read","path":["ServerScriptService","Main"],"start_line":1,"line_count":40}}
```

`replace` takes `find` and `replace` and requires exactly one occurrence. `write` takes `source` and the complete `expected_source`; if it changed, the write fails. For new scripts, create an empty Script/ModuleScript with `roblox_edit`, then write with `expected_source:""`. Source changes use ScriptEditorService so open tabs are respected.

For Play: call `roblox_play start` with an **official ID**, inspect `state`, then `roblox_luau` with `datamodel_type:"Server"` or `"Client"`. Run bounded assertions and return a short result. Collect relevant logs, then call `roblox_play stop`. Arbitrary Luau doesn't get an automatic Undo recording; use structured edit tools for regular edits.

Need another tool? `roblox_advanced list` returns names. `describe` with `tool` returns one schema; `call` uses that exact schema in `arguments`. Don't invent parameters. Screenshots/audio are forwarded only when explicitly requested through advanced calls. Paid generation and upstream subagents aren't invoked automatically.

Long responses are saved locally for 10 minutes (20 results max). The first response includes `result_id`, `text`, and `next_offset`. Fetch only the needed continuation with `roblox_advanced`, `action:"result"`, `result_id`, and `offset`. `text` pages are slices of serialized JSON; concatenate them before parsing if you need the whole result. Filtering at the source is still cheaper than paging.

## status / things i actually checked

Early version. Automated checks cover authentication, session isolation, bounded query schemas, pagination reconstruction, cache limits, and timeout behavior. Read-only live checks have passed against an open Windows Studio: exact ModuleScript count, two-item hierarchy query, Studio mode, and a requested Workspace property.

Another live check passed 15 assertions on detached engine objects: typed properties, tags, exact counts, attributes/removal, clone/delete, source reads/writes/replacements, grep, duplicate-name rejection and stale-write protection. Those objects never enter the real game's DataModel. The Undo service is mocked in that check, so it doesn't prove Studio's actual rollback behavior.

Plugin toolbar behavior, actual Undo rollback, and gameplay assertions still need validation in a disposable place. A successful count doesn't prove your game's combat system works. Don't treat this as a finished Roblox QA department lol

```powershell
node --test
node scripts/probe.mjs
node scripts/check-live.mjs
node scripts/check-operations.mjs
```

`check-live` is read-only and targets the first listed Studio for smoke checks; it doesn't edit anything or start Play. Production tools always require explicit IDs. Node/protocol tests use a fake plugin; they don't touch your game.

Overrides: `ROBLOX_PORT`, `ROBLOX_BRIDGE_TOKEN`, `ROBLOX_REMOTE_TOKEN`, `ROBLOX_MCP_COMMAND`, `ROBLOX_MCP_ARGS` (JSON array). Configure an alternate port **before** generating the plugin; its endpoint is baked into the generated file. `ROBLOX_ALLOWED_ORIGINS` is a comma-separated explicit Origin allowlist for browser-based clients. Requests without Origin work for normal MCP clients.

There's no auto-retry for edits. If a dispatched request times out, it may have run: inspect before trying it again. One process owns the local port; stdio and HTTP modes can't both use it at once. API keys, local place contents and raw tool results don't go into analytics because there aren't any.

## license

MIT. Unofficial project, not affiliated with Roblox or xAI. The official Studio MCP remains Roblox's own thing; no upstream code was copied into this repo.

TDQS

B3.4/5.0

Scored across 8 tools

Disambiguation4/5

The eight tools have distinct primary purposes: session management, instance search, instance inspection, batch editing, script manipulation, Luau execution, playtest control, and an advanced gateway. Minor potential overlap exists between roblox_advanced and tools like roblox_studio and roblox_luau, but the descriptions provide clear usage contexts.

Naming Consistency4/5

All tools use the consistent 'roblox_' prefix with lowercase snake_case, making them easily identifiable as a set. The suffixes mix nouns and verbs (e.g., studio, query, get, edit), but the format is uniform and each name clearly conveys its purpose.

Tool Count5/5

Eight tools is well-scoped for a compact MCP covering Roblox Studio interactions, offering a focused set without redundancy. Each tool addresses a distinct operational area, and the count falls comfortably within the ideal 3–15 range.

Completeness5/5

The toolset covers the essential lifecycle: connecting to sessions, querying and inspecting instances, editing them, reading/writing scripts, executing Luau, managing playtests, and accessing additional official tools via the advanced gateway. No obvious critical gaps exist for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues