@chrrxs/robloxstudio-mcp 3.0.0 → 3.0.2

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 (48) hide show
  1. package/dist/index.js +9401 -9047
  2. package/package.json +2 -2
  3. package/studio-plugin/MCPPlugin.rbxmx +400 -97
  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 -169759
  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 -601
  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 -527
  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/CaptureHandlers.ts +0 -170
  32. package/studio-plugin/src/modules/handlers/EvalRuntimeHandlers.ts +0 -149
  33. package/studio-plugin/src/modules/handlers/GenerateModelHandlers.ts +0 -168
  34. package/studio-plugin/src/modules/handlers/InputHandlers.ts +0 -163
  35. package/studio-plugin/src/modules/handlers/LogHandlers.ts +0 -14
  36. package/studio-plugin/src/modules/handlers/MemoryHandlers.ts +0 -44
  37. package/studio-plugin/src/modules/handlers/MetadataHandlers.ts +0 -96
  38. package/studio-plugin/src/modules/handlers/MicroProfilerHandlers.ts +0 -1263
  39. package/studio-plugin/src/modules/handlers/PropertyHandlers.ts +0 -62
  40. package/studio-plugin/src/modules/handlers/QueryHandlers.ts +0 -716
  41. package/studio-plugin/src/modules/handlers/SceneAnalysisHandlers.ts +0 -216
  42. package/studio-plugin/src/modules/handlers/ScriptHandlers.ts +0 -531
  43. package/studio-plugin/src/modules/handlers/ScriptProfilerHandlers.ts +0 -386
  44. package/studio-plugin/src/modules/handlers/SerializationHandlers.ts +0 -172
  45. package/studio-plugin/src/modules/handlers/TestHandlers.ts +0 -350
  46. package/studio-plugin/src/server/index.server.ts +0 -135
  47. package/studio-plugin/src/types/index.d.ts +0 -57
  48. package/studio-plugin/tsconfig.json +0 -20
