Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1", features = ["v4", "serde"] }
axum = { version = "0.8", features = ["macros"] }
reqwest = { version = "0.12", features = ["json"] }
urlencoding = "2.1"
color-eyre = "0.6"
clap = { version = "4.5.37", features = ["derive"] }
roblox_install = "1.0.0"
base64 = "0.22"

[target.'cfg(target_os = "macos")'.dependencies]
native-dialog = "0.8.8"
Expand Down
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,57 @@ To make sure everything is set up correctly, follow these steps:
which you can also verify in the console output.
1. Verify that Claude Desktop is correctly configured by clicking on the hammer icon for MCP tools
beneath the text field where you enter prompts. This should open a window with the list of
available Roblox Studio tools (`insert_model` and `run_code`).
available Roblox Studio tools (`insert_model`, `run_code`, and `capture_screenshot`).

**Note**: You can fix common issues with setup by restarting Studio and Claude Desktop. Claude
sometimes is hidden in the system tray, so ensure you've exited it completely.

## Available Tools

The MCP server provides the following tools for Claude to interact with Roblox Studio:

### `run_code`
Runs Luau code in Roblox Studio and returns the printed output. Can be used to make changes or retrieve information from the currently open place.

**Example prompts:**
- "Add a red part to the workspace"
- "List all parts in the workspace"
- "Move the camera to position (0, 50, 50)"

### `insert_model`
Inserts a model from the Roblox marketplace into the workspace. Returns the inserted model name.

**Example prompts:**
- "Insert a tree model"
- "Add a car from the marketplace"

### `capture_screenshot`
Captures a screenshot of the Roblox Studio window and returns it as base64-encoded PNG data. This allows Claude to visually analyze your workspace, debug UI issues, or verify changes.

**Example prompts:**
- "Take a screenshot of my workspace"
- "Show me what the current scene looks like"
- "Screenshot the studio and analyze the lighting"

**Requirements:**
- **macOS**: Screen Recording permission must be granted to Terminal (or your MCP client)
- Go to **System Settings** → **Privacy & Security** → **Screen Recording**
- Enable **Terminal** (or your MCP client application)
- Restart Terminal/client after granting permission
- **Windows**: No additional permissions required

**Note:** The screenshot captures the entire Roblox Studio window, including all panels and UI elements. The Studio window does not need to be focused or in the foreground.

## Send requests

1. Open a place in Studio.
1. Type a prompt in Claude Desktop and accept any permissions to communicate with Studio.
1. Verify that the intended action is performed in Studio by checking the console, inspecting the
data model in Explorer, or visually confirming the desired changes occurred in your place.

## Using `insert_model`

The `insert_model` tool searches the Roblox catalog for free models and inserts them into your place. For this to work, your place must have HTTP requests enabled:

1. **Publish your place** — The place must be published to Roblox (it can be private). Go to **File > Publish to Roblox** in Studio.
2. **Enable HTTP requests** — In Studio, go to **Home > Game Settings > Security** and enable **Allow HTTP Requests**.
48 changes: 9 additions & 39 deletions plugin/src/Tools/InsertModel.luau
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,6 @@ local function getInsertPosition()
end
end

local InsertService = game:GetService("InsertService")

type GetFreeModelsResponse = {
[number]: {
CurrentStartIndex: number,
TotalCount: number,
Results: {
[number]: {
Name: string,
AssetId: number,
AssetVersionId: number,
CreatorName: string,
},
},
},
}

local function toTitleCase(str: string): string
local function titleCase(first: string, rest: string)
return first:upper() .. rest:lower()
Expand Down Expand Up @@ -81,28 +64,10 @@ local function loadAsset(assetId: number): Instance?
return collapseObjectsIntoContainer(objects)
end

local function getAssets(query: string): number?
local results: GetFreeModelsResponse = InsertService:GetFreeModels(query, 0)
local assets = {}
for i, result in results[1].Results do
if i > 6 then
break
end
table.insert(assets, result.AssetId)
end

return table.remove(assets, 1)
end

local function insertFromMarketplace(query: string): string
local primaryResult = getAssets(query)
if not primaryResult then
error("Failed to find asset")
end

local instance = loadAsset(primaryResult)
local function insertFromAssetId(assetId: number, query: string): string
local instance = loadAsset(assetId)
if not instance then
error("Failed to load asset")
error("Failed to load asset with ID: " .. tostring(assetId))
end

local name = toTitleCase(query)
Expand Down Expand Up @@ -132,7 +97,12 @@ local function handleInsertModel(args: Types.ToolArgs): string?
error("Missing query in InsertModel")
end

return insertFromMarketplace(insertModelArgs.query)
-- Use asset_id provided by the Rust server (from catalog API)
if insertModelArgs.asset_id then
return insertFromAssetId(insertModelArgs.asset_id, insertModelArgs.query)
else
error("No asset_id provided - catalog search must be done server-side")
end
end

return handleInsertModel :: Types.ToolFunction
1 change: 1 addition & 0 deletions plugin/src/Types.luau
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export type InsertModelArgs = {
query: string,
asset_id: number?,
}

export type RunCodeArgs = {
Expand Down
Loading