@openfairygui/mcp 0.3.0 → 0.4.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.
@@ -1,8 +1,35 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
1
23
  let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
24
+ let _modelcontextprotocol_sdk_types_js = require("@modelcontextprotocol/sdk/types.js");
2
25
  let _openfairygui_backend_node = require("@openfairygui/backend/node");
3
26
  let node_module = require("node:module");
4
27
  let zod = require("zod");
28
+ let _openfairygui_backend = require("@openfairygui/backend");
29
+ let _openfairygui_backend_docs = require("@openfairygui/backend/docs");
5
30
  let _modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
31
+ let node_path = require("node:path");
32
+ node_path = __toESM(node_path);
6
33
  let node_url = require("node:url");
7
34
  //#region src/prompt-definitions.ts
8
35
  const OPENFAIRYGUI_BACKEND_PROMPT_NAMES = [
@@ -51,6 +78,11 @@ const OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS = [
51
78
  description: "Guide a client through backend-owned revision checks without inventing operation grammar.",
52
79
  text: [
53
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.",
54
86
  "Call openfairygui_backend_apply_transaction with sessionId, expectedRevision, and backend/UAM-owned operations.",
55
87
  "If the backend returns a stale revision error, refresh the session snapshot and re-plan against the new revision.",
56
88
  "Do not invent selector grammar, transaction grammar, or operation payload semantics at the MCP layer."
@@ -94,6 +126,32 @@ function registerOpenFairyGuiBackendPrompts(server) {
94
126
  }, () => promptResult(definition.text));
95
127
  }
96
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
97
155
  //#region src/resource-definitions.ts
98
156
  const JSON_MIME_TYPE = "application/json";
99
157
  function firstVariable(value) {
@@ -108,12 +166,69 @@ function jsonResource(uri, backendResult) {
108
166
  }
109
167
  const OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI = "openfairygui://backend/capabilities";
110
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,
111
173
  "openfairygui://backend/session/{sessionId}",
112
174
  "openfairygui://backend/session/{sessionId}/outline",
113
175
  "openfairygui://backend/cache/{sessionId}",
114
176
  "openfairygui://backend/job/{sessionId}/{jobId}"
115
177
  ];
116
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))));
117
232
  server.registerResource("openfairygui_backend_capabilities", OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, {
118
233
  title: "OpenFairyGUI Backend Capabilities",
119
234
  description: "Read the backend capability and version envelope as JSON.",
@@ -144,152 +259,13 @@ function registerOpenFairyGuiBackendResources(server, runtime) {
144
259
  })));
145
260
  }
146
261
  //#endregion
