@chrrxs/robloxstudio-mcp-inspector 2.11.1 → 2.11.3

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
@@ -1217,7 +1217,28 @@ ${s}
1217
1217
  }
1218
1218
  function buildModuleScriptInvokeWrapper(opts) {
1219
1219
  const wrapped = `return ((function()
1220
+ local __mcp_output = {}
1221
+ local __mcp_real_print = print
1222
+ local __mcp_real_warn = warn
1223
+ local print = function(...)
1224
+ __mcp_real_print(...)
1225
+ local args = {...}
1226
+ local parts = table.create(#args)
1227
+ for i, a in ipairs(args) do parts[i] = tostring(a) end
1228
+ table.insert(__mcp_output, table.concat(parts, "\\t"))
1229
+ end
1230
+ local warn = function(...)
1231
+ __mcp_real_warn(...)
1232
+ local args = {...}
1233
+ local parts = table.create(#args)
1234
+ for i, a in ipairs(args) do parts[i] = tostring(a) end
1235
+ table.insert(__mcp_output, "[warn] " .. table.concat(parts, "\\t"))
1236
+ end
1237
+ local function __mcp_run()
1220
1238
  ${opts.userCode}
1239
+ end
1240
+ local ok, errOrValue = xpcall(__mcp_run, debug.traceback)
1241
+ return { ok = ok, value = errOrValue, output = __mcp_output }
1221
1242
  end)())`;
1222
1243
  return `
1223
1244
  local HttpService = game:GetService("HttpService")
@@ -1234,15 +1255,29 @@ m.Name = "__MCPEvalPayload"
1234
1255
  local okSet, setErr = pcall(function() m.Source = USER_CODE end)
1235
1256
  if not okSet then
1236
1257
  m:Destroy()
1237
- return HttpService:JSONEncode({ bridge = "ok", ok = false, result = "ModuleScript Source set failed: " .. tostring(setErr) })
1258
+ return HttpService:JSONEncode({ bridge = "ok", ok = false, error = "ModuleScript Source set failed: " .. tostring(setErr) })
1238
1259
  end
1239
1260
  m.Parent = workspace
1240
- local ok, result = bf:Invoke(m)
1261
+ local bridgeOk, inner = bf:Invoke(m)
1241
1262
  m:Destroy()
1263
+ if not bridgeOk then
1264
+ return HttpService:JSONEncode({ bridge = "ok", ok = false, error = tostring(inner) })
1265
+ end
1266
+ -- inner is the {ok, value, output} table from our IIFE. Defensive: if it's
1267
+ -- somehow not a table (caller bypassed the wrapper), fall back to old shape.
1268
+ if typeof(inner) ~= "table" then
1269
+ return HttpService:JSONEncode({
1270
+ bridge = "ok",
1271
+ ok = true,
1272
+ result = if inner == nil then nil else tostring(inner),
1273
+ })
1274
+ end
1242
1275
  return HttpService:JSONEncode({
1243
1276
  bridge = "ok",
1244
- ok = ok,
1245
- result = if result == nil then nil else tostring(result),
1277
+ ok = inner.ok == true,
1278
+ result = if inner.ok and inner.value ~= nil then tostring(inner.value) else nil,
1279
+ error = if not inner.ok then tostring(inner.value) else nil,
1280
+ output = inner.output or {},
1246
1281
  })
1247
1282
  `;
1248
1283
  }
