roblox-mcp-pro 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: roblox-mcp-pro
3
+ description: >-
4
+ How to drive the roblox-mcp-pro MCP server to control a live Roblox Studio session — running
5
+ Luau, querying/mutating the DataModel, building UI, terrain, lighting/effects, audio/animation,
6
+ spatial queries, and bidirectional Studio↔disk sync. Use this whenever the task involves the
7
+ roblox-mcp-pro tools (names like execute_luau, query_instances, mutate_instances, manage_sync,
8
+ manage_ui, manage_terrain, manage_lighting, spatial_query, batch_execute, workspace_state),
9
+ or whenever the user wants an agent to change things inside Roblox Studio through this server —
10
+ even if they don't name a specific tool. Read this before the first tool call to pick the right
11
+ tool and confirm the connection.
12
+ ---
13
+
14
+ # Using roblox-mcp-pro
15
+
16
+ roblox-mcp-pro exposes 23 tools that act on a **live Roblox Studio session**. The MCP server
17
+ talks to a Studio plugin over a localhost bridge; the plugin runs each command with full plugin
18
+ privileges and returns structured results. Your job is to choose the right tool and verify the
19
+ session is actually connected before relying on results.
20
+
21
+ ## Always start with the connection check
22
+
23
+ Tools fail with `Error: Roblox Studio plugin is not connected…` if no Studio session is attached.
24
+ Before a sequence of changes, call **`system_info`** once. If `bridge.pluginConnected` is false,
25
+ tell the user to open Studio and click the **MCP** button on the toolbar (it must be highlighted),
26
+ and that the roblox-mcp-pro server must be running. Don't keep retrying blindly — surface the fix.
27
+
28
+ `workspace_state` is a good second call to orient yourself (place name, child counts per service,
29
+ camera, selection) before editing.
30
+
31
+ ## Choosing the right tool
32
+
33
+ Prefer the **specific** tool over `execute_luau`. The structured tools return clean, validated
34
+ data and guard against damaging the session (they refuse to touch CoreGui/CorePackages). Reach for
35
+ `execute_luau` only for one-off logic no dedicated tool covers — and remember its output is
36
+ captured print/warn plus `return` values.
37
+
38
+ | You want to… | Use |
39
+ | --- | --- |
40
+ | Read instances / the tree | `query_instances` |
41
+ | Create / edit / move / clone / delete instances | `mutate_instances` |
42
+ | Read or write specific properties | `manage_properties` |
43
+ | Do many edits atomically (one undo) | `batch_execute` |
44
+ | Run arbitrary Luau | `execute_luau` |
45
+ | Find parts by space (box, radius, raycast, nearest) | `spatial_query` |
46
+ | Generate/edit terrain | `manage_terrain` |
47
+ | Time of day, fog, ambient, shadows | `manage_lighting` |
48
+ | Bloom/Blur/ColorCorrection/DoF/Atmosphere/Sky | `manage_effects` |
49
+ | Move/aim the camera | `manage_camera` |
50
+ | Animate properties over time | `manage_tween` |
51
+ | Physical props / welds | `manage_physics` |
52
+ | Build GUI (ScreenGui/Frame/labels/buttons) | `manage_ui` |
53
+ | Create/preview Sound | `manage_audio` |
54
+ | Animation instances / preview | `manage_animation` |
55
+ | Insert marketplace assets by id | `manage_assets` |
56
+ | Read/write script source, create scripts | `manage_scripts` |
57
+ | Mirror Studio↔disk both ways | `manage_sync` |
58
+ | Explorer selection | `manage_selection` |
59
+ | Studio version/theme/run state | `manage_studio` |
60
+ | Recent Output logs | `manage_logs` |
61
+ | High-level session snapshot | `workspace_state` |
62
+
63
+ ## Patterns that work well
64
+
65
+ - **Batch related edits.** Building several parts or a whole structure? Wrap the operations in
66
+ `batch_execute` so it's one undo step and one round-trip, not N. Each step reports its own
67
+ ok/error, so a single bad op doesn't lose the rest.
68
+ - **Verify after you change.** After a `mutate_instances`/`manage_ui` create, a quick
69
+ `query_instances` (with `include_properties: true`) confirms it landed as intended.
70
+ - **Property value shapes.** Vectors are `[x,y,z]`; Color3 is `[r,g,b]` (0–1); UDim2 is
71
+ `[[xScale,xOffset],[yScale,yOffset]]`; CFrame is a 12-number array; enums and BrickColor are
72
+ their string names. Values are coerced to the property's current type, so match the existing type.
73
+ - **Check logs after running code.** If `execute_luau` or a playtest misbehaves, `manage_logs`
74
+ returns the recent Output history (newest first) so you can see prints and errors.
75
+
76
+ ## Bidirectional sync (`manage_sync`)
77
+
78
+ `manage_sync` mirrors a Studio subtree to `roblox-mcp-sync/place_{placeId}/explorer/` and keeps
79
+ both sides in step, plus a `sourcemap.json` for luau-lsp. This is how you give the agent (and the
80
+ human) a stable on-disk copy of the project to edit with normal file tools.
81
+
82
+ - `action: "start"` — snapshot + begin two-way watching. Defaults to the script-bearing services
83
+ (ServerScriptService, ReplicatedStorage, StarterGui, StarterPlayer, ServerStorage). Pass
84
+ `roots: ["Workspace.Systems", …]` to scope differently.
85
+ - `action: "pull"` — force Studio → disk (after big structural changes).
86
+ - `action: "push"` — force disk → Studio (apply all local script edits).
87
+ - `action: "stop"` / `"status"`.
88
+
89
+ Script sync is fully two-way: creating, editing, or deleting a `.luau` file on disk creates,
90
+ updates, or deletes the script in Studio, and the same edits in Studio flow back to disk. The one
91
+ remaining gap is that non-script property changes in Studio mirror on resync (when instances are
92
+ added/removed), not live — use `pull` to resynchronize when in doubt.
93
+
94
+ ## When something fails
95
+
96
+ Errors are returned as actionable text. Common ones: not connected (fix the plugin/server),
97
+ "protected service" (CoreGui/CorePackages are intentionally off-limits), and per-operation errors
98
+ inside `batch_execute`/`mutate_instances` results (other operations still ran). Read the `error`
99
+ string and adjust rather than retrying the same call.
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: roblox-studio-plugin
3
+ description: >-
4
+ How to build, package, and install a high-quality Roblox Studio plugin in Luau — toolbar
5
+ buttons, dock widgets, the plugin lifecycle, persisting state across reloads, undo via
6
+ ChangeHistoryService, reading/writing script source via ScriptEditorService, HttpService from
7
+ plugins, and packaging to .rbxmx with Rojo. Use this whenever building or editing a Roblox
8
+ Studio plugin (anything under a plugin/ folder, a `*.server.luau` plugin entry, PluginToolbar,
9
+ PluginToolbarButton, CreateDockWidgetPluginGui, plugin:GetSetting/SetSetting, plugin:Activate),
10
+ or when the user wants to make, package, install, or debug a Studio plugin — even if they only
11
+ say "Studio plugin", "toolbar button", or "rbxmx". Read this before writing plugin code; it
12
+ encodes non-obvious Studio behaviors that are easy to get wrong.
13
+ ---
14
+
15
+ # Building Roblox Studio plugins
16
+
17
+ A Studio plugin is Luau that runs inside Studio's editor with elevated privileges (the global
18
+ `plugin` object). It can read/edit the DataModel, add toolbar UI, talk to localhost over HTTP,
19
+ and register undo waypoints. The hard part isn't the API surface — it's a handful of Studio
20
+ behaviors that silently misbehave if you don't know them. This skill front-loads those.
21
+
22
+ A complete, tested reference implementation lives in this repo under `plugin/` (entry
23
+ `plugin/src/init.server.luau`, modules in `plugin/src/`). Read it for a working example.
24
+
25
+ ## Project layout (Rojo)
26
+
27
+ Structure the plugin as a directory that Rojo compiles into one `.rbxmx`:
28
+
29
+ ```
30
+ plugin/
31
+ default.project.json # { "name": "MyPlugin", "tree": { "$path": "src" } }
32
+ src/
33
+ init.server.luau # a folder with init.server.luau becomes the root *Script* (the plugin)
34
+ Foo.luau # sibling ModuleScripts → require(script.Foo)
35
+ Handlers/Bar.luau # subfolder → require(script.Handlers.Bar)
36
+ ```
37
+
38
+ Build + install (Windows):
39
+
40
+ ```powershell
41
+ rojo build default.project.json --output MyPlugin.rbxmx
42
+ Copy-Item MyPlugin.rbxmx (Join-Path $env:LOCALAPPDATA "Roblox\Plugins\MyPlugin.rbxmx") -Force
43
+ ```
44
+
45
+ `%LOCALAPPDATA%\Roblox\Plugins` is the local plugins folder. Pin the Rojo version in `aftman.toml`
46
+ so builds are reproducible.
47
+
48
+ ## Gotchas that will bite you
49
+
50
+ These are the non-obvious behaviors. Internalize them — each one is a bug we hit and fixed.
51
+
52
+ - **`PluginToolbarButton` has no settable `Text` or `Tooltip`.** The label/tooltip are fixed at
53
+ `CreateButton(id, tooltip, icon, text)` time. Assigning `button.Text = …` errors at runtime.
54
+ Show on/off state with `button:SetActive(bool)` (it highlights), not by changing text.
55
+ - **Studio does NOT reliably hot-reload local `.rbxmx` plugins.** After rebuilding, the running
56
+ plugin is often the *old* one until Studio restarts (or sometimes regains focus). Always log a
57
+ startup line like `("loaded %d tools"):format(n)` (and a version) so you can confirm in the
58
+ Output which build is actually live. When new code "isn't taking effect", suspect this first.
59
+ - **Plugin state resets every reload.** Persist anything that should survive a reload with
60
+ `plugin:SetSetting(key, value)` / `plugin:GetSetting(key)`. A common pattern: save whether the
61
+ plugin was "active", and on load auto-restore it so iterating doesn't require re-clicking.
62
+ - **One bad module breaks the whole plugin.** If `require(child)` errors at load, the requiring
63
+ module errors too, cascading to the entry script and the plugin silently does nothing. Load
64
+ optional/numerous modules defensively so a single failure disables only that feature:
65
+ ```lua
66
+ local map = {}
67
+ for name, moduleName in pairs(HANDLER_NAMES) do
68
+ local ok, mod = pcall(require, Handlers[moduleName])
69
+ if ok then map[name] = mod else warn("failed to load " .. moduleName .. ": " .. tostring(mod)) end
70
+ end
71
+ ```
72
+ - **`loadstring` works in plugin context** (unlike in a running game without LoadStringEnabled),
73
+ but guard it: `if type(loadstring) ~= "function" then …`. Capture printed output by connecting
74
+ `LogService.MessageOut` for the duration of the call rather than trying to override `print`
75
+ (`setfenv` is unavailable in modern Luau).
76
+
77
+ ## Toolbar + lifecycle skeleton
78
+
79
+ ```lua
80
+ local toolbar = plugin:CreateToolbar("My Plugin")
81
+ -- label & tooltip are fixed here; state is shown via SetActive
82
+ local button = toolbar:CreateButton("MyToggle", "Toggle the thing", "", "My Plugin")
83
+ button.ClickableWhenViewportHidden = true
84
+
85
+ local active = false
86
+ local function setActive(v)
87
+ active = v
88
+ button:SetActive(v)
89
+ plugin:SetSetting("active", v) -- survive reloads
90
+ -- start/stop your work here
91
+ end
92
+
93
+ button.Click:Connect(function() setActive(not active) end)
94
+ plugin.Unloading:Connect(function() active = false end)
95
+
96
+ if plugin:GetSetting("active") == true then setActive(true) end -- auto-restore
97
+ ```
98
+
99
+ For richer UI, use a dock widget:
100
+ `plugin:CreateDockWidgetPluginGui(id, DockWidgetPluginGuiInfo.new(...))`, then parent a ScreenGui's
101
+ contents into it. Keep it lazy — only build the widget when first shown.
102
+
103
+ ## Editing the DataModel safely
104
+
105
+ - **Wrap mutations in undo recordings** so the user can Ctrl+Z your changes:
106
+ ```lua
107
+ local rec = ChangeHistoryService:TryBeginRecording("My action")
108
+ -- …make changes…
109
+ if rec then ChangeHistoryService:FinishRecording(rec, Enum.FinishRecordingOperation.Commit) end
110
+ ```
111
+ Use `Cancel` instead of `Commit` when the operation failed, so a no-op doesn't pollute history.
112
+ - **Read/write script source via `ScriptEditorService`**, not just `.Source`. `GetEditorSource(s)`
113
+ reflects unsaved edits in an open document; `UpdateSourceAsync(s, fn)` writes safely even when
114
+ the script is open in the editor. Fall back to `s.Source` if the service call fails.
115
+ - **Never edit `CoreGui` / `CorePackages`** (and the plugin's own GUI service). Guard against it —
116
+ walking an instance's ancestry and refusing protected names prevents corrupting the session.
117
+
118
+ ## HttpService from a plugin
119
+
120
+ Plugins can call localhost. `HttpService:RequestAsync` **yields**, so run loops in `task.spawn`.
121
+ A long-poll request blocks up to the server's hold time (well under the ~60s client timeout), so
122
+ it's fine for command/event channels. Best-effort enable with `pcall(function()
123
+ HttpService.HttpEnabled = true end)` and surface a clear message if requests fail (HTTP disabled or
124
+ server down) instead of spamming — warn once per outage and back off.
125
+
126
+ ## Edit-mode caveats for common services
127
+
128
+ Most things work in edit mode, but a few are subtle:
129
+ - `TweenService:Create(…):Play()` advances in edit mode (Heartbeat runs) — usable for previews.
130
+ - Playing animations on a rig needs an `Animator`; visible motion of a programmatic
131
+ `LoadAnimation():Play()` may require Play mode.
132
+ - `Sound:Play()` previews in edit mode.
133
+ - `Selection` (`game:GetService("Selection")`) reads/sets the Explorer selection; great for
134
+ pointing the user at what you changed.
135
+
136
+ ## Verifying a plugin
137
+
138
+ You usually can't fully drive Studio headlessly. Verify by: build + install, restart/reopen Studio,
139
+ confirm your startup log line appears (proves the new build loaded), click the toolbar button, and
140
+ watch the Output. If the plugin pairs with an external process (e.g. an MCP/localhost server), a
141
+ harness that starts the server and exercises it end-to-end while the real plugin answers is the
142
+ most reliable check — see this repo's test pattern.
@@ -0,0 +1,233 @@
1
+ ---
2
+ name: roblox-ui-animation
3
+ description: >-
4
+ How to animate a Roblox GUI through the roblox-mcp-pro tools — open/close transitions, slide-ins,
5
+ fades, scale "pops", button hover/press feedback, and reproducing a motion you see in a reference
6
+ video. Covers TweenService/TweenInfo, which GUI properties to tween (Size, Position, transparency,
7
+ UIScale, Rotation), easing choices, and a growing library of reusable motion patterns. Use this
8
+ whenever the task involves animating UI in Roblox Studio — phrases like "animate this popup",
9
+ "make the menu slide in", "add an open/close animation", "tween the panel", "button hover effect",
10
+ "match the animation in this clip/video", or whenever a UI should move/fade/scale rather than just
11
+ appear. Pairs with manage_ui (build the GUI) and manage_tween/execute_luau (drive the motion).
12
+ Read this before writing any UI tween.
13
+ ---
14
+
15
+ # Animating Roblox UI
16
+
17
+ UI motion in Roblox is almost always `TweenService` interpolating GUI properties over time. The
18
+ craft is picking **what** to tween, over **how long**, with **which easing**, so the result reads
19
+ the way the reference does. Build the static UI first (see the `roblox-ui-from-image` skill), then
20
+ layer motion on top.
21
+
22
+ Drive tweens with `execute_luau` (full control, can `:Wait()` on completion) or the `manage_tween`
23
+ tool for simple single-property tweens. Tweens **run in Studio edit mode** (Heartbeat advances), so
24
+ you can build and watch them without entering Play mode.
25
+
26
+ ## Decode an animation from a reference video
27
+
28
+ When the user gives you a clip/video of the motion they want, don't guess from memory — **extract
29
+ frames and read the motion**. You can view images directly, so turn the video into images:
30
+
31
+ 1. **Probe** it: `ffprobe -v error -select_streams v:0 -show_entries stream=width,height,r_frame_rate,duration -of default=noprint_wrappers=1 <file>`.
32
+ 2. **Survey montage** — one image, whole clip at a glance, to find the animation windows and how
33
+ many cycles there are:
34
+ ```bash
35
+ ffmpeg -v error -i <file> -vf "fps=3,scale=300:-1,tile=6x7" -frames:v 1 survey.png
36
+ ```
37
+ 3. **Zoom in** on one transition at high fps to read direction + easing frame-by-frame:
38
+ ```bash
39
+ ffmpeg -v error -ss <start> -t <dur> -i <file> -vf "fps=15,scale=480:-1,tile=5x3" -frames:v 1 open.png
40
+ ```
41
+ 4. **Write the frames to a Windows-readable path** (e.g. `C:/Users/<you>/Downloads/_anim/`) so the
42
+ Read tool can open them — `/tmp` (bash) isn't visible to Read on Windows.
43
+ 5. **Read the montages** and write a **spec**: what element moves, which property changes (size /
44
+ position / transparency / scale / rotation), start→end values, duration, easing
45
+ (accelerate-in / decelerate-out / overshoot), and any delay/stagger between elements.
46
+
47
+ Gotchas reading video: `drawtext` needs fontconfig (often missing) — skip frame-number overlays and
48
+ track frames by tile position instead. A montage tile is low-res; zoom tighter (higher `-ss`/`-t`,
49
+ fewer tiles, bigger `scale`) when you need to nail easing or see small elements.
50
+
51
+ State the spec back to the user and note easing values are estimates from the footage (tunable).
52
+
53
+ ## Fundamentals
54
+
55
+ **TweenInfo** is the recipe: `TweenInfo.new(time, easingStyle, easingDirection, repeatCount,
56
+ reverses, delayTime)`.
57
+
58
+ - **Time**: UI snaps best at **0.15–0.35s**. Longer feels sluggish; shorter is imperceptible.
59
+ - **EasingStyle** — match the feel:
60
+ | Feel | Style / Direction |
61
+ | --- | --- |
62
+ | Smooth, neutral settle (default choice) | `Quad`/`Quint` + `Out` |
63
+ | Snappy, energetic settle | `Quint`/`Expo` + `Out` |
64
+ | Playful overshoot (pops past then back) | `Back` + `Out` |
65
+ | Bouncy landing | `Bounce` + `Out` |
66
+ | Elastic wobble | `Elastic` + `Out` |
67
+ | Symmetric in-and-out (e.g. a pulse) | `Sine` + `InOut` |
68
+ - `Out` decelerates into the end (best for things appearing). `In` accelerates (best for things
69
+ leaving). `InOut` for moves between two on-screen states.
70
+
71
+ **What to tween** (and how):
72
+ - **Size** — grow/shrink. For a "scale pop" prefer a child **`UIScale`** (`Scale` 0→1) over tweening
73
+ `Size` directly: it scales children too and won't fight layout. **A scale pivots around the
74
+ object's `AnchorPoint`** — set it to `[0.5,0.5]` for a symmetric pop from the center; an edge anchor
75
+ (e.g. `[1,0.5]`) makes it grow toward that edge, which looks lopsided. This applies to *every*
76
+ scale animation (panel open, hover pop): the anchor is the pivot. For an element that must stay
77
+ edge-pinned for layout, re-center it first (preserve its rect by reading `AbsolutePosition`/
78
+ `AbsoluteSize`) or scale an inner wrapper instead.
79
+ - **Position** — slide in/out. Tween a `UDim2`; pair with `AnchorPoint` so it slides from the right edge.
80
+ - **Transparency** — fade. `BackgroundTransparency` + `TextTransparency`/`ImageTransparency`. To fade
81
+ a whole panel as one unit, wrap it in a **`CanvasGroup`** and tween `GroupTransparency` (one value
82
+ fades everything, including strokes/gradients).
83
+ - **Rotation** — spin/settle (e.g. an icon, or a slight tilt-in).
84
+ - **UICorner / UIStroke.Thickness** — secondary polish.
85
+
86
+ **Sequencing / stagger**: animate list items with a small increasing `delayTime` (e.g. 0.04s × index)
87
+ so rows cascade instead of popping together — reads as "alive".
88
+
89
+ ## Pattern library
90
+
91
+ ### 1. Scale-pop panel + staggered children
92
+
93
+ The popup **scales up from its center** (a small box that grows to full size with a slight overshoot
94
+ "pop"); on close it scales back down. Its contents (cards/rows) then **fade in one by one** in a
95
+ cascade. This is the classic "juicy" game HUD popup — what most reference clips of menus opening show.
96
+
97
+ Two independent layers:
98
+ - **Panel scale** via a `UIScale` (`Scale` 0→1). The panel **must be center-anchored**
99
+ (`AnchorPoint = [0.5,0.5]`) so it grows from the middle — the anchor is the pivot of the scale.
100
+ `Back`/`Out` gives the overshoot pop.
101
+ - **Child stagger** via per-item **transparency** with an increasing `delayTime`. Fade — *not* Size
102
+ or UIScale — because items in a `UIListLayout` reflow when their `AbsoluteSize` changes, making the
103
+ list jump. Transparency doesn't touch layout.
104
+
105
+ ```lua
106
+ local TweenService = game:GetService("TweenService")
107
+ local panel = script.Parent:WaitForChild("Panel")
108
+ panel.AnchorPoint = Vector2.new(0.5, 0.5) -- pivot of the scale = center
109
+ panel.Position = UDim2.new(0.5, 0, 0.5, 0)
110
+ local scale = Instance.new("UIScale"); scale.Parent = panel
111
+
112
+ local OPEN = TweenInfo.new(0.30, Enum.EasingStyle.Back, Enum.EasingDirection.Out)
113
+ local CLOSE = TweenInfo.new(0.20, Enum.EasingStyle.Quad, Enum.EasingDirection.In)
114
+
115
+ -- every transparency-bearing prop of a subtree, so we can hide → fade it back in
116
+ local FADE = { BackgroundTransparency = true, TextTransparency = true, ImageTransparency = true }
117
+ local function faders(root)
118
+ local out, scan = {}, root:GetDescendants(); table.insert(scan, root)
119
+ for _, d in ipairs(scan) do
120
+ for p in pairs(FADE) do
121
+ local ok, v = pcall(function() return d[p] end)
122
+ if ok and typeof(v) == "number" and v < 1 then table.insert(out, { d, p, v }) end
123
+ end
124
+ if d:IsA("UIStroke") then table.insert(out, { d, "Transparency", d.Transparency }) end
125
+ end
126
+ return out
127
+ end
128
+
129
+ local rows = {} -- the cards, in layout order
130
+ for _, r in ipairs(panel.List:GetChildren()) do
131
+ if r:IsA("GuiObject") then table.insert(rows, r) end
132
+ end
133
+ table.sort(rows, function(a, b) return a.LayoutOrder < b.LayoutOrder end)
134
+
135
+ local function open()
136
+ scale.Scale = 0
137
+ for _, r in ipairs(rows) do
138
+ for _, f in ipairs(faders(r)) do f[1][f[2]] = 1 end -- hide cards
139
+ end
140
+ panel.Visible = true
141
+ TweenService:Create(scale, OPEN, { Scale = 1 }):Play() -- pop the panel
142
+ for i, r in ipairs(rows) do -- cascade the cards
143
+ local delay = (i - 1) * 0.05
144
+ for _, f in ipairs(faders(r)) do
145
+ TweenService:Create(f[1],
146
+ TweenInfo.new(0.22, Enum.EasingStyle.Quad, Enum.EasingDirection.Out, 0, false, delay),
147
+ { [f[2]] = f[3] }):Play()
148
+ end
149
+ end
150
+ end
151
+
152
+ local function close()
153
+ return TweenService:Create(scale, CLOSE, { Scale = 0 }) -- :Play() then hide on .Completed
154
+ end
155
+ ```
156
+
157
+ The cards also *rise* a little as they fade in the reference. A true positional rise inside a
158
+ `UIListLayout` fights the layout — wrap each card in a `CanvasGroup` and tween `GroupTransparency`
159
+ plus an inner offset, rather than moving the card frame itself.
160
+
161
+ For **hover pop** on the buttons, the same pivot rule bites: a button anchored `[1,0.5]` pops toward
162
+ its right edge. Re-center it before adding the hover `UIScale`:
163
+ ```lua
164
+ local function recenter(o) -- preserve the rect, move the anchor to the middle
165
+ local rel = o.AbsolutePosition - o.Parent.AbsolutePosition
166
+ o.AnchorPoint = Vector2.new(0.5, 0.5)
167
+ o.Position = UDim2.fromOffset(math.round(rel.X + o.AbsoluteSize.X/2),
168
+ math.round(rel.Y + o.AbsoluteSize.Y/2))
169
+ end -- run after layout settles (RunService.Heartbeat:Wait())
170
+ ```
171
+
172
+ ### 2. Clip-reveal panel (vertical "unroll")
173
+
174
+ The panel keeps full width and **grows in height from its top edge**; a `ClipsDescendants` container
175
+ reveals its contents top-to-bottom like unrolling a scroll. Close is the reverse. Snappy and clean —
176
+ common in game HUD popups.
177
+
178
+ **Critical rule — anchor every child to the TOP** (position by offset from the top edge). A child
179
+ anchored to the panel's *bottom* edge (`AnchorPoint.Y = 1`, `Position [[..],[1,..]]`) will **slide up
180
+ with the shrinking edge instead of being revealed in place**, breaking the illusion. Convert any
181
+ bottom-pinned child to a top offset at the same visual Y before animating.
182
+
183
+ ```lua
184
+ local TweenService = game:GetService("TweenService")
185
+
186
+ -- one-time rig: clip + re-anchor to the top so a height tween unrolls downward.
187
+ local function setupReveal(panel)
188
+ local fullH = panel.Size.Y.Offset -- assumes a fixed-offset height
189
+ panel.ClipsDescendants = true
190
+ panel.AnchorPoint = Vector2.new(0.5, 0)
191
+ panel.Position = UDim2.new(0.5, 0, 0.5, -fullH / 2) -- same visual center as a centered panel
192
+ panel:SetAttribute("FullH", fullH)
193
+ end
194
+
195
+ local OPEN = TweenInfo.new(0.32, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)
196
+ local CLOSE = TweenInfo.new(0.26, Enum.EasingStyle.Quint, Enum.EasingDirection.In)
197
+
198
+ local function open(panel)
199
+ local h = panel:GetAttribute("FullH")
200
+ panel.Size = UDim2.new(panel.Size.X.Scale, panel.Size.X.Offset, 0, 0)
201
+ panel.Visible = true
202
+ TweenService:Create(panel, OPEN,
203
+ { Size = UDim2.new(panel.Size.X.Scale, panel.Size.X.Offset, 0, h) }):Play()
204
+ end
205
+
206
+ local function close(panel)
207
+ local t = TweenService:Create(panel, CLOSE,
208
+ { Size = UDim2.new(panel.Size.X.Scale, panel.Size.X.Offset, 0, 0) })
209
+ t.Completed:Connect(function() panel.Visible = false end)
210
+ t:Play()
211
+ end
212
+ ```
213
+
214
+ Optional flourish seen in reference clips: a **burst/sparkle** sprite that scales up and fades at the
215
+ center on open/close — an `ImageLabel` with a starburst asset, `UIScale` 0.5→1.4 + `ImageTransparency`
216
+ 0→1 over ~0.25s. Needs a real `rbxassetid://` image.
217
+
218
+ <!-- Append new patterns here as they're decoded: scale-pop, slide-in, fade+blur backdrop, stagger list, etc. -->
219
+
220
+ ## Gotchas
221
+
222
+ - **Verifying motion with a single screenshot is impossible.** `capture_studio` is one frame. To
223
+ sanity-check a transition, set the element to a **mid-state** (e.g. 45% of the way) and capture
224
+ that, or `:Wait()` on the tween and capture the settled end state. Tell the user to watch the live
225
+ tween in Studio for the motion itself.
226
+ - **`ui_preview` shows a clone.** Tweening the StarterGui original won't animate the preview overlay.
227
+ Hide the preview and run the tween on the real GUI (Studio renders StarterGui in the edit viewport).
228
+ - **ColorSequence (UIGradient.Color) can't be tweened by TweenService.** Tween `UIGradient.Offset` or
229
+ `Rotation` for motion, or step the color in a `RenderStepped`/`Heartbeat` loop.
230
+ - **Reuse, don't recreate.** Define `open`/`close` once (a ModuleScript or LocalScript) and call them;
231
+ don't rebuild tweens per click.
232
+ - **Clean up on close.** Disconnect `Completed`/input connections and set `Visible=false` after a
233
+ close tween so a hidden panel doesn't eat clicks.
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: roblox-ui-from-image
3
+ description: >-
4
+ How to reproduce a UI design/mockup as a Roblox GUI through the roblox-mcp-pro tools — turning a
5
+ pasted screenshot, Figma export, or reference image into a pixel-close ScreenGui. Use whenever
6
+ the user supplies an image of a UI (HUD, menu, shop, popup, title screen, inventory, settings
7
+ panel) and wants it built in Roblox Studio, or says things like "make this UI", "build this
8
+ screen", "copy this layout", "match this design". Covers the build→preview→capture→compare→refine
9
+ loop with manage_ui, ui_preview, and capture_studio, plus UDim2/Color3 conventions and component
10
+ recipes. Read this before building UI from a reference.
11
+ ---
12
+
13
+ # Building Roblox UI from an image
14
+
15
+ Goal: given a reference image of a UI, produce a Roblox `ScreenGui` that looks like it. The win
16
+ comes from a **visual feedback loop** — build, look at the real render, compare to the mockup, fix
17
+ — not from one-shot guessing.
18
+
19
+ ## The loop
20
+
21
+ 1. **Get the image.** The user pastes/drags it into chat (you see it directly — no tool needed) or
22
+ gives a file path you can read. Study it before building.
23
+ 2. **Analyze the design** (see checklist below) — structure, sizes, colors, fonts, spacing.
24
+ 3. **Build** with `manage_ui` (`action: "create"`, `replace: true` so re-runs don't stack copies).
25
+ 4. **Preview clean**: `ui_preview` (`action: "show"`, `path:` your ScreenGui) renders it full-screen
26
+ on a solid backdrop in edit mode (hides the 3D scene behind it).
27
+ 5. **Capture**: `capture_studio` — now you see your UI isolated, centered in the viewport.
28
+ 6. **Compare** the capture to the mockup. Note differences (position, size, color, font, spacing).
29
+ 7. **Refine** with `manage_ui` (`action: "set"`) on the specific paths, then repeat 5–6 until close.
30
+ 8. **Done**: `ui_preview` (`action: "hide"`) to remove the preview overlay.
31
+
32
+ Always `system_info` first to confirm the plugin is connected. Studio renders `StarterGui` content
33
+ in the edit viewport, so a capture without `ui_preview` also works — but `ui_preview` gives a clean
34
+ backdrop for accurate comparison.
35
+
36
+ ## Analyze the mockup (before building)
37
+
38
+ - **Hierarchy**: outermost container(s) → panels → elements. Map to ScreenGui → Frame(s) → children.
39
+ - **Anchoring/position**: is it centered, corner-pinned, edge-docked? Pick `AnchorPoint` + `Position`
40
+ accordingly (center = `AnchorPoint [0.5,0.5]`, `Position [[0.5,0],[0.5,0]]`).
41
+ - **Sizing**: fixed px (offset) vs proportional (scale). Cards/buttons are usually offset; full-screen
42
+ dim layers are scale.
43
+ - **Colors**: background, panel, accent, text. Convert to `[r,g,b]` 0–1.
44
+ - **Text**: content, font (GothamBold/Gotham/etc.), size, alignment, color.
45
+ - **Corners/strokes/gradients**: rounded → `UICorner`; outline → `UIStroke`; gradient → `UIGradient`.
46
+ - **Repetition/lists**: rows/grids → `UIListLayout`/`UIGridLayout` instead of hand-placing each item.
47
+
48
+ ## Conventions (roblox-mcp-pro property formats)
49
+
50
+ - `Size`/`Position` are **UDim2**: `[[xScale,xOffset],[yScale,yOffset]]`.
51
+ - Centered 360×200 card: `Size [[0,360],[0,200]]`, `AnchorPoint [0.5,0.5]`, `Position [[0.5,0],[0.5,0]]`.
52
+ - `BackgroundColor3`/`TextColor3` are **Color3** `[r,g,b]` 0–1 (e.g. `[0.12,0.14,0.2]`).
53
+ - `AnchorPoint` is `[x,y]` (Vector2, 0–1). `Font` is an enum name string (`"GothamBold"`).
54
+ - `UICorner.CornerRadius` is a **UDim**: `[scale,offset]` → `[0,16]` for 16px corners.
55
+ - Set `BorderSizePixel: 0` on Frames/buttons; `BackgroundTransparency: 1` on text-only labels.
56
+
57
+ ## Component recipes
58
+
59
+ **Rounded panel**
60
+ ```
61
+ { className:"Frame", name:"Card",
62
+ properties:{ Size:[[0,360],[0,220]], AnchorPoint:[0.5,0.5], Position:[[0.5,0],[0.5,0]],
63
+ BackgroundColor3:[0.12,0.14,0.2], BorderSizePixel:0 },
64
+ children:[ { className:"UICorner", properties:{ CornerRadius:[0,16] } } ] }
65
+ ```
66
+
67
+ **Button (rounded, accent — with edge highlight + readable text)**
68
+ ```
69
+ { className:"TextButton", name:"Play",
70
+ properties:{ Text:"PLAY", Font:"GothamBold", TextSize:20, TextColor3:[1,1,1],
71
+ BackgroundColor3:[0.2,0.7,0.4], Size:[[0,160],[0,48]], BorderSizePixel:0 },
72
+ children:[ { className:"UICorner", properties:{ CornerRadius:[0,10] } },
73
+ // edge highlight — ApplyStrokeMode "Border" (see below); Color is a LIGHTER tint of
74
+ // the fill ([0.2,0.7,0.4] → [0.4,0.95,0.5]) so the button pops
75
+ { className:"UIStroke", name:"Border",
76
+ properties:{ ApplyStrokeMode:"Border", Thickness:2, Color:[0.4,0.95,0.5] } },
77
+ // text outline — a SECOND stroke, Contextual + dark, so white text stays legible
78
+ { className:"UIStroke", name:"TextStroke",
79
+ properties:{ ApplyStrokeMode:"Contextual", Thickness:2, Color:[0,0,0] } } ] }
80
+ ```
81
+
82
+ **Button with a gradient background (gradient must NOT tint the text)**
83
+ A `UIGradient` on a `TextButton` recolors the **text too**, not just the fill. To keep the gradient
84
+ on the button only, give the button empty `Text` and move the label into a child `TextLabel`.
85
+ Also: `UIGradient` **multiplies** with the object's own color, so set the fill `BackgroundColor3` to
86
+ **white `[1,1,1]`** — otherwise the gradient comes out tinted/darker than the colors you specified:
87
+ ```
88
+ { className:"TextButton", name:"Play",
89
+ properties:{ Text:"", BackgroundColor3:[1,1,1], Size:[[0,160],[0,48]], BorderSizePixel:0 },
90
+ children:[ { className:"UICorner", properties:{ CornerRadius:[0,10] } },
91
+ { className:"UIGradient", properties:{ Rotation:90 } }, // set .Color via execute_luau
92
+ { className:"UIStroke", name:"Border",
93
+ properties:{ ApplyStrokeMode:"Border", Thickness:2, Color:[0.4,0.95,0.5] } },
94
+ // text in its OWN label → the button's gradient can't reach it
95
+ { className:"TextLabel", name:"Label",
96
+ properties:{ Text:"PLAY", Font:"GothamBold", TextSize:20, TextColor3:[1,1,1],
97
+ BackgroundTransparency:1, Size:[[1,0],[1,0]] },
98
+ children:[ { className:"UIStroke", properties:{ Thickness:2, Color:[0,0,0] } } ] } ] }
99
+ ```
100
+
101
+ **Vertical list (auto-stacked rows)**
102
+ ```
103
+ { className:"Frame", name:"List", properties:{ BackgroundTransparency:1, Size:[[1,0],[1,0]] },
104
+ children:[ { className:"UIListLayout",
105
+ properties:{ Padding:[0,8], FillDirection:"Vertical",
106
+ HorizontalAlignment:"Center", SortOrder:"LayoutOrder" } },
107
+ /* row frames here */ ] }
108
+ ```
109
+
110
+ ## Notes & limits
111
+
112
+ - **One-call builds**: pass the whole nested `tree` to `manage_ui create` — don't create node-by-node.
113
+ - **Iterate with `set`**: once built, tweak single properties via `manage_ui set` on the exact path
114
+ (cheaper than rebuilding) — unless restructuring, then `create` with `replace:true`.
115
+ - **ImageLabel/ImageButton** need a real `Image` asset id (`rbxassetid://…`). If the mockup has
116
+ images you don't have ids for, build the layout with placeholder Frames and tell the user which
117
+ images to upload.
118
+ - **Gradients/sequences**: `UIGradient.Color` (ColorSequence) and similar sequence properties may not
119
+ coerce from simple arrays — set a solid color first, and use `execute_luau` for a true gradient if
120
+ needed. A `UIGradient` on a `TextButton`/`TextLabel` also **tints the text**, not just the fill —
121
+ to gradient the button only, give the button empty `Text` and move the label into a child
122
+ `TextLabel` with `BackgroundTransparency:1` (see the gradient-button recipe above).
123
+ - **UIGradient multiplies with the base color**: the gradient is *multiplied over* the object's own
124
+ color, so to get the exact ColorSequence colors you specified, first set the underlying property to
125
+ **white** — `BackgroundColor3:[1,1,1]` for a fill gradient, `TextColor3:[1,1,1]` for a text
126
+ gradient. A non-white base darkens/tints the whole gradient.
127
+ - **UIStroke on buttons/text — the two-stroke rule**: a `UIStroke` under a `TextButton`/`TextLabel`
128
+ defaults to `ApplyStrokeMode = "Contextual"`, which outlines the **text glyphs, not the button
129
+ border**. So a single default stroke gives you a text outline when you wanted an edge highlight.
130
+ - For a button **edge/glow highlight**, set `ApplyStrokeMode:"Border"`.
131
+ - **Highlight color = a lighter/whiter tint of the button's own fill**, not a contrasting accent.
132
+ Push the `BackgroundColor3` toward white (raise each channel, e.g. green fill `[0.2,0.7,0.4]` →
133
+ edge `[0.4,0.95,0.5]`) so the rim reads as a lit edge and the button visually pops off the panel.
134
+ - To get **both** an edge highlight *and* a crisp text outline, add **two** UIStrokes on the same
135
+ button: one `"Border"` (the lighter-tint edge) and one `"Contextual"` (the text). They target
136
+ different parts and both render.
137
+ - The **text** stroke should be a **dark/near-black** color so white button text stays legible.
138
+ - After previewing, always `ui_preview hide` so the overlay doesn't linger in CoreGui.
package/README.md CHANGED
@@ -199,22 +199,16 @@ behavior, set the env var `ROBLOX_MCP_NO_PLUGIN_AUTOINSTALL=1`.)
199
199
  Open Roblox Studio — a **Roblox MCP Pro** button appears in the toolbar. (Studio turns on HTTP
200
200
  requests automatically when you connect; if needed, set `HttpService.HttpEnabled = true`.)
