Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
STUDIO_LIVE_HOMENoThe bridge home directory used for persisted controllers, Open Cloud key files and other state.~/.studio-live
STUDIO_LIVE_PORTNoThe bridge listens on 127.0.0.1:47800 (STUDIO_LIVE_PORT overrides; Studio connects to the literal 127.0.0.1).47800
ANTHROPIC_API_KEYNoAnthropic API credential so the look tool can call the Claude API directly (~2 s per look).
ANTHROPIC_AUTH_TOKENNoAnthropic API credential (alternative to ANTHROPIC_API_KEY) so the look tool can call the Claude API directly.
ROBLOX_OPEN_CLOUD_KEYNoRoblox Open Cloud API key for the open place, read per call by the cloud tool (alternative: <STUDIO_LIVE_HOME>/opencloud.json / .key).
STUDIO_LIVE_STUDIO_EXENoPath to a RobloxStudioBeta.exe, used by 'studio-live twin' to skip the version scan.
STUDIO_LIVE_GEOMETRY_POLICYNoSet to 'reject' (STUDIO_LIVE_GEOMETRY_POLICY=reject) to roll back edit-DM runs that add overlapping or nested parts with geometry_violation.
STUDIO_LIVE_VISION_PROVIDERNoChooses the vision provider for the look tool: auto | api | claude-cli (default auto: the API when a credential resolves, else the Claude CLI).auto

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
runA

Run a Luau program in Roblox Studio against the resident S API. Ship whole programs, not micro-calls: build, query and verify in one call and return one JSON-safe summary. Globals: S, ARGS (= args), print/warn (captured), game, workspace, task. dm 'edit' (default): ONE ChangeHistory recording — one undo step, rolled back on error/timeout. 'server' | 'client' | 'client:N': inside the live playtest, ephemeral (lost on stop, not undoable). code or code_file (absolute path the bridge reads). Never heredoc Luau: \n in a string becomes a real newline → syntax_error "Malformed string". S: get, ensure(path,class,props), find{root,name,class,tag,attr,max}, tree, props, new, set (unwritable props → result.unwritable), batchSet, clone, destroy (undoable), part, model, grid, placeOn(part,target,{align,gap}) sets a part on top of target, fits(cframe,size) → ok, blockers, overlaps(parts), script.get(path,{from,to})/set/patch/restart/create, emit, log, yield() (in long loops), wait, raycast, distance, remaining(). Geometry is enforced: parts intersecting parts (touching faces are fine) and BaseParts parented under BaseParts (use Models/Folders) are reported. geometry_policy warn (default) → result.geometry {overlaps[{a,b,depth}], nested[{path,parent}], checked, totals} + warnings; reject → rolled back, error geometry_violation with the report; off. Fix before moving on. Result: {value, output[], duration_ms, changes{added,removed,paths}, undo: committed|cancelled|unavailable|n/a, ephemeral, dm, warnings?, detached?, unwritable?, geometry?}. response_format 'detailed' adds 12-component CFrames. Errors: {error:{code: luau_error|syntax_error|timeout|no_peer|cancelled|busy|geometry_violation|…, message, stack, output}}. Still running after wait_ms (default 25000)? {job_id, status:'running'} — the program keeps running; use job. Edit-DM writes queue FIFO. dry_run (edit only) rolls back; a raw :Destroy() is neither rolled back nor undoable (use S.destroy). Several Studios connected: pass session.

observeA

