@chrrxs/robloxstudio-mcp 2.11.4 → 2.12.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.
package/dist/index.js CHANGED
@@ -9,6 +9,299 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // ../core/dist/bridge-service.js
13
+ import { v4 as uuidv4 } from "uuid";
14
+ function toPublic(inst) {
15
+ return {
16
+ instanceId: inst.instanceId,
17
+ role: inst.role,
18
+ placeId: inst.placeId,
19
+ placeName: inst.placeName,
20
+ dataModelName: inst.dataModelName,
21
+ isRunning: inst.isRunning,
22
+ lastActivity: inst.lastActivity,
23
+ connectedAt: inst.connectedAt
24
+ };
25
+ }
26
+ var RoutingFailure, STALE_INSTANCE_MS, BridgeService;
27
+ var init_bridge_service = __esm({
28
+ "../core/dist/bridge-service.js"() {
29
+ "use strict";
30
+ RoutingFailure = class extends Error {
31
+ routingError;
32
+ constructor(routingError) {
33
+ super(routingError.message);
34
+ this.name = "RoutingFailure";
35
+ this.routingError = routingError;
36
+ }
37
+ };
38
+ STALE_INSTANCE_MS = 3e4;
39
+ BridgeService = class {
40
+ pendingRequests = /* @__PURE__ */ new Map();
41
+ // Keyed by pluginSessionId (the per-plugin GUID).
42
+ instances = /* @__PURE__ */ new Map();
43
+ requestTimeout = 3e4;
44
+ registerInstance(input) {
45
+ const { pluginSessionId, instanceId, role } = input;
46
+ let assignedRole = role;
47
+ if (role === "client") {
48
+ const used = /* @__PURE__ */ new Set();
49
+ for (const inst of this.instances.values()) {
50
+ const match = inst.role.match(/^client-(\d+)$/);
51
+ if (match)
52
+ used.add(Number(match[1]));
53
+ }
54
+ let idx = 1;
55
+ while (used.has(idx))
56
+ idx++;
57
+ assignedRole = `client-${idx}`;
58
+ }
59
+ const existing = Array.from(this.instances.values()).find((i) => i.instanceId === instanceId && i.role === assignedRole && i.pluginSessionId !== pluginSessionId);
60
+ if (existing) {
61
+ return {
62
+ ok: false,
63
+ error: {
64
+ code: "duplicate_instance_role",
65
+ message: `Another plugin is already registered as (${instanceId}, ${assignedRole}).`,
66
+ existing: toPublic(existing)
67
+ }
68
+ };
69
+ }
70
+ this.instances.set(pluginSessionId, {
71
+ pluginSessionId,
72
+ instanceId,
73
+ role: assignedRole,
74
+ placeId: input.placeId ?? 0,
75
+ placeName: input.placeName ?? "",
76
+ dataModelName: input.dataModelName ?? "",
77
+ isRunning: input.isRunning ?? false,
78
+ lastActivity: Date.now(),
79
+ connectedAt: Date.now()
80
+ });
81
+ return { ok: true, assignedRole, instanceId };
82
+ }
83
+ unregisterInstance(pluginSessionId) {
84
+ const removed = this.instances.get(pluginSessionId);
85
+ this.instances.delete(pluginSessionId);
86
+ if (!removed)
87
+ return;
88
+ for (const [id, req] of this.pendingRequests.entries()) {
89
+ const stillHasHandler = Array.from(this.instances.values()).some((i) => i.instanceId === req.targetInstanceId && i.role === req.targetRole);
90
+ if (!stillHasHandler) {
91
+ clearTimeout(req.timeoutId);
92
+ this.pendingRequests.delete(id);
93
+ req.reject(new Error(`Target (${req.targetInstanceId}, ${req.targetRole}) disconnected`));
94
+ }
95
+ }
96
+ }
97
+ getInstances() {
98
+ return Array.from(this.instances.values());
99
+ }
100
+ getPublicInstances() {
101
+ return this.getInstances().map(toPublic);
102
+ }
103
+ getInstanceBySessionId(pluginSessionId) {
104
+ return this.instances.get(pluginSessionId);
105
+ }
106
+ getPendingRequestCount() {
107
+ return this.pendingRequests.size;
108
+ }
109
+ updateInstanceActivity(pluginSessionId) {
110
+ const inst = this.instances.get(pluginSessionId);
111
+ if (inst) {
112
+ inst.lastActivity = Date.now();
113
+ }
114
+ }
115
+ updateInstanceMetadata(pluginSessionId, metadata) {
116
+ const inst = this.instances.get(pluginSessionId);
117
+ if (!inst)
118
+ return;
119
+ if (metadata.placeId !== void 0)
120
+ inst.placeId = metadata.placeId;
121
+ if (metadata.placeName !== void 0)
122
+ inst.placeName = metadata.placeName;
123
+ if (metadata.dataModelName !== void 0)
124
+ inst.dataModelName = metadata.dataModelName;
125
+ if (metadata.isRunning !== void 0)
126
+ inst.isRunning = metadata.isRunning;
127
+ }
128
+ cleanupStaleInstances() {
129
+ const now = Date.now();
130
+ for (const [id, inst] of this.instances.entries()) {
131
+ if (now - inst.lastActivity > STALE_INSTANCE_MS) {
132
+ this.unregisterInstance(id);
133
+ }
134
+ }
135
+ }
136
+ // Resolves (instance_id, target-role) MCP arguments to a concrete
137
+ // routing decision: either a single (instanceId, role) tuple or a fanout
138
+ // list. Returns an error result with the full instance list embedded so
139
+ // the caller (tool layer) can surface it without a second round-trip.
140
+ resolveTarget(input) {
141
+ const instances = this.getInstances();
142
+ const publicList = instances.map(toPublic);
143
+ const errorData = { instances: publicList, count: publicList.length };
144
+ const { instance_id, target } = input;
145
+ const isFanout = target === "all";
146
+ const role = target && target !== "all" ? target : void 0;
147
+ if (instance_id !== void 0) {
148
+ const matchingInstances = instances.filter((i) => i.instanceId === instance_id);
149
+ if (matchingInstances.length === 0) {
150
+ return {
151
+ ok: false,
152
+ error: {
153
+ code: "unrecognized_instance_id",
154
+ message: `instance_id "${instance_id}" is not connected. Pass one from data.instances.`,
155
+ data: errorData
156
+ }
157
+ };
158
+ }
159
+ if (isFanout) {
160
+ return {
161
+ ok: true,
162
+ mode: "fanout",
163
+ targets: matchingInstances.map((i) => ({
164
+ targetInstanceId: i.instanceId,
165
+ targetRole: i.role
166
+ }))
167
+ };
168
+ }
169
+ if (role) {
170
+ const exact = matchingInstances.find((i) => i.role === role);
171
+ if (!exact) {
172
+ return {
173
+ ok: false,
174
+ error: {
175
+ code: "target_role_not_present_on_instance",
176
+ message: `instance "${instance_id}" has no role "${role}". Available roles: ${matchingInstances.map((i) => i.role).join(", ")}.`,
177
+ data: errorData
178
+ }
179
+ };
180
+ }
181
+ return { ok: true, mode: "single", targetInstanceId: instance_id, targetRole: role };
182
+ }
183
+ if (matchingInstances.length === 1) {
184
+ return {
185
+ ok: true,
186
+ mode: "single",
187
+ targetInstanceId: instance_id,
188
+ targetRole: matchingInstances[0].role
189
+ };
190
+ }
191
+ const edit = matchingInstances.find((i) => i.role === "edit");
192
+ if (edit) {
193
+ return { ok: true, mode: "single", targetInstanceId: instance_id, targetRole: "edit" };
194
+ }
195
+ return {
196
+ ok: false,
197
+ error: {
198
+ code: "target_role_required",
199
+ message: `instance "${instance_id}" has multiple roles connected: ${matchingInstances.map((i) => i.role).join(", ")}. Pass target=<role>.`,
200
+ data: errorData
201
+ }
202
+ };
203
+ }
204
+ const distinctInstanceIds = new Set(instances.map((i) => i.instanceId));
205
+ if (distinctInstanceIds.size === 0) {
206
+ return {
207
+ ok: false,
208
+ error: {
209
+ code: "unrecognized_instance_id",
210
+ message: "No Studio plugin is connected.",
211
+ data: errorData
212
+ }
213
+ };
214
+ }
215
+ if (distinctInstanceIds.size > 1) {
216
+ const errorCode = role ? "ambiguous_target" : "multiple_instances_connected";
217
+ const msg = role ? `target=${role} is ambiguous: multiple places have this role. Pass instance_id.` : "Multiple Studio places are connected. Pass instance_id to disambiguate.";
218
+ return { ok: false, error: { code: errorCode, message: msg, data: errorData } };
219
+ }
220
+ const onlyInstanceId = instances[0].instanceId;
221
+ return this.resolveTarget({ instance_id: onlyInstanceId, target });
222
+ }
223
+ async sendRequest(endpoint, data, targetInstanceId, targetRole) {
224
+ const requestId = uuidv4();
225
+ return new Promise((resolve2, reject) => {
226
+ const timeoutId = setTimeout(() => {
227
+ if (this.pendingRequests.has(requestId)) {
228
+ this.pendingRequests.delete(requestId);
229
+ reject(new Error("Request timeout"));
230
+ }
231
+ }, this.requestTimeout);
232
+ const request = {
233
+ id: requestId,
234
+ endpoint,
235
+ data,
236
+ targetInstanceId,
237
+ targetRole,
238
+ timestamp: Date.now(),
239
+ resolve: resolve2,
240
+ reject,
241
+ timeoutId
242
+ };
243
+ this.pendingRequests.set(requestId, request);
244
+ });
245
+ }
246
+ getPendingRequest(callerInstanceId, callerRole) {
247
+ let oldestRequest = null;
248
+ for (const request of this.pendingRequests.values()) {
249
+ if (request.targetInstanceId !== callerInstanceId)
250
+ continue;
251
+ if (request.targetRole !== callerRole)
252
+ continue;
253
+ if (!oldestRequest || request.timestamp < oldestRequest.timestamp) {
254
+ oldestRequest = request;
255
+ }
256
+ }
257
+ if (oldestRequest) {
258
+ return {
259
+ requestId: oldestRequest.id,
260
+ request: {
261
+ endpoint: oldestRequest.endpoint,
262
+ data: oldestRequest.data
263
+ }
264
+ };
265
+ }
266
+ return null;
267
+ }
268
+ resolveRequest(requestId, response) {
269
+ const request = this.pendingRequests.get(requestId);
270
+ if (request) {
271
+ clearTimeout(request.timeoutId);
272
+ this.pendingRequests.delete(requestId);
273
+ request.resolve(response);
274
+ }
275
+ }
276
+ rejectRequest(requestId, error) {
277
+ const request = this.pendingRequests.get(requestId);
278
+ if (request) {
279
+ clearTimeout(request.timeoutId);
280
+ this.pendingRequests.delete(requestId);
281
+ request.reject(error);
282
+ }
283
+ }
284
+ cleanupOldRequests() {
285
+ const now = Date.now();
286
+ for (const [id, request] of this.pendingRequests.entries()) {
287
+ if (now - request.timestamp > this.requestTimeout) {
288
+ clearTimeout(request.timeoutId);
289
+ this.pendingRequests.delete(id);
290
+ request.reject(new Error("Request timeout"));
291
+ }
292
+ }
293
+ }
294
+ clearAllPendingRequests() {
295
+ for (const [, request] of this.pendingRequests.entries()) {
296
+ clearTimeout(request.timeoutId);
297
+ request.reject(new Error("Connection closed"));
298
+ }
299
+ this.pendingRequests.clear();
300
+ }
301
+ };
302
+ }
303
+ });
304
+
12
305
  // ../core/dist/http-server.js
13
306
  import express from "express";
14
307
  import cors from "cors";
@@ -56,12 +349,7 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
56
349
  version: serverConfig?.version,
57
350
  pluginConnected: instances.length > 0,
58
351
  instanceCount: instances.length,
59
- instances: instances.map((i) => ({
60
- instanceId: i.instanceId,
61
- role: i.role,
62
- lastActivity: i.lastActivity,
63
- connectedAt: i.connectedAt
64
- })),
352
+ instances: instances.map(toPublic),
65
353
  mcpServerActive: isMCPServerActive(),
66
354
  uptime: mcpServerActive ? Date.now() - mcpServerStartTime : 0,
67
355
  pendingRequests: bridge.getPendingRequestCount(),
@@ -70,22 +358,42 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
70
358
  });
71
359
  });