@@ -1928,23 +1963,9 @@ ${code}`
1928
1963
  };
1929
1964
  }
1930
1965
  async stopPlaytest() {
1931
- let hasProxy = this.bridge.getInstances().some((i) => i.role === "edit-proxy");
1932
- if (!hasProxy) {
1933
- const deadline = Date.now() + 1500;
1934
- while (!hasProxy && Date.now() < deadline) {
1935
- await new Promise((r) => setTimeout(r, 150));
1936
- hasProxy = this.bridge.getInstances().some((i) => i.role === "edit-proxy");
1937
- }
1938
- }
1939
- const target = hasProxy ? "edit-proxy" : "edit";
1940
- const response = await this.client.request("/api/stop-playtest", {}, target);
1966
+ const response = await this.client.request("/api/stop-playtest", {}, "edit");
1941
1967
  return {
1942
- content: [
1943
- {
1944
- type: "text",
1945
- text: JSON.stringify(response)
1946
- }
1947
- ]
1968
+ content: [{ type: "text", text: JSON.stringify(response) }]
1948
1969
  };
1949
1970
  }
1950
1971
  async getPlaytestOutput(target) {
@@ -3087,14 +3108,42 @@ var init_proxy_bridge_service = __esm({
3087
3108
  "../core/dist/proxy-bridge-service.js"() {
3088
3109
  "use strict";
3089
3110
  init_bridge_service();
3090
- ProxyBridgeService = class extends BridgeService {
3111
+ ProxyBridgeService = class _ProxyBridgeService extends BridgeService {
3091
3112
  primaryBaseUrl;
3092
3113
  proxyInstanceId;
3093
3114
  proxyRequestTimeout = 3e4;
3115
+ cachedInstances = [];
3116
+ refreshTimer;
3117
+ static REFRESH_INTERVAL_MS = 1e3;
3094
3118
  constructor(primaryBaseUrl) {
3095
3119
  super();
3096
3120
  this.primaryBaseUrl = primaryBaseUrl;
3097
3121
  this.proxyInstanceId = uuidv42();
3122
+ this.refreshInstances();
3123
+ this.refreshTimer = setInterval(() => this.refreshInstances(), _ProxyBridgeService.REFRESH_INTERVAL_MS);
3124
+ }
3125
+ async refreshInstances() {
3126
+ try {
3127
+ const res = await fetch(`${this.primaryBaseUrl}/instances`);
3128
+ if (!res.ok)
3129
+ return;
3130
+ const body = await res.json();
3131
+ if (Array.isArray(body.instances)) {
3132
+ this.cachedInstances = body.instances;
3133
+ }
3134
+ } catch {
3135
+ }
3136
+ }
3137
+ getInstances() {
3138
+ return this.cachedInstances;
3139
+ }
3140
+ /** Called when this proxy is being discarded (e.g. promotion to primary
3141
+ replaced it). Stops the background refresh so it doesn't leak. */
3142
+ stop() {
3143
+ if (this.refreshTimer) {
3144
+ clearInterval(this.refreshTimer);
3145
+ this.refreshTimer = void 0;
3146
+ }
3098
3147
  }
3099
3148
  async sendRequest(endpoint, data, target = "edit") {
3100
3149
  const controller = new AbortController();
@@ -3222,8 +3271,12 @@ var init_server = __esm({
3222
3271
  const candidateApp = createHttpServer(candidateTools, candidateBridge, this.allowedToolNames, this.config);
3223
3272
  try {
3224
3273
  const result = await listenWithRetry(candidateApp, host, basePort, 1);
3274
+ const oldBridge = this.bridge;
3225
3275
  this.bridge = candidateBridge;
3226
3276
  this.tools = candidateTools;
3277
+ if (oldBridge instanceof ProxyBridgeService) {
3278
+ oldBridge.stop();
3279
+ }
3227
3280
  httpHandle = result.server;
3228
3281
  boundPort = result.port;
3229
3282
  primaryApp = candidateApp;
@@ -3286,6 +3339,9 @@ var init_server = __esm({
3286
3339
  clearInterval(cleanupInterval);
3287
3340
  if (promotionInterval)
3288
3341
  clearInterval(promotionInterval);
3342
+ if (this.bridge instanceof ProxyBridgeService) {
3343
+ this.bridge.stop();
3344
+ }
3289
3345
  await this.server.close().catch(() => {
3290
3346
  });
3291
3347
  if (httpHandle)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrrxs/robloxstudio-mcp-inspector",
3
- "version": "2.11.1",
3
+ "version": "2.11.3",
4
4
  "description": "Read-only MCP Server for Roblox Studio (fork of boshyxd/robloxstudio-mcp-inspector with per-peer execute_luau fixes baked in)",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",