Studio Sidecar

io.github.gjlmotea由 GitHub 账号 gjlmotea 发布v0.1.1更新于 Nov 2, 2026

Companion to Roblox Studio's built-in MCP: open places, guard writes, Open Cloud asset uploads.

概览

AI 生成的概览

Roblox Studio 的配套 MCP 服务器,可打开未打开的场景、按 PlaceId 保护写入、提取脚本并通过 Open Cloud 上传资源。

可以做什么
  • 查看 Studio 进程与 StudioMCP broker,打开尚未打开的场景,并清理残留的 stub
  • 在强制 PlaceId 校验下运行 Luau,运行时错误以可读值返回,并可在 100k 输出上限之外按字节校验提取场景脚本
  • 通过 Roblox Open Cloud 读取、上传、更新和归档资源,并在更新被去重时给出明确结论
  • 报告状态,包括 Open Cloud 是否已配置以及上传可读取哪些文件夹
适用场景
适合与 Roblox Studio 内置 MCP 服务器配合使用:需要打开尚未运行的游戏、避免误覆盖其他游戏、导出超过内置输出上限的脚本快照,或通过 Open Cloud 管理资源时。脚本编辑、场景查询、截图和试玩刻意留给内置服务器。
运行要求
以 npx 启动的本地 stdio 进程,需要 Node.js 22 或更高版本,以及已开启“Enable Studio as MCP server”的 Roblox Studio。可选环境变量:ROBLOX_API_KEY(具备 Asset 读写权限的 Open Cloud 密钥,仅四个 Open Cloud 工具需要)、ROBLOX_CREATOR_USER_ID、ROBLOX_UPLOAD_ROOT(未设置时拒绝所有上传)和 ROBLOX_STUDIO_PATH。Windows 已完整验证,macOS 部分验证,Linux 会被明确拒绝。
安装前请注意
ROBLOX_API_KEY 是可写入的 Open Cloud 凭据,不要放进受版本控制的配置文件。上传会把本地文件发送到 Roblox,模型上传无法撤销,因此用 ROBLOX_UPLOAD_ROOT 限定可读取的文件夹。工具可打开 Studio 进程、终止 stub,并写入或归档资源。roblox_luau_safe 不是沙箱:运行时错误之前所做的更改不会回滚。macOS 上窗口标题需要辅助功能权限。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Studio Sidecar,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

Studio Sidecar — companion MCP server for Roblox Studio

English | 繁體中文

[Studio Sidecar: a companion MCP server for Roblox Studio]

Studio Sidecar fills the gaps in Roblox Studio's built-in MCP server. It does not replace it.

Since 2026, Roblox Studio ships its own MCP server (StudioMCP, installed with Studio, stdio transport), and the standalone Roblox/studio-rust-mcp-server repository has been archived. The built-in server has tools that no third party can rebuild: generate_mesh, generate_material and generate_procedural_model use Roblox's cloud AI with moderation; search_asset, insert_asset and upload_image carry your signed-in inventory permissions; user_keyboard_input and user_mouse_input go through CreateVirtualInput. Replacing it would throw all of that away.

So Studio Sidecar adds only what the built-in server structurally cannot do, and leaves everything else to it:

  • Open a place that is not open yet, and see every Studio process and StudioMCP broker/stub on the machine.
  • Guard writes by PlaceId, run Luau with runtime errors returned as readable values and output never truncated, and extract scripts past the 100k output limit with byte-exact verification.
  • Upload, update and archive assets through Roblox Open Cloud, with an explicit verdict when an update was silently deduplicated.

No Studio plugin is needed. 11 tools; Windows fully verified, macOS partly (see Platform support).

Contents

Tools