72
360
  app.post("/ready", (req, res) => {
73
- const { instanceId, role } = req.body;
74
- if (instanceId && role) {
75
- const assignedRole = bridge.registerInstance(instanceId, role);
76
- res.json({ success: true, assignedRole });
77
- } else {
78
- bridge.registerInstance("legacy", "edit");
79
- res.json({ success: true, assignedRole: "edit" });
361
+ const { pluginSessionId, instanceId, role, placeId, placeName, dataModelName, isRunning } = req.body;
362
+ if (!pluginSessionId || !instanceId || !role) {
363
+ res.status(400).json({
364
+ success: false,
365
+ error: "pluginSessionId, instanceId, and role are required"
366
+ });
367
+ return;
368
+ }
369
+ const result = bridge.registerInstance({
370
+ pluginSessionId,
371
+ instanceId,
372
+ role,
373
+ placeId: typeof placeId === "number" ? placeId : 0,
374
+ placeName: typeof placeName === "string" ? placeName : "",
375
+ dataModelName: typeof dataModelName === "string" ? dataModelName : "",
376
+ isRunning: !!isRunning
377
+ });
378
+ if (!result.ok) {
379
+ res.status(409).json({
380
+ success: false,
381
+ error: result.error.code,
382
+ message: result.error.message,
383
+ existing: result.error.existing
384
+ });
385
+ return;
80
386
  }
387
+ res.json({
388
+ success: true,
389
+ assignedRole: result.assignedRole,
390
+ instanceId: result.instanceId
391
+ });
81
392
  });
82
393
  app.post("/disconnect", (req, res) => {
83
- const { instanceId } = req.body;
84
- if (instanceId) {
85
- bridge.unregisterInstance(instanceId);
86
- } else {
87
- bridge.unregisterInstance("legacy");
88
- bridge.clearAllPendingRequests();
394
+ const { pluginSessionId } = req.body;
395
+ if (pluginSessionId) {
396
+ bridge.unregisterInstance(pluginSessionId);
89
397
  }
90
398
  res.json({ success: true });
91
399
  });
@@ -94,7 +402,7 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
94
402
  res.json({
95
403
  pluginConnected: instances.length > 0,
96
404
  instanceCount: instances.length,
97
- instances: instances.map((i) => ({ instanceId: i.instanceId, role: i.role })),
405
+ instances: instances.map(toPublic),
98
406
  mcpServerActive: isMCPServerActive(),
99
407
  lastMCPActivity,
100
408
  uptime: mcpServerActive ? Date.now() - mcpServerStartTime : 0
@@ -104,15 +412,17 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
104
412
  res.json({ instances: bridge.getInstances() });
105
413
  });
106
414
  app.get("/poll", (req, res) => {
107
- const instanceId = req.query.instanceId;
108
- if (instanceId) {
109
- bridge.updateInstanceActivity(instanceId);
415
+ const pluginSessionId = req.query.pluginSessionId;
416
+ if (pluginSessionId) {
417
+ bridge.updateInstanceActivity(pluginSessionId);
110
418
  }
111
- let callerRole = "edit";
419
+ let callerInstanceId;
420
+ let callerRole;
112
421
  let knownInstance = false;
113
- if (instanceId) {
114
- const inst = bridge.getInstances().find((i) => i.instanceId === instanceId);
422
+ if (pluginSessionId) {
423
+ const inst = bridge.getInstanceBySessionId(pluginSessionId);
115
424
  if (inst) {
425
+ callerInstanceId = inst.instanceId;
116
426
  callerRole = inst.role;
117
427
  knownInstance = true;
118
428
  }
@@ -127,7 +437,7 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
127
437
  });
128
438
  return;
129
439
  }
130
- const pendingRequest = bridge.getPendingRequest(callerRole);
440
+ const pendingRequest = knownInstance && callerInstanceId && callerRole ? bridge.getPendingRequest(callerInstanceId, callerRole) : null;
131
441
  if (pendingRequest) {
132
442
  res.json({
133
443
  request: pendingRequest.request,
@@ -157,16 +467,16 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
157
467
  res.json({ success: true });
158
468
  });
159
469
  app.post("/proxy", async (req, res) => {
160
- const { endpoint, data, target, proxyInstanceId } = req.body;
161
- if (!endpoint) {
162
- res.status(400).json({ error: "endpoint is required" });
470
+ const { endpoint, data, targetInstanceId, targetRole, proxyInstanceId } = req.body;
471
+ if (!endpoint || !targetInstanceId || !targetRole) {
472
+ res.status(400).json({ error: "endpoint, targetInstanceId, and targetRole are required" });
163
473
  return;
164
474
  }
165
475
  if (proxyInstanceId) {
166
476
  proxyInstances.add(proxyInstanceId);
167
477
  }
168
478
  try {
169
- const response = await bridge.sendRequest(endpoint, data, target || "edit");
479
+ const response = await bridge.sendRequest(endpoint, data, targetInstanceId, targetRole);
170
480
  res.json({ response });
171
481
  } catch (err) {
172
482
  res.status(500).json({ error: err.message || "Proxy request failed" });
@@ -197,6 +507,19 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
197
507
  try {
198
508
  return await handler(tools, args || {});
199
509
  } catch (error) {
510
+ if (error instanceof RoutingFailure) {
511
+ return {
512
+ content: [{
513
+ type: "text",
514
+ text: JSON.stringify({
515
+ error: error.routingError.code,
516
+ message: error.routingError.message,
517
+ data: error.routingError.data
518
+ })
519
+ }],
520
+ isError: true
521
+ };
522
+ }
200
523
  if (error instanceof McpError)
201
524
  throw error;
202
525
  throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -248,6 +571,14 @@ function createHttpServer(tools, bridge, allowedTools, serverConfig) {
248
571
  const result = await handler(tools, req.body);
249
572
  res.json(result);
250
573
  } catch (error) {
574
+ if (error instanceof RoutingFailure) {
575
+ res.status(400).json({
576
+ error: error.routingError.code,
577
+ message: error.routingError.message,
578
+ data: error.routingError.data
579
+ });
580
+ return;
581
+ }
251
582
  res.status(500).json({ error: error instanceof Error ? error.message : "Unknown error" });
252
583
  }
253
584
  });
@@ -296,26 +627,27 @@ var TOOL_HANDLERS;
296
627
  var init_http_server = __esm({
297
628
  "../core/dist/http-server.js"() {
298
629
  "use strict";
630
+ init_bridge_service();
299
631
  TOOL_HANDLERS = {
300
- get_file_tree: (tools, body) => tools.getFileTree(body.path),
301
- search_files: (tools, body) => tools.searchFiles(body.query, body.searchType),
302
- get_place_info: (tools) => tools.getPlaceInfo(),
303
- get_services: (tools, body) => tools.getServices(body.serviceName),
304
- search_objects: (tools, body) => tools.searchObjects(body.query, body.searchType, body.propertyName),
305
- get_instance_properties: (tools, body) => tools.getInstanceProperties(body.instancePath, body.excludeSource),
306
- get_instance_children: (tools, body) => tools.getInstanceChildren(body.instancePath),
307
- search_by_property: (tools, body) => tools.searchByProperty(body.propertyName, body.propertyValue),
308
- get_class_info: (tools, body) => tools.getClassInfo(body.className),
309
- get_project_structure: (tools, body) => tools.getProjectStructure(body.path, body.maxDepth, body.scriptsOnly),
310
- set_property: (tools, body) => tools.setProperty(body.instancePath, body.propertyName, body.propertyValue),
311
- set_properties: (tools, body) => tools.setProperties(body.instancePath, body.properties),
312
- mass_set_property: (tools, body) => tools.massSetProperty(body.paths, body.propertyName, body.propertyValue),
313
- mass_get_property: (tools, body) => tools.massGetProperty(body.paths, body.propertyName),
314
- create_object: (tools, body) => tools.createObject(body.className, body.parent, body.name, body.properties),
315
- mass_create_objects: (tools, body) => tools.massCreateObjects(body.objects),
316
- delete_object: (tools, body) => tools.deleteObject(body.instancePath),
317
- smart_duplicate: (tools, body) => tools.smartDuplicate(body.instancePath, body.count, body.options),
318
- mass_duplicate: (tools, body) => tools.massDuplicate(body.duplications),
632
+ get_file_tree: (tools, body) => tools.getFileTree(body.path, body.instance_id),
633
+ search_files: (tools, body) => tools.searchFiles(body.query, body.searchType, body.instance_id),
634
+ get_place_info: (tools, body) => tools.getPlaceInfo(body.instance_id),
635
+ get_services: (tools, body) => tools.getServices(body.serviceName, body.instance_id),
636
+ search_objects: (tools, body) => tools.searchObjects(body.query, body.searchType, body.propertyName, body.instance_id),
637
+ get_instance_properties: (tools, body) => tools.getInstanceProperties(body.instancePath, body.excludeSource, body.instance_id),
638
+ get_instance_children: (tools, body) => tools.getInstanceChildren(body.instancePath, body.instance_id),
639
+ search_by_property: (tools, body) => tools.searchByProperty(body.propertyName, body.propertyValue, body.instance_id),
640
+ get_class_info: (tools, body) => tools.getClassInfo(body.className, body.instance_id),
641
+ get_project_structure: (tools, body) => tools.getProjectStructure(body.path, body.maxDepth, body.scriptsOnly, body.instance_id),
642
+ set_property: (tools, body) => tools.setProperty(body.instancePath, body.propertyName, body.propertyValue, body.instance_id),
643
+ set_properties: (tools, body) => tools.setProperties(body.instancePath, body.properties, body.instance_id),
644
+ mass_set_property: (tools, body) => tools.massSetProperty(body.paths, body.propertyName, body.propertyValue, body.instance_id),
645
+ mass_get_property: (tools, body) => tools.massGetProperty(body.paths, body.propertyName, body.instance_id),
646
+ create_object: (tools, body) => tools.createObject(body.className, body.parent, body.name, body.properties, body.instance_id),
647
+ mass_create_objects: (tools, body) => tools.massCreateObjects(body.objects, body.instance_id),
648
+ delete_object: (tools, body) => tools.deleteObject(body.instancePath, body.instance_id),
649
+ smart_duplicate: (tools, body) => tools.smartDuplicate(body.instancePath, body.count, body.options, body.instance_id),
650
+ mass_duplicate: (tools, body) => tools.massDuplicate(body.duplications, body.instance_id),
319
651
  grep_scripts: (tools, body) => tools.grepScripts(body.pattern, {
320
652
  caseSensitive: body.caseSensitive,
321
653
  usePattern: body.usePattern,
@@ -325,56 +657,56 @@ var init_http_server = __esm({
325
657
  filesOnly: body.filesOnly,
326
658
  path: body.path,
327
659
  classFilter: body.classFilter
328
- }),
329
- get_script_source: (tools, body) => tools.getScriptSource(body.instancePath, body.startLine, body.endLine),
330
- set_script_source: (tools, body) => tools.setScriptSource(body.instancePath, body.source),
331
- edit_script_lines: (tools, body) => tools.editScriptLines(body.instancePath, body.old_string, body.new_string, body.startLine),
332
- insert_script_lines: (tools, body) => tools.insertScriptLines(body.instancePath, body.afterLine, body.newContent),
333
- delete_script_lines: (tools, body) => tools.deleteScriptLines(body.instancePath, body.startLine, body.endLine),
334
- set_attribute: (tools, body) => tools.setAttribute(body.instancePath, body.attributeName, body.attributeValue, body.valueType),
335
- get_attributes: (tools, body) => tools.getAttributes(body.instancePath),
336
- delete_attribute: (tools, body) => tools.deleteAttribute(body.instancePath, body.attributeName),
337
- get_tags: (tools, body) => tools.getTags(body.instancePath),
338
- add_tag: (tools, body) => tools.addTag(body.instancePath, body.tagName),
339
- remove_tag: (tools, body) => tools.removeTag(body.instancePath, body.tagName),
340
- get_tagged: (tools, body) => tools.getTagged(body.tagName),
341
- get_selection: (tools) => tools.getSelection(),
342
- execute_luau: (tools, body) => tools.executeLuau(body.code, body.target),
343
- eval_server_runtime: (tools, body) => tools.evalServerRuntime(body.code),
344
- eval_client_runtime: (tools, body) => tools.evalClientRuntime(body.code, body.target),
345
- start_playtest: (tools, body) => tools.startPlaytest(body.mode, body.numPlayers),
346
- stop_playtest: (tools) => tools.stopPlaytest(),
347
- get_playtest_output: (tools, body) => tools.getPlaytestOutput(body.target),
348
- get_runtime_logs: (tools, body) => tools.getRuntimeLogs(body.target, body.since, body.tail, body.filter),
660
+ }, body.instance_id),
661
+ get_script_source: (tools, body) => tools.getScriptSource(body.instancePath, body.startLine, body.endLine, body.instance_id),
662
+ set_script_source: (tools, body) => tools.setScriptSource(body.instancePath, body.source, body.instance_id),
663
+ edit_script_lines: (tools, body) => tools.editScriptLines(body.instancePath, body.old_string, body.new_string, body.startLine, body.instance_id),
664
+ insert_script_lines: (tools, body) => tools.insertScriptLines(body.instancePath, body.afterLine, body.newContent, body.instance_id),
665
+ delete_script_lines: (tools, body) => tools.deleteScriptLines(body.instancePath, body.startLine, body.endLine, body.instance_id),
666
+ set_attribute: (tools, body) => tools.setAttribute(body.instancePath, body.attributeName, body.attributeValue, body.valueType, body.instance_id),
667
+ get_attributes: (tools, body) => tools.getAttributes(body.instancePath, body.instance_id),
668
+ delete_attribute: (tools, body) => tools.deleteAttribute(body.instancePath, body.attributeName, body.instance_id),
669
+ get_tags: (tools, body) => tools.getTags(body.instancePath, body.instance_id),
670
+ add_tag: (tools, body) => tools.addTag(body.instancePath, body.tagName, body.instance_id),
671
+ remove_tag: (tools, body) => tools.removeTag(body.instancePath, body.tagName, body.instance_id),
672
+ get_tagged: (tools, body) => tools.getTagged(body.tagName, body.instance_id),
673
+ get_selection: (tools, body) => tools.getSelection(body.instance_id),
674
+ execute_luau: (tools, body) => tools.executeLuau(body.code, body.target, body.instance_id),
675
+ eval_server_runtime: (tools, body) => tools.evalServerRuntime(body.code, body.instance_id),
676
+ eval_client_runtime: (tools, body) => tools.evalClientRuntime(body.code, body.target, body.instance_id),
677
+ start_playtest: (tools, body) => tools.startPlaytest(body.mode, body.numPlayers, body.instance_id),
678
+ stop_playtest: (tools, body) => tools.stopPlaytest(body.instance_id),
679
+ get_playtest_output: (tools, body) => tools.getPlaytestOutput(body.target, body.instance_id),
680
+ get_runtime_logs: (tools, body) => tools.getRuntimeLogs(body.target, body.since, body.tail, body.filter, body.instance_id),
349
681
  get_connected_instances: (tools) => tools.getConnectedInstances(),
350
- export_build: (tools, body) => tools.exportBuild(body.instancePath, body.outputId, body.style),
682
+ export_build: (tools, body) => tools.exportBuild(body.instancePath, body.outputId, body.style, body.instance_id),
351
683
  create_build: (tools, body) => tools.createBuild(body.id, body.style, body.palette, body.parts, body.bounds),
352
684
  generate_build: (tools, body) => tools.generateBuild(body.id, body.style, body.palette, body.code, body.seed),
353
- import_build: (tools, body) => tools.importBuild(body.buildData, body.targetPath, body.position),
685
+ import_build: (tools, body) => tools.importBuild(body.buildData, body.targetPath, body.position, body.instance_id),
354
686
  list_library: (tools, body) => tools.listLibrary(body.style),
355
- search_materials: (tools, body) => tools.searchMaterials(body.query, body.maxResults),
687
+ search_materials: (tools, body) => tools.searchMaterials(body.query, body.maxResults, body.instance_id),
356
688
  get_build: (tools, body) => tools.getBuild(body.id),
357
- import_scene: (tools, body) => tools.importScene(body.sceneData, body.targetPath),
358
- undo: (tools) => tools.undo(),
359
- redo: (tools) => tools.redo(),
689
+ import_scene: (tools, body) => tools.importScene(body.sceneData, body.targetPath, body.instance_id),
690
+ undo: (tools, body) => tools.undo(body.instance_id),
691
+ redo: (tools, body) => tools.redo(body.instance_id),
360
692
  search_assets: (tools, body) => tools.searchAssets(body.assetType, body.query, body.maxResults, body.sortBy, body.verifiedCreatorsOnly),
361
693
  get_asset_details: (tools, body) => tools.getAssetDetails(body.assetId),
362
694
  get_asset_thumbnail: (tools, body) => tools.getAssetThumbnail(body.assetId, body.size),
363
- insert_asset: (tools, body) => tools.insertAsset(body.assetId, body.parentPath, body.position),
364
- preview_asset: (tools, body) => tools.previewAsset(body.assetId, body.includeProperties, body.maxDepth),
695
+ insert_asset: (tools, body) => tools.insertAsset(body.assetId, body.parentPath, body.position, body.instance_id),
696
+ preview_asset: (tools, body) => tools.previewAsset(body.assetId, body.includeProperties, body.maxDepth, body.instance_id),
365
697
  upload_asset: (tools, body) => tools.uploadAsset(body.filePath, body.assetType, body.displayName, body.description, body.userId, body.groupId),
366
- clone_object: (tools, body) => tools.cloneObject(body.instancePath, body.targetParentPath),
367
- get_descendants: (tools, body) => tools.getDescendants(body.instancePath, body.maxDepth, body.classFilter),
368
- compare_instances: (tools, body) => tools.compareInstances(body.instancePathA, body.instancePathB),
369
- get_output_log: (tools, body) => tools.getOutputLog(body.maxEntries, body.messageType),
370
- bulk_set_attributes: (tools, body) => tools.bulkSetAttributes(body.instancePath, body.attributes),
371
- capture_screenshot: (tools) => tools.captureScreenshot(),
372
- simulate_mouse_input: (tools, body) => tools.simulateMouseInput(body.action, body.x, body.y, body.button, body.scrollDirection, body.target),
373
- simulate_keyboard_input: (tools, body) => tools.simulateKeyboardInput(body.keyCode, body.action, body.duration, body.target),
374
- character_navigation: (tools, body) => tools.characterNavigation(body.position, body.instancePath, body.waitForCompletion, body.timeout, body.target),
375
- get_memory_breakdown: (tools, body) => tools.getMemoryBreakdown(body.target, body.tags),
376
- export_rbxm: (tools, body) => tools.exportRbxm(body.instance_paths, body.output_path, body.target),
377
- import_rbxm: (tools, body) => tools.importRbxm(body.source, body.parent_path, body.target),
698
+ clone_object: (tools, body) => tools.cloneObject(body.instancePath, body.targetParentPath, body.instance_id),
699
+ get_descendants: (tools, body) => tools.getDescendants(body.instancePath, body.maxDepth, body.classFilter, body.instance_id),
700
+ compare_instances: (tools, body) => tools.compareInstances(body.instancePathA, body.instancePathB, body.instance_id),
701
+ get_output_log: (tools, body) => tools.getOutputLog(body.maxEntries, body.messageType, body.instance_id),
702
+ bulk_set_attributes: (tools, body) => tools.bulkSetAttributes(body.instancePath, body.attributes, body.instance_id),
703
+ capture_screenshot: (tools, body) => tools.captureScreenshot(body.instance_id),
704
+ simulate_mouse_input: (tools, body) => tools.simulateMouseInput(body.action, body.x, body.y, body.button, body.scrollDirection, body.target, body.instance_id),
705
+ simulate_keyboard_input: (tools, body) => tools.simulateKeyboardInput(body.keyCode, body.action, body.duration, body.target, body.instance_id),
706
+ character_navigation: (tools, body) => tools.characterNavigation(body.position, body.instancePath, body.waitForCompletion, body.timeout, body.target, body.instance_id),
707
+ get_memory_breakdown: (tools, body) => tools.getMemoryBreakdown(body.target, body.tags, body.instance_id),
708
+ export_rbxm: (tools, body) => tools.exportRbxm(body.instance_paths, body.output_path, body.target, body.instance_id),
709
+ import_rbxm: (tools, body) => tools.importRbxm(body.source, body.parent_path, body.target, body.instance_id),
378
710
  find_and_replace_in_scripts: (tools, body) => tools.findAndReplaceInScripts(body.pattern, body.replacement, {
379
711
  caseSensitive: body.caseSensitive,
380
712
  usePattern: body.usePattern,
@@ -382,7 +714,7 @@ var init_http_server = __esm({
382
714
  classFilter: body.classFilter,
383
715
  dryRun: body.dryRun,
384
716
  maxReplacements: body.maxReplacements
385
- })
717
+ }, body.instance_id)
386
718
  };
387
719
  }
388
720
  });
@@ -397,9 +729,9 @@ var init_studio_client = __esm({
397
729
  constructor(bridge) {
398
730
  this.bridge = bridge;
399
731
  }
400
- async request(endpoint, data, target = "edit") {
732
+ async request(endpoint, data, targetInstanceId, targetRole) {
401
733
  try {
402
- const response = await this.bridge.sendRequest(endpoint, data, target);
734
+ const response = await this.bridge.sendRequest(endpoint, data, targetInstanceId, targetRole);
403
735
  return response;
404
736
  } catch (error) {
405
737
  if (error instanceof Error && error.message === "Request timeout") {
@@ -1215,8 +1547,16 @@ function luaLongQuote(s) {
1215
1547
  ${s}
1216
1548
  ]${eq}]`;
1217
1549
  }
1550
+ function evalCountLines(s) {
1551
+ return s.split("\n").length;
1552
+ }
1218
1553
  function buildModuleScriptInvokeWrapper(opts) {
1554
+ const userLines = evalCountLines(opts.userCode);
1219
1555
  const wrapped = `return ((function()
1556
+ local __mcp_traceback
1557
+ local __mcp_remap
1558
+ local __mcp_LINE_OFFSET = ${EVAL_WRAPPER_LINE_OFFSET}
1559
+ local __mcp_USER_LINES = ${userLines}
1220
1560
  local __mcp_output = {}
1221
1561
  local __mcp_real_print = print
1222
1562
  local __mcp_real_warn = warn
@@ -1237,7 +1577,65 @@ function buildModuleScriptInvokeWrapper(opts) {
1237
1577
  local function __mcp_run()
1238
1578
  ${opts.userCode}
1239
1579
  end
1240
- local ok, errOrValue = xpcall(__mcp_run, debug.traceback)
1580
+ __mcp_remap = function(s)
1581
+ -- Two chunk-name formats can reference our payload: the
1582
+ -- ModuleScript path "Workspace.__MCPEvalPayload:N" and the
1583
+ -- loadstring chunk "[string \\"return ((function()...\\"]:N" (if
1584
+ -- the IIFE happens to compile via loadstring). Normalize both to
1585
+ -- "user_code:N" with the offset stripped AND clamped to user
1586
+ -- range, otherwise unclosed constructs report nonsense lines deep
1587
+ -- in the wrapper. Strip the "Workspace." parent prefix too so the
1588
+ -- final output reads "user_code:N" not "Workspace.user_code:N".
1589
+ local function __mcp_user_line(payload_n)
1590
+ local user_n = payload_n - __mcp_LINE_OFFSET
1591
+ if user_n < 1 then return "1" end
1592
+ if user_n > __mcp_USER_LINES then return tostring(__mcp_USER_LINES) .. " (at end of input)" end
1593
+ return tostring(user_n)
1594
+ end
1595
+ s = string.gsub(s, "Workspace%.__MCPEvalPayload:(%d+)", function(num)
1596
+ local n = tonumber(num)
1597
+ if n then return "user_code:" .. __mcp_user_line(n) end
1598
+ return "user_code:" .. num
1599
+ end)
1600
+ s = string.gsub(s, "__MCPEvalPayload:(%d+)", function(num)
1601
+ local n = tonumber(num)
1602
+ if n then return "user_code:" .. __mcp_user_line(n) end
1603
+ return "user_code:" .. num
1604
+ end)
1605
+ s = string.gsub(s, '%[string "[^"]+"%]:(%d+)', function(num)
1606
+ local n = tonumber(num)
1607
+ if n then return "user_code:" .. __mcp_user_line(n) end
1608
+ return "user_code:" .. num
1609
+ end)
1610
+ return s
1611
+ end
1612
+ __mcp_traceback = function(err)
1613
+ local raw = debug.traceback(tostring(err), 2)
1614
+ local kept = {}
1615
+ for line in string.gmatch(raw, "[^\\n]+") do
1616
+ local num_str = string.match(line, "__MCPEvalPayload:(%d+)")
1617
+ or string.match(line, '%[string "[^"]+"%]:(%d+)')
1618
+ local n = num_str and tonumber(num_str)
1619
+ -- Strip "in function '__mcp_run'" annotation BEFORE filtering:
1620
+ -- user-code frames all carry that suffix (their source is
1621
+ -- hosted inside __mcp_run), so a naive "__mcp_" filter would
1622
+ -- drop every user frame and leave only the error header.
1623
+ line = (string.gsub(line, " in function '__mcp_run'", ""))
1624
+ local skip = string.find(line, "MCPPlugin", 1, true)
1625
+ or string.find(line, "__mcp_", 1, true)
1626
+ or string.find(line, "in function 'xpcall'", 1, true)
1627
+ -- Drop wrapper preamble/postamble frames whose line falls
1628
+ -- outside the user-code range \u2014 those are wrapper internals.
1629
+ if n and (n <= __mcp_LINE_OFFSET or n > __mcp_LINE_OFFSET + __mcp_USER_LINES) then
1630
+ skip = true
1631
+ end
1632
+ if not skip then
1633
+ table.insert(kept, __mcp_remap(line))
1634
+ end
1635
+ end
1636
+ return table.concat(kept, "\\n")
1637
+ end
1638
+ local ok, errOrValue = xpcall(__mcp_run, __mcp_traceback)
1241
1639
  return { ok = ok, value = errOrValue, output = __mcp_output }
1242
1640
  end)())`;
1243
1641
  return `
@@ -1249,6 +1647,46 @@ if not bf then
1249
1647
  error = ${luaLongQuote(opts.missingError)},
1250
1648
  })
1251
1649
  end
1650
+ -- Outer-scope mirror of the in-IIFE __mcp_remap. Applied to parser errors
1651
+ -- we pull out of LogService (those never pass through the IIFE) and to
1652
+ -- the canned engine error string. Same offset as the IIFE's
1653
+ -- __mcp_LINE_OFFSET; covers both chunk-name formats.
1654
+ local __mcp_USER_LINES_OUTER = ${userLines}
1655
+ local function __mcp_outer_user_line(payload_n)
1656
+ local user_n = payload_n - ${EVAL_WRAPPER_LINE_OFFSET}
1657
+ if user_n < 1 then return "1" end
1658
+ if user_n > __mcp_USER_LINES_OUTER then return tostring(__mcp_USER_LINES_OUTER) .. " (at end of input)" end
1659
+ return tostring(user_n)
1660
+ end
1661
+ local function __mcp_outer_remap(s)
1662
+ s = string.gsub(s, "Workspace%.__MCPEvalPayload:(%d+)", function(num)
1663
+ local n = tonumber(num)
1664
+ if n then return "user_code:" .. __mcp_outer_user_line(n) end
1665
+ return "user_code:" .. num
1666
+ end)
1667
+ s = string.gsub(s, "__MCPEvalPayload:(%d+)", function(num)
1668
+ local n = tonumber(num)
1669
+ if n then return "user_code:" .. __mcp_outer_user_line(n) end
1670
+ return "user_code:" .. num
1671
+ end)
1672
+ s = string.gsub(s, '%[string "[^"]+"%]:(%d+)', function(num)
1673
+ local n = tonumber(num)
1674
+ if n then return "user_code:" .. __mcp_outer_user_line(n) end
1675
+ return "user_code:" .. num
1676
+ end)
1677
+ return s
1678
+ end
1679
+ -- JSON-encode tables; otherwise tostring. Cycles or non-serializable
1680
+ -- values fall back to tostring instead of erroring. This is what makes
1681
+ -- eval_server_runtime / eval_client_runtime return structured table data
1682
+ -- (matching execute_luau) instead of "table: 0xaddr".
1683
+ local function __mcp_format(v)
1684
+ if typeof(v) == "table" then
1685
+ local ok, encoded = pcall(function() return HttpService:JSONEncode(v) end)
1686
+ if ok then return encoded end
1687
+ end
1688
+ return tostring(v)
1689
+ end
1252
1690
  local USER_CODE = ${luaLongQuote(wrapped)}
1253
1691
  local m = Instance.new("ModuleScript")
1254
1692
  m.Name = "__MCPEvalPayload"
@@ -1282,7 +1720,7 @@ if not bridgeOk then
1282
1720
  end
1283
1721
  end
1284
1722
  end
1285
- return HttpService:JSONEncode({ bridge = "ok", ok = false, error = errMsg })
1723
+ return HttpService:JSONEncode({ bridge = "ok", ok = false, error = __mcp_outer_remap(errMsg) })
1286
1724
  end
1287
1725
  -- inner is the {ok, value, output} table from our IIFE. Defensive: if it's
1288
1726
  -- somehow not a table (caller bypassed the wrapper), fall back to old shape.
@@ -1290,13 +1728,13 @@ if typeof(inner) ~= "table" then
1290
1728
  return HttpService:JSONEncode({
1291
1729
  bridge = "ok",
1292
1730
  ok = true,
1293
- result = if inner == nil then nil else tostring(inner),
1731
+ result = if inner == nil then nil else __mcp_format(inner),
1294
1732
  })
1295
1733
  end
1296
1734
  return HttpService:JSONEncode({
1297
1735
  bridge = "ok",
1298
1736
  ok = inner.ok == true,
1299
- result = if inner.ok and inner.value ~= nil then tostring(inner.value) else nil,
1737
+ result = if inner.ok and inner.value ~= nil then __mcp_format(inner.value) else nil,
1300
1738
  error = if not inner.ok then tostring(inner.value) else nil,
1301
1739
  output = inner.output or {},
1302
1740
  })
@@ -1313,17 +1751,19 @@ function parseBridgeResponse(response) {
1313
1751
  }
1314
1752
  return JSON.stringify(response);
1315
1753
  }
1316
- var SERVER_LOCAL_NAME, CLIENT_LOCAL_NAME, RobloxStudioTools;
1754
+ var SERVER_LOCAL_NAME, CLIENT_LOCAL_NAME, EVAL_WRAPPER_LINE_OFFSET, RobloxStudioTools;
1317
1755
  var init_tools = __esm({
1318
1756
  "../core/dist/tools/index.js"() {
1319
1757
  "use strict";
1320
1758
  init_studio_client();
1759
+ init_bridge_service();
1321
1760
  init_build_executor();
1322
1761
  init_opencloud_client();
1323
1762
  init_roblox_cookie_client();
1324
1763
  init_png_encoder();
1325
1764
  SERVER_LOCAL_NAME = "__MCP_ServerEvalLocal";
1326
1765
  CLIENT_LOCAL_NAME = "__MCP_ClientEvalBridge";
1766
+ EVAL_WRAPPER_LINE_OFFSET = 23;
1327
1767
  RobloxStudioTools = class _RobloxStudioTools {
1328
1768
  client;
1329
1769
  bridge;
@@ -1335,8 +1775,29 @@ var init_tools = __esm({
1335
1775
  this.openCloudClient = new OpenCloudClient();
1336
1776
  this.cookieClient = new RobloxCookieClient();
1337
1777
  }
1338
- async getFileTree(path2 = "") {
1339
- const response = await this.client.request("/api/file-tree", { path: path2 });
1778
+ // Resolve (instance_id, target-role) → concrete (instanceId, role) and
1779
+ // dispatch a single request. Throws RoutingFailure if the resolution is
1780
+ // ambiguous, missing, or asks for fanout on a non-fanout-capable tool —
1781
+ // the MCP transport layer surfaces it as a structured error result so
1782
+ // the LLM can recover via the embedded data.instances list.
1783
+ async _callSingle(endpoint, data, target, instance_id) {
1784
+ const r = this.bridge.resolveTarget({ instance_id, target });
1785
+ if (!r.ok)
1786
+ throw new RoutingFailure(r.error);
1787
+ if (r.mode !== "single") {
1788
+ throw new RoutingFailure({
1789
+ code: "target_role_not_present_on_instance",
1790
+ message: "This tool does not support target=all. Pick a specific role or omit target.",
1791
+ data: {
1792
+ instances: this.bridge.getPublicInstances(),
1793
+ count: this.bridge.getInstances().length
1794
+ }
1795
+ });
1796
+ }
1797
+ return this.client.request(endpoint, data, r.targetInstanceId, r.targetRole);
1798
+ }
1799
+ async getFileTree(path2 = "", instance_id) {
1800
+ const response = await this._callSingle("/api/file-tree", { path: path2 }, void 0, instance_id);
1340
1801
  return {
1341
1802
  content: [
1342
1803
  {
@@ -1346,8 +1807,8 @@ var init_tools = __esm({
1346
1807
  ]
1347
1808
  };
1348
1809
  }
1349
- async searchFiles(query, searchType = "name") {
1350
- const response = await this.client.request("/api/search-files", { query, searchType });
1810
+ async searchFiles(query, searchType = "name", instance_id) {
1811
+ const response = await this._callSingle("/api/search-files", { query, searchType }, void 0, instance_id);
1351
1812
  return {
1352
1813
  content: [
1353
1814
  {
@@ -1357,8 +1818,8 @@ var init_tools = __esm({
1357
1818
  ]
1358
1819
  };
1359
1820
  }
1360
- async getPlaceInfo() {
1361
- const response = await this.client.request("/api/place-info", {});
1821
+ async getPlaceInfo(instance_id) {
1822
+ const response = await this._callSingle("/api/place-info", {}, void 0, instance_id);
1362
1823
  return {
1363
1824
  content: [
1364
1825
  {
@@ -1368,8 +1829,8 @@ var init_tools = __esm({
1368
1829
  ]
1369
1830
  };
1370
1831
  }
1371
- async getServices(serviceName) {
1372
- const response = await this.client.request("/api/services", { serviceName });
1832
+ async getServices(serviceName, instance_id) {
1833
+ const response = await this._callSingle("/api/services", { serviceName }, void 0, instance_id);
1373
1834
  return {
1374
1835
  content: [
1375
1836
  {
@@ -1379,12 +1840,12 @@ var init_tools = __esm({
1379
1840
  ]
1380
1841
  };
1381
1842
  }
1382
- async searchObjects(query, searchType = "name", propertyName) {
1383
- const response = await this.client.request("/api/search-objects", {
1843
+ async searchObjects(query, searchType = "name", propertyName, instance_id) {
1844
+ const response = await this._callSingle("/api/search-objects", {
1384
1845
  query,
1385
1846
  searchType,
1386
1847
  propertyName
1387
- });
1848
+ }, void 0, instance_id);
1388
1849
  return {
1389
1850
  content: [
1390
1851
  {
@@ -1394,11 +1855,11 @@ var init_tools = __esm({
1394
1855
  ]
1395
1856
  };
1396
1857
  }
1397
- async getInstanceProperties(instancePath, excludeSource) {
1858
+ async getInstanceProperties(instancePath, excludeSource, instance_id) {
1398
1859
  if (!instancePath) {
1399
1860
  throw new Error("Instance path is required for get_instance_properties");
1400
1861
  }
1401
- const response = await this.client.request("/api/instance-properties", { instancePath, excludeSource });
1862
+ const response = await this._callSingle("/api/instance-properties", { instancePath, excludeSource }, void 0, instance_id);
1402
1863
  return {
1403
1864
  content: [
1404
1865
  {
@@ -1408,11 +1869,11 @@ var init_tools = __esm({
1408
1869
  ]
1409
1870
  };
1410
1871
  }
1411
- async getInstanceChildren(instancePath) {
1872
+ async getInstanceChildren(instancePath, instance_id) {
1412
1873
  if (!instancePath) {
1413
1874
  throw new Error("Instance path is required for get_instance_children");
1414
1875
  }
1415
- const response = await this.client.request("/api/instance-children", { instancePath });
1876
+ const response = await this._callSingle("/api/instance-children", { instancePath }, void 0, instance_id);
1416
1877
  return {
1417
1878
  content: [
1418
1879
  {
@@ -1422,14 +1883,14 @@ var init_tools = __esm({
1422
1883
  ]
1423
1884
  };
1424
1885
  }
1425
- async searchByProperty(propertyName, propertyValue) {
1886
+ async searchByProperty(propertyName, propertyValue, instance_id) {
1426
1887
  if (!propertyName || !propertyValue) {
1427
1888
  throw new Error("Property name and value are required for search_by_property");
1428
1889
  }
1429
- const response = await this.client.request("/api/search-by-property", {
1890
+ const response = await this._callSingle("/api/search-by-property", {
1430
1891
  propertyName,
1431
1892
  propertyValue
1432
- });
1893
+ }, void 0, instance_id);
1433
1894
  return {
1434
1895
  content: [
1435
1896
  {
@@ -1439,11 +1900,11 @@ var init_tools = __esm({
1439
1900
  ]
1440
1901
  };
1441
1902
  }
1442
- async getClassInfo(className) {
1903
+ async getClassInfo(className, instance_id) {
1443
1904
  if (!className) {
1444
1905
  throw new Error("Class name is required for get_class_info");
1445
1906
  }
1446
- const response = await this.client.request("/api/class-info", { className });
1907
+ const response = await this._callSingle("/api/class-info", { className }, void 0, instance_id);
1447
1908
  return {
1448
1909
  content: [
1449
1910
  {
@@ -1453,12 +1914,12 @@ var init_tools = __esm({
1453
1914
  ]
1454
1915
  };
1455
1916
  }
1456
- async getProjectStructure(path2, maxDepth, scriptsOnly) {
1457
- const response = await this.client.request("/api/project-structure", {
1917
+ async getProjectStructure(path2, maxDepth, scriptsOnly, instance_id) {
1918
+ const response = await this._callSingle("/api/project-structure", {
1458
1919
  path: path2,
1459
1920
  maxDepth,
1460
1921
  scriptsOnly
1461
- });
1922
+ }, void 0, instance_id);
1462
1923
  return {
1463
1924
  content: [
1464
1925
  {
@@ -1468,15 +1929,15 @@ var init_tools = __esm({
1468
1929
  ]
1469
1930
  };
1470
1931
  }
1471
- async setProperty(instancePath, propertyName, propertyValue) {
1932
+ async setProperty(instancePath, propertyName, propertyValue, instance_id) {
1472
1933
  if (!instancePath || !propertyName) {
1473
1934
  throw new Error("Instance path and property name are required for set_property");
1474
1935
  }
1475
- const response = await this.client.request("/api/set-property", {
1936
+ const response = await this._callSingle("/api/set-property", {
1476
1937
  instancePath,
1477
1938
  propertyName,
1478
1939
  propertyValue
1479
- });
1940
+ }, void 0, instance_id);
1480
1941
  return {
1481
1942
  content: [
1482
1943
  {
@@ -1486,22 +1947,22 @@ var init_tools = __esm({
1486
1947
  ]
1487
1948
  };
1488
1949
  }
1489
- async setProperties(instancePath, properties) {
1950
+ async setProperties(instancePath, properties, instance_id) {
1490
1951
  if (!instancePath || !properties) {
1491
1952
  throw new Error("instancePath and properties are required for set_properties");
1492
1953
  }
1493
- const response = await this.client.request("/api/set-properties", { instancePath, properties });
1954
+ const response = await this._callSingle("/api/set-properties", { instancePath, properties }, void 0, instance_id);
1494
1955
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
1495
1956
  }
1496
- async massSetProperty(paths, propertyName, propertyValue) {
1957
+ async massSetProperty(paths, propertyName, propertyValue, instance_id) {
1497
1958
  if (!paths || paths.length === 0 || !propertyName) {
1498
1959
  throw new Error("Paths array and property name are required for mass_set_property");
1499
1960
  }
1500
- const response = await this.client.request("/api/mass-set-property", {
1961
+ const response = await this._callSingle("/api/mass-set-property", {
1501
1962
  paths,
1502
1963
  propertyName,
1503
1964
  propertyValue
1504
- });
1965
+ }, void 0, instance_id);
1505
1966
  return {
1506
1967
  content: [
1507
1968
  {
@@ -1511,14 +1972,14 @@ var init_tools = __esm({
1511
1972
  ]
1512
1973
  };
1513
1974
  }
1514
- async massGetProperty(paths, propertyName) {
1975
+ async massGetProperty(paths, propertyName, instance_id) {
1515
1976
  if (!paths || paths.length === 0 || !propertyName) {
1516
1977
  throw new Error("Paths array and property name are required for mass_get_property");
1517
1978
  }
1518
- const response = await this.client.request("/api/mass-get-property", {
1979
+ const response = await this._callSingle("/api/mass-get-property", {
1519
1980
  paths,
1520
1981
  propertyName
1521
- });
1982
+ }, void 0, instance_id);
1522
1983
  return {
1523
1984
  content: [
1524
1985
  {
@@ -1528,16 +1989,16 @@ var init_tools = __esm({
1528
1989
  ]
1529
1990
  };
1530
1991
  }
1531
- async createObject(className, parent, name, properties) {
1992
+ async createObject(className, parent, name, properties, instance_id) {
1532
1993
  if (!className || !parent) {
1533
1994
  throw new Error("Class name and parent are required for create_object");
1534
1995
  }
1535
- const response = await this.client.request("/api/create-object", {
1996
+ const response = await this._callSingle("/api/create-object", {
1536
1997
  className,
1537
1998
  parent,
1538
1999
  name,
1539
2000
  properties
1540
- });
2001
+ }, void 0, instance_id);
1541
2002
  return {
1542
2003
  content: [
1543
2004
  {
@@ -1547,11 +2008,11 @@ var init_tools = __esm({
1547
2008
  ]
1548
2009
  };
1549
2010
  }
1550
- async massCreateObjects(objects) {
2011
+ async massCreateObjects(objects, instance_id) {
1551
2012
  if (!objects || objects.length === 0) {
1552
2013
  throw new Error("Objects array is required for mass_create_objects");
1553
2014
  }
1554
- const response = await this.client.request("/api/mass-create-objects", { objects });
2015
+ const response = await this._callSingle("/api/mass-create-objects", { objects }, void 0, instance_id);
1555
2016
  return {
1556
2017
  content: [
1557
2018
  {
@@ -1561,11 +2022,11 @@ var init_tools = __esm({
1561
2022
  ]
1562
2023
  };
1563
2024
  }
1564
- async deleteObject(instancePath) {
2025
+ async deleteObject(instancePath, instance_id) {
1565
2026
  if (!instancePath) {
1566
2027
  throw new Error("Instance path is required for delete_object");
1567
2028
  }
1568
- const response = await this.client.request("/api/delete-object", { instancePath });
2029
+ const response = await this._callSingle("/api/delete-object", { instancePath }, void 0, instance_id);
1569
2030
  return {
1570
2031
  content: [
1571
2032
  {
@@ -1575,15 +2036,15 @@ var init_tools = __esm({
1575
2036
  ]
1576
2037
  };
1577
2038
  }
1578
- async smartDuplicate(instancePath, count, options) {
2039
+ async smartDuplicate(instancePath, count, options, instance_id) {
1579
2040
  if (!instancePath || count < 1) {
1580
2041
  throw new Error("Instance path and count > 0 are required for smart_duplicate");
1581
2042
  }
1582
- const response = await this.client.request("/api/smart-duplicate", {
2043
+ const response = await this._callSingle("/api/smart-duplicate", {
1583
2044
  instancePath,
1584
2045
  count,
1585
2046
  options
1586
- });
2047
+ }, void 0, instance_id);
1587
2048
  return {
1588
2049
  content: [
1589
2050
  {
@@ -1593,11 +2054,11 @@ var init_tools = __esm({
1593
2054
  ]
1594
2055
  };
1595
2056
  }
1596
- async massDuplicate(duplications) {
2057
+ async massDuplicate(duplications, instance_id) {
1597
2058
  if (!duplications || duplications.length === 0) {
1598
2059
  throw new Error("Duplications array is required for mass_duplicate");
1599
2060
  }
1600
- const response = await this.client.request("/api/mass-duplicate", { duplications });
2061
+ const response = await this._callSingle("/api/mass-duplicate", { duplications }, void 0, instance_id);
1601
2062
  return {
1602
2063
  content: [
1603
2064
  {
@@ -1607,11 +2068,11 @@ var init_tools = __esm({
1607
2068
  ]
1608
2069
  };
1609
2070
  }
1610
- async getScriptSource(instancePath, startLine, endLine) {
2071
+ async getScriptSource(instancePath, startLine, endLine, instance_id) {
1611
2072
  if (!instancePath) {
1612
2073
  throw new Error("Instance path is required for get_script_source");
1613
2074
  }
1614
- const response = await this.client.request("/api/get-script-source", { instancePath, startLine, endLine });
2075
+ const response = await this._callSingle("/api/get-script-source", { instancePath, startLine, endLine }, void 0, instance_id);
1615
2076
  if (response.error) {
1616
2077
  return { content: [{ type: "text", text: `Error: ${response.error}` }] };
1617
2078
  }
@@ -1658,11 +2119,11 @@ ${code}`
1658
2119
  }]
1659
2120
  };
1660
2121
  }
1661
- async setScriptSource(instancePath, source) {
2122
+ async setScriptSource(instancePath, source, instance_id) {
1662
2123
  if (!instancePath || typeof source !== "string") {
1663
2124
  throw new Error("Instance path and source code string are required for set_script_source");
1664
2125
  }
1665
- const response = await this.client.request("/api/set-script-source", { instancePath, source });
2126
+ const response = await this._callSingle("/api/set-script-source", { instancePath, source }, void 0, instance_id);
1666
2127
  return {
1667
2128
  content: [
1668
2129
  {
@@ -1672,14 +2133,14 @@ ${code}`
1672
2133
  ]
1673
2134
  };
1674
2135
  }
1675
- async editScriptLines(instancePath, oldString, newString, startLine) {
2136
+ async editScriptLines(instancePath, oldString, newString, startLine, instance_id) {
1676
2137
  if (!instancePath || typeof oldString !== "string" || typeof newString !== "string") {
1677
2138
  throw new Error("Instance path, old_string, and new_string are required for edit_script_lines");
1678
2139
  }
1679
2140
  const payload = { instancePath, old_string: oldString, new_string: newString };
1680
2141
  if (startLine !== void 0)
1681
2142
  payload.startLine = startLine;
1682
- const response = await this.client.request("/api/edit-script-lines", payload);
2143
+ const response = await this._callSingle("/api/edit-script-lines", payload, void 0, instance_id);
1683
2144
  return {
1684
2145
  content: [
1685
2146
  {
@@ -1689,11 +2150,11 @@ ${code}`
1689
2150
  ]
1690
2151
  };
1691
2152
  }
1692
- async insertScriptLines(instancePath, afterLine, newContent) {
2153
+ async insertScriptLines(instancePath, afterLine, newContent, instance_id) {
1693
2154
  if (!instancePath || typeof newContent !== "string") {
1694
2155
  throw new Error("Instance path and newContent are required for insert_script_lines");
1695
2156
  }
1696
- const response = await this.client.request("/api/insert-script-lines", { instancePath, afterLine: afterLine || 0, newContent });
2157
+ const response = await this._callSingle("/api/insert-script-lines", { instancePath, afterLine: afterLine || 0, newContent }, void 0, instance_id);
1697
2158
  return {
1698
2159
  content: [
1699
2160
  {
@@ -1703,11 +2164,11 @@ ${code}`
1703
2164
  ]
1704
2165
  };
1705
2166
  }
1706
- async deleteScriptLines(instancePath, startLine, endLine) {
2167
+ async deleteScriptLines(instancePath, startLine, endLine, instance_id) {
1707
2168
  if (!instancePath || !startLine || !endLine) {
1708
2169
  throw new Error("Instance path, startLine, and endLine are required for delete_script_lines");
1709
2170
  }
1710
- const response = await this.client.request("/api/delete-script-lines", { instancePath, startLine, endLine });
2171
+ const response = await this._callSingle("/api/delete-script-lines", { instancePath, startLine, endLine }, void 0, instance_id);
1711
2172
  return {
1712
2173
  content: [
1713
2174
  {
@@ -1717,14 +2178,14 @@ ${code}`
1717
2178
  ]
1718
2179
  };
1719
2180
  }
1720
- async grepScripts(pattern, options) {
2181
+ async grepScripts(pattern, options, instance_id) {
1721
2182
  if (!pattern) {
1722
2183
  throw new Error("Pattern is required for grep_scripts");
1723
2184
  }
1724
- const response = await this.client.request("/api/grep-scripts", {
2185
+ const response = await this._callSingle("/api/grep-scripts", {
1725
2186
  pattern,
1726
2187
  ...options
1727
- });
2188
+ }, void 0, instance_id);
1728
2189
  return {
1729
2190
  content: [
1730
2191
  {
@@ -1734,11 +2195,11 @@ ${code}`
1734
2195
  ]
1735
2196
  };
1736
2197
  }
1737
- async setAttribute(instancePath, attributeName, attributeValue, valueType) {
2198
+ async setAttribute(instancePath, attributeName, attributeValue, valueType, instance_id) {
1738
2199
  if (!instancePath || !attributeName) {
1739
2200
  throw new Error("Instance path and attribute name are required for set_attribute");
1740
2201
  }
1741
- const response = await this.client.request("/api/set-attribute", { instancePath, attributeName, attributeValue, valueType });
2202
+ const response = await this._callSingle("/api/set-attribute", { instancePath, attributeName, attributeValue, valueType }, void 0, instance_id);
1742
2203
  return {
1743
2204
  content: [
1744
2205
  {
@@ -1748,11 +2209,11 @@ ${code}`
1748
2209
  ]
1749
2210
  };
1750
2211
  }
1751
- async getAttributes(instancePath) {
2212
+ async getAttributes(instancePath, instance_id) {
1752
2213
  if (!instancePath) {
1753
2214
  throw new Error("Instance path is required for get_attributes");
1754
2215
  }
1755
- const response = await this.client.request("/api/get-attributes", { instancePath });
2216
+ const response = await this._callSingle("/api/get-attributes", { instancePath }, void 0, instance_id);
1756
2217
  return {
1757
2218
  content: [
1758
2219
  {
@@ -1762,11 +2223,11 @@ ${code}`
1762
2223
  ]
1763
2224
  };
1764
2225
  }
1765
- async deleteAttribute(instancePath, attributeName) {
2226
+ async deleteAttribute(instancePath, attributeName, instance_id) {
1766
2227
  if (!instancePath || !attributeName) {
1767
2228
  throw new Error("Instance path and attribute name are required for delete_attribute");
1768
2229
  }
1769
- const response = await this.client.request("/api/delete-attribute", { instancePath, attributeName });
2230
+ const response = await this._callSingle("/api/delete-attribute", { instancePath, attributeName }, void 0, instance_id);
1770
2231
  return {
1771
2232
  content: [
1772
2233
  {
@@ -1776,11 +2237,11 @@ ${code}`
1776
2237
  ]
1777
2238
  };
1778
2239
  }
1779
- async getTags(instancePath) {
2240
+ async getTags(instancePath, instance_id) {
1780
2241
  if (!instancePath) {
1781
2242
  throw new Error("Instance path is required for get_tags");
1782
2243
  }
1783
- const response = await this.client.request("/api/get-tags", { instancePath });
2244
+ const response = await this._callSingle("/api/get-tags", { instancePath }, void 0, instance_id);
1784
2245
  return {
1785
2246
  content: [
1786
2247
  {
@@ -1790,11 +2251,11 @@ ${code}`
1790
2251
  ]
1791
2252
  };
1792
2253
  }
1793
- async addTag(instancePath, tagName) {
2254
+ async addTag(instancePath, tagName, instance_id) {
1794
2255
  if (!instancePath || !tagName) {
1795
2256
  throw new Error("Instance path and tag name are required for add_tag");
1796
2257
  }
1797
- const response = await this.client.request("/api/add-tag", { instancePath, tagName });
2258
+ const response = await this._callSingle("/api/add-tag", { instancePath, tagName }, void 0, instance_id);
1798
2259
  return {
1799
2260
  content: [
1800
2261
  {
@@ -1804,11 +2265,11 @@ ${code}`
1804
2265
  ]
1805
2266
  };
1806
2267
  }
1807
- async removeTag(instancePath, tagName) {
2268
+ async removeTag(instancePath, tagName, instance_id) {
1808
2269
  if (!instancePath || !tagName) {
1809
2270
  throw new Error("Instance path and tag name are required for remove_tag");
1810
2271
  }
1811
- const response = await this.client.request("/api/remove-tag", { instancePath, tagName });
2272
+ const response = await this._callSingle("/api/remove-tag", { instancePath, tagName }, void 0, instance_id);
1812
2273
  return {
1813
2274
  content: [
1814
2275
  {
@@ -1818,11 +2279,11 @@ ${code}`
1818
2279
  ]
1819
2280
  };
1820
2281
  }
1821
- async getTagged(tagName) {
2282
+ async getTagged(tagName, instance_id) {
1822
2283
  if (!tagName) {
1823
2284
  throw new Error("Tag name is required for get_tagged");
1824
2285
  }
1825
- const response = await this.client.request("/api/get-tagged", { tagName });
2286
+ const response = await this._callSingle("/api/get-tagged", { tagName }, void 0, instance_id);
1826
2287
  return {
1827
2288
  content: [
1828
2289
  {
@@ -1832,8 +2293,8 @@ ${code}`
1832
2293
  ]
1833
2294
  };
1834
2295
  }
1835
- async getSelection() {
1836
- const response = await this.client.request("/api/get-selection", {});
2296
+ async getSelection(instance_id) {
2297
+ const response = await this._callSingle("/api/get-selection", {}, void 0, instance_id);
1837
2298
  return {
1838
2299
  content: [
1839
2300
  {
@@ -1843,11 +2304,11 @@ ${code}`
1843
2304
  ]
1844
2305
  };
1845
2306
  }
1846
- async executeLuau(code, target) {
2307
+ async executeLuau(code, target, instance_id) {
1847
2308
  if (!code) {
1848
2309
  throw new Error("Code is required for execute_luau");
1849
2310
  }
1850
- const response = await this.client.request("/api/execute-luau", { code }, target || "edit");
2311
+ const response = await this._callSingle("/api/execute-luau", { code }, target || "edit", instance_id);
1851
2312
  return {
1852
2313
  content: [
1853
2314
  {
@@ -1857,7 +2318,7 @@ ${code}`
1857
2318
  ]
1858
2319
  };
1859
2320
  }
1860
- async evalServerRuntime(code) {
2321
+ async evalServerRuntime(code, instance_id) {
1861
2322
  if (!code) {
1862
2323
  throw new Error("Code is required for eval_server_runtime");
1863
2324
  }
@@ -1867,7 +2328,7 @@ ${code}`
1867
2328
  missingError: "ServerEvalBridge not installed. Bridges are auto-installed at start_playtest and removed at stop_playtest. Start a playtest before calling eval_server_runtime.",
1868
2329
  userCode: code
1869
2330
  });
1870
- const response = await this.client.request("/api/execute-luau", { code: wrapper }, "server");
2331
+ const response = await this._callSingle("/api/execute-luau", { code: wrapper }, "server", instance_id);
1871
2332
  return {
1872
2333
  content: [
1873
2334
  {
@@ -1877,7 +2338,7 @@ ${code}`
1877
2338
  ]
1878
2339
  };
1879
2340
  }
1880
- async evalClientRuntime(code, target) {
2341
+ async evalClientRuntime(code, target, instance_id) {
1881
2342
  if (!code) {
1882
2343
  throw new Error("Code is required for eval_client_runtime");
1883
2344
  }
@@ -1891,7 +2352,7 @@ ${code}`
1891
2352
  missingError: "ClientEvalBridge not installed. Bridges are auto-installed at start_playtest and removed at stop_playtest. Start a playtest before calling eval_client_runtime.",
1892
2353
  userCode: code
1893
2354
  });
1894
- const response = await this.client.request("/api/execute-luau", { code: wrapper }, clientTarget);
2355
+ const response = await this._callSingle("/api/execute-luau", { code: wrapper }, clientTarget, instance_id);
1895
2356
  return {
1896
2357
  content: [
1897
2358
  {
@@ -1901,7 +2362,7 @@ ${code}`
1901
2362
  ]
1902
2363
  };
1903
2364
  }
1904
- async getRuntimeLogs(target, since, tail, filter) {
2365
+ async getRuntimeLogs(target, since, tail, filter, instance_id) {
1905
2366
  const tgt = target ?? "all";
1906
2367
  const data = {};
1907
2368
  if (since !== void 0)
@@ -1910,16 +2371,26 @@ ${code}`
1910
2371
  data.tail = tail;
1911
2372
  if (filter !== void 0)
1912
2373
  data.filter = filter;
1913
- if (tgt !== "all") {
1914
- const response = await this.client.request("/api/get-runtime-logs", data, tgt);
2374
+ const resolved = this.bridge.resolveTarget({ instance_id, target: tgt });
2375
+ if (!resolved.ok)
2376
+ throw new RoutingFailure(resolved.error);
2377
+ if (resolved.mode === "single") {
2378
+ const response = await this.client.request("/api/get-runtime-logs", data, resolved.targetInstanceId, resolved.targetRole);
2379
+ response.peer = resolved.targetRole;
2380
+ if (Array.isArray(response.entries)) {
2381
+ for (const e of response.entries) {
2382
+ if (e.peer !== void 0)
2383
+ e.peer = resolved.targetRole;
2384
+ }
2385
+ }
1915
2386
  return {
1916
2387
  content: [{ type: "text", text: JSON.stringify(response) }]
1917
2388
  };
1918
2389
  }
1919
- const targets = this.bridge.getInstances().filter((i) => i.role !== "edit-proxy").map((i) => i.role);
2390
+ const targets = resolved.targets.filter((t) => t.targetRole !== "edit-proxy");
1920
2391
  const responses = await Promise.allSettled(targets.map(async (t) => {
1921
- const r = await this.client.request("/api/get-runtime-logs", data, t);
1922
- return { ...r, peer: t };
2392
+ const r = await this.client.request("/api/get-runtime-logs", data, t.targetInstanceId, t.targetRole);
2393
+ return { ...r, peer: t.targetRole };
1923
2394
  }));
1924
2395
  const merged = [];
1925
2396
  const perPeerNextSince = {};
@@ -1965,7 +2436,7 @@ ${code}`
1965
2436
  content: [{ type: "text", text: JSON.stringify(body) }]
1966
2437
  };
1967
2438
  }
1968
- async startPlaytest(mode, numPlayers) {
2439
+ async startPlaytest(mode, numPlayers, instance_id) {
1969
2440
  if (mode !== "play" && mode !== "run") {
1970
2441
  throw new Error('mode must be "play" or "run"');
1971
2442
  }
@@ -1973,7 +2444,7 @@ ${code}`
1973
2444
  if (numPlayers !== void 0) {
1974
2445
  data.numPlayers = numPlayers;
1975
2446
  }
1976
- const response = await this.client.request("/api/start-playtest", data);
2447
+ const response = await this._callSingle("/api/start-playtest", data, void 0, instance_id);
1977
2448
  return {
1978
2449
  content: [
1979
2450
  {
@@ -1983,14 +2454,14 @@ ${code}`
1983
2454
  ]
1984
2455
  };
1985
2456
  }
1986
- async stopPlaytest() {
1987
- const response = await this.client.request("/api/stop-playtest", {}, "edit");
2457
+ async stopPlaytest(instance_id) {
2458
+ const response = await this._callSingle("/api/stop-playtest", {}, "edit", instance_id);
1988
2459
  return {
1989
2460
  content: [{ type: "text", text: JSON.stringify(response) }]
1990
2461
  };
1991
2462
  }
1992
- async getPlaytestOutput(target) {
1993
- const response = await this.client.request("/api/get-playtest-output", {}, target || "edit");
2463
+ async getPlaytestOutput(target, instance_id) {
2464
+ const response = await this._callSingle("/api/get-playtest-output", {}, target || "edit", instance_id);
1994
2465
  return {
1995
2466
  content: [
1996
2467
  {
@@ -2001,7 +2472,7 @@ ${code}`
2001
2472
  };
2002
2473
  }
2003
2474
  async getConnectedInstances() {
2004
- const instances = this.bridge.getInstances();
2475
+ const instances = this.bridge.getPublicInstances();
2005
2476
  return {
2006
2477
  content: [
2007
2478
  {
@@ -2011,8 +2482,8 @@ ${code}`
2011
2482
  ]
2012
2483
  };
2013
2484
  }
2014
- async undo() {
2015
- const response = await this.client.request("/api/undo", {});
2485
+ async undo(instance_id) {
2486
+ const response = await this._callSingle("/api/undo", {}, void 0, instance_id);
2016
2487
  return {
2017
2488
  content: [
2018
2489
  {
@@ -2022,8 +2493,8 @@ ${code}`
2022
2493
  ]
2023
2494
  };
2024
2495
  }
2025
- async redo() {
2026
- const response = await this.client.request("/api/redo", {});
2496
+ async redo(instance_id) {
2497
+ const response = await this._callSingle("/api/redo", {}, void 0, instance_id);
2027
2498
  return {
2028
2499
  content: [
2029
2500
  {
@@ -2109,15 +2580,15 @@ ${code}`
2109
2580
  _RobloxStudioTools._cachedLibraryPath = result;
2110
2581
  return result;
2111
2582
  }
2112
- async exportBuild(instancePath, outputId, style = "misc") {
2583
+ async exportBuild(instancePath, outputId, style = "misc", instance_id) {
2113
2584
  if (!instancePath) {
2114
2585
  throw new Error("Instance path is required for export_build");
2115
2586
  }
2116
- const response = await this.client.request("/api/export-build", {
2587
+ const response = await this._callSingle("/api/export-build", {
2117
2588
  instancePath,
2118
2589
  outputId,
2119
2590
  style
2120
- });
2591
+ }, void 0, instance_id);
2121
2592
  if (response && response.success && response.buildData) {
2122
2593
  const buildData = response.buildData;
2123
2594
  const buildId = buildData.id || `${style}/exported`;
@@ -2305,7 +2776,7 @@ ${code}`
2305
2776
  ]
2306
2777
  };
2307
2778
  }
2308
- async importBuild(buildData, targetPath, position) {
2779
+ async importBuild(buildData, targetPath, position, instance_id) {
2309
2780
  if (!buildData || !targetPath) {
2310
2781
  throw new Error("buildData (or library ID string) and targetPath are required for import_build");
2311
2782
  }
@@ -2325,11 +2796,11 @@ ${code}`
2325
2796
  } else {
2326
2797
  resolved = buildData;
2327
2798
  }
2328
- const response = await this.client.request("/api/import-build", {
2799
+ const response = await this._callSingle("/api/import-build", {
2329
2800
  buildData: resolved,
2330
2801
  targetPath,
2331
2802
  position
2332
- });
2803
+ }, void 0, instance_id);
2333
2804
  return {
2334
2805
  content: [
2335
2806
  {
@@ -2371,11 +2842,11 @@ ${code}`
2371
2842
  ]
2372
2843
  };
2373
2844
  }
2374
- async searchMaterials(query, maxResults) {
2375
- const response = await this.client.request("/api/search-materials", {
2845
+ async searchMaterials(query, maxResults, instance_id) {
2846
+ const response = await this._callSingle("/api/search-materials", {
2376
2847
  query: query ?? "",
2377
2848
  maxResults: maxResults ?? 50
2378
- });
2849
+ }, void 0, instance_id);
2379
2850
  return {
2380
2851
  content: [
2381
2852
  {
@@ -2415,7 +2886,7 @@ ${code}`
2415
2886
  ]
2416
2887
  };
2417
2888
  }
2418
- async importScene(sceneData, targetPath = "game.Workspace") {
2889
+ async importScene(sceneData, targetPath = "game.Workspace", instance_id) {
2419
2890
  if (!sceneData) {
2420
2891
  throw new Error("sceneData is required for import_scene");
2421
2892
  }
@@ -2505,10 +2976,10 @@ ${code}`
2505
2976
  if (expandedBuilds.length === 0) {
2506
2977
  throw new Error("No builds to import - check model references and library");
2507
2978
  }
2508
- const response = await this.client.request("/api/import-scene", {
2979
+ const response = await this._callSingle("/api/import-scene", {
2509
2980
  expandedBuilds,
2510
2981
  targetPath
2511
- });
2982
+ }, void 0, instance_id);
2512
2983
  return {
2513
2984
  content: [
2514
2985
  {
@@ -2599,15 +3070,15 @@ ${code}`
2599
3070
  }]
2600
3071
  };
2601
3072
  }
2602
- async insertAsset(assetId, parentPath, position) {
3073
+ async insertAsset(assetId, parentPath, position, instance_id) {
2603
3074
  if (!assetId) {
2604
3075
  throw new Error("Asset ID is required for insert_asset");
2605
3076
  }
2606
- const response = await this.client.request("/api/insert-asset", {
3077
+ const response = await this._callSingle("/api/insert-asset", {
2607
3078
  assetId,
2608
3079
  parentPath: parentPath || "game.Workspace",
2609
3080
  position
2610
- });
3081
+ }, void 0, instance_id);
2611
3082
  return {
2612
3083
  content: [{
2613
3084
  type: "text",
@@ -2615,15 +3086,15 @@ ${code}`
2615
3086
  }]
2616
3087
  };
2617
3088
  }
2618
- async previewAsset(assetId, includeProperties, maxDepth) {
3089
+ async previewAsset(assetId, includeProperties, maxDepth, instance_id) {
2619
3090
  if (!assetId) {
2620
3091
  throw new Error("Asset ID is required for preview_asset");
2621
3092
  }
2622
- const response = await this.client.request("/api/preview-asset", {
3093
+ const response = await this._callSingle("/api/preview-asset", {
2623
3094
  assetId,
2624
3095
  includeProperties: includeProperties ?? true,
2625
3096
  maxDepth: maxDepth ?? 10
2626
- });
3097
+ }, void 0, instance_id);
2627
3098
  return {
2628
3099
  content: [{
2629
3100
  type: "text",
@@ -2645,7 +3116,7 @@ ${code}`
2645
3116
  return id
2646
3117
  `;
2647
3118
  try {
2648
- const response = await this.client.request("/api/execute-luau", { code }, "edit");
3119
+ const response = await this._callSingle("/api/execute-luau", { code }, "edit", void 0);
2649
3120
  const returnValue = response?.returnValue;
2650
3121
  if (returnValue !== void 0 && returnValue !== null && /^\d+$/.test(String(returnValue))) {
2651
3122
  return String(returnValue);
@@ -2720,17 +3191,17 @@ ${code}`
2720
3191
  }]
2721
3192
  };
2722
3193
  }
2723
- async simulateMouseInput(action, x, y, button, scrollDirection, target) {
3194
+ async simulateMouseInput(action, x, y, button, scrollDirection, target, instance_id) {
2724
3195
  if (!action) {
2725
3196
  throw new Error("action is required for simulate_mouse_input");
2726
3197
  }
2727
- const response = await this.client.request("/api/simulate-mouse-input", {
3198
+ const response = await this._callSingle("/api/simulate-mouse-input", {
2728
3199
  action,
2729
3200
  x,
2730
3201
  y,
2731
3202
  button,
2732
3203
  scrollDirection
2733
- }, target || "edit");
3204
+ }, target || "edit", instance_id);
2734
3205
  return {
2735
3206
  content: [{
2736
3207
  type: "text",
@@ -2738,15 +3209,15 @@ ${code}`
2738
3209
  }]
2739
3210
  };
2740
3211
  }
2741
- async simulateKeyboardInput(keyCode, action, duration, target) {
3212
+ async simulateKeyboardInput(keyCode, action, duration, target, instance_id) {
2742
3213
  if (!keyCode) {
2743
3214
  throw new Error("keyCode is required for simulate_keyboard_input");
2744
3215
  }
2745
- const response = await this.client.request("/api/simulate-keyboard-input", {
3216
+ const response = await this._callSingle("/api/simulate-keyboard-input", {
2746
3217
  keyCode,
2747
3218
  action,
2748
3219
  duration
2749
- }, target || "edit");
3220
+ }, target || "edit", instance_id);
2750
3221
  return {
2751
3222
  content: [{
2752
3223
  type: "text",
@@ -2754,16 +3225,16 @@ ${code}`
2754
3225
  }]
2755
3226
  };
2756
3227
  }
2757
- async characterNavigation(position, instancePath, waitForCompletion, timeout, target) {
3228
+ async characterNavigation(position, instancePath, waitForCompletion, timeout, target, instance_id) {
2758
3229
  if (!position && !instancePath) {
2759
3230
  throw new Error("Either position or instancePath is required for character_navigation");
2760
3231
  }
2761
- const response = await this.client.request("/api/character-navigation", {
3232
+ const response = await this._callSingle("/api/character-navigation", {
2762
3233
  position,
2763
3234
  instancePath,
2764
3235
  waitForCompletion,
2765
3236
  timeout
2766
- }, target || "edit");
3237
+ }, target || "edit", instance_id);
2767
3238
  return {
2768
3239
  content: [{
2769
3240
  type: "text",
@@ -2771,50 +3242,50 @@ ${code}`
2771
3242
  }]
2772
3243
  };
2773
3244
  }
2774
- async cloneObject(instancePath, targetParentPath) {
3245
+ async cloneObject(instancePath, targetParentPath, instance_id) {
2775
3246
  if (!instancePath || !targetParentPath) {
2776
3247
  throw new Error("instancePath and targetParentPath are required for clone_object");
2777
3248
  }
2778
- const response = await this.client.request("/api/clone-object", { instancePath, targetParentPath });
3249
+ const response = await this._callSingle("/api/clone-object", { instancePath, targetParentPath }, void 0, instance_id);
2779
3250
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
2780
3251
  }
2781
- async getDescendants(instancePath, maxDepth, classFilter) {
3252
+ async getDescendants(instancePath, maxDepth, classFilter, instance_id) {
2782
3253
  if (!instancePath) {
2783
3254
  throw new Error("instancePath is required for get_descendants");
2784
3255
  }
2785
- const response = await this.client.request("/api/get-descendants", { instancePath, maxDepth, classFilter });
3256
+ const response = await this._callSingle("/api/get-descendants", { instancePath, maxDepth, classFilter }, void 0, instance_id);
2786
3257
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
2787
3258
  }
2788
- async compareInstances(instancePathA, instancePathB) {
3259
+ async compareInstances(instancePathA, instancePathB, instance_id) {
2789
3260
  if (!instancePathA || !instancePathB) {
2790
3261
  throw new Error("instancePathA and instancePathB are required for compare_instances");
2791
3262
  }
2792
- const response = await this.client.request("/api/compare-instances", { instancePathA, instancePathB });
3263
+ const response = await this._callSingle("/api/compare-instances", { instancePathA, instancePathB }, void 0, instance_id);
2793
3264
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
2794
3265
  }
2795
- async getOutputLog(maxEntries, messageType) {
2796
- const response = await this.client.request("/api/get-output-log", { maxEntries, messageType });
3266
+ async getOutputLog(maxEntries, messageType, instance_id) {
3267
+ const response = await this._callSingle("/api/get-output-log", { maxEntries, messageType }, void 0, instance_id);
2797
3268
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
2798
3269
  }
2799
- async bulkSetAttributes(instancePath, attributes) {
3270
+ async bulkSetAttributes(instancePath, attributes, instance_id) {
2800
3271
  if (!instancePath || !attributes) {
2801
3272
  throw new Error("instancePath and attributes are required for bulk_set_attributes");
2802
3273
  }
2803
- const response = await this.client.request("/api/bulk-set-attributes", { instancePath, attributes });
3274
+ const response = await this._callSingle("/api/bulk-set-attributes", { instancePath, attributes }, void 0, instance_id);
2804
3275
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
2805
3276
  }
2806
- async findAndReplaceInScripts(pattern, replacement, options) {
3277
+ async findAndReplaceInScripts(pattern, replacement, options, instance_id) {
2807
3278
  if (!pattern) {
2808
3279
  throw new Error("pattern is required for find_and_replace_in_scripts");
2809
3280
  }
2810
3281
  if (replacement === void 0 || replacement === null) {
2811
3282
  throw new Error("replacement is required for find_and_replace_in_scripts");
2812
3283
  }
2813
- const response = await this.client.request("/api/find-and-replace-in-scripts", {
3284
+ const response = await this._callSingle("/api/find-and-replace-in-scripts", {
2814
3285
  pattern,
2815
3286
  replacement,
2816
3287
  ...options
2817
- });
3288
+ }, void 0, instance_id);
2818
3289
  return {
2819
3290
  content: [{
2820
3291
  type: "text",
@@ -2822,24 +3293,27 @@ ${code}`
2822
3293
  }]
2823
3294
  };
2824
3295
  }
2825
- async getMemoryBreakdown(target, tags) {
3296
+ async getMemoryBreakdown(target, tags, instance_id) {
2826
3297
  const tgt = target ?? "all";
2827
3298
  const data = {};
2828
3299
  if (tags !== void 0)
2829
3300
  data.tags = tags;
2830
- if (tgt !== "all") {
2831
- const response = await this.client.request("/api/get-memory-breakdown", data, tgt);
3301
+ const resolved = this.bridge.resolveTarget({ instance_id, target: tgt });
3302
+ if (!resolved.ok)
3303
+ throw new RoutingFailure(resolved.error);
3304
+ if (resolved.mode === "single") {
3305
+ const response = await this.client.request("/api/get-memory-breakdown", data, resolved.targetInstanceId, resolved.targetRole);
2832
3306
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
2833
3307
  }
2834
- const targets = this.bridge.getInstances().filter((i) => i.role !== "edit-proxy").map((i) => i.role);
3308
+ const targets = resolved.targets.filter((t) => t.targetRole !== "edit-proxy");
2835
3309
  const responses = await Promise.allSettled(targets.map(async (t) => ({
2836
- peer: t,
2837
- result: await this.client.request("/api/get-memory-breakdown", data, t)
3310
+ peer: t.targetRole,
3311
+ result: await this.client.request("/api/get-memory-breakdown", data, t.targetInstanceId, t.targetRole)
2838
3312
  })));
2839
3313
  const body = {};
2840
3314
  for (let i = 0; i < responses.length; i++) {
2841
3315
  const r = responses[i];
2842
- const peer = targets[i];
3316
+ const peer = targets[i].targetRole;
2843
3317
  if (r.status === "fulfilled") {
2844
3318
  body[peer] = r.value.result;
2845
3319
  } else {
@@ -2848,7 +3322,7 @@ ${code}`
2848
3322
  }
2849
3323
  return { content: [{ type: "text", text: JSON.stringify(body) }] };
2850
3324
  }
2851
- async exportRbxm(instancePaths, outputPath, target) {
3325
+ async exportRbxm(instancePaths, outputPath, target, instance_id) {
2852
3326
  if (!Array.isArray(instancePaths) || instancePaths.length === 0) {
2853
3327
  throw new Error("instance_paths must be a non-empty array for export_rbxm");
2854
3328
  }
@@ -2859,7 +3333,7 @@ ${code}`
2859
3333
  if (tgt !== "edit" && tgt !== "server") {
2860
3334
  throw new Error(`export_rbxm target must be "edit" or "server" (got: ${tgt})`);
2861
3335
  }
2862
- const response = await this.client.request("/api/export-rbxm", { instance_paths: instancePaths }, tgt);
3336
+ const response = await this._callSingle("/api/export-rbxm", { instance_paths: instancePaths }, tgt, instance_id);
2863
3337
  if (response.error) {
2864
3338
  return { content: [{ type: "text", text: JSON.stringify({ error: response.error }) }] };
2865
3339
  }
@@ -2885,7 +3359,7 @@ ${code}`
2885
3359
  }]
2886
3360
  };
2887
3361
  }
2888
- async importRbxm(source, parentPath, target) {
3362
+ async importRbxm(source, parentPath, target, instance_id) {
2889
3363
  if (!source || typeof source !== "object") {
2890
3364
  throw new Error("source is required for import_rbxm");
2891
3365
  }
@@ -2948,15 +3422,15 @@ ${code}`
2948
3422
  }
2949
3423
  sourceLabel = `base64(${bytes.length}B)`;
2950
3424
  }
2951
- const response = await this.client.request("/api/import-rbxm", {
3425
+ const response = await this._callSingle("/api/import-rbxm", {
2952
3426
  base64: bytes.toString("base64"),
2953
3427
  parent_path: parentPath,
2954
3428
  source_label: sourceLabel
2955
- }, tgt);
3429
+ }, tgt, instance_id);
2956
3430
  return { content: [{ type: "text", text: JSON.stringify(response) }] };
2957
3431
  }
2958
- async captureScreenshot() {
2959
- const response = await this.client.request("/api/capture-screenshot", {});
3432
+ async captureScreenshot(instance_id) {
3433
+ const response = await this._callSingle("/api/capture-screenshot", {}, void 0, instance_id);
2960
3434
  if (response.error) {
2961
3435
  return {
2962
3436
  content: [{
@@ -2978,150 +3452,6 @@ ${code}`
2978
3452
  }
2979
3453
  });
2980
3454
 
2981
- // ../core/dist/bridge-service.js
2982
- import { v4 as uuidv4 } from "uuid";
2983
- var STALE_INSTANCE_MS, BridgeService;
2984
- var init_bridge_service = __esm({
2985
- "../core/dist/bridge-service.js"() {
2986
- "use strict";
2987
- STALE_INSTANCE_MS = 3e4;
2988
- BridgeService = class {
2989
- pendingRequests = /* @__PURE__ */ new Map();
2990
- instances = /* @__PURE__ */ new Map();
2991
- requestTimeout = 3e4;
2992
- registerInstance(instanceId, role) {
2993
- let assignedRole = role;
2994
- if (role === "client") {
2995
- const used = /* @__PURE__ */ new Set();
2996
- for (const inst of this.instances.values()) {
2997
- const match = inst.role.match(/^client-(\d+)$/);
2998
- if (match)
2999
- used.add(Number(match[1]));
3000
- }
3001
- let idx = 1;
3002
- while (used.has(idx))
3003
- idx++;
3004
- assignedRole = `client-${idx}`;
3005
- }
3006
- this.instances.set(instanceId, {
3007
- instanceId,
3008
- role: assignedRole,
3009
- lastActivity: Date.now(),
3010
- connectedAt: Date.now()
3011
- });
3012
- return assignedRole;
3013
- }
3014
- unregisterInstance(instanceId) {
3015
- this.instances.delete(instanceId);
3016
- for (const [id, req] of this.pendingRequests.entries()) {
3017
- const targetRole = req.target;
3018
- const hasHandler = Array.from(this.instances.values()).some((i) => i.role === targetRole);
3019
- if (!hasHandler) {
3020
- clearTimeout(req.timeoutId);
3021
- this.pendingRequests.delete(id);
3022
- req.reject(new Error(`Target instance "${targetRole}" disconnected`));
3023
- }
3024
- }
3025
- }
3026
- getInstances() {
3027
- return Array.from(this.instances.values());
3028
- }
3029
- getPendingRequestCount() {
3030
- return this.pendingRequests.size;
3031
- }
3032
- updateInstanceActivity(instanceId) {
3033
- const inst = this.instances.get(instanceId);
3034
- if (inst) {
3035
- inst.lastActivity = Date.now();
3036
- }
3037
- }
3038
- cleanupStaleInstances() {
3039
- const now = Date.now();
3040
- for (const [id, inst] of this.instances.entries()) {
3041
- if (now - inst.lastActivity > STALE_INSTANCE_MS) {
3042
- this.unregisterInstance(id);
3043
- }
3044
- }
3045
- }
3046
- async sendRequest(endpoint, data, target = "edit") {
3047
- const requestId = uuidv4();
3048
- return new Promise((resolve2, reject) => {
3049
- const timeoutId = setTimeout(() => {
3050
- if (this.pendingRequests.has(requestId)) {
3051
- this.pendingRequests.delete(requestId);
3052
- reject(new Error("Request timeout"));
3053
- }
3054
- }, this.requestTimeout);
3055
- const request = {
3056
- id: requestId,
3057
- endpoint,
3058
- data,
3059
- target,
3060
- timestamp: Date.now(),
3061
- resolve: resolve2,
3062
- reject,
3063
- timeoutId
3064
- };
3065
- this.pendingRequests.set(requestId, request);
3066
- });
3067
- }
3068
- getPendingRequest(callerRole = "edit") {
3069
- let oldestRequest = null;
3070
- for (const request of this.pendingRequests.values()) {
3071
- if (request.target !== callerRole)
3072
- continue;
3073
- if (!oldestRequest || request.timestamp < oldestRequest.timestamp) {
3074
- oldestRequest = request;
3075
- }
3076
- }
3077
- if (oldestRequest) {
3078
- return {
3079
- requestId: oldestRequest.id,
3080
- request: {
3081
- endpoint: oldestRequest.endpoint,
3082
- data: oldestRequest.data
3083
- }
3084
- };
3085
- }
3086
- return null;
3087
- }
3088
- resolveRequest(requestId, response) {
3089
- const request = this.pendingRequests.get(requestId);
3090
- if (request) {
3091
- clearTimeout(request.timeoutId);
3092
- this.pendingRequests.delete(requestId);
3093
- request.resolve(response);
3094
- }
3095
- }
3096
- rejectRequest(requestId, error) {
3097
- const request = this.pendingRequests.get(requestId);
3098
- if (request) {
3099
- clearTimeout(request.timeoutId);
3100
- this.pendingRequests.delete(requestId);
3101
- request.reject(error);
3102
- }
3103
- }
3104
- cleanupOldRequests() {
3105
- const now = Date.now();
3106
- for (const [id, request] of this.pendingRequests.entries()) {
3107
- if (now - request.timestamp > this.requestTimeout) {
3108
- clearTimeout(request.timeoutId);
3109
- this.pendingRequests.delete(id);
3110
- request.reject(new Error("Request timeout"));
3111
- }
3112
- }
3113
- }
3114
- clearAllPendingRequests() {
3115
- for (const [, request] of this.pendingRequests.entries()) {
3116
- clearTimeout(request.timeoutId);
3117
- request.reject(new Error("Connection closed"));
3118
- }
3119
- this.pendingRequests.clear();
3120
- }
3121
- };
3122
- }
3123
- });
3124
-
3125
3455
  // ../core/dist/proxy-bridge-service.js
3126
3456
  import { v4 as uuidv42 } from "uuid";
3127
3457
  var ProxyBridgeService;
@@ -3166,14 +3496,20 @@ var init_proxy_bridge_service = __esm({
3166
3496
  this.refreshTimer = void 0;
3167
3497
  }
3168
3498
  }
3169
- async sendRequest(endpoint, data, target = "edit") {
3499
+ async sendRequest(endpoint, data, targetInstanceId, targetRole) {
3170
3500
  const controller = new AbortController();
3171
3501
  const timeoutId = setTimeout(() => controller.abort(), this.proxyRequestTimeout);
3172
3502
  try {
3173
3503
  const response = await fetch(`${this.primaryBaseUrl}/proxy`, {
3174
3504
  method: "POST",
3175
3505
  headers: { "Content-Type": "application/json" },
3176
- body: JSON.stringify({ endpoint, data, target, proxyInstanceId: this.proxyInstanceId }),
3506
+ body: JSON.stringify({
3507
+ endpoint,
3508
+ data,
3509
+ targetInstanceId,
3510
+ targetRole,
3511
+ proxyInstanceId: this.proxyInstanceId
3512
+ }),
3177
3513
  signal: controller.signal
3178
3514
  });
3179
3515
  clearTimeout(timeoutId);
@@ -3257,6 +3593,19 @@ var init_server = __esm({
3257
3593
  try {
3258
3594
  return await handler(this.tools, args ?? {});
3259
3595
  } catch (error) {
3596
+ if (error instanceof RoutingFailure) {
3597
+ return {
3598
+ content: [{
3599
+ type: "text",
3600
+ text: JSON.stringify({
3601
+ error: error.routingError.code,
3602
+ message: error.routingError.message,
3603
+ data: error.routingError.data
3604
+ })
3605
+ }],
3606
+ isError: true
3607
+ };
3608
+ }
3260
3609
  if (error instanceof McpError2)
3261
3610
  throw error;
3262
3611
  throw new McpError2(ErrorCode2.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
@@ -3398,6 +3747,10 @@ var init_definitions = __esm({
3398
3747
  path: {
3399
3748
  type: "string",
3400
3749
  description: "Root path (default: game root)"
3750
+ },
3751
+ instance_id: {
3752
+ type: "string",
3753
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3401
3754
  }
3402
3755
  }
3403
3756
  }
@@ -3417,6 +3770,10 @@ var init_definitions = __esm({
3417
3770
  type: "string",
3418
3771
  enum: ["name", "type", "content"],
3419
3772
  description: "Search mode (default: name)"
3773
+ },
3774
+ instance_id: {
3775
+ type: "string",
3776
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3420
3777
  }
3421
3778
  },
3422
3779
  required: ["query"]
@@ -3429,7 +3786,12 @@ var init_definitions = __esm({
3429
3786
  description: "Get place ID, name, and game settings",
3430
3787
  inputSchema: {
3431
3788
  type: "object",
3432
- properties: {}
3789
+ properties: {
3790
+ instance_id: {
3791
+ type: "string",
3792
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3793
+ }
3794
+ }
3433
3795
  }
3434
3796
  },
3435
3797
  {
@@ -3442,6 +3804,10 @@ var init_definitions = __esm({
3442
3804
  serviceName: {
3443
3805
  type: "string",
3444
3806
  description: "Specific service name"
3807
+ },
3808
+ instance_id: {
3809
+ type: "string",
3810
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3445
3811
  }
3446
3812
  }
3447
3813
  }
@@ -3465,6 +3831,10 @@ var init_definitions = __esm({
3465
3831
  propertyName: {
3466
3832
  type: "string",
3467
3833
  description: 'Property name when searchType is "property"'
3834
+ },
3835
+ instance_id: {
3836
+ type: "string",
3837
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3468
3838
  }
3469
3839
  },
3470
3840
  required: ["query"]
@@ -3485,6 +3855,10 @@ var init_definitions = __esm({
3485
3855
  excludeSource: {
3486
3856
  type: "boolean",
3487
3857
  description: "For scripts, return SourceLength/LineCount instead of full source (default: false)"
3858
+ },
3859
+ instance_id: {
3860
+ type: "string",
3861
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3488
3862
  }
3489
3863
  },
3490
3864
  required: ["instancePath"]
@@ -3500,6 +3874,10 @@ var init_definitions = __esm({
3500
3874
  instancePath: {
3501
3875
  type: "string",
3502
3876
  description: "Instance path (dot notation)"
3877
+ },
3878
+ instance_id: {
3879
+ type: "string",
3880
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3503
3881
  }
3504
3882
  },
3505
3883
  required: ["instancePath"]
@@ -3519,6 +3897,10 @@ var init_definitions = __esm({
3519
3897
  propertyValue: {
3520
3898
  type: "string",
3521
3899
  description: "Value to match"
3900
+ },
3901
+ instance_id: {
3902
+ type: "string",
3903
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3522
3904
  }
3523
3905
  },
3524
3906
  required: ["propertyName", "propertyValue"]
@@ -3534,6 +3916,10 @@ var init_definitions = __esm({
3534
3916
  className: {
3535
3917
  type: "string",
3536
3918
  description: "Roblox class name"
3919
+ },
3920
+ instance_id: {
3921
+ type: "string",
3922
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3537
3923
  }
3538
3924
  },
3539
3925
  required: ["className"]
@@ -3558,6 +3944,10 @@ var init_definitions = __esm({
3558
3944
  scriptsOnly: {
3559
3945
  type: "boolean",
3560
3946
  description: "Show only scripts (default: false)"
3947
+ },
3948
+ instance_id: {
3949
+ type: "string",
3950
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3561
3951
  }
3562
3952
  }
3563
3953
  }
@@ -3580,6 +3970,10 @@ var init_definitions = __esm({
3580
3970
  },
3581
3971
  propertyValue: {
3582
3972
  description: "Value to set (string, number, boolean, or object for Vector3/Color3/UDim2)"
3973
+ },
3974
+ instance_id: {
3975
+ type: "string",
3976
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3583
3977
  }
3584
3978
  },
3585
3979
  required: ["instancePath", "propertyName", "propertyValue"]
@@ -3603,6 +3997,10 @@ var init_definitions = __esm({
3603
3997
  },
3604
3998
  propertyValue: {
3605
3999
  description: "Value to set (string, number, boolean, or object for Vector3/Color3/UDim2)"
4000
+ },
4001
+ instance_id: {
4002
+ type: "string",
4003
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3606
4004
  }
3607
4005
  },
3608
4006
  required: ["paths", "propertyName", "propertyValue"]
@@ -3623,6 +4021,10 @@ var init_definitions = __esm({
3623
4021
  propertyName: {
3624
4022
  type: "string",
3625
4023
  description: "Property name"
4024
+ },
4025
+ instance_id: {
4026
+ type: "string",
4027
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3626
4028
  }
3627
4029
  },
3628
4030
  required: ["paths", "propertyName"]
@@ -3642,6 +4044,10 @@ var init_definitions = __esm({
3642
4044
  properties: {
3643
4045
  type: "object",
3644
4046
  description: "Map of property name to value"
4047
+ },
4048
+ instance_id: {
4049
+ type: "string",
4050
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3645
4051
  }
3646
4052
  },
3647
4053
  required: ["instancePath", "properties"]
@@ -3670,6 +4076,10 @@ var init_definitions = __esm({
3670
4076
  properties: {
3671
4077
  type: "object",
3672
4078
  description: "Properties to set on creation"
4079
+ },
4080
+ instance_id: {
4081
+ type: "string",
4082
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3673
4083
  }
3674
4084
  },
3675
4085
  required: ["className", "parent"]
@@ -3707,6 +4117,10 @@ var init_definitions = __esm({
3707
4117
  required: ["className", "parent"]
3708
4118
  },
3709
4119
  description: "Objects to create"
4120
+ },
4121
+ instance_id: {
4122
+ type: "string",
4123
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3710
4124
  }
3711
4125
  },
3712
4126
  required: ["objects"]
@@ -3722,6 +4136,10 @@ var init_definitions = __esm({
3722
4136
  instancePath: {
3723
4137
  type: "string",
3724
4138
  description: "Instance path (dot notation)"
4139
+ },
4140
+ instance_id: {
4141
+ type: "string",
4142
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3725
4143
  }
3726
4144
  },
3727
4145
  required: ["instancePath"]
@@ -3775,6 +4193,10 @@ var init_definitions = __esm({
3775
4193
  description: "Different parent per duplicate"
3776
4194
  }
3777
4195
  }
4196
+ },
4197
+ instance_id: {
4198
+ type: "string",
4199
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3778
4200
  }
3779
4201
  },
3780
4202
  required: ["instancePath", "count"]
@@ -3837,6 +4259,10 @@ var init_definitions = __esm({
3837
4259
  required: ["instancePath", "count"]
3838
4260
  },
3839
4261
  description: "Duplication operations"
4262
+ },
4263
+ instance_id: {
4264
+ type: "string",
4265
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3840
4266
  }
3841
4267
  },
3842
4268
  required: ["duplications"]
@@ -3862,6 +4288,10 @@ var init_definitions = __esm({
3862
4288
  endLine: {
3863
4289
  type: "number",
3864
4290
  description: "End line (inclusive)"
4291
+ },
4292
+ instance_id: {
4293
+ type: "string",
4294
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3865
4295
  }
3866
4296
  },
3867
4297
  required: ["instancePath"]
@@ -3881,6 +4311,10 @@ var init_definitions = __esm({
3881
4311
  source: {
3882
4312
  type: "string",
3883
4313
  description: "New source code"
4314
+ },
4315
+ instance_id: {
4316
+ type: "string",
4317
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3884
4318
  }
3885
4319
  },
3886
4320
  required: ["instancePath", "source"]
@@ -3908,6 +4342,10 @@ var init_definitions = __esm({
3908
4342
  startLine: {
3909
4343
  type: "number",
3910
4344
  description: "Optional 1-indexed line where old_string begins. When provided, skips uniqueness check and requires old_string to match starting at that exact line."
4345
+ },
4346
+ instance_id: {
4347
+ type: "string",
4348
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3911
4349
  }
3912
4350
  },
3913
4351
  required: ["instancePath", "old_string", "new_string"]
@@ -3931,6 +4369,10 @@ var init_definitions = __esm({
3931
4369
  newContent: {
3932
4370
  type: "string",
3933
4371
  description: "Content to insert"
4372
+ },
4373
+ instance_id: {
4374
+ type: "string",
4375
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3934
4376
  }
3935
4377
  },
3936
4378
  required: ["instancePath", "newContent"]
@@ -3954,6 +4396,10 @@ var init_definitions = __esm({
3954
4396
  endLine: {
3955
4397
  type: "number",
3956
4398
  description: "End line (inclusive)"
4399
+ },
4400
+ instance_id: {
4401
+ type: "string",
4402
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3957
4403
  }
3958
4404
  },
3959
4405
  required: ["instancePath", "startLine", "endLine"]
@@ -3981,6 +4427,10 @@ var init_definitions = __esm({
3981
4427
  valueType: {
3982
4428
  type: "string",
3983
4429
  description: "Type hint if needed"
4430
+ },
4431
+ instance_id: {
4432
+ type: "string",
4433
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3984
4434
  }
3985
4435
  },
3986
4436
  required: ["instancePath", "attributeName", "attributeValue"]
@@ -3996,6 +4446,10 @@ var init_definitions = __esm({
3996
4446
  instancePath: {
3997
4447
  type: "string",
3998
4448
  description: "Instance path (dot notation)"
4449
+ },
4450
+ instance_id: {
4451
+ type: "string",
4452
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
3999
4453
  }
4000
4454
  },
4001
4455
  required: ["instancePath"]
@@ -4015,6 +4469,10 @@ var init_definitions = __esm({
4015
4469
  attributeName: {
4016
4470
  type: "string",
4017
4471
  description: "Attribute name"
4472
+ },
4473
+ instance_id: {
4474
+ type: "string",
4475
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4018
4476
  }
4019
4477
  },
4020
4478
  required: ["instancePath", "attributeName"]
@@ -4031,6 +4489,10 @@ var init_definitions = __esm({
4031
4489
  instancePath: {
4032
4490
  type: "string",
4033
4491
  description: "Instance path (dot notation)"
4492
+ },
4493
+ instance_id: {
4494
+ type: "string",
4495
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4034
4496
  }
4035
4497
  },
4036
4498
  required: ["instancePath"]
@@ -4050,6 +4512,10 @@ var init_definitions = __esm({
4050
4512
  tagName: {
4051
4513
  type: "string",
4052
4514
  description: "Tag name"
4515
+ },
4516
+ instance_id: {
4517
+ type: "string",
4518
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4053
4519
  }
4054
4520
  },
4055
4521
  required: ["instancePath", "tagName"]
@@ -4069,6 +4535,10 @@ var init_definitions = __esm({
4069
4535
  tagName: {
4070
4536
  type: "string",
4071
4537
  description: "Tag name"
4538
+ },
4539
+ instance_id: {
4540
+ type: "string",
4541
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4072
4542
  }
4073
4543
  },
4074
4544
  required: ["instancePath", "tagName"]
@@ -4084,6 +4554,10 @@ var init_definitions = __esm({
4084
4554
  tagName: {
4085
4555
  type: "string",
4086
4556
  description: "Tag name"
4557
+ },
4558
+ instance_id: {
4559
+ type: "string",
4560
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4087
4561
  }
4088
4562
  },
4089
4563
  required: ["tagName"]
@@ -4096,7 +4570,12 @@ var init_definitions = __esm({
4096
4570
  description: "Get all currently selected objects",
4097
4571
  inputSchema: {
4098
4572
  type: "object",
4099
- properties: {}
4573
+ properties: {
4574
+ instance_id: {
4575
+ type: "string",
4576
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4577
+ }
4578
+ }
4100
4579
  }
4101
4580
  },
4102
4581
  // === Luau Execution ===
@@ -4114,6 +4593,10 @@ var init_definitions = __esm({
4114
4593
  target: {
4115
4594
  type: "string",
4116
4595
  description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
4596
+ },
4597
+ instance_id: {
4598
+ type: "string",
4599
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4117
4600
  }
4118
4601
  },
4119
4602
  required: ["code"]
@@ -4129,6 +4612,10 @@ var init_definitions = __esm({
4129
4612
  code: {
4130
4613
  type: "string",
4131
4614
  description: "Luau code to execute. Use return ... to get a value back."
4615
+ },
4616
+ instance_id: {
4617
+ type: "string",
4618
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4132
4619
  }
4133
4620
  },
4134
4621
  required: ["code"]
@@ -4148,6 +4635,10 @@ var init_definitions = __esm({
4148
4635
  target: {
4149
4636
  type: "string",
4150
4637
  description: 'Client target: "client-1" (default), "client-2", etc.'
4638
+ },
4639
+ instance_id: {
4640
+ type: "string",
4641
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4151
4642
  }
4152
4643
  },
4153
4644
  required: ["code"]
@@ -4197,6 +4688,10 @@ var init_definitions = __esm({
4197
4688
  type: "string",
4198
4689
  enum: ["Script", "LocalScript", "ModuleScript"],
4199
4690
  description: "Only search scripts of this class type"
4691
+ },
4692
+ instance_id: {
4693
+ type: "string",
4694
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4200
4695
  }
4201
4696
  },
4202
4697
  required: ["pattern"]
@@ -4218,6 +4713,10 @@ var init_definitions = __esm({
4218
4713
  numPlayers: {
4219
4714
  type: "number",
4220
4715
  description: "Number of client players (1-8). Triggers server + clients mode via TestService."
4716
+ },
4717
+ instance_id: {
4718
+ type: "string",
4719
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4221
4720
  }
4222
4721
  },
4223
4722
  required: ["mode"]
@@ -4229,7 +4728,12 @@ var init_definitions = __esm({
4229
4728
  description: "Stop playtest and return all captured output.",
4230
4729
  inputSchema: {
4231
4730
  type: "object",
4232
- properties: {}
4731
+ properties: {
4732
+ instance_id: {
4733
+ type: "string",
4734
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4735
+ }
4736
+ }
4233
4737
  }
4234
4738
  },
4235
4739
  {
@@ -4242,6 +4746,10 @@ var init_definitions = __esm({
4242
4746
  target: {
4243
4747
  type: "string",
4244
4748
  description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
4749
+ },
4750
+ instance_id: {
4751
+ type: "string",
4752
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4245
4753
  }
4246
4754
  }
4247
4755
  }
@@ -4268,6 +4776,10 @@ var init_definitions = __esm({
4268
4776
  filter: {
4269
4777
  type: "string",
4270
4778
  description: "Plain substring matched against each entry's message (no pattern semantics; literal text). Applied after since, before tail."
4779
+ },
4780
+ instance_id: {
4781
+ type: "string",
4782
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4271
4783
  }
4272
4784
  }
4273
4785
  }
@@ -4289,7 +4801,12 @@ var init_definitions = __esm({
4289
4801
  description: "Undo the last change in Roblox Studio. Uses ChangeHistoryService to reverse the most recent operation.",
4290
4802
  inputSchema: {
4291
4803
  type: "object",
4292
- properties: {}
4804
+ properties: {
4805
+ instance_id: {
4806
+ type: "string",
4807
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4808
+ }
4809
+ }
4293
4810
  }
4294
4811
  },
4295
4812
  {
@@ -4298,7 +4815,12 @@ var init_definitions = __esm({
4298
4815
  description: "Redo the last undone change in Roblox Studio. Uses ChangeHistoryService to reapply the most recently undone operation.",
4299
4816
  inputSchema: {
4300
4817
  type: "object",
4301
- properties: {}
4818
+ properties: {
4819
+ instance_id: {
4820
+ type: "string",
4821
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4822
+ }
4823
+ }
4302
4824
  }
4303
4825
  },
4304
4826
  // === Build Library ===
@@ -4321,6 +4843,10 @@ var init_definitions = __esm({
4321
4843
  type: "string",
4322
4844
  enum: ["medieval", "modern", "nature", "scifi", "misc"],
4323
4845
  description: "Style category for the build (default: misc)"
4846
+ },
4847
+ instance_id: {
4848
+ type: "string",
4849
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4324
4850
  }
4325
4851
  },
4326
4852
  required: ["instancePath"]
@@ -4470,6 +4996,10 @@ part(0,2,0,2,1,1,"b")`,
4470
4996
  type: "array",
4471
4997
  items: { type: "number" },
4472
4998
  description: "World position offset [X, Y, Z]"
4999
+ },
5000
+ instance_id: {
5001
+ type: "string",
5002
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4473
5003
  }
4474
5004
  },
4475
5005
  required: ["buildData", "targetPath"]
@@ -4504,6 +5034,10 @@ part(0,2,0,2,1,1,"b")`,
4504
5034
  maxResults: {
4505
5035
  type: "number",
4506
5036
  description: "Max results to return (default: 50)"
5037
+ },
5038
+ instance_id: {
5039
+ type: "string",
5040
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4507
5041
  }
4508
5042
  }
4509
5043
  }
@@ -4588,6 +5122,10 @@ part(0,2,0,2,1,1,"b")`,
4588
5122
  targetPath: {
4589
5123
  type: "string",
4590
5124
  description: "Parent instance path for the scene (default: game.Workspace)"
5125
+ },
5126
+ instance_id: {
5127
+ type: "string",
5128
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4591
5129
  }
4592
5130
  },
4593
5131
  required: ["sceneData"]
@@ -4685,6 +5223,10 @@ part(0,2,0,2,1,1,"b")`,
4685
5223
  z: { type: "number" }
4686
5224
  },
4687
5225
  description: "Optional world position to place the asset"
5226
+ },
5227
+ instance_id: {
5228
+ type: "string",
5229
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4688
5230
  }
4689
5231
  },
4690
5232
  required: ["assetId"]
@@ -4708,6 +5250,10 @@ part(0,2,0,2,1,1,"b")`,
4708
5250
  maxDepth: {
4709
5251
  type: "number",
4710
5252
  description: "Max hierarchy traversal depth (default: 10)"
5253
+ },
5254
+ instance_id: {
5255
+ type: "string",
5256
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4711
5257
  }
4712
5258
  },
4713
5259
  required: ["assetId"]
@@ -4755,7 +5301,12 @@ part(0,2,0,2,1,1,"b")`,
4755
5301
  description: 'Capture a screenshot of the Roblox Studio viewport and return it as a PNG image. Requires EditableImage API to be enabled: Game Settings > Security > "Allow Mesh / Image APIs". Only works in Edit mode with the viewport visible.',
4756
5302
  inputSchema: {
4757
5303
  type: "object",
4758
- properties: {}
5304
+ properties: {
5305
+ instance_id: {
5306
+ type: "string",
5307
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
5308
+ }
5309
+ }
4759
5310
  }
4760
5311
  },
4761
5312
  // === Input Simulation ===
@@ -4792,6 +5343,10 @@ part(0,2,0,2,1,1,"b")`,
4792
5343
  target: {
4793
5344
  type: "string",
4794
5345
  description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
5346
+ },
5347
+ instance_id: {
5348
+ type: "string",
5349
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4795
5350
  }
4796
5351
  },
4797
5352
  required: ["action", "x", "y"]
@@ -4820,6 +5375,10 @@ part(0,2,0,2,1,1,"b")`,
4820
5375
  target: {
4821
5376
  type: "string",
4822
5377
  description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
5378
+ },
5379
+ instance_id: {
5380
+ type: "string",
5381
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4823
5382
  }
4824
5383
  },
4825
5384
  required: ["keyCode"]
@@ -4853,6 +5412,10 @@ part(0,2,0,2,1,1,"b")`,
4853
5412
  target: {
4854
5413
  type: "string",
4855
5414
  description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
5415
+ },
5416
+ instance_id: {
5417
+ type: "string",
5418
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4856
5419
  }
4857
5420
  }
4858
5421
  }
@@ -4872,6 +5435,10 @@ part(0,2,0,2,1,1,"b")`,
4872
5435
  targetParentPath: {
4873
5436
  type: "string",
4874
5437
  description: "Path of the parent to place the clone under"
5438
+ },
5439
+ instance_id: {
5440
+ type: "string",
5441
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4875
5442
  }
4876
5443
  },
4877
5444
  required: ["instancePath", "targetParentPath"]
@@ -4896,6 +5463,10 @@ part(0,2,0,2,1,1,"b")`,
4896
5463
  classFilter: {
4897
5464
  type: "string",
4898
5465
  description: 'Only include instances of this class (uses IsA, so "BasePart" matches Part, MeshPart, etc.)'
5466
+ },
5467
+ instance_id: {
5468
+ type: "string",
5469
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4899
5470
  }
4900
5471
  },
4901
5472
  required: ["instancePath"]
@@ -4915,6 +5486,10 @@ part(0,2,0,2,1,1,"b")`,
4915
5486
  instancePathB: {
4916
5487
  type: "string",
4917
5488
  description: "Second instance path"
5489
+ },
5490
+ instance_id: {
5491
+ type: "string",
5492
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4918
5493
  }
4919
5494
  },
4920
5495
  required: ["instancePathA", "instancePathB"]
@@ -4935,6 +5510,10 @@ part(0,2,0,2,1,1,"b")`,
4935
5510
  messageType: {
4936
5511
  type: "string",
4937
5512
  description: 'Filter by message type (e.g. "Enum.MessageType.MessageOutput", "Enum.MessageType.MessageWarning", "Enum.MessageType.MessageError")'
5513
+ },
5514
+ instance_id: {
5515
+ type: "string",
5516
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4938
5517
  }
4939
5518
  }
4940
5519
  }
@@ -4954,6 +5533,10 @@ part(0,2,0,2,1,1,"b")`,
4954
5533
  attributes: {
4955
5534
  type: "object",
4956
5535
  description: "Map of attribute names to values. Supports Vector3, Color3, UDim2 via _type convention."
5536
+ },
5537
+ instance_id: {
5538
+ type: "string",
5539
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4957
5540
  }
4958
5541
  },
4959
5542
  required: ["instancePath", "attributes"]
@@ -4975,6 +5558,10 @@ part(0,2,0,2,1,1,"b")`,
4975
5558
  type: "array",
4976
5559
  items: { type: "string" },
4977
5560
  description: "Optional DeveloperMemoryTag whitelist. Unknown tag names return 0 + unknown_tags list."
5561
+ },
5562
+ instance_id: {
5563
+ type: "string",
5564
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
4978
5565
  }
4979
5566
  }
4980
5567
  }
@@ -5000,6 +5587,10 @@ part(0,2,0,2,1,1,"b")`,
5000
5587
  type: "string",
5001
5588
  enum: ["edit", "server"],
5002
5589
  description: 'Which DataModel to read from (default: "edit"). "server" serializes live runtime state during a playtest.'
5590
+ },
5591
+ instance_id: {
5592
+ type: "string",
5593
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
5003
5594
  }
5004
5595
  },
5005
5596
  required: ["instance_paths", "output_path"]
@@ -5034,6 +5625,10 @@ part(0,2,0,2,1,1,"b")`,
5034
5625
  type: "string",
5035
5626
  enum: ["edit", "server"],
5036
5627
  description: 'Which DataModel to import into (default: "edit"). "server" parents into the live play-server DM.'
5628
+ },
5629
+ instance_id: {
5630
+ type: "string",
5631
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
5037
5632
  }
5038
5633
  },
5039
5634
  required: ["source", "parent_path"]
@@ -5079,6 +5674,10 @@ part(0,2,0,2,1,1,"b")`,
5079
5674
  maxReplacements: {
5080
5675
  type: "number",
5081
5676
  description: "Safety limit on total replacements (default: 1000)"
5677
+ },
5678
+ instance_id: {
5679
+ type: "string",
5680
+ description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
5082
5681
  }
5083
5682
  },
5084
5683
  required: ["pattern", "replacement"]