@chrrxs/robloxstudio-mcp 2.11.3 → 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 +1024 -404
- package/package.json +1 -1
- package/studio-plugin/MCPInspectorPlugin.rbxmx +24 -3
- package/studio-plugin/MCPPlugin.rbxmx +596 -196
- package/studio-plugin/src/modules/ClientBroker.ts +69 -35
- package/studio-plugin/src/modules/Communication.ts +101 -5
- package/studio-plugin/src/modules/LuauExec.ts +305 -0
- package/studio-plugin/src/modules/StopPlayMonitor.ts +67 -31
- package/studio-plugin/src/modules/handlers/MetadataHandlers.ts +6 -121
- package/studio-plugin/src/modules/handlers/TestHandlers.ts +20 -2
- package/studio-plugin/src/types/index.d.ts +5 -2
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(
|
|
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
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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;
|
|
80
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;
|
|
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 {
|
|
84
|
-
if (
|
|
85
|
-
bridge.unregisterInstance(
|
|
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(
|
|
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
|
|
108
|
-
if (
|
|
109
|
-
bridge.updateInstanceActivity(
|
|
415
|
+
const pluginSessionId = req.query.pluginSessionId;
|
|
416
|
+
if (pluginSessionId) {
|
|
417
|
+
bridge.updateInstanceActivity(pluginSessionId);
|
|
110
418
|
}
|
|
111
|
-
let
|
|
419
|
+
let callerInstanceId;
|
|
420
|
+
let callerRole;
|
|
112
421
|
let knownInstance = false;
|
|
113
|
-
if (
|
|
114
|
-
const inst = bridge.
|
|
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,
|
|
161
|
-
if (!endpoint) {
|
|
162
|
-
res.status(400).json({ error: "endpoint
|
|
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,
|
|
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,
|
|
732
|
+
async request(endpoint, data, targetInstanceId, targetRole) {
|
|
401
733
|
try {
|
|
402
|
-
const response = await this.bridge.sendRequest(endpoint, data,
|
|
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
|
-
|
|
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"
|
|
@@ -1261,7 +1699,28 @@ m.Parent = workspace
|
|
|
1261
1699
|
local bridgeOk, inner = bf:Invoke(m)
|
|
1262
1700
|
m:Destroy()
|
|
1263
1701
|
if not bridgeOk then
|
|
1264
|
-
|
|
1702
|
+
local errMsg = tostring(inner)
|
|
1703
|
+
-- pcall(require, payload) collapses parse/compile failures into the
|
|
1704
|
+
-- canned engine string below. The real parser diagnostic was emitted
|
|
1705
|
+
-- to LogService just before. Walk GetLogHistory backward for the most
|
|
1706
|
+
-- recent ERR entry tagged at our payload path and substitute.
|
|
1707
|
+
if errMsg == "Requested module experienced an error while loading" then
|
|
1708
|
+
-- The parser diagnostic is emitted to LogService on the next
|
|
1709
|
+
-- engine frame, not synchronously with pcall(require). task.wait(0)
|
|
1710
|
+
-- yields too early; 50ms is enough to let the frame complete and
|
|
1711
|
+
-- the message land in GetLogHistory.
|
|
1712
|
+
task.wait(0.05)
|
|
1713
|
+
local LogService = game:GetService("LogService")
|
|
1714
|
+
local hist = LogService:GetLogHistory()
|
|
1715
|
+
for i = #hist, 1, -1 do
|
|
1716
|
+
local e = hist[i]
|
|
1717
|
+
if e.messageType == Enum.MessageType.MessageError and string.sub(e.message, 1, 27) == "Workspace.__MCPEvalPayload:" then
|
|
1718
|
+
errMsg = e.message
|
|
1719
|
+
break
|
|
1720
|
+
end
|
|
1721
|
+
end
|
|
1722
|
+
end
|
|
1723
|
+
return HttpService:JSONEncode({ bridge = "ok", ok = false, error = __mcp_outer_remap(errMsg) })
|
|
1265
1724
|
end
|
|
1266
1725
|
-- inner is the {ok, value, output} table from our IIFE. Defensive: if it's
|
|
1267
1726
|
-- somehow not a table (caller bypassed the wrapper), fall back to old shape.
|
|
@@ -1269,13 +1728,13 @@ if typeof(inner) ~= "table" then
|
|
|
1269
1728
|
return HttpService:JSONEncode({
|
|
1270
1729
|
bridge = "ok",
|
|
1271
1730
|
ok = true,
|
|
1272
|
-
result = if inner == nil then nil else
|
|
1731
|
+
result = if inner == nil then nil else __mcp_format(inner),
|
|
1273
1732
|
})
|
|
1274
1733
|
end
|
|
1275
1734
|
return HttpService:JSONEncode({
|
|
1276
1735
|
bridge = "ok",
|
|
1277
1736
|
ok = inner.ok == true,
|
|
1278
|
-
result = if inner.ok and inner.value ~= nil then
|
|
1737
|
+
result = if inner.ok and inner.value ~= nil then __mcp_format(inner.value) else nil,
|
|
1279
1738
|
error = if not inner.ok then tostring(inner.value) else nil,
|
|
1280
1739
|
output = inner.output or {},
|
|
1281
1740
|
})
|
|
@@ -1292,17 +1751,19 @@ function parseBridgeResponse(response) {
|
|
|
1292
1751
|
}
|
|
1293
1752
|
return JSON.stringify(response);
|
|
1294
1753
|
}
|
|
1295
|
-
var SERVER_LOCAL_NAME, CLIENT_LOCAL_NAME, RobloxStudioTools;
|
|
1754
|
+
var SERVER_LOCAL_NAME, CLIENT_LOCAL_NAME, EVAL_WRAPPER_LINE_OFFSET, RobloxStudioTools;
|
|
1296
1755
|
var init_tools = __esm({
|
|
1297
1756
|
"../core/dist/tools/index.js"() {
|
|
1298
1757
|
"use strict";
|
|
1299
1758
|
init_studio_client();
|
|
1759
|
+
init_bridge_service();
|
|
1300
1760
|
init_build_executor();
|
|
1301
1761
|
init_opencloud_client();
|
|
1302
1762
|
init_roblox_cookie_client();
|
|
1303
1763
|
init_png_encoder();
|
|
1304
1764
|
SERVER_LOCAL_NAME = "__MCP_ServerEvalLocal";
|
|
1305
1765
|
CLIENT_LOCAL_NAME = "__MCP_ClientEvalBridge";
|
|
1766
|
+
EVAL_WRAPPER_LINE_OFFSET = 23;
|
|
1306
1767
|
RobloxStudioTools = class _RobloxStudioTools {
|
|
1307
1768
|
client;
|
|
1308
1769
|
bridge;
|
|
@@ -1314,8 +1775,29 @@ var init_tools = __esm({
|
|
|
1314
1775
|
this.openCloudClient = new OpenCloudClient();
|
|
1315
1776
|
this.cookieClient = new RobloxCookieClient();
|
|
1316
1777
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
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);
|
|
1319
1801
|
return {
|
|
1320
1802
|
content: [
|
|
1321
1803
|
{
|
|
@@ -1325,8 +1807,8 @@ var init_tools = __esm({
|
|
|
1325
1807
|
]
|
|
1326
1808
|
};
|
|
1327
1809
|
}
|
|
1328
|
-
async searchFiles(query, searchType = "name") {
|
|
1329
|
-
const response = await this.
|
|
1810
|
+
async searchFiles(query, searchType = "name", instance_id) {
|
|
1811
|
+
const response = await this._callSingle("/api/search-files", { query, searchType }, void 0, instance_id);
|
|
1330
1812
|
return {
|
|
1331
1813
|
content: [
|
|
1332
1814
|
{
|
|
@@ -1336,8 +1818,8 @@ var init_tools = __esm({
|
|
|
1336
1818
|
]
|
|
1337
1819
|
};
|
|
1338
1820
|
}
|
|
1339
|
-
async getPlaceInfo() {
|
|
1340
|
-
const response = await this.
|
|
1821
|
+
async getPlaceInfo(instance_id) {
|
|
1822
|
+
const response = await this._callSingle("/api/place-info", {}, void 0, instance_id);
|
|
1341
1823
|
return {
|
|
1342
1824
|
content: [
|
|
1343
1825
|
{
|
|
@@ -1347,8 +1829,8 @@ var init_tools = __esm({
|
|
|
1347
1829
|
]
|
|
1348
1830
|
};
|
|
1349
1831
|
}
|
|
1350
|
-
async getServices(serviceName) {
|
|
1351
|
-
const response = await this.
|
|
1832
|
+
async getServices(serviceName, instance_id) {
|
|
1833
|
+
const response = await this._callSingle("/api/services", { serviceName }, void 0, instance_id);
|
|
1352
1834
|
return {
|
|
1353
1835
|
content: [
|
|
1354
1836
|
{
|
|
@@ -1358,12 +1840,12 @@ var init_tools = __esm({
|
|
|
1358
1840
|
]
|
|
1359
1841
|
};
|
|
1360
1842
|
}
|
|
1361
|
-
async searchObjects(query, searchType = "name", propertyName) {
|
|
1362
|
-
const response = await this.
|
|
1843
|
+
async searchObjects(query, searchType = "name", propertyName, instance_id) {
|
|
1844
|
+
const response = await this._callSingle("/api/search-objects", {
|
|
1363
1845
|
query,
|
|
1364
1846
|
searchType,
|
|
1365
1847
|
propertyName
|
|
1366
|
-
});
|
|
1848
|
+
}, void 0, instance_id);
|
|
1367
1849
|
return {
|
|
1368
1850
|
content: [
|
|
1369
1851
|
{
|
|
@@ -1373,11 +1855,11 @@ var init_tools = __esm({
|
|
|
1373
1855
|
]
|
|
1374
1856
|
};
|
|
1375
1857
|
}
|
|
1376
|
-
async getInstanceProperties(instancePath, excludeSource) {
|
|
1858
|
+
async getInstanceProperties(instancePath, excludeSource, instance_id) {
|
|
1377
1859
|
if (!instancePath) {
|
|
1378
1860
|
throw new Error("Instance path is required for get_instance_properties");
|
|
1379
1861
|
}
|
|
1380
|
-
const response = await this.
|
|
1862
|
+
const response = await this._callSingle("/api/instance-properties", { instancePath, excludeSource }, void 0, instance_id);
|
|
1381
1863
|
return {
|
|
1382
1864
|
content: [
|
|
1383
1865
|
{
|
|
@@ -1387,11 +1869,11 @@ var init_tools = __esm({
|
|
|
1387
1869
|
]
|
|
1388
1870
|
};
|
|
1389
1871
|
}
|
|
1390
|
-
async getInstanceChildren(instancePath) {
|
|
1872
|
+
async getInstanceChildren(instancePath, instance_id) {
|
|
1391
1873
|
if (!instancePath) {
|
|
1392
1874
|
throw new Error("Instance path is required for get_instance_children");
|
|
1393
1875
|
}
|
|
1394
|
-
const response = await this.
|
|
1876
|
+
const response = await this._callSingle("/api/instance-children", { instancePath }, void 0, instance_id);
|
|
1395
1877
|
return {
|
|
1396
1878
|
content: [
|
|
1397
1879
|
{
|
|
@@ -1401,14 +1883,14 @@ var init_tools = __esm({
|
|
|
1401
1883
|
]
|
|
1402
1884
|
};
|
|
1403
1885
|
}
|
|
1404
|
-
async searchByProperty(propertyName, propertyValue) {
|
|
1886
|
+
async searchByProperty(propertyName, propertyValue, instance_id) {
|
|
1405
1887
|
if (!propertyName || !propertyValue) {
|
|
1406
1888
|
throw new Error("Property name and value are required for search_by_property");
|
|
1407
1889
|
}
|
|
1408
|
-
const response = await this.
|
|
1890
|
+
const response = await this._callSingle("/api/search-by-property", {
|
|
1409
1891
|
propertyName,
|
|
1410
1892
|
propertyValue
|
|
1411
|
-
});
|
|
1893
|
+
}, void 0, instance_id);
|
|
1412
1894
|
return {
|
|
1413
1895
|
content: [
|
|
1414
1896
|
{
|
|
@@ -1418,11 +1900,11 @@ var init_tools = __esm({
|
|
|
1418
1900
|
]
|
|
1419
1901
|
};
|
|
1420
1902
|
}
|
|
1421
|
-
async getClassInfo(className) {
|
|
1903
|
+
async getClassInfo(className, instance_id) {
|
|
1422
1904
|
if (!className) {
|
|
1423
1905
|
throw new Error("Class name is required for get_class_info");
|
|
1424
1906
|
}
|
|
1425
|
-
const response = await this.
|
|
1907
|
+
const response = await this._callSingle("/api/class-info", { className }, void 0, instance_id);
|
|
1426
1908
|
return {
|
|
1427
1909
|
content: [
|
|
1428
1910
|
{
|
|
@@ -1432,12 +1914,12 @@ var init_tools = __esm({
|
|
|
1432
1914
|
]
|
|
1433
1915
|
};
|
|
1434
1916
|
}
|
|
1435
|
-
async getProjectStructure(path2, maxDepth, scriptsOnly) {
|
|
1436
|
-
const response = await this.
|
|
1917
|
+
async getProjectStructure(path2, maxDepth, scriptsOnly, instance_id) {
|
|
1918
|
+
const response = await this._callSingle("/api/project-structure", {
|
|
1437
1919
|
path: path2,
|
|
1438
1920
|
maxDepth,
|
|
1439
1921
|
scriptsOnly
|
|
1440
|
-
});
|
|
1922
|
+
}, void 0, instance_id);
|
|
1441
1923
|
return {
|
|
1442
1924
|
content: [
|
|
1443
1925
|
{
|
|
@@ -1447,15 +1929,15 @@ var init_tools = __esm({
|
|
|
1447
1929
|
]
|
|
1448
1930
|
};
|
|
1449
1931
|
}
|
|
1450
|
-
async setProperty(instancePath, propertyName, propertyValue) {
|
|
1932
|
+
async setProperty(instancePath, propertyName, propertyValue, instance_id) {
|
|
1451
1933
|
if (!instancePath || !propertyName) {
|
|
1452
1934
|
throw new Error("Instance path and property name are required for set_property");
|
|
1453
1935
|
}
|
|
1454
|
-
const response = await this.
|
|
1936
|
+
const response = await this._callSingle("/api/set-property", {
|
|
1455
1937
|
instancePath,
|
|
1456
1938
|
propertyName,
|
|
1457
1939
|
propertyValue
|
|
1458
|
-
});
|
|
1940
|
+
}, void 0, instance_id);
|
|
1459
1941
|
return {
|
|
1460
1942
|
content: [
|
|
1461
1943
|
{
|
|
@@ -1465,22 +1947,22 @@ var init_tools = __esm({
|
|
|
1465
1947
|
]
|
|
1466
1948
|
};
|
|
1467
1949
|
}
|
|
1468
|
-
async setProperties(instancePath, properties) {
|
|
1950
|
+
async setProperties(instancePath, properties, instance_id) {
|
|
1469
1951
|
if (!instancePath || !properties) {
|
|
1470
1952
|
throw new Error("instancePath and properties are required for set_properties");
|
|
1471
1953
|
}
|
|
1472
|
-
const response = await this.
|
|
1954
|
+
const response = await this._callSingle("/api/set-properties", { instancePath, properties }, void 0, instance_id);
|
|
1473
1955
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
1474
1956
|
}
|
|
1475
|
-
async massSetProperty(paths, propertyName, propertyValue) {
|
|
1957
|
+
async massSetProperty(paths, propertyName, propertyValue, instance_id) {
|
|
1476
1958
|
if (!paths || paths.length === 0 || !propertyName) {
|
|
1477
1959
|
throw new Error("Paths array and property name are required for mass_set_property");
|
|
1478
1960
|
}
|
|
1479
|
-
const response = await this.
|
|
1961
|
+
const response = await this._callSingle("/api/mass-set-property", {
|
|
1480
1962
|
paths,
|
|
1481
1963
|
propertyName,
|
|
1482
1964
|
propertyValue
|
|
1483
|
-
});
|
|
1965
|
+
}, void 0, instance_id);
|
|
1484
1966
|
return {
|
|
1485
1967
|
content: [
|
|
1486
1968
|
{
|
|
@@ -1490,14 +1972,14 @@ var init_tools = __esm({
|
|
|
1490
1972
|
]
|
|
1491
1973
|
};
|
|
1492
1974
|
}
|
|
1493
|
-
async massGetProperty(paths, propertyName) {
|
|
1975
|
+
async massGetProperty(paths, propertyName, instance_id) {
|
|
1494
1976
|
if (!paths || paths.length === 0 || !propertyName) {
|
|
1495
1977
|
throw new Error("Paths array and property name are required for mass_get_property");
|
|
1496
1978
|
}
|
|
1497
|
-
const response = await this.
|
|
1979
|
+
const response = await this._callSingle("/api/mass-get-property", {
|
|
1498
1980
|
paths,
|
|
1499
1981
|
propertyName
|
|
1500
|
-
});
|
|
1982
|
+
}, void 0, instance_id);
|
|
1501
1983
|
return {
|
|
1502
1984
|
content: [
|
|
1503
1985
|
{
|
|
@@ -1507,16 +1989,16 @@ var init_tools = __esm({
|
|
|
1507
1989
|
]
|
|
1508
1990
|
};
|
|
1509
1991
|
}
|
|
1510
|
-
async createObject(className, parent, name, properties) {
|
|
1992
|
+
async createObject(className, parent, name, properties, instance_id) {
|
|
1511
1993
|
if (!className || !parent) {
|
|
1512
1994
|
throw new Error("Class name and parent are required for create_object");
|
|
1513
1995
|
}
|
|
1514
|
-
const response = await this.
|
|
1996
|
+
const response = await this._callSingle("/api/create-object", {
|
|
1515
1997
|
className,
|
|
1516
1998
|
parent,
|
|
1517
1999
|
name,
|
|
1518
2000
|
properties
|
|
1519
|
-
});
|
|
2001
|
+
}, void 0, instance_id);
|
|
1520
2002
|
return {
|
|
1521
2003
|
content: [
|
|
1522
2004
|
{
|
|
@@ -1526,11 +2008,11 @@ var init_tools = __esm({
|
|
|
1526
2008
|
]
|
|
1527
2009
|
};
|
|
1528
2010
|
}
|
|
1529
|
-
async massCreateObjects(objects) {
|
|
2011
|
+
async massCreateObjects(objects, instance_id) {
|
|
1530
2012
|
if (!objects || objects.length === 0) {
|
|
1531
2013
|
throw new Error("Objects array is required for mass_create_objects");
|
|
1532
2014
|
}
|
|
1533
|
-
const response = await this.
|
|
2015
|
+
const response = await this._callSingle("/api/mass-create-objects", { objects }, void 0, instance_id);
|
|
1534
2016
|
return {
|
|
1535
2017
|
content: [
|
|
1536
2018
|
{
|
|
@@ -1540,11 +2022,11 @@ var init_tools = __esm({
|
|
|
1540
2022
|
]
|
|
1541
2023
|
};
|
|
1542
2024
|
}
|
|
1543
|
-
async deleteObject(instancePath) {
|
|
2025
|
+
async deleteObject(instancePath, instance_id) {
|
|
1544
2026
|
if (!instancePath) {
|
|
1545
2027
|
throw new Error("Instance path is required for delete_object");
|
|
1546
2028
|
}
|
|
1547
|
-
const response = await this.
|
|
2029
|
+
const response = await this._callSingle("/api/delete-object", { instancePath }, void 0, instance_id);
|
|
1548
2030
|
return {
|
|
1549
2031
|
content: [
|
|
1550
2032
|
{
|
|
@@ -1554,15 +2036,15 @@ var init_tools = __esm({
|
|
|
1554
2036
|
]
|
|
1555
2037
|
};
|
|
1556
2038
|
}
|
|
1557
|
-
async smartDuplicate(instancePath, count, options) {
|
|
2039
|
+
async smartDuplicate(instancePath, count, options, instance_id) {
|
|
1558
2040
|
if (!instancePath || count < 1) {
|
|
1559
2041
|
throw new Error("Instance path and count > 0 are required for smart_duplicate");
|
|
1560
2042
|
}
|
|
1561
|
-
const response = await this.
|
|
2043
|
+
const response = await this._callSingle("/api/smart-duplicate", {
|
|
1562
2044
|
instancePath,
|
|
1563
2045
|
count,
|
|
1564
2046
|
options
|
|
1565
|
-
});
|
|
2047
|
+
}, void 0, instance_id);
|
|
1566
2048
|
return {
|
|
1567
2049
|
content: [
|
|
1568
2050
|
{
|
|
@@ -1572,11 +2054,11 @@ var init_tools = __esm({
|
|
|
1572
2054
|
]
|
|
1573
2055
|
};
|
|
1574
2056
|
}
|
|
1575
|
-
async massDuplicate(duplications) {
|
|
2057
|
+
async massDuplicate(duplications, instance_id) {
|
|
1576
2058
|
if (!duplications || duplications.length === 0) {
|
|
1577
2059
|
throw new Error("Duplications array is required for mass_duplicate");
|
|
1578
2060
|
}
|
|
1579
|
-
const response = await this.
|
|
2061
|
+
const response = await this._callSingle("/api/mass-duplicate", { duplications }, void 0, instance_id);
|
|
1580
2062
|
return {
|
|
1581
2063
|
content: [
|
|
1582
2064
|
{
|
|
@@ -1586,11 +2068,11 @@ var init_tools = __esm({
|
|
|
1586
2068
|
]
|
|
1587
2069
|
};
|
|
1588
2070
|
}
|
|
1589
|
-
async getScriptSource(instancePath, startLine, endLine) {
|
|
2071
|
+
async getScriptSource(instancePath, startLine, endLine, instance_id) {
|
|
1590
2072
|
if (!instancePath) {
|
|
1591
2073
|
throw new Error("Instance path is required for get_script_source");
|
|
1592
2074
|
}
|
|
1593
|
-
const response = await this.
|
|
2075
|
+
const response = await this._callSingle("/api/get-script-source", { instancePath, startLine, endLine }, void 0, instance_id);
|
|
1594
2076
|
if (response.error) {
|
|
1595
2077
|
return { content: [{ type: "text", text: `Error: ${response.error}` }] };
|
|
1596
2078
|
}
|
|
@@ -1637,11 +2119,11 @@ ${code}`
|
|
|
1637
2119
|
}]
|
|
1638
2120
|
};
|
|
1639
2121
|
}
|
|
1640
|
-
async setScriptSource(instancePath, source) {
|
|
2122
|
+
async setScriptSource(instancePath, source, instance_id) {
|
|
1641
2123
|
if (!instancePath || typeof source !== "string") {
|
|
1642
2124
|
throw new Error("Instance path and source code string are required for set_script_source");
|
|
1643
2125
|
}
|
|
1644
|
-
const response = await this.
|
|
2126
|
+
const response = await this._callSingle("/api/set-script-source", { instancePath, source }, void 0, instance_id);
|
|
1645
2127
|
return {
|
|
1646
2128
|
content: [
|
|
1647
2129
|
{
|
|
@@ -1651,14 +2133,14 @@ ${code}`
|
|
|
1651
2133
|
]
|
|
1652
2134
|
};
|
|
1653
2135
|
}
|
|
1654
|
-
async editScriptLines(instancePath, oldString, newString, startLine) {
|
|
2136
|
+
async editScriptLines(instancePath, oldString, newString, startLine, instance_id) {
|
|
1655
2137
|
if (!instancePath || typeof oldString !== "string" || typeof newString !== "string") {
|
|
1656
2138
|
throw new Error("Instance path, old_string, and new_string are required for edit_script_lines");
|
|
1657
2139
|
}
|
|
1658
2140
|
const payload = { instancePath, old_string: oldString, new_string: newString };
|
|
1659
2141
|
if (startLine !== void 0)
|
|
1660
2142
|
payload.startLine = startLine;
|
|
1661
|
-
const response = await this.
|
|
2143
|
+
const response = await this._callSingle("/api/edit-script-lines", payload, void 0, instance_id);
|
|
1662
2144
|
return {
|
|
1663
2145
|
content: [
|
|
1664
2146
|
{
|
|
@@ -1668,11 +2150,11 @@ ${code}`
|
|
|
1668
2150
|
]
|
|
1669
2151
|
};
|
|
1670
2152
|
}
|
|
1671
|
-
async insertScriptLines(instancePath, afterLine, newContent) {
|
|
2153
|
+
async insertScriptLines(instancePath, afterLine, newContent, instance_id) {
|
|
1672
2154
|
if (!instancePath || typeof newContent !== "string") {
|
|
1673
2155
|
throw new Error("Instance path and newContent are required for insert_script_lines");
|
|
1674
2156
|
}
|
|
1675
|
-
const response = await this.
|
|
2157
|
+
const response = await this._callSingle("/api/insert-script-lines", { instancePath, afterLine: afterLine || 0, newContent }, void 0, instance_id);
|
|
1676
2158
|
return {
|
|
1677
2159
|
content: [
|
|
1678
2160
|
{
|
|
@@ -1682,11 +2164,11 @@ ${code}`
|
|
|
1682
2164
|
]
|
|
1683
2165
|
};
|
|
1684
2166
|
}
|
|
1685
|
-
async deleteScriptLines(instancePath, startLine, endLine) {
|
|
2167
|
+
async deleteScriptLines(instancePath, startLine, endLine, instance_id) {
|
|
1686
2168
|
if (!instancePath || !startLine || !endLine) {
|
|
1687
2169
|
throw new Error("Instance path, startLine, and endLine are required for delete_script_lines");
|
|
1688
2170
|
}
|
|
1689
|
-
const response = await this.
|
|
2171
|
+
const response = await this._callSingle("/api/delete-script-lines", { instancePath, startLine, endLine }, void 0, instance_id);
|
|
1690
2172
|
return {
|
|
1691
2173
|
content: [
|
|
1692
2174
|
{
|
|
@@ -1696,14 +2178,14 @@ ${code}`
|
|
|
1696
2178
|
]
|
|
1697
2179
|
};
|
|
1698
2180
|
}
|
|
1699
|
-
async grepScripts(pattern, options) {
|
|
2181
|
+
async grepScripts(pattern, options, instance_id) {
|
|
1700
2182
|
if (!pattern) {
|
|
1701
2183
|
throw new Error("Pattern is required for grep_scripts");
|
|
1702
2184
|
}
|
|
1703
|
-
const response = await this.
|
|
2185
|
+
const response = await this._callSingle("/api/grep-scripts", {
|
|
1704
2186
|
pattern,
|
|
1705
2187
|
...options
|
|
1706
|
-
});
|
|
2188
|
+
}, void 0, instance_id);
|
|
1707
2189
|
return {
|
|
1708
2190
|
content: [
|
|
1709
2191
|
{
|
|
@@ -1713,11 +2195,11 @@ ${code}`
|
|
|
1713
2195
|
]
|
|
1714
2196
|
};
|
|
1715
2197
|
}
|
|
1716
|
-
async setAttribute(instancePath, attributeName, attributeValue, valueType) {
|
|
2198
|
+
async setAttribute(instancePath, attributeName, attributeValue, valueType, instance_id) {
|
|
1717
2199
|
if (!instancePath || !attributeName) {
|
|
1718
2200
|
throw new Error("Instance path and attribute name are required for set_attribute");
|
|
1719
2201
|
}
|
|
1720
|
-
const response = await this.
|
|
2202
|
+
const response = await this._callSingle("/api/set-attribute", { instancePath, attributeName, attributeValue, valueType }, void 0, instance_id);
|
|
1721
2203
|
return {
|
|
1722
2204
|
content: [
|
|
1723
2205
|
{
|
|
@@ -1727,11 +2209,11 @@ ${code}`
|
|
|
1727
2209
|
]
|
|
1728
2210
|
};
|
|
1729
2211
|
}
|
|
1730
|
-
async getAttributes(instancePath) {
|
|
2212
|
+
async getAttributes(instancePath, instance_id) {
|
|
1731
2213
|
if (!instancePath) {
|
|
1732
2214
|
throw new Error("Instance path is required for get_attributes");
|
|
1733
2215
|
}
|
|
1734
|
-
const response = await this.
|
|
2216
|
+
const response = await this._callSingle("/api/get-attributes", { instancePath }, void 0, instance_id);
|
|
1735
2217
|
return {
|
|
1736
2218
|
content: [
|
|
1737
2219
|
{
|
|
@@ -1741,11 +2223,11 @@ ${code}`
|
|
|
1741
2223
|
]
|
|
1742
2224
|
};
|
|
1743
2225
|
}
|
|
1744
|
-
async deleteAttribute(instancePath, attributeName) {
|
|
2226
|
+
async deleteAttribute(instancePath, attributeName, instance_id) {
|
|
1745
2227
|
if (!instancePath || !attributeName) {
|
|
1746
2228
|
throw new Error("Instance path and attribute name are required for delete_attribute");
|
|
1747
2229
|
}
|
|
1748
|
-
const response = await this.
|
|
2230
|
+
const response = await this._callSingle("/api/delete-attribute", { instancePath, attributeName }, void 0, instance_id);
|
|
1749
2231
|
return {
|
|
1750
2232
|
content: [
|
|
1751
2233
|
{
|
|
@@ -1755,11 +2237,11 @@ ${code}`
|
|
|
1755
2237
|
]
|
|
1756
2238
|
};
|
|
1757
2239
|
}
|
|
1758
|
-
async getTags(instancePath) {
|
|
2240
|
+
async getTags(instancePath, instance_id) {
|
|
1759
2241
|
if (!instancePath) {
|
|
1760
2242
|
throw new Error("Instance path is required for get_tags");
|
|
1761
2243
|
}
|
|
1762
|
-
const response = await this.
|
|
2244
|
+
const response = await this._callSingle("/api/get-tags", { instancePath }, void 0, instance_id);
|
|
1763
2245
|
return {
|
|
1764
2246
|
content: [
|
|
1765
2247
|
{
|
|
@@ -1769,11 +2251,11 @@ ${code}`
|
|
|
1769
2251
|
]
|
|
1770
2252
|
};
|
|
1771
2253
|
}
|
|
1772
|
-
async addTag(instancePath, tagName) {
|
|
2254
|
+
async addTag(instancePath, tagName, instance_id) {
|
|
1773
2255
|
if (!instancePath || !tagName) {
|
|
1774
2256
|
throw new Error("Instance path and tag name are required for add_tag");
|
|
1775
2257
|
}
|
|
1776
|
-
const response = await this.
|
|
2258
|
+
const response = await this._callSingle("/api/add-tag", { instancePath, tagName }, void 0, instance_id);
|
|
1777
2259
|
return {
|
|
1778
2260
|
content: [
|
|
1779
2261
|
{
|
|
@@ -1783,11 +2265,11 @@ ${code}`
|
|
|
1783
2265
|
]
|
|
1784
2266
|
};
|
|
1785
2267
|
}
|
|
1786
|
-
async removeTag(instancePath, tagName) {
|
|
2268
|
+
async removeTag(instancePath, tagName, instance_id) {
|
|
1787
2269
|
if (!instancePath || !tagName) {
|
|
1788
2270
|
throw new Error("Instance path and tag name are required for remove_tag");
|
|
1789
2271
|
}
|
|
1790
|
-
const response = await this.
|
|
2272
|
+
const response = await this._callSingle("/api/remove-tag", { instancePath, tagName }, void 0, instance_id);
|
|
1791
2273
|
return {
|
|
1792
2274
|
content: [
|
|
1793
2275
|
{
|
|
@@ -1797,11 +2279,11 @@ ${code}`
|
|
|
1797
2279
|
]
|
|
1798
2280
|
};
|
|
1799
2281
|
}
|
|
1800
|
-
async getTagged(tagName) {
|
|
2282
|
+
async getTagged(tagName, instance_id) {
|
|
1801
2283
|
if (!tagName) {
|
|
1802
2284
|
throw new Error("Tag name is required for get_tagged");
|
|
1803
2285
|
}
|
|
1804
|
-
const response = await this.
|
|
2286
|
+
const response = await this._callSingle("/api/get-tagged", { tagName }, void 0, instance_id);
|
|
1805
2287
|
return {
|
|
1806
2288
|
content: [
|
|
1807
2289
|
{
|
|
@@ -1811,8 +2293,8 @@ ${code}`
|
|
|
1811
2293
|
]
|
|
1812
2294
|
};
|
|
1813
2295
|
}
|
|
1814
|
-
async getSelection() {
|
|
1815
|
-
const response = await this.
|
|
2296
|
+
async getSelection(instance_id) {
|
|
2297
|
+
const response = await this._callSingle("/api/get-selection", {}, void 0, instance_id);
|
|
1816
2298
|
return {
|
|
1817
2299
|
content: [
|
|
1818
2300
|
{
|
|
@@ -1822,11 +2304,11 @@ ${code}`
|
|
|
1822
2304
|
]
|
|
1823
2305
|
};
|
|
1824
2306
|
}
|
|
1825
|
-
async executeLuau(code, target) {
|
|
2307
|
+
async executeLuau(code, target, instance_id) {
|
|
1826
2308
|
if (!code) {
|
|
1827
2309
|
throw new Error("Code is required for execute_luau");
|
|
1828
2310
|
}
|
|
1829
|
-
const response = await this.
|
|
2311
|
+
const response = await this._callSingle("/api/execute-luau", { code }, target || "edit", instance_id);
|
|
1830
2312
|
return {
|
|
1831
2313
|
content: [
|
|
1832
2314
|
{
|
|
@@ -1836,7 +2318,7 @@ ${code}`
|
|
|
1836
2318
|
]
|
|
1837
2319
|
};
|
|
1838
2320
|
}
|
|
1839
|
-
async evalServerRuntime(code) {
|
|
2321
|
+
async evalServerRuntime(code, instance_id) {
|
|
1840
2322
|
if (!code) {
|
|
1841
2323
|
throw new Error("Code is required for eval_server_runtime");
|
|
1842
2324
|
}
|
|
@@ -1846,7 +2328,7 @@ ${code}`
|
|
|
1846
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.",
|
|
1847
2329
|
userCode: code
|
|
1848
2330
|
});
|
|
1849
|
-
const response = await this.
|
|
2331
|
+
const response = await this._callSingle("/api/execute-luau", { code: wrapper }, "server", instance_id);
|
|
1850
2332
|
return {
|
|
1851
2333
|
content: [
|
|
1852
2334
|
{
|
|
@@ -1856,7 +2338,7 @@ ${code}`
|
|
|
1856
2338
|
]
|
|
1857
2339
|
};
|
|
1858
2340
|
}
|
|
1859
|
-
async evalClientRuntime(code, target) {
|
|
2341
|
+
async evalClientRuntime(code, target, instance_id) {
|
|
1860
2342
|
if (!code) {
|
|
1861
2343
|
throw new Error("Code is required for eval_client_runtime");
|
|
1862
2344
|
}
|
|
@@ -1870,7 +2352,7 @@ ${code}`
|
|
|
1870
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.",
|
|
1871
2353
|
userCode: code
|
|
1872
2354
|
});
|
|
1873
|
-
const response = await this.
|
|
2355
|
+
const response = await this._callSingle("/api/execute-luau", { code: wrapper }, clientTarget, instance_id);
|
|
1874
2356
|
return {
|
|
1875
2357
|
content: [
|
|
1876
2358
|
{
|
|
@@ -1880,7 +2362,7 @@ ${code}`
|
|
|
1880
2362
|
]
|
|
1881
2363
|
};
|
|
1882
2364
|
}
|
|
1883
|
-
async getRuntimeLogs(target, since, tail, filter) {
|
|
2365
|
+
async getRuntimeLogs(target, since, tail, filter, instance_id) {
|
|
1884
2366
|
const tgt = target ?? "all";
|
|
1885
2367
|
const data = {};
|
|
1886
2368
|
if (since !== void 0)
|
|
@@ -1889,16 +2371,26 @@ ${code}`
|
|
|
1889
2371
|
data.tail = tail;
|
|
1890
2372
|
if (filter !== void 0)
|
|
1891
2373
|
data.filter = filter;
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
+
}
|
|
1894
2386
|
return {
|
|
1895
2387
|
content: [{ type: "text", text: JSON.stringify(response) }]
|
|
1896
2388
|
};
|
|
1897
2389
|
}
|
|
1898
|
-
const targets =
|
|
2390
|
+
const targets = resolved.targets.filter((t) => t.targetRole !== "edit-proxy");
|
|
1899
2391
|
const responses = await Promise.allSettled(targets.map(async (t) => {
|
|
1900
|
-
const r = await this.client.request("/api/get-runtime-logs", data, t);
|
|
1901
|
-
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 };
|
|
1902
2394
|
}));
|
|
1903
2395
|
const merged = [];
|
|
1904
2396
|
const perPeerNextSince = {};
|
|
@@ -1944,7 +2436,7 @@ ${code}`
|
|
|
1944
2436
|
content: [{ type: "text", text: JSON.stringify(body) }]
|
|
1945
2437
|
};
|
|
1946
2438
|
}
|
|
1947
|
-
async startPlaytest(mode, numPlayers) {
|
|
2439
|
+
async startPlaytest(mode, numPlayers, instance_id) {
|
|
1948
2440
|
if (mode !== "play" && mode !== "run") {
|
|
1949
2441
|
throw new Error('mode must be "play" or "run"');
|
|
1950
2442
|
}
|
|
@@ -1952,7 +2444,7 @@ ${code}`
|
|
|
1952
2444
|
if (numPlayers !== void 0) {
|
|
1953
2445
|
data.numPlayers = numPlayers;
|
|
1954
2446
|
}
|
|
1955
|
-
const response = await this.
|
|
2447
|
+
const response = await this._callSingle("/api/start-playtest", data, void 0, instance_id);
|
|
1956
2448
|
return {
|
|
1957
2449
|
content: [
|
|
1958
2450
|
{
|
|
@@ -1962,14 +2454,14 @@ ${code}`
|
|
|
1962
2454
|
]
|
|
1963
2455
|
};
|
|
1964
2456
|
}
|
|
1965
|
-
async stopPlaytest() {
|
|
1966
|
-
const response = await this.
|
|
2457
|
+
async stopPlaytest(instance_id) {
|
|
2458
|
+
const response = await this._callSingle("/api/stop-playtest", {}, "edit", instance_id);
|
|
1967
2459
|
return {
|
|
1968
2460
|
content: [{ type: "text", text: JSON.stringify(response) }]
|
|
1969
2461
|
};
|
|
1970
2462
|
}
|
|
1971
|
-
async getPlaytestOutput(target) {
|
|
1972
|
-
const response = await this.
|
|
2463
|
+
async getPlaytestOutput(target, instance_id) {
|
|
2464
|
+
const response = await this._callSingle("/api/get-playtest-output", {}, target || "edit", instance_id);
|
|
1973
2465
|
return {
|
|
1974
2466
|
content: [
|
|
1975
2467
|
{
|
|
@@ -1980,7 +2472,7 @@ ${code}`
|
|
|
1980
2472
|
};
|
|
1981
2473
|
}
|
|
1982
2474
|
async getConnectedInstances() {
|
|
1983
|
-
const instances = this.bridge.
|
|
2475
|
+
const instances = this.bridge.getPublicInstances();
|
|
1984
2476
|
return {
|
|
1985
2477
|
content: [
|
|
1986
2478
|
{
|
|
@@ -1990,8 +2482,8 @@ ${code}`
|
|
|
1990
2482
|
]
|
|
1991
2483
|
};
|
|
1992
2484
|
}
|
|
1993
|
-
async undo() {
|
|
1994
|
-
const response = await this.
|
|
2485
|
+
async undo(instance_id) {
|
|
2486
|
+
const response = await this._callSingle("/api/undo", {}, void 0, instance_id);
|
|
1995
2487
|
return {
|
|
1996
2488
|
content: [
|
|
1997
2489
|
{
|
|
@@ -2001,8 +2493,8 @@ ${code}`
|
|
|
2001
2493
|
]
|
|
2002
2494
|
};
|
|
2003
2495
|
}
|
|
2004
|
-
async redo() {
|
|
2005
|
-
const response = await this.
|
|
2496
|
+
async redo(instance_id) {
|
|
2497
|
+
const response = await this._callSingle("/api/redo", {}, void 0, instance_id);
|
|
2006
2498
|
return {
|
|
2007
2499
|
content: [
|
|
2008
2500
|
{
|
|
@@ -2088,15 +2580,15 @@ ${code}`
|
|
|
2088
2580
|
_RobloxStudioTools._cachedLibraryPath = result;
|
|
2089
2581
|
return result;
|
|
2090
2582
|
}
|
|
2091
|
-
async exportBuild(instancePath, outputId, style = "misc") {
|
|
2583
|
+
async exportBuild(instancePath, outputId, style = "misc", instance_id) {
|
|
2092
2584
|
if (!instancePath) {
|
|
2093
2585
|
throw new Error("Instance path is required for export_build");
|
|
2094
2586
|
}
|
|
2095
|
-
const response = await this.
|
|
2587
|
+
const response = await this._callSingle("/api/export-build", {
|
|
2096
2588
|
instancePath,
|
|
2097
2589
|
outputId,
|
|
2098
2590
|
style
|
|
2099
|
-
});
|
|
2591
|
+
}, void 0, instance_id);
|
|
2100
2592
|
if (response && response.success && response.buildData) {
|
|
2101
2593
|
const buildData = response.buildData;
|
|
2102
2594
|
const buildId = buildData.id || `${style}/exported`;
|
|
@@ -2284,7 +2776,7 @@ ${code}`
|
|
|
2284
2776
|
]
|
|
2285
2777
|
};
|
|
2286
2778
|
}
|
|
2287
|
-
async importBuild(buildData, targetPath, position) {
|
|
2779
|
+
async importBuild(buildData, targetPath, position, instance_id) {
|
|
2288
2780
|
if (!buildData || !targetPath) {
|
|
2289
2781
|
throw new Error("buildData (or library ID string) and targetPath are required for import_build");
|
|
2290
2782
|
}
|
|
@@ -2304,11 +2796,11 @@ ${code}`
|
|
|
2304
2796
|
} else {
|
|
2305
2797
|
resolved = buildData;
|
|
2306
2798
|
}
|
|
2307
|
-
const response = await this.
|
|
2799
|
+
const response = await this._callSingle("/api/import-build", {
|
|
2308
2800
|
buildData: resolved,
|
|
2309
2801
|
targetPath,
|
|
2310
2802
|
position
|
|
2311
|
-
});
|
|
2803
|
+
}, void 0, instance_id);
|
|
2312
2804
|
return {
|
|
2313
2805
|
content: [
|
|
2314
2806
|
{
|
|
@@ -2350,11 +2842,11 @@ ${code}`
|
|
|
2350
2842
|
]
|
|
2351
2843
|
};
|
|
2352
2844
|
}
|
|
2353
|
-
async searchMaterials(query, maxResults) {
|
|
2354
|
-
const response = await this.
|
|
2845
|
+
async searchMaterials(query, maxResults, instance_id) {
|
|
2846
|
+
const response = await this._callSingle("/api/search-materials", {
|
|
2355
2847
|
query: query ?? "",
|
|
2356
2848
|
maxResults: maxResults ?? 50
|
|
2357
|
-
});
|
|
2849
|
+
}, void 0, instance_id);
|
|
2358
2850
|
return {
|
|
2359
2851
|
content: [
|
|
2360
2852
|
{
|
|
@@ -2394,7 +2886,7 @@ ${code}`
|
|
|
2394
2886
|
]
|
|
2395
2887
|
};
|
|
2396
2888
|
}
|
|
2397
|
-
async importScene(sceneData, targetPath = "game.Workspace") {
|
|
2889
|
+
async importScene(sceneData, targetPath = "game.Workspace", instance_id) {
|
|
2398
2890
|
if (!sceneData) {
|
|
2399
2891
|
throw new Error("sceneData is required for import_scene");
|
|
2400
2892
|
}
|
|
@@ -2484,10 +2976,10 @@ ${code}`
|
|
|
2484
2976
|
if (expandedBuilds.length === 0) {
|
|
2485
2977
|
throw new Error("No builds to import - check model references and library");
|
|
2486
2978
|
}
|
|
2487
|
-
const response = await this.
|
|
2979
|
+
const response = await this._callSingle("/api/import-scene", {
|
|
2488
2980
|
expandedBuilds,
|
|
2489
2981
|
targetPath
|
|
2490
|
-
});
|
|
2982
|
+
}, void 0, instance_id);
|
|
2491
2983
|
return {
|
|
2492
2984
|
content: [
|
|
2493
2985
|
{
|
|
@@ -2578,15 +3070,15 @@ ${code}`
|
|
|
2578
3070
|
}]
|
|
2579
3071
|
};
|
|
2580
3072
|
}
|
|
2581
|
-
async insertAsset(assetId, parentPath, position) {
|
|
3073
|
+
async insertAsset(assetId, parentPath, position, instance_id) {
|
|
2582
3074
|
if (!assetId) {
|
|
2583
3075
|
throw new Error("Asset ID is required for insert_asset");
|
|
2584
3076
|
}
|
|
2585
|
-
const response = await this.
|
|
3077
|
+
const response = await this._callSingle("/api/insert-asset", {
|
|
2586
3078
|
assetId,
|
|
2587
3079
|
parentPath: parentPath || "game.Workspace",
|
|
2588
3080
|
position
|
|
2589
|
-
});
|
|
3081
|
+
}, void 0, instance_id);
|
|
2590
3082
|
return {
|
|
2591
3083
|
content: [{
|
|
2592
3084
|
type: "text",
|
|
@@ -2594,15 +3086,15 @@ ${code}`
|
|
|
2594
3086
|
}]
|
|
2595
3087
|
};
|
|
2596
3088
|
}
|
|
2597
|
-
async previewAsset(assetId, includeProperties, maxDepth) {
|
|
3089
|
+
async previewAsset(assetId, includeProperties, maxDepth, instance_id) {
|
|
2598
3090
|
if (!assetId) {
|
|
2599
3091
|
throw new Error("Asset ID is required for preview_asset");
|
|
2600
3092
|
}
|
|
2601
|
-
const response = await this.
|
|
3093
|
+
const response = await this._callSingle("/api/preview-asset", {
|
|
2602
3094
|
assetId,
|
|
2603
3095
|
includeProperties: includeProperties ?? true,
|
|
2604
3096
|
maxDepth: maxDepth ?? 10
|
|
2605
|
-
});
|
|
3097
|
+
}, void 0, instance_id);
|
|
2606
3098
|
return {
|
|
2607
3099
|
content: [{
|
|
2608
3100
|
type: "text",
|
|
@@ -2624,7 +3116,7 @@ ${code}`
|
|
|
2624
3116
|
return id
|
|
2625
3117
|
`;
|
|
2626
3118
|
try {
|
|
2627
|
-
const response = await this.
|
|
3119
|
+
const response = await this._callSingle("/api/execute-luau", { code }, "edit", void 0);
|
|
2628
3120
|
const returnValue = response?.returnValue;
|
|
2629
3121
|
if (returnValue !== void 0 && returnValue !== null && /^\d+$/.test(String(returnValue))) {
|
|
2630
3122
|
return String(returnValue);
|
|
@@ -2699,17 +3191,17 @@ ${code}`
|
|
|
2699
3191
|
}]
|
|
2700
3192
|
};
|
|
2701
3193
|
}
|
|
2702
|
-
async simulateMouseInput(action, x, y, button, scrollDirection, target) {
|
|
3194
|
+
async simulateMouseInput(action, x, y, button, scrollDirection, target, instance_id) {
|
|
2703
3195
|
if (!action) {
|
|
2704
3196
|
throw new Error("action is required for simulate_mouse_input");
|
|
2705
3197
|
}
|
|
2706
|
-
const response = await this.
|
|
3198
|
+
const response = await this._callSingle("/api/simulate-mouse-input", {
|
|
2707
3199
|
action,
|
|
2708
3200
|
x,
|
|
2709
3201
|
y,
|
|
2710
3202
|
button,
|
|
2711
3203
|
scrollDirection
|
|
2712
|
-
}, target || "edit");
|
|
3204
|
+
}, target || "edit", instance_id);
|
|
2713
3205
|
return {
|
|
2714
3206
|
content: [{
|
|
2715
3207
|
type: "text",
|
|
@@ -2717,15 +3209,15 @@ ${code}`
|
|
|
2717
3209
|
}]
|
|
2718
3210
|
};
|
|
2719
3211
|
}
|
|
2720
|
-
async simulateKeyboardInput(keyCode, action, duration, target) {
|
|
3212
|
+
async simulateKeyboardInput(keyCode, action, duration, target, instance_id) {
|
|
2721
3213
|
if (!keyCode) {
|
|
2722
3214
|
throw new Error("keyCode is required for simulate_keyboard_input");
|
|
2723
3215
|
}
|
|
2724
|
-
const response = await this.
|
|
3216
|
+
const response = await this._callSingle("/api/simulate-keyboard-input", {
|
|
2725
3217
|
keyCode,
|
|
2726
3218
|
action,
|
|
2727
3219
|
duration
|
|
2728
|
-
}, target || "edit");
|
|
3220
|
+
}, target || "edit", instance_id);
|
|
2729
3221
|
return {
|
|
2730
3222
|
content: [{
|
|
2731
3223
|
type: "text",
|
|
@@ -2733,16 +3225,16 @@ ${code}`
|
|
|
2733
3225
|
}]
|
|
2734
3226
|
};
|
|
2735
3227
|
}
|
|
2736
|
-
async characterNavigation(position, instancePath, waitForCompletion, timeout, target) {
|
|
3228
|
+
async characterNavigation(position, instancePath, waitForCompletion, timeout, target, instance_id) {
|
|
2737
3229
|
if (!position && !instancePath) {
|
|
2738
3230
|
throw new Error("Either position or instancePath is required for character_navigation");
|
|
2739
3231
|
}
|
|
2740
|
-
const response = await this.
|
|
3232
|
+
const response = await this._callSingle("/api/character-navigation", {
|
|
2741
3233
|
position,
|
|
2742
3234
|
instancePath,
|
|
2743
3235
|
waitForCompletion,
|
|
2744
3236
|
timeout
|
|
2745
|
-
}, target || "edit");
|
|
3237
|
+
}, target || "edit", instance_id);
|
|
2746
3238
|
return {
|
|
2747
3239
|
content: [{
|
|
2748
3240
|
type: "text",
|
|
@@ -2750,50 +3242,50 @@ ${code}`
|
|
|
2750
3242
|
}]
|
|
2751
3243
|
};
|
|
2752
3244
|
}
|
|
2753
|
-
async cloneObject(instancePath, targetParentPath) {
|
|
3245
|
+
async cloneObject(instancePath, targetParentPath, instance_id) {
|
|
2754
3246
|
if (!instancePath || !targetParentPath) {
|
|
2755
3247
|
throw new Error("instancePath and targetParentPath are required for clone_object");
|
|
2756
3248
|
}
|
|
2757
|
-
const response = await this.
|
|
3249
|
+
const response = await this._callSingle("/api/clone-object", { instancePath, targetParentPath }, void 0, instance_id);
|
|
2758
3250
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2759
3251
|
}
|
|
2760
|
-
async getDescendants(instancePath, maxDepth, classFilter) {
|
|
3252
|
+
async getDescendants(instancePath, maxDepth, classFilter, instance_id) {
|
|
2761
3253
|
if (!instancePath) {
|
|
2762
3254
|
throw new Error("instancePath is required for get_descendants");
|
|
2763
3255
|
}
|
|
2764
|
-
const response = await this.
|
|
3256
|
+
const response = await this._callSingle("/api/get-descendants", { instancePath, maxDepth, classFilter }, void 0, instance_id);
|
|
2765
3257
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2766
3258
|
}
|
|
2767
|
-
async compareInstances(instancePathA, instancePathB) {
|
|
3259
|
+
async compareInstances(instancePathA, instancePathB, instance_id) {
|
|
2768
3260
|
if (!instancePathA || !instancePathB) {
|
|
2769
3261
|
throw new Error("instancePathA and instancePathB are required for compare_instances");
|
|
2770
3262
|
}
|
|
2771
|
-
const response = await this.
|
|
3263
|
+
const response = await this._callSingle("/api/compare-instances", { instancePathA, instancePathB }, void 0, instance_id);
|
|
2772
3264
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2773
3265
|
}
|
|
2774
|
-
async getOutputLog(maxEntries, messageType) {
|
|
2775
|
-
const response = await this.
|
|
3266
|
+
async getOutputLog(maxEntries, messageType, instance_id) {
|
|
3267
|
+
const response = await this._callSingle("/api/get-output-log", { maxEntries, messageType }, void 0, instance_id);
|
|
2776
3268
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2777
3269
|
}
|
|
2778
|
-
async bulkSetAttributes(instancePath, attributes) {
|
|
3270
|
+
async bulkSetAttributes(instancePath, attributes, instance_id) {
|
|
2779
3271
|
if (!instancePath || !attributes) {
|
|
2780
3272
|
throw new Error("instancePath and attributes are required for bulk_set_attributes");
|
|
2781
3273
|
}
|
|
2782
|
-
const response = await this.
|
|
3274
|
+
const response = await this._callSingle("/api/bulk-set-attributes", { instancePath, attributes }, void 0, instance_id);
|
|
2783
3275
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2784
3276
|
}
|
|
2785
|
-
async findAndReplaceInScripts(pattern, replacement, options) {
|
|
3277
|
+
async findAndReplaceInScripts(pattern, replacement, options, instance_id) {
|
|
2786
3278
|
if (!pattern) {
|
|
2787
3279
|
throw new Error("pattern is required for find_and_replace_in_scripts");
|
|
2788
3280
|
}
|
|
2789
3281
|
if (replacement === void 0 || replacement === null) {
|
|
2790
3282
|
throw new Error("replacement is required for find_and_replace_in_scripts");
|
|
2791
3283
|
}
|
|
2792
|
-
const response = await this.
|
|
3284
|
+
const response = await this._callSingle("/api/find-and-replace-in-scripts", {
|
|
2793
3285
|
pattern,
|
|
2794
3286
|
replacement,
|
|
2795
3287
|
...options
|
|
2796
|
-
});
|
|
3288
|
+
}, void 0, instance_id);
|
|
2797
3289
|
return {
|
|
2798
3290
|
content: [{
|
|
2799
3291
|
type: "text",
|
|
@@ -2801,24 +3293,27 @@ ${code}`
|
|
|
2801
3293
|
}]
|
|
2802
3294
|
};
|
|
2803
3295
|
}
|
|
2804
|
-
async getMemoryBreakdown(target, tags) {
|
|
3296
|
+
async getMemoryBreakdown(target, tags, instance_id) {
|
|
2805
3297
|
const tgt = target ?? "all";
|
|
2806
3298
|
const data = {};
|
|
2807
3299
|
if (tags !== void 0)
|
|
2808
3300
|
data.tags = tags;
|
|
2809
|
-
|
|
2810
|
-
|
|
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);
|
|
2811
3306
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2812
3307
|
}
|
|
2813
|
-
const targets =
|
|
3308
|
+
const targets = resolved.targets.filter((t) => t.targetRole !== "edit-proxy");
|
|
2814
3309
|
const responses = await Promise.allSettled(targets.map(async (t) => ({
|
|
2815
|
-
peer: t,
|
|
2816
|
-
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)
|
|
2817
3312
|
})));
|
|
2818
3313
|
const body = {};
|
|
2819
3314
|
for (let i = 0; i < responses.length; i++) {
|
|
2820
3315
|
const r = responses[i];
|
|
2821
|
-
const peer = targets[i];
|
|
3316
|
+
const peer = targets[i].targetRole;
|
|
2822
3317
|
if (r.status === "fulfilled") {
|
|
2823
3318
|
body[peer] = r.value.result;
|
|
2824
3319
|
} else {
|
|
@@ -2827,7 +3322,7 @@ ${code}`
|
|
|
2827
3322
|
}
|
|
2828
3323
|
return { content: [{ type: "text", text: JSON.stringify(body) }] };
|
|
2829
3324
|
}
|
|
2830
|
-
async exportRbxm(instancePaths, outputPath, target) {
|
|
3325
|
+
async exportRbxm(instancePaths, outputPath, target, instance_id) {
|
|
2831
3326
|
if (!Array.isArray(instancePaths) || instancePaths.length === 0) {
|
|
2832
3327
|
throw new Error("instance_paths must be a non-empty array for export_rbxm");
|
|
2833
3328
|
}
|
|
@@ -2838,7 +3333,7 @@ ${code}`
|
|
|
2838
3333
|
if (tgt !== "edit" && tgt !== "server") {
|
|
2839
3334
|
throw new Error(`export_rbxm target must be "edit" or "server" (got: ${tgt})`);
|
|
2840
3335
|
}
|
|
2841
|
-
const response = await this.
|
|
3336
|
+
const response = await this._callSingle("/api/export-rbxm", { instance_paths: instancePaths }, tgt, instance_id);
|
|
2842
3337
|
if (response.error) {
|
|
2843
3338
|
return { content: [{ type: "text", text: JSON.stringify({ error: response.error }) }] };
|
|
2844
3339
|
}
|
|
@@ -2864,7 +3359,7 @@ ${code}`
|
|
|
2864
3359
|
}]
|
|
2865
3360
|
};
|
|
2866
3361
|
}
|
|
2867
|
-
async importRbxm(source, parentPath, target) {
|
|
3362
|
+
async importRbxm(source, parentPath, target, instance_id) {
|
|
2868
3363
|
if (!source || typeof source !== "object") {
|
|
2869
3364
|
throw new Error("source is required for import_rbxm");
|
|
2870
3365
|
}
|
|
@@ -2927,15 +3422,15 @@ ${code}`
|
|
|
2927
3422
|
}
|
|
2928
3423
|
sourceLabel = `base64(${bytes.length}B)`;
|
|
2929
3424
|
}
|
|
2930
|
-
const response = await this.
|
|
3425
|
+
const response = await this._callSingle("/api/import-rbxm", {
|
|
2931
3426
|
base64: bytes.toString("base64"),
|
|
2932
3427
|
parent_path: parentPath,
|
|
2933
3428
|
source_label: sourceLabel
|
|
2934
|
-
}, tgt);
|
|
3429
|
+
}, tgt, instance_id);
|
|
2935
3430
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2936
3431
|
}
|
|
2937
|
-
async captureScreenshot() {
|
|
2938
|
-
const response = await this.
|
|
3432
|
+
async captureScreenshot(instance_id) {
|
|
3433
|
+
const response = await this._callSingle("/api/capture-screenshot", {}, void 0, instance_id);
|
|
2939
3434
|
if (response.error) {
|
|
2940
3435
|
return {
|
|
2941
3436
|
content: [{
|
|
@@ -2957,150 +3452,6 @@ ${code}`
|
|
|
2957
3452
|
}
|
|
2958
3453
|
});
|
|
2959
3454
|
|
|
2960
|
-
// ../core/dist/bridge-service.js
|
|
2961
|
-
import { v4 as uuidv4 } from "uuid";
|
|
2962
|
-
var STALE_INSTANCE_MS, BridgeService;
|
|
2963
|
-
var init_bridge_service = __esm({
|
|
2964
|
-
"../core/dist/bridge-service.js"() {
|
|
2965
|
-
"use strict";
|
|
2966
|
-
STALE_INSTANCE_MS = 3e4;
|
|
2967
|
-
BridgeService = class {
|
|
2968
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
2969
|
-
instances = /* @__PURE__ */ new Map();
|
|
2970
|
-
requestTimeout = 3e4;
|
|
2971
|
-
registerInstance(instanceId, role) {
|
|
2972
|
-
let assignedRole = role;
|
|
2973
|
-
if (role === "client") {
|
|
2974
|
-
const used = /* @__PURE__ */ new Set();
|
|
2975
|
-
for (const inst of this.instances.values()) {
|
|
2976
|
-
const match = inst.role.match(/^client-(\d+)$/);
|
|
2977
|
-
if (match)
|
|
2978
|
-
used.add(Number(match[1]));
|
|
2979
|
-
}
|
|
2980
|
-
let idx = 1;
|
|
2981
|
-
while (used.has(idx))
|
|
2982
|
-
idx++;
|
|
2983
|
-
assignedRole = `client-${idx}`;
|
|
2984
|
-
}
|
|
2985
|
-
this.instances.set(instanceId, {
|
|
2986
|
-
instanceId,
|
|
2987
|
-
role: assignedRole,
|
|
2988
|
-
lastActivity: Date.now(),
|
|
2989
|
-
connectedAt: Date.now()
|
|
2990
|
-
});
|
|
2991
|
-
return assignedRole;
|
|
2992
|
-
}
|
|
2993
|
-
unregisterInstance(instanceId) {
|
|
2994
|
-
this.instances.delete(instanceId);
|
|
2995
|
-
for (const [id, req] of this.pendingRequests.entries()) {
|
|
2996
|
-
const targetRole = req.target;
|
|
2997
|
-
const hasHandler = Array.from(this.instances.values()).some((i) => i.role === targetRole);
|
|
2998
|
-
if (!hasHandler) {
|
|
2999
|
-
clearTimeout(req.timeoutId);
|
|
3000
|
-
this.pendingRequests.delete(id);
|
|
3001
|
-
req.reject(new Error(`Target instance "${targetRole}" disconnected`));
|
|
3002
|
-
}
|
|
3003
|
-
}
|
|
3004
|
-
}
|
|
3005
|
-
getInstances() {
|
|
3006
|
-
return Array.from(this.instances.values());
|
|
3007
|
-
}
|
|
3008
|
-
getPendingRequestCount() {
|
|
3009
|
-
return this.pendingRequests.size;
|
|
3010
|
-
}
|
|
3011
|
-
updateInstanceActivity(instanceId) {
|
|
3012
|
-
const inst = this.instances.get(instanceId);
|
|
3013
|
-
if (inst) {
|
|
3014
|
-
inst.lastActivity = Date.now();
|
|
3015
|
-
}
|
|
3016
|
-
}
|
|
3017
|
-
cleanupStaleInstances() {
|
|
3018
|
-
const now = Date.now();
|
|
3019
|
-
for (const [id, inst] of this.instances.entries()) {
|
|
3020
|
-
if (now - inst.lastActivity > STALE_INSTANCE_MS) {
|
|
3021
|
-
this.unregisterInstance(id);
|
|
3022
|
-
}
|
|
3023
|
-
}
|
|
3024
|
-
}
|
|
3025
|
-
async sendRequest(endpoint, data, target = "edit") {
|
|
3026
|
-
const requestId = uuidv4();
|
|
3027
|
-
return new Promise((resolve2, reject) => {
|
|
3028
|
-
const timeoutId = setTimeout(() => {
|
|
3029
|
-
if (this.pendingRequests.has(requestId)) {
|
|
3030
|
-
this.pendingRequests.delete(requestId);
|
|
3031
|
-
reject(new Error("Request timeout"));
|
|
3032
|
-
}
|
|
3033
|
-
}, this.requestTimeout);
|
|
3034
|
-
const request = {
|
|
3035
|
-
id: requestId,
|
|
3036
|
-
endpoint,
|
|
3037
|
-
data,
|
|
3038
|
-
target,
|
|
3039
|
-
timestamp: Date.now(),
|
|
3040
|
-
resolve: resolve2,
|
|
3041
|
-
reject,
|
|
3042
|
-
timeoutId
|
|
3043
|
-
};
|
|
3044
|
-
this.pendingRequests.set(requestId, request);
|
|
3045
|
-
});
|
|
3046
|
-
}
|
|
3047
|
-
getPendingRequest(callerRole = "edit") {
|
|
3048
|
-
let oldestRequest = null;
|
|
3049
|
-
for (const request of this.pendingRequests.values()) {
|
|
3050
|
-
if (request.target !== callerRole)
|
|
3051
|
-
continue;
|
|
3052
|
-
if (!oldestRequest || request.timestamp < oldestRequest.timestamp) {
|
|
3053
|
-
oldestRequest = request;
|
|
3054
|
-
}
|
|
3055
|
-
}
|
|
3056
|
-
if (oldestRequest) {
|
|
3057
|
-
return {
|
|
3058
|
-
requestId: oldestRequest.id,
|
|
3059
|
-
request: {
|
|
3060
|
-
endpoint: oldestRequest.endpoint,
|
|
3061
|
-
data: oldestRequest.data
|
|
3062
|
-
}
|
|
3063
|
-
};
|
|
3064
|
-
}
|
|
3065
|
-
return null;
|
|
3066
|
-
}
|
|
3067
|
-
resolveRequest(requestId, response) {
|
|
3068
|
-
const request = this.pendingRequests.get(requestId);
|
|
3069
|
-
if (request) {
|
|
3070
|
-
clearTimeout(request.timeoutId);
|
|
3071
|
-
this.pendingRequests.delete(requestId);
|
|
3072
|
-
request.resolve(response);
|
|
3073
|
-
}
|
|
3074
|
-
}
|
|
3075
|
-
rejectRequest(requestId, error) {
|
|
3076
|
-
const request = this.pendingRequests.get(requestId);
|
|
3077
|
-
if (request) {
|
|
3078
|
-
clearTimeout(request.timeoutId);
|
|
3079
|
-
this.pendingRequests.delete(requestId);
|
|
3080
|
-
request.reject(error);
|
|
3081
|
-
}
|
|
3082
|
-
}
|
|
3083
|
-
cleanupOldRequests() {
|
|
3084
|
-
const now = Date.now();
|
|
3085
|
-
for (const [id, request] of this.pendingRequests.entries()) {
|
|
3086
|
-
if (now - request.timestamp > this.requestTimeout) {
|
|
3087
|
-
clearTimeout(request.timeoutId);
|
|
3088
|
-
this.pendingRequests.delete(id);
|
|
3089
|
-
request.reject(new Error("Request timeout"));
|
|
3090
|
-
}
|
|
3091
|
-
}
|
|
3092
|
-
}
|
|
3093
|
-
clearAllPendingRequests() {
|
|
3094
|
-
for (const [, request] of this.pendingRequests.entries()) {
|
|
3095
|
-
clearTimeout(request.timeoutId);
|
|
3096
|
-
request.reject(new Error("Connection closed"));
|
|
3097
|
-
}
|
|
3098
|
-
this.pendingRequests.clear();
|
|
3099
|
-
}
|
|
3100
|
-
};
|
|
3101
|
-
}
|
|
3102
|
-
});
|
|
3103
|
-
|
|
3104
3455
|
// ../core/dist/proxy-bridge-service.js
|
|
3105
3456
|
import { v4 as uuidv42 } from "uuid";
|
|
3106
3457
|
var ProxyBridgeService;
|
|
@@ -3145,14 +3496,20 @@ var init_proxy_bridge_service = __esm({
|
|
|
3145
3496
|
this.refreshTimer = void 0;
|
|
3146
3497
|
}
|
|
3147
3498
|
}
|
|
3148
|
-
async sendRequest(endpoint, data,
|
|
3499
|
+
async sendRequest(endpoint, data, targetInstanceId, targetRole) {
|
|
3149
3500
|
const controller = new AbortController();
|
|
3150
3501
|
const timeoutId = setTimeout(() => controller.abort(), this.proxyRequestTimeout);
|
|
3151
3502
|
try {
|
|
3152
3503
|
const response = await fetch(`${this.primaryBaseUrl}/proxy`, {
|
|
3153
3504
|
method: "POST",
|
|
3154
3505
|
headers: { "Content-Type": "application/json" },
|
|
3155
|
-
body: JSON.stringify({
|
|
3506
|
+
body: JSON.stringify({
|
|
3507
|
+
endpoint,
|
|
3508
|
+
data,
|
|
3509
|
+
targetInstanceId,
|
|
3510
|
+
targetRole,
|
|
3511
|
+
proxyInstanceId: this.proxyInstanceId
|
|
3512
|
+
}),
|
|
3156
3513
|
signal: controller.signal
|
|
3157
3514
|
});
|
|
3158
3515
|
clearTimeout(timeoutId);
|
|
@@ -3236,6 +3593,19 @@ var init_server = __esm({
|
|
|
3236
3593
|
try {
|
|
3237
3594
|
return await handler(this.tools, args ?? {});
|
|
3238
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
|
+
}
|
|
3239
3609
|
if (error instanceof McpError2)
|
|
3240
3610
|
throw error;
|
|
3241
3611
|
throw new McpError2(ErrorCode2.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -3377,6 +3747,10 @@ var init_definitions = __esm({
|
|
|
3377
3747
|
path: {
|
|
3378
3748
|
type: "string",
|
|
3379
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."
|
|
3380
3754
|
}
|
|
3381
3755
|
}
|
|
3382
3756
|
}
|
|
@@ -3396,6 +3770,10 @@ var init_definitions = __esm({
|
|
|
3396
3770
|
type: "string",
|
|
3397
3771
|
enum: ["name", "type", "content"],
|
|
3398
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."
|
|
3399
3777
|
}
|
|
3400
3778
|
},
|
|
3401
3779
|
required: ["query"]
|
|
@@ -3408,7 +3786,12 @@ var init_definitions = __esm({
|
|
|
3408
3786
|
description: "Get place ID, name, and game settings",
|
|
3409
3787
|
inputSchema: {
|
|
3410
3788
|
type: "object",
|
|
3411
|
-
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
|
+
}
|
|
3412
3795
|
}
|
|
3413
3796
|
},
|
|
3414
3797
|
{
|
|
@@ -3421,6 +3804,10 @@ var init_definitions = __esm({
|
|
|
3421
3804
|
serviceName: {
|
|
3422
3805
|
type: "string",
|
|
3423
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."
|
|
3424
3811
|
}
|
|
3425
3812
|
}
|
|
3426
3813
|
}
|
|
@@ -3444,6 +3831,10 @@ var init_definitions = __esm({
|
|
|
3444
3831
|
propertyName: {
|
|
3445
3832
|
type: "string",
|
|
3446
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."
|
|
3447
3838
|
}
|
|
3448
3839
|
},
|
|
3449
3840
|
required: ["query"]
|
|
@@ -3464,6 +3855,10 @@ var init_definitions = __esm({
|
|
|
3464
3855
|
excludeSource: {
|
|
3465
3856
|
type: "boolean",
|
|
3466
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."
|
|
3467
3862
|
}
|
|
3468
3863
|
},
|
|
3469
3864
|
required: ["instancePath"]
|
|
@@ -3479,6 +3874,10 @@ var init_definitions = __esm({
|
|
|
3479
3874
|
instancePath: {
|
|
3480
3875
|
type: "string",
|
|
3481
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."
|
|
3482
3881
|
}
|
|
3483
3882
|
},
|
|
3484
3883
|
required: ["instancePath"]
|
|
@@ -3498,6 +3897,10 @@ var init_definitions = __esm({
|
|
|
3498
3897
|
propertyValue: {
|
|
3499
3898
|
type: "string",
|
|
3500
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."
|
|
3501
3904
|
}
|
|
3502
3905
|
},
|
|
3503
3906
|
required: ["propertyName", "propertyValue"]
|
|
@@ -3513,6 +3916,10 @@ var init_definitions = __esm({
|
|
|
3513
3916
|
className: {
|
|
3514
3917
|
type: "string",
|
|
3515
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."
|
|
3516
3923
|
}
|
|
3517
3924
|
},
|
|
3518
3925
|
required: ["className"]
|
|
@@ -3537,6 +3944,10 @@ var init_definitions = __esm({
|
|
|
3537
3944
|
scriptsOnly: {
|
|
3538
3945
|
type: "boolean",
|
|
3539
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."
|
|
3540
3951
|
}
|
|
3541
3952
|
}
|
|
3542
3953
|
}
|
|
@@ -3559,6 +3970,10 @@ var init_definitions = __esm({
|
|
|
3559
3970
|
},
|
|
3560
3971
|
propertyValue: {
|
|
3561
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."
|
|
3562
3977
|
}
|
|
3563
3978
|
},
|
|
3564
3979
|
required: ["instancePath", "propertyName", "propertyValue"]
|
|
@@ -3582,6 +3997,10 @@ var init_definitions = __esm({
|
|
|
3582
3997
|
},
|
|
3583
3998
|
propertyValue: {
|
|
3584
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."
|
|
3585
4004
|
}
|
|
3586
4005
|
},
|
|
3587
4006
|
required: ["paths", "propertyName", "propertyValue"]
|
|
@@ -3602,6 +4021,10 @@ var init_definitions = __esm({
|
|
|
3602
4021
|
propertyName: {
|
|
3603
4022
|
type: "string",
|
|
3604
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."
|
|
3605
4028
|
}
|
|
3606
4029
|
},
|
|
3607
4030
|
required: ["paths", "propertyName"]
|
|
@@ -3621,6 +4044,10 @@ var init_definitions = __esm({
|
|
|
3621
4044
|
properties: {
|
|
3622
4045
|
type: "object",
|
|
3623
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."
|
|
3624
4051
|
}
|
|
3625
4052
|
},
|
|
3626
4053
|
required: ["instancePath", "properties"]
|
|
@@ -3649,6 +4076,10 @@ var init_definitions = __esm({
|
|
|
3649
4076
|
properties: {
|
|
3650
4077
|
type: "object",
|
|
3651
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."
|
|
3652
4083
|
}
|
|
3653
4084
|
},
|
|
3654
4085
|
required: ["className", "parent"]
|
|
@@ -3686,6 +4117,10 @@ var init_definitions = __esm({
|
|
|
3686
4117
|
required: ["className", "parent"]
|
|
3687
4118
|
},
|
|
3688
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."
|
|
3689
4124
|
}
|
|
3690
4125
|
},
|
|
3691
4126
|
required: ["objects"]
|
|
@@ -3701,6 +4136,10 @@ var init_definitions = __esm({
|
|
|
3701
4136
|
instancePath: {
|
|
3702
4137
|
type: "string",
|
|
3703
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."
|
|
3704
4143
|
}
|
|
3705
4144
|
},
|
|
3706
4145
|
required: ["instancePath"]
|
|
@@ -3754,6 +4193,10 @@ var init_definitions = __esm({
|
|
|
3754
4193
|
description: "Different parent per duplicate"
|
|
3755
4194
|
}
|
|
3756
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."
|
|
3757
4200
|
}
|
|
3758
4201
|
},
|
|
3759
4202
|
required: ["instancePath", "count"]
|
|
@@ -3816,6 +4259,10 @@ var init_definitions = __esm({
|
|
|
3816
4259
|
required: ["instancePath", "count"]
|
|
3817
4260
|
},
|
|
3818
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."
|
|
3819
4266
|
}
|
|
3820
4267
|
},
|
|
3821
4268
|
required: ["duplications"]
|
|
@@ -3841,6 +4288,10 @@ var init_definitions = __esm({
|
|
|
3841
4288
|
endLine: {
|
|
3842
4289
|
type: "number",
|
|
3843
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."
|
|
3844
4295
|
}
|
|
3845
4296
|
},
|
|
3846
4297
|
required: ["instancePath"]
|
|
@@ -3860,6 +4311,10 @@ var init_definitions = __esm({
|
|
|
3860
4311
|
source: {
|
|
3861
4312
|
type: "string",
|
|
3862
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."
|
|
3863
4318
|
}
|
|
3864
4319
|
},
|
|
3865
4320
|
required: ["instancePath", "source"]
|
|
@@ -3887,6 +4342,10 @@ var init_definitions = __esm({
|
|
|
3887
4342
|
startLine: {
|
|
3888
4343
|
type: "number",
|
|
3889
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."
|
|
3890
4349
|
}
|
|
3891
4350
|
},
|
|
3892
4351
|
required: ["instancePath", "old_string", "new_string"]
|
|
@@ -3910,6 +4369,10 @@ var init_definitions = __esm({
|
|
|
3910
4369
|
newContent: {
|
|
3911
4370
|
type: "string",
|
|
3912
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."
|
|
3913
4376
|
}
|
|
3914
4377
|
},
|
|
3915
4378
|
required: ["instancePath", "newContent"]
|
|
@@ -3933,6 +4396,10 @@ var init_definitions = __esm({
|
|
|
3933
4396
|
endLine: {
|
|
3934
4397
|
type: "number",
|
|
3935
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."
|
|
3936
4403
|
}
|
|
3937
4404
|
},
|
|
3938
4405
|
required: ["instancePath", "startLine", "endLine"]
|
|
@@ -3960,6 +4427,10 @@ var init_definitions = __esm({
|
|
|
3960
4427
|
valueType: {
|
|
3961
4428
|
type: "string",
|
|
3962
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."
|
|
3963
4434
|
}
|
|
3964
4435
|
},
|
|
3965
4436
|
required: ["instancePath", "attributeName", "attributeValue"]
|
|
@@ -3975,6 +4446,10 @@ var init_definitions = __esm({
|
|
|
3975
4446
|
instancePath: {
|
|
3976
4447
|
type: "string",
|
|
3977
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."
|
|
3978
4453
|
}
|
|
3979
4454
|
},
|
|
3980
4455
|
required: ["instancePath"]
|
|
@@ -3994,6 +4469,10 @@ var init_definitions = __esm({
|
|
|
3994
4469
|
attributeName: {
|
|
3995
4470
|
type: "string",
|
|
3996
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."
|
|
3997
4476
|
}
|
|
3998
4477
|
},
|
|
3999
4478
|
required: ["instancePath", "attributeName"]
|
|
@@ -4010,6 +4489,10 @@ var init_definitions = __esm({
|
|
|
4010
4489
|
instancePath: {
|
|
4011
4490
|
type: "string",
|
|
4012
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."
|
|
4013
4496
|
}
|
|
4014
4497
|
},
|
|
4015
4498
|
required: ["instancePath"]
|
|
@@ -4029,6 +4512,10 @@ var init_definitions = __esm({
|
|
|
4029
4512
|
tagName: {
|
|
4030
4513
|
type: "string",
|
|
4031
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."
|
|
4032
4519
|
}
|
|
4033
4520
|
},
|
|
4034
4521
|
required: ["instancePath", "tagName"]
|
|
@@ -4048,6 +4535,10 @@ var init_definitions = __esm({
|
|
|
4048
4535
|
tagName: {
|
|
4049
4536
|
type: "string",
|
|
4050
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."
|
|
4051
4542
|
}
|
|
4052
4543
|
},
|
|
4053
4544
|
required: ["instancePath", "tagName"]
|
|
@@ -4063,6 +4554,10 @@ var init_definitions = __esm({
|
|
|
4063
4554
|
tagName: {
|
|
4064
4555
|
type: "string",
|
|
4065
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."
|
|
4066
4561
|
}
|
|
4067
4562
|
},
|
|
4068
4563
|
required: ["tagName"]
|
|
@@ -4075,7 +4570,12 @@ var init_definitions = __esm({
|
|
|
4075
4570
|
description: "Get all currently selected objects",
|
|
4076
4571
|
inputSchema: {
|
|
4077
4572
|
type: "object",
|
|
4078
|
-
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
|
+
}
|
|
4079
4579
|
}
|
|
4080
4580
|
},
|
|
4081
4581
|
// === Luau Execution ===
|
|
@@ -4093,6 +4593,10 @@ var init_definitions = __esm({
|
|
|
4093
4593
|
target: {
|
|
4094
4594
|
type: "string",
|
|
4095
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."
|
|
4096
4600
|
}
|
|
4097
4601
|
},
|
|
4098
4602
|
required: ["code"]
|
|
@@ -4108,6 +4612,10 @@ var init_definitions = __esm({
|
|
|
4108
4612
|
code: {
|
|
4109
4613
|
type: "string",
|
|
4110
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."
|
|
4111
4619
|
}
|
|
4112
4620
|
},
|
|
4113
4621
|
required: ["code"]
|
|
@@ -4127,6 +4635,10 @@ var init_definitions = __esm({
|
|
|
4127
4635
|
target: {
|
|
4128
4636
|
type: "string",
|
|
4129
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."
|
|
4130
4642
|
}
|
|
4131
4643
|
},
|
|
4132
4644
|
required: ["code"]
|
|
@@ -4176,6 +4688,10 @@ var init_definitions = __esm({
|
|
|
4176
4688
|
type: "string",
|
|
4177
4689
|
enum: ["Script", "LocalScript", "ModuleScript"],
|
|
4178
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."
|
|
4179
4695
|
}
|
|
4180
4696
|
},
|
|
4181
4697
|
required: ["pattern"]
|
|
@@ -4197,6 +4713,10 @@ var init_definitions = __esm({
|
|
|
4197
4713
|
numPlayers: {
|
|
4198
4714
|
type: "number",
|
|
4199
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."
|
|
4200
4720
|
}
|
|
4201
4721
|
},
|
|
4202
4722
|
required: ["mode"]
|
|
@@ -4208,7 +4728,12 @@ var init_definitions = __esm({
|
|
|
4208
4728
|
description: "Stop playtest and return all captured output.",
|
|
4209
4729
|
inputSchema: {
|
|
4210
4730
|
type: "object",
|
|
4211
|
-
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
|
+
}
|
|
4212
4737
|
}
|
|
4213
4738
|
},
|
|
4214
4739
|
{
|
|
@@ -4221,6 +4746,10 @@ var init_definitions = __esm({
|
|
|
4221
4746
|
target: {
|
|
4222
4747
|
type: "string",
|
|
4223
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."
|
|
4224
4753
|
}
|
|
4225
4754
|
}
|
|
4226
4755
|
}
|
|
@@ -4247,6 +4776,10 @@ var init_definitions = __esm({
|
|
|
4247
4776
|
filter: {
|
|
4248
4777
|
type: "string",
|
|
4249
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."
|
|
4250
4783
|
}
|
|
4251
4784
|
}
|
|
4252
4785
|
}
|
|
@@ -4268,7 +4801,12 @@ var init_definitions = __esm({
|
|
|
4268
4801
|
description: "Undo the last change in Roblox Studio. Uses ChangeHistoryService to reverse the most recent operation.",
|
|
4269
4802
|
inputSchema: {
|
|
4270
4803
|
type: "object",
|
|
4271
|
-
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
|
+
}
|
|
4272
4810
|
}
|
|
4273
4811
|
},
|
|
4274
4812
|
{
|
|
@@ -4277,7 +4815,12 @@ var init_definitions = __esm({
|
|
|
4277
4815
|
description: "Redo the last undone change in Roblox Studio. Uses ChangeHistoryService to reapply the most recently undone operation.",
|
|
4278
4816
|
inputSchema: {
|
|
4279
4817
|
type: "object",
|
|
4280
|
-
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
|
+
}
|
|
4281
4824
|
}
|
|
4282
4825
|
},
|
|
4283
4826
|
// === Build Library ===
|
|
@@ -4300,6 +4843,10 @@ var init_definitions = __esm({
|
|
|
4300
4843
|
type: "string",
|
|
4301
4844
|
enum: ["medieval", "modern", "nature", "scifi", "misc"],
|
|
4302
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."
|
|
4303
4850
|
}
|
|
4304
4851
|
},
|
|
4305
4852
|
required: ["instancePath"]
|
|
@@ -4449,6 +4996,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4449
4996
|
type: "array",
|
|
4450
4997
|
items: { type: "number" },
|
|
4451
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."
|
|
4452
5003
|
}
|
|
4453
5004
|
},
|
|
4454
5005
|
required: ["buildData", "targetPath"]
|
|
@@ -4483,6 +5034,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4483
5034
|
maxResults: {
|
|
4484
5035
|
type: "number",
|
|
4485
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."
|
|
4486
5041
|
}
|
|
4487
5042
|
}
|
|
4488
5043
|
}
|
|
@@ -4567,6 +5122,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4567
5122
|
targetPath: {
|
|
4568
5123
|
type: "string",
|
|
4569
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."
|
|
4570
5129
|
}
|
|
4571
5130
|
},
|
|
4572
5131
|
required: ["sceneData"]
|
|
@@ -4664,6 +5223,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4664
5223
|
z: { type: "number" }
|
|
4665
5224
|
},
|
|
4666
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."
|
|
4667
5230
|
}
|
|
4668
5231
|
},
|
|
4669
5232
|
required: ["assetId"]
|
|
@@ -4687,6 +5250,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4687
5250
|
maxDepth: {
|
|
4688
5251
|
type: "number",
|
|
4689
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."
|
|
4690
5257
|
}
|
|
4691
5258
|
},
|
|
4692
5259
|
required: ["assetId"]
|
|
@@ -4734,7 +5301,12 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4734
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.',
|
|
4735
5302
|
inputSchema: {
|
|
4736
5303
|
type: "object",
|
|
4737
|
-
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
|
+
}
|
|
4738
5310
|
}
|
|
4739
5311
|
},
|
|
4740
5312
|
// === Input Simulation ===
|
|
@@ -4771,6 +5343,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4771
5343
|
target: {
|
|
4772
5344
|
type: "string",
|
|
4773
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."
|
|
4774
5350
|
}
|
|
4775
5351
|
},
|
|
4776
5352
|
required: ["action", "x", "y"]
|
|
@@ -4799,6 +5375,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4799
5375
|
target: {
|
|
4800
5376
|
type: "string",
|
|
4801
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."
|
|
4802
5382
|
}
|
|
4803
5383
|
},
|
|
4804
5384
|
required: ["keyCode"]
|
|
@@ -4832,6 +5412,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4832
5412
|
target: {
|
|
4833
5413
|
type: "string",
|
|
4834
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."
|
|
4835
5419
|
}
|
|
4836
5420
|
}
|
|
4837
5421
|
}
|
|
@@ -4851,6 +5435,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4851
5435
|
targetParentPath: {
|
|
4852
5436
|
type: "string",
|
|
4853
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."
|
|
4854
5442
|
}
|
|
4855
5443
|
},
|
|
4856
5444
|
required: ["instancePath", "targetParentPath"]
|
|
@@ -4875,6 +5463,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4875
5463
|
classFilter: {
|
|
4876
5464
|
type: "string",
|
|
4877
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."
|
|
4878
5470
|
}
|
|
4879
5471
|
},
|
|
4880
5472
|
required: ["instancePath"]
|
|
@@ -4894,6 +5486,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4894
5486
|
instancePathB: {
|
|
4895
5487
|
type: "string",
|
|
4896
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."
|
|
4897
5493
|
}
|
|
4898
5494
|
},
|
|
4899
5495
|
required: ["instancePathA", "instancePathB"]
|
|
@@ -4914,6 +5510,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4914
5510
|
messageType: {
|
|
4915
5511
|
type: "string",
|
|
4916
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."
|
|
4917
5517
|
}
|
|
4918
5518
|
}
|
|
4919
5519
|
}
|
|
@@ -4933,6 +5533,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4933
5533
|
attributes: {
|
|
4934
5534
|
type: "object",
|
|
4935
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."
|
|
4936
5540
|
}
|
|
4937
5541
|
},
|
|
4938
5542
|
required: ["instancePath", "attributes"]
|
|
@@ -4954,6 +5558,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4954
5558
|
type: "array",
|
|
4955
5559
|
items: { type: "string" },
|
|
4956
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."
|
|
4957
5565
|
}
|
|
4958
5566
|
}
|
|
4959
5567
|
}
|
|
@@ -4979,6 +5587,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4979
5587
|
type: "string",
|
|
4980
5588
|
enum: ["edit", "server"],
|
|
4981
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."
|
|
4982
5594
|
}
|
|
4983
5595
|
},
|
|
4984
5596
|
required: ["instance_paths", "output_path"]
|
|
@@ -5013,6 +5625,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5013
5625
|
type: "string",
|
|
5014
5626
|
enum: ["edit", "server"],
|
|
5015
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."
|
|
5016
5632
|
}
|
|
5017
5633
|
},
|
|
5018
5634
|
required: ["source", "parent_path"]
|
|
@@ -5058,6 +5674,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5058
5674
|
maxReplacements: {
|
|
5059
5675
|
type: "number",
|
|
5060
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."
|
|
5061
5681
|
}
|
|
5062
5682
|
},
|
|
5063
5683
|
required: ["pattern", "replacement"]
|