Read-only view of Studio. what:

  • status: session, place{placeId,placeName,universeId,creatorType,creatorId}, peers, playtest, capabilities, fps, journal seq.

  • tree: root (game), depth (2, max 6), max (500), classes, fields (extra props) → {nodes:[{path,class,name,n,props?}], truncated} (explicit root always returned).

  • props: paths[], props[] → {items:[{path,class,props}], missing} (unreadable props omitted).

  • find: root, name (substring), class (IsA), tag, attr{name,value}, prop{name,value} → {items, total}.

  • diff: since (seq) → {added[], removed[]} instance paths changed since a cursor.

  • logs: since, level, filter, tail (100), dm ('all' = merged journal, lines carry src) → {items:[{seq,t,level,msg,src}], next}; startup prints of play DMs included.

  • script: path, from, to (1-based lines) → {path, class, lines, total_lines, text} (not cut at 8 KB). stats: fps, frameMs, heartbeatMs, physicsMs, instances, memoryMB. selection: {paths} (edit only).

  • geometry: root (Workspace), max (5000; sampled beyond), tolerance (0.05), include_nested (true) → {overlaps:[{a,b,depth}], nested:[{path,parent}], checked, sampled, ms, totals}: parts intersecting parts, BaseParts under BaseParts. Audit a build; fix all listed.

  • player: dm (client) → name, position, velocity, state, health, walkSpeed, cameraCFrame, floorMaterial, seated.

  • screenshot: the Studio window captured by the bridge (works occluded, un-minimizes without focus). max_width (1024; 0 = none), format jpeg|png, quality (70), region{x,y,w,h} window px, hwnd | title_match → image + {path,width,height,source_width,source_height,scale,windowTitle,hwnd,captured_ms}; image px × scale = window px. windows: lists Studio windows.

  • selftest: the hub's runtime self-check → {ok, checks[]}; use after install or when results look wrong. Prefer structured reads (exact, cheap); screenshots only for visual checks (look answers them in text). dm routes the read into the playtest. response_format 'detailed': ~200 KB cap, full CFrames.

playtestA

Control the live playtest and the in-engine runtime. Keep ONE playtest alive; a restart costs ~3 s and loses play-DM state.

  • start {mode: play|run|multiplayer, players}: waits for the runtimes → {running, mode, players, peers, started_ms}. multiplayer spawns a server DM plus players (1-8, default 2) client Studios (client:1..N; slow → job handle). no_peer: turn on "Load User Plugins In Run Modes" / install the plugin (test still running: Stop in Studio).

  • stop → {stopped_ms}. status → {running, mode, players, peers, elapsed_s, controllers}. add_players {count} (multiplayer).

  • run_until {dm, predicate | predicate_file, timeout_ms ≤ 120000, interval_ms, args}: Luau predicate evaluated in-engine each Heartbeat until truthy → {result: true|'timeout', value, elapsed_ms, checks}.

  • install {dm, name, code | code_file, persist}: code returns { load = function(ctx) … end, unload = function() … end }. ctx: S, assert(name,cond,detail), milestone, emit, log, onHeartbeat, onEvent, every, after, player, character(), input.*, moveTo (straight line), pathTo (pathfinding), state(), storage. Same name replaces; asserts/milestones → /events. persist: kept by the bridge, re-installed whenever that DM appears in a later playtest.

  • uninstall {dm, name} (drops the persisted entry). list → controllers on all peers + persisted.

  • hotpatch {dm, path, source | source_file, restart}: writes a script Source in the live DM and restarts it, playtest kept (ModuleScript: re-require needed).

  • push {paths, dm, parent, replace}: copies edit-DM instances into the live DM at their own paths (or under parent) → {dm, paths, count, bytes, replaced, skipped?, replicated}; replace (default true) removes a same-name, same-class sibling first (never Terrain, the camera, a character); ephemeral, not undoable. dm: 'server' | 'client' | 'client:N'. *_file = absolute path the bridge reads (never heredoc Luau). Slow actions return {job_id, status:'running'} after wait_ms; use job.

inputA

