Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Chrrxs/robloxstudio-mcp/llms.txt

Use this file to discover all available pages before exploring further.

Roblox Studio MCP supports any number of Studio windows connected to the same server simultaneously — two games open side by side, a published place alongside a local prototype, or a full playtest with separate edit, server, and client DataModels all registered at once. Every MCP tool call must ultimately execute inside exactly one Studio DataModel, so the server needs a way to decide which one. That decision is called routing, and instance_id is the parameter that controls it.

How instanceId Is Assigned

When a Studio plugin loads, it calls /ready and provides the server with its placeId, placeName, and a self-generated instanceId. The server normalizes this into a canonical routing key:
  • Published places (placeId > 0): "place:<PlaceId>" — for example "place:12345678". All DataModel roles for the same published place share this key.
  • Unpublished / local places (placeId == 0): "anon:<uuid>" — the UUID is persisted as the __MCPPlaceId attribute on ServerStorage inside the .rbxl file, so it travels with the file. Re-opening the same local file returns the same instanceId across server restarts.
Published place  →  instanceId: "place:12345678"
Local .rbxl      →  instanceId: "anon:f47ac10b-58cc-4372-a567-0e02b2c3d479"
The pluginSessionId (an internal per-plugin GUID, regenerated on every plugin load) is used only inside the server as a map key — it is never exposed to MCP tool callers.

Discovering Connected Instances

Before routing, you can call get_connected_instances to see everything the server knows about:
// Example response from get_connected_instances
{
  "instances": [
    {
      "instanceId": "place:12345678",
      "role": "edit",
      "placeId": 12345678,
      "placeName": "My RPG Game",
      "dataModelName": "Game",
      "isRunning": false,
      "pluginVersion": "2.20.0",
      "pluginVariant": "full",
      "serverVersion": "2.20.0",
      "versionMismatch": false,
      "lastActivity": 1720000000000,
      "connectedAt": 1719999900000
    },
    {
      "instanceId": "anon:f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "role": "edit",
      "placeId": 0,
      "placeName": "Untitled",
      "dataModelName": "Game",
      "isRunning": false,
      "pluginVersion": "2.20.0",
      "pluginVariant": "full",
      "serverVersion": "2.20.0",
      "versionMismatch": false,
      "lastActivity": 1720000001000,
      "connectedAt": 1719999910000
    }
  ]
}
The isRunning field becomes true when a playtest is active in that window. versionMismatch flags a plugin/server version difference that may affect tool behavior.

The instance_id Parameter

Every tool in the catalog includes an instance_id parameter in its inputSchema. The routing rules are:
Omit instance_id. The server routes automatically to the only connected instance. Passing it is harmless.

The target / Role Parameter

During a playtest, a single instanceId (place) has multiple connected DataModel peers:
RoleDataModelWhen present
editEditable DataModelAlways (not during multiplayer test)
serverPlay-server DataModelDuring any playtest
client-1First play-client DataModelDuring play mode or multiplayer test
client-2Second play-client DataModelDuring multiplayer test with ≥ 2 players
client-NNth play-clientAs above
Tools that support runtime targeting (like execute_luau, get_runtime_logs, eval_server_runtime) accept a target parameter. When instance_id is provided and the target role is present, routing resolves to a single (instanceId, role) tuple. When target is omitted and only one role is connected for that instanceId, routing resolves automatically. If multiple roles are connected and target is omitted, the server prefers edit if present; otherwise it returns a target_role_required error. Passing target="all" triggers fanout — the tool runs against every role simultaneously and returns aggregated results.

Routing Error Codes

Routing failures return structured JSON that embeds the full current instance list. The AI agent can recover by reading data.instances and retrying with the correct instance_id — no extra round-trip required.
// Example: multiple_instances_connected error
{
  "error": "multiple_instances_connected",
  "message": "Multiple Studio places are connected. Pass instance_id to disambiguate.",
  "data": {
    "instances": [ /* full PublicPluginInstance list */ ],
    "count": 2
  }
}
All five error codes from bridge-service.ts:
CodeCauseResolution
multiple_instances_connectedMore than one distinct place is connected and instance_id was omittedPass instance_id from data.instances
ambiguous_targetMultiple places are connected, a target role was specified, but no instance_idPass instance_id to select the place
target_role_requiredinstance_id is valid but the place has multiple roles connected and target was omitted (and no edit role to default to)Pass target=<role> using the available roles listed in the error message
target_role_not_present_on_instanceinstance_id is valid but the specified target role is not connected for that instanceCheck data.instances for available roles; start a playtest if the runtime role is needed
unrecognized_instance_idThe provided instance_id does not match any connected pluginCheck data.instances for valid IDs; the place may have been closed or not yet connected
Routing errors arrive as isError: true tool results with a JSON body — not as MCP protocol errors. The agent can parse the error code and data.instances array inline without invoking a separate tool.

Stale Instance Cleanup

The server considers an instance stale if it has not polled /poll within 30 seconds. Stale instances are automatically unregistered during the periodic cleanup cycle (every 5 seconds). Any pending requests targeting a stale instance are rejected immediately with "Target disconnected". This prevents tool calls from hanging when Studio is minimized or the plugin crashes. Instance aliases (anon:place: transitions when a local file is published mid-session) are retained for up to 5 minutes after the last-seen timestamp before being pruned.

Example: Routing Tool Calls Across Two Open Places

The following scenario shows how to work with two simultaneously open Studio windows.
1

List connected instances

Call get_connected_instances with no arguments to see what is connected.
// Response
{
  "instances": [
    { "instanceId": "place:11111111", "role": "edit", "placeName": "Lobby" },
    { "instanceId": "place:22222222", "role": "edit", "placeName": "Game World" }
  ]
}
2

Read the file tree from one place

Pass the correct instance_id to target the Lobby.
// get_file_tree request
{ "path": "game.ServerScriptService", "instance_id": "place:11111111" }
3

Run a script edit on the other place

Target the Game World for a write operation.
// set_script_source request
{
  "instancePath": "game.ServerScriptService.Main",
  "source": "-- updated logic",
  "instance_id": "place:22222222"
}
4

Start a playtest and target runtime roles

Start a playtest in Game World, then use target to reach the server DataModel.
// solo_playtest start
{ "action": "start", "mode": "play", "instance_id": "place:22222222" }

// get_runtime_logs from the server peer
{ "target": "server", "instance_id": "place:22222222" }

Anonymous Place IDs and the __MCPPlaceId Attribute

For unpublished places, the plugin stores the generated UUID as a StringValue-style attribute named __MCPPlaceId on ServerStorage. Because this attribute is part of the DataModel, it is saved when you save the .rbxl file. The same anon:<uuid> value is used every time that file is opened, even across MCP server restarts, so scripts or workflows referencing a local prototype’s instance_id remain stable across sessions.
If you want to reset an anonymous place’s ID (to force a fresh instanceId after forking a file), delete the __MCPPlaceId attribute from ServerStorage in Studio and reopen the file.

Build docs developers (and LLMs) love