| studio_statusA | One-call snapshot of the connected Roblox Studio: place name and id, whether it is in edit / run / play mode, the current selection, which scripts are open in the editor, and how big the data model is. Call this FIRST in any Studio session, and again whenever a tool reports NO_STUDIO or TIMEOUT — it is the cheapest way to tell a disconnected plugin apart from a genuinely failing request. Also call it before and after playtest, because most tools behave differently in run mode. openScripts is what the user is actually working on: for each open tab it gives the script's path, the cursor line, any selected text, and which lines are on screen. Use it whenever a request is deictic — 'this function', 'the script I'm in', 'fix this' — instead of searching the place or asking which file they mean. Studio exposes no focused-tab API, so with several open, prefer the one holding a selection and otherwise ask.
Returns JSON. Selection is capped at 50 entries and selected text at 400 characters; use find or script_read for more. |
| list_studiosA | Lists every Roblox Studio window currently connected to this server, with its studioId, place name, transport (sse or poll), when it connected, and which one is active. Call this whenever a tool reports AMBIGUOUS_STUDIO, and whenever the user refers to 'the other place' or 'my other window'. With a single Studio open every other tool targets it automatically, so you can skip it then. Nothing is targeted by default when several are connected: pick one with set_active_studio, or pass studioId to a single tool call to act on one place without changing the default. Each Studio is queried live, so placeName is the published name the user would recognise. A place never saved to Roblox has no published name and falls back to its data model name ('Place1'). context matters more than it looks. Pressing Play adds a second entry for the playtest's server — same place, same name, same id as the editor session. Instances created or changed in a 'playtest' context are thrown away the moment the user stops, so building there looks like it worked and then vanishes. Target 'edit' unless the user specifically wants to inspect or affect the running game.
|
| set_active_studioA | Chooses which connected Studio window every other tool targets by default. Use it after list_studios when several places are open, and again whenever the user says to switch to another place. The choice persists until it is changed or that Studio disconnects. While several Studios are connected and none has been chosen, tools refuse with AMBIGUOUS_STUDIO rather than guessing. The choice belongs to this MCP connection alone. Several agents can share one Studio, and each keeps its own target, so calling this never moves another client's — two editors, or two sessions, can work on two places at once. SUBAGENTS SHARE THEIR PARENT'S CONNECTION, and therefore its target. A subagent calling this retargets its parent and every sibling, and the damage is silent: later calls that name no studioId still succeed, just against the wrong place — and if that place is a playtest, everything written there is discarded when it stops. Inside a subagent, pass studioId on each call instead of calling this. |
| treeA | Lists the instance hierarchy under a path, breadth-first to a given depth. Returns a flat array of paths — flat is both cheaper and easier to act on than nested JSON, since every entry is directly usable as a path. Use this to orient yourself in an unfamiliar place. Use find instead when you already know what you are looking for; a deep tree over a whole place wastes context on instances you will never touch. With path omitted it lists only the containers a place is authored in — Workspace, ReplicatedStorage, ServerScriptService and friends. Roblox exposes ~120 services at the root, almost all engine internals; those are hidden and the response says how many. Pass an explicit path to look inside one of them anyway. |
| inspectA | Reads properties, attributes, tags and children of one or more instances. Pass every path you care about in a single call — batching costs one round trip instead of N. Property selection comes from the live Roblox API dump for each instance's actual class, so it stays correct across engine updates:
concise — class and child count only
standard — the properties that characterise the class (Part gets Size, Position, CFrame, Anchored, Material...)
full — every readable property; expensive, use on one or two instances at most Bad paths do not fail the call: they come back under failures while the valid ones still return, so one typo does not cost you the whole batch. |
| findA | Searches the data model by name, class, property value and/or tag. Every filter you supply must match, so one call answers questions that would otherwise take several: "anchored BaseParts under Workspace.Map whose name contains door" is a single request. This replaces separate name / class / property / tag search tools. Prefer it over tree whenever you know what you are looking for. Tag searches are answered from CollectionService's index rather than by walking the tree, so they stay fast on large places. Narrow with path if a search reports TOO_BROAD. op="tags" lists which tags the place actually USES, with counts and a few example paths. Call it before filtering by tag on a place you do not know: a tag search that returns nothing looks the same whether you spelled it wrong or nothing carries it, and the tag names are often the clearest description of how a game is organised (Enemy, Checkpoint, Interactable say more than the folder layout does).
selector is the engine's own query language and is the fastest option of all — the matching happens in C++ and only survivors come back. Reach for it when the shape of the tree is part of the question (Model > Part) or when one call should answer two (Part, Model); the filters above still apply on top of it.
|
| script_readA | Reads Luau source from one or more scripts, with line numbers that script_edit accepts back verbatim. Source comes from the Studio script editor's live buffer, so anything the user has typed but not yet saved is included. Reading the saved property instead would hand you stale code and you would 'fix' the change they just made. Pass every script you need in one call, including when you want a different part of each: an entry may be a bare path for the whole file, or {path, startLine, endLine} for a window into that one script. The top-level startLine/endLine are the default for entries that do not carry their own. A script bound to a file on disk is flagged in the result. Editing one of those is a race: whatever writes the file wins, and your change disappears the next time it does, with nothing anywhere reporting a failure. open puts a script on the user's screen at a line, instead of telling them where to look. Ask for it when you are pointing at something they should see; it is not automatic, and reading twenty scripts does not rearrange their editor.
target="live" reads the code of the PUBLISHED place instead, with no Studio involved — which is how you check what is actually deployed rather than what is on someone's machine. Two limits are real and worth knowing before you reach for it: Roblox's Instance API can only see Folders and scripts, so a path through a Model or a Part cannot be walked at all; and it addresses things by GUID with no search, so each segment of the path costs a round trip. Expect seconds. list: true shows what is under a path instead of reading it, which is how you find your way down.
|
| script_editA | Edits Luau source through the Studio script editor. This is the tool to use for any change to existing code. Every edit in one call is all-or-nothing: the whole batch is resolved against current source before anything is written, so if one edit cannot be applied nothing is. Batch related changes together, even across different scripts. Each edit picks exactly one mode:
find/replace — literal text, not a pattern. Preferred: it survives line numbers shifting. Fails if the text is not unique, unless you set replaceAll, so include enough surrounding lines to pin it down.
startLine/endLine + replacement — for line ranges from script_read. Numbers refer to the file as you read it; several line edits to one script are applied bottom-up so they do not shift each other.
source — replaces the whole script. Only for small files or a rewrite; it discards anything the user changed since you read it. Pass revision on every edit. script_read prints it as rev beside each file, and sending it back makes the write conditional: if the script changed since you read it the batch is refused with STALE_SCRIPT and nothing is written. Without it the edit is applied blind, which matters most for the two modes that cannot notice: a line range still applies cleanly to source somebody else moved, it just lands on the wrong lines, and source discards their work entirely. Another agent editing the same place, or the user typing in the editor, is enough. Writes go through ScriptEditorService:UpdateSourceAsync, so an open editor tab updates in place and unsaved work is preserved. Undo for source changes is the script editor's own, per script — Ctrl+Z in a script tab reverts that script, not the whole batch. target="live" edits the PUBLISHED place instead. It takes ONE edit, it replaces the whole source rather than finding and replacing, and there is no undo of any kind — so read the script with script_read target="live" first and send back the whole thing. Needs confirm: true.
It changes the SAVED place, not running servers: people already playing keep the old code until their server empties. Follow it with universe op="restart" to roll them over. |
| script_grepA | Searches inside Luau source across the place and returns matching lines with their paths and line numbers. Use this to find where something is defined or used before editing it — it is far cheaper than reading whole scripts to look for one call. Patterns are Lua patterns, which are not regular expressions: % escapes instead of backslash, there is no alternation, and - means a lazy quantifier. Set literal to search for text exactly as written, which is usually what you want for identifiers. Matches come from the script editor's live buffer, so unsaved edits are searched too. |
| script_createA | Creates Script, LocalScript or ModuleScript instances with their source. Batch related scripts into one call: they are created inside one ChangeHistoryService recording, so the user can drop a whole generated system in a single undo. The response says whether that recording was actually opened — Studio refuses while another one is in progress. Prefer Script with runContext: "Client" over LocalScript in new work — a Script with an explicit RunContext runs wherever you parent it, while LocalScript only runs under a player's character, backpack or PlayerGui. The exception is the starter containers — StarterGui, StarterPack, StarterPlayerScripts, StarterCharacterScripts. They are COPIED into each player, so a Script with a non-Legacy RunContext there runs once where it sits and again in every copy, while a Legacy one does not run at all. Use LocalScript inside those. Creating one anyway comes back with a warning, because Studio's own warning about it goes to its Output and never reaches console. Use script_edit to change a script that already exists. |
| createA | Creates instances with their properties, attributes and tags set at creation, as one undoable step. Nest with children to build a whole model in a single call. That is both faster and safer than creating a parent and then addressing it: a new instance's path is not knowable until it exists, and same-named siblings make guessing it unreliable. Property names are checked against the live Roblox API dump before anything is sent to Studio, so a typo comes back with the closest real names rather than an engine error. Use script_create for Script, LocalScript and ModuleScript — it takes source directly. |
| modifyA | Sets properties, attributes and tags on existing instances, as one undoable step. Each entry takes a list of paths, so one entry can apply the same change to many instances — anchoring 200 parts is one entry, not 200. Combine with find to build the path list. The batch is all-or-nothing: if any value is rejected the recording is cancelled and every instance reverts, rather than leaving the place half-changed. Values use the same notation the Properties panel shows — see the properties field. To change a script's code use script_edit. |
| deleteA | Destroys instances and everything inside them, as one undoable step. Deleting a container deletes its whole subtree, so the response reports how many descendants went with each one — check it before telling the user what happened. Services cannot be deleted and are refused. Paths shift when same-named siblings are removed, so read fresh paths from find or tree before a second delete rather than reusing indexes from an earlier call. |
| moveA | Reparents instances, or clones them into a new parent, as one undoable step. Set mode: "clone" to copy instead of move — that is how to duplicate something, optionally renaming it in the same call. Moving an instance into itself or its own descendant is refused: it silently detaches the branch from the data model and undo does not bring it back. |
| consoleA | Reads the Studio Output window — prints, warnings and runtime errors, newest last. This is how to find out what actually happened after a playtest or an execute_luau call. An error here usually names the script and line, which script_read can then open directly. Filter with level to see only errors, or pattern to follow one subsystem's logging. Up to 2000 lines are held, so prefer a filter over a large limit. Each connected session keeps its own log, recorded from the moment its plugin loaded — the editor session and a running playtest server do not share one. To read what a playtest printed, target the playtest's studioId (see list_studios); the editor's log will not have it. Nothing printed before the plugin loaded is recoverable, and output from the playtest client is not reachable at all, because Studio forbids client sessions from making HTTP requests. A quiet log is not proof nothing was said. Anything the playtest CLIENT printed is never here. Messages Studio itself emits — the ones the Output window attributes to "Studio" rather than to a script — are inconsistent, and they arrive in the session that RAISED them, which is not always the one you are looking at: the warning that a Script with a non-legacy RunContext inside a starter container will run multiple times shows up in the playtest server's log, where the script actually loads, and never in the editor's, where it was created. Do not read silence as an all-clear — when a script misbehaves in a way nothing here explains, check the Output window yourself, or ask the user what it says. |
| performanceA | Reads the engine's own counters, and can run the script profiler. snapshot returns what the Developer Console shows: frame, physics and render times in milliseconds, instance and part counts, draw calls, network rates, and memory broken down by category. Use it to answer 'why is this place heavy' with numbers instead of guesses.
profile runs Studio's script profiler — the Script Performance window — for seconds and reports which scripts consumed CPU. It blocks for that long, so keep it short. It only sees code that actually runs, so start a playtest first; profiling an idle edit session returns nothing.
coverage reports which lines of which scripts actually executed — dead code, untested branches, whether a fix was even reached. Pass enable first, then play, then read the coverage back FROM THE PLAYTEST session, not the editor: instrumenting is per data model, and the playtest is a different one. enable is remembered for the place and re-applied by each new session as it loads. Pass an empty enable array to stop.
What it can and cannot see: instrumentation is fixed when a script is first compiled, so it measures modules required after that point — where most game logic lives — but never a script that starts with the place, which the data model compiles before any plugin exists. Those report 0 lines and are named as unmeasurable rather than counted as dead code. scene breaks the place down by what it is actually made of: instances by category, triangles and draw calls FOR WHAT THE CAMERA CAN SEE, and the assets holding script, animation and audio memory — each named, so "2.4GB of memory" becomes "this animation is 138KB and these are the Animators using it". It also reports UNPARENTED INSTANCES, which is the closest thing here to a leak detector: objects still alive with nothing holding them in the tree, invisible to find and to tree because they are in neither.
The triangle and draw-call section is the one number here that depends on where the camera is pointing, and it moves enormously: the same place measured 332 triangles looking at empty sky and 29,060 looking at 1,800 parts, seconds apart. So it answers "how heavy is this view", not "how heavy is this place" — point the camera first with viewport op="focus", and compare two views only if both were framed the same way. audit is a health check rather than a performance one: it finds every reference in the place that points at NOTHING. A Sound whose id was deleted or made private plays silence, a Decal shows nothing, an Animation does nothing — none of them errors, none warns, and the instance looks perfectly healthy because the id is still a string. The only other way to find them is to play the game and notice something missing. It also reports ids left blank, scripts left Disabled, and same-named siblings, which is what makes WaitForChild return the wrong one.
audit fetches the assets to test them, so Studio's Output window will show load errors for the dead ones. That is the engine confirming the finding, not a fault in the tool.
Frame and network figures are only meaningful while something is running. Instance counts and memory are useful in edit mode too. |
| playtestA | Starts and stops playtests, so scripts can be made to run and then observed without asking the user to press anything. play is the Play button: a character spawns and Players.PlayerAdded fires. run is Run mode, which executes scripts with no player at all. multiplayer starts a test with several players for testing replication. state reports without changing anything.
Pressing play adds a SECOND connected session for the playtest's server, and that is where the running game lives — console, performance and execute_luau must target its studioId, not the editor's. Call list_studios after starting and look for the entry whose context is a playtest. A test does not block this call: it starts and the reply reports the state reached. Studio only ends it when something inside calls StudioTestService:EndTest(value) or when stop is used here; whatever EndTest passed comes back as lastResult on a later state. That makes a scripted check possible end to end: args is readable inside the test via StudioTestService:GetTestArgs(), so a test can be told what to do and report back what happened. Stopping discards everything the playtest changed, exactly as pressing Stop does. Build in edit mode, then play — not the other way round. The reply says whether the mode actually moved, not merely that Studio accepted the request. |
| execute_luauA | 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: 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. 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="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. |
| viewportA | Works with the 3D view and the Studio selection. select sets, extends or shrinks what is highlighted in Studio. Select what you just built or changed — it shows the user the result, and puts the instance under Studio's own move and scale handles. studio_status reports the current selection; this sets it.
focus aims the Studio camera at an instance and frames it so the whole thing is on screen. This is what makes screenshot worth having: a picture of wherever the camera happened to be answers nothing, while a picture of the thing you just built answers 'does it look right', which no amount of reading properties can. Build, focus, screenshot.
The distance is computed from the subject's size and the camera's field of view, so a doorway and a whole map both arrive filling a similar share of the frame. from changes the angle you view it from, and padding how tightly it is framed. camera sets or reads the camera directly, for shots framing cannot express — standing inside a room, or looking along a corridor.
raycast fires a ray through the world and reports the first thing it hits, with position, surface normal, distance and material. This answers 'what occupies this space', which the data model alone cannot: use it to find the ground under a spawn point, or check whether a gap is clear before placing something.
ui audits a whole interface for the faults that are invisible in the data model: elements off the side of the screen, elements covering each other, zero-size elements, text too small to read, and text that overflows its label. A button positioned off a phone screen has a perfectly correct Position and Size — nothing about the instance is wrong, it is just somewhere nobody can reach.
It measures against whatever device is currently emulating, so the way to use it is twice: once as-is, then device op="set" a phone and again. Layout is live in edit mode — no playtest needed. textbounds measures how big a piece of text actually renders. Point it at a TextLabel, TextButton or TextBox with path and it reads that label's own text, font, size and width and answers whether the text fits inside it. Give text and size directly and it just measures. There is no other honest way to answer 'will this label overflow' — character counts ignore the font, and font size is not a width.
|
| debugA | Sets breakpoints that record the stack and variables when they are hit, then reads back what they caught. These are tracepoints, not a step debugger. A breakpoint fires, captures the call stack and the variables in scope, and lets execution continue; op: "snapshots" returns what was captured. Studio's debugger has to decide whether to resume the instant it stops, and cannot wait for a tool call to come back with an answer, so stepping through code line by line is not possible this way — but 'what was this value when it got here' is, which is usually the actual question. condition is a Luau expression evaluated where the breakpoint sits, so a breakpoint can fire only on the case that matters — health < 0, player.Name == "someone".
logMessage is ALSO a Luau expression, not a template string: its value is printed when the breakpoint is hit, so write "health=" .. health rather than health={health}. Prose is a syntax error. Read the lines back with console.
Two things about it are measured, not assumed, and both waste your time otherwise. A breakpoint fires ONCE PER RUN, not once per pass: on a five-iteration loop it printed a single line, for the first iteration only. It is not a way to watch a value change inside a loop — to see every pass, have the code itself print and read that with console. And a log expression CANNOT SEE THE LOOP CONTROL VARIABLE: on for index = 1, 5 do, a breakpoint in the body read the body's own locals correctly and index as nil. Wrap values in tostring so a nil prints as "nil" instead of throwing. A log expression that throws is reported as "Breakpoint ... ignored" in console, NOT here — set still returns Verified, because Studio only compiles the expression once the line is reached. So the two kinds cost different things: a logMessage breakpoint never stops and gives you one line you composed in advance, while one without it stops briefly and gives you the whole frame — every local and its type, without having to guess beforehand which value would matter. Both give you that for one pass only. Reach for the log when you know what to watch, the capture when you do not. Only one breakpoint exists per line, so the same line cannot both log and capture. Put the breakpoint on a line that does something. A return, an end or a bare declaration can verify and then never fire — measured, not guessed: the same breakpoint moved from return squared, tag to the assignment above it went from silent to firing on every pass. If one verifies but catches nothing, suspect the line before suspecting the condition. Breakpoints belong to the session that holds them. Set them in the editor session BEFORE starting a playtest, since code that already ran cannot be caught retroactively. Nothing here leaves a thread stopped waiting for you. A capture breakpoint stops for as long as it takes to read the frame and then resumes itself, so a script with one mid-loop still runs to its last line, and the user is never left with a frozen Studio to rescue. |
| screenshotA | Takes a picture of the Studio viewport and returns it as an image you can actually look at. Every other tool here reads the data model — names, properties, numbers — which answers 'is it there' but never 'does it look right'. A part can be at the correct position, anchored, correctly sized, and still be buried inside a wall, facing backwards, or hidden behind a GUI. Take a screenshot after building something visual, and before reporting that it worked. It captures the viewport as the user currently sees it, so it shows their camera angle, not a framing of your choosing. Frame the subject with viewport op="focus" first — that is what makes this tool worth calling. Works during a playtest too — address it at the playtest's studioId and you get the player's own view, which is the only way to check what a GUI actually looks like in front of the game. That one is taken on the client and read back through the editor session, so it is a little slower and needs the editor window still connected; the caption says playtest client when it came from there. |
| inputA | Sends real keyboard and mouse input to a running playtest — the same events a person pressing the keys would produce. This is how to test what character cannot reach. character drives the Humanoid directly, which answers 'can it get to the door'; this answers 'does pressing E open it', 'does the sprint key work', 'does the menu close on Escape' — anything bound to input rather than to movement. Use character for going places and this for controls. Steps run in order, so a sequence is one call: tap E, wait, click at a point, type a name. hold is how long a key or button stays down, after is how long to wait before the next step — a jump held for a second is a different test from a tapped one. REQUIRES A RUNNING PLAYTEST, and must be addressed to the playtest's studioId from list_studios, not the editor's. A pointer is drawn on screen and travels to each target before the click, so the user can see what you are aiming at. Turn it off with cursor: false. How it works, because it explains the one thing that will surprise you: input belongs to the data model that creates it, and the character is driven by the CLIENT. Sending from the playtest's server succeeds and moves nothing. So this parents a short script into the player's PlayerGui, which runs on their client, and that reports back when the input has actually been delivered. Nothing is reported as sent until the client confirms it. If confirmation never arrives you get an error, not a success — check where things really are with character op="state". Mouse coordinates are viewport pixels from the top-left, so pair this with screenshot to see what is where before clicking it, and send what you read off the picture unchanged. The reply's landed shows the same click in the game's own coordinates, which sit a topbar lower — that difference is two ways of describing one point, not an error to correct for. Under an emulated device it is a real distortion instead, and the reply says so; there, re-read it after each click rather than reusing an earlier one. Take the screenshot immediately before clicking. The reply is measured against the viewport as it is NOW, and a Studio window that changed size since the picture was taken moves everything in it — measured, a window that went from 435 to 952 pixels wide between a screenshot and a click, where the click reported success and hit nothing. A text step types into the FOCUSED TextBox. Click the box in the same call, one step before the text, and the focus is taken for you; with no box to type into the step is reported as having done nothing rather than as delivered. |
| deviceA | Resizes the Studio viewport to a real device, so you can see what a player on that device sees. Most Roblox players are on a phone and most UI is built on a desktop monitor, which is where interfaces break: a button under the notch, a menu off the bottom of a 393-pixel-tall screen, text sized for a display three times larger. None of that is visible in the data model — every one of those instances has perfectly correct properties — so this is the only way to find it short of owning the hardware. The workflow is: set a device, screenshot, look. Pair it with playtest to check a running game's HUD rather than the editor. list gives the ids, each with its real name, form factor and resolution — ids look like "iphone_16", "ipad_a16", "samsung_galaxy_s25_ultra", "xbox", "meta_quest_3".
network degrades the connection on purpose — latency, jitter and packet loss — which is the other half of what a phone player actually gets. A menu that works at 0ms is not evidence that it works at 300: the spinner that never stops, the button that fires twice, the HUD that arrives after the round started are all invisible on a local connection. Use a preset (wifi, 4g, 3g, poor, clear) or set the numbers yourself, then playtest and watch.
stop returns Studio to the normal editor viewport AND clears the network shaping. Do that when you are finished: a left-over emulated device makes every later screenshot the wrong shape, a left-over 400ms delay makes the whole place feel broken, and nothing on screen says why in either case.
|
| apiA | Lists the properties, methods and events of any Roblox class, read from the engine that is running. Use it before writing Luau against a class you are not certain of. Guessing a method name costs a runtime error and a round trip; this costs one call and is never out of date, because the answer comes from the running binary rather than from a published dump or from training data. That matters most for exactly the classes worth checking — new ones, and ones that changed recently. Members come back as signatures rather than bare names — AddAccessory(accessory: Instance), HoldDuration: number — because a name tells you something exists and a signature tells you how to call it, which is the actual question. describe takes a class name and gives the members it declares itself, counting the inherited ones separately. classes searches class names, which is how to find one whose exact spelling you do not have.
Deprecated members are never listed, only counted — Instance has eight, including clone, remove and getChildren. They still run, so picking one from a list gives you working code and a deprecation warning in the user's output. This is not the same as inspect. inspect reads the values on an instance that exists; this reads the shape of a class whether or not anything in the place is one — which is what you need when deciding what to create in the first place. |
| audioA | Builds and inspects the modern audio API — AudioPlayer, emitters, effects and the Wires between them. Roblox's modern audio is a signal graph, not one instance with a Play method. An AudioPlayer holds the asset, an AudioEmitter puts the sound in the world or an AudioDeviceOutput sends it to the player's speakers, effects sit in between, and NOTHING is connected until a Wire joins two named pins. A place can hold a perfectly configured AudioPlayer with the right asset and the right volume and be completely silent, with no error anywhere, because the wire was never made. That is what this tool is for: create can make each instance, but the pin names, the direction and the choice of sink are where it actually goes wrong. graph is the one to reach for: it builds a whole working chain in one undoable step. kind="world" gives a sound that comes from a part; kind="ui" gives one with no position, for menus and music. Add effects to splice reverb, EQ or a fader into the chain.
wire joins two instances you already have. inspect reads an existing graph back and reports every connection — including the ones that report Connected = false, which the Explorer does not show and which are the usual reason for silence.
The old Sound instance still works and is still shorter for a plain one-off noise; use create for that. Come here when the case needs effects, per-listener mixing, or one emitter fed by several sources. |
| universeA | Acts on the PUBLISHED experience and the people in it — not on the place open in Studio. restart rolls live servers onto the version you just published. Publishing on its own changes nothing for anyone already playing: they stay on their server, running the old code, until it empties. This is the step people forget. By default it bleeds off over 10 minutes — matchmaking stops and players finish what they are doing — rather than shutting servers down under them, which is what Roblox's own default does.
message publishes to MessagingService, reaching every live server at once. Only servers with a SubscribeAsync listener on that exact topic receive it, and nothing reports whether anything was listening, so success here does not mean delivery.
ban and unban set a player's game-join restriction. A ban with no durationSeconds is PERMANENT. displayReason is shown to the player; privateReason is for your records. Scope it to one place with placeId, or leave that out to cover the whole experience. bans lists who is currently restricted.
user looks up a user id — the name-to-id step most other calls need. inventory reports what someone owns: passes, badges, assets.
Everything here needs an Open Cloud key and a universe id. The user sets both once with cloud in the Studio panel. |
| datastoreA | Reads and writes the game's saved data — DataStore and MemoryStore — from the connected Studio. This is the only tool here that looks at anything outside the place file. Every other tool answers 'is the instance right'; this one answers 'is what the player saved right', which is a different question and the one behind most reports of lost progress, reset stats, or items that come back after a rejoin. kind="data" (the default) is DataStoreService: permanent, per-player, and version-tracked. kind="memory" is MemoryStoreService: a shared scratchpad that expires on its own — queues, locks, live leaderboards.
The workflow for a bug report is: list with no store to see what exists, list with one to see its keys, get the player's key, and — the part worth knowing about — versions then get with a version to see what that same key held BEFORE it broke. You cannot diagnose a bad save by looking only at the bad save. Writes need confirm: true on kind="data", because nothing in this server can undo one: there is no recording to cancel and no Ctrl+Z. Read the key first. DataStore needs 'Enable Studio Access to API Services' ticked in Game Settings → Security, and a published place. If it is off, this tool says so in those words rather than reporting the raw 502. MemoryStore needs neither. target="live" is the other half of this tool and the one that answers a real bug report. It goes to Roblox directly instead of through Studio, so it sees exactly what the running servers see — not what the place happens to be connected to, and with no Studio API toggle involved. Use it whenever the question is about a player who is actually playing. It needs an Open Cloud key and a universe id; the user sets both once with cloud in the Studio panel.
kind="ordered" (live only) is OrderedDataStoreService, the leaderboard backend: numbers only, always sorted, no history. list returns it ranked highest first, which is the leaderboard itself.
op="snapshot" is the safety net. It tells Roblox to snapshot every data store in the experience, so support can roll them back. TAKE ONE BEFORE ANY LIVE WRITE. Roblox allows one per experience per UTC day, and the result says whether this call actually took one — a second call the same day reports success while doing nothing, and anything written since the first one is not covered.
|
| animationA | Reads an animation's actual keyframes, and builds new ones that play immediately in the open Studio. Animations are the one part of a place no other tool can see. inspect on an Animation instance returns an asset id and stops there — the poses live on Roblox's servers. read downloads them, so you can answer 'how long is it', 'which joints does it move', and 'does it end where it started' without opening the Animation Editor and scrubbing. read takes an asset id, an rbxassetid:// string, or the path of an Animation instance in the place — whichever you already have. It reports which RIG the animation was made for, which is the thing most worth knowing: an R15 animation on an R6 character does nothing at all — no error, no movement — and the asset id gives no hint either way.
build goes the other way: give it keyframes and it returns a content id you can put straight into an Animation's AnimationId. Nothing is uploaded and nothing is moderated — the id works in this Studio session and nowhere else, which makes it the right way to try an idea and the wrong way to ship one.
preview puts an animation ONTO a rig in the open place and freezes it at a chosen moment, so screenshot can show you the pose. It works in edit mode — no playtest. Ask for several moments in turn to compare poses across the animation; the rig stays posed until stop.
Poses are written the way a CFrame property is: "0, 1, 0" for a position, "0, 1, 0 | 0, 45, 0" to rotate as well. The id build returns is a bare hash, not an rbxassetid:// URL. Use it exactly as given — prefixing it stops it working. |
| terrainA | Fills, repaints and clears Roblox terrain — hills, water, caves, roads. Terrain is not made of instances, so none of the instance tools reach it: there is nothing to create, no path for find, and no property for modify. This is the only way to shape it short of writing FillBall calls by hand through execute_luau. fill takes an ARRAY of solids and applies them as one undo step, which is how terrain is actually built: a hill is several overlapping balls, a road is a row of blocks. Shapes are block (needs size), ball (needs radius), cylinder (needs radius and height) and wedge (needs size).
To CARVE, fill with material Air. That is not a special mode — a cave is a ball of Air inside a hill, and a tunnel is a row of them. replace swaps one material for another inside a region and leaves the shape alone, which is how you turn a grass hill to snow without rebuilding it. clear empties a region, or everything with confirm=true. stats says whether the place uses terrain at all -- call it first in an unfamiliar place. It cannot say WHERE the terrain is: Roblox exposes no bounding box for it, only the fixed limit. Take a screenshot to see the shape.
Positions are the centre of the solid, in studs, as "x, y, z". Terrain snaps to a 4-stud voxel grid, so small features come out blockier than the numbers suggest; nothing thinner than about 4 studs survives. |
| geometryA | Every operation that reshapes solid geometry, in one place. Boolean - union merges parts into one solid, subtract cuts the with parts out of path, intersect keeps only the overlap. This is how to build a shape that is not a box without importing a mesh. Breaking apart - fragment shatters a part into random debris, for destruction. segment is the opposite kind of break: it cuts a MeshPart into parts you NAME, so a solid car mesh becomes a body and four wheels a script can find and turn. Use fragment for rubble and segment for articulation. Motion - sweep builds the volume a part passes through as it moves, which is the only real answer to 'does this door hit the wall when it opens'. Give to for a slide, or spin degrees with a pivot for a hinge. Pass checkAgainst and it reports what the swept volume overlaps; with keep: false it measures and cleans up after itself, leaving nothing behind. subtract and intersect need the parts to actually overlap, and they fail differently when they do not. intersect returns nothing, which comes back as an error rather than a silent no-op. subtract returns the subject UNCHANGED - a full-size copy of it, reported as a created part - because cutting nothing out of something legitimately leaves it whole. So a subtract that succeeds is not proof that anything was cut: check the positions overlap with inspect first, or compare the result's size against the original.
Results keep the original's material, colour, texture and anchoring. Roblox returns bare grey MeshParts, so a brick wall with a hole cut in it would otherwise come back as a grey slab - correct geometry that looks like a mistake. mesh reads the real triangle and vertex counts of MeshParts, which is the only way to tell a 40,000-triangle tree from a 400-triangle one — they are identical in the Explorer and in Properties, and the difference is whether the place runs on a phone. It also reports mesh size against part size: the same triangles stretched over a bigger object is the usual reason a model costs more than it looks like it should.
mesh only works on meshes the signed-in Studio user or the experience owner OWNS. Roblox refuses to open anyone else's, so a model inserted from the Creator Store cannot be measured this way — the tool says which parts were skipped rather than failing the whole batch.
mirror flips instances across a plane and has no engine API behind it — Studio simply cannot do this, which is why people ask for it. Mirroring about the middle of the selection is the default, because mirroring a building at x=200 about the world origin puts it 400 studs away rather than flipping it in place. It COPIES by default; pass copy: false to flip the originals. MeshParts move and rotate correctly but their meshes are not remade, so an asymmetric mesh still reads the same way round.
segment runs Roblox's Cube model and takes tens of seconds; the rest are fast. Each call is one undo step.
|
| assetsA | Searches Roblox's Creator Store and inserts models into the place. search looks through the same public index Studio's own asset browser uses. It reports script COUNT, triangles, whether the creator is verified, whether the asset is free, and what Roblox thinks it is ("Door/Furniture"). insert puts one into the place by id.
Results are ranked by approval WEIGHTED BY vote count, because the raw percentage lies: 100% from two voters outranks 82% from five thousand unless the count is taken into account. The vote count is shown beside the percentage for the same reason. Filters — excludeScripts, maxTriangles, verifiedOnly, freeOnly, minVotes — are applied here, not by Roblox, and several pages are fetched to fill the results. Roblox's own sort and creator filters are accepted by the endpoint and silently ignored, so they are not offered. ALWAYS check hasScripts before inserting. Free models carrying scripts are the oldest hazard on the platform, and a model dropped into someone's game can run whatever it likes. The insert reports the script count again, and names them, so it can still be undone. peek is the safer half of that: it loads the asset in memory WITHOUT putting it in the place and tells you exactly what is inside — every class, every script by name. Nothing is parented, so there is nothing to undo. Use it whenever hasScripts says YES and the model still looks worth having.
Audio searches take a different path from everything else here. They go to the engine's own audio index, so results carry duration, artist and whether the clip is music or a sound effect — the fields that actually decide which sound you want. They return SOUND EFFECTS by default; pass audioType: "Music" for tracks. Filter with minDuration / maxDuration — a footstep is under a second and a music bed is minutes. Only public assets can be inserted. A private or deleted id fails with a message saying so rather than inserting nothing quietly. bake is unrelated to the Creator Store and does not upload anything. It turns EditableMesh and EditableImage data into static content, which frees the editable memory budget and lets a mesh built at runtime replicate from the server down to clients.
READ THIS BEFORE REACHING FOR IT. What it produces is scoped to the data model session it was made in. Baking in edit mode therefore carries NOTHING into a playtest — a playtest is a new data model, and the content reads as empty there. Measured, not assumed. Its real use is against a RUNNING playtest server session: pass that studioId, and baking a mesh the game just built is what lets clients see it. It does not help generate at all. Generated meshes hold opaque content, which the engine refuses to bake. THE OTHER DIRECTION: upload sends a local file TO Roblox and gives you the asset id. Audio, an image, a 3D model or a video, picked by extension — .mp3/.ogg/.wav/.flac, .png/.jpg/.bmp/.tga, .fbx/.gltf/.glb, .mp4/.mov. This closes the one hole nothing else here covers: a sound effect sitting in a folder on disk used to need Studio's import dialog before anything could reference it. Uploads are moderated and count against a real monthly quota. Do not guess what it is — Roblox's own guide and the live API disagree, and the account's verification level changes it. Ask op="quota". Do not upload speculatively, and do not re-upload to retry: the first one probably worked. grant gives a game or a person permission to use assets you own. You do NOT need this for your own assets in your own game — those always work. It is for a collaborator's place, or a group game you do not own. A grant to a game is PERMANENT; Roblox provides no way to revoke one, so it needs confirm: true.
publish sends a .rbxl or .rbxlx from disk to a place. It SAVES a new version by default and only goes live with confirm: true. Note a real limitation: Roblox's publishing API does not update EditableImage, EditableMesh, PartOperation, SurfaceAppearance or BaseWrap instances, and reports success anyway — publish from Studio if the place uses any of those.
Publishing alone does NOT move anyone already playing — they stay on their server running the old code until it empties. Pass restart: true to roll live servers onto the new version, which bleeds them off over 10 minutes rather than dropping players. quota reports how many uploads are left before Roblox starts refusing them, per asset type, read from the account itself. Check it before a batch rather than discovering the ceiling halfway through.
All of these need an Open Cloud API key. The user sets it once by typing cloud in the Studio panel; never ask them to paste a key into this conversation. |
| undoA | Steps Studio's undo history backwards or forwards. Every write this server makes is already wrapped in an undo recording, so this reverses your own work as cleanly as the user pressing Ctrl+Z — one tool call is one step. Use it when the user says an edit was wrong, instead of trying to reconstruct the previous state by hand, which is guesswork and usually incomplete. It reports how many steps actually applied, which is not always what was asked: the stack runs out, and an undo that did nothing otherwise looks exactly like one that worked. Studio's history covers the whole session, including the user's own edits — undoing more steps than you made will start reverting THEIR work. Undo only what you just did, and only when asked. |
| collisionA | Controls which parts physically collide with which. This is the right answer to 'these should pass through each other'. The alternative — turning CanCollide off — disables collision against everything, so a ghost that should pass through walls also falls through the floor. The order is: create a group, assign parts to it, then set what it is collidable with. A group with nothing assigned does nothing. Assigning a Model assigns every part inside it, which is almost always what is meant. Groups are not undoable and not scoped to a session: remove when one was created to try something and is no longer wanted, rather than leaving it registered in the place indefinitely. The built-in "Default" group cannot be removed. Groups belong to a world, not to the place. The Workspace is the default and is what nearly every question is about; a WorldModel inside a ViewportFrame keeps its own separate registry, so pass worldModel to reach that one. A group of the same name in each is two different groups. THE SAME TOOL ANSWERS WHAT IS ACTUALLY THERE. cast fires a ray, block or sphere and reports the first thing it meets — the part, the hit point, the surface normal, the material and the distance. overlap lists everything inside a box, a radius, or overlapping an existing part. That is the one question the Explorer cannot answer. A path tells you an instance exists and where its pivot sits; it does not tell you the door frame is clipping into the wall, that the spawn is buried a stud inside the floor, or that nothing stands between the turret and the player. Geometry wrong in exactly those ways looks perfect in inspect. The queries live here because they ARE collision queries: they honour the very groups the other half of this tool manages. A cast run in the wrong collisionGroup reports a clear path through a wall the player cannot walk through — a wrong answer indistinguishable from a right one. A miss comes back as hit: false, which is a real answer and usually the one being checked for. |
| generateA | Makes 3D geometry from a text prompt, using Roblox's Cube model. THE SCHEMA IS THE IMPORTANT ARGUMENT. It decides how the result is broken up, and it cannot be changed afterwards without another generation: Body1 — one MeshPart. Right for props: a crate, a tree, a lamp.
Car5 — a body and four wheels, under the fixed names body, front left wheel, front right wheel, rear left wheel, rear right wheel. Right for anything that has to drive, because a script can find the wheels by name.
groups — your own list of part names, for a structure the two predefined schemas do not cover.
Asking for a car under Body1 gives you a car-shaped rock. It looks right and nothing can be articulated. If you only realise afterwards, geometry op="segment" cuts an existing mesh into named parts without generating it again. Expect tens of seconds per call. The service is metered and moderated: a rejected prompt and a rate limit both come back as a failure that says which, so read the hint before retrying. imageAssetId conditions the generation on a picture — supply it with a prompt or instead of one. size suggests proportions and maxTriangles caps the poly count (low values give a faceted, low-poly look). Results are anchored on arrival, because a multi-part model dropped into the workspace unanchored falls apart.
EDIT MODE ONLY, for now. A generated mesh does not survive into a playtest: inside one, its MeshContent and TextureContent read as empty. assets op="bake" does not fix this — the engine refuses to bake the kind of content generation produces. So generate for building and greyboxing, and do not rely on a generated mesh being visible in a test or after a reopen until it has been published as a real asset. |
| characterA | Moves and acts as the player character in a running playtest, so gameplay can be tested without asking the user to play it. moveTo walks to a position or to an instance, following a path computed around walls and gaps rather than a straight line into them. It reports whether it ACTUALLY ARRIVED and how far short it stopped — a route blocked by something you did not know about otherwise looks identical to a successful walk.
act does the one-shot things worth testing: jump, sit, stand, respawn, kill (to exercise the death and respawn path), teleport, and equip/activate to use a Tool — which is how combat gets tested, since Activate is exactly what a mouse click triggers. Note that teleport skips everything in between, so triggers and collisions along the route do not fire — walk if you are testing those.
state reports position, health, walk speed and what the humanoid is doing. Call it before and after anything else here.
This drives the Humanoid directly rather than simulating keystrokes, which is the right tool for going places: pathfinding around a wall is one call here and a sequence of guessed key presses otherwise. For anything bound to a control rather than to movement — does E open the door, does the sprint key work, does Escape close the menu — use input, which sends real key and mouse events. REQUIRES A RUNNING PLAYTEST, and the character lives in the playtest's data model — address these to the playtest's studioId from list_studios, not the editor's. Run mode has no character at all; use playtest op=play. |