Use when writing, changing, debugging or playtesting Luau game code in this repo (game/ or runs/<slug>/game), when Roblox Studio or mcp__Roblox_Studio__* tools are involved, or when a change has to be checked inside a running Roblox game.
Instal·la
npx skillscat add chocoliticekreem/mubithack/roblox-studio-dev Instal·la des del registre de SkillsCat.
Developing the Roblox game with Studio MCP
Overview
Game code lives on disk as .luau files and is pushed into Roblox Studio automatically. You edit
files, never scripts inside Studio. You check your work with a real playtest driven over the Studio
MCP, then commit. The loop is: edit file → it syncs → playtest → read the report → fix → commit.
File → Studio mapping
On disk (game/…) |
In Studio |
|---|---|
ServerScriptService/X.server.luau |
Script (server) |
StarterPlayerScripts/X.client.luau |
LocalScript (under StarterPlayer) |
…/X.luau |
ModuleScript |
| directories | Folders |
Only scripts sync. Build parts, maps, UI and remotes from code (Instance.new), never by hand in
Studio, because hand-made objects aren't in git. The game's file layout per system is indocs/game/03-tech-architecture.md. Make each new system its own file so nobody else's file changes.
Setup check (once per session)
.venv/bin/forge doctor # Studio ✓ needed; if none: forge place --open, then enable MCP
.venv/bin/forge watch --studio Forge # run in background; every save → luau-compile → StudioIf forge watch can't run, push once with .venv/bin/forge sync --studio <name>. It checks the
files, rebuilds the manifest, serves them and pulls them in, and it never deletes anything.
Playtest over MCP (do it yourself, never ask the human to press Play)
list_roblox_studios→studio_id. Pick by id; the name is just a label. A local file shows asForge.rbxlx. A name withplaceIdis the shared published place (Firefighter Mubit, Team
Create), which may hold teammates' live work. In the shared place, only playtest or read, and
push only withforge syncfrommain.start_stop_play {is_start: true}. Then pollget_studio_stateuntilServeris listed.- Wait a few seconds, then read the ForgeProbe report with
execute_luau(datamodelServer):return game:GetService("ServerStorage").ForgeProbe:Invoke("report"). It returns JSON with
errors (with traces), warnings, players, counters and instance count. get_console_outputfor prints. Usescreen_capturewhen the change is visual.- Use
character_navigation,user_keyboard_inputoruser_mouse_input(datamodelClient) to
act like a player. - Always
start_stop_play {is_start: false}before you finish, even after a failure.
Testing with mubit-tester (MCP)
mcp__mubit-tester__* drives every player through the in-game harness (ServerStorage.ForgeTest,
Studio only) and knows our tags/attributes. Contract: docs/TESTER.md. Players are addressed by
name, UserId or helmet "1".."4". Same tools from the shell: forge test <tool> ... (-h lists them).
How to test a change (6 lines):
sync(or keepforge watchrunning) →statusshows harness ready after step 2.play_start(solo) ormultiplayer_start {players: 2}(if it can't, it prints the Test-tab steps).scenario_run {scenario: "smoke"}, then a scenario for your feature (scenario_list; add JSON toscenarios/).- Poke it:
players,query {tag},act {player, action, args},ignite,events {since},counters. errors(ForgeProbe) must be empty; for open-ended checksplaytest {goal, roles}orexplore {minutes}.play_stop, always, even after a failure.
AI tools go through Mubit. If an AI player chose wrong at step n, callplaytest_correct {run_id, step, note: "it should have ...", player}: that is a training example in
the player's Mubit task. suggest_fix {run_id, bug} proposes a patch; report/reports show runs.
Scenario steps: call, act (player: "all", offset), wait_until/assert (cmd, path,op, value, from, timeout, any), wait; save + "$var.path" reuse results
(see forge/tester/scenario.py).
Gotchas
| Symptom | Cause, and the fix |
|---|---|
| Edit tool rejected during play | Edit datamodel is locked while playing. Stop first, or use Server/Client. |
require in execute_luau returns a fresh, empty module |
execute_luau runs in its own VM. Reach live modules through ServerStorage.DevHook:Invoke("Module", "Method", ...). |
| Studio freezes or MCP drops | Long loops without yielding. Add task.wait() in loops and chunk big builds. |
| Change didn't show up | forge watch printed ✗ (syntax error), or a playtest was running (the change is queued until Stop). |
forge watch refuses: "published (Team Create) place" |
You picked the shared place. Open your local one: forge place --open, then --studio Forge. |
| Second start/stop fails | A known Studio bug. Retry once and check get_studio_state. |
A new file didn't arrive with a manual sync.py pull |
The manifest was stale. Use forge sync, or run sync.py manifest --root game first. |
| A deleted file is still in Studio | A no-prune sync never deletes. In your local place forge watch prunes. In the shared place, delete just that instance with execute_luau (Edit). |
| Errors about sound or asset permissions in the report | The place uses assets it isn't allowed to load. They're unrelated to your change, so ignore them unless you added that asset. |
| Counters missing from the report | Call require(game.ReplicatedStorage.ForgeTelemetry).count("name", 1) on the server. |
Luau rules
task.wait/task.spawn (never wait/spawn). Server-authoritative logic; clients only send
intent through RemoteEvents that the server creates and validates. Handle Players.PlayerAdded
and players already in the game. No _G. Keep each module small and owned by one system.
Finish
python3 game/tools/sync.py check --root gamepasses.scenario_run smokepasses,errorsis empty, and your feature's scenario orplaytestmeets its goal.- Studio is back in Edit, and the Workspace has no leftover test objects.
- Commit on your branch, then open a PR (
docs/TEAM.md).