@chrrxs/robloxstudio-mcp-inspector 2.11.4 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1003 -404
- package/package.json +1 -1
- package/studio-plugin/MCPPlugin.rbxmx +597 -218
- 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 +7 -146
- 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;
|
|
368
|
+
}
|
|
369
|
+
const result = bridge.registerInstance({
|
|
370
|
+
pluginSessionId,
|
|
371
|
+
instanceId,
|
|
372
|
+
role,
|
|
373
|
+
placeId: typeof placeId === "number" ? placeId : 0,
|
|
374
|
+
placeName: typeof placeName === "string" ? placeName : "",
|
|
375
|
+
dataModelName: typeof dataModelName === "string" ? dataModelName : "",
|
|
376
|
+
isRunning: !!isRunning
|
|
377
|
+
});
|
|
378
|
+
if (!result.ok) {
|
|
379
|
+
res.status(409).json({
|
|
380
|
+
success: false,
|
|
381
|
+
error: result.error.code,
|
|
382
|
+
message: result.error.message,
|
|
383
|
+
existing: result.error.existing
|
|
384
|
+
});
|
|
385
|
+
return;
|
|
80
386
|
}
|
|
387
|
+
res.json({
|
|
388
|
+
success: true,
|
|
389
|
+
assignedRole: result.assignedRole,
|
|
390
|
+
instanceId: result.instanceId
|
|
391
|
+
});
|
|
81
392
|
});
|
|
82
393
|
app.post("/disconnect", (req, res) => {
|
|
83
|
-
const {
|
|
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"
|
|
@@ -1282,7 +1720,7 @@ if not bridgeOk then
|
|
|
1282
1720
|
end
|
|
1283
1721
|
end
|
|
1284
1722
|
end
|
|
1285
|
-
return HttpService:JSONEncode({ bridge = "ok", ok = false, error = errMsg })
|
|
1723
|
+
return HttpService:JSONEncode({ bridge = "ok", ok = false, error = __mcp_outer_remap(errMsg) })
|
|
1286
1724
|
end
|
|
1287
1725
|
-- inner is the {ok, value, output} table from our IIFE. Defensive: if it's
|
|
1288
1726
|
-- somehow not a table (caller bypassed the wrapper), fall back to old shape.
|
|
@@ -1290,13 +1728,13 @@ if typeof(inner) ~= "table" then
|
|
|
1290
1728
|
return HttpService:JSONEncode({
|
|
1291
1729
|
bridge = "ok",
|
|
1292
1730
|
ok = true,
|
|
1293
|
-
result = if inner == nil then nil else
|
|
1731
|
+
result = if inner == nil then nil else __mcp_format(inner),
|
|
1294
1732
|
})
|
|
1295
1733
|
end
|
|
1296
1734
|
return HttpService:JSONEncode({
|
|
1297
1735
|
bridge = "ok",
|
|
1298
1736
|
ok = inner.ok == true,
|
|
1299
|
-
result = if inner.ok and inner.value ~= nil then
|
|
1737
|
+
result = if inner.ok and inner.value ~= nil then __mcp_format(inner.value) else nil,
|
|
1300
1738
|
error = if not inner.ok then tostring(inner.value) else nil,
|
|
1301
1739
|
output = inner.output or {},
|
|
1302
1740
|
})
|
|
@@ -1313,17 +1751,19 @@ function parseBridgeResponse(response) {
|
|
|
1313
1751
|
}
|
|
1314
1752
|
return JSON.stringify(response);
|
|
1315
1753
|
}
|
|
1316
|
-
var SERVER_LOCAL_NAME, CLIENT_LOCAL_NAME, RobloxStudioTools;
|
|
1754
|
+
var SERVER_LOCAL_NAME, CLIENT_LOCAL_NAME, EVAL_WRAPPER_LINE_OFFSET, RobloxStudioTools;
|
|
1317
1755
|
var init_tools = __esm({
|
|
1318
1756
|
"../core/dist/tools/index.js"() {
|
|
1319
1757
|
"use strict";
|
|
1320
1758
|
init_studio_client();
|
|
1759
|
+
init_bridge_service();
|
|
1321
1760
|
init_build_executor();
|
|
1322
1761
|
init_opencloud_client();
|
|
1323
1762
|
init_roblox_cookie_client();
|
|
1324
1763
|
init_png_encoder();
|
|
1325
1764
|
SERVER_LOCAL_NAME = "__MCP_ServerEvalLocal";
|
|
1326
1765
|
CLIENT_LOCAL_NAME = "__MCP_ClientEvalBridge";
|
|
1766
|
+
EVAL_WRAPPER_LINE_OFFSET = 23;
|
|
1327
1767
|
RobloxStudioTools = class _RobloxStudioTools {
|
|
1328
1768
|
client;
|
|
1329
1769
|
bridge;
|
|
@@ -1335,8 +1775,29 @@ var init_tools = __esm({
|
|
|
1335
1775
|
this.openCloudClient = new OpenCloudClient();
|
|
1336
1776
|
this.cookieClient = new RobloxCookieClient();
|
|
1337
1777
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1778
|
+
// Resolve (instance_id, target-role) → concrete (instanceId, role) and
|
|
1779
|
+
// dispatch a single request. Throws RoutingFailure if the resolution is
|
|
1780
|
+
// ambiguous, missing, or asks for fanout on a non-fanout-capable tool —
|
|
1781
|
+
// the MCP transport layer surfaces it as a structured error result so
|
|
1782
|
+
// the LLM can recover via the embedded data.instances list.
|
|
1783
|
+
async _callSingle(endpoint, data, target, instance_id) {
|
|
1784
|
+
const r = this.bridge.resolveTarget({ instance_id, target });
|
|
1785
|
+
if (!r.ok)
|
|
1786
|
+
throw new RoutingFailure(r.error);
|
|
1787
|
+
if (r.mode !== "single") {
|
|
1788
|
+
throw new RoutingFailure({
|
|
1789
|
+
code: "target_role_not_present_on_instance",
|
|
1790
|
+
message: "This tool does not support target=all. Pick a specific role or omit target.",
|
|
1791
|
+
data: {
|
|
1792
|
+
instances: this.bridge.getPublicInstances(),
|
|
1793
|
+
count: this.bridge.getInstances().length
|
|
1794
|
+
}
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
return this.client.request(endpoint, data, r.targetInstanceId, r.targetRole);
|
|
1798
|
+
}
|
|
1799
|
+
async getFileTree(path2 = "", instance_id) {
|
|
1800
|
+
const response = await this._callSingle("/api/file-tree", { path: path2 }, void 0, instance_id);
|
|
1340
1801
|
return {
|
|
1341
1802
|
content: [
|
|
1342
1803
|
{
|
|
@@ -1346,8 +1807,8 @@ var init_tools = __esm({
|
|
|
1346
1807
|
]
|
|
1347
1808
|
};
|
|
1348
1809
|
}
|
|
1349
|
-
async searchFiles(query, searchType = "name") {
|
|
1350
|
-
const response = await this.
|
|
1810
|
+
async searchFiles(query, searchType = "name", instance_id) {
|
|
1811
|
+
const response = await this._callSingle("/api/search-files", { query, searchType }, void 0, instance_id);
|
|
1351
1812
|
return {
|
|
1352
1813
|
content: [
|
|
1353
1814
|
{
|
|
@@ -1357,8 +1818,8 @@ var init_tools = __esm({
|
|
|
1357
1818
|
]
|
|
1358
1819
|
};
|
|
1359
1820
|
}
|
|
1360
|
-
async getPlaceInfo() {
|
|
1361
|
-
const response = await this.
|
|
1821
|
+
async getPlaceInfo(instance_id) {
|
|
1822
|
+
const response = await this._callSingle("/api/place-info", {}, void 0, instance_id);
|
|
1362
1823
|
return {
|
|
1363
1824
|
content: [
|
|
1364
1825
|
{
|
|
@@ -1368,8 +1829,8 @@ var init_tools = __esm({
|
|
|
1368
1829
|
]
|
|
1369
1830
|
};
|
|
1370
1831
|
}
|
|
1371
|
-
async getServices(serviceName) {
|
|
1372
|
-
const response = await this.
|
|
1832
|
+
async getServices(serviceName, instance_id) {
|
|
1833
|
+
const response = await this._callSingle("/api/services", { serviceName }, void 0, instance_id);
|
|
1373
1834
|
return {
|
|
1374
1835
|
content: [
|
|
1375
1836
|
{
|
|
@@ -1379,12 +1840,12 @@ var init_tools = __esm({
|
|
|
1379
1840
|
]
|
|
1380
1841
|
};
|
|
1381
1842
|
}
|
|
1382
|
-
async searchObjects(query, searchType = "name", propertyName) {
|
|
1383
|
-
const response = await this.
|
|
1843
|
+
async searchObjects(query, searchType = "name", propertyName, instance_id) {
|
|
1844
|
+
const response = await this._callSingle("/api/search-objects", {
|
|
1384
1845
|
query,
|
|
1385
1846
|
searchType,
|
|
1386
1847
|
propertyName
|
|
1387
|
-
});
|
|
1848
|
+
}, void 0, instance_id);
|
|
1388
1849
|
return {
|
|
1389
1850
|
content: [
|
|
1390
1851
|
{
|
|
@@ -1394,11 +1855,11 @@ var init_tools = __esm({
|
|
|
1394
1855
|
]
|
|
1395
1856
|
};
|
|
1396
1857
|
}
|
|
1397
|
-
async getInstanceProperties(instancePath, excludeSource) {
|
|
1858
|
+
async getInstanceProperties(instancePath, excludeSource, instance_id) {
|
|
1398
1859
|
if (!instancePath) {
|
|
1399
1860
|
throw new Error("Instance path is required for get_instance_properties");
|
|
1400
1861
|
}
|
|
1401
|
-
const response = await this.
|
|
1862
|
+
const response = await this._callSingle("/api/instance-properties", { instancePath, excludeSource }, void 0, instance_id);
|
|
1402
1863
|
return {
|
|
1403
1864
|
content: [
|
|
1404
1865
|
{
|
|
@@ -1408,11 +1869,11 @@ var init_tools = __esm({
|
|
|
1408
1869
|
]
|
|
1409
1870
|
};
|
|
1410
1871
|
}
|
|
1411
|
-
async getInstanceChildren(instancePath) {
|
|
1872
|
+
async getInstanceChildren(instancePath, instance_id) {
|
|
1412
1873
|
if (!instancePath) {
|
|
1413
1874
|
throw new Error("Instance path is required for get_instance_children");
|
|
1414
1875
|
}
|
|
1415
|
-
const response = await this.
|
|
1876
|
+
const response = await this._callSingle("/api/instance-children", { instancePath }, void 0, instance_id);
|
|
1416
1877
|
return {
|
|
1417
1878
|
content: [
|
|
1418
1879
|
{
|
|
@@ -1422,14 +1883,14 @@ var init_tools = __esm({
|
|
|
1422
1883
|
]
|
|
1423
1884
|
};
|
|
1424
1885
|
}
|
|
1425
|
-
async searchByProperty(propertyName, propertyValue) {
|
|
1886
|
+
async searchByProperty(propertyName, propertyValue, instance_id) {
|
|
1426
1887
|
if (!propertyName || !propertyValue) {
|
|
1427
1888
|
throw new Error("Property name and value are required for search_by_property");
|
|
1428
1889
|
}
|
|
1429
|
-
const response = await this.
|
|
1890
|
+
const response = await this._callSingle("/api/search-by-property", {
|
|
1430
1891
|
propertyName,
|
|
1431
1892
|
propertyValue
|
|
1432
|
-
});
|
|
1893
|
+
}, void 0, instance_id);
|
|
1433
1894
|
return {
|
|
1434
1895
|
content: [
|
|
1435
1896
|
{
|
|
@@ -1439,11 +1900,11 @@ var init_tools = __esm({
|
|
|
1439
1900
|
]
|
|
1440
1901
|
};
|
|
1441
1902
|
}
|
|
1442
|
-
async getClassInfo(className) {
|
|
1903
|
+
async getClassInfo(className, instance_id) {
|
|
1443
1904
|
if (!className) {
|
|
1444
1905
|
throw new Error("Class name is required for get_class_info");
|
|
1445
1906
|
}
|
|
1446
|
-
const response = await this.
|
|
1907
|
+
const response = await this._callSingle("/api/class-info", { className }, void 0, instance_id);
|
|
1447
1908
|
return {
|
|
1448
1909
|
content: [
|
|
1449
1910
|
{
|
|
@@ -1453,12 +1914,12 @@ var init_tools = __esm({
|
|
|
1453
1914
|
]
|
|
1454
1915
|
};
|
|
1455
1916
|
}
|
|
1456
|
-
async getProjectStructure(path2, maxDepth, scriptsOnly) {
|
|
1457
|
-
const response = await this.
|
|
1917
|
+
async getProjectStructure(path2, maxDepth, scriptsOnly, instance_id) {
|
|
1918
|
+
const response = await this._callSingle("/api/project-structure", {
|
|
1458
1919
|
path: path2,
|
|
1459
1920
|
maxDepth,
|
|
1460
1921
|
scriptsOnly
|
|
1461
|
-
});
|
|
1922
|
+
}, void 0, instance_id);
|
|
1462
1923
|
return {
|
|
1463
1924
|
content: [
|
|
1464
1925
|
{
|
|
@@ -1468,15 +1929,15 @@ var init_tools = __esm({
|
|
|
1468
1929
|
]
|
|
1469
1930
|
};
|
|
1470
1931
|
}
|
|
1471
|
-
async setProperty(instancePath, propertyName, propertyValue) {
|
|
1932
|
+
async setProperty(instancePath, propertyName, propertyValue, instance_id) {
|
|
1472
1933
|
if (!instancePath || !propertyName) {
|
|
1473
1934
|
throw new Error("Instance path and property name are required for set_property");
|
|
1474
1935
|
}
|
|
1475
|
-
const response = await this.
|
|
1936
|
+
const response = await this._callSingle("/api/set-property", {
|
|
1476
1937
|
instancePath,
|
|
1477
1938
|
propertyName,
|
|
1478
1939
|
propertyValue
|
|
1479
|
-
});
|
|
1940
|
+
}, void 0, instance_id);
|
|
1480
1941
|
return {
|
|
1481
1942
|
content: [
|
|
1482
1943
|
{
|
|
@@ -1486,22 +1947,22 @@ var init_tools = __esm({
|
|
|
1486
1947
|
]
|
|
1487
1948
|
};
|
|
1488
1949
|
}
|
|
1489
|
-
async setProperties(instancePath, properties) {
|
|
1950
|
+
async setProperties(instancePath, properties, instance_id) {
|
|
1490
1951
|
if (!instancePath || !properties) {
|
|
1491
1952
|
throw new Error("instancePath and properties are required for set_properties");
|
|
1492
1953
|
}
|
|
1493
|
-
const response = await this.
|
|
1954
|
+
const response = await this._callSingle("/api/set-properties", { instancePath, properties }, void 0, instance_id);
|
|
1494
1955
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
1495
1956
|
}
|
|
1496
|
-
async massSetProperty(paths, propertyName, propertyValue) {
|
|
1957
|
+
async massSetProperty(paths, propertyName, propertyValue, instance_id) {
|
|
1497
1958
|
if (!paths || paths.length === 0 || !propertyName) {
|
|
1498
1959
|
throw new Error("Paths array and property name are required for mass_set_property");
|
|
1499
1960
|
}
|
|
1500
|
-
const response = await this.
|
|
1961
|
+
const response = await this._callSingle("/api/mass-set-property", {
|
|
1501
1962
|
paths,
|
|
1502
1963
|
propertyName,
|
|
1503
1964
|
propertyValue
|
|
1504
|
-
});
|
|
1965
|
+
}, void 0, instance_id);
|
|
1505
1966
|
return {
|
|
1506
1967
|
content: [
|
|
1507
1968
|
{
|
|
@@ -1511,14 +1972,14 @@ var init_tools = __esm({
|
|
|
1511
1972
|
]
|
|
1512
1973
|
};
|
|
1513
1974
|
}
|
|
1514
|
-
async massGetProperty(paths, propertyName) {
|
|
1975
|
+
async massGetProperty(paths, propertyName, instance_id) {
|
|
1515
1976
|
if (!paths || paths.length === 0 || !propertyName) {
|
|
1516
1977
|
throw new Error("Paths array and property name are required for mass_get_property");
|
|
1517
1978
|
}
|
|
1518
|
-
const response = await this.
|
|
1979
|
+
const response = await this._callSingle("/api/mass-get-property", {
|
|
1519
1980
|
paths,
|
|
1520
1981
|
propertyName
|
|
1521
|
-
});
|
|
1982
|
+
}, void 0, instance_id);
|
|
1522
1983
|
return {
|
|
1523
1984
|
content: [
|
|
1524
1985
|
{
|
|
@@ -1528,16 +1989,16 @@ var init_tools = __esm({
|
|
|
1528
1989
|
]
|
|
1529
1990
|
};
|
|
1530
1991
|
}
|
|
1531
|
-
async createObject(className, parent, name, properties) {
|
|
1992
|
+
async createObject(className, parent, name, properties, instance_id) {
|
|
1532
1993
|
if (!className || !parent) {
|
|
1533
1994
|
throw new Error("Class name and parent are required for create_object");
|
|
1534
1995
|
}
|
|
1535
|
-
const response = await this.
|
|
1996
|
+
const response = await this._callSingle("/api/create-object", {
|
|
1536
1997
|
className,
|
|
1537
1998
|
parent,
|
|
1538
1999
|
name,
|
|
1539
2000
|
properties
|
|
1540
|
-
});
|
|
2001
|
+
}, void 0, instance_id);
|
|
1541
2002
|
return {
|
|
1542
2003
|
content: [
|
|
1543
2004
|
{
|
|
@@ -1547,11 +2008,11 @@ var init_tools = __esm({
|
|
|
1547
2008
|
]
|
|
1548
2009
|
};
|
|
1549
2010
|
}
|
|
1550
|
-
async massCreateObjects(objects) {
|
|
2011
|
+
async massCreateObjects(objects, instance_id) {
|
|
1551
2012
|
if (!objects || objects.length === 0) {
|
|
1552
2013
|
throw new Error("Objects array is required for mass_create_objects");
|
|
1553
2014
|
}
|
|
1554
|
-
const response = await this.
|
|
2015
|
+
const response = await this._callSingle("/api/mass-create-objects", { objects }, void 0, instance_id);
|
|
1555
2016
|
return {
|
|
1556
2017
|
content: [
|
|
1557
2018
|
{
|
|
@@ -1561,11 +2022,11 @@ var init_tools = __esm({
|
|
|
1561
2022
|
]
|
|
1562
2023
|
};
|
|
1563
2024
|
}
|
|
1564
|
-
async deleteObject(instancePath) {
|
|
2025
|
+
async deleteObject(instancePath, instance_id) {
|
|
1565
2026
|
if (!instancePath) {
|
|
1566
2027
|
throw new Error("Instance path is required for delete_object");
|
|
1567
2028
|
}
|
|
1568
|
-
const response = await this.
|
|
2029
|
+
const response = await this._callSingle("/api/delete-object", { instancePath }, void 0, instance_id);
|
|
1569
2030
|
return {
|
|
1570
2031
|
content: [
|
|
1571
2032
|
{
|
|
@@ -1575,15 +2036,15 @@ var init_tools = __esm({
|
|
|
1575
2036
|
]
|
|
1576
2037
|
};
|
|
1577
2038
|
}
|
|
1578
|
-
async smartDuplicate(instancePath, count, options) {
|
|
2039
|
+
async smartDuplicate(instancePath, count, options, instance_id) {
|
|
1579
2040
|
if (!instancePath || count < 1) {
|
|
1580
2041
|
throw new Error("Instance path and count > 0 are required for smart_duplicate");
|
|
1581
2042
|
}
|
|
1582
|
-
const response = await this.
|
|
2043
|
+
const response = await this._callSingle("/api/smart-duplicate", {
|
|
1583
2044
|
instancePath,
|
|
1584
2045
|
count,
|
|
1585
2046
|
options
|
|
1586
|
-
});
|
|
2047
|
+
}, void 0, instance_id);
|
|
1587
2048
|
return {
|
|
1588
2049
|
content: [
|
|
1589
2050
|
{
|
|
@@ -1593,11 +2054,11 @@ var init_tools = __esm({
|
|
|
1593
2054
|
]
|
|
1594
2055
|
};
|
|
1595
2056
|
}
|
|
1596
|
-
async massDuplicate(duplications) {
|
|
2057
|
+
async massDuplicate(duplications, instance_id) {
|
|
1597
2058
|
if (!duplications || duplications.length === 0) {
|
|
1598
2059
|
throw new Error("Duplications array is required for mass_duplicate");
|
|
1599
2060
|
}
|
|
1600
|
-
const response = await this.
|
|
2061
|
+
const response = await this._callSingle("/api/mass-duplicate", { duplications }, void 0, instance_id);
|
|
1601
2062
|
return {
|
|
1602
2063
|
content: [
|
|
1603
2064
|
{
|
|
@@ -1607,11 +2068,11 @@ var init_tools = __esm({
|
|
|
1607
2068
|
]
|
|
1608
2069
|
};
|
|
1609
2070
|
}
|
|
1610
|
-
async getScriptSource(instancePath, startLine, endLine) {
|
|
2071
|
+
async getScriptSource(instancePath, startLine, endLine, instance_id) {
|
|
1611
2072
|
if (!instancePath) {
|
|
1612
2073
|
throw new Error("Instance path is required for get_script_source");
|
|
1613
2074
|
}
|
|
1614
|
-
const response = await this.
|
|
2075
|
+
const response = await this._callSingle("/api/get-script-source", { instancePath, startLine, endLine }, void 0, instance_id);
|
|
1615
2076
|
if (response.error) {
|
|
1616
2077
|
return { content: [{ type: "text", text: `Error: ${response.error}` }] };
|
|
1617
2078
|
}
|
|
@@ -1658,11 +2119,11 @@ ${code}`
|
|
|
1658
2119
|
}]
|
|
1659
2120
|
};
|
|
1660
2121
|
}
|
|
1661
|
-
async setScriptSource(instancePath, source) {
|
|
2122
|
+
async setScriptSource(instancePath, source, instance_id) {
|
|
1662
2123
|
if (!instancePath || typeof source !== "string") {
|
|
1663
2124
|
throw new Error("Instance path and source code string are required for set_script_source");
|
|
1664
2125
|
}
|
|
1665
|
-
const response = await this.
|
|
2126
|
+
const response = await this._callSingle("/api/set-script-source", { instancePath, source }, void 0, instance_id);
|
|
1666
2127
|
return {
|
|
1667
2128
|
content: [
|
|
1668
2129
|
{
|
|
@@ -1672,14 +2133,14 @@ ${code}`
|
|
|
1672
2133
|
]
|
|
1673
2134
|
};
|
|
1674
2135
|
}
|
|
1675
|
-
async editScriptLines(instancePath, oldString, newString, startLine) {
|
|
2136
|
+
async editScriptLines(instancePath, oldString, newString, startLine, instance_id) {
|
|
1676
2137
|
if (!instancePath || typeof oldString !== "string" || typeof newString !== "string") {
|
|
1677
2138
|
throw new Error("Instance path, old_string, and new_string are required for edit_script_lines");
|
|
1678
2139
|
}
|
|
1679
2140
|
const payload = { instancePath, old_string: oldString, new_string: newString };
|
|
1680
2141
|
if (startLine !== void 0)
|
|
1681
2142
|
payload.startLine = startLine;
|
|
1682
|
-
const response = await this.
|
|
2143
|
+
const response = await this._callSingle("/api/edit-script-lines", payload, void 0, instance_id);
|
|
1683
2144
|
return {
|
|
1684
2145
|
content: [
|
|
1685
2146
|
{
|
|
@@ -1689,11 +2150,11 @@ ${code}`
|
|
|
1689
2150
|
]
|
|
1690
2151
|
};
|
|
1691
2152
|
}
|
|
1692
|
-
async insertScriptLines(instancePath, afterLine, newContent) {
|
|
2153
|
+
async insertScriptLines(instancePath, afterLine, newContent, instance_id) {
|
|
1693
2154
|
if (!instancePath || typeof newContent !== "string") {
|
|
1694
2155
|
throw new Error("Instance path and newContent are required for insert_script_lines");
|
|
1695
2156
|
}
|
|
1696
|
-
const response = await this.
|
|
2157
|
+
const response = await this._callSingle("/api/insert-script-lines", { instancePath, afterLine: afterLine || 0, newContent }, void 0, instance_id);
|
|
1697
2158
|
return {
|
|
1698
2159
|
content: [
|
|
1699
2160
|
{
|
|
@@ -1703,11 +2164,11 @@ ${code}`
|
|
|
1703
2164
|
]
|
|
1704
2165
|
};
|
|
1705
2166
|
}
|
|
1706
|
-
async deleteScriptLines(instancePath, startLine, endLine) {
|
|
2167
|
+
async deleteScriptLines(instancePath, startLine, endLine, instance_id) {
|
|
1707
2168
|
if (!instancePath || !startLine || !endLine) {
|
|
1708
2169
|
throw new Error("Instance path, startLine, and endLine are required for delete_script_lines");
|
|
1709
2170
|
}
|
|
1710
|
-
const response = await this.
|
|
2171
|
+
const response = await this._callSingle("/api/delete-script-lines", { instancePath, startLine, endLine }, void 0, instance_id);
|
|
1711
2172
|
return {
|
|
1712
2173
|
content: [
|
|
1713
2174
|
{
|
|
@@ -1717,14 +2178,14 @@ ${code}`
|
|
|
1717
2178
|
]
|
|
1718
2179
|
};
|
|
1719
2180
|
}
|
|
1720
|
-
async grepScripts(pattern, options) {
|
|
2181
|
+
async grepScripts(pattern, options, instance_id) {
|
|
1721
2182
|
if (!pattern) {
|
|
1722
2183
|
throw new Error("Pattern is required for grep_scripts");
|
|
1723
2184
|
}
|
|
1724
|
-
const response = await this.
|
|
2185
|
+
const response = await this._callSingle("/api/grep-scripts", {
|
|
1725
2186
|
pattern,
|
|
1726
2187
|
...options
|
|
1727
|
-
});
|
|
2188
|
+
}, void 0, instance_id);
|
|
1728
2189
|
return {
|
|
1729
2190
|
content: [
|
|
1730
2191
|
{
|
|
@@ -1734,11 +2195,11 @@ ${code}`
|
|
|
1734
2195
|
]
|
|
1735
2196
|
};
|
|
1736
2197
|
}
|
|
1737
|
-
async setAttribute(instancePath, attributeName, attributeValue, valueType) {
|
|
2198
|
+
async setAttribute(instancePath, attributeName, attributeValue, valueType, instance_id) {
|
|
1738
2199
|
if (!instancePath || !attributeName) {
|
|
1739
2200
|
throw new Error("Instance path and attribute name are required for set_attribute");
|
|
1740
2201
|
}
|
|
1741
|
-
const response = await this.
|
|
2202
|
+
const response = await this._callSingle("/api/set-attribute", { instancePath, attributeName, attributeValue, valueType }, void 0, instance_id);
|
|
1742
2203
|
return {
|
|
1743
2204
|
content: [
|
|
1744
2205
|
{
|
|
@@ -1748,11 +2209,11 @@ ${code}`
|
|
|
1748
2209
|
]
|
|
1749
2210
|
};
|
|
1750
2211
|
}
|
|
1751
|
-
async getAttributes(instancePath) {
|
|
2212
|
+
async getAttributes(instancePath, instance_id) {
|
|
1752
2213
|
if (!instancePath) {
|
|
1753
2214
|
throw new Error("Instance path is required for get_attributes");
|
|
1754
2215
|
}
|
|
1755
|
-
const response = await this.
|
|
2216
|
+
const response = await this._callSingle("/api/get-attributes", { instancePath }, void 0, instance_id);
|
|
1756
2217
|
return {
|
|
1757
2218
|
content: [
|
|
1758
2219
|
{
|
|
@@ -1762,11 +2223,11 @@ ${code}`
|
|
|
1762
2223
|
]
|
|
1763
2224
|
};
|
|
1764
2225
|
}
|
|
1765
|
-
async deleteAttribute(instancePath, attributeName) {
|
|
2226
|
+
async deleteAttribute(instancePath, attributeName, instance_id) {
|
|
1766
2227
|
if (!instancePath || !attributeName) {
|
|
1767
2228
|
throw new Error("Instance path and attribute name are required for delete_attribute");
|
|
1768
2229
|
}
|
|
1769
|
-
const response = await this.
|
|
2230
|
+
const response = await this._callSingle("/api/delete-attribute", { instancePath, attributeName }, void 0, instance_id);
|
|
1770
2231
|
return {
|
|
1771
2232
|
content: [
|
|
1772
2233
|
{
|
|
@@ -1776,11 +2237,11 @@ ${code}`
|
|
|
1776
2237
|
]
|
|
1777
2238
|
};
|
|
1778
2239
|
}
|
|
1779
|
-
async getTags(instancePath) {
|
|
2240
|
+
async getTags(instancePath, instance_id) {
|
|
1780
2241
|
if (!instancePath) {
|
|
1781
2242
|
throw new Error("Instance path is required for get_tags");
|
|
1782
2243
|
}
|
|
1783
|
-
const response = await this.
|
|
2244
|
+
const response = await this._callSingle("/api/get-tags", { instancePath }, void 0, instance_id);
|
|
1784
2245
|
return {
|
|
1785
2246
|
content: [
|
|
1786
2247
|
{
|
|
@@ -1790,11 +2251,11 @@ ${code}`
|
|
|
1790
2251
|
]
|
|
1791
2252
|
};
|
|
1792
2253
|
}
|
|
1793
|
-
async addTag(instancePath, tagName) {
|
|
2254
|
+
async addTag(instancePath, tagName, instance_id) {
|
|
1794
2255
|
if (!instancePath || !tagName) {
|
|
1795
2256
|
throw new Error("Instance path and tag name are required for add_tag");
|
|
1796
2257
|
}
|
|
1797
|
-
const response = await this.
|
|
2258
|
+
const response = await this._callSingle("/api/add-tag", { instancePath, tagName }, void 0, instance_id);
|
|
1798
2259
|
return {
|
|
1799
2260
|
content: [
|
|
1800
2261
|
{
|
|
@@ -1804,11 +2265,11 @@ ${code}`
|
|
|
1804
2265
|
]
|
|
1805
2266
|
};
|
|
1806
2267
|
}
|
|
1807
|
-
async removeTag(instancePath, tagName) {
|
|
2268
|
+
async removeTag(instancePath, tagName, instance_id) {
|
|
1808
2269
|
if (!instancePath || !tagName) {
|
|
1809
2270
|
throw new Error("Instance path and tag name are required for remove_tag");
|
|
1810
2271
|
}
|
|
1811
|
-
const response = await this.
|
|
2272
|
+
const response = await this._callSingle("/api/remove-tag", { instancePath, tagName }, void 0, instance_id);
|
|
1812
2273
|
return {
|
|
1813
2274
|
content: [
|
|
1814
2275
|
{
|
|
@@ -1818,11 +2279,11 @@ ${code}`
|
|
|
1818
2279
|
]
|
|
1819
2280
|
};
|
|
1820
2281
|
}
|
|
1821
|
-
async getTagged(tagName) {
|
|
2282
|
+
async getTagged(tagName, instance_id) {
|
|
1822
2283
|
if (!tagName) {
|
|
1823
2284
|
throw new Error("Tag name is required for get_tagged");
|
|
1824
2285
|
}
|
|
1825
|
-
const response = await this.
|
|
2286
|
+
const response = await this._callSingle("/api/get-tagged", { tagName }, void 0, instance_id);
|
|
1826
2287
|
return {
|
|
1827
2288
|
content: [
|
|
1828
2289
|
{
|
|
@@ -1832,8 +2293,8 @@ ${code}`
|
|
|
1832
2293
|
]
|
|
1833
2294
|
};
|
|
1834
2295
|
}
|
|
1835
|
-
async getSelection() {
|
|
1836
|
-
const response = await this.
|
|
2296
|
+
async getSelection(instance_id) {
|
|
2297
|
+
const response = await this._callSingle("/api/get-selection", {}, void 0, instance_id);
|
|
1837
2298
|
return {
|
|
1838
2299
|
content: [
|
|
1839
2300
|
{
|
|
@@ -1843,11 +2304,11 @@ ${code}`
|
|
|
1843
2304
|
]
|
|
1844
2305
|
};
|
|
1845
2306
|
}
|
|
1846
|
-
async executeLuau(code, target) {
|
|
2307
|
+
async executeLuau(code, target, instance_id) {
|
|
1847
2308
|
if (!code) {
|
|
1848
2309
|
throw new Error("Code is required for execute_luau");
|
|
1849
2310
|
}
|
|
1850
|
-
const response = await this.
|
|
2311
|
+
const response = await this._callSingle("/api/execute-luau", { code }, target || "edit", instance_id);
|
|
1851
2312
|
return {
|
|
1852
2313
|
content: [
|
|
1853
2314
|
{
|
|
@@ -1857,7 +2318,7 @@ ${code}`
|
|
|
1857
2318
|
]
|
|
1858
2319
|
};
|
|
1859
2320
|
}
|
|
1860
|
-
async evalServerRuntime(code) {
|
|
2321
|
+
async evalServerRuntime(code, instance_id) {
|
|
1861
2322
|
if (!code) {
|
|
1862
2323
|
throw new Error("Code is required for eval_server_runtime");
|
|
1863
2324
|
}
|
|
@@ -1867,7 +2328,7 @@ ${code}`
|
|
|
1867
2328
|
missingError: "ServerEvalBridge not installed. Bridges are auto-installed at start_playtest and removed at stop_playtest. Start a playtest before calling eval_server_runtime.",
|
|
1868
2329
|
userCode: code
|
|
1869
2330
|
});
|
|
1870
|
-
const response = await this.
|
|
2331
|
+
const response = await this._callSingle("/api/execute-luau", { code: wrapper }, "server", instance_id);
|
|
1871
2332
|
return {
|
|
1872
2333
|
content: [
|
|
1873
2334
|
{
|
|
@@ -1877,7 +2338,7 @@ ${code}`
|
|
|
1877
2338
|
]
|
|
1878
2339
|
};
|
|
1879
2340
|
}
|
|
1880
|
-
async evalClientRuntime(code, target) {
|
|
2341
|
+
async evalClientRuntime(code, target, instance_id) {
|
|
1881
2342
|
if (!code) {
|
|
1882
2343
|
throw new Error("Code is required for eval_client_runtime");
|
|
1883
2344
|
}
|
|
@@ -1891,7 +2352,7 @@ ${code}`
|
|
|
1891
2352
|
missingError: "ClientEvalBridge not installed. Bridges are auto-installed at start_playtest and removed at stop_playtest. Start a playtest before calling eval_client_runtime.",
|
|
1892
2353
|
userCode: code
|
|
1893
2354
|
});
|
|
1894
|
-
const response = await this.
|
|
2355
|
+
const response = await this._callSingle("/api/execute-luau", { code: wrapper }, clientTarget, instance_id);
|
|
1895
2356
|
return {
|
|
1896
2357
|
content: [
|
|
1897
2358
|
{
|
|
@@ -1901,7 +2362,7 @@ ${code}`
|
|
|
1901
2362
|
]
|
|
1902
2363
|
};
|
|
1903
2364
|
}
|
|
1904
|
-
async getRuntimeLogs(target, since, tail, filter) {
|
|
2365
|
+
async getRuntimeLogs(target, since, tail, filter, instance_id) {
|
|
1905
2366
|
const tgt = target ?? "all";
|
|
1906
2367
|
const data = {};
|
|
1907
2368
|
if (since !== void 0)
|
|
@@ -1910,16 +2371,26 @@ ${code}`
|
|
|
1910
2371
|
data.tail = tail;
|
|
1911
2372
|
if (filter !== void 0)
|
|
1912
2373
|
data.filter = filter;
|
|
1913
|
-
|
|
1914
|
-
|
|
2374
|
+
const resolved = this.bridge.resolveTarget({ instance_id, target: tgt });
|
|
2375
|
+
if (!resolved.ok)
|
|
2376
|
+
throw new RoutingFailure(resolved.error);
|
|
2377
|
+
if (resolved.mode === "single") {
|
|
2378
|
+
const response = await this.client.request("/api/get-runtime-logs", data, resolved.targetInstanceId, resolved.targetRole);
|
|
2379
|
+
response.peer = resolved.targetRole;
|
|
2380
|
+
if (Array.isArray(response.entries)) {
|
|
2381
|
+
for (const e of response.entries) {
|
|
2382
|
+
if (e.peer !== void 0)
|
|
2383
|
+
e.peer = resolved.targetRole;
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
1915
2386
|
return {
|
|
1916
2387
|
content: [{ type: "text", text: JSON.stringify(response) }]
|
|
1917
2388
|
};
|
|
1918
2389
|
}
|
|
1919
|
-
const targets =
|
|
2390
|
+
const targets = resolved.targets.filter((t) => t.targetRole !== "edit-proxy");
|
|
1920
2391
|
const responses = await Promise.allSettled(targets.map(async (t) => {
|
|
1921
|
-
const r = await this.client.request("/api/get-runtime-logs", data, t);
|
|
1922
|
-
return { ...r, peer: t };
|
|
2392
|
+
const r = await this.client.request("/api/get-runtime-logs", data, t.targetInstanceId, t.targetRole);
|
|
2393
|
+
return { ...r, peer: t.targetRole };
|
|
1923
2394
|
}));
|
|
1924
2395
|
const merged = [];
|
|
1925
2396
|
const perPeerNextSince = {};
|
|
@@ -1965,7 +2436,7 @@ ${code}`
|
|
|
1965
2436
|
content: [{ type: "text", text: JSON.stringify(body) }]
|
|
1966
2437
|
};
|
|
1967
2438
|
}
|
|
1968
|
-
async startPlaytest(mode, numPlayers) {
|
|
2439
|
+
async startPlaytest(mode, numPlayers, instance_id) {
|
|
1969
2440
|
if (mode !== "play" && mode !== "run") {
|
|
1970
2441
|
throw new Error('mode must be "play" or "run"');
|
|
1971
2442
|
}
|
|
@@ -1973,7 +2444,7 @@ ${code}`
|
|
|
1973
2444
|
if (numPlayers !== void 0) {
|
|
1974
2445
|
data.numPlayers = numPlayers;
|
|
1975
2446
|
}
|
|
1976
|
-
const response = await this.
|
|
2447
|
+
const response = await this._callSingle("/api/start-playtest", data, void 0, instance_id);
|
|
1977
2448
|
return {
|
|
1978
2449
|
content: [
|
|
1979
2450
|
{
|
|
@@ -1983,14 +2454,14 @@ ${code}`
|
|
|
1983
2454
|
]
|
|
1984
2455
|
};
|
|
1985
2456
|
}
|
|
1986
|
-
async stopPlaytest() {
|
|
1987
|
-
const response = await this.
|
|
2457
|
+
async stopPlaytest(instance_id) {
|
|
2458
|
+
const response = await this._callSingle("/api/stop-playtest", {}, "edit", instance_id);
|
|
1988
2459
|
return {
|
|
1989
2460
|
content: [{ type: "text", text: JSON.stringify(response) }]
|
|
1990
2461
|
};
|
|
1991
2462
|
}
|
|
1992
|
-
async getPlaytestOutput(target) {
|
|
1993
|
-
const response = await this.
|
|
2463
|
+
async getPlaytestOutput(target, instance_id) {
|
|
2464
|
+
const response = await this._callSingle("/api/get-playtest-output", {}, target || "edit", instance_id);
|
|
1994
2465
|
return {
|
|
1995
2466
|
content: [
|
|
1996
2467
|
{
|
|
@@ -2001,7 +2472,7 @@ ${code}`
|
|
|
2001
2472
|
};
|
|
2002
2473
|
}
|
|
2003
2474
|
async getConnectedInstances() {
|
|
2004
|
-
const instances = this.bridge.
|
|
2475
|
+
const instances = this.bridge.getPublicInstances();
|
|
2005
2476
|
return {
|
|
2006
2477
|
content: [
|
|
2007
2478
|
{
|
|
@@ -2011,8 +2482,8 @@ ${code}`
|
|
|
2011
2482
|
]
|
|
2012
2483
|
};
|
|
2013
2484
|
}
|
|
2014
|
-
async undo() {
|
|
2015
|
-
const response = await this.
|
|
2485
|
+
async undo(instance_id) {
|
|
2486
|
+
const response = await this._callSingle("/api/undo", {}, void 0, instance_id);
|
|
2016
2487
|
return {
|
|
2017
2488
|
content: [
|
|
2018
2489
|
{
|
|
@@ -2022,8 +2493,8 @@ ${code}`
|
|
|
2022
2493
|
]
|
|
2023
2494
|
};
|
|
2024
2495
|
}
|
|
2025
|
-
async redo() {
|
|
2026
|
-
const response = await this.
|
|
2496
|
+
async redo(instance_id) {
|
|
2497
|
+
const response = await this._callSingle("/api/redo", {}, void 0, instance_id);
|
|
2027
2498
|
return {
|
|
2028
2499
|
content: [
|
|
2029
2500
|
{
|
|
@@ -2109,15 +2580,15 @@ ${code}`
|
|
|
2109
2580
|
_RobloxStudioTools._cachedLibraryPath = result;
|
|
2110
2581
|
return result;
|
|
2111
2582
|
}
|
|
2112
|
-
async exportBuild(instancePath, outputId, style = "misc") {
|
|
2583
|
+
async exportBuild(instancePath, outputId, style = "misc", instance_id) {
|
|
2113
2584
|
if (!instancePath) {
|
|
2114
2585
|
throw new Error("Instance path is required for export_build");
|
|
2115
2586
|
}
|
|
2116
|
-
const response = await this.
|
|
2587
|
+
const response = await this._callSingle("/api/export-build", {
|
|
2117
2588
|
instancePath,
|
|
2118
2589
|
outputId,
|
|
2119
2590
|
style
|
|
2120
|
-
});
|
|
2591
|
+
}, void 0, instance_id);
|
|
2121
2592
|
if (response && response.success && response.buildData) {
|
|
2122
2593
|
const buildData = response.buildData;
|
|
2123
2594
|
const buildId = buildData.id || `${style}/exported`;
|
|
@@ -2305,7 +2776,7 @@ ${code}`
|
|
|
2305
2776
|
]
|
|
2306
2777
|
};
|
|
2307
2778
|
}
|
|
2308
|
-
async importBuild(buildData, targetPath, position) {
|
|
2779
|
+
async importBuild(buildData, targetPath, position, instance_id) {
|
|
2309
2780
|
if (!buildData || !targetPath) {
|
|
2310
2781
|
throw new Error("buildData (or library ID string) and targetPath are required for import_build");
|
|
2311
2782
|
}
|
|
@@ -2325,11 +2796,11 @@ ${code}`
|
|
|
2325
2796
|
} else {
|
|
2326
2797
|
resolved = buildData;
|
|
2327
2798
|
}
|
|
2328
|
-
const response = await this.
|
|
2799
|
+
const response = await this._callSingle("/api/import-build", {
|
|
2329
2800
|
buildData: resolved,
|
|
2330
2801
|
targetPath,
|
|
2331
2802
|
position
|
|
2332
|
-
});
|
|
2803
|
+
}, void 0, instance_id);
|
|
2333
2804
|
return {
|
|
2334
2805
|
content: [
|
|
2335
2806
|
{
|
|
@@ -2371,11 +2842,11 @@ ${code}`
|
|
|
2371
2842
|
]
|
|
2372
2843
|
};
|
|
2373
2844
|
}
|
|
2374
|
-
async searchMaterials(query, maxResults) {
|
|
2375
|
-
const response = await this.
|
|
2845
|
+
async searchMaterials(query, maxResults, instance_id) {
|
|
2846
|
+
const response = await this._callSingle("/api/search-materials", {
|
|
2376
2847
|
query: query ?? "",
|
|
2377
2848
|
maxResults: maxResults ?? 50
|
|
2378
|
-
});
|
|
2849
|
+
}, void 0, instance_id);
|
|
2379
2850
|
return {
|
|
2380
2851
|
content: [
|
|
2381
2852
|
{
|
|
@@ -2415,7 +2886,7 @@ ${code}`
|
|
|
2415
2886
|
]
|
|
2416
2887
|
};
|
|
2417
2888
|
}
|
|
2418
|
-
async importScene(sceneData, targetPath = "game.Workspace") {
|
|
2889
|
+
async importScene(sceneData, targetPath = "game.Workspace", instance_id) {
|
|
2419
2890
|
if (!sceneData) {
|
|
2420
2891
|
throw new Error("sceneData is required for import_scene");
|
|
2421
2892
|
}
|
|
@@ -2505,10 +2976,10 @@ ${code}`
|
|
|
2505
2976
|
if (expandedBuilds.length === 0) {
|
|
2506
2977
|
throw new Error("No builds to import - check model references and library");
|
|
2507
2978
|
}
|
|
2508
|
-
const response = await this.
|
|
2979
|
+
const response = await this._callSingle("/api/import-scene", {
|
|
2509
2980
|
expandedBuilds,
|
|
2510
2981
|
targetPath
|
|
2511
|
-
});
|
|
2982
|
+
}, void 0, instance_id);
|
|
2512
2983
|
return {
|
|
2513
2984
|
content: [
|
|
2514
2985
|
{
|
|
@@ -2599,15 +3070,15 @@ ${code}`
|
|
|
2599
3070
|
}]
|
|
2600
3071
|
};
|
|
2601
3072
|
}
|
|
2602
|
-
async insertAsset(assetId, parentPath, position) {
|
|
3073
|
+
async insertAsset(assetId, parentPath, position, instance_id) {
|
|
2603
3074
|
if (!assetId) {
|
|
2604
3075
|
throw new Error("Asset ID is required for insert_asset");
|
|
2605
3076
|
}
|
|
2606
|
-
const response = await this.
|
|
3077
|
+
const response = await this._callSingle("/api/insert-asset", {
|
|
2607
3078
|
assetId,
|
|
2608
3079
|
parentPath: parentPath || "game.Workspace",
|
|
2609
3080
|
position
|
|
2610
|
-
});
|
|
3081
|
+
}, void 0, instance_id);
|
|
2611
3082
|
return {
|
|
2612
3083
|
content: [{
|
|
2613
3084
|
type: "text",
|
|
@@ -2615,15 +3086,15 @@ ${code}`
|
|
|
2615
3086
|
}]
|
|
2616
3087
|
};
|
|
2617
3088
|
}
|
|
2618
|
-
async previewAsset(assetId, includeProperties, maxDepth) {
|
|
3089
|
+
async previewAsset(assetId, includeProperties, maxDepth, instance_id) {
|
|
2619
3090
|
if (!assetId) {
|
|
2620
3091
|
throw new Error("Asset ID is required for preview_asset");
|
|
2621
3092
|
}
|
|
2622
|
-
const response = await this.
|
|
3093
|
+
const response = await this._callSingle("/api/preview-asset", {
|
|
2623
3094
|
assetId,
|
|
2624
3095
|
includeProperties: includeProperties ?? true,
|
|
2625
3096
|
maxDepth: maxDepth ?? 10
|
|
2626
|
-
});
|
|
3097
|
+
}, void 0, instance_id);
|
|
2627
3098
|
return {
|
|
2628
3099
|
content: [{
|
|
2629
3100
|
type: "text",
|
|
@@ -2645,7 +3116,7 @@ ${code}`
|
|
|
2645
3116
|
return id
|
|
2646
3117
|
`;
|
|
2647
3118
|
try {
|
|
2648
|
-
const response = await this.
|
|
3119
|
+
const response = await this._callSingle("/api/execute-luau", { code }, "edit", void 0);
|
|
2649
3120
|
const returnValue = response?.returnValue;
|
|
2650
3121
|
if (returnValue !== void 0 && returnValue !== null && /^\d+$/.test(String(returnValue))) {
|
|
2651
3122
|
return String(returnValue);
|
|
@@ -2720,17 +3191,17 @@ ${code}`
|
|
|
2720
3191
|
}]
|
|
2721
3192
|
};
|
|
2722
3193
|
}
|
|
2723
|
-
async simulateMouseInput(action, x, y, button, scrollDirection, target) {
|
|
3194
|
+
async simulateMouseInput(action, x, y, button, scrollDirection, target, instance_id) {
|
|
2724
3195
|
if (!action) {
|
|
2725
3196
|
throw new Error("action is required for simulate_mouse_input");
|
|
2726
3197
|
}
|
|
2727
|
-
const response = await this.
|
|
3198
|
+
const response = await this._callSingle("/api/simulate-mouse-input", {
|
|
2728
3199
|
action,
|
|
2729
3200
|
x,
|
|
2730
3201
|
y,
|
|
2731
3202
|
button,
|
|
2732
3203
|
scrollDirection
|
|
2733
|
-
}, target || "edit");
|
|
3204
|
+
}, target || "edit", instance_id);
|
|
2734
3205
|
return {
|
|
2735
3206
|
content: [{
|
|
2736
3207
|
type: "text",
|
|
@@ -2738,15 +3209,15 @@ ${code}`
|
|
|
2738
3209
|
}]
|
|
2739
3210
|
};
|
|
2740
3211
|
}
|
|
2741
|
-
async simulateKeyboardInput(keyCode, action, duration, target) {
|
|
3212
|
+
async simulateKeyboardInput(keyCode, action, duration, target, instance_id) {
|
|
2742
3213
|
if (!keyCode) {
|
|
2743
3214
|
throw new Error("keyCode is required for simulate_keyboard_input");
|
|
2744
3215
|
}
|
|
2745
|
-
const response = await this.
|
|
3216
|
+
const response = await this._callSingle("/api/simulate-keyboard-input", {
|
|
2746
3217
|
keyCode,
|
|
2747
3218
|
action,
|
|
2748
3219
|
duration
|
|
2749
|
-
}, target || "edit");
|
|
3220
|
+
}, target || "edit", instance_id);
|
|
2750
3221
|
return {
|
|
2751
3222
|
content: [{
|
|
2752
3223
|
type: "text",
|
|
@@ -2754,16 +3225,16 @@ ${code}`
|
|
|
2754
3225
|
}]
|
|
2755
3226
|
};
|
|
2756
3227
|
}
|
|
2757
|
-
async characterNavigation(position, instancePath, waitForCompletion, timeout, target) {
|
|
3228
|
+
async characterNavigation(position, instancePath, waitForCompletion, timeout, target, instance_id) {
|
|
2758
3229
|
if (!position && !instancePath) {
|
|
2759
3230
|
throw new Error("Either position or instancePath is required for character_navigation");
|
|
2760
3231
|
}
|
|
2761
|
-
const response = await this.
|
|
3232
|
+
const response = await this._callSingle("/api/character-navigation", {
|
|
2762
3233
|
position,
|
|
2763
3234
|
instancePath,
|
|
2764
3235
|
waitForCompletion,
|
|
2765
3236
|
timeout
|
|
2766
|
-
}, target || "edit");
|
|
3237
|
+
}, target || "edit", instance_id);
|
|
2767
3238
|
return {
|
|
2768
3239
|
content: [{
|
|
2769
3240
|
type: "text",
|
|
@@ -2771,50 +3242,50 @@ ${code}`
|
|
|
2771
3242
|
}]
|
|
2772
3243
|
};
|
|
2773
3244
|
}
|
|
2774
|
-
async cloneObject(instancePath, targetParentPath) {
|
|
3245
|
+
async cloneObject(instancePath, targetParentPath, instance_id) {
|
|
2775
3246
|
if (!instancePath || !targetParentPath) {
|
|
2776
3247
|
throw new Error("instancePath and targetParentPath are required for clone_object");
|
|
2777
3248
|
}
|
|
2778
|
-
const response = await this.
|
|
3249
|
+
const response = await this._callSingle("/api/clone-object", { instancePath, targetParentPath }, void 0, instance_id);
|
|
2779
3250
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2780
3251
|
}
|
|
2781
|
-
async getDescendants(instancePath, maxDepth, classFilter) {
|
|
3252
|
+
async getDescendants(instancePath, maxDepth, classFilter, instance_id) {
|
|
2782
3253
|
if (!instancePath) {
|
|
2783
3254
|
throw new Error("instancePath is required for get_descendants");
|
|
2784
3255
|
}
|
|
2785
|
-
const response = await this.
|
|
3256
|
+
const response = await this._callSingle("/api/get-descendants", { instancePath, maxDepth, classFilter }, void 0, instance_id);
|
|
2786
3257
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2787
3258
|
}
|
|
2788
|
-
async compareInstances(instancePathA, instancePathB) {
|
|
3259
|
+
async compareInstances(instancePathA, instancePathB, instance_id) {
|
|
2789
3260
|
if (!instancePathA || !instancePathB) {
|
|
2790
3261
|
throw new Error("instancePathA and instancePathB are required for compare_instances");
|
|
2791
3262
|
}
|
|
2792
|
-
const response = await this.
|
|
3263
|
+
const response = await this._callSingle("/api/compare-instances", { instancePathA, instancePathB }, void 0, instance_id);
|
|
2793
3264
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2794
3265
|
}
|
|
2795
|
-
async getOutputLog(maxEntries, messageType) {
|
|
2796
|
-
const response = await this.
|
|
3266
|
+
async getOutputLog(maxEntries, messageType, instance_id) {
|
|
3267
|
+
const response = await this._callSingle("/api/get-output-log", { maxEntries, messageType }, void 0, instance_id);
|
|
2797
3268
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2798
3269
|
}
|
|
2799
|
-
async bulkSetAttributes(instancePath, attributes) {
|
|
3270
|
+
async bulkSetAttributes(instancePath, attributes, instance_id) {
|
|
2800
3271
|
if (!instancePath || !attributes) {
|
|
2801
3272
|
throw new Error("instancePath and attributes are required for bulk_set_attributes");
|
|
2802
3273
|
}
|
|
2803
|
-
const response = await this.
|
|
3274
|
+
const response = await this._callSingle("/api/bulk-set-attributes", { instancePath, attributes }, void 0, instance_id);
|
|
2804
3275
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2805
3276
|
}
|
|
2806
|
-
async findAndReplaceInScripts(pattern, replacement, options) {
|
|
3277
|
+
async findAndReplaceInScripts(pattern, replacement, options, instance_id) {
|
|
2807
3278
|
if (!pattern) {
|
|
2808
3279
|
throw new Error("pattern is required for find_and_replace_in_scripts");
|
|
2809
3280
|
}
|
|
2810
3281
|
if (replacement === void 0 || replacement === null) {
|
|
2811
3282
|
throw new Error("replacement is required for find_and_replace_in_scripts");
|
|
2812
3283
|
}
|
|
2813
|
-
const response = await this.
|
|
3284
|
+
const response = await this._callSingle("/api/find-and-replace-in-scripts", {
|
|
2814
3285
|
pattern,
|
|
2815
3286
|
replacement,
|
|
2816
3287
|
...options
|
|
2817
|
-
});
|
|
3288
|
+
}, void 0, instance_id);
|
|
2818
3289
|
return {
|
|
2819
3290
|
content: [{
|
|
2820
3291
|
type: "text",
|
|
@@ -2822,24 +3293,27 @@ ${code}`
|
|
|
2822
3293
|
}]
|
|
2823
3294
|
};
|
|
2824
3295
|
}
|
|
2825
|
-
async getMemoryBreakdown(target, tags) {
|
|
3296
|
+
async getMemoryBreakdown(target, tags, instance_id) {
|
|
2826
3297
|
const tgt = target ?? "all";
|
|
2827
3298
|
const data = {};
|
|
2828
3299
|
if (tags !== void 0)
|
|
2829
3300
|
data.tags = tags;
|
|
2830
|
-
|
|
2831
|
-
|
|
3301
|
+
const resolved = this.bridge.resolveTarget({ instance_id, target: tgt });
|
|
3302
|
+
if (!resolved.ok)
|
|
3303
|
+
throw new RoutingFailure(resolved.error);
|
|
3304
|
+
if (resolved.mode === "single") {
|
|
3305
|
+
const response = await this.client.request("/api/get-memory-breakdown", data, resolved.targetInstanceId, resolved.targetRole);
|
|
2832
3306
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2833
3307
|
}
|
|
2834
|
-
const targets =
|
|
3308
|
+
const targets = resolved.targets.filter((t) => t.targetRole !== "edit-proxy");
|
|
2835
3309
|
const responses = await Promise.allSettled(targets.map(async (t) => ({
|
|
2836
|
-
peer: t,
|
|
2837
|
-
result: await this.client.request("/api/get-memory-breakdown", data, t)
|
|
3310
|
+
peer: t.targetRole,
|
|
3311
|
+
result: await this.client.request("/api/get-memory-breakdown", data, t.targetInstanceId, t.targetRole)
|
|
2838
3312
|
})));
|
|
2839
3313
|
const body = {};
|
|
2840
3314
|
for (let i = 0; i < responses.length; i++) {
|
|
2841
3315
|
const r = responses[i];
|
|
2842
|
-
const peer = targets[i];
|
|
3316
|
+
const peer = targets[i].targetRole;
|
|
2843
3317
|
if (r.status === "fulfilled") {
|
|
2844
3318
|
body[peer] = r.value.result;
|
|
2845
3319
|
} else {
|
|
@@ -2848,7 +3322,7 @@ ${code}`
|
|
|
2848
3322
|
}
|
|
2849
3323
|
return { content: [{ type: "text", text: JSON.stringify(body) }] };
|
|
2850
3324
|
}
|
|
2851
|
-
async exportRbxm(instancePaths, outputPath, target) {
|
|
3325
|
+
async exportRbxm(instancePaths, outputPath, target, instance_id) {
|
|
2852
3326
|
if (!Array.isArray(instancePaths) || instancePaths.length === 0) {
|
|
2853
3327
|
throw new Error("instance_paths must be a non-empty array for export_rbxm");
|
|
2854
3328
|
}
|
|
@@ -2859,7 +3333,7 @@ ${code}`
|
|
|
2859
3333
|
if (tgt !== "edit" && tgt !== "server") {
|
|
2860
3334
|
throw new Error(`export_rbxm target must be "edit" or "server" (got: ${tgt})`);
|
|
2861
3335
|
}
|
|
2862
|
-
const response = await this.
|
|
3336
|
+
const response = await this._callSingle("/api/export-rbxm", { instance_paths: instancePaths }, tgt, instance_id);
|
|
2863
3337
|
if (response.error) {
|
|
2864
3338
|
return { content: [{ type: "text", text: JSON.stringify({ error: response.error }) }] };
|
|
2865
3339
|
}
|
|
@@ -2885,7 +3359,7 @@ ${code}`
|
|
|
2885
3359
|
}]
|
|
2886
3360
|
};
|
|
2887
3361
|
}
|
|
2888
|
-
async importRbxm(source, parentPath, target) {
|
|
3362
|
+
async importRbxm(source, parentPath, target, instance_id) {
|
|
2889
3363
|
if (!source || typeof source !== "object") {
|
|
2890
3364
|
throw new Error("source is required for import_rbxm");
|
|
2891
3365
|
}
|
|
@@ -2948,15 +3422,15 @@ ${code}`
|
|
|
2948
3422
|
}
|
|
2949
3423
|
sourceLabel = `base64(${bytes.length}B)`;
|
|
2950
3424
|
}
|
|
2951
|
-
const response = await this.
|
|
3425
|
+
const response = await this._callSingle("/api/import-rbxm", {
|
|
2952
3426
|
base64: bytes.toString("base64"),
|
|
2953
3427
|
parent_path: parentPath,
|
|
2954
3428
|
source_label: sourceLabel
|
|
2955
|
-
}, tgt);
|
|
3429
|
+
}, tgt, instance_id);
|
|
2956
3430
|
return { content: [{ type: "text", text: JSON.stringify(response) }] };
|
|
2957
3431
|
}
|
|
2958
|
-
async captureScreenshot() {
|
|
2959
|
-
const response = await this.
|
|
3432
|
+
async captureScreenshot(instance_id) {
|
|
3433
|
+
const response = await this._callSingle("/api/capture-screenshot", {}, void 0, instance_id);
|
|
2960
3434
|
if (response.error) {
|
|
2961
3435
|
return {
|
|
2962
3436
|
content: [{
|
|
@@ -2978,150 +3452,6 @@ ${code}`
|
|
|
2978
3452
|
}
|
|
2979
3453
|
});
|
|
2980
3454
|
|
|
2981
|
-
// ../core/dist/bridge-service.js
|
|
2982
|
-
import { v4 as uuidv4 } from "uuid";
|
|
2983
|
-
var STALE_INSTANCE_MS, BridgeService;
|
|
2984
|
-
var init_bridge_service = __esm({
|
|
2985
|
-
"../core/dist/bridge-service.js"() {
|
|
2986
|
-
"use strict";
|
|
2987
|
-
STALE_INSTANCE_MS = 3e4;
|
|
2988
|
-
BridgeService = class {
|
|
2989
|
-
pendingRequests = /* @__PURE__ */ new Map();
|
|
2990
|
-
instances = /* @__PURE__ */ new Map();
|
|
2991
|
-
requestTimeout = 3e4;
|
|
2992
|
-
registerInstance(instanceId, role) {
|
|
2993
|
-
let assignedRole = role;
|
|
2994
|
-
if (role === "client") {
|
|
2995
|
-
const used = /* @__PURE__ */ new Set();
|
|
2996
|
-
for (const inst of this.instances.values()) {
|
|
2997
|
-
const match = inst.role.match(/^client-(\d+)$/);
|
|
2998
|
-
if (match)
|
|
2999
|
-
used.add(Number(match[1]));
|
|
3000
|
-
}
|
|
3001
|
-
let idx = 1;
|
|
3002
|
-
while (used.has(idx))
|
|
3003
|
-
idx++;
|
|
3004
|
-
assignedRole = `client-${idx}`;
|
|
3005
|
-
}
|
|
3006
|
-
this.instances.set(instanceId, {
|
|
3007
|
-
instanceId,
|
|
3008
|
-
role: assignedRole,
|
|
3009
|
-
lastActivity: Date.now(),
|
|
3010
|
-
connectedAt: Date.now()
|
|
3011
|
-
});
|
|
3012
|
-
return assignedRole;
|
|
3013
|
-
}
|
|
3014
|
-
unregisterInstance(instanceId) {
|
|
3015
|
-
this.instances.delete(instanceId);
|
|
3016
|
-
for (const [id, req] of this.pendingRequests.entries()) {
|
|
3017
|
-
const targetRole = req.target;
|
|
3018
|
-
const hasHandler = Array.from(this.instances.values()).some((i) => i.role === targetRole);
|
|
3019
|
-
if (!hasHandler) {
|
|
3020
|
-
clearTimeout(req.timeoutId);
|
|
3021
|
-
this.pendingRequests.delete(id);
|
|
3022
|
-
req.reject(new Error(`Target instance "${targetRole}" disconnected`));
|
|
3023
|
-
}
|
|
3024
|
-
}
|
|
3025
|
-
}
|
|
3026
|
-
getInstances() {
|
|
3027
|
-
return Array.from(this.instances.values());
|
|
3028
|
-
}
|
|
3029
|
-
getPendingRequestCount() {
|
|
3030
|
-
return this.pendingRequests.size;
|
|
3031
|
-
}
|
|
3032
|
-
updateInstanceActivity(instanceId) {
|
|
3033
|
-
const inst = this.instances.get(instanceId);
|
|
3034
|
-
if (inst) {
|
|
3035
|
-
inst.lastActivity = Date.now();
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
cleanupStaleInstances() {
|
|
3039
|
-
const now = Date.now();
|
|
3040
|
-
for (const [id, inst] of this.instances.entries()) {
|
|
3041
|
-
if (now - inst.lastActivity > STALE_INSTANCE_MS) {
|
|
3042
|
-
this.unregisterInstance(id);
|
|
3043
|
-
}
|
|
3044
|
-
}
|
|
3045
|
-
}
|
|
3046
|
-
async sendRequest(endpoint, data, target = "edit") {
|
|
3047
|
-
const requestId = uuidv4();
|
|
3048
|
-
return new Promise((resolve2, reject) => {
|
|
3049
|
-
const timeoutId = setTimeout(() => {
|
|
3050
|
-
if (this.pendingRequests.has(requestId)) {
|
|
3051
|
-
this.pendingRequests.delete(requestId);
|
|
3052
|
-
reject(new Error("Request timeout"));
|
|
3053
|
-
}
|
|
3054
|
-
}, this.requestTimeout);
|
|
3055
|
-
const request = {
|
|
3056
|
-
id: requestId,
|
|
3057
|
-
endpoint,
|
|
3058
|
-
data,
|
|
3059
|
-
target,
|
|
3060
|
-
timestamp: Date.now(),
|
|
3061
|
-
resolve: resolve2,
|
|
3062
|
-
reject,
|
|
3063
|
-
timeoutId
|
|
3064
|
-
};
|
|
3065
|
-
this.pendingRequests.set(requestId, request);
|
|
3066
|
-
});
|
|
3067
|
-
}
|
|
3068
|
-
getPendingRequest(callerRole = "edit") {
|
|
3069
|
-
let oldestRequest = null;
|
|
3070
|
-
for (const request of this.pendingRequests.values()) {
|
|
3071
|
-
if (request.target !== callerRole)
|
|
3072
|
-
continue;
|
|
3073
|
-
if (!oldestRequest || request.timestamp < oldestRequest.timestamp) {
|
|
3074
|
-
oldestRequest = request;
|
|
3075
|
-
}
|
|
3076
|
-
}
|
|
3077
|
-
if (oldestRequest) {
|
|
3078
|
-
return {
|
|
3079
|
-
requestId: oldestRequest.id,
|
|
3080
|
-
request: {
|
|
3081
|
-
endpoint: oldestRequest.endpoint,
|
|
3082
|
-
data: oldestRequest.data
|
|
3083
|
-
}
|
|
3084
|
-
};
|
|
3085
|
-
}
|
|
3086
|
-
return null;
|
|
3087
|
-
}
|
|
3088
|
-
resolveRequest(requestId, response) {
|
|
3089
|
-
const request = this.pendingRequests.get(requestId);
|
|
3090
|
-
if (request) {
|
|
3091
|
-
clearTimeout(request.timeoutId);
|
|
3092
|
-
this.pendingRequests.delete(requestId);
|
|
3093
|
-
request.resolve(response);
|
|
3094
|
-
}
|
|
3095
|
-
}
|
|
3096
|
-
rejectRequest(requestId, error) {
|
|
3097
|
-
const request = this.pendingRequests.get(requestId);
|
|
3098
|
-
if (request) {
|
|
3099
|
-
clearTimeout(request.timeoutId);
|
|
3100
|
-
this.pendingRequests.delete(requestId);
|
|
3101
|
-
request.reject(error);
|
|
3102
|
-
}
|
|
3103
|
-
}
|
|
3104
|
-
cleanupOldRequests() {
|
|
3105
|
-
const now = Date.now();
|
|
3106
|
-
for (const [id, request] of this.pendingRequests.entries()) {
|
|
3107
|
-
if (now - request.timestamp > this.requestTimeout) {
|
|
3108
|
-
clearTimeout(request.timeoutId);
|
|
3109
|
-
this.pendingRequests.delete(id);
|
|
3110
|
-
request.reject(new Error("Request timeout"));
|
|
3111
|
-
}
|
|
3112
|
-
}
|
|
3113
|
-
}
|
|
3114
|
-
clearAllPendingRequests() {
|
|
3115
|
-
for (const [, request] of this.pendingRequests.entries()) {
|
|
3116
|
-
clearTimeout(request.timeoutId);
|
|
3117
|
-
request.reject(new Error("Connection closed"));
|
|
3118
|
-
}
|
|
3119
|
-
this.pendingRequests.clear();
|
|
3120
|
-
}
|
|
3121
|
-
};
|
|
3122
|
-
}
|
|
3123
|
-
});
|
|
3124
|
-
|
|
3125
3455
|
// ../core/dist/proxy-bridge-service.js
|
|
3126
3456
|
import { v4 as uuidv42 } from "uuid";
|
|
3127
3457
|
var ProxyBridgeService;
|
|
@@ -3166,14 +3496,20 @@ var init_proxy_bridge_service = __esm({
|
|
|
3166
3496
|
this.refreshTimer = void 0;
|
|
3167
3497
|
}
|
|
3168
3498
|
}
|
|
3169
|
-
async sendRequest(endpoint, data,
|
|
3499
|
+
async sendRequest(endpoint, data, targetInstanceId, targetRole) {
|
|
3170
3500
|
const controller = new AbortController();
|
|
3171
3501
|
const timeoutId = setTimeout(() => controller.abort(), this.proxyRequestTimeout);
|
|
3172
3502
|
try {
|
|
3173
3503
|
const response = await fetch(`${this.primaryBaseUrl}/proxy`, {
|
|
3174
3504
|
method: "POST",
|
|
3175
3505
|
headers: { "Content-Type": "application/json" },
|
|
3176
|
-
body: JSON.stringify({
|
|
3506
|
+
body: JSON.stringify({
|
|
3507
|
+
endpoint,
|
|
3508
|
+
data,
|
|
3509
|
+
targetInstanceId,
|
|
3510
|
+
targetRole,
|
|
3511
|
+
proxyInstanceId: this.proxyInstanceId
|
|
3512
|
+
}),
|
|
3177
3513
|
signal: controller.signal
|
|
3178
3514
|
});
|
|
3179
3515
|
clearTimeout(timeoutId);
|
|
@@ -3257,6 +3593,19 @@ var init_server = __esm({
|
|
|
3257
3593
|
try {
|
|
3258
3594
|
return await handler(this.tools, args ?? {});
|
|
3259
3595
|
} catch (error) {
|
|
3596
|
+
if (error instanceof RoutingFailure) {
|
|
3597
|
+
return {
|
|
3598
|
+
content: [{
|
|
3599
|
+
type: "text",
|
|
3600
|
+
text: JSON.stringify({
|
|
3601
|
+
error: error.routingError.code,
|
|
3602
|
+
message: error.routingError.message,
|
|
3603
|
+
data: error.routingError.data
|
|
3604
|
+
})
|
|
3605
|
+
}],
|
|
3606
|
+
isError: true
|
|
3607
|
+
};
|
|
3608
|
+
}
|
|
3260
3609
|
if (error instanceof McpError2)
|
|
3261
3610
|
throw error;
|
|
3262
3611
|
throw new McpError2(ErrorCode2.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -3398,6 +3747,10 @@ var init_definitions = __esm({
|
|
|
3398
3747
|
path: {
|
|
3399
3748
|
type: "string",
|
|
3400
3749
|
description: "Root path (default: game root)"
|
|
3750
|
+
},
|
|
3751
|
+
instance_id: {
|
|
3752
|
+
type: "string",
|
|
3753
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3401
3754
|
}
|
|
3402
3755
|
}
|
|
3403
3756
|
}
|
|
@@ -3417,6 +3770,10 @@ var init_definitions = __esm({
|
|
|
3417
3770
|
type: "string",
|
|
3418
3771
|
enum: ["name", "type", "content"],
|
|
3419
3772
|
description: "Search mode (default: name)"
|
|
3773
|
+
},
|
|
3774
|
+
instance_id: {
|
|
3775
|
+
type: "string",
|
|
3776
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3420
3777
|
}
|
|
3421
3778
|
},
|
|
3422
3779
|
required: ["query"]
|
|
@@ -3429,7 +3786,12 @@ var init_definitions = __esm({
|
|
|
3429
3786
|
description: "Get place ID, name, and game settings",
|
|
3430
3787
|
inputSchema: {
|
|
3431
3788
|
type: "object",
|
|
3432
|
-
properties: {
|
|
3789
|
+
properties: {
|
|
3790
|
+
instance_id: {
|
|
3791
|
+
type: "string",
|
|
3792
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3433
3795
|
}
|
|
3434
3796
|
},
|
|
3435
3797
|
{
|
|
@@ -3442,6 +3804,10 @@ var init_definitions = __esm({
|
|
|
3442
3804
|
serviceName: {
|
|
3443
3805
|
type: "string",
|
|
3444
3806
|
description: "Specific service name"
|
|
3807
|
+
},
|
|
3808
|
+
instance_id: {
|
|
3809
|
+
type: "string",
|
|
3810
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3445
3811
|
}
|
|
3446
3812
|
}
|
|
3447
3813
|
}
|
|
@@ -3465,6 +3831,10 @@ var init_definitions = __esm({
|
|
|
3465
3831
|
propertyName: {
|
|
3466
3832
|
type: "string",
|
|
3467
3833
|
description: 'Property name when searchType is "property"'
|
|
3834
|
+
},
|
|
3835
|
+
instance_id: {
|
|
3836
|
+
type: "string",
|
|
3837
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3468
3838
|
}
|
|
3469
3839
|
},
|
|
3470
3840
|
required: ["query"]
|
|
@@ -3485,6 +3855,10 @@ var init_definitions = __esm({
|
|
|
3485
3855
|
excludeSource: {
|
|
3486
3856
|
type: "boolean",
|
|
3487
3857
|
description: "For scripts, return SourceLength/LineCount instead of full source (default: false)"
|
|
3858
|
+
},
|
|
3859
|
+
instance_id: {
|
|
3860
|
+
type: "string",
|
|
3861
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3488
3862
|
}
|
|
3489
3863
|
},
|
|
3490
3864
|
required: ["instancePath"]
|
|
@@ -3500,6 +3874,10 @@ var init_definitions = __esm({
|
|
|
3500
3874
|
instancePath: {
|
|
3501
3875
|
type: "string",
|
|
3502
3876
|
description: "Instance path (dot notation)"
|
|
3877
|
+
},
|
|
3878
|
+
instance_id: {
|
|
3879
|
+
type: "string",
|
|
3880
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3503
3881
|
}
|
|
3504
3882
|
},
|
|
3505
3883
|
required: ["instancePath"]
|
|
@@ -3519,6 +3897,10 @@ var init_definitions = __esm({
|
|
|
3519
3897
|
propertyValue: {
|
|
3520
3898
|
type: "string",
|
|
3521
3899
|
description: "Value to match"
|
|
3900
|
+
},
|
|
3901
|
+
instance_id: {
|
|
3902
|
+
type: "string",
|
|
3903
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3522
3904
|
}
|
|
3523
3905
|
},
|
|
3524
3906
|
required: ["propertyName", "propertyValue"]
|
|
@@ -3534,6 +3916,10 @@ var init_definitions = __esm({
|
|
|
3534
3916
|
className: {
|
|
3535
3917
|
type: "string",
|
|
3536
3918
|
description: "Roblox class name"
|
|
3919
|
+
},
|
|
3920
|
+
instance_id: {
|
|
3921
|
+
type: "string",
|
|
3922
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3537
3923
|
}
|
|
3538
3924
|
},
|
|
3539
3925
|
required: ["className"]
|
|
@@ -3558,6 +3944,10 @@ var init_definitions = __esm({
|
|
|
3558
3944
|
scriptsOnly: {
|
|
3559
3945
|
type: "boolean",
|
|
3560
3946
|
description: "Show only scripts (default: false)"
|
|
3947
|
+
},
|
|
3948
|
+
instance_id: {
|
|
3949
|
+
type: "string",
|
|
3950
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3561
3951
|
}
|
|
3562
3952
|
}
|
|
3563
3953
|
}
|
|
@@ -3580,6 +3970,10 @@ var init_definitions = __esm({
|
|
|
3580
3970
|
},
|
|
3581
3971
|
propertyValue: {
|
|
3582
3972
|
description: "Value to set (string, number, boolean, or object for Vector3/Color3/UDim2)"
|
|
3973
|
+
},
|
|
3974
|
+
instance_id: {
|
|
3975
|
+
type: "string",
|
|
3976
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3583
3977
|
}
|
|
3584
3978
|
},
|
|
3585
3979
|
required: ["instancePath", "propertyName", "propertyValue"]
|
|
@@ -3603,6 +3997,10 @@ var init_definitions = __esm({
|
|
|
3603
3997
|
},
|
|
3604
3998
|
propertyValue: {
|
|
3605
3999
|
description: "Value to set (string, number, boolean, or object for Vector3/Color3/UDim2)"
|
|
4000
|
+
},
|
|
4001
|
+
instance_id: {
|
|
4002
|
+
type: "string",
|
|
4003
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3606
4004
|
}
|
|
3607
4005
|
},
|
|
3608
4006
|
required: ["paths", "propertyName", "propertyValue"]
|
|
@@ -3623,6 +4021,10 @@ var init_definitions = __esm({
|
|
|
3623
4021
|
propertyName: {
|
|
3624
4022
|
type: "string",
|
|
3625
4023
|
description: "Property name"
|
|
4024
|
+
},
|
|
4025
|
+
instance_id: {
|
|
4026
|
+
type: "string",
|
|
4027
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3626
4028
|
}
|
|
3627
4029
|
},
|
|
3628
4030
|
required: ["paths", "propertyName"]
|
|
@@ -3642,6 +4044,10 @@ var init_definitions = __esm({
|
|
|
3642
4044
|
properties: {
|
|
3643
4045
|
type: "object",
|
|
3644
4046
|
description: "Map of property name to value"
|
|
4047
|
+
},
|
|
4048
|
+
instance_id: {
|
|
4049
|
+
type: "string",
|
|
4050
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3645
4051
|
}
|
|
3646
4052
|
},
|
|
3647
4053
|
required: ["instancePath", "properties"]
|
|
@@ -3670,6 +4076,10 @@ var init_definitions = __esm({
|
|
|
3670
4076
|
properties: {
|
|
3671
4077
|
type: "object",
|
|
3672
4078
|
description: "Properties to set on creation"
|
|
4079
|
+
},
|
|
4080
|
+
instance_id: {
|
|
4081
|
+
type: "string",
|
|
4082
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3673
4083
|
}
|
|
3674
4084
|
},
|
|
3675
4085
|
required: ["className", "parent"]
|
|
@@ -3707,6 +4117,10 @@ var init_definitions = __esm({
|
|
|
3707
4117
|
required: ["className", "parent"]
|
|
3708
4118
|
},
|
|
3709
4119
|
description: "Objects to create"
|
|
4120
|
+
},
|
|
4121
|
+
instance_id: {
|
|
4122
|
+
type: "string",
|
|
4123
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3710
4124
|
}
|
|
3711
4125
|
},
|
|
3712
4126
|
required: ["objects"]
|
|
@@ -3722,6 +4136,10 @@ var init_definitions = __esm({
|
|
|
3722
4136
|
instancePath: {
|
|
3723
4137
|
type: "string",
|
|
3724
4138
|
description: "Instance path (dot notation)"
|
|
4139
|
+
},
|
|
4140
|
+
instance_id: {
|
|
4141
|
+
type: "string",
|
|
4142
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3725
4143
|
}
|
|
3726
4144
|
},
|
|
3727
4145
|
required: ["instancePath"]
|
|
@@ -3775,6 +4193,10 @@ var init_definitions = __esm({
|
|
|
3775
4193
|
description: "Different parent per duplicate"
|
|
3776
4194
|
}
|
|
3777
4195
|
}
|
|
4196
|
+
},
|
|
4197
|
+
instance_id: {
|
|
4198
|
+
type: "string",
|
|
4199
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3778
4200
|
}
|
|
3779
4201
|
},
|
|
3780
4202
|
required: ["instancePath", "count"]
|
|
@@ -3837,6 +4259,10 @@ var init_definitions = __esm({
|
|
|
3837
4259
|
required: ["instancePath", "count"]
|
|
3838
4260
|
},
|
|
3839
4261
|
description: "Duplication operations"
|
|
4262
|
+
},
|
|
4263
|
+
instance_id: {
|
|
4264
|
+
type: "string",
|
|
4265
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3840
4266
|
}
|
|
3841
4267
|
},
|
|
3842
4268
|
required: ["duplications"]
|
|
@@ -3862,6 +4288,10 @@ var init_definitions = __esm({
|
|
|
3862
4288
|
endLine: {
|
|
3863
4289
|
type: "number",
|
|
3864
4290
|
description: "End line (inclusive)"
|
|
4291
|
+
},
|
|
4292
|
+
instance_id: {
|
|
4293
|
+
type: "string",
|
|
4294
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3865
4295
|
}
|
|
3866
4296
|
},
|
|
3867
4297
|
required: ["instancePath"]
|
|
@@ -3881,6 +4311,10 @@ var init_definitions = __esm({
|
|
|
3881
4311
|
source: {
|
|
3882
4312
|
type: "string",
|
|
3883
4313
|
description: "New source code"
|
|
4314
|
+
},
|
|
4315
|
+
instance_id: {
|
|
4316
|
+
type: "string",
|
|
4317
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3884
4318
|
}
|
|
3885
4319
|
},
|
|
3886
4320
|
required: ["instancePath", "source"]
|
|
@@ -3908,6 +4342,10 @@ var init_definitions = __esm({
|
|
|
3908
4342
|
startLine: {
|
|
3909
4343
|
type: "number",
|
|
3910
4344
|
description: "Optional 1-indexed line where old_string begins. When provided, skips uniqueness check and requires old_string to match starting at that exact line."
|
|
4345
|
+
},
|
|
4346
|
+
instance_id: {
|
|
4347
|
+
type: "string",
|
|
4348
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3911
4349
|
}
|
|
3912
4350
|
},
|
|
3913
4351
|
required: ["instancePath", "old_string", "new_string"]
|
|
@@ -3931,6 +4369,10 @@ var init_definitions = __esm({
|
|
|
3931
4369
|
newContent: {
|
|
3932
4370
|
type: "string",
|
|
3933
4371
|
description: "Content to insert"
|
|
4372
|
+
},
|
|
4373
|
+
instance_id: {
|
|
4374
|
+
type: "string",
|
|
4375
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3934
4376
|
}
|
|
3935
4377
|
},
|
|
3936
4378
|
required: ["instancePath", "newContent"]
|
|
@@ -3954,6 +4396,10 @@ var init_definitions = __esm({
|
|
|
3954
4396
|
endLine: {
|
|
3955
4397
|
type: "number",
|
|
3956
4398
|
description: "End line (inclusive)"
|
|
4399
|
+
},
|
|
4400
|
+
instance_id: {
|
|
4401
|
+
type: "string",
|
|
4402
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3957
4403
|
}
|
|
3958
4404
|
},
|
|
3959
4405
|
required: ["instancePath", "startLine", "endLine"]
|
|
@@ -3981,6 +4427,10 @@ var init_definitions = __esm({
|
|
|
3981
4427
|
valueType: {
|
|
3982
4428
|
type: "string",
|
|
3983
4429
|
description: "Type hint if needed"
|
|
4430
|
+
},
|
|
4431
|
+
instance_id: {
|
|
4432
|
+
type: "string",
|
|
4433
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3984
4434
|
}
|
|
3985
4435
|
},
|
|
3986
4436
|
required: ["instancePath", "attributeName", "attributeValue"]
|
|
@@ -3996,6 +4446,10 @@ var init_definitions = __esm({
|
|
|
3996
4446
|
instancePath: {
|
|
3997
4447
|
type: "string",
|
|
3998
4448
|
description: "Instance path (dot notation)"
|
|
4449
|
+
},
|
|
4450
|
+
instance_id: {
|
|
4451
|
+
type: "string",
|
|
4452
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
3999
4453
|
}
|
|
4000
4454
|
},
|
|
4001
4455
|
required: ["instancePath"]
|
|
@@ -4015,6 +4469,10 @@ var init_definitions = __esm({
|
|
|
4015
4469
|
attributeName: {
|
|
4016
4470
|
type: "string",
|
|
4017
4471
|
description: "Attribute name"
|
|
4472
|
+
},
|
|
4473
|
+
instance_id: {
|
|
4474
|
+
type: "string",
|
|
4475
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4018
4476
|
}
|
|
4019
4477
|
},
|
|
4020
4478
|
required: ["instancePath", "attributeName"]
|
|
@@ -4031,6 +4489,10 @@ var init_definitions = __esm({
|
|
|
4031
4489
|
instancePath: {
|
|
4032
4490
|
type: "string",
|
|
4033
4491
|
description: "Instance path (dot notation)"
|
|
4492
|
+
},
|
|
4493
|
+
instance_id: {
|
|
4494
|
+
type: "string",
|
|
4495
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4034
4496
|
}
|
|
4035
4497
|
},
|
|
4036
4498
|
required: ["instancePath"]
|
|
@@ -4050,6 +4512,10 @@ var init_definitions = __esm({
|
|
|
4050
4512
|
tagName: {
|
|
4051
4513
|
type: "string",
|
|
4052
4514
|
description: "Tag name"
|
|
4515
|
+
},
|
|
4516
|
+
instance_id: {
|
|
4517
|
+
type: "string",
|
|
4518
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4053
4519
|
}
|
|
4054
4520
|
},
|
|
4055
4521
|
required: ["instancePath", "tagName"]
|
|
@@ -4069,6 +4535,10 @@ var init_definitions = __esm({
|
|
|
4069
4535
|
tagName: {
|
|
4070
4536
|
type: "string",
|
|
4071
4537
|
description: "Tag name"
|
|
4538
|
+
},
|
|
4539
|
+
instance_id: {
|
|
4540
|
+
type: "string",
|
|
4541
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4072
4542
|
}
|
|
4073
4543
|
},
|
|
4074
4544
|
required: ["instancePath", "tagName"]
|
|
@@ -4084,6 +4554,10 @@ var init_definitions = __esm({
|
|
|
4084
4554
|
tagName: {
|
|
4085
4555
|
type: "string",
|
|
4086
4556
|
description: "Tag name"
|
|
4557
|
+
},
|
|
4558
|
+
instance_id: {
|
|
4559
|
+
type: "string",
|
|
4560
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4087
4561
|
}
|
|
4088
4562
|
},
|
|
4089
4563
|
required: ["tagName"]
|
|
@@ -4096,7 +4570,12 @@ var init_definitions = __esm({
|
|
|
4096
4570
|
description: "Get all currently selected objects",
|
|
4097
4571
|
inputSchema: {
|
|
4098
4572
|
type: "object",
|
|
4099
|
-
properties: {
|
|
4573
|
+
properties: {
|
|
4574
|
+
instance_id: {
|
|
4575
|
+
type: "string",
|
|
4576
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4100
4579
|
}
|
|
4101
4580
|
},
|
|
4102
4581
|
// === Luau Execution ===
|
|
@@ -4114,6 +4593,10 @@ var init_definitions = __esm({
|
|
|
4114
4593
|
target: {
|
|
4115
4594
|
type: "string",
|
|
4116
4595
|
description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
|
|
4596
|
+
},
|
|
4597
|
+
instance_id: {
|
|
4598
|
+
type: "string",
|
|
4599
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4117
4600
|
}
|
|
4118
4601
|
},
|
|
4119
4602
|
required: ["code"]
|
|
@@ -4129,6 +4612,10 @@ var init_definitions = __esm({
|
|
|
4129
4612
|
code: {
|
|
4130
4613
|
type: "string",
|
|
4131
4614
|
description: "Luau code to execute. Use return ... to get a value back."
|
|
4615
|
+
},
|
|
4616
|
+
instance_id: {
|
|
4617
|
+
type: "string",
|
|
4618
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4132
4619
|
}
|
|
4133
4620
|
},
|
|
4134
4621
|
required: ["code"]
|
|
@@ -4148,6 +4635,10 @@ var init_definitions = __esm({
|
|
|
4148
4635
|
target: {
|
|
4149
4636
|
type: "string",
|
|
4150
4637
|
description: 'Client target: "client-1" (default), "client-2", etc.'
|
|
4638
|
+
},
|
|
4639
|
+
instance_id: {
|
|
4640
|
+
type: "string",
|
|
4641
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4151
4642
|
}
|
|
4152
4643
|
},
|
|
4153
4644
|
required: ["code"]
|
|
@@ -4197,6 +4688,10 @@ var init_definitions = __esm({
|
|
|
4197
4688
|
type: "string",
|
|
4198
4689
|
enum: ["Script", "LocalScript", "ModuleScript"],
|
|
4199
4690
|
description: "Only search scripts of this class type"
|
|
4691
|
+
},
|
|
4692
|
+
instance_id: {
|
|
4693
|
+
type: "string",
|
|
4694
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4200
4695
|
}
|
|
4201
4696
|
},
|
|
4202
4697
|
required: ["pattern"]
|
|
@@ -4218,6 +4713,10 @@ var init_definitions = __esm({
|
|
|
4218
4713
|
numPlayers: {
|
|
4219
4714
|
type: "number",
|
|
4220
4715
|
description: "Number of client players (1-8). Triggers server + clients mode via TestService."
|
|
4716
|
+
},
|
|
4717
|
+
instance_id: {
|
|
4718
|
+
type: "string",
|
|
4719
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4221
4720
|
}
|
|
4222
4721
|
},
|
|
4223
4722
|
required: ["mode"]
|
|
@@ -4229,7 +4728,12 @@ var init_definitions = __esm({
|
|
|
4229
4728
|
description: "Stop playtest and return all captured output.",
|
|
4230
4729
|
inputSchema: {
|
|
4231
4730
|
type: "object",
|
|
4232
|
-
properties: {
|
|
4731
|
+
properties: {
|
|
4732
|
+
instance_id: {
|
|
4733
|
+
type: "string",
|
|
4734
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4233
4737
|
}
|
|
4234
4738
|
},
|
|
4235
4739
|
{
|
|
@@ -4242,6 +4746,10 @@ var init_definitions = __esm({
|
|
|
4242
4746
|
target: {
|
|
4243
4747
|
type: "string",
|
|
4244
4748
|
description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
|
|
4749
|
+
},
|
|
4750
|
+
instance_id: {
|
|
4751
|
+
type: "string",
|
|
4752
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4245
4753
|
}
|
|
4246
4754
|
}
|
|
4247
4755
|
}
|
|
@@ -4268,6 +4776,10 @@ var init_definitions = __esm({
|
|
|
4268
4776
|
filter: {
|
|
4269
4777
|
type: "string",
|
|
4270
4778
|
description: "Plain substring matched against each entry's message (no pattern semantics; literal text). Applied after since, before tail."
|
|
4779
|
+
},
|
|
4780
|
+
instance_id: {
|
|
4781
|
+
type: "string",
|
|
4782
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4271
4783
|
}
|
|
4272
4784
|
}
|
|
4273
4785
|
}
|
|
@@ -4289,7 +4801,12 @@ var init_definitions = __esm({
|
|
|
4289
4801
|
description: "Undo the last change in Roblox Studio. Uses ChangeHistoryService to reverse the most recent operation.",
|
|
4290
4802
|
inputSchema: {
|
|
4291
4803
|
type: "object",
|
|
4292
|
-
properties: {
|
|
4804
|
+
properties: {
|
|
4805
|
+
instance_id: {
|
|
4806
|
+
type: "string",
|
|
4807
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4293
4810
|
}
|
|
4294
4811
|
},
|
|
4295
4812
|
{
|
|
@@ -4298,7 +4815,12 @@ var init_definitions = __esm({
|
|
|
4298
4815
|
description: "Redo the last undone change in Roblox Studio. Uses ChangeHistoryService to reapply the most recently undone operation.",
|
|
4299
4816
|
inputSchema: {
|
|
4300
4817
|
type: "object",
|
|
4301
|
-
properties: {
|
|
4818
|
+
properties: {
|
|
4819
|
+
instance_id: {
|
|
4820
|
+
type: "string",
|
|
4821
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4822
|
+
}
|
|
4823
|
+
}
|
|
4302
4824
|
}
|
|
4303
4825
|
},
|
|
4304
4826
|
// === Build Library ===
|
|
@@ -4321,6 +4843,10 @@ var init_definitions = __esm({
|
|
|
4321
4843
|
type: "string",
|
|
4322
4844
|
enum: ["medieval", "modern", "nature", "scifi", "misc"],
|
|
4323
4845
|
description: "Style category for the build (default: misc)"
|
|
4846
|
+
},
|
|
4847
|
+
instance_id: {
|
|
4848
|
+
type: "string",
|
|
4849
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4324
4850
|
}
|
|
4325
4851
|
},
|
|
4326
4852
|
required: ["instancePath"]
|
|
@@ -4470,6 +4996,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4470
4996
|
type: "array",
|
|
4471
4997
|
items: { type: "number" },
|
|
4472
4998
|
description: "World position offset [X, Y, Z]"
|
|
4999
|
+
},
|
|
5000
|
+
instance_id: {
|
|
5001
|
+
type: "string",
|
|
5002
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4473
5003
|
}
|
|
4474
5004
|
},
|
|
4475
5005
|
required: ["buildData", "targetPath"]
|
|
@@ -4504,6 +5034,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4504
5034
|
maxResults: {
|
|
4505
5035
|
type: "number",
|
|
4506
5036
|
description: "Max results to return (default: 50)"
|
|
5037
|
+
},
|
|
5038
|
+
instance_id: {
|
|
5039
|
+
type: "string",
|
|
5040
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4507
5041
|
}
|
|
4508
5042
|
}
|
|
4509
5043
|
}
|
|
@@ -4588,6 +5122,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4588
5122
|
targetPath: {
|
|
4589
5123
|
type: "string",
|
|
4590
5124
|
description: "Parent instance path for the scene (default: game.Workspace)"
|
|
5125
|
+
},
|
|
5126
|
+
instance_id: {
|
|
5127
|
+
type: "string",
|
|
5128
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4591
5129
|
}
|
|
4592
5130
|
},
|
|
4593
5131
|
required: ["sceneData"]
|
|
@@ -4685,6 +5223,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4685
5223
|
z: { type: "number" }
|
|
4686
5224
|
},
|
|
4687
5225
|
description: "Optional world position to place the asset"
|
|
5226
|
+
},
|
|
5227
|
+
instance_id: {
|
|
5228
|
+
type: "string",
|
|
5229
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4688
5230
|
}
|
|
4689
5231
|
},
|
|
4690
5232
|
required: ["assetId"]
|
|
@@ -4708,6 +5250,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4708
5250
|
maxDepth: {
|
|
4709
5251
|
type: "number",
|
|
4710
5252
|
description: "Max hierarchy traversal depth (default: 10)"
|
|
5253
|
+
},
|
|
5254
|
+
instance_id: {
|
|
5255
|
+
type: "string",
|
|
5256
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4711
5257
|
}
|
|
4712
5258
|
},
|
|
4713
5259
|
required: ["assetId"]
|
|
@@ -4755,7 +5301,12 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4755
5301
|
description: 'Capture a screenshot of the Roblox Studio viewport and return it as a PNG image. Requires EditableImage API to be enabled: Game Settings > Security > "Allow Mesh / Image APIs". Only works in Edit mode with the viewport visible.',
|
|
4756
5302
|
inputSchema: {
|
|
4757
5303
|
type: "object",
|
|
4758
|
-
properties: {
|
|
5304
|
+
properties: {
|
|
5305
|
+
instance_id: {
|
|
5306
|
+
type: "string",
|
|
5307
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5308
|
+
}
|
|
5309
|
+
}
|
|
4759
5310
|
}
|
|
4760
5311
|
},
|
|
4761
5312
|
// === Input Simulation ===
|
|
@@ -4792,6 +5343,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4792
5343
|
target: {
|
|
4793
5344
|
type: "string",
|
|
4794
5345
|
description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
|
|
5346
|
+
},
|
|
5347
|
+
instance_id: {
|
|
5348
|
+
type: "string",
|
|
5349
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4795
5350
|
}
|
|
4796
5351
|
},
|
|
4797
5352
|
required: ["action", "x", "y"]
|
|
@@ -4820,6 +5375,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4820
5375
|
target: {
|
|
4821
5376
|
type: "string",
|
|
4822
5377
|
description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
|
|
5378
|
+
},
|
|
5379
|
+
instance_id: {
|
|
5380
|
+
type: "string",
|
|
5381
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4823
5382
|
}
|
|
4824
5383
|
},
|
|
4825
5384
|
required: ["keyCode"]
|
|
@@ -4853,6 +5412,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4853
5412
|
target: {
|
|
4854
5413
|
type: "string",
|
|
4855
5414
|
description: 'Instance target: "edit" (default), "server", "client-1", "client-2", etc.'
|
|
5415
|
+
},
|
|
5416
|
+
instance_id: {
|
|
5417
|
+
type: "string",
|
|
5418
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4856
5419
|
}
|
|
4857
5420
|
}
|
|
4858
5421
|
}
|
|
@@ -4872,6 +5435,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4872
5435
|
targetParentPath: {
|
|
4873
5436
|
type: "string",
|
|
4874
5437
|
description: "Path of the parent to place the clone under"
|
|
5438
|
+
},
|
|
5439
|
+
instance_id: {
|
|
5440
|
+
type: "string",
|
|
5441
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4875
5442
|
}
|
|
4876
5443
|
},
|
|
4877
5444
|
required: ["instancePath", "targetParentPath"]
|
|
@@ -4896,6 +5463,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4896
5463
|
classFilter: {
|
|
4897
5464
|
type: "string",
|
|
4898
5465
|
description: 'Only include instances of this class (uses IsA, so "BasePart" matches Part, MeshPart, etc.)'
|
|
5466
|
+
},
|
|
5467
|
+
instance_id: {
|
|
5468
|
+
type: "string",
|
|
5469
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4899
5470
|
}
|
|
4900
5471
|
},
|
|
4901
5472
|
required: ["instancePath"]
|
|
@@ -4915,6 +5486,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4915
5486
|
instancePathB: {
|
|
4916
5487
|
type: "string",
|
|
4917
5488
|
description: "Second instance path"
|
|
5489
|
+
},
|
|
5490
|
+
instance_id: {
|
|
5491
|
+
type: "string",
|
|
5492
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4918
5493
|
}
|
|
4919
5494
|
},
|
|
4920
5495
|
required: ["instancePathA", "instancePathB"]
|
|
@@ -4935,6 +5510,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4935
5510
|
messageType: {
|
|
4936
5511
|
type: "string",
|
|
4937
5512
|
description: 'Filter by message type (e.g. "Enum.MessageType.MessageOutput", "Enum.MessageType.MessageWarning", "Enum.MessageType.MessageError")'
|
|
5513
|
+
},
|
|
5514
|
+
instance_id: {
|
|
5515
|
+
type: "string",
|
|
5516
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4938
5517
|
}
|
|
4939
5518
|
}
|
|
4940
5519
|
}
|
|
@@ -4954,6 +5533,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4954
5533
|
attributes: {
|
|
4955
5534
|
type: "object",
|
|
4956
5535
|
description: "Map of attribute names to values. Supports Vector3, Color3, UDim2 via _type convention."
|
|
5536
|
+
},
|
|
5537
|
+
instance_id: {
|
|
5538
|
+
type: "string",
|
|
5539
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4957
5540
|
}
|
|
4958
5541
|
},
|
|
4959
5542
|
required: ["instancePath", "attributes"]
|
|
@@ -4975,6 +5558,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
4975
5558
|
type: "array",
|
|
4976
5559
|
items: { type: "string" },
|
|
4977
5560
|
description: "Optional DeveloperMemoryTag whitelist. Unknown tag names return 0 + unknown_tags list."
|
|
5561
|
+
},
|
|
5562
|
+
instance_id: {
|
|
5563
|
+
type: "string",
|
|
5564
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
4978
5565
|
}
|
|
4979
5566
|
}
|
|
4980
5567
|
}
|
|
@@ -5000,6 +5587,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5000
5587
|
type: "string",
|
|
5001
5588
|
enum: ["edit", "server"],
|
|
5002
5589
|
description: 'Which DataModel to read from (default: "edit"). "server" serializes live runtime state during a playtest.'
|
|
5590
|
+
},
|
|
5591
|
+
instance_id: {
|
|
5592
|
+
type: "string",
|
|
5593
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5003
5594
|
}
|
|
5004
5595
|
},
|
|
5005
5596
|
required: ["instance_paths", "output_path"]
|
|
@@ -5034,6 +5625,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5034
5625
|
type: "string",
|
|
5035
5626
|
enum: ["edit", "server"],
|
|
5036
5627
|
description: 'Which DataModel to import into (default: "edit"). "server" parents into the live play-server DM.'
|
|
5628
|
+
},
|
|
5629
|
+
instance_id: {
|
|
5630
|
+
type: "string",
|
|
5631
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5037
5632
|
}
|
|
5038
5633
|
},
|
|
5039
5634
|
required: ["source", "parent_path"]
|
|
@@ -5079,6 +5674,10 @@ part(0,2,0,2,1,1,"b")`,
|
|
|
5079
5674
|
maxReplacements: {
|
|
5080
5675
|
type: "number",
|
|
5081
5676
|
description: "Safety limit on total replacements (default: 1000)"
|
|
5677
|
+
},
|
|
5678
|
+
instance_id: {
|
|
5679
|
+
type: "string",
|
|
5680
|
+
description: "Which connected Studio place to target. Required when multiple places are connected; omit when one. Use get_connected_instances to list available IDs."
|
|
5082
5681
|
}
|
|
5083
5682
|
},
|
|
5084
5683
|
required: ["pattern", "replacement"]
|