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 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 response = await this.client.request("/api/edit-script-lines", { instancePath, old_string: oldString, new_string: newString });
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 resp = await fetch(`https://economy.roblox.com/v2/assets/${decalAssetId}/details`);
2502
- if (!resp.ok)
2503
- return decalAssetId;
2504
- const details = await resp.json();
2505
- if (details.TextureId)
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 decalAssetId;
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) : void 0;
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
- backingAssetId: String(result.backingAssetId),
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
- backingAssetId: String(result2.backingAssetId)
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 (whitespace-sensitive). Use get_script_source first to see current content.",
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 (must be unique in the script)"
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",
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
+ }