@chrrxs/robloxstudio-mcp 2.23.1 → 3.0.1

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 (50) hide show
  1. package/dist/index.js +1688 -4547
  2. package/package.json +7 -5
  3. package/studio-plugin/MCPPlugin.rbxmx +489 -1965
  4. package/studio-plugin/.Carbon.rbxm.lock +0 -0
  5. package/studio-plugin/Carbon.rbxm +0 -0
  6. package/studio-plugin/INSTALLATION.md +0 -170
  7. package/studio-plugin/MCPInspectorPlugin.rbxmx +0 -171527
  8. package/studio-plugin/default.project.json +0 -19
  9. package/studio-plugin/dev.project.json +0 -23
  10. package/studio-plugin/include/LibMP.lua +0 -156378
  11. package/studio-plugin/inspector-icon.png +0 -0
  12. package/studio-plugin/package-lock.json +0 -706
  13. package/studio-plugin/package.json +0 -19
  14. package/studio-plugin/plugin.json +0 -10
  15. package/studio-plugin/src/modules/AssetSanitizationPolicy.ts +0 -127
  16. package/studio-plugin/src/modules/ClientBroker.ts +0 -450
  17. package/studio-plugin/src/modules/Communication.ts +0 -632
  18. package/studio-plugin/src/modules/EvalBridges.ts +0 -255
  19. package/studio-plugin/src/modules/HttpDiagnostics.ts +0 -50
  20. package/studio-plugin/src/modules/LuauExec.ts +0 -403
  21. package/studio-plugin/src/modules/Recording.ts +0 -28
  22. package/studio-plugin/src/modules/RenderMonitor.ts +0 -60
  23. package/studio-plugin/src/modules/RuntimeLogBuffer.ts +0 -210
  24. package/studio-plugin/src/modules/ServerUrlSettings.ts +0 -117
  25. package/studio-plugin/src/modules/State.ts +0 -39
  26. package/studio-plugin/src/modules/StopPlayMonitor.ts +0 -267
  27. package/studio-plugin/src/modules/UI.ts +0 -597
  28. package/studio-plugin/src/modules/Utils.ts +0 -469
  29. package/studio-plugin/src/modules/handlers/AssetHandlers.ts +0 -391
  30. package/studio-plugin/src/modules/handlers/BreakpointHandlers.ts +0 -460
  31. package/studio-plugin/src/modules/handlers/BuildHandlers.ts +0 -481
  32. package/studio-plugin/src/modules/handlers/CaptureHandlers.ts +0 -170
  33. package/studio-plugin/src/modules/handlers/EvalRuntimeHandlers.ts +0 -149
  34. package/studio-plugin/src/modules/handlers/GenerateModelHandlers.ts +0 -168
  35. package/studio-plugin/src/modules/handlers/InputHandlers.ts +0 -163
  36. package/studio-plugin/src/modules/handlers/InstanceHandlers.ts +0 -380
  37. package/studio-plugin/src/modules/handlers/LogHandlers.ts +0 -14
  38. package/studio-plugin/src/modules/handlers/MemoryHandlers.ts +0 -44
  39. package/studio-plugin/src/modules/handlers/MetadataHandlers.ts +0 -354
  40. package/studio-plugin/src/modules/handlers/MicroProfilerHandlers.ts +0 -1263
  41. package/studio-plugin/src/modules/handlers/PropertyHandlers.ts +0 -191
  42. package/studio-plugin/src/modules/handlers/QueryHandlers.ts +0 -874
  43. package/studio-plugin/src/modules/handlers/SceneAnalysisHandlers.ts +0 -216
  44. package/studio-plugin/src/modules/handlers/ScriptHandlers.ts +0 -530
  45. package/studio-plugin/src/modules/handlers/ScriptProfilerHandlers.ts +0 -386
  46. package/studio-plugin/src/modules/handlers/SerializationHandlers.ts +0 -172
  47. package/studio-plugin/src/modules/handlers/TestHandlers.ts +0 -350
  48. package/studio-plugin/src/server/index.server.ts +0 -135
  49. package/studio-plugin/src/types/index.d.ts +0 -57
  50. package/studio-plugin/tsconfig.json +0 -20
