@openfairygui/mcp 0.3.1 → 0.5.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/index.cjs +26 -2
- package/dist/index.d.cts +65 -640
- package/dist/index.d.mts +66 -641
- package/dist/index.mjs +2 -2
- package/dist/{stdio-HOHWH0x4.mjs → stdio-BNobuRxx.mjs} +243 -528
- package/dist/{stdio-N6Yxacus.cjs → stdio-CB98zdOf.cjs} +246 -531
- package/dist/stdio.cjs +1 -1
- package/dist/stdio.mjs +1 -1
- package/package.json +5 -5
- package/src/contract-schema.ts +29 -0
- package/src/index.ts +6 -1
- package/src/prompt-definitions.ts +5 -0
- package/src/resource-definitions.ts +59 -0
- package/src/server.ts +22 -7
- package/src/tool-definitions.ts +17 -306
- package/src/tool-handler.ts +22 -133
- package/src/tool-metadata.ts +181 -0
|
@@ -21,10 +21,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
21
21
|
}) : target, mod));
|
|
22
22
|
//#endregion
|
|
23
23
|
let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
24
|
+
let _modelcontextprotocol_sdk_types_js = require("@modelcontextprotocol/sdk/types.js");
|
|
24
25
|
let _openfairygui_backend_node = require("@openfairygui/backend/node");
|
|
25
26
|
let node_module = require("node:module");
|
|
26
|
-
let _openfairygui_backend = require("@openfairygui/backend");
|
|
27
27
|
let zod = require("zod");
|
|
28
|
+
let _openfairygui_backend = require("@openfairygui/backend");
|
|
29
|
+
let _openfairygui_backend_docs = require("@openfairygui/backend/docs");
|
|
28
30
|
let _modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
29
31
|
let node_path = require("node:path");
|
|
30
32
|
node_path = __toESM(node_path);
|
|
@@ -76,6 +78,11 @@ const OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS = [
|
|
|
76
78
|
description: "Guide a client through backend-owned revision checks without inventing operation grammar.",
|
|
77
79
|
text: [
|
|
78
80
|
"Use openfairygui_backend_get_session to read the current revision before mutation.",
|
|
81
|
+
"Use openfairygui_backend_get_project_outline for identities, then openfairygui_backend_query_entity for current properties at the returned revision.",
|
|
82
|
+
"For settings edits, query target {kind:\"project\"} or {kind:\"package\",selector:{packageId}}; copy entity.properties.settings, change the requested fields, and submit the complete settings to updateProjectSettings or updatePackageSettings.",
|
|
83
|
+
"Read openfairygui://contracts/operations and openfairygui://contracts/operations/{kind} for the current operation names and exact JSON parameters.",
|
|
84
|
+
"Call openfairygui_backend_preflight_transaction with the queried revision and planned operations to execute and discard an isolated preview; inspect the backend diagnostics.",
|
|
85
|
+
"A successful preview does not reserve a revision or guarantee save. Apply the same batch with expectedRevision set to the preview baseRevision; refresh properties and re-plan on stale revision.",
|
|
79
86
|
"Call openfairygui_backend_apply_transaction with sessionId, expectedRevision, and backend/UAM-owned operations.",
|
|
80
87
|
"If the backend returns a stale revision error, refresh the session snapshot and re-plan against the new revision.",
|
|
81
88
|
"Do not invent selector grammar, transaction grammar, or operation payload semantics at the MCP layer."
|
|
@@ -119,6 +126,32 @@ function registerOpenFairyGuiBackendPrompts(server) {
|
|
|
119
126
|
}, () => promptResult(definition.text));
|
|
120
127
|
}
|
|
121
128
|
//#endregion
|
|
129
|
+
//#region src/contract-schema.ts
|
|
130
|
+
const CONTRACT_SNAPSHOT = (0, _openfairygui_backend_docs.getInstalledContractSnapshot)();
|
|
131
|
+
function contractObjectSchema(schema) {
|
|
132
|
+
const result = zod.z.fromJSONSchema({
|
|
133
|
+
...schema,
|
|
134
|
+
$defs: CONTRACT_SNAPSHOT.$defs
|
|
135
|
+
});
|
|
136
|
+
if (!(result instanceof zod.z.ZodObject)) throw new TypeError("Tool contract must be an object");
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
/** Decode only generated Uint8Array locations; arbitrary JSON metadata is not rewritten. */
|
|
140
|
+
function decodeToolBytes(input, paths) {
|
|
141
|
+
if (!paths.length) return input;
|
|
142
|
+
const result = structuredClone(input);
|
|
143
|
+
function visit(value, parts) {
|
|
144
|
+
if (!parts.length) return value === null ? value : Uint8Array.from(value);
|
|
145
|
+
if (!value || typeof value !== "object") return value;
|
|
146
|
+
const [key, ...rest] = parts;
|
|
147
|
+
const record = value;
|
|
148
|
+
for (const name of key === "*" ? Object.keys(record) : [key]) if (Object.hasOwn(record, name)) record[name] = visit(record[name], rest);
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
for (const parts of paths) visit(result, parts);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
122
155
|
//#region src/resource-definitions.ts
|
|
123
156
|
const JSON_MIME_TYPE = "application/json";
|
|
124
157
|
function firstVariable(value) {
|
|
@@ -133,12 +166,69 @@ function jsonResource(uri, backendResult) {
|
|
|
133
166
|
}
|
|
134
167
|
const OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI = "openfairygui://backend/capabilities";
|
|
135
168
|
const OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES = [
|
|
169
|
+
"openfairygui://docs/methods/{method}",
|
|
170
|
+
"openfairygui://docs/cli/{command}",
|
|
171
|
+
_openfairygui_backend.BACKEND_DIAGNOSTIC_TEMPLATE,
|
|
172
|
+
_openfairygui_backend_docs.OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE,
|
|
136
173
|
"openfairygui://backend/session/{sessionId}",
|
|
137
174
|
"openfairygui://backend/session/{sessionId}/outline",
|
|
138
175
|
"openfairygui://backend/cache/{sessionId}",
|
|
139
176
|
"openfairygui://backend/job/{sessionId}/{jobId}"
|
|
140
177
|
];
|
|
141
178
|
function registerOpenFairyGuiBackendResources(server, runtime) {
|
|
179
|
+
server.registerResource("openfairygui_docs_index", _openfairygui_backend_docs.OPENFAIRYGUI_DOCS_INDEX_URI, {
|
|
180
|
+
title: "Installed Documentation",
|
|
181
|
+
description: "Offline documentation IDs, URIs, installed package version and contract digest shared with the CLI.",
|
|
182
|
+
mimeType: JSON_MIME_TYPE
|
|
183
|
+
}, (uri) => jsonResource(uri, (0, _openfairygui_backend_docs.getInstalledDocumentationIndex)()));
|
|
184
|
+
function installedDocument(uri, id) {
|
|
185
|
+
const document = (0, _openfairygui_backend_docs.readInstalledDocumentation)(id);
|
|
186
|
+
return { contents: [{
|
|
187
|
+
uri: uri.toString(),
|
|
188
|
+
mimeType: document.mimeType,
|
|
189
|
+
text: document.text
|
|
190
|
+
}] };
|
|
191
|
+
}
|
|
192
|
+
for (const id of [
|
|
193
|
+
"workflow",
|
|
194
|
+
"restore-limits",
|
|
195
|
+
"skill",
|
|
196
|
+
"contracts"
|
|
197
|
+
]) server.registerResource(`openfairygui_docs_${id}`, `openfairygui://docs/${id}`, {
|
|
198
|
+
title: `Installed ${id}`,
|
|
199
|
+
description: "Read the installed-version corpus without repository or network access.",
|
|
200
|
+
mimeType: id === "contracts" ? JSON_MIME_TYPE : "text/markdown"
|
|
201
|
+
}, (uri) => installedDocument(uri, id));
|
|
202
|
+
server.registerResource("openfairygui_docs_method", new _modelcontextprotocol_sdk_server_mcp_js.ResourceTemplate("openfairygui://docs/methods/{method}", { list: void 0 }), {
|
|
203
|
+
title: "Installed Method Contract",
|
|
204
|
+
description: "Read self-contained Backend/MCP wire input/output schemas and metadata.",
|
|
205
|
+
mimeType: JSON_MIME_TYPE
|
|
206
|
+
}, (uri, variables) => installedDocument(uri, `methods/${firstVariable(variables.method)}`));
|
|
207
|
+
server.registerResource("openfairygui_diagnostic_catalog", _openfairygui_backend.BACKEND_DIAGNOSTICS_URI, {
|
|
208
|
+
title: "Diagnostic Recovery Catalog",
|
|
209
|
+
description: "Complete formal diagnostic ownership and recovery guidance; never automatic repair.",
|
|
210
|
+
mimeType: JSON_MIME_TYPE
|
|
211
|
+
}, (uri) => jsonResource(uri, (0, _openfairygui_backend.getBackendDiagnosticCatalog)()));
|
|
212
|
+
server.registerResource("openfairygui_docs_cli", new _modelcontextprotocol_sdk_server_mcp_js.ResourceTemplate("openfairygui://docs/cli/{command}", { list: void 0 }), {
|
|
213
|
+
title: "Installed CLI Output Contract",
|
|
214
|
+
description: "Read a generated, self-contained CLI JSON envelope schema.",
|
|
215
|
+
mimeType: JSON_MIME_TYPE
|
|
216
|
+
}, (uri, variables) => installedDocument(uri, `cli/${decodeURIComponent(firstVariable(variables.command))}`));
|
|
217
|
+
server.registerResource("openfairygui_diagnostic_guide", new _modelcontextprotocol_sdk_server_mcp_js.ResourceTemplate(_openfairygui_backend.BACKEND_DIAGNOSTIC_TEMPLATE, { list: void 0 }), {
|
|
218
|
+
title: "Diagnostic Recovery Guide",
|
|
219
|
+
description: "Read the recovery boundary for one stable diagnostic code.",
|
|
220
|
+
mimeType: JSON_MIME_TYPE
|
|
221
|
+
}, (uri, variables) => jsonResource(uri, (0, _openfairygui_backend.getBackendDiagnosticGuide)(firstVariable(variables.code))));
|
|
222
|
+
server.registerResource("openfairygui_operation_catalog", _openfairygui_backend_docs.OPENFAIRYGUI_OPERATION_CATALOG_URI, {
|
|
223
|
+
title: "UAM Operation Catalog",
|
|
224
|
+
description: "Discover current operations and their generated JSON schemas.",
|
|
225
|
+
mimeType: JSON_MIME_TYPE
|
|
226
|
+
}, (uri) => jsonResource(uri, (0, _openfairygui_backend_docs.getOpenFairyGuiOperationCatalog)()));
|
|
227
|
+
server.registerResource("openfairygui_operation_schema", new _modelcontextprotocol_sdk_server_mcp_js.ResourceTemplate(_openfairygui_backend_docs.OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE, { list: void 0 }), {
|
|
228
|
+
title: "UAM Operation Schema",
|
|
229
|
+
description: "Read the precise Core-derived JSON wire schema for one operation. Structure is not semantic preflight.",
|
|
230
|
+
mimeType: JSON_MIME_TYPE
|
|
231
|
+
}, (uri, variables) => jsonResource(uri, (0, _openfairygui_backend_docs.getOpenFairyGuiOperationSchema)(firstVariable(variables.kind))));
|
|
142
232
|
server.registerResource("openfairygui_backend_capabilities", OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, {
|
|
143
233
|
title: "OpenFairyGUI Backend Capabilities",
|
|
144
234
|
description: "Read the backend capability and version envelope as JSON.",
|
|
@@ -169,348 +259,13 @@ function registerOpenFairyGuiBackendResources(server, runtime) {
|
|
|
169
259
|
})));
|
|
170
260
|
}
|
|
171
261
|
//#endregion
|
|
172
|
-
//#region src/tool-
|
|
173
|
-
const
|
|
174
|
-
const OPENFAIRYGUI_BACKEND_TOOL_NAMES = [
|
|
175
|
-
"openfairygui_backend_get_capabilities",
|
|
176
|
-
"openfairygui_backend_open_session",
|
|
177
|
-
"openfairygui_backend_open_project_session",
|
|
178
|
-
"openfairygui_backend_get_session",
|
|
179
|
-
"openfairygui_backend_get_project_outline",
|
|
180
|
-
"openfairygui_backend_validate_session",
|
|
181
|
-
"openfairygui_backend_apply_transaction",
|
|
182
|
-
"openfairygui_backend_save_session",
|
|
183
|
-
"openfairygui_backend_materialize_session",
|
|
184
|
-
"openfairygui_backend_close_session",
|
|
185
|
-
"openfairygui_backend_get_events",
|
|
186
|
-
"openfairygui_backend_get_job",
|
|
187
|
-
"openfairygui_backend_list_jobs",
|
|
188
|
-
"openfairygui_backend_cancel_job",
|
|
189
|
-
"openfairygui_backend_get_cache_snapshot",
|
|
190
|
-
"openfairygui_backend_refresh_cache"
|
|
191
|
-
];
|
|
192
|
-
const sessionId = zod.z.string().min(1);
|
|
193
|
-
const jobId = zod.z.string().min(1);
|
|
194
|
-
const expectedRevision = zod.z.number().int().nonnegative();
|
|
195
|
-
const limit = zod.z.number().int().nonnegative().optional();
|
|
196
|
-
const identifier = zod.z.string().min(1).max(256);
|
|
197
|
-
function isOpenFairyGuiMcpPayloadWithinBudget(root) {
|
|
198
|
-
const pending = [{
|
|
199
|
-
value: root,
|
|
200
|
-
depth: 0
|
|
201
|
-
}];
|
|
202
|
-
let nodes = 0;
|
|
203
|
-
while (pending.length > 0) {
|
|
204
|
-
const { value, depth } = pending.pop();
|
|
205
|
-
nodes += 1;
|
|
206
|
-
if (nodes > 1e5 || depth > 32) return false;
|
|
207
|
-
if (value === null || typeof value === "boolean") continue;
|
|
208
|
-
if (typeof value === "number") {
|
|
209
|
-
if (!Number.isFinite(value)) return false;
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
if (typeof value === "string") {
|
|
213
|
-
if (value.length > 1e6) return false;
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
if (value instanceof Uint8Array) {
|
|
217
|
-
if (value.byteLength > 8 * 1024 * 1024) return false;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (Array.isArray(value)) {
|
|
221
|
-
if (value.length > 1e4) return false;
|
|
222
|
-
for (const child of value) pending.push({
|
|
223
|
-
value: child,
|
|
224
|
-
depth: depth + 1
|
|
225
|
-
});
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
if (typeof value !== "object") return false;
|
|
229
|
-
const entries = Object.entries(value);
|
|
230
|
-
if (entries.length > 1e4 || entries.some(([key]) => key.length > 256)) return false;
|
|
231
|
-
for (const [, child] of entries) pending.push({
|
|
232
|
-
value: child,
|
|
233
|
-
depth: depth + 1
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
|
-
const boundedPayload = zod.z.json();
|
|
239
|
-
const bytes = zod.z.array(zod.z.number().int().min(0).max(255)).max(8 * 1024 * 1024);
|
|
240
|
-
const packageSelector = zod.z.object({ packageId: identifier });
|
|
241
|
-
const resourceSelector = zod.z.object({
|
|
242
|
-
packageId: identifier,
|
|
243
|
-
resourceId: identifier
|
|
244
|
-
});
|
|
245
|
-
const componentSelector = zod.z.object({
|
|
246
|
-
packageId: identifier,
|
|
247
|
-
componentResourceId: identifier
|
|
248
|
-
});
|
|
249
|
-
const displayNodeSelector = componentSelector.extend({ displayNodeId: identifier });
|
|
250
|
-
const controllerSelector = componentSelector.extend({ controllerName: identifier });
|
|
251
|
-
const transitionSelector = componentSelector.extend({ transitionName: identifier });
|
|
252
|
-
const folderSelector = packageSelector.extend({
|
|
253
|
-
branch: zod.z.string().max(256).optional(),
|
|
254
|
-
path: zod.z.string().min(1).max(4096)
|
|
255
|
-
});
|
|
256
|
-
const operationBase = { opId: identifier.optional() };
|
|
257
|
-
const operation = zod.z.discriminatedUnion("kind", [
|
|
258
|
-
zod.z.object({
|
|
259
|
-
...operationBase,
|
|
260
|
-
kind: zod.z.literal("updateProjectSettings"),
|
|
261
|
-
settings: boundedPayload
|
|
262
|
-
}),
|
|
263
|
-
zod.z.object({
|
|
264
|
-
...operationBase,
|
|
265
|
-
kind: zod.z.literal("updatePackageSettings"),
|
|
266
|
-
selector: packageSelector,
|
|
267
|
-
settings: boundedPayload
|
|
268
|
-
}),
|
|
269
|
-
zod.z.object({
|
|
270
|
-
...operationBase,
|
|
271
|
-
kind: zod.z.literal("renameResource"),
|
|
272
|
-
selector: resourceSelector,
|
|
273
|
-
newName: identifier
|
|
274
|
-
}),
|
|
275
|
-
zod.z.object({
|
|
276
|
-
...operationBase,
|
|
277
|
-
kind: zod.z.literal("moveResource"),
|
|
278
|
-
selector: resourceSelector,
|
|
279
|
-
toPath: zod.z.string().max(4096)
|
|
280
|
-
}),
|
|
281
|
-
zod.z.object({
|
|
282
|
-
...operationBase,
|
|
283
|
-
kind: zod.z.literal("setResourceFavorite"),
|
|
284
|
-
selector: resourceSelector,
|
|
285
|
-
favorite: zod.z.boolean()
|
|
286
|
-
}),
|
|
287
|
-
zod.z.object({
|
|
288
|
-
...operationBase,
|
|
289
|
-
kind: zod.z.literal("setResourceFolderFavorite"),
|
|
290
|
-
selector: folderSelector,
|
|
291
|
-
favorite: zod.z.boolean()
|
|
292
|
-
}),
|
|
293
|
-
zod.z.object({
|
|
294
|
-
...operationBase,
|
|
295
|
-
kind: zod.z.literal("setResourceFolderAtlas"),
|
|
296
|
-
selector: folderSelector,
|
|
297
|
-
atlas: zod.z.string().max(32)
|
|
298
|
-
}),
|
|
299
|
-
zod.z.object({
|
|
300
|
-
...operationBase,
|
|
301
|
-
kind: zod.z.literal("setResourceExported"),
|
|
302
|
-
selector: resourceSelector,
|
|
303
|
-
exported: zod.z.boolean()
|
|
304
|
-
}),
|
|
305
|
-
zod.z.object({
|
|
306
|
-
...operationBase,
|
|
307
|
-
kind: zod.z.literal("addResourceFolder"),
|
|
308
|
-
selector: packageSelector,
|
|
309
|
-
path: zod.z.string().max(4096),
|
|
310
|
-
branch: zod.z.string().max(256).optional(),
|
|
311
|
-
favorite: zod.z.boolean().optional(),
|
|
312
|
-
atlas: zod.z.string().max(32).optional()
|
|
313
|
-
}),
|
|
314
|
-
zod.z.object({
|
|
315
|
-
...operationBase,
|
|
316
|
-
kind: zod.z.literal("renameResourceFolder"),
|
|
317
|
-
selector: folderSelector,
|
|
318
|
-
newName: identifier
|
|
319
|
-
}),
|
|
320
|
-
zod.z.object({
|
|
321
|
-
...operationBase,
|
|
322
|
-
kind: zod.z.literal("moveResourceFolder"),
|
|
323
|
-
selector: folderSelector,
|
|
324
|
-
toPath: zod.z.string().max(4096)
|
|
325
|
-
}),
|
|
326
|
-
zod.z.object({
|
|
327
|
-
...operationBase,
|
|
328
|
-
kind: zod.z.literal("removeResourceFolder"),
|
|
329
|
-
selector: folderSelector
|
|
330
|
-
}),
|
|
331
|
-
zod.z.object({
|
|
332
|
-
...operationBase,
|
|
333
|
-
kind: zod.z.literal("setImageResourceProps"),
|
|
334
|
-
selector: resourceSelector,
|
|
335
|
-
props: boundedPayload
|
|
336
|
-
}),
|
|
337
|
-
zod.z.object({
|
|
338
|
-
...operationBase,
|
|
339
|
-
kind: zod.z.literal("addResource"),
|
|
340
|
-
selector: packageSelector,
|
|
341
|
-
resource: boundedPayload,
|
|
342
|
-
atIndex: zod.z.number().int().nonnegative().optional()
|
|
343
|
-
}),
|
|
344
|
-
zod.z.object({
|
|
345
|
-
...operationBase,
|
|
346
|
-
kind: zod.z.literal("addBranch"),
|
|
347
|
-
branch: identifier
|
|
348
|
-
}),
|
|
349
|
-
zod.z.object({
|
|
350
|
-
...operationBase,
|
|
351
|
-
kind: zod.z.literal("renameBranch"),
|
|
352
|
-
selector: zod.z.object({ branch: identifier }),
|
|
353
|
-
newName: identifier
|
|
354
|
-
}),
|
|
355
|
-
zod.z.object({
|
|
356
|
-
...operationBase,
|
|
357
|
-
kind: zod.z.literal("removeBranch"),
|
|
358
|
-
selector: zod.z.object({ branch: identifier })
|
|
359
|
-
}),
|
|
360
|
-
zod.z.object({
|
|
361
|
-
...operationBase,
|
|
362
|
-
kind: zod.z.literal("addPackage"),
|
|
363
|
-
package: boundedPayload,
|
|
364
|
-
atIndex: zod.z.number().int().nonnegative()
|
|
365
|
-
}),
|
|
366
|
-
zod.z.object({
|
|
367
|
-
...operationBase,
|
|
368
|
-
kind: zod.z.literal("renamePackage"),
|
|
369
|
-
selector: packageSelector,
|
|
370
|
-
newName: identifier
|
|
371
|
-
}),
|
|
372
|
-
zod.z.object({
|
|
373
|
-
...operationBase,
|
|
374
|
-
kind: zod.z.literal("removePackage"),
|
|
375
|
-
selector: packageSelector
|
|
376
|
-
}),
|
|
377
|
-
zod.z.object({
|
|
378
|
-
...operationBase,
|
|
379
|
-
kind: zod.z.literal("addComponent"),
|
|
380
|
-
selector: packageSelector,
|
|
381
|
-
component: boundedPayload,
|
|
382
|
-
atIndex: zod.z.number().int().nonnegative()
|
|
383
|
-
}),
|
|
384
|
-
zod.z.object({
|
|
385
|
-
...operationBase,
|
|
386
|
-
kind: zod.z.literal("removeComponent"),
|
|
387
|
-
selector: componentSelector
|
|
388
|
-
}),
|
|
389
|
-
zod.z.object({
|
|
390
|
-
...operationBase,
|
|
391
|
-
kind: zod.z.literal("moveComponent"),
|
|
392
|
-
selector: componentSelector,
|
|
393
|
-
toPackageId: identifier,
|
|
394
|
-
toIndex: zod.z.number().int().nonnegative()
|
|
395
|
-
}),
|
|
396
|
-
zod.z.object({
|
|
397
|
-
...operationBase,
|
|
398
|
-
kind: zod.z.literal("replaceResourceBytes"),
|
|
399
|
-
selector: resourceSelector,
|
|
400
|
-
sourceBytes: bytes
|
|
401
|
-
}),
|
|
402
|
-
zod.z.object({
|
|
403
|
-
...operationBase,
|
|
404
|
-
kind: zod.z.literal("removeResource"),
|
|
405
|
-
selector: resourceSelector
|
|
406
|
-
}),
|
|
407
|
-
zod.z.object({
|
|
408
|
-
...operationBase,
|
|
409
|
-
kind: zod.z.literal("setDisplayNodeProps"),
|
|
410
|
-
selector: displayNodeSelector,
|
|
411
|
-
props: boundedPayload
|
|
412
|
-
}),
|
|
413
|
-
zod.z.object({
|
|
414
|
-
...operationBase,
|
|
415
|
-
kind: zod.z.literal("setComponentProps"),
|
|
416
|
-
selector: componentSelector,
|
|
417
|
-
props: boundedPayload
|
|
418
|
-
}),
|
|
419
|
-
zod.z.object({
|
|
420
|
-
...operationBase,
|
|
421
|
-
kind: zod.z.literal("attachDisplayNode"),
|
|
422
|
-
selector: componentSelector,
|
|
423
|
-
atIndex: zod.z.number().int().nonnegative(),
|
|
424
|
-
node: boundedPayload
|
|
425
|
-
}),
|
|
426
|
-
zod.z.object({
|
|
427
|
-
...operationBase,
|
|
428
|
-
kind: zod.z.literal("detachDisplayNode"),
|
|
429
|
-
selector: displayNodeSelector
|
|
430
|
-
}),
|
|
431
|
-
...["addController", "updateController"].map((kind) => zod.z.object({
|
|
432
|
-
...operationBase,
|
|
433
|
-
kind: zod.z.literal(kind),
|
|
434
|
-
selector: controllerSelector,
|
|
435
|
-
controller: boundedPayload
|
|
436
|
-
})),
|
|
437
|
-
zod.z.object({
|
|
438
|
-
...operationBase,
|
|
439
|
-
kind: zod.z.literal("removeController"),
|
|
440
|
-
selector: controllerSelector
|
|
441
|
-
}),
|
|
442
|
-
...["addTransition", "updateTransition"].map((kind) => zod.z.object({
|
|
443
|
-
...operationBase,
|
|
444
|
-
kind: zod.z.literal(kind),
|
|
445
|
-
selector: transitionSelector,
|
|
446
|
-
transition: boundedPayload
|
|
447
|
-
})),
|
|
448
|
-
zod.z.object({
|
|
449
|
-
...operationBase,
|
|
450
|
-
kind: zod.z.literal("removeTransition"),
|
|
451
|
-
selector: transitionSelector
|
|
452
|
-
}),
|
|
453
|
-
...[
|
|
454
|
-
"addLookGear",
|
|
455
|
-
"updateLookGear",
|
|
456
|
-
"addGear",
|
|
457
|
-
"updateGear"
|
|
458
|
-
].map((kind) => zod.z.object({
|
|
459
|
-
...operationBase,
|
|
460
|
-
kind: zod.z.literal(kind),
|
|
461
|
-
selector: displayNodeSelector.extend({
|
|
462
|
-
kind: identifier,
|
|
463
|
-
controllerName: identifier
|
|
464
|
-
}),
|
|
465
|
-
gear: boundedPayload
|
|
466
|
-
})),
|
|
467
|
-
...["removeLookGear", "removeGear"].map((kind) => zod.z.object({
|
|
468
|
-
...operationBase,
|
|
469
|
-
kind: zod.z.literal(kind),
|
|
470
|
-
selector: displayNodeSelector.extend({
|
|
471
|
-
kind: identifier,
|
|
472
|
-
controllerName: identifier
|
|
473
|
-
})
|
|
474
|
-
}))
|
|
475
|
-
]);
|
|
476
|
-
const project = zod.z.object({
|
|
477
|
-
projectId: identifier,
|
|
478
|
-
projectType: zod.z.number().int(),
|
|
479
|
-
version: zod.z.string().max(256),
|
|
480
|
-
branches: zod.z.array(zod.z.string().max(256)).max(256),
|
|
481
|
-
settings: boundedPayload,
|
|
482
|
-
packages: zod.z.array(zod.z.object({
|
|
483
|
-
id: identifier,
|
|
484
|
-
name: identifier,
|
|
485
|
-
compressPNG: zod.z.boolean().nullable(),
|
|
486
|
-
jpegQuality: zod.z.number().finite().nullable(),
|
|
487
|
-
publish: boundedPayload.nullable(),
|
|
488
|
-
branchNames: zod.z.array(zod.z.string().max(256)).max(256),
|
|
489
|
-
folders: zod.z.array(boundedPayload).max(1e4),
|
|
490
|
-
resources: zod.z.array(boundedPayload).max(1e5)
|
|
491
|
-
})).max(1e3)
|
|
492
|
-
});
|
|
493
|
-
const OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA = zod.z.object({ backendResult: zod.z.discriminatedUnion("ok", [zod.z.object({
|
|
494
|
-
ok: zod.z.literal(true),
|
|
495
|
-
data: boundedPayload,
|
|
496
|
-
meta: boundedPayload
|
|
497
|
-
}), zod.z.object({
|
|
498
|
-
ok: zod.z.literal(false),
|
|
499
|
-
error: zod.z.object({
|
|
500
|
-
code: identifier,
|
|
501
|
-
message: zod.z.string().max(1e6)
|
|
502
|
-
}).passthrough(),
|
|
503
|
-
meta: boundedPayload,
|
|
504
|
-
session: boundedPayload.optional()
|
|
505
|
-
})]) });
|
|
506
|
-
const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
262
|
+
//#region src/tool-metadata.ts
|
|
263
|
+
const OPENFAIRYGUI_BACKEND_TOOL_METADATA = [
|
|
507
264
|
{
|
|
508
265
|
name: "openfairygui_backend_get_capabilities",
|
|
509
266
|
backendMethod: "getCapabilities",
|
|
510
267
|
title: "Get Backend Capabilities",
|
|
511
268
|
description: "Return the OpenFairyGUI backend capability, version, and service-plane snapshot.",
|
|
512
|
-
inputSchema: zod.z.object({}),
|
|
513
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
514
269
|
annotations: {
|
|
515
270
|
readOnlyHint: true,
|
|
516
271
|
idempotentHint: true,
|
|
@@ -522,8 +277,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
522
277
|
backendMethod: "openSession",
|
|
523
278
|
title: "Open Backend Session",
|
|
524
279
|
description: "Open a FairyGUI project through BackendRuntime and acquire its backend-local session lock.",
|
|
525
|
-
inputSchema: zod.z.object({ projectPath: zod.z.string().min(1) }),
|
|
526
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
527
280
|
annotations: {
|
|
528
281
|
readOnlyHint: false,
|
|
529
282
|
idempotentHint: false,
|
|
@@ -535,13 +288,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
535
288
|
backendMethod: "openProjectSession",
|
|
536
289
|
title: "Open Project Session",
|
|
537
290
|
description: "Open a browser-safe backend session from an already loaded UAM project without filesystem access.",
|
|
538
|
-
inputSchema: zod.z.object({
|
|
539
|
-
project,
|
|
540
|
-
sessionId: zod.z.string().min(1).optional(),
|
|
541
|
-
canonicalProjectPath: zod.z.string().min(1).optional(),
|
|
542
|
-
canonicalPathKey: zod.z.string().min(1).optional()
|
|
543
|
-
}),
|
|
544
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
545
291
|
annotations: {
|
|
546
292
|
readOnlyHint: false,
|
|
547
293
|
idempotentHint: false,
|
|
@@ -553,8 +299,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
553
299
|
backendMethod: "getSession",
|
|
554
300
|
title: "Get Backend Session",
|
|
555
301
|
description: "Return a backend session snapshot by session id.",
|
|
556
|
-
inputSchema: zod.z.object({ sessionId }),
|
|
557
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
558
302
|
annotations: {
|
|
559
303
|
readOnlyHint: true,
|
|
560
304
|
idempotentHint: true,
|
|
@@ -566,8 +310,41 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
566
310
|
backendMethod: "getProjectOutline",
|
|
567
311
|
title: "Get Project Outline",
|
|
568
312
|
description: "Return a revision-bound project/package/resource/component identity outline without source bytes or full property payloads.",
|
|
569
|
-
|
|
570
|
-
|
|
313
|
+
annotations: {
|
|
314
|
+
readOnlyHint: true,
|
|
315
|
+
idempotentHint: true,
|
|
316
|
+
openWorldHint: false
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
name: "openfairygui_backend_query_entity",
|
|
321
|
+
backendMethod: "queryEntity",
|
|
322
|
+
title: "Query Entity Properties",
|
|
323
|
+
description: "Read revision-bound project/package settings, resource, component-property, display-node, controller (including pages/actions), or transition (including items) snapshots. Project queries use only kind; other queries use formal selectors. Settings snapshots include the complete settings payload for updateProjectSettings/updatePackageSettings. No source bytes; fixed projection with explicit response limits.",
|
|
324
|
+
annotations: {
|
|
325
|
+
readOnlyHint: true,
|
|
326
|
+
idempotentHint: true,
|
|
327
|
+
openWorldHint: false
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: "openfairygui_backend_read_session_state",
|
|
332
|
+
backendMethod: "readSessionState",
|
|
333
|
+
title: "Read Session State",
|
|
334
|
+
description: "Read a detached copy of the currently committed public UAM model without primary asset sourceBytes, with revision, dirty state and source-read diagnostics. Optional expectedRevision rejects stale reads. Does not hydrate, write, reserve history or guarantee downstream usability. Complete tool response is limited to 16 MiB.",
|
|
335
|
+
maxResponseBytes: 16777216,
|
|
336
|
+
annotations: {
|
|
337
|
+
readOnlyHint: true,
|
|
338
|
+
idempotentHint: true,
|
|
339
|
+
openWorldHint: false
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
name: "openfairygui_backend_read_resource_bytes",
|
|
344
|
+
backendMethod: "readResourceBytes",
|
|
345
|
+
title: "Read Resource Bytes",
|
|
346
|
+
description: "Read a detached copy of one asset resource primary sourceBytes already held in the session, using exact packageId/resourceId and the required model edit revision. No filesystem hydration or auxiliary-file discovery. Stale reads require restarting the model/bytes read. Complete tool response is limited to 16 MiB.",
|
|
347
|
+
maxResponseBytes: 16777216,
|
|
571
348
|
annotations: {
|
|
572
349
|
readOnlyHint: true,
|
|
573
350
|
idempotentHint: true,
|
|
@@ -579,8 +356,17 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
579
356
|
backendMethod: "validateSession",
|
|
580
357
|
title: "Validate Project Session",
|
|
581
358
|
description: "Validate the current session project structure, references, paths, and available source bytes without writing files.",
|
|
582
|
-
|
|
583
|
-
|
|
359
|
+
annotations: {
|
|
360
|
+
readOnlyHint: true,
|
|
361
|
+
idempotentHint: true,
|
|
362
|
+
openWorldHint: false
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: "openfairygui_backend_preflight_transaction",
|
|
367
|
+
backendMethod: "preflightTransaction",
|
|
368
|
+
title: "Preview UAM Transaction",
|
|
369
|
+
description: "Execute a revision-checked operation batch on an isolated project snapshot and discard the result. Returns the base revision and Core diagnostics; does not write, reserve a revision, or guarantee a later apply/save.",
|
|
584
370
|
annotations: {
|
|
585
371
|
readOnlyHint: true,
|
|
586
372
|
idempotentHint: true,
|
|
@@ -592,12 +378,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
592
378
|
backendMethod: "applyTransaction",
|
|
593
379
|
title: "Apply UAM Transaction",
|
|
594
380
|
description: "Apply a bounded, revision-checked UAM operation batch using the Core transaction discriminants.",
|
|
595
|
-
inputSchema: zod.z.object({
|
|
596
|
-
sessionId,
|
|
597
|
-
expectedRevision,
|
|
598
|
-
operations: zod.z.array(operation).min(1).max(1e3)
|
|
599
|
-
}),
|
|
600
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
601
381
|
annotations: {
|
|
602
382
|
readOnlyHint: false,
|
|
603
383
|
destructiveHint: true,
|
|
@@ -610,14 +390,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
610
390
|
backendMethod: "saveSession",
|
|
611
391
|
title: "Save Backend Session",
|
|
612
392
|
description: "Write the current backend session through its coordinated save path; Node uses an atomic staged directory swap.",
|
|
613
|
-
inputSchema: zod.z.object({
|
|
614
|
-
sessionId,
|
|
615
|
-
expectedRevision: expectedRevision.optional(),
|
|
616
|
-
targetPath: zod.z.string().min(1).optional(),
|
|
617
|
-
force: zod.z.boolean().optional(),
|
|
618
|
-
mode: zod.z.literal("materializeCleanSession").optional()
|
|
619
|
-
}),
|
|
620
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
621
393
|
annotations: {
|
|
622
394
|
readOnlyHint: false,
|
|
623
395
|
destructiveHint: true,
|
|
@@ -630,13 +402,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
630
402
|
backendMethod: "materializeSession",
|
|
631
403
|
title: "Materialize Backend Session",
|
|
632
404
|
description: "Force materialize the current backend session project through the configured project storage without requiring a dirty edit revision.",
|
|
633
|
-
inputSchema: zod.z.object({
|
|
634
|
-
sessionId,
|
|
635
|
-
expectedRevision: expectedRevision.optional(),
|
|
636
|
-
mode: zod.z.literal("fullProject").optional(),
|
|
637
|
-
reason: zod.z.string().min(1).optional()
|
|
638
|
-
}),
|
|
639
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
640
405
|
annotations: {
|
|
641
406
|
readOnlyHint: false,
|
|
642
407
|
destructiveHint: true,
|
|
@@ -649,8 +414,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
649
414
|
backendMethod: "closeSession",
|
|
650
415
|
title: "Close Backend Session",
|
|
651
416
|
description: "Close a backend session and release its backend-local session lock.",
|
|
652
|
-
inputSchema: zod.z.object({ sessionId }),
|
|
653
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
654
417
|
annotations: {
|
|
655
418
|
readOnlyHint: false,
|
|
656
419
|
idempotentHint: false,
|
|
@@ -662,12 +425,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
662
425
|
backendMethod: "getEvents",
|
|
663
426
|
title: "Get Runtime Events",
|
|
664
427
|
description: "Poll backend runtime events for a session using the backend P2 event cursor contract.",
|
|
665
|
-
inputSchema: zod.z.object({
|
|
666
|
-
sessionId,
|
|
667
|
-
after: zod.z.string().optional(),
|
|
668
|
-
limit
|
|
669
|
-
}),
|
|
670
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
671
428
|
annotations: {
|
|
672
429
|
readOnlyHint: true,
|
|
673
430
|
idempotentHint: true,
|
|
@@ -679,11 +436,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
679
436
|
backendMethod: "getJob",
|
|
680
437
|
title: "Get Runtime Job",
|
|
681
438
|
description: "Return a backend runtime job snapshot by session and backend-local job id.",
|
|
682
|
-
inputSchema: zod.z.object({
|
|
683
|
-
sessionId,
|
|
684
|
-
jobId
|
|
685
|
-
}),
|
|
686
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
687
439
|
annotations: {
|
|
688
440
|
readOnlyHint: true,
|
|
689
441
|
idempotentHint: true,
|
|
@@ -695,21 +447,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
695
447
|
backendMethod: "listJobs",
|
|
696
448
|
title: "List Runtime Jobs",
|
|
697
449
|
description: "List backend runtime jobs for a session with backend P2 status/kind filters.",
|
|
698
|
-
inputSchema: zod.z.object({
|
|
699
|
-
sessionId,
|
|
700
|
-
status: zod.z.enum([
|
|
701
|
-
"queued",
|
|
702
|
-
"running",
|
|
703
|
-
"completed",
|
|
704
|
-
"failed",
|
|
705
|
-
"cancelled",
|
|
706
|
-
"active",
|
|
707
|
-
"terminal"
|
|
708
|
-
]).optional(),
|
|
709
|
-
kind: zod.z.literal("cache.refresh").optional(),
|
|
710
|
-
limit
|
|
711
|
-
}),
|
|
712
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
713
450
|
annotations: {
|
|
714
451
|
readOnlyHint: true,
|
|
715
452
|
idempotentHint: true,
|
|
@@ -721,11 +458,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
721
458
|
backendMethod: "cancelJob",
|
|
722
459
|
title: "Cancel Runtime Job",
|
|
723
460
|
description: "Request cooperative cancellation for a backend runtime job.",
|
|
724
|
-
inputSchema: zod.z.object({
|
|
725
|
-
sessionId,
|
|
726
|
-
jobId
|
|
727
|
-
}),
|
|
728
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
729
461
|
annotations: {
|
|
730
462
|
readOnlyHint: false,
|
|
731
463
|
idempotentHint: false,
|
|
@@ -737,8 +469,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
737
469
|
backendMethod: "getCacheSnapshot",
|
|
738
470
|
title: "Get Cache Snapshot",
|
|
739
471
|
description: "Return the backend P2 derived read-only cache snapshot for a session.",
|
|
740
|
-
inputSchema: zod.z.object({ sessionId }),
|
|
741
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
742
472
|
annotations: {
|
|
743
473
|
readOnlyHint: true,
|
|
744
474
|
idempotentHint: true,
|
|
@@ -750,15 +480,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
750
480
|
backendMethod: "refreshCache",
|
|
751
481
|
title: "Refresh Cache",
|
|
752
482
|
description: "Create a backend P2 cache.refresh job for the session cache snapshot.",
|
|
753
|
-
inputSchema: zod.z.object({
|
|
754
|
-
sessionId,
|
|
755
|
-
reason: zod.z.enum([
|
|
756
|
-
"manual",
|
|
757
|
-
"session_open",
|
|
758
|
-
"after_save"
|
|
759
|
-
]).optional()
|
|
760
|
-
}),
|
|
761
|
-
outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
|
|
762
483
|
annotations: {
|
|
763
484
|
readOnlyHint: false,
|
|
764
485
|
idempotentHint: false,
|
|
@@ -767,9 +488,62 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
|
|
|
767
488
|
}
|
|
768
489
|
];
|
|
769
490
|
//#endregion
|
|
491
|
+
//#region src/tool-definitions.ts
|
|
492
|
+
const OPENFAIRYGUI_BACKEND_TOOL_PREFIX = "openfairygui_backend_";
|
|
493
|
+
const OPENFAIRYGUI_BACKEND_TOOL_NAMES = OPENFAIRYGUI_BACKEND_TOOL_METADATA.map((entry) => entry.name);
|
|
494
|
+
function isOpenFairyGuiMcpPayloadWithinBudget(root) {
|
|
495
|
+
const pending = [{
|
|
496
|
+
value: root,
|
|
497
|
+
depth: 0
|
|
498
|
+
}];
|
|
499
|
+
let nodes = 0;
|
|
500
|
+
while (pending.length > 0) {
|
|
501
|
+
const { value, depth } = pending.pop();
|
|
502
|
+
nodes += 1;
|
|
503
|
+
if (nodes > 1e5 || depth > 32) return false;
|
|
504
|
+
if (value === null || typeof value === "boolean") continue;
|
|
505
|
+
if (typeof value === "number") {
|
|
506
|
+
if (!Number.isFinite(value)) return false;
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (typeof value === "string") {
|
|
510
|
+
if (value.length > 1e6) return false;
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
if (value instanceof Uint8Array) {
|
|
514
|
+
if (value.byteLength > 8 * 1024 * 1024) return false;
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (Array.isArray(value)) {
|
|
518
|
+
if (value.length > 1e4) return false;
|
|
519
|
+
for (const child of value) pending.push({
|
|
520
|
+
value: child,
|
|
521
|
+
depth: depth + 1
|
|
522
|
+
});
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
if (typeof value !== "object") return false;
|
|
526
|
+
const entries = Object.entries(value);
|
|
527
|
+
if (entries.length > 1e4 || entries.some(([key]) => key.length > 256)) return false;
|
|
528
|
+
for (const [, child] of entries) pending.push({
|
|
529
|
+
value: child,
|
|
530
|
+
depth: depth + 1
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
return true;
|
|
534
|
+
}
|
|
535
|
+
const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = OPENFAIRYGUI_BACKEND_TOOL_METADATA.map((metadata) => {
|
|
536
|
+
const contract = CONTRACT_SNAPSHOT.tools[metadata.backendMethod];
|
|
537
|
+
return {
|
|
538
|
+
...metadata,
|
|
539
|
+
inputSchema: contractObjectSchema(contract.input),
|
|
540
|
+
outputSchema: contractObjectSchema(contract.output)
|
|
541
|
+
};
|
|
542
|
+
});
|
|
543
|
+
//#endregion
|
|
770
544
|
//#region src/tool-handler.ts
|
|
771
|
-
function jsonResult(payload, isError = false) {
|
|
772
|
-
const text = JSON.stringify(payload,
|
|
545
|
+
function jsonResult(payload, isError = false, compact = false) {
|
|
546
|
+
const text = JSON.stringify(payload, (_key, value) => value instanceof Uint8Array ? [...value] : value, compact ? void 0 : 2);
|
|
773
547
|
const wirePayload = JSON.parse(text);
|
|
774
548
|
return {
|
|
775
549
|
content: [{
|
|
@@ -803,113 +577,32 @@ function unhandledBackendFailure(startedAt) {
|
|
|
803
577
|
}
|
|
804
578
|
async function callOpenFairyGuiBackendTool(runtime, name, input) {
|
|
805
579
|
if (!isOpenFairyGuiMcpPayloadWithinBudget(input)) throw new RangeError("MCP input exceeds the depth, node, key, string, or byte budget.");
|
|
580
|
+
const definition = OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS.find((entry) => entry.name === name);
|
|
581
|
+
if (!definition) throw new RangeError(`Unknown OpenFairyGUI backend MCP tool: ${name}`);
|
|
582
|
+
const decoded = decodeToolBytes(definition.inputSchema.parse(input), CONTRACT_SNAPSHOT.tools[definition.backendMethod].bytePaths);
|
|
806
583
|
const startedAt = Date.now();
|
|
807
|
-
let result;
|
|
808
584
|
try {
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
result = runtime.openProjectSession({
|
|
818
|
-
project: input.project,
|
|
819
|
-
sessionId: input.sessionId === void 0 ? void 0 : String(input.sessionId),
|
|
820
|
-
canonicalProjectPath: input.canonicalProjectPath === void 0 ? void 0 : String(input.canonicalProjectPath),
|
|
821
|
-
canonicalPathKey: input.canonicalPathKey === void 0 ? void 0 : String(input.canonicalPathKey)
|
|
822
|
-
});
|
|
823
|
-
break;
|
|
824
|
-
case "openfairygui_backend_get_session":
|
|
825
|
-
result = runtime.getSession({ sessionId: String(input.sessionId) });
|
|
826
|
-
break;
|
|
827
|
-
case "openfairygui_backend_get_project_outline":
|
|
828
|
-
result = runtime.getProjectOutline({ sessionId: String(input.sessionId) });
|
|
829
|
-
break;
|
|
830
|
-
case "openfairygui_backend_validate_session":
|
|
831
|
-
result = runtime.validateSession({ sessionId: String(input.sessionId) });
|
|
832
|
-
break;
|
|
833
|
-
case "openfairygui_backend_apply_transaction": {
|
|
834
|
-
const operations = input.operations.map((operation) => operation.kind === "replaceResourceBytes" ? {
|
|
835
|
-
...operation,
|
|
836
|
-
sourceBytes: new Uint8Array(operation.sourceBytes)
|
|
837
|
-
} : operation);
|
|
838
|
-
result = await runtime.applyTransaction({
|
|
839
|
-
sessionId: String(input.sessionId),
|
|
840
|
-
expectedRevision: Number(input.expectedRevision),
|
|
841
|
-
operations
|
|
842
|
-
});
|
|
843
|
-
break;
|
|
585
|
+
const result = await Reflect.apply(runtime[definition.backendMethod], runtime, definition.backendMethod === "getCapabilities" ? [] : [decoded]);
|
|
586
|
+
let response = jsonResult(result, isBackendFailure(result), definition.maxResponseBytes !== void 0);
|
|
587
|
+
if (definition.maxResponseBytes !== void 0 && new TextEncoder().encode(JSON.stringify(response)).byteLength > definition.maxResponseBytes) response = jsonResult({
|
|
588
|
+
...unhandledBackendFailure(startedAt),
|
|
589
|
+
error: {
|
|
590
|
+
code: "mcp_response_budget_exceeded",
|
|
591
|
+
message: "The complete MCP tool response exceeds its byte limit.",
|
|
592
|
+
maxBytes: definition.maxResponseBytes
|
|
844
593
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
expectedRevision: input.expectedRevision === void 0 ? void 0 : Number(input.expectedRevision),
|
|
849
|
-
targetPath: input.targetPath === void 0 ? void 0 : String(input.targetPath),
|
|
850
|
-
force: input.force === void 0 ? void 0 : Boolean(input.force),
|
|
851
|
-
mode: input.mode
|
|
852
|
-
});
|
|
853
|
-
break;
|
|
854
|
-
case "openfairygui_backend_materialize_session":
|
|
855
|
-
result = await runtime.materializeSession({
|
|
856
|
-
sessionId: String(input.sessionId),
|
|
857
|
-
expectedRevision: input.expectedRevision === void 0 ? void 0 : Number(input.expectedRevision),
|
|
858
|
-
mode: input.mode,
|
|
859
|
-
reason: input.reason === void 0 ? void 0 : String(input.reason)
|
|
860
|
-
});
|
|
861
|
-
break;
|
|
862
|
-
case "openfairygui_backend_close_session":
|
|
863
|
-
result = await runtime.closeSession({ sessionId: String(input.sessionId) });
|
|
864
|
-
break;
|
|
865
|
-
case "openfairygui_backend_get_events":
|
|
866
|
-
result = runtime.getEvents({
|
|
867
|
-
sessionId: String(input.sessionId),
|
|
868
|
-
after: input.after === void 0 ? void 0 : String(input.after),
|
|
869
|
-
limit: input.limit === void 0 ? void 0 : Number(input.limit)
|
|
870
|
-
});
|
|
871
|
-
break;
|
|
872
|
-
case "openfairygui_backend_get_job":
|
|
873
|
-
result = runtime.getJob({
|
|
874
|
-
sessionId: String(input.sessionId),
|
|
875
|
-
jobId: String(input.jobId)
|
|
876
|
-
});
|
|
877
|
-
break;
|
|
878
|
-
case "openfairygui_backend_list_jobs":
|
|
879
|
-
result = runtime.listJobs({
|
|
880
|
-
sessionId: String(input.sessionId),
|
|
881
|
-
status: input.status,
|
|
882
|
-
kind: input.kind,
|
|
883
|
-
limit: input.limit === void 0 ? void 0 : Number(input.limit)
|
|
884
|
-
});
|
|
885
|
-
break;
|
|
886
|
-
case "openfairygui_backend_cancel_job":
|
|
887
|
-
result = runtime.cancelJob({
|
|
888
|
-
sessionId: String(input.sessionId),
|
|
889
|
-
jobId: String(input.jobId)
|
|
890
|
-
});
|
|
891
|
-
break;
|
|
892
|
-
case "openfairygui_backend_get_cache_snapshot":
|
|
893
|
-
result = runtime.getCacheSnapshot({ sessionId: String(input.sessionId) });
|
|
894
|
-
break;
|
|
895
|
-
case "openfairygui_backend_refresh_cache":
|
|
896
|
-
result = runtime.refreshCache({
|
|
897
|
-
sessionId: String(input.sessionId),
|
|
898
|
-
reason: input.reason
|
|
899
|
-
});
|
|
900
|
-
break;
|
|
901
|
-
default: throw new Error(`Unknown OpenFairyGUI backend MCP tool: ${name}`);
|
|
902
|
-
}
|
|
594
|
+
}, true);
|
|
595
|
+
definition.outputSchema.parse(response.structuredContent);
|
|
596
|
+
return response;
|
|
903
597
|
} catch {
|
|
904
598
|
return jsonResult(unhandledBackendFailure(startedAt), true);
|
|
905
599
|
}
|
|
906
|
-
return jsonResult(result, isBackendFailure(result));
|
|
907
600
|
}
|
|
908
601
|
//#endregion
|
|
909
602
|
//#region src/server.ts
|
|
910
603
|
const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
911
604
|
function getInjectedPackageVersion() {
|
|
912
|
-
const version = "0.
|
|
605
|
+
const version = "0.5.0-alpha.1";
|
|
913
606
|
return typeof version === "string" && true ? version : null;
|
|
914
607
|
}
|
|
915
608
|
function readPackageVersion() {
|
|
@@ -928,17 +621,39 @@ function createOpenFairyGuiMcpServer(options = {}) {
|
|
|
928
621
|
name: options.name ?? "openfairygui-mcp",
|
|
929
622
|
version: options.version ?? PACKAGE_VERSION
|
|
930
623
|
});
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
624
|
+
const tools = [];
|
|
625
|
+
for (const definition of OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS) {
|
|
626
|
+
const metadata = {
|
|
627
|
+
name: definition.name,
|
|
628
|
+
title: definition.title,
|
|
629
|
+
description: definition.description,
|
|
630
|
+
annotations: definition.annotations,
|
|
631
|
+
_meta: {
|
|
632
|
+
"openfairygui/backendMethod": definition.backendMethod,
|
|
633
|
+
"openfairygui/adapter": "thin-backend-p2",
|
|
634
|
+
"openfairygui/contractDigest": CONTRACT_SNAPSHOT.digest
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
server.registerTool(definition.name, {
|
|
638
|
+
...metadata,
|
|
639
|
+
inputSchema: definition.inputSchema,
|
|
640
|
+
outputSchema: definition.outputSchema
|
|
641
|
+
}, async (args) => callOpenFairyGuiBackendTool(runtime, definition.name, args));
|
|
642
|
+
tools.push(_modelcontextprotocol_sdk_types_js.ToolSchema.parse({
|
|
643
|
+
...metadata,
|
|
644
|
+
inputSchema: zod.z.toJSONSchema(definition.inputSchema, {
|
|
645
|
+
target: "draft-07",
|
|
646
|
+
io: "input",
|
|
647
|
+
reused: "ref"
|
|
648
|
+
}),
|
|
649
|
+
outputSchema: zod.z.toJSONSchema(definition.outputSchema, {
|
|
650
|
+
target: "draft-07",
|
|
651
|
+
io: "output",
|
|
652
|
+
reused: "ref"
|
|
653
|
+
})
|
|
654
|
+
}));
|
|
655
|
+
}
|
|
656
|
+
server.server.setRequestHandler(_modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, () => ({ tools: structuredClone(tools) }));
|
|
942
657
|
registerOpenFairyGuiBackendResources(server, runtime);
|
|
943
658
|
registerOpenFairyGuiBackendPrompts(server);
|
|
944
659
|
return server;
|
|
@@ -990,16 +705,16 @@ Object.defineProperty(exports, "OPENFAIRYGUI_BACKEND_TOOL_NAMES", {
|
|
|
990
705
|
return OPENFAIRYGUI_BACKEND_TOOL_NAMES;
|
|
991
706
|
}
|
|
992
707
|
});
|
|
993
|
-
Object.defineProperty(exports, "
|
|
708
|
+
Object.defineProperty(exports, "OPENFAIRYGUI_BACKEND_TOOL_PREFIX", {
|
|
994
709
|
enumerable: true,
|
|
995
710
|
get: function() {
|
|
996
|
-
return
|
|
711
|
+
return OPENFAIRYGUI_BACKEND_TOOL_PREFIX;
|
|
997
712
|
}
|
|
998
713
|
});
|
|
999
|
-
Object.defineProperty(exports, "
|
|
714
|
+
Object.defineProperty(exports, "__toESM", {
|
|
1000
715
|
enumerable: true,
|
|
1001
716
|
get: function() {
|
|
1002
|
-
return
|
|
717
|
+
return __toESM;
|
|
1003
718
|
}
|
|
1004
719
|
});
|
|
1005
720
|
Object.defineProperty(exports, "callOpenFairyGuiBackendTool", {
|