Skip to main content
Glama
EL4CTEO

Roblox Studio MCP

Run Luau in Studio

execute_luau
Destructive

Run Luau scripts inside Roblox Studio, a playtest client, or live servers to read data, run one-off calculations, or call APIs not covered by other tools, returning printed and returned output.

Instructions

Runs Luau in Studio's plugin context and returns whatever it printed, returned, or threw.

This is the escape hatch. Reach for it only when no dedicated tool fits — create, modify, delete, move, script_edit and find validate their input, type values from the live API dump, and wrap writes in an undo recording. Code run here does none of that, so a typo becomes a runtime error instead of a suggestion, and changes it makes may not be undoable as one step.

Good uses: reading something no tool exposes, a one-off calculation over many instances, or calling an engine API the tools do not cover.

Output printed while it runs is captured and returned, so print is a reasonable way to get values out. return works too, including returning a table — it comes back as a structure, not a summary. There is no timeout for target="studio": an infinite loop will hang Studio until it is force-quit.

Against a running playtest server, Studio disables loadstring, so the code is compiled through a ModuleScript instead and runs at script identity — plugin-only APIs are unavailable there. When that happens it is stated in the result rather than left to be inferred from a failure.

With target="studio", do not use require to read live state out of a running game. This runs in the plugin's own Luau VM with its own module cache, so require here returns a second, freshly-initialised copy of the ModuleScript — its counters and caches read as empty while the real one is running fine, and a zero is indistinguishable from a genuine zero. Read live state off the DataModel instead (instances, attributes, properties), or have the game print it and read that with console. The result warns when a call could have hit this.

target="client" runs in the selected player's actual playtest client VM, including its live require cache. Requires a running playtest server studioId. Output is capped at 200 lines, 10 returns, table depth 4 and 50 entries. The relay is removed on completion or timeout; non-yielding code can still stall the client. Connections/hooks created by the temporary relay (such as Connect or RenderStepped) do not persist after the call returns.

target="live" runs the script on Roblox's servers against the PUBLISHED place instead, with no Studio involved. That is how you read or repair production: a real player's data store entry, what the live game actually holds, a migration over saved data. Everything the script prints comes back in logs.

BE CAREFUL WITH IT. The Studio path has an undo stack and a place nobody is playing. This one touches live data and live players, and nothing here can put any of it back — so it needs confirm: true and you should read before you write. Roblox queues it as a task, so expect seconds, not milliseconds, and a state of COMPLETE or FAILED rather than a bare value.

Input Schema