Human-like input in a play client through the real input pipeline (dm 'client' = lowest-numbered client, or 'client:N'). actions run in order:

  • {type:'key', key:'W', hold_ms:600 (≤ 60000)} press, hold, release; {type:'key', key:'Space', down:true|false} single edge

  • {type:'click', x, y, button:'left'|'right'|'middle'} down, 50 ms, up; {type:'move', x, y} absolute cursor; {type:'look', dx, dy} relative camera look — best effort: the step fails unless the camera actually rotated (the default camera script ignores virtual deltas; drive workspace.CurrentCamera from a run instead)

  • {type:'text', text}; {type:'focus', path:'PlayerGui.Hud.Input'} TextBox:CaptureFocus(); {type:'wait', ms (≤ 60000)} x,y default to GUI space (gui: true): the coordinates a GuiObject reports as AbsolutePosition; the runtime adds GuiService:GetGuiInset(). gui: false sends raw viewport pixels (inset included). Screenshot pixels are NOT viewport pixels: the screenshot is the whole Studio window scaled by scale; the 3D viewport sits inside it at an offset you must calibrate (see the agent guide). Input only reaches the game while the Studio window renders (not minimized). Result: {steps:[{i, ok, error?}], elapsed_ms}. A failing step does not abort the sequence unless abort_on_error=true; keys still held when a sequence aborts or is cancelled are released. No scroll action: virtual input produces no MouseWheel events. For reactive or long-running input, install a controller with playtest.

eventsA

Backfill from the bridge's event journal (ring of 10,000 per session). Returns events with seq > since, oldest first → {cursor, events[], dropped, truncated, latest_seq, hub_dropped}. cursor is the seq of the last event actually returned: pass it as the next since. truncated means more events wait after cursor; dropped > 0 means the ring evicted events you never saw. kinds filters by event type: log, error, assert, milestone, custom, playtest, peer, selection, job, controller, change (default all); levels filters log events (print|info|warn|error). timeout_ms > 0 long-polls: returns as soon as a matching event arrives, or empty when the timeout (≤ 50000) elapses. This is the portable fallback to push. Prefer Monitor on ws://127.0.0.1:/events: batched frames ≤ 4 KB carrying seq and dropped; default kinds error, assert, milestone, custom, playtest, peer, controller, job plus warn/error logs (?kinds=…&levels=… per socket). After any dropped > 0 or seq gap on the socket, call events with since = the last seq you saw.

skillsA

Luau program library on disk (/skills/.luau with a --[[ studio-live skill … ]] header holding name, description and params). Save programs you will run again; run them with args (ARGS), same environment and result shape as run. action: list → [{name, description, params, builtin}]; get {name} → {source, …}; save {name, source | source_file (absolute path), description, params}; delete {name}; run {name, args, dm, undo_label, dry_run, geometry_policy, timeout_ms, response_format, wait_ms, session} → the run result (geometry checked exactly as for run). Builtins ship read-only (builtin: true): settle_physics, device_sim, profile_scripts, bulk_attributes, insert_asset, lighting_preset, list_scripts, remote_map — get one to read its params. Saving a skill with a builtin name overrides it; deleting the override restores it; builtins themselves cannot be deleted.

jobA

Track long operations that returned {job_id, status:'running'}. status {job_id} → {status: running|done|error, op, dm, elapsed_ms, progress, notes, hub_connected, result?, error?}. wait {job_id, wait_ms ≤ 50000} blocks until the job finishes or the wait elapses, then returns the same snapshot. list → {jobs:[…]} running first, then recent ones (find an id you lost). cancel {job_id} asks Studio to stop the program at its next S.yield()/slice boundary and rolls back an edit-DM recording; the job then finishes with error.code 'cancelled'. Jobs survive a Studio reconnect (hub_connected false while it is away) and end at their deadline. Finished jobs expire 10 minutes after completion.

cloudA