ToolWhy the built-in server cannot do it
roblox_get_statusShows whether Open Cloud is configured and which folders uploads may read from
roblox_list_studiosThe built-in server lives inside one Studio process; it cannot see other Studio processes or the StudioMCP broker/stub structure
roblox_open_placeOpening a place that is not open yet is an operating-system job: it needs a new sibling process
roblox_cleanup_stubsSame reason; reports by default and only terminates stubs with apply=true, never the broker
roblox_place_guardThe built-in multi_edit writes by path, and a wrong studio_id silently overwrites another game with no undo or warning
roblox_luau_safeThe built-in execute_luau has no PlaceId check and silently truncates output beyond 100k characters
roblox_extract_placeThere is no tool that handles the 100k truncation boundary: chunking, sentinels and per-file byte checks are left to the caller, and getting them wrong does not raise an error
roblox_get_assetThe built-in search_asset reports the holder as creatorId, not the author
roblox_upload_assetAssetService:CreateAssetAsync inside Studio currently always throws not available yet
roblox_update_assetCreateAssetVersionAsync is unavailable in the same way
roblox_archive_assetThe built-in server has no Open Cloud surface

Deliberately not included: script reading and writing, scene queries, execute_luau, screen_capture, playtesting. The built-in server already has them, and a second implementation would only drift.

How Luau runs on the Studio side

An MCP process can be a server and a client at the same time. Studio Sidecar starts StudioMCP as its own child process and connects to it as an MCP client, which gives it the built-in execute_luau. The chunking, sentinel and byte-check machinery is then wrapped into single tools. When the client closes normally, the child process goes away too, so this route does not leak stubs.

To start StudioMCP, Studio Sidecar uses the same launcher as Roblox's setup guide. On Windows that is %LOCALAPPDATA%\Roblox\mcp.bat, run through cmd.exe /c; when mcp.bat is missing, it falls back to the most recently modified StudioMCP.exe under %LOCALAPPDATA%\Roblox\Versions\. On macOS it uses StudioMCP inside RobloxStudio.app, in /Applications or ~/Applications.

Quick start

Requirements: Roblox Studio with Enable Studio as MCP server turned on (in Studio: Assistant → … → Manage MCP Servers), and Node.js 22 or newer.

Register Studio Sidecar with your AI client. Claude Code:

bash
claude mcp add studio-sidecar --scope user \  -e ROBLOX_API_KEY="<your Open Cloud API key>" \  -e ROBLOX_CREATOR_USER_ID="<your Roblox user ID>" \  -e ROBLOX_UPLOAD_ROOT="<folder that uploads may read from>" \  -- npx -y @gjlmotea/studio-sidecar-mcp

Clients that read an mcpServers JSON config (Claude Desktop, Cursor, Gemini CLI):

json
{  "mcpServers": {    "studio-sidecar": {      "command": "npx",      "args": ["-y", "@gjlmotea/studio-sidecar-mcp"],      "env": {        "ROBLOX_API_KEY": "<your Open Cloud API key>",        "ROBLOX_CREATOR_USER_ID": "<your Roblox user ID>",        "ROBLOX_UPLOAD_ROOT": "<folder that uploads may read from>"      }    }  }}

VS Code uses a top-level servers key instead, and each server has a type. An input variable keeps the key out of the file: VS Code asks for it the first time the server starts and stores it securely.

json
{  "inputs": [    {      "type": "promptString",      "id": "roblox-api-key",      "description": "Roblox Open Cloud API key",      "password": true    }  ],  "servers": {    "studio-sidecar": {      "type": "stdio",      "command": "npx",      "args": ["-y", "@gjlmotea/studio-sidecar-mcp"],      "env": {        "ROBLOX_API_KEY": "${input:roblox-api-key}",        "ROBLOX_CREATOR_USER_ID": "<your Roblox user ID>",        "ROBLOX_UPLOAD_ROOT": "<folder that uploads may read from>"      }    }  }}

Where each client keeps the file:

ClientConfig file
Claude DesktopmacOS: ~/Library/Application Support/Claude/claude_desktop_config.json; Windows: %APPDATA%\Claude\claude_desktop_config.json. Settings → Developer → Edit Config opens it
Cursor~/.cursor/mcp.json for every project, .cursor/mcp.json inside one project
Gemini CLI~/.gemini/settings.json (user), .gemini/settings.json (project)
VS Code.vscode/mcp.json in the workspace, or the user mcp.json that the MCP: Open User Configuration command opens

