@chrrxs/robloxstudio-mcp 2.9.1 → 2.10.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.
@@ -294,8 +294,19 @@ function executeLuau(requestData: Record<string, unknown>) {
294
294
  const runViaModuleScript = () => {
295
295
  const m = new Instance("ModuleScript");
296
296
  m.Name = "__MCPExecLuauPayload";
297
+ // Wrap user code in an IIFE so require() always gets exactly one
298
+ // return value. Without this, code like `print("x")` errors with
299
+ // "Module code did not return exactly one value" because top-level
300
+ // ModuleScripts must return exactly one value.
301
+ //
302
+ // The DOUBLE parens around the call are load-bearing: in Luau,
303
+ // `return f()` propagates whatever multi-value tuple f returns,
304
+ // including zero values. Outer parens adjust the call to exactly
305
+ // one value (the first, or nil). So `return ((f)())` always
306
+ // returns exactly one value, regardless of what f does.
307
+ const wrapped = `return ((function()\n${code}\nend)())`;
297
308
  const [okSet, setErr] = pcall(() => {
298
- (m as unknown as { Source: string }).Source = code;
309
+ (m as unknown as { Source: string }).Source = wrapped;
299
310
  });
300
311
  if (!okSet) {
301
312
  m.Destroy();
@@ -96,8 +96,23 @@ function searchFiles(requestData: Record<string, unknown>) {
96
96
  }
97
97
 
98
98
  function getPlaceInfo(_requestData: Record<string, unknown>) {
99
+ const dataModelName = game.Name;
100
+ let placeName = dataModelName;
101
+
102
+ if (game.PlaceId > 0) {
103
+ const MarketplaceService = game.GetService("MarketplaceService");
104
+ const [ok, info] = pcall(() => MarketplaceService.GetProductInfo(game.PlaceId));
105
+ if (ok && info !== undefined) {
106
+ const name = (info as { Name?: string }).Name;
107
+ if (typeIs(name, "string") && name !== "") {
108
+ placeName = name;
109
+ }
110
+ }
111
+ }
112
+
99
113
  return {
100
- placeName: game.Name,
114
+ placeName,
115
+ dataModelName,
101
116
  placeId: game.PlaceId,
102
117
  gameId: game.GameId,
103
118
  jobId: game.JobId,
@@ -1,5 +1,5 @@
1
1
  import { HttpService, LogService } from "@rbxts/services";
2
- import { installBridges, cleanupBridges, loadStringEnabled } from "../EvalBridges";
2
+ import { installBridges, cleanupBridges } from "../EvalBridges";
3
3
 
4
4
  const StudioTestService = game.GetService("StudioTestService");
5
5
  const ServerScriptService = game.GetService("ServerScriptService");
@@ -159,7 +159,6 @@ function startPlaytest(requestData: Record<string, unknown>) {
159
159
  // so eval_server_runtime / eval_client_runtime work without manual setup.
160
160
  // Bridges are cleaned up from the edit DM after the play DMs tear down.
161
161
  const bridgeInstall = installBridges();
162
- const hasLoadString = loadStringEnabled();
163
162
  if (!bridgeInstall.installed) {
164
163
  warn(`[MCP] Eval bridge install failed: ${bridgeInstall.error}`);
165
164
  }
@@ -203,17 +202,6 @@ function startPlaytest(requestData: Record<string, unknown>) {
203
202
  evalBridges: bridgeInstall.installed ? "installed" : `failed: ${bridgeInstall.error}`,
204
203
  };
205
204
 
206
- // Surface loadstring availability up-front so callers know whether
207
- // eval_server_runtime will work before they try it. eval_client_runtime
208
- // doesn't need loadstring (it uses ModuleScript+require), so this only
209
- // affects the server bridge.
210
- if (!hasLoadString) {
211
- response.serverEvalNote =
212
- "ServerScriptService.LoadStringEnabled is false. eval_server_runtime will not work " +
213
- "until you enable it (ServerScriptService > Properties > LoadStringEnabled = true) " +
214
- "and restart the playtest. eval_client_runtime is unaffected.";
215
- }
216
-
217
205
  return response;
218
206
  }
219
207
 
@@ -2,14 +2,24 @@ import State from "../modules/State";
2
2
  import UI from "../modules/UI";
3
3
  import Communication from "../modules/Communication";
4
4
  import ClientBroker from "../modules/ClientBroker";
5
+ import RuntimeLogBuffer from "../modules/RuntimeLogBuffer";
5
6
 
7
+ // Attach the per-peer LogService.MessageOut listener as early as possible so
8
+ // boot-time prints from the user's place scripts are captured. Powers the
9
+ // get_runtime_logs MCP tool. Idempotent; safe to call before UI.init().
10
+ RuntimeLogBuffer.install();
6
11
 
7
12
  UI.init(plugin);
8
13
  const elements = UI.getElements();
9
14
 
10
15
 
16
+ const ICON_DISCONNECTED = "rbxassetid://__BUTTON_ICON_DISCONNECTED__";
17
+ const ICON_CONNECTING = "rbxassetid://__BUTTON_ICON_CONNECTING__";
18
+ const ICON_CONNECTED = "rbxassetid://__BUTTON_ICON_CONNECTED__";
19
+
11
20
  const toolbar = plugin.CreateToolbar("__TOOLBAR_NAME__");
12
- const button = toolbar.CreateButton("__BUTTON_TITLE__", "__BUTTON_TOOLTIP__", "rbxassetid://__BUTTON_ICON_ID__");
21
+ const button = toolbar.CreateButton("__BUTTON_TITLE__", "__BUTTON_TOOLTIP__", ICON_DISCONNECTED);
22
+ UI.setToolbarButton(button, { disconnected: ICON_DISCONNECTED, connecting: ICON_CONNECTING, connected: ICON_CONNECTED });
13
23
 
14
24
 
15
25
  elements.connectButton.Activated.Connect(() => {
@@ -32,6 +32,10 @@ export interface PollResponse {
32
32
  mcpConnected: boolean;
33
33
  request?: RequestPayload;
34
34
  requestId?: string;
35
+ // Server signals knownInstance=false when its in-memory instances map
36
+ // doesn't contain our instanceId (typically after an MCP process restart).
37
+ // The plugin re-issues /ready when it sees this.
38
+ knownInstance?: boolean;
35
39
  }
36
40
 
37
41
  export interface ReadyResponse {