147
- //#region src/tool-handler.ts
148
- function jsonResult(payload, isError = false) {
149
- return {
150
- content: [{
151
- type: "text",
152
- text: JSON.stringify(payload, null, 2)
153
- }],
154
- structuredContent: { backendResult: payload },
155
- isError
156
- };
157
- }
158
- function isBackendFailure(value) {
159
- return typeof value === "object" && value !== null && "ok" in value && value.ok === false;
160
- }
161
- async function callOpenFairyGuiBackendTool(runtime, name, input) {
162
- let result;
163
- switch (name) {
164
- case "openfairygui_backend_get_capabilities":
165
- result = runtime.getCapabilities();
166
- break;
167
- case "openfairygui_backend_open_session":
168
- result = await runtime.openSession({ projectPath: String(input.projectPath) });
169
- break;
170
- case "openfairygui_backend_open_project_session":
171
- result = runtime.openProjectSession({
172
- project: input.project,
173
- sessionId: input.sessionId === void 0 ? void 0 : String(input.sessionId),
174
- canonicalProjectPath: input.canonicalProjectPath === void 0 ? void 0 : String(input.canonicalProjectPath),
175
- canonicalPathKey: input.canonicalPathKey === void 0 ? void 0 : String(input.canonicalPathKey)
176
- });
177
- break;
178
- case "openfairygui_backend_get_session":
179
- result = runtime.getSession({ sessionId: String(input.sessionId) });
180
- break;
181
- case "openfairygui_backend_get_project_outline":
182
- result = runtime.getProjectOutline({ sessionId: String(input.sessionId) });
183
- break;
184
- case "openfairygui_backend_validate_session":
185
- result = runtime.validateSession({ sessionId: String(input.sessionId) });
186
- break;
187
- case "openfairygui_backend_apply_transaction":
188
- result = await runtime.applyTransaction({
189
- sessionId: String(input.sessionId),
190
- expectedRevision: Number(input.expectedRevision),
191
- operations: input.operations
192
- });
193
- break;
194
- case "openfairygui_backend_save_session":
195
- result = await runtime.saveSession({
196
- sessionId: String(input.sessionId),
197
- expectedRevision: input.expectedRevision === void 0 ? void 0 : Number(input.expectedRevision),
198
- targetPath: input.targetPath === void 0 ? void 0 : String(input.targetPath),
199
- force: input.force === void 0 ? void 0 : Boolean(input.force),
200
- mode: input.mode
201
- });
202
- break;
203
- case "openfairygui_backend_materialize_session":
204
- result = await runtime.materializeSession({
205
- sessionId: String(input.sessionId),
206
- expectedRevision: input.expectedRevision === void 0 ? void 0 : Number(input.expectedRevision),
207
- mode: input.mode,
208
- reason: input.reason === void 0 ? void 0 : String(input.reason)
209
- });
210
- break;
211
- case "openfairygui_backend_close_session":
212
- result = await runtime.closeSession({ sessionId: String(input.sessionId) });
213
- break;
214
- case "openfairygui_backend_get_events":
215
- result = runtime.getEvents({
216
- sessionId: String(input.sessionId),
217
- after: input.after === void 0 ? void 0 : String(input.after),
218
- limit: input.limit === void 0 ? void 0 : Number(input.limit)
219
- });
220
- break;
221
- case "openfairygui_backend_get_job":
222
- result = runtime.getJob({
223
- sessionId: String(input.sessionId),
224
- jobId: String(input.jobId)
225
- });
226
- break;
227
- case "openfairygui_backend_list_jobs":
228
- result = runtime.listJobs({
229
- sessionId: String(input.sessionId),
230
- status: input.status,
231
- kind: input.kind,
232
- limit: input.limit === void 0 ? void 0 : Number(input.limit)
233
- });
234
- break;
235
- case "openfairygui_backend_cancel_job":
236
- result = runtime.cancelJob({
237
- sessionId: String(input.sessionId),
238
- jobId: String(input.jobId)
239
- });
240
- break;
241
- case "openfairygui_backend_get_cache_snapshot":
242
- result = runtime.getCacheSnapshot({ sessionId: String(input.sessionId) });
243
- break;
244
- case "openfairygui_backend_refresh_cache":
245
- result = runtime.refreshCache({
246
- sessionId: String(input.sessionId),
247
- reason: input.reason
248
- });
249
- break;
250
- default: throw new Error(`Unknown OpenFairyGUI backend MCP tool: ${name}`);
251
- }
252
- return jsonResult(result, isBackendFailure(result));
253
- }
254
- //#endregion
255
- //#region src/tool-definitions.ts
256
- const OPENFAIRYGUI_BACKEND_TOOL_PREFIX = "openfairygui_backend_";
257
- const OPENFAIRYGUI_BACKEND_TOOL_NAMES = [
258
- "openfairygui_backend_get_capabilities",
259
- "openfairygui_backend_open_session",
260
- "openfairygui_backend_open_project_session",
261
- "openfairygui_backend_get_session",
262
- "openfairygui_backend_get_project_outline",
263
- "openfairygui_backend_validate_session",
264
- "openfairygui_backend_apply_transaction",
265
- "openfairygui_backend_save_session",
266
- "openfairygui_backend_materialize_session",
267
- "openfairygui_backend_close_session",
268
- "openfairygui_backend_get_events",
269
- "openfairygui_backend_get_job",
270
- "openfairygui_backend_list_jobs",
271
- "openfairygui_backend_cancel_job",
272
- "openfairygui_backend_get_cache_snapshot",
273
- "openfairygui_backend_refresh_cache"
274
- ];
275
- const sessionId = zod.z.string().min(1);
276
- const jobId = zod.z.string().min(1);
277
- const expectedRevision = zod.z.number().int().nonnegative();
278
- const limit = zod.z.number().int().nonnegative().optional();
279
- const OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA = zod.z.object({ backendResult: zod.z.object({
280
- ok: zod.z.boolean(),
281
- data: zod.z.unknown().optional(),
282
- error: zod.z.unknown().optional(),
283
- meta: zod.z.unknown().optional()
284
- }).passthrough() });
285
- const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
262
+ //#region src/tool-metadata.ts
263
+ const OPENFAIRYGUI_BACKEND_TOOL_METADATA = [
286
264
  {
287
265
  name: "openfairygui_backend_get_capabilities",
288
266
  backendMethod: "getCapabilities",
289
267
  title: "Get Backend Capabilities",
290
268
  description: "Return the OpenFairyGUI backend capability, version, and service-plane snapshot.",
291
- inputSchema: zod.z.object({}),
292
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
293
269
  annotations: {
294
270
  readOnlyHint: true,
295
271
  idempotentHint: true,
@@ -301,8 +277,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
301
277
  backendMethod: "openSession",
302
278
  title: "Open Backend Session",
303
279
  description: "Open a FairyGUI project through BackendRuntime and acquire its backend-local session lock.",
304
- inputSchema: zod.z.object({ projectPath: zod.z.string().min(1) }),
305
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
306
280
  annotations: {
307
281
  readOnlyHint: false,
308
282
  idempotentHint: false,
@@ -314,13 +288,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
314
288
  backendMethod: "openProjectSession",
315
289
  title: "Open Project Session",
316
290
  description: "Open a browser-safe backend session from an already loaded UAM project without filesystem access.",
317
- inputSchema: zod.z.object({
318
- project: zod.z.unknown(),
319
- sessionId: zod.z.string().min(1).optional(),
320
- canonicalProjectPath: zod.z.string().min(1).optional(),
321
- canonicalPathKey: zod.z.string().min(1).optional()
322
- }),
323
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
324
291
  annotations: {
325
292
  readOnlyHint: false,
326
293
  idempotentHint: false,
@@ -332,8 +299,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
332
299
  backendMethod: "getSession",
333
300
  title: "Get Backend Session",
334
301
  description: "Return a backend session snapshot by session id.",
335
- inputSchema: zod.z.object({ sessionId }),
336
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
337
302
  annotations: {
338
303
  readOnlyHint: true,
339
304
  idempotentHint: true,
@@ -345,8 +310,17 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
345
310
  backendMethod: "getProjectOutline",
346
311
  title: "Get Project Outline",
347
312
  description: "Return a revision-bound project/package/resource/component identity outline without source bytes or full property payloads.",
348
- inputSchema: zod.z.object({ sessionId }),
349
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
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.",
350
324
  annotations: {
351
325
  readOnlyHint: true,
352
326
  idempotentHint: true,
@@ -358,8 +332,17 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
358
332
  backendMethod: "validateSession",
359
333
  title: "Validate Project Session",
360
334
  description: "Validate the current session project structure, references, paths, and available source bytes without writing files.",
361
- inputSchema: zod.z.object({ sessionId }),
362
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
335
+ annotations: {
336
+ readOnlyHint: true,
337
+ idempotentHint: true,
338
+ openWorldHint: false
339
+ }
340
+ },
341
+ {
342
+ name: "openfairygui_backend_preflight_transaction",
343
+ backendMethod: "preflightTransaction",
344
+ title: "Preview UAM Transaction",
345
+ 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.",
363
346
  annotations: {
364
347
  readOnlyHint: true,
365
348
  idempotentHint: true,
@@ -370,13 +353,7 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
370
353
  name: "openfairygui_backend_apply_transaction",
371
354
  backendMethod: "applyTransaction",
372
355
  title: "Apply UAM Transaction",
373
- description: "Apply a backend revision-checked UAM operation batch without redefining selector or operation grammar.",
374
- inputSchema: zod.z.object({
375
- sessionId,
376
- expectedRevision,
377
- operations: zod.z.array(zod.z.unknown())
378
- }),
379
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
356
+ description: "Apply a bounded, revision-checked UAM operation batch using the Core transaction discriminants.",
380
357
  annotations: {
381
358
  readOnlyHint: false,
382
359
  destructiveHint: true,
@@ -388,15 +365,7 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
388
365
  name: "openfairygui_backend_save_session",
389
366
  backendMethod: "saveSession",
390
367
  title: "Save Backend Session",
391
- description: "Write the current backend session back through the backend coordinated non-atomic save path.",
392
- inputSchema: zod.z.object({
393
- sessionId,
394
- expectedRevision: expectedRevision.optional(),
395
- targetPath: zod.z.string().min(1).optional(),
396
- force: zod.z.boolean().optional(),
397
- mode: zod.z.literal("materializeCleanSession").optional()
398
- }),
399
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
368
+ description: "Write the current backend session through its coordinated save path; Node uses an atomic staged directory swap.",
400
369
  annotations: {
401
370
  readOnlyHint: false,
402
371
  destructiveHint: true,
@@ -409,13 +378,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
409
378
  backendMethod: "materializeSession",
410
379
  title: "Materialize Backend Session",
411
380
  description: "Force materialize the current backend session project through the configured project storage without requiring a dirty edit revision.",
412
- inputSchema: zod.z.object({
413
- sessionId,
414
- expectedRevision: expectedRevision.optional(),
415
- mode: zod.z.literal("fullProject").optional(),
416
- reason: zod.z.string().min(1).optional()
417
- }),
418
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
419
381
  annotations: {
420
382
  readOnlyHint: false,
421
383
  destructiveHint: true,
@@ -428,8 +390,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
428
390
  backendMethod: "closeSession",
429
391
  title: "Close Backend Session",
430
392
  description: "Close a backend session and release its backend-local session lock.",
431
- inputSchema: zod.z.object({ sessionId }),
432
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
433
393
  annotations: {
434
394
  readOnlyHint: false,
435
395
  idempotentHint: false,
@@ -441,12 +401,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
441
401
  backendMethod: "getEvents",
442
402
  title: "Get Runtime Events",
443
403
  description: "Poll backend runtime events for a session using the backend P2 event cursor contract.",
444
- inputSchema: zod.z.object({
445
- sessionId,
446
- after: zod.z.string().optional(),
447
- limit
448
- }),
449
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
450
404
  annotations: {
451
405
  readOnlyHint: true,
452
406
  idempotentHint: true,
@@ -458,11 +412,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
458
412
  backendMethod: "getJob",
459
413
  title: "Get Runtime Job",
460
414
  description: "Return a backend runtime job snapshot by session and backend-local job id.",
461
- inputSchema: zod.z.object({
462
- sessionId,
463
- jobId
464
- }),
465
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
466
415
  annotations: {
467
416
  readOnlyHint: true,
468
417
  idempotentHint: true,
@@ -474,21 +423,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
474
423
  backendMethod: "listJobs",
475
424
  title: "List Runtime Jobs",
476
425
  description: "List backend runtime jobs for a session with backend P2 status/kind filters.",
477
- inputSchema: zod.z.object({
478
- sessionId,
479
- status: zod.z.enum([
480
- "queued",
481
- "running",
482
- "completed",
483
- "failed",
484
- "cancelled",
485
- "active",
486
- "terminal"
487
- ]).optional(),
488
- kind: zod.z.literal("cache.refresh").optional(),
489
- limit
490
- }),
491
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
492
426
  annotations: {
493
427
  readOnlyHint: true,
494
428
  idempotentHint: true,
@@ -500,11 +434,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
500
434
  backendMethod: "cancelJob",
501
435
  title: "Cancel Runtime Job",
502
436
  description: "Request cooperative cancellation for a backend runtime job.",
503
- inputSchema: zod.z.object({
504
- sessionId,
505
- jobId
506
- }),
507
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
508
437
  annotations: {
509
438
  readOnlyHint: false,
510
439
  idempotentHint: false,
@@ -516,8 +445,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
516
445
  backendMethod: "getCacheSnapshot",
517
446
  title: "Get Cache Snapshot",
518
447
  description: "Return the backend P2 derived read-only cache snapshot for a session.",
519
- inputSchema: zod.z.object({ sessionId }),
520
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
521
448
  annotations: {
522
449
  readOnlyHint: true,
523
450
  idempotentHint: true,
@@ -529,15 +456,6 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
529
456
  backendMethod: "refreshCache",
530
457
  title: "Refresh Cache",
531
458
  description: "Create a backend P2 cache.refresh job for the session cache snapshot.",
532
- inputSchema: zod.z.object({
533
- sessionId,
534
- reason: zod.z.enum([
535
- "manual",
536
- "session_open",
537
- "after_save"
538
- ]).optional()
539
- }),
540
- outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
541
459
  annotations: {
542
460
  readOnlyHint: false,
543
461
  idempotentHint: false,
@@ -546,10 +464,113 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
546
464
  }
547
465
  ];
548
466
  //#endregion
467
+ //#region src/tool-definitions.ts
468
+ const OPENFAIRYGUI_BACKEND_TOOL_PREFIX = "openfairygui_backend_";
469
+ const OPENFAIRYGUI_BACKEND_TOOL_NAMES = OPENFAIRYGUI_BACKEND_TOOL_METADATA.map((entry) => entry.name);
470
+ function isOpenFairyGuiMcpPayloadWithinBudget(root) {
471
+ const pending = [{
472
+ value: root,
473
+ depth: 0
474
+ }];
475
+ let nodes = 0;
476
+ while (pending.length > 0) {
477
+ const { value, depth } = pending.pop();
478
+ nodes += 1;
479
+ if (nodes > 1e5 || depth > 32) return false;
480
+ if (value === null || typeof value === "boolean") continue;
481
+ if (typeof value === "number") {
482
+ if (!Number.isFinite(value)) return false;
483
+ continue;
484
+ }
485
+ if (typeof value === "string") {
486
+ if (value.length > 1e6) return false;
487
+ continue;
488
+ }
489
+ if (value instanceof Uint8Array) {
490
+ if (value.byteLength > 8 * 1024 * 1024) return false;
491
+ continue;
492
+ }
493
+ if (Array.isArray(value)) {
494
+ if (value.length > 1e4) return false;
495
+ for (const child of value) pending.push({
496
+ value: child,
497
+ depth: depth + 1
498
+ });
499
+ continue;
500
+ }
501
+ if (typeof value !== "object") return false;
502
+ const entries = Object.entries(value);
503
+ if (entries.length > 1e4 || entries.some(([key]) => key.length > 256)) return false;
504
+ for (const [, child] of entries) pending.push({
505
+ value: child,
506
+ depth: depth + 1
507
+ });
508
+ }
509
+ return true;
510
+ }
511
+ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = OPENFAIRYGUI_BACKEND_TOOL_METADATA.map((metadata) => {
512
+ const contract = CONTRACT_SNAPSHOT.tools[metadata.backendMethod];
513
+ return {
514
+ ...metadata,
515
+ inputSchema: contractObjectSchema(contract.input),
516
+ outputSchema: contractObjectSchema(contract.output)
517
+ };
518
+ });
519
+ //#endregion
520
+ //#region src/tool-handler.ts
521
+ function jsonResult(payload, isError = false) {
522
+ const text = JSON.stringify(payload, (_key, value) => value instanceof Uint8Array ? [...value] : value, 2);
523
+ const wirePayload = JSON.parse(text);
524
+ return {
525
+ content: [{
526
+ type: "text",
527
+ text
528
+ }],
529
+ structuredContent: { backendResult: wirePayload },
530
+ isError
531
+ };
532
+ }
533
+ function isBackendFailure(value) {
534
+ return typeof value === "object" && value !== null && "ok" in value && value.ok === false;
535
+ }
536
+ function unhandledBackendFailure(startedAt) {
537
+ return {
538
+ ok: false,
539
+ meta: {
540
+ requestId: crypto.randomUUID(),
541
+ durationMs: Math.max(0, Date.now() - startedAt),
542
+ warnings: [],
543
+ diagnostics: [],
544
+ stage: "runtime",
545
+ contractVersion: _openfairygui_backend.BACKEND_CONTRACT_VERSION,
546
+ capabilitySchemaVersion: _openfairygui_backend.BACKEND_CAPABILITY_SCHEMA_VERSION
547
+ },
548
+ error: {
549
+ code: "backend_unhandled_error",
550
+ message: "Backend tool execution failed."
551
+ }
552
+ };
553
+ }
554
+ async function callOpenFairyGuiBackendTool(runtime, name, input) {
555
+ if (!isOpenFairyGuiMcpPayloadWithinBudget(input)) throw new RangeError("MCP input exceeds the depth, node, key, string, or byte budget.");
556
+ const definition = OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS.find((entry) => entry.name === name);
557
+ if (!definition) throw new RangeError(`Unknown OpenFairyGUI backend MCP tool: ${name}`);
558
+ const decoded = decodeToolBytes(definition.inputSchema.parse(input), CONTRACT_SNAPSHOT.tools[definition.backendMethod].bytePaths);
559
+ const startedAt = Date.now();
560
+ try {
561
+ const result = await Reflect.apply(runtime[definition.backendMethod], runtime, definition.backendMethod === "getCapabilities" ? [] : [decoded]);
562
+ const response = jsonResult(result, isBackendFailure(result));
563
+ definition.outputSchema.parse(response.structuredContent);
564
+ return response;
565
+ } catch {
566
+ return jsonResult(unhandledBackendFailure(startedAt), true);
567
+ }
568
+ }
569
+ //#endregion
549
570
  //#region src/server.ts
550
571
  const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
551
572
  function getInjectedPackageVersion() {
552
- const version = "0.3.0";
573
+ const version = "0.4.0";
553
574
  return typeof version === "string" && true ? version : null;
554
575
  }
555
576
  function readPackageVersion() {
@@ -563,22 +584,44 @@ function readPackageVersion() {
563
584
  }
564
585
  const PACKAGE_VERSION = readPackageVersion();
565
586
  function createOpenFairyGuiMcpServer(options = {}) {
566
- const runtime = options.runtime ?? (0, _openfairygui_backend_node.createNodeBackendRuntime)();
587
+ const runtime = options.runtime ?? (0, _openfairygui_backend_node.createNodeBackendRuntime)({ allowedProjectRoots: options.allowedProjectRoots ?? [process.cwd()] });
567
588
  const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
568
589
  name: options.name ?? "openfairygui-mcp",
569
590
  version: options.version ?? PACKAGE_VERSION
570
591
  });
571
- for (const definition of OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS) server.registerTool(definition.name, {
572
- title: definition.title,
573
- description: definition.description,
574
- inputSchema: definition.inputSchema,
575
- outputSchema: definition.outputSchema,
576
- annotations: definition.annotations,
577
- _meta: {
578
- "openfairygui/backendMethod": definition.backendMethod,
579
- "openfairygui/adapter": "thin-backend-p2"
580
- }
581
- }, async (args) => callOpenFairyGuiBackendTool(runtime, definition.name, args));
592
+ const tools = [];
593
+ for (const definition of OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS) {
594
+ const metadata = {
595
+ name: definition.name,
596
+ title: definition.title,
597
+ description: definition.description,
598
+ annotations: definition.annotations,
599
+ _meta: {
600
+ "openfairygui/backendMethod": definition.backendMethod,
601
+ "openfairygui/adapter": "thin-backend-p2",
602
+ "openfairygui/contractDigest": CONTRACT_SNAPSHOT.digest
603
+ }
604
+ };
605
+ server.registerTool(definition.name, {
606
+ ...metadata,
607
+ inputSchema: definition.inputSchema,
608
+ outputSchema: definition.outputSchema
609
+ }, async (args) => callOpenFairyGuiBackendTool(runtime, definition.name, args));
610
+ tools.push(_modelcontextprotocol_sdk_types_js.ToolSchema.parse({
611
+ ...metadata,
612
+ inputSchema: zod.z.toJSONSchema(definition.inputSchema, {
613
+ target: "draft-07",
614
+ io: "input",
615
+ reused: "ref"
616
+ }),
617
+ outputSchema: zod.z.toJSONSchema(definition.outputSchema, {
618
+ target: "draft-07",
619
+ io: "output",
620
+ reused: "ref"
621
+ })
622
+ }));
623
+ }
624
+ server.server.setRequestHandler(_modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, () => ({ tools: structuredClone(tools) }));
582
625
  registerOpenFairyGuiBackendResources(server, runtime);
583
626
  registerOpenFairyGuiBackendPrompts(server);
584
627
  return server;
@@ -586,7 +629,8 @@ function createOpenFairyGuiMcpServer(options = {}) {
586
629
  //#endregion
587
630
  //#region src/stdio.ts
588
631
  async function connectOpenFairyGuiMcpStdio() {
589
- await createOpenFairyGuiMcpServer().connect(new _modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport());
632
+ const configuredRoots = process.env.OPENFAIRYGUI_ALLOWED_PROJECT_ROOTS?.split(node_path.default.delimiter).map((value) => value.trim()).filter(Boolean);
633
+ await createOpenFairyGuiMcpServer({ allowedProjectRoots: configuredRoots }).connect(new _modelcontextprotocol_sdk_server_stdio_js.StdioServerTransport());
590
634
  }
591
635
  if (process.argv[1] && require("url").pathToFileURL(__filename).href === (0, node_url.pathToFileURL)(process.argv[1]).href) connectOpenFairyGuiMcpStdio().catch((error) => {
592
636
  console.error(error instanceof Error ? error.stack ?? error.message : String(error));
@@ -629,16 +673,16 @@ Object.defineProperty(exports, "OPENFAIRYGUI_BACKEND_TOOL_NAMES", {
629
673
  return OPENFAIRYGUI_BACKEND_TOOL_NAMES;
630
674
  }
631
675
  });
632
- Object.defineProperty(exports, "OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA", {
676
+ Object.defineProperty(exports, "OPENFAIRYGUI_BACKEND_TOOL_PREFIX", {
633
677
  enumerable: true,
634
678
  get: function() {
635
- return OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA;
679
+ return OPENFAIRYGUI_BACKEND_TOOL_PREFIX;
636
680
  }
637
681
  });
638
- Object.defineProperty(exports, "OPENFAIRYGUI_BACKEND_TOOL_PREFIX", {
682
+ Object.defineProperty(exports, "__toESM", {
639
683
  enumerable: true,
640
684
  get: function() {
641
- return OPENFAIRYGUI_BACKEND_TOOL_PREFIX;
685
+ return __toESM;
642
686
  }
643
687
  });
644
688
  Object.defineProperty(exports, "callOpenFairyGuiBackendTool", {