Sources: Claude Desktop, Cursor, Gemini CLI, VS Code.

All three variables are optional: without the key the four Open Cloud tools refuse, without ROBLOX_UPLOAD_ROOT every upload is refused, and the other tools work as usual. Do not put the key in any config file that is tracked by version control.

Register Studio's built-in MCP server as well; script editing, screenshots and playtesting live there. The launch command on each platform, StudioMCP on macOS and cmd.exe /c %LOCALAPPDATA%\Roblox\mcp.bat on Windows, comes from Roblox's setup guide.

macOS:

bash
claude mcp add roblox-studio --scope user -- /Applications/RobloxStudio.app/Contents/MacOS/StudioMCP

Windows:

bat
claude mcp add roblox-studio --scope user -- cmd.exe /c "%LOCALAPPDATA%\Roblox\mcp.bat"

In a JSON config, the macOS entry is "command": "/Applications/RobloxStudio.app/Contents/MacOS/StudioMCP", and the Windows entry is "command": "cmd.exe" with "args": ["/c", "%LOCALAPPDATA%\\Roblox\\mcp.bat"]. Register mcp.bat rather than StudioMCP.exe on Windows: mcp.bat keeps its path, while StudioMCP.exe lives in %LOCALAPPDATA%\Roblox\Versions\version-<hash>\, a folder that changes with Studio updates.

Restart the session after registering, then call roblox_get_status and check openCloudConfigured and allowedUploadRoots.

Desktop apps started from Finder or the Start menu often do not see your shell's PATH; if the server does not start, use the absolute path of npx (or of node when running from source).

Running from source

bash
git clone https://github.com/gjlmotea/studio-sidecar-mcp.gitcd studio-sidecar-mcpcorepack pnpm install --frozen-lockfilecorepack pnpm run buildclaude mcp add studio-sidecar --scope user -- "$(which node)" "$PWD/dist/index.js"

Register with absolute paths, as above, rather than a hand-written .mcp.json that points at the project folder. Claude Code sets CLAUDE_PROJECT_DIR in the server's environment, not in its own, so a ${CLAUDE_PROJECT_DIR} reference in the command or args of a .mcp.json entry, or of a local- or user-scope entry in ~/.claude.json, needs a default such as ${CLAUDE_PROJECT_DIR:-.}. Without a default the entry still loads, claude mcp list shows a missing-variable warning, and the unexpanded ${CLAUDE_PROJECT_DIR} text is used as is. Project-scope servers from .mcp.json also need approval: interactive sessions ask before using them, while claude -p runs, Agent SDK sessions and cloud sessions load them without asking. See the Claude Code MCP documentation.

Configuration

VariablePurpose
ROBLOX_API_KEYOpen Cloud API key with Asset read and write (Creator Dashboard → Open Cloud API Keys)
ROBLOX_CREATOR_USER_IDCreator recorded in creationContext when uploading
ROBLOX_UPLOAD_ROOTFolders uploads may read from. Several are allowed, joined with the platform's path delimiter: ; on Windows (C:\assets\models;D:\decals), : on macOS (/Users/you/models:/Volumes/art/decals). When unset, every upload is refused
ROBLOX_STUDIO_PATHStudio executable; resolved automatically from the current installation when unset

ROBLOX_UPLOAD_ROOT is a deliberate guard rail: uploading sends a local file to an external service, and for Models it cannot be undone, so the MCP client may not pick arbitrary file paths. Both the file and the allowed folders are resolved with realpath before they are compared, so a symlink inside an allowed folder cannot point the upload elsewhere. The file is opened once, checked again through that open handle (still inside an allowed folder, still the same file), and the bytes that go out are read from that handle, so swapping the path after the check changes nothing. A file with more than one hard link is refused, because a hard link inside an allowed folder can be a file from anywhere else on the same volume; upload a copy instead.

What the tools know that the built-in server does not tell you

Each of these was found on real hardware and has a regression test.

