robloxstudio-mcp 2.7.0-next.4 → 2.7.0-next.6
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.
- package/dist/index.js +51 -17
- package/package.json +48 -48
- package/studio-plugin/MCPInspectorPlugin.rbxmx +9011 -0
- package/studio-plugin/MCPPlugin.rbxmx +48 -24
- package/studio-plugin/inspector-icon.png +0 -0
- package/studio-plugin/src/modules/handlers/ScriptHandlers.ts +38 -17
- package/studio-plugin/src/server/index.server.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -158,7 +158,7 @@ var TOOL_HANDLERS = {
|
|
|
158
158
|
}),
|
|
159
159
|
get_script_source: (tools, body) => tools.getScriptSource(body.instancePath, body.startLine, body.endLine),
|
|
160
160
|
set_script_source: (tools, body) => tools.setScriptSource(body.instancePath, body.source),
|
|
161
|
-
edit_script_lines: (tools, body) => tools.editScriptLines(body.instancePath, body.old_string, body.new_string),
|
|
161
|
+
edit_script_lines: (tools, body) => tools.editScriptLines(body.instancePath, body.old_string, body.new_string, body.startLine),
|
|
162
162
|
insert_script_lines: (tools, body) => tools.insertScriptLines(body.instancePath, body.afterLine, body.newContent),
|
|
163
163
|
delete_script_lines: (tools, body) => tools.deleteScriptLines(body.instancePath, body.startLine, body.endLine),
|
|
164
164
|
get_attribute: (tools, body) => tools.getAttribute(body.instancePath, body.attributeName),
|
|
@@ -1629,11 +1629,14 @@ ${code}`
|
|
|
1629
1629
|
]
|
|
1630
1630
|
};
|
|
1631
1631
|
}
|
|
1632
|
-
async editScriptLines(instancePath, oldString, newString) {
|
|
1632
|
+
async editScriptLines(instancePath, oldString, newString, startLine) {
|
|
1633
1633
|
if (!instancePath || typeof oldString !== "string" || typeof newString !== "string") {
|
|
1634
1634
|
throw new Error("Instance path, old_string, and new_string are required for edit_script_lines");
|
|
1635
1635
|
}
|
|
1636
|
-
const
|
|
1636
|
+
const payload = { instancePath, old_string: oldString, new_string: newString };
|
|
1637
|
+
if (startLine !== void 0)
|
|
1638
|
+
payload.startLine = startLine;
|
|
1639
|
+
const response = await this.client.request("/api/edit-script-lines", payload);
|
|
1637
1640
|
return {
|
|
1638
1641
|
content: [
|
|
1639
1642
|
{
|
|
@@ -2496,17 +2499,28 @@ ${code}`
|
|
|
2496
2499
|
}]
|
|
2497
2500
|
};
|
|
2498
2501
|
}
|
|
2502
|
+
// Decal asset IDs are the wrapper asset; ImageLabel.Image needs the underlying image
|
|
2503
|
+
// content ID. The only reliable cross-auth way to resolve this is InsertService:LoadAsset
|
|
2504
|
+
// via the connected Studio plugin — the unauthenticated economy endpoint returns 401.
|
|
2499
2505
|
async resolveImageId(decalAssetId) {
|
|
2506
|
+
const code = `
|
|
2507
|
+
local InsertService = game:GetService("InsertService")
|
|
2508
|
+
local ok, result = pcall(function() return InsertService:LoadAsset(${decalAssetId}) end)
|
|
2509
|
+
if not ok then return nil end
|
|
2510
|
+
local decal = result:FindFirstChildWhichIsA("Decal", true)
|
|
2511
|
+
local id = decal and decal.Texture:match("(%d+)") or nil
|
|
2512
|
+
result:Destroy()
|
|
2513
|
+
return id
|
|
2514
|
+
`;
|
|
2500
2515
|
try {
|
|
2501
|
-
const
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
return String(details.TextureId);
|
|
2516
|
+
const response = await this.client.request("/api/execute-luau", { code }, "edit");
|
|
2517
|
+
const returnValue = response?.returnValue;
|
|
2518
|
+
if (returnValue !== void 0 && returnValue !== null && /^\d+$/.test(String(returnValue))) {
|
|
2519
|
+
return String(returnValue);
|
|
2520
|
+
}
|
|
2507
2521
|
} catch {
|
|
2508
2522
|
}
|
|
2509
|
-
return
|
|
2523
|
+
return null;
|
|
2510
2524
|
}
|
|
2511
2525
|
async uploadDecal(filePath, displayName, description, userId, groupId) {
|
|
2512
2526
|
if (!fs.existsSync(filePath)) {
|
|
@@ -2530,12 +2544,13 @@ ${code}`
|
|
|
2530
2544
|
creationContext: { creator }
|
|
2531
2545
|
}, fileContent, fileName);
|
|
2532
2546
|
const decalId = result.response?.assetId;
|
|
2533
|
-
const imageId = decalId ? await this.resolveImageId(decalId) :
|
|
2547
|
+
const imageId = decalId ? await this.resolveImageId(decalId) : null;
|
|
2534
2548
|
return {
|
|
2535
2549
|
content: [{
|
|
2536
2550
|
type: "text",
|
|
2537
2551
|
text: JSON.stringify({
|
|
2538
2552
|
...result,
|
|
2553
|
+
decalId: decalId ?? null,
|
|
2539
2554
|
imageId
|
|
2540
2555
|
})
|
|
2541
2556
|
}]
|
|
@@ -2552,7 +2567,7 @@ ${code}`
|
|
|
2552
2567
|
assetId: String(result.assetId),
|
|
2553
2568
|
displayName,
|
|
2554
2569
|
assetType: "Decal",
|
|
2555
|
-
|
|
2570
|
+
decalId: String(result.assetId),
|
|
2556
2571
|
imageId: String(result.backingAssetId)
|
|
2557
2572
|
}
|
|
2558
2573
|
})
|
|
@@ -2578,7 +2593,8 @@ ${code}`
|
|
|
2578
2593
|
assetId: String(result2.assetId),
|
|
2579
2594
|
displayName,
|
|
2580
2595
|
assetType,
|
|
2581
|
-
|
|
2596
|
+
decalId: String(result2.assetId),
|
|
2597
|
+
imageId: String(result2.backingAssetId)
|
|
2582
2598
|
}
|
|
2583
2599
|
})
|
|
2584
2600
|
}]
|
|
@@ -2605,6 +2621,20 @@ ${code}`
|
|
|
2605
2621
|
description: description || "",
|
|
2606
2622
|
creationContext: { creator }
|
|
2607
2623
|
}, fileContent, fileName);
|
|
2624
|
+
if (assetType === "Decal") {
|
|
2625
|
+
const decalId = result.response?.assetId;
|
|
2626
|
+
const imageId = decalId ? await this.resolveImageId(decalId) : null;
|
|
2627
|
+
return {
|
|
2628
|
+
content: [{
|
|
2629
|
+
type: "text",
|
|
2630
|
+
text: JSON.stringify({
|
|
2631
|
+
...result,
|
|
2632
|
+
decalId: decalId ?? null,
|
|
2633
|
+
imageId
|
|
2634
|
+
})
|
|
2635
|
+
}]
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2608
2638
|
return {
|
|
2609
2639
|
content: [{
|
|
2610
2640
|
type: "text",
|
|
@@ -3031,7 +3061,7 @@ var RobloxStudioMCPServer = class {
|
|
|
3031
3061
|
case "set_script_source":
|
|
3032
3062
|
return await this.tools.setScriptSource(args?.instancePath, args?.source);
|
|
3033
3063
|
case "edit_script_lines":
|
|
3034
|
-
return await this.tools.editScriptLines(args?.instancePath, args?.old_string, args?.new_string);
|
|
3064
|
+
return await this.tools.editScriptLines(args?.instancePath, args?.old_string, args?.new_string, args?.startLine);
|
|
3035
3065
|
case "insert_script_lines":
|
|
3036
3066
|
return await this.tools.insertScriptLines(args?.instancePath, args?.afterLine, args?.newContent);
|
|
3037
3067
|
case "delete_script_lines":
|
|
@@ -3784,7 +3814,7 @@ var TOOL_DEFINITIONS = [
|
|
|
3784
3814
|
{
|
|
3785
3815
|
name: "edit_script_lines",
|
|
3786
3816
|
category: "write",
|
|
3787
|
-
description: "Replace exact text in a script. old_string must match exactly once in the script (
|
|
3817
|
+
description: "Replace exact text in a script. Without startLine, old_string must match exactly once in the script. Pass startLine (1-indexed, from get_script_source) to anchor the edit to a specific line when old_string is ambiguous (e.g. repeated closing braces).",
|
|
3788
3818
|
inputSchema: {
|
|
3789
3819
|
type: "object",
|
|
3790
3820
|
properties: {
|
|
@@ -3794,11 +3824,15 @@ var TOOL_DEFINITIONS = [
|
|
|
3794
3824
|
},
|
|
3795
3825
|
old_string: {
|
|
3796
3826
|
type: "string",
|
|
3797
|
-
description: "Exact text to find and replace
|
|
3827
|
+
description: "Exact text to find and replace. Must be unique in the script unless startLine is provided."
|
|
3798
3828
|
},
|
|
3799
3829
|
new_string: {
|
|
3800
3830
|
type: "string",
|
|
3801
3831
|
description: "Replacement text"
|
|
3832
|
+
},
|
|
3833
|
+
startLine: {
|
|
3834
|
+
type: "number",
|
|
3835
|
+
description: "Optional 1-indexed line where old_string begins. When provided, skips uniqueness check and requires old_string to match starting at that exact line."
|
|
3802
3836
|
}
|
|
3803
3837
|
},
|
|
3804
3838
|
required: ["instancePath", "old_string", "new_string"]
|
|
@@ -4566,7 +4600,7 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4566
4600
|
{
|
|
4567
4601
|
name: "upload_decal",
|
|
4568
4602
|
category: "write",
|
|
4569
|
-
description: "Upload an image file as a Decal asset to Roblox. Supports ROBLOSECURITY cookie auth (recommended, simpler) or ROBLOX_OPEN_CLOUD_API_KEY (needs asset:write scope + creator ID). Cookie auth is used automatically when ROBLOSECURITY is set.",
|
|
4603
|
+
description: "Upload an image file as a Decal asset to Roblox. Supports ROBLOSECURITY cookie auth (recommended, simpler) or ROBLOX_OPEN_CLOUD_API_KEY (needs asset:write scope + creator ID). Cookie auth is used automatically when ROBLOSECURITY is set. Response includes both decalId (Decal wrapper asset ID) and imageId (underlying image content ID \u2014 use this for ImageLabel.Image). For Open Cloud uploads, imageId is resolved via the connected Studio plugin (InsertService:LoadAsset); if the plugin is not connected, imageId is null.",
|
|
4570
4604
|
inputSchema: {
|
|
4571
4605
|
type: "object",
|
|
4572
4606
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "robloxstudio-mcp",
|
|
3
|
-
"version": "2.7.0-next.
|
|
4
|
-
"description": "MCP Server for Roblox Studio Integration - Access Studio data, scripts, and objects through AI tools",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"robloxstudio-mcp": "dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"dist/**/*",
|
|
12
|
-
"studio-plugin/**/*"
|
|
13
|
-
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"build": "tsup",
|
|
16
|
-
"prepack": "node ../../scripts/prepack.mjs",
|
|
17
|
-
"postpack": "node ../../scripts/postpack.mjs"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"mcp",
|
|
21
|
-
"roblox",
|
|
22
|
-
"studio",
|
|
23
|
-
"ai",
|
|
24
|
-
"model-context-protocol",
|
|
25
|
-
"game-development"
|
|
26
|
-
],
|
|
27
|
-
"author": "",
|
|
28
|
-
"license": "MIT",
|
|
29
|
-
"repository": {
|
|
30
|
-
"type": "git",
|
|
31
|
-
"url": "https://github.com/boshyxd/robloxstudio-mcp.git"
|
|
32
|
-
},
|
|
33
|
-
"homepage": "https://github.com/boshyxd/robloxstudio-mcp#readme",
|
|
34
|
-
"bugs": {
|
|
35
|
-
"url": "https://github.com/boshyxd/robloxstudio-mcp/issues"
|
|
36
|
-
},
|
|
37
|
-
"dependencies": {
|
|
38
|
-
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
39
|
-
"cors": "^2.8.5",
|
|
40
|
-
"express": "^4.18.2",
|
|
41
|
-
"node-fetch": "^3.3.2",
|
|
42
|
-
"uuid": "^9.0.1",
|
|
43
|
-
"ws": "^8.14.2"
|
|
44
|
-
},
|
|
45
|
-
"devDependencies": {
|
|
46
|
-
"@robloxstudio-mcp/core": "*"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "robloxstudio-mcp",
|
|
3
|
+
"version": "2.7.0-next.6",
|
|
4
|
+
"description": "MCP Server for Roblox Studio Integration - Access Studio data, scripts, and objects through AI tools",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"robloxstudio-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**/*",
|
|
12
|
+
"studio-plugin/**/*"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsup",
|
|
16
|
+
"prepack": "node ../../scripts/prepack.mjs",
|
|
17
|
+
"postpack": "node ../../scripts/postpack.mjs"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"roblox",
|
|
22
|
+
"studio",
|
|
23
|
+
"ai",
|
|
24
|
+
"model-context-protocol",
|
|
25
|
+
"game-development"
|
|
26
|
+
],
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/boshyxd/robloxstudio-mcp.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/boshyxd/robloxstudio-mcp#readme",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/boshyxd/robloxstudio-mcp/issues"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
39
|
+
"cors": "^2.8.5",
|
|
40
|
+
"express": "^4.18.2",
|
|
41
|
+
"node-fetch": "^3.3.2",
|
|
42
|
+
"uuid": "^9.0.1",
|
|
43
|
+
"ws": "^8.14.2"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@robloxstudio-mcp/core": "*"
|
|
47
|
+
}
|
|
48
|
+
}
|