@@ -1,380 +0,0 @@
1
- import Utils from "../Utils";
2
- import Recording from "../Recording";
3
-
4
- const { getInstancePath, getInstanceByPath, convertPropertyValue } = Utils;
5
- const { beginRecording, finishRecording } = Recording;
6
-
7
- type ProcessedCreateResult =
8
- | {
9
- instance: Instance;
10
- className: string;
11
- parentPath: string;
12
- }
13
- | {
14
- error: string;
15
- className?: string;
16
- parentPath?: string;
17
- };
18
-
19
- type ProcessedBatchResult = {
20
- results: Record<string, unknown>[];
21
- successCount: number;
22
- failureCount: number;
23
- };
24
-
25
- function processObjectEntries(
26
- objects: Record<string, unknown>[],
27
- createFn: (objData: Record<string, unknown>) => ProcessedCreateResult,
28
- ): ProcessedBatchResult {
29
- const results: Record<string, unknown>[] = [];
30
- let successCount = 0;
31
- let failureCount = 0;
32
-
33
- const [loopSuccess, loopError] = pcall(() => {
34
- for (const entry of objects) {
35
- if (!typeIs(entry, "table")) {
36
- failureCount++;
37
- results.push({ success: false, error: "Each object entry must be a table" });
38
- continue;
39
- }
40
-
41
- const objData = entry as Record<string, unknown>;
42
- const className = objData.className as string;
43
- const parentPath = objData.parent as string;
44
-
45
- if (!className || !parentPath) {
46
- failureCount++;
47
- results.push({ success: false, error: "Class name and parent are required" });
48
- continue;
49
- }
50
-
51
- const [entrySuccess, entryResult] = pcall(() => createFn(objData));
52
- if (!entrySuccess) {
53
- failureCount++;
54
- results.push({ success: false, className, parent: parentPath, error: tostring(entryResult) });
55
- continue;
56
- }
57
-
58
- if ("instance" in entryResult) {
59
- successCount++;
60
- results.push({
61
- success: true,
62
- className: entryResult.className,
63
- parent: entryResult.parentPath,
64
- instancePath: getInstancePath(entryResult.instance),
65
- name: entryResult.instance.Name,
66
- });
67
- } else {
68
- failureCount++;
69
- results.push({
70
- success: false,
71
- className: entryResult.className ?? className,
72
- parent: entryResult.parentPath ?? parentPath,
73
- error: entryResult.error,
74
- });
75
- }
76
- }
77
- });
78
-
79
- if (!loopSuccess) {
80
- failureCount++;
81
- results.push({ success: false, error: `Unexpected mass create failure: ${tostring(loopError)}` });
82
- }
83
-
84
- return { results, successCount, failureCount };
85
- }
86
-
87
- function createObject(requestData: Record<string, unknown>) {
88
- const className = requestData.className as string;
89
- const parentPath = requestData.parent as string;
90
- const name = requestData.name as string | undefined;
91
- const properties = (requestData.properties as Record<string, unknown>) ?? {};
92
-
93
- if (!className || !parentPath) {
94
- return { error: "Class name and parent are required" };
95
- }
96
-
97
- const parentInstance = getInstanceByPath(parentPath);
98
- if (!parentInstance) return { error: `Parent instance not found: ${parentPath}` };
99
- const recordingId = beginRecording(`Create ${className}`);
100
-
101
- const [success, newInstance] = pcall(() => {
102
- const instance = new Instance(className as keyof CreatableInstances);
103
- if (name) instance.Name = name;
104
-
105
- for (const [propertyName, propertyValue] of pairs(properties)) {
106
- pcall(() => {
107
- const converted = convertPropertyValue(instance, propertyName as string, propertyValue);
108
- (instance as unknown as { [key: string]: unknown })[propertyName as string] =
109
- converted !== undefined ? converted : propertyValue;
110
- });
111
- }
112
-
113
- instance.Parent = parentInstance;
114
- return instance;
115
- });
116
-
117
- if (success && newInstance) {
118
- finishRecording(recordingId, true);
119
- return {
120
- success: true,
121
- className,
122
- parent: parentPath,
123
- instancePath: getInstancePath(newInstance as Instance),
124
- name: (newInstance as Instance).Name,
125
- message: "Object created successfully",
126
- };
127
- } else {
128
- finishRecording(recordingId, false);
129
- return { error: `Failed to create object: ${newInstance}`, className, parent: parentPath };
130
- }
131
- }
132
-
133
- function deleteObject(requestData: Record<string, unknown>) {
134
- const instancePath = requestData.instancePath as string;
135
- if (!instancePath) return { error: "Instance path is required" };
136
-
137
- const instance = getInstanceByPath(instancePath);
138
- if (!instance) return { error: `Instance not found: ${instancePath}` };
139
- if (instance === game) return { error: "Cannot delete the game instance" };
140
- const recordingId = beginRecording(`Delete ${instance.ClassName} (${instance.Name})`);
141
-
142
- const [success, result] = pcall(() => {
143
- instance.Destroy();
144
- return true;
145
- });
146
-
147
- if (success) {
148
- finishRecording(recordingId, true);
149
- return { success: true, instancePath, message: "Object deleted successfully" };
150
- } else {
151
- finishRecording(recordingId, false);
152
- return { error: `Failed to delete object: ${result}`, instancePath };
153
- }
154
- }
155
-
156
- function massCreateObjects(requestData: Record<string, unknown>) {
157
- const objects = requestData.objects as Record<string, unknown>[];
158
- if (!objects || !typeIs(objects, "table") || (objects as defined[]).size() === 0) {
159
- return { error: "Objects array is required" };
160
- }
161
-
162
- const recordingId = beginRecording("Mass create objects");
163
-
164
- const { results, successCount, failureCount } = processObjectEntries(objects, (objData) => {
165
- const className = objData.className as string;
166
- const parentPath = objData.parent as string;
167
- const name = objData.name as string | undefined;
168
- const properties = (objData.properties as Record<string, unknown>) ?? {};
169
- const parentInstance = getInstanceByPath(parentPath);
170
- if (!parentInstance) {
171
- return { error: "Parent instance not found", className, parentPath };
172
- }
173
-
174
- const [success, newInstance] = pcall(() => {
175
- const instance = new Instance(className as keyof CreatableInstances);
176
- if (name) instance.Name = name;
177
-
178
- for (const [propertyName, propertyValue] of pairs(properties)) {
179
- pcall(() => {
180
- const converted = convertPropertyValue(instance, propertyName as string, propertyValue);
181
- (instance as unknown as { [key: string]: unknown })[propertyName as string] =
182
- converted !== undefined ? converted : propertyValue;
183
- });
184
- }
185
-
186
- instance.Parent = parentInstance;
187
- return instance;
188
- });
189
-
190
- if (!success || !newInstance) {
191
- return { error: tostring(newInstance), className, parentPath };
192
- }
193
-
194
- return { instance: newInstance as Instance, className, parentPath };
195
- });
196
-
197
- finishRecording(recordingId, successCount > 0);
198
- return { results, summary: { total: (objects as defined[]).size(), succeeded: successCount, failed: failureCount } };
199
- }
200
-
201
-
202
-
203
- function performSmartDuplicate(requestData: Record<string, unknown>, useRecording = true) {
204
- const instancePath = requestData.instancePath as string;
205
- const count = requestData.count as number;
206
- const options = (requestData.options as Record<string, unknown>) ?? {};
207
-
208
- if (!instancePath || !count || count < 1) {
209
- return { error: "Instance path and count > 0 are required" };
210
- }
211
-
212
- const instance = getInstanceByPath(instancePath);
213
- if (!instance) return { error: `Instance not found: ${instancePath}` };
214
- const recordingId = useRecording ? beginRecording(`Smart duplicate ${instance.Name}`) : undefined;
215
-
216
- const results: Record<string, unknown>[] = [];
217
- let successCount = 0;
218
- let failureCount = 0;
219
-
220
- for (let i = 1; i <= count; i++) {
221
- const [success, newInstance] = pcall(() => {
222
- const clone = instance.Clone();
223
-
224
- if (options.namePattern) {
225
- clone.Name = (options.namePattern as string).gsub("{n}", tostring(i))[0];
226
- } else {
227
- clone.Name = instance.Name + i;
228
- }
229
-
230
- if (options.positionOffset && clone.IsA("BasePart")) {
231
- const offset = options.positionOffset as number[];
232
- const currentPos = clone.Position;
233
- clone.Position = new Vector3(
234
- currentPos.X + ((offset[0] ?? 0) as number) * i,
235
- currentPos.Y + ((offset[1] ?? 0) as number) * i,
236
- currentPos.Z + ((offset[2] ?? 0) as number) * i,
237
- );
238
- }
239
-
240
- if (options.rotationOffset && clone.IsA("BasePart")) {
241
- const offset = options.rotationOffset as number[];
242
- clone.CFrame = clone.CFrame.mul(CFrame.Angles(
243
- math.rad(((offset[0] ?? 0) as number) * i),
244
- math.rad(((offset[1] ?? 0) as number) * i),
245
- math.rad(((offset[2] ?? 0) as number) * i),
246
- ));
247
- }
248
-
249
- if (options.scaleOffset && clone.IsA("BasePart")) {
250
- const offset = options.scaleOffset as number[];
251
- const currentSize = clone.Size;
252
- clone.Size = new Vector3(
253
- currentSize.X * (((offset[0] ?? 1) as number) ** i),
254
- currentSize.Y * (((offset[1] ?? 1) as number) ** i),
255
- currentSize.Z * (((offset[2] ?? 1) as number) ** i),
256
- );
257
- }
258
-
259
- if (options.propertyVariations) {
260
- for (const [propName, values] of pairs(options.propertyVariations as Record<string, unknown[]>)) {
261
- if (values && (values as defined[]).size() > 0) {
262
- const valueIndex = ((i - 1) % (values as defined[]).size());
263
- pcall(() => {
264
- (clone as unknown as { [key: string]: unknown })[propName as string] = (values as unknown[])[valueIndex];
265
- });
266
- }
267
- }
268
- }
269
-
270
- const targetParents = options.targetParents as string[] | undefined;
271
- if (targetParents && targetParents[i - 1]) {
272
- const targetParent = getInstanceByPath(targetParents[i - 1]);
273
- clone.Parent = targetParent ?? instance.Parent;
274
- } else {
275
- clone.Parent = instance.Parent;
276
- }
277
-
278
- return clone;
279
- });
280
-
281
- if (success && newInstance) {
282
- successCount++;
283
- results.push({
284
- success: true,
285
- instancePath: getInstancePath(newInstance as Instance),
286
- name: (newInstance as Instance).Name,
287
- index: i,
288
- });
289
- } else {
290
- failureCount++;
291
- results.push({ success: false, index: i, error: tostring(newInstance) });
292
- }
293
- }
294
-
295
- finishRecording(recordingId, successCount > 0);
296
-
297
- return {
298
- results,
299
- summary: { total: count, succeeded: successCount, failed: failureCount },
300
- sourceInstance: instancePath,
301
- };
302
- }
303
-
304
- function smartDuplicate(requestData: Record<string, unknown>) {
305
- return performSmartDuplicate(requestData, true);
306
- }
307
-
308
- function massDuplicate(requestData: Record<string, unknown>) {
309
- const duplications = requestData.duplications as Record<string, unknown>[];
310
- if (!duplications || !typeIs(duplications, "table") || (duplications as defined[]).size() === 0) {
311
- return { error: "Duplications array is required" };
312
- }
313
-
314
- const allResults: Record<string, unknown>[] = [];
315
- let totalSuccess = 0;
316
- let totalFailures = 0;
317
- const recordingId = beginRecording("Mass duplicate operations");
318
-
319
- for (const duplication of duplications) {
320
- const result = performSmartDuplicate(duplication, false) as { summary?: { succeeded: number; failed: number } };
321
- allResults.push(result as unknown as Record<string, unknown>);
322
- if (result.summary) {
323
- totalSuccess += result.summary.succeeded;
324
- totalFailures += result.summary.failed;
325
- }
326
- }
327
-
328
- finishRecording(recordingId, totalSuccess > 0);
329
-
330
- return {
331
- results: allResults,
332
- summary: { total: totalSuccess + totalFailures, succeeded: totalSuccess, failed: totalFailures },
333
- };
334
- }
335
-
336
- function cloneObject(requestData: Record<string, unknown>) {
337
- const instancePath = requestData.instancePath as string;
338
- const targetParentPath = requestData.targetParentPath as string;
339
-
340
- if (!instancePath || !targetParentPath) {
341
- return { error: "Instance path and target parent path are required" };
342
- }
343
-
344
- const instance = getInstanceByPath(instancePath);
345
- if (!instance) return { error: `Instance not found: ${instancePath}` };
346
-
347
- const targetParent = getInstanceByPath(targetParentPath);
348
- if (!targetParent) return { error: `Target parent not found: ${targetParentPath}` };
349
-
350
- const recordingId = beginRecording(`Clone ${instance.Name}`);
351
-
352
- const [success, clone] = pcall(() => {
353
- const cloned = instance.Clone();
354
- cloned.Parent = targetParent;
355
- return cloned;
356
- });
357
-
358
- if (success && clone) {
359
- finishRecording(recordingId, true);
360
- return {
361
- success: true,
362
- instancePath: getInstancePath(clone as Instance),
363
- name: (clone as Instance).Name,
364
- className: (clone as Instance).ClassName,
365
- parent: targetParentPath,
366
- message: "Object cloned successfully",
367
- };
368
- }
369
- finishRecording(recordingId, false);
370
- return { error: `Failed to clone object: ${clone}` };
371
- }
372
-
373
- export = {
374
- createObject,
375
- deleteObject,
376
- massCreateObjects,
377
- smartDuplicate,
378
- massDuplicate,
379
- cloneObject,
380
- };
@@ -1,14 +0,0 @@
1
- import RuntimeLogBuffer from "../RuntimeLogBuffer";
2
-
3
- function getRuntimeLogs(requestData: Record<string, unknown>): unknown {
4
- const since = requestData.since as number | undefined;
5
- const tail = requestData.tail as number | undefined;
6
- const filter = requestData.filter as string | undefined;
7
- // This is the buffer that captured the LogService event, not necessarily
8
- // the script-origin peer. Ordinary playtests share/reflect logs across
9
- // edit/server/client LogService buffers.
10
- const capturedBy = RuntimeLogBuffer.detectPeer();
11
- return RuntimeLogBuffer.query({ since, tail, filter }, capturedBy);
12
- }
13
-
14
- export = { getRuntimeLogs };
@@ -1,44 +0,0 @@
1
- const Stats = game.GetService("Stats");
2
-
3
- // GetMemoryUsageMbAllCategories is gated by capability "InternalTest" and not
4
- // callable from plugin context. GetMemoryUsageMbForTag is not - so we iterate
5
- // Enum.DeveloperMemoryTag and ask per-tag.
6
- function getMemoryBreakdown(requestData: Record<string, unknown>): unknown {
7
- if (!Stats.MemoryTrackingEnabled) {
8
- return { error: "MemoryTrackingEnabled is false on this peer" };
9
- }
10
-
11
- const requested = requestData.tags as string[] | undefined;
12
- const requestedSet = requested && requested.size() > 0 ? new Set(requested) : undefined;
13
-
14
- const categories: Record<string, number> = {};
15
- for (const item of Enum.DeveloperMemoryTag.GetEnumItems()) {
16
- const name = item.Name;
17
- if (requestedSet && !requestedSet.has(name)) continue;
18
- const [ok, mb] = pcall(() => Stats.GetMemoryUsageMbForTag(item));
19
- categories[name] = ok ? (mb as number) : 0;
20
- }
21
-
22
- const unknownTags: string[] = [];
23
- if (requestedSet) {
24
- const known = new Set<string>();
25
- for (const i of Enum.DeveloperMemoryTag.GetEnumItems()) known.add(i.Name);
26
- for (const t of requestedSet) {
27
- if (!known.has(t)) {
28
- unknownTags.push(t);
29
- categories[t] = 0;
30
- }
31
- }
32
- }
33
-
34
- const result: Record<string, unknown> = {
35
- total_mb: Stats.GetTotalMemoryUsageMb(),
36
- categories,
37
- memory_tracking_enabled: true,
38
- timestamp: DateTime.now().UnixTimestampMillis,
39
- };
40
- if (unknownTags.size() > 0) result.unknown_tags = unknownTags;
41
- return result;
42
- }
43
-
44
- export = { getMemoryBreakdown };