Launch intent quoting fails silently. Studio expects -launchIntentString "{\"task\":\"EditPlace\",\"universeid\":\"…\",\"placeid\":\"…\"}". Unescaped quotes are eaten by the Windows command-line parser, Studio receives broken JSON, ignores the intent and opens an empty session with no error. The only clue is a window title of Roblox Studio instead of <place name> - Roblox Studio. Studio Sidecar passes arguments to spawn as an array and makes the CommandLineToArgvW rules testable. universeid is the gameId in scene/place.json.

Never steal window focus. Two seconds after SetForegroundWindow, a stray key press in Studio closed the place that had just loaded. roblox_open_place only polls the broker connection and process titles.

The broker must not be killed, and its identity moves. StudioMCP is one broker plus one stub per client; the first one listens on 127.0.0.1:13469, and Studio itself and every stub connect to it. Killing the broker drops every connection, Studio's included. The broker role also moves to a different process over time, so the broker is always identified as "whoever listens on 13469", never by a remembered PID. roblox_cleanup_stubs gives up when it cannot identify the broker. Stubs pile up because some clients do not reap them on exit (see openai/codex#25893); an age threshold keeps cleanup away from sessions that may still be alive.

An update succeeds by revisionId, not by HTTP status. PATCHing an asset with byte-identical content returns HTTP 200, done: true and no error, but revisionId does not move: the content was deduplicated. roblox_update_asset compares revisionId before and after and reports updated, deduplicated or indeterminate. A higher revisionId still does not guarantee that Studio sees the new content; Studio-side caching is outside what Open Cloud can confirm. To replace content, do not send updateMask (updateMask=fileContent fails with Unknown field file_content); it is only for metadata changes.

The 100k truncation belongs to StudioMCP itself. Measured directly against StudioMCP: a 100,000-character reply arrives whole; a 120,000-character one stops at 100,015 characters ending in ... (truncated). No client can lift it. roblox_extract_place therefore uses three safeguards, all required:

  1. A sentinel at the end of every chunk; a chunk without it counts as incomplete. Length alone cannot tell a complete reply from a truncated one.
  2. The UTF-8-safe cut point is decided in Lua and reported back, and the caller continues from there, so the two sides never compute boundaries separately.
  3. A byte-count check per file (Buffer.byteLength, not String.length). Any mismatch aborts the whole extraction, because a partially correct snapshot looks like success.

Each call stages its data under its own key, shared.RBXEXTRACT_<32 hex digits> (Lua VM memory only, never the DataModel or the .rbxl), and clears it afterwards, so overlapping calls never read each other's data. If clearing fails, the next call that reaches the same Studio clears it again, up to three attempts in all. Every key also records when it was last used, and each new key first clears any key that has sat idle for 15 minutes, so a key left behind by a failed release, a shutdown or a crash goes away on the next call to that Studio. During shutdown the server never reconnects to Studio just to clean up.

Instance names can be almost anything, including .., path separators and Windows device names such as CON or NUL, so every path segment is sanitized. A device name gets a trailing _ (CON becomes CON_.server.luau) on every platform, so a snapshot made on macOS still opens on Windows, and the final path is checked to be inside the output folder before writing.

The whole new snapshot is first written to a fresh folder that the extraction creates inside the output folder (.studio-sidecar-staging-<random>). Only after every file is there does the switch start: the files of the previous snapshot are moved aside, and the new ones are put in place with hard links that fail if the name is taken (or, on file systems without hard links, with exclusive creation). If anything fails during the switch, the new files are removed and the previous snapshot is moved back, so a failed run leaves the previous snapshot as it was. Extractions into the same folder run one after another. A run that is killed midway can leave a .studio-sidecar-staging-* folder behind; it holds only snapshot files and is safe to delete.

Nothing is ever overwritten. When a name is already taken, including by a name the file system treats as equal (Σ, ς and σ on a case-insensitive volume), the script gets the next ~n suffix, and scene/scripts.json records the path actually written. Re-running into the same folder replaces only the files listed in that folder's previous scene/scripts.json; a folder whose scene/ files were not written by Studio Sidecar is refused. If one of those files has been turned into a hard link, only that name is removed, and the content under the other name stays as it was.

