robloxstudio-mcp 2.6.0 → 2.7.0-next.0

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.
Files changed (2) hide show
  1. package/dist/index.js +117 -5
  2. package/package.json +48 -48
package/dist/index.js CHANGED
@@ -1046,14 +1046,29 @@ var OpenCloudClient = class {
1046
1046
  getMimeType(fileName) {
1047
1047
  const ext = fileName.split(".").pop()?.toLowerCase();
1048
1048
  const mimeTypes = {
1049
+ // Image (Decal)
1049
1050
  png: "image/png",
1050
1051
  jpg: "image/jpeg",
1051
1052
  jpeg: "image/jpeg",
1052
1053
  bmp: "image/bmp",
1053
- tga: "image/x-tga"
1054
+ tga: "image/tga",
1055
+ // Audio
1056
+ mp3: "audio/mpeg",
1057
+ ogg: "audio/ogg",
1058
+ wav: "audio/wav",
1059
+ flac: "audio/flac",
1060
+ // Model
1061
+ fbx: "model/fbx",
1062
+ gltf: "model/gltf+json",
1063
+ glb: "model/gltf-binary",
1064
+ rbxm: "model/x-rbxm",
1065
+ rbxmx: "model/x-rbxm",
1066
+ // Video
1067
+ mp4: "video/mp4",
1068
+ mov: "video/mov"
1054
1069
  };
1055
1070
  if (!ext || !mimeTypes[ext]) {
1056
- throw new Error(`Unsupported image format: ${fileName}. Supported: PNG, JPG, BMP, TGA`);
1071
+ throw new Error(`Unsupported file format: .${ext ?? "(none)"}. Supported: Image: png/jpg/bmp/tga, Audio: mp3/ogg/wav/flac, Model: fbx/gltf/glb/rbxm/rbxmx, Video: mp4/mov`);
1057
1072
  }
1058
1073
  return mimeTypes[ext];
1059
1074
  }
@@ -1148,12 +1163,18 @@ var RobloxCookieClient = class {
1148
1163
  return response;
1149
1164
  }
1150
1165
  async uploadDecal(fileContent, name, description) {
1166
+ return this.uploadAssetByTypeId(fileContent, name, description, 13, "Decal");
1167
+ }
1168
+ async uploadAudio(fileContent, name, description) {
1169
+ return this.uploadAssetByTypeId(fileContent, name, description, 3, "Audio");
1170
+ }
1171
+ async uploadAssetByTypeId(fileContent, name, description, assetTypeId, assetLabel) {
1151
1172
  if (!this.cookie) {
1152
1173
  throw new Error("ROBLOSECURITY cookie is not set.");
1153
1174
  }
1154
1175
  const encodedName = encodeURIComponent(name);
1155
1176
  const encodedDesc = encodeURIComponent(description);
1156
- const url = `https://data.roblox.com/data/upload/json?assetTypeId=13&name=${encodedName}&description=${encodedDesc}`;
1177
+ const url = `https://data.roblox.com/data/upload/json?assetTypeId=${assetTypeId}&name=${encodedName}&description=${encodedDesc}`;
1157
1178
  const response = await this.fetchWithCsrf(url, {
1158
1179
  method: "POST",
1159
1180
  headers: {
@@ -1165,11 +1186,11 @@ var RobloxCookieClient = class {
1165
1186
  });
1166
1187
  if (!response.ok) {
1167
1188
  const body = await response.text();
1168
- throw new Error(`Decal upload failed (${response.status}): ${body}`);
1189
+ throw new Error(`${assetLabel} upload failed (${response.status}): ${body}`);
1169
1190
  }
1170
1191
  const result = await response.json();
1171
1192
  if (!result.Success || !result.AssetId) {
1172
- throw new Error(`Decal upload failed: ${result.Message || "Unknown error"}`);
1193
+ throw new Error(`${assetLabel} upload failed: ${result.Message || "Unknown error"}`);
1173
1194
  }
1174
1195
  return {
1175
1196
  assetId: result.AssetId,
@@ -2531,6 +2552,59 @@ ${code}`
2531
2552
  }]
2532
2553
  };
2533
2554
  }
2555
+ async uploadAsset(filePath, assetType, displayName, description, userId, groupId) {
2556
+ if (!fs.existsSync(filePath)) {
2557
+ throw new Error(`File not found: ${filePath}`);
2558
+ }
2559
+ const fileContent = fs.readFileSync(filePath);
2560
+ const fileName = path.basename(filePath);
2561
+ const cookieSupported = assetType === "Decal" || assetType === "Audio";
2562
+ if (cookieSupported && this.cookieClient.hasCookie()) {
2563
+ const upload = assetType === "Decal" ? this.cookieClient.uploadDecal(fileContent, displayName, description || "") : this.cookieClient.uploadAudio(fileContent, displayName, description || "");
2564
+ const result2 = await upload;
2565
+ return {
2566
+ content: [{
2567
+ type: "text",
2568
+ text: JSON.stringify({
2569
+ done: true,
2570
+ response: {
2571
+ assetId: String(result2.assetId),
2572
+ displayName,
2573
+ assetType,
2574
+ backingAssetId: String(result2.backingAssetId)
2575
+ }
2576
+ })
2577
+ }]
2578
+ };
2579
+ }
2580
+ if (!this.openCloudClient.hasApiKey()) {
2581
+ const cookieHint = cookieSupported ? " Alternatively, set ROBLOSECURITY to use cookie auth." : "";
2582
+ throw new Error(`No auth configured for ${assetType} upload. Set ROBLOX_OPEN_CLOUD_API_KEY (needs asset:write scope).${cookieHint}`);
2583
+ }
2584
+ const resolvedGroupId = groupId || process.env.ROBLOX_CREATOR_GROUP_ID;
2585
+ const resolvedUserId = userId || process.env.ROBLOX_CREATOR_USER_ID;
2586
+ if (!resolvedUserId && !resolvedGroupId) {
2587
+ throw new Error("Creator identity required for Open Cloud upload. Set ROBLOX_CREATOR_USER_ID or ROBLOX_CREATOR_GROUP_ID, or pass userId/groupId as parameters.");
2588
+ }
2589
+ const creator = {};
2590
+ if (resolvedGroupId) {
2591
+ creator.groupId = resolvedGroupId;
2592
+ } else {
2593
+ creator.userId = resolvedUserId;
2594
+ }
2595
+ const result = await this.openCloudClient.createAsset({
2596
+ assetType,
2597
+ displayName,
2598
+ description: description || "",
2599
+ creationContext: { creator }
2600
+ }, fileContent, fileName);
2601
+ return {
2602
+ content: [{
2603
+ type: "text",
2604
+ text: JSON.stringify(result)
2605
+ }]
2606
+ };
2607
+ }
2534
2608
  async simulateMouseInput(action, x, y, button, scrollDirection, target) {
2535
2609
  if (!action) {
2536
2610
  throw new Error("action is required for simulate_mouse_input");
@@ -3015,6 +3089,8 @@ var RobloxStudioMCPServer = class {
3015
3089
  return await this.tools.previewAsset(args?.assetId, args?.includeProperties, args?.maxDepth);
3016
3090
  case "upload_decal":
3017
3091
  return await this.tools.uploadDecal(args?.filePath, args?.displayName, args?.description, args?.userId, args?.groupId);
3092
+ case "upload_asset":
3093
+ return await this.tools.uploadAsset(args?.filePath, args?.assetType, args?.displayName, args?.description, args?.userId, args?.groupId);
3018
3094
  case "clone_object":
3019
3095
  return await this.tools.cloneObject(args?.instancePath, args?.targetParentPath);
3020
3096
  case "move_object":
@@ -4511,6 +4587,42 @@ part(0,2,0,2,1,1,"b")`,
4511
4587
  required: ["filePath", "displayName"]
4512
4588
  }
4513
4589
  },
4590
+ {
4591
+ name: "upload_asset",
4592
+ category: "write",
4593
+ description: "Upload any supported asset type to Roblox: Audio (mp3/ogg/wav/flac), Decal (png/jpg/bmp/tga), Model (fbx/gltf/glb/rbxm/rbxmx), Animation (rbxm/rbxmx), or Video (mp4/mov). For Audio and Decal, supports ROBLOSECURITY cookie auth (recommended) or ROBLOX_OPEN_CLOUD_API_KEY. Other types require Open Cloud API key. Audio: max 7 min, 100 uploads/month (ID-verified). Video: max 5 min, requires 13+ ID-verified.",
4594
+ inputSchema: {
4595
+ type: "object",
4596
+ properties: {
4597
+ filePath: {
4598
+ type: "string",
4599
+ description: "Absolute path to the file on disk"
4600
+ },
4601
+ assetType: {
4602
+ type: "string",
4603
+ enum: ["Audio", "Decal", "Model", "Animation", "Video"],
4604
+ description: "Type of asset to upload. Must match the file format."
4605
+ },
4606
+ displayName: {
4607
+ type: "string",
4608
+ description: "Display name for the asset (max 50 characters)"
4609
+ },
4610
+ description: {
4611
+ type: "string",
4612
+ description: "Description for the asset (default: empty string)"
4613
+ },
4614
+ userId: {
4615
+ type: "string",
4616
+ description: "Roblox user ID for the asset creator. Overrides ROBLOX_CREATOR_USER_ID env var."
4617
+ },
4618
+ groupId: {
4619
+ type: "string",
4620
+ description: "Roblox group ID for the asset creator. Overrides ROBLOX_CREATOR_GROUP_ID env var. Takes precedence over userId if both provided."
4621
+ }
4622
+ },
4623
+ required: ["filePath", "assetType", "displayName"]
4624
+ }
4625
+ },
4514
4626
  {
4515
4627
  name: "capture_screenshot",
4516
4628
  category: "read",
package/package.json CHANGED
@@ -1,48 +1,48 @@
1
- {
2
- "name": "robloxstudio-mcp",
3
- "version": "2.6.0",
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.0",
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
+ }