@@ -1,255 +0,0 @@
1
- // Game-VM eval bridges, ported from chrrxs/roblox-mcp-primitives.
2
- //
3
- // Our standard `execute_luau target=server/client-N` runs in the plugin VM
4
- // with a fresh ModuleScript per call. That gives a clean sandbox but means
5
- // `require(SomeModule)` returns a fresh copy, not the one the running game
6
- // scripts hold. So runtime-mutated module state is invisible to probes.
7
- //
8
- // These bridges fix that by living inside the user's game scripts. Both
9
- // peers use the same symmetric shape:
10
- // - Server: a Script in ServerScriptService that creates a BindableFunction.
11
- // Plugin (server peer) invokes it with a fresh ModuleScript payload;
12
- // require() runs inside the Script VM so it shares the running server's
13
- // require cache.
14
- // - Client: a LocalScript in StarterPlayer.StarterPlayerScripts that
15
- // creates a BindableFunction. Plugin invokes it with a fresh ModuleScript
16
- // payload; require() runs inside the LocalScript VM so it shares the
17
- // game's require cache.
18
- //
19
- // Why ModuleScript+require on both sides (no loadstring): require'd modules
20
- // run with the security level they were created at and don't need
21
- // ServerScriptService.LoadStringEnabled, so eval_server_runtime works even
22
- // when LoadStringEnabled=false (the default in fresh places).
23
- //
24
- // Lifecycle: bridge scripts are created only in running play DataModels.
25
- // The server plugin peer creates the Script in runtime ServerScriptService;
26
- // each client plugin peer creates its LocalScript in that client's
27
- // PlayerScripts. Nothing is installed into the edit DataModel anymore.
28
- // Runtime-created scripts disappear naturally when the playtest stops.
29
-
30
- import { Players, ReplicatedStorage, RunService, ServerScriptService, StarterPlayer } from "@rbxts/services";
31
-
32
- const ScriptEditorService = game.GetService("ScriptEditorService");
33
-
34
- function getStarterPlayerScripts(): Instance | undefined {
35
- return StarterPlayer.FindFirstChild("StarterPlayerScripts");
36
- }
37
-
38
- const SERVER_SCRIPT_NAME = "__MCP_ServerEvalBridge";
39
- const CLIENT_SCRIPT_NAME = "__MCP_ClientEvalBridge";
40
-
41
- // Public so the eval_*_runtime tool wrappers can reference the same names.
42
- export const BRIDGE_NAMES = {
43
- serverScript: SERVER_SCRIPT_NAME,
44
- clientScript: CLIENT_SCRIPT_NAME,
45
- serverLocal: "__MCP_ServerEvalLocal",
46
- clientLocal: "__MCP_ClientEvalBridge",
47
- } as const;
48
-
49
- // Embedded Luau. The double `${...}` references our exported names so a
50
- // rename here propagates to both the script source and the tool wrappers.
51
- const SERVER_BRIDGE_SOURCE = `
52
- -- Installed by @chrrxs/robloxstudio-mcp to power the eval_server_runtime MCP
53
- -- tool (shared-require-cache eval on the server during playtests). Inert
54
- -- outside Studio (no-ops in live games); safe to leave in place.
55
-
56
- local ServerScriptService = game:GetService("ServerScriptService")
57
- local RunService = game:GetService("RunService")
58
-
59
- if not RunService:IsStudio() then
60
- return
61
- end
62
-
63
- local prevBf = ServerScriptService:FindFirstChild("${BRIDGE_NAMES.serverLocal}")
64
- if prevBf then prevBf:Destroy() end
65
-
66
- local bf = Instance.new("BindableFunction")
67
- bf.Name = "${BRIDGE_NAMES.serverLocal}"
68
- bf.Archivable = false
69
- bf.Parent = ServerScriptService
70
- bf.OnInvoke = function(payload)
71
- if typeof(payload) ~= "Instance" or not payload:IsA("ModuleScript") then
72
- return { ok = false, value = "payload must be a ModuleScript instance" }
73
- end
74
- local ok, value = pcall(require, payload)
75
- return { ok = ok, value = value }
76
- end
77
- `;
78
-
79
- const CLIENT_BRIDGE_SOURCE = `
80
- -- Installed by @chrrxs/robloxstudio-mcp to power the eval_client_runtime MCP
81
- -- tool (shared-require-cache eval on the client during playtests). Inert
82
- -- outside Studio (no-ops in live games); safe to leave in place.
83
-
84
- local ReplicatedStorage = game:GetService("ReplicatedStorage")
85
- local RunService = game:GetService("RunService")
86
-
87
- if not RunService:IsStudio() then
88
- return
89
- end
90
-
91
- local prevBf = ReplicatedStorage:FindFirstChild("${BRIDGE_NAMES.clientLocal}")
92
- if prevBf then prevBf:Destroy() end
93
-
94
- local bf = Instance.new("BindableFunction")
95
- bf.Name = "${BRIDGE_NAMES.clientLocal}"
96
- bf.Archivable = false
97
- bf.Parent = ReplicatedStorage
98
- bf.OnInvoke = function(payload)
99
- if typeof(payload) ~= "Instance" or not payload:IsA("ModuleScript") then
100
- return { ok = false, value = "payload must be a ModuleScript instance" }
101
- end
102
- local ok, value = pcall(require, payload)
103
- return { ok = ok, value = value }
104
- end
105
- `;
106
-
107
- // Stamp written onto each installed bridge Script so we can tell whether the
108
- // runtime bridge currently in the play DM was produced by THIS plugin build.
109
- // It's a djb2 hash of the actual bridge source plus the plugin version, so ANY
110
- // change to the source (or a version bump) yields a new stamp and triggers a
111
- // runtime refresh instead of keeping a stale bridge.
112
- const STAMP_ATTR = "__MCPBridgeStamp";
113
-
114
- function computeBridgeStamp(): string {
115
- const combined = `${SERVER_BRIDGE_SOURCE}|${CLIENT_BRIDGE_SOURCE}`;
116
- let h = 5381;
117
- for (let i = 1; i <= combined.size(); i++) {
118
- h = (h * 33 + string.byte(combined, i)[0]) % 2147483647;
119
- }
120
- // "__VERSION__" is replaced with the package version at package time
121
- // (scripts/build-plugin.mjs injectVersion), so a release bump also restamps.
122
- return `${tostring(h)}-__VERSION__`;
123
- }
124
-
125
- const BRIDGE_STAMP = computeBridgeStamp();
126
-
127
- function setSource(scriptInst: Script | LocalScript, source: string): void {
128
- // ScriptEditorService is the cleaner API and integrates with Studio's
129
- // edit history; fall back to direct Source mutation (allowed in plugin
130
- // context with PluginSecurity) if the edit service rejects the call.
131
- const [seOk] = pcall(() => {
132
- ScriptEditorService.UpdateSourceAsync(scriptInst, () => source);
133
- });
134
- if (!seOk) {
135
- (scriptInst as unknown as { Source: string }).Source = source;
136
- }
137
- }
138
-
139
- interface InstallResult {
140
- installed: boolean;
141
- error?: string;
142
- }
143
-
144
- function findLegacyEditBridges(): { server?: Instance; client?: Instance } {
145
- const sps = getStarterPlayerScripts();
146
- return {
147
- server: ServerScriptService.FindFirstChild(SERVER_SCRIPT_NAME),
148
- client: sps ? sps.FindFirstChild(CLIENT_SCRIPT_NAME) : undefined,
149
- };
150
- }
151
-
152
- function destroyIfPresent(parent: Instance, name: string): void {
153
- const existing = parent.FindFirstChild(name);
154
- if (existing) {
155
- pcall(() => existing.Destroy());
156
- }
157
- }
158
-
159
- export function cleanupLegacyEditBridges(): void {
160
- if (RunService.IsRunning()) return;
161
- const { server, client } = findLegacyEditBridges();
162
- if (server) {
163
- pcall(() => server.Destroy());
164
- }
165
- if (client) {
166
- pcall(() => client.Destroy());
167
- }
168
- }
169
-
170
- function serverRuntimeBridgeReady(): boolean {
171
- const scriptInst = ServerScriptService.FindFirstChild(SERVER_SCRIPT_NAME);
172
- const bindable = ServerScriptService.FindFirstChild(BRIDGE_NAMES.serverLocal);
173
- return scriptInst !== undefined &&
174
- scriptInst.GetAttribute(STAMP_ATTR) === BRIDGE_STAMP &&
175
- bindable !== undefined &&
176
- bindable.IsA("BindableFunction");
177
- }
178
-
179
- function getPlayerScripts(): Instance | undefined {
180
- const localPlayer = Players.LocalPlayer;
181
- if (!localPlayer) return undefined;
182
- let playerScripts = localPlayer.FindFirstChild("PlayerScripts");
183
- if (!playerScripts) {
184
- playerScripts = localPlayer.WaitForChild("PlayerScripts", 5);
185
- }
186
- return playerScripts;
187
- }
188
-
189
- function clientRuntimeBridgeReady(): boolean {
190
- const playerScripts = getPlayerScripts();
191
- if (!playerScripts) return false;
192
- const scriptInst = playerScripts.FindFirstChild(CLIENT_SCRIPT_NAME);
193
- const bindable = ReplicatedStorage.FindFirstChild(BRIDGE_NAMES.clientLocal);
194
- return scriptInst !== undefined &&
195
- scriptInst.GetAttribute(STAMP_ATTR) === BRIDGE_STAMP &&
196
- bindable !== undefined &&
197
- bindable.IsA("BindableFunction");
198
- }
199
-
200
- function installServerRuntimeBridge(): InstallResult {
201
- if (serverRuntimeBridgeReady()) return { installed: true };
202
-
203
- const [ok, err] = pcall(() => {
204
- destroyIfPresent(ServerScriptService, SERVER_SCRIPT_NAME);
205
- destroyIfPresent(ServerScriptService, BRIDGE_NAMES.serverLocal);
206
-
207
- const serverScript = new Instance("Script");
208
- serverScript.Name = SERVER_SCRIPT_NAME;
209
- serverScript.Archivable = false;
210
- setSource(serverScript, SERVER_BRIDGE_SOURCE);
211
- serverScript.SetAttribute(STAMP_ATTR, BRIDGE_STAMP);
212
- serverScript.Parent = ServerScriptService;
213
- });
214
-
215
- if (!ok) {
216
- return { installed: false, error: tostring(err) };
217
- }
218
- return { installed: true };
219
- }
220
-
221
- function installClientRuntimeBridge(): InstallResult {
222
- if (clientRuntimeBridgeReady()) return { installed: true };
223
-
224
- const playerScripts = getPlayerScripts();
225
- if (!playerScripts) {
226
- return { installed: false, error: "Players.LocalPlayer.PlayerScripts not found - cannot install client eval bridge" };
227
- }
228
-
229
- const [ok, err] = pcall(() => {
230
- destroyIfPresent(playerScripts, CLIENT_SCRIPT_NAME);
231
- destroyIfPresent(ReplicatedStorage, BRIDGE_NAMES.clientLocal);
232
-
233
- const clientScript = new Instance("LocalScript");
234
- clientScript.Name = CLIENT_SCRIPT_NAME;
235
- clientScript.Archivable = false;
236
- setSource(clientScript, CLIENT_BRIDGE_SOURCE);
237
- clientScript.SetAttribute(STAMP_ATTR, BRIDGE_STAMP);
238
- clientScript.Parent = playerScripts;
239
- });
240
-
241
- if (!ok) {
242
- return { installed: false, error: tostring(err) };
243
- }
244
- return { installed: true };
245
- }
246
-
247
- export function ensureRuntimeBridgeInstalled(): InstallResult {
248
- if (!RunService.IsRunning()) {
249
- return { installed: false, error: "Eval bridges are installed only in running play DataModels" };
250
- }
251
- if (RunService.IsServer()) {
252
- return installServerRuntimeBridge();
253
- }
254
- return installClientRuntimeBridge();
255
- }
@@ -1,50 +0,0 @@
1
- import { HttpService } from "@rbxts/services";
2
-
3
- interface FailureBody {
4
- error?: string;
5
- message?: string;
6
- missingFields?: unknown;
7
- request?: unknown;
8
- existing?: unknown;
9
- details?: unknown;
10
- }
11
-
12
- function encodeForLog(value: unknown): string {
13
- const [ok, encoded] = pcall(() => HttpService.JSONEncode(value));
14
- return ok ? encoded : tostring(value);
15
- }
16
-
17
- function formatBody(body: string): string {
18
- if (body === "") return "";
19
- const [ok, decoded] = pcall(() => HttpService.JSONDecode(body));
20
- if (ok && typeIs(decoded, "table")) {
21
- const data = decoded as FailureBody;
22
- const parts: string[] = [];
23
- if (typeIs(data.error, "string") && data.error !== "") parts.push(`error=${data.error}`);
24
- if (typeIs(data.message, "string") && data.message !== "") parts.push(`message=${data.message}`);
25
- if (data.missingFields !== undefined) parts.push(`missingFields=${encodeForLog(data.missingFields)}`);
26
- if (data.request !== undefined) parts.push(`request=${encodeForLog(data.request)}`);
27
- if (data.existing !== undefined) parts.push(`existing=${encodeForLog(data.existing)}`);
28
- if (data.details !== undefined) parts.push(`details=${encodeForLog(data.details)}`);
29
- if (parts.size() > 0) return parts.join(" ");
30
- }
31
- return `body=${body}`;
32
- }
33
-
34
- function formatRequestFailure(url: string, ok: boolean, res: unknown): string {
35
- if (!ok) {
36
- return `RequestAsync threw for ${url}: ${tostring(res)}`;
37
- }
38
- if (res === undefined) {
39
- return `RequestAsync returned no response for ${url}`;
40
- }
41
- const response = res as RequestAsyncResponse;
42
- const statusMessage = response.StatusMessage !== "" ? ` ${response.StatusMessage}` : "";
43
- const body = formatBody(response.Body);
44
- const suffix = body !== "" ? `: ${body}` : "";
45
- return `HTTP ${response.StatusCode}${statusMessage} from ${url}${suffix}`;
46
- }
47
-
48
- export = {
49
- formatRequestFailure,
50
- };