The Studio bridge must live as long as the server. Closing it after every call makes the first call succeed and every later one fail with "Unable to reach Roblox Studio right now", which sounds like Studio is closed. It is not: closing kills the stub, and the next call races the broker. The same message also appears during a cold start before a fresh stub reaches the broker, so the bridge retries the Studio listing on that one message, and only that one, for up to 20 seconds; the wait grows by 250 ms per attempt, up to 1.5 seconds. Once the server starts shutting down, the bridge refuses new connections, so a call that is still finishing cannot start a fresh stub.

Open Cloud formats are narrow. Models accept .fbx only; Decals accept bmp, jpeg, png and tga; Audio accepts mp3 and ogg and can only be created, not updated, under a monthly quota. Wrong formats would only fail after polling the operation, so they are rejected locally. Models and Decals have never supported archiving, and roblox_archive_asset says so up front.

Platform support

WindowsmacOS
Open a given place✅ verified✅ implemented, not verified on hardware
Process and broker inspection✅ verified (PowerShell)✅ verified (ps + lsof)
Stub cleanup✅ verified✅ implemented, not verified on hardware
Window titles✅❌ always undefined (needs Accessibility permission)
Open Cloud tools✅✅ (plain HTTP)

Platform differences sit behind ports/process-inspector.ts; broker detection, stub classification and cleanup are shared. Linux is refused explicitly rather than returning an empty snapshot, because Roblox Studio does not run there and an empty answer would read as "no Studio running".

Boundaries

  • No window focus changes. Stealing focus can make a stray key close a freshly loaded place.
  • roblox_luau_safe is not a new way to run arbitrary code. execute_luau is already in the client's hands; this tool adds a mandatory PlaceId guard, one pcall around the whole chunk and chunked results in front of the same entry point. The pcall catches a runtime error and returns it as a readable value; the code after the error does not run, and changes made before the error are not rolled back. It is not a sandbox.
  • No reverse engineering of the broker's private protocol on 13469. Talking to StudioMCP as an ordinary MCP client makes that unnecessary.
  • No "remove from inventory". That goes through inventory.roblox.com with a signed-in session cookie and CSRF token, which an Open Cloud API key cannot do.
  • Known limitation: a folder that already exists in the output folder can be swapped during the switch. Files are written into the fresh staging folder, but putting them in place still walks the output folder's existing sub-folders by path. Each level is resolved with realpath and checked to be inside the output folder first; another program on the same machine could still replace a folder with a symlink between that check and the move (time-of-check/time-of-use). The move only ever creates a new name, so at worst one new file appears outside the output folder; nothing outside is overwritten or deleted.
  • Known limitation: an unreleased buffer lasts until the next call. A Luau buffer that could not be released stays in Studio's shared table until a later call reaches the same Studio (and, for the idle sweep, until it has been idle for 15 minutes), or until Studio closes.

Development

bash
corepack pnpm installcorepack pnpm run verify          # typecheck, tests, build, stdio smokecorepack pnpm run verify:live     # read-only checks against real Studio and a real accountcorepack pnpm run verify:extract  # end to end: open_place → place_guard → luau_safe → extract_place

verify:live only performs read-only operations: no uploads, updates, opened places or terminated processes. Its Open Cloud part needs ROBLOX_API_KEY and an asset ID (node scripts/verify-live.mjs <assetId>) and is skipped otherwise.

verify:extract needs ROBLOX_VERIFY_PLACE_ID, ROBLOX_VERIFY_UNIVERSE_ID (a place you own) and ROBLOX_VERIFY_BASELINE (a hand-made, byte-verified script snapshot). It opens a Studio instance and compares the extracted scripts byte for byte against the snapshot. The steps are chained on purpose, because the bridge lifetime bug only shows up across several consecutive tool calls.

License and trademarks

MIT.

Studio Sidecar is an independent open-source project, not affiliated with or endorsed by Roblox Corporation. Roblox, Roblox Studio and Luau are trademarks of Roblox Corporation.

来源:README.md,提交 eebeb8b

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

2
  1. v0.1.1最新Oct 19, 2026
  2. v0.1.0Oct 18, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分