Roblox Open Cloud for the place open in Studio. IDs default to the connected session (universe = game.GameId, place = game.PlaceId, creator = the place owner); pass universe_id / place_id / id / creator only to override. Needs an API key: env ROBLOX_OPEN_CLOUD_KEY or /opencloud.json {"key":"…"}, re-read every call and never returned. A 403 names the exact Creator Hub permission to add — or call info what:"key" first: it probes the key and reports allowed | denied | unknown per action. action (ops in parens; arg help is on each field):

  • datastore (list_stores|list_entries|get|set|delete|increment), ordered (list|get|set|delete|increment): persistent entries; etag makes a set conditional.

  • memory (map_list|map_get|map_set|map_delete|queue_add|queue_read|queue_discard): MemoryStore sorted maps + queues, fast cross-server state with a ttl.

  • message: topic + message (≤1 KB) → MessagingService in live servers, not playtests.

  • info (universe|place|group|user|me|key|memberships|roles|inventory|subscription): reads. key = the capability probe.

  • publish: uploads a local .rbxl/.rbxlx as the place's new live version. Do this before luau / instance, which read the PUBLISHED place, never the Studio session.

  • asset_upload: file + asset_type (Model|Decal|Audio|Video|Animation|Mesh|Image) → asset_id + moderation. asset (get|update|versions|rollback|archive|restore): the rest of the lifecycle; update puts a new .fbx behind an existing Model asset_id.

  • luau: runs a script in a fresh server copy of the published place; returns results + logs.

  • instance (get|update|children): read/edit instances of the published place ("root" = the DataModel). For the OPEN place use the run tool.

  • restriction (list|get|ban|unban|logs): ban a user from the experience or one place.

  • notify: send an experience notification to a user (message_id = a Creator Hub template). Results are JSON ≤ 20 KB; long calls return pending: true + a re-poll handle after timeout_ms.

lookA

Look at the Roblox Studio window through a vision model and get a short TEXT answer instead of an image — the screenshot never enters your context (a 768 px frame is ≈ 500 image tokens on the sidecar, a few hundred characters back to you). One-shot: {question, max_width? (768), region? {x,y,w,h} window px, model?} → {answer, model, provider, captured_ms, model_ms, usage, frame_path}. Ask concrete visual questions: 'is there a red error in the Output panel? quote it', 'where is the Play button (region or px)?', 'is the character standing on the platform or falling?'. The model sees only the screenshot and answers 'not visible' when it cannot tell. Watch: {watch: {question, interval_s (5, min 2; 15 on claude-cli), max_frames (60), stop_when? (substring, or /regex/i), diff_only? (true)}} → {watch_id}. Frames are analysed in the background, one model call at a time; each answer arrives as a Monitor event {type:'vision', watch_id, frame, answer, changed, provider}. With diff_only, frames whose bytes differ < 2% from the last analysed frame are skipped (no model call, no event). The watch ends on max_frames, when the answer matches stop_when, on {stop: watch_id | 'all'}, or after 3 consecutive failures; the last event has done:true and reason. {list: true} shows running watches with counts and the last answer. Prefer observe tree|props|find|player for state — they are exact and free. look is for what only pixels can tell: rendering, layout, UI text, visual glitches, what a playtest looks like. Works with an Anthropic API key (ANTHROPIC_API_KEY / ant auth login, ~2 s per look) OR a logged-in Claude Code install (claude on PATH, ~10–15 s per look on the subscription); STUDIO_LIVE_VISION_PROVIDER = auto (default) | api | claude-cli. Models: STUDIO_LIVE_VISION_MODEL (look; default claude-opus-5 / sonnet on the CLI) and STUDIO_LIVE_WATCH_MODEL (watch; default claude-sonnet-5 / haiku).

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A4.4/5.0

Scored across 9 tools

Disambiguation4/5

Each tool has a distinct primary job—observe for structured reads, look for vision QA, run for scripted execution, playtest for session lifecycle, input for synthetic control, events for journal backfill, skills for reusable programs, job for async tracking, cloud for Open Cloud. A couple of near-overlaps remain (observe status/logs versus playtest status and the events journal), but descriptions generally call out when to use each.

Naming Consistency5/5

All tool names are lowercase single words in a consistent shorthand style (observe, look, run, playtest, input, events, skills, job, cloud). No casing or separator conventions are mixed, making the naming predictable.

Tool Count5/5

Nine top-level tools is well-scoped for a Studio automation server; each tool covers a necessary capability, and the broad observe modes are bundled under one read tool rather than fragmented into many MCP tools. Nothing feels redundant at the top level.

Completeness5/5

The surface covers the full loop: inspect (observe), visually verify (look), mutate and script (run), control playtests (playtest), send input (input), consume events (events), persist reusable programs (skills), manage long jobs (job), and access Open Cloud (cloud). The run tool's S API also provides an escape hatch for anything not explicitly modeled, so there are no obvious dead ends.

Maintenance

ActivityMaintained
ResponsivenessNo issues