201
201
 
202
- ### Part 3 — Install the agent skills (recommended)
202
+ ### Part 3 — Agent skills (automatic)
203
203
 
204
204
  Skills are short guides that teach your AI how to use the tools well (building UI from an image,
205
- animating GUIs, writing Studio plugins, etc.). **Only Claude Code and Codex read skills today**
206
- other clients still work fine, just without the extra guidance.
205
+ animating GUIs, writing Studio plugins, etc.). **You don't need to do anything** the server
206
+ installs them for you on startup, the same way it installs the plugin. Only **Claude Code**
207
+ (`~/.claude/skills`) and **Codex** (`~/.codex/skills`) have a skills mechanism, so skills are
208
+ copied there when those clients are present; other clients still work fine, just without the extra
209
+ guidance.
207
210
 
208
- The Windows quick-installer above does this for you. To do it manually, copy each folder from this
209
- repo's `.agents/skills/` into your agent's skills directory:
210
-
211
- | Agent | Skills folder |
212
- |-------|----------------|
213
- | Claude Code | `%USERPROFILE%\.claude\skills\` |
214
- | Codex | `%USERPROFILE%\.codex\skills\` |
215
-
216
- Skills to copy: `roblox-mcp-pro`, `roblox-ui-from-image`, `roblox-ui-animation`,
217
- `roblox-studio-plugin`. Each ends up as `…\skills\<name>\SKILL.md`.
211
+ (To opt out, set `ROBLOX_MCP_NO_SKILL_AUTOINSTALL=1`.)
218
212
 
219
213
  ### Part 4 — Connect & verify
220
214
 
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ import { BRIDGE_HOST, BRIDGE_PORT } from "./constants.js";
16
16
  import { resolveLicense } from "./licensing/license.js";
17
17
  import { installLicenseGate } from "./licensing/gate.js";
18
18
  import { installPlugin, ensurePluginInstalled } from "./install-plugin.js";
19
+ import { ensureSkillsInstalled } from "./install-skills.js";
19
20
  import { VERSION } from "./version.js";
20
21
  function log(message) {
21
22
  process.stderr.write(`[roblox-mcp-pro] ${message}\n`);
@@ -43,6 +44,13 @@ async function main() {
43
44
  log(`updated Studio plugin → restart Roblox Studio to load the new version`);
44
45
  }
45
46
  }
47
+ // Keep agent skills up to date for skill-capable clients (Claude Code, Codex).
48
+ if (process.env.ROBLOX_MCP_NO_SKILL_AUTOINSTALL !== "1") {
49
+ const skills = await ensureSkillsInstalled();
50
+ if (skills.changed > 0) {
51
+ log(`installed/updated ${skills.changed} agent skill file(s)`);
52
+ }
53
+ }
46
54
  const server = new McpServer({
47
55
  name: "roblox-studio-mcp-server",
48
56
  version: "0.1.0",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Agent-skill distribution. The product skills (guides that teach an agent how
3
+ * to drive the tools well) ship inside this npm package. On server startup we
4
+ * copy them into the skill folders of any skill-capable client present on the
5
+ * machine, so a customer gets them with zero effort, the same way the Studio
6
+ * plugin self-installs.
7
+ *
8
+ * Only Claude Code (~/.claude/skills) and Codex (~/.codex/skills) have a skills
9
+ * mechanism today; other clients still work, just without the extra guidance.
10
+ */
11
+ export interface SkillSyncResult {
12
+ /** Number of skill files newly written or updated. */
13
+ changed: number;
14
+ /** Skill-capable clients that received skills. */
15
+ clients: number;
16
+ }
17
+ /**
18
+ * Copy bundled skills into each present skill-capable client, writing only when
19
+ * a file is missing or different. Best-effort: never throws.
20
+ */
21
+ export declare function ensureSkillsInstalled(): Promise<SkillSyncResult>;
22
+ //# sourceMappingURL=install-skills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-skills.d.ts","sourceRoot":"","sources":["../src/install-skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAsCH,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,eAAe,CAAC,CAiCtE"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Agent-skill distribution. The product skills (guides that teach an agent how
3
+ * to drive the tools well) ship inside this npm package. On server startup we
4
+ * copy them into the skill folders of any skill-capable client present on the
5
+ * machine, so a customer gets them with zero effort, the same way the Studio
6
+ * plugin self-installs.
7
+ *
8
+ * Only Claude Code (~/.claude/skills) and Codex (~/.codex/skills) have a skills
9
+ * mechanism today; other clients still work, just without the extra guidance.
10
+ */
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { promises as fs } from "node:fs";
15
+ /** Product skills bundled in the package (must match package.json `files`). */
16
+ const SKILLS = [
17
+ "roblox-mcp-pro",
18
+ "roblox-studio-plugin",
19
+ "roblox-ui-animation",
20
+ "roblox-ui-from-image",
21
+ ];
22
+ /** Skill-capable clients: parent dir that must exist → its skills/ root. */
23
+ function skillTargets() {
24
+ const h = os.homedir();
25
+ return [
26
+ { parent: path.join(h, ".claude"), skillsDir: path.join(h, ".claude", "skills") },
27
+ { parent: path.join(h, ".codex"), skillsDir: path.join(h, ".codex", "skills") },
28
+ ];
29
+ }
30
+ function skillsSourceDir() {
31
+ // dist/install-skills.js → ../.agents/skills (package root; shipped by npm).
32
+ const here = path.dirname(fileURLToPath(import.meta.url));
33
+ return path.join(here, "..", ".agents", "skills");
34
+ }
35
+ async function readIfPresent(p) {
36
+ try {
37
+ return await fs.readFile(p);
38
+ }
39
+ catch {
40
+ return null;
41
+ }
42
+ }
43
+ /**
44
+ * Copy bundled skills into each present skill-capable client, writing only when
45
+ * a file is missing or different. Best-effort: never throws.
46
+ */
47
+ export async function ensureSkillsInstalled() {
48
+ const result = { changed: 0, clients: 0 };
49
+ const sourceDir = skillsSourceDir();
50
+ for (const target of skillTargets()) {
51
+ try {
52
+ // Only act on clients that are actually installed.
53
+ await fs.access(target.parent);
54
+ }
55
+ catch {
56
+ continue;
57
+ }
58
+ result.clients++;
59
+ for (const skill of SKILLS) {
60
+ try {
61
+ const src = path.join(sourceDir, skill, "SKILL.md");
62
+ const source = await readIfPresent(src);
63
+ if (!source)
64
+ continue; // skill not in package; skip quietly
65
+ const destDir = path.join(target.skillsDir, skill);
66
+ const dest = path.join(destDir, "SKILL.md");
67
+ const existing = await readIfPresent(dest);
68
+ if (existing && existing.equals(source))
69
+ continue;
70
+ await fs.mkdir(destDir, { recursive: true });
71
+ await fs.writeFile(dest, source);
72
+ result.changed++;
73
+ }
74
+ catch {
75
+ // Skip this skill on error; keep going.
76
+ }
77
+ }
78
+ }
79
+ return result;
80
+ }
81
+ //# sourceMappingURL=install-skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-skills.js","sourceRoot":"","sources":["../src/install-skills.ts"],"names":[],"mappings":"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"}
@@ -2,13 +2,10 @@
2
2
  * Reusable Zod fragments shared across tool schemas.
3
3
  */
4
4
  import { z } from "zod";
5
- import { ResponseFormat } from "../types.js";
6
5
  /** A dotted instance path rooted at `game`, e.g. "game.Workspace.Baseplate". */
7
6
  export declare const InstancePath: z.ZodString;
8
7
  /** A 3-number [x, y, z] vector. */
9
8
  export declare const Vec3: z.ZodArray<z.ZodNumber, "many">;
10
- /** Output format selector (markdown for humans, json for machines). */
11
- export declare const responseFormat: z.ZodDefault<z.ZodNativeEnum<typeof ResponseFormat>>;
12
9
  /** Standard pagination fragment. */
13
10
  export declare const pagination: {
14
11
  limit: z.ZodDefault<z.ZodNumber>;
@@ -1 +1 @@
1
- {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/schemas/common.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,gFAAgF;AAChF,eAAO,MAAM,YAAY,aAOtB,CAAC;AAEJ,mCAAmC;AACnC,eAAO,MAAM,IAAI,iCAG0B,CAAC;AAE5C,uEAAuE;AACvE,eAAO,MAAM,cAAc,sDAKxB,CAAC;AAEJ,oCAAoC;AACpC,eAAO,MAAM,UAAU;;;CActB,CAAC"}
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/schemas/common.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gFAAgF;AAChF,eAAO,MAAM,YAAY,aAOtB,CAAC;AAEJ,mCAAmC;AACnC,eAAO,MAAM,IAAI,iCAG0B,CAAC;AAE5C,oCAAoC;AACpC,eAAO,MAAM,UAAU;;;CActB,CAAC"}
@@ -2,7 +2,6 @@
2
2
  * Reusable Zod fragments shared across tool schemas.
3
3
  */
4
4
  import { z } from "zod";
5
- import { ResponseFormat } from "../types.js";
6
5
  /** A dotted instance path rooted at `game`, e.g. "game.Workspace.Baseplate". */
7
6
  export const InstancePath = z
8
7
  .string()
@@ -15,11 +14,6 @@ export const Vec3 = z
15
14
  .array(z.number())
16
15
  .length(3)
17
16
  .describe("A 3-number [x, y, z] vector.");
18
- /** Output format selector (markdown for humans, json for machines). */
19
- export const responseFormat = z
20
- .nativeEnum(ResponseFormat)
21
- .default(ResponseFormat.MARKDOWN)
22
- .describe("Output format: 'markdown' for human-readable text or 'json' for machine-readable.");
23
17
  /** Standard pagination fragment. */
24
18
  export const pagination = {
25
19
  limit: z
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/schemas/common.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,gFAAgF;AAChF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC;KAC1B,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;KAChC,GAAG,CAAC,GAAG,EAAE,qCAAqC,CAAC;KAC/C,QAAQ,CACP,2EAA2E;IACzE,2BAA2B,CAC9B,CAAC;AAEJ,mCAAmC;AACnC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC;KAClB,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACjB,MAAM,CAAC,CAAC,CAAC;KACT,QAAQ,CAAC,8BAA8B,CAAC,CAAC;AAE5C,uEAAuE;AACvE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC;KAC5B,UAAU,CAAC,cAAc,CAAC;KAC1B,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC;KAChC,QAAQ,CACP,mFAAmF,CACpF,CAAC;AAEJ,oCAAoC;AACpC,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,OAAO,CAAC,GAAG,CAAC;SACZ,QAAQ,CAAC,2DAA2D,CAAC;IACxE,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,OAAO,CAAC,CAAC,CAAC;SACV,QAAQ,CAAC,wDAAwD,CAAC;CACtE,CAAC"}
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/schemas/common.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,gFAAgF;AAChF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC;KAC1B,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,wBAAwB,CAAC;KAChC,GAAG,CAAC,GAAG,EAAE,qCAAqC,CAAC;KAC/C,QAAQ,CACP,2EAA2E;IACzE,2BAA2B,CAC9B,CAAC;AAEJ,mCAAmC;AACnC,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC;KAClB,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACjB,MAAM,CAAC,CAAC,CAAC;KACT,QAAQ,CAAC,8BAA8B,CAAC,CAAC;AAE5C,oCAAoC;AACpC,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,OAAO,CAAC,GAAG,CAAC;SACZ,QAAQ,CAAC,2DAA2D,CAAC;IACxE,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,EAAE;SACL,GAAG,CAAC,CAAC,CAAC;SACN,OAAO,CAAC,CAAC,CAAC;SACV,QAAQ,CAAC,wDAAwD,CAAC;CACtE,CAAC"}
package/dist/types.d.ts CHANGED
@@ -35,11 +35,6 @@ export interface BridgeStatus {
35
35
  inflight: number;
36
36
  lastPollAt: number | null;
37
37
  }
38
- /** Output format shared by many tools. */
39
- export declare enum ResponseFormat {
40
- MARKDOWN = "markdown",
41
- JSON = "json"
42
- }
43
38
  /** A change event pushed from Studio -> server (used by sync). */
44
39
  export interface StudioEvent {
45
40
  /** Event kind, e.g. "added" | "removing" | "changed". */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,uEAAuE;AACvE,MAAM,WAAW,OAAO;IACtB,6BAA6B;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,OAAO,CAAC;IACd,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,gCAAgC;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qEAAqE;AACrE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,0CAA0C;AAC1C,oBAAY,cAAc;IACxB,QAAQ,aAAa;IACrB,IAAI,SAAS;CACd;AAED,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,uEAAuE;AACvE,MAAM,WAAW,OAAO;IACtB,6BAA6B;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,IAAI,EAAE,OAAO,CAAC;IACd,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,gCAAgC;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qEAAqE;AACrE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB"}
package/dist/types.js CHANGED
@@ -1,10 +1,5 @@
1
1
  /**
2
2
  * Core type definitions shared across the MCP server and the Studio bridge.
3
3
  */
4
- /** Output format shared by many tools. */
5
- export var ResponseFormat;
6
- (function (ResponseFormat) {
7
- ResponseFormat["MARKDOWN"] = "markdown";
8
- ResponseFormat["JSON"] = "json";
9
- })(ResponseFormat || (ResponseFormat = {}));
4
+ export {};
10
5
  //# sourceMappingURL=types.js.map
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAuCH,0CAA0C;AAC1C,MAAM,CAAN,IAAY,cAGX;AAHD,WAAY,cAAc;IACxB,uCAAqB,CAAA;IACrB,+BAAa,CAAA;AACf,CAAC,EAHW,cAAc,KAAd,cAAc,QAGzB"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roblox-mcp-pro",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "MCP server that lets AI agents control Roblox Studio: live Luau execution, instance query/mutate, UI/terrain/lighting, and bidirectional Studio<->local sync. Paid subscription with a free trial.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Peerapol",
@@ -32,6 +32,10 @@
32
32
  "plugin/src",
33
33
  "plugin/default.project.json",
34
34
  "plugin/RobloxMcpPro.rbxmx",
35
+ ".agents/skills/roblox-mcp-pro",
36
+ ".agents/skills/roblox-studio-plugin",
37
+ ".agents/skills/roblox-ui-animation",
38
+ ".agents/skills/roblox-ui-from-image",
35
39
  "README.md",
36
40
  "LICENSE"
37
41
  ],