NameRequiredDescriptionDefault
playerNoclient only: player name; required with multiple players.
sourceNoLuau to run. In an editor session this has plugin permissions, so `game`, `workspace` and plugin-only APIs are all reachable.
targetNo'studio' runs in the connected Studio, with plugin permissions. 'live' runs on Roblox's servers against the published place — production, with no undo. 'client' runs in a player's playtest client VM.studio
confirmNoRequired for target="live". This runs against the game people are playing and nothing here can undo it.
placeIdNolive only: which place. Omit to use `cloud place`.
resultIdNoRetrieve a retained result without rerunning source. Keep its original target/player/studioId.
studioIdNoTarget Studio; omit for the active one.
resultPathNoRetained result path, default [returned,1]. Use [] for its root; [output,1,message] for a log string.
universeIdNolive only: which game. Omit to use `cloud universe`.
resultLimitNoRetained table entries per page.
resultFieldsNoProject these fields on retained table rows.
resultOffsetNoResult page offset; use nextOffset from the previous page. String offsets are UTF-8 bytes.
timeoutSecondsNolive/client only: timeout in seconds. Defaults to 30.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed6 schema fields changedv0.9.1
    • addedInput schema / properties / resultFields
      Added value: +{
      +  "description": "Project these fields on retained table rows.",
      +  "items": {
      +    "type": "string"
      +  },
      +  "maxItems": 16,
      +  "type": "array"
      +}
    • addedInput schema / properties / resultId
      Added value: +{
      +  "description": "Retrieve a retained result without rerunning source. Keep its original target/player/studioId.",
      +  "type": "string"
      +}
    • addedInput schema / properties / resultLimit
      Added value: +{
      +  "default": 25,
      +  "description": "Retained table entries per page.",
      +  "maximum": 100,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • addedInput schema / properties / resultOffset
      Added value: +{
      +  "default": 0,
      +  "description": "Result page offset; use nextOffset from the previous page. String offsets are UTF-8 bytes.",
      +  "maximum": 9007199254740991,
      +  "minimum": 0,
      +  "type": "integer"
      +}
    • addedInput schema / properties / resultPath
      Added value: +{
      +  "description": "Retained result path, default [returned,1]. Use [] for its root; [output,1,message] for a log string.",
      +  "items": {
      +    "anyOf": [
      +      {
      +        "type": "string"
      +      },
      +      {
      +        "maximum": 9007199254740991,
      +        "minimum": -9007199254740991,
      +        "type": "integer"
      +      }
      +    ]
      +  },
      +  "maxItems": 20,
      +  "type": "array"
      +}
    • removedInput schema / required
      Removed value: -[
      -  "source"
      -]
  2. Changed4 schema fields changedv0.7.5
    • addedInput schema / properties / player
      Added value: +{
      +  "description": "client only: player name; required with multiple players.",
      +  "type": "string"
      +}
    • changedInput schema / properties / target / description
      Previous value: -"'studio' runs in the connected Studio, with plugin permissions. 'live' runs on Roblox's servers against the published place — production, with no undo."New value: +"'studio' runs in the connected Studio, with plugin permissions. 'live' runs on Roblox's servers against the published place — production, with no undo. 'client' runs in a player's playtest client VM."
    • changedInput schema / properties / target / enum
      Previous value: -[
      -  "studio",
      -  "live"
      -]New value: +[
      +  "studio",
      +  "live",
      +  "client"
      +]
    • changedInput schema / properties / timeoutSeconds / description
      Previous value: -"live only: how long the script may run. Defaults to 30."New value: +"live/client only: timeout in seconds. Defaults to 30."
  3. Changed5 schema fields changedv0.6.8
    • addedInput schema / properties / confirm
      Added value: +{
      +  "description": "Required for target=\"live\". This runs against the game people are playing and nothing here can undo it.",
      +  "type": "boolean"
      +}
    • addedInput schema / properties / placeId
      Added value: +{
      +  "description": "live only: which place. Omit to use `cloud place`.",
      +  "type": "string"
      +}
    • addedInput schema / properties / target
      Added value: +{
      +  "default": "studio",
      +  "description": "'studio' runs in the connected Studio, with plugin permissions. 'live' runs on Roblox's servers against the published place — production, with no undo.",
      +  "enum": [
      +    "studio",
      +    "live"
      +  ],
      +  "type": "string"
      +}
    • addedInput schema / properties / timeoutSeconds
      Added value: +{
      +  "description": "live only: how long the script may run. Defaults to 30.",
      +  "maximum": 300,
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • addedInput schema / properties / universeId
      Added value: +{
      +  "description": "live only: which game. Omit to use `cloud universe`.",
      +  "type": "string"
      +}
  4. First observedv0.1.8

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare destructive/openWorld/non-idempotent, and the description goes well beyond them: no undo for the live path, confirm:true requirement, no timeout on target=studio (hang risk), loadstring disabled on playtest, the require module-cache trap that can silently return misleading zeros, result caps (200 lines/10 returns/depth 4/50 entries), relay cleanup, and async COMPLETE/FAILED state. This is unusually rich disclosure of failure modes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and the escape-hatch warning are front-loaded before the target breakdown, which is the right ordering for a high-risk tool. It is long, and a few clauses (e.g., the second require-cache warning sentence) restate earlier points, but for a 13-parameter, three-target, destructive tool the length is largely earned.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers the safety profile, the three execution environments, timeout behavior, result-size limits, and how values surface from the script — enough for a tool with no output schema and a complex multi-target surface. An agent has what it needs to choose a target and avoid the documented traps.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the prose adds real meaning the schema does not: why confirm is needed, what happens to live data, and the behavioral implications of each target value rather than a bare enum restatement. It stops short of explaining resultId/resultPath retrieval mechanics, which remain schema-only.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence gives a specific verb and resource — runs Luau in Studio's plugin context — and states what comes back (printed, returned, or thrown). It explicitly frames itself as the escape hatch and names the dedicated siblings (create, modify, delete, move, script_edit, find) it should not replace, so an agent can place it precisely among 34 tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit when-not (only when no dedicated tool fits) plus enumerated good uses (reading unexposed state, one-off calculations, uncovered engine APIs), and differentiates the three targets by scenario: studio for safe dev, client for live playtest client VM, live for production repair. Nothing about tool selection is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.