@openfairygui/mcp 0.4.0 → 0.5.0-alpha.2
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 +46 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +45 -4
- package/dist/index.d.mts +45 -4
- package/dist/index.mjs +1 -1
- package/dist/{stdio-9ka7bvOr.mjs → stdio--4YykL4s.mjs} +70 -29
- package/dist/{stdio-B0OU-oZC.cjs → stdio-D33c37mD.cjs} +70 -29
- package/dist/stdio.cjs +1 -1
- package/dist/stdio.mjs +1 -1
- package/package.json +4 -4
- package/src/contract-schema.ts +10 -0
- package/src/index.ts +1 -0
- package/src/server.ts +20 -16
- package/src/tool-handler.ts +29 -6
- package/src/tool-metadata.ts +24 -0
package/README.md
CHANGED
|
@@ -14,6 +14,8 @@ It maps the backend P2 runtime surface into MCP tools:
|
|
|
14
14
|
- `getSession`
|
|
15
15
|
- `getProjectOutline`
|
|
16
16
|
- `queryEntity`
|
|
17
|
+
- `readSessionState`
|
|
18
|
+
- `readResourceBytes`
|
|
17
19
|
- `validateSession`
|
|
18
20
|
- `preflightTransaction`
|
|
19
21
|
- `applyTransaction`
|
|
@@ -34,7 +36,7 @@ Each tool exposes a method-specific output schema for `structuredContent.backend
|
|
|
34
36
|
- `error?`
|
|
35
37
|
- `meta?`
|
|
36
38
|
|
|
37
|
-
The factory
|
|
39
|
+
The factory registers the 20-method Backend catalog. The SDK owns dynamic discovery and dispatch, including Host tools added through `server.registerTool()`. Input/output schemas come from the canonical installed contract; discovery uses self-contained draft-07 `definitions`/`$ref` to reuse repeated structures without loosening the 41-operation union, call validators or input budgets. Installed operation documentation remains available through `openfairygui://contracts/operations` and `openfairygui://docs/index`.
|
|
38
40
|
|
|
39
41
|
P1 also registers MCP-native ergonomics around the same backend surface:
|
|
40
42
|
|
|
@@ -49,6 +51,8 @@ P1 also registers MCP-native ergonomics around the same backend surface:
|
|
|
49
51
|
Resources return `application/json` text containing the unchanged backend result envelope. Parameterized polling remains tool-based: `getEvents` and `listJobs` are not exposed as resource URI query grammars.
|
|
50
52
|
The project outline is revision-bound and exposes package, resource, folder, display-node, controller-page, and transition identities for transaction planning. It intentionally omits source bytes and full property payloads. `validateSession` returns the backend-owned read-only project validation report; the MCP adapter does not reinterpret its diagnostics.
|
|
51
53
|
|
|
54
|
+
`readSessionState` returns a detached public UAM model without primary asset bytes, plus the current edit revision and source-read diagnostics. `readResourceBytes` reads one already-loaded primary asset with a required matching revision. Neither reads storage or changes the session. Both tools enforce their native response limits and a separate 16 MiB complete MCP response limit; see the installed method schemas and [workflow](../backend/docs/workflow.md).
|
|
55
|
+
|
|
52
56
|
It does **not** redefine transaction selectors, transaction operations, path policy, session semantics, job semantics, cache semantics, or backend error envelopes. Those remain owned by `@openfairygui/backend`, `@openfairygui/functions`, and `@openfairygui/core`.
|
|
53
57
|
|
|
54
58
|
It also does **not** activate artifact publish/restore jobs, subscriptions, persistent jobs, or cache-as-source-of-truth behavior. MCP roots may be useful client context, but this package does not enforce roots or duplicate backend path canonicalization; backend path policy remains authoritative.
|
|
@@ -67,6 +71,47 @@ For stdio clients, use the package binary:
|
|
|
67
71
|
ofgui-mcp
|
|
68
72
|
```
|
|
69
73
|
|
|
74
|
+
### Host composition
|
|
75
|
+
|
|
76
|
+
Pass `instructions` to the factory to publish Host guidance in the SDK initialize handshake.
|
|
77
|
+
|
|
78
|
+
Use `toolPolicies` to gate selected tools before Backend executes. Each policy declares a synchronous Zod `failureSchema` and a `beforeCall` callback. The callback receives a detached copy of the validated wire input and may be async. Returning `undefined` invokes the original Backend method once with the original input; returning a declared `ok: false` envelope stops the call and produces matching text/`structuredContent.backendResult` with `isError: true`.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { createOpenFairyGuiMcpServer, type OpenFairyGuiMcpToolPolicy } from '@openfairygui/mcp';
|
|
82
|
+
import { z } from 'zod';
|
|
83
|
+
|
|
84
|
+
const policy: OpenFairyGuiMcpToolPolicy = {
|
|
85
|
+
failureSchema: z.strictObject({
|
|
86
|
+
ok: z.literal(false),
|
|
87
|
+
error: z.strictObject({
|
|
88
|
+
code: z.literal('save_approval_required'),
|
|
89
|
+
approvalRequestId: z.string(),
|
|
90
|
+
approvalPath: z.string(),
|
|
91
|
+
}),
|
|
92
|
+
}),
|
|
93
|
+
beforeCall(input) {
|
|
94
|
+
// Host-owned grant store: bind the grant to session, revision, operation and all options.
|
|
95
|
+
if (hostGrants.consume('saveSession', input)) return undefined;
|
|
96
|
+
return { ok: false, error: {
|
|
97
|
+
code: 'save_approval_required',
|
|
98
|
+
approvalRequestId: hostGrants.request('saveSession', input),
|
|
99
|
+
approvalPath: '/#save-approvals',
|
|
100
|
+
} };
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
const server = createOpenFairyGuiMcpServer({ runtime, instructions: 'Host writes require owner approval.', toolPolicies: {
|
|
104
|
+
openfairygui_backend_save_session: policy,
|
|
105
|
+
} });
|
|
106
|
+
server.registerTool('host_probe', { inputSchema: z.object({}) }, async () => ({
|
|
107
|
+
content: [{ type: 'text', text: 'ok' }],
|
|
108
|
+
}));
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`runtime` and `hostGrants` belong to the embedding Host. Configure `materialize_session` separately when it also requires approval; grants must distinguish the two operations. The policy must consume approval before allowing the call and must not call the Backend write itself. Backend still checks revision, paths and disk state after approval; failures are not retried automatically. Read tools without policies do not require grants.
|
|
112
|
+
|
|
113
|
+
The selected tool's advertised output includes its declared Host failure branch. `_meta['openfairygui/hostPolicy']` marks that extension; the contract digest and installed documentation describe the unchanged Backend branch. Host failures do not become Backend error codes. Unknown tool-policy names fail server construction. Invalid inputs never reach the policy; throwing policies or invalid policy results stop before Backend and return `backend_unhandled_error`. Backend results always pass their canonical schema, even if a Host schema would accept them. Method response budgets also apply to policy failures. The direct `callOpenFairyGuiBackendTool(runtime, name, input, policy)` entry accepts the same policy as its optional fourth argument.
|
|
114
|
+
|
|
70
115
|
Example local MCP client configuration:
|
|
71
116
|
|
|
72
117
|
```json
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_stdio = require("./stdio-
|
|
2
|
+
const require_stdio = require("./stdio-D33c37mD.cjs");
|
|
3
3
|
let _openfairygui_backend_docs = require("@openfairygui/backend/docs");
|
|
4
4
|
exports.OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI = require_stdio.OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI;
|
|
5
5
|
exports.OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS = require_stdio.OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { connectOpenFairyGuiMcpStdio } from "./stdio.cjs";
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
-
import { BackendMethodName, BackendMethodName as BackendMethodName$1, BackendRuntime } from "@openfairygui/backend";
|
|
5
4
|
import { z } from "zod";
|
|
5
|
+
import { BackendMethodName, BackendMethodName as BackendMethodName$1, BackendRuntime } from "@openfairygui/backend";
|
|
6
6
|
import { OPENFAIRYGUI_OPERATION_CATALOG_URI, OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE, getOpenFairyGuiOperationCatalog, getOpenFairyGuiOperationSchema } from "@openfairygui/backend/docs";
|
|
7
7
|
|
|
8
8
|
//#region src/tool-metadata.d.ts
|
|
@@ -11,6 +11,8 @@ interface BackendToolMetadata {
|
|
|
11
11
|
backendMethod: BackendMethodName$1;
|
|
12
12
|
title: string;
|
|
13
13
|
description: string;
|
|
14
|
+
/** Bound the complete CallToolResult JSON; bounded reads use compact text JSON. */
|
|
15
|
+
maxResponseBytes?: number;
|
|
14
16
|
annotations: {
|
|
15
17
|
readOnlyHint?: boolean;
|
|
16
18
|
destructiveHint?: boolean;
|
|
@@ -78,6 +80,28 @@ declare const OPENFAIRYGUI_BACKEND_TOOL_METADATA: readonly [{
|
|
|
78
80
|
readonly idempotentHint: true;
|
|
79
81
|
readonly openWorldHint: false;
|
|
80
82
|
};
|
|
83
|
+
}, {
|
|
84
|
+
readonly name: "openfairygui_backend_read_session_state";
|
|
85
|
+
readonly backendMethod: "readSessionState";
|
|
86
|
+
readonly title: "Read Session State";
|
|
87
|
+
readonly 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.";
|
|
88
|
+
readonly maxResponseBytes: 16777216;
|
|
89
|
+
readonly annotations: {
|
|
90
|
+
readonly readOnlyHint: true;
|
|
91
|
+
readonly idempotentHint: true;
|
|
92
|
+
readonly openWorldHint: false;
|
|
93
|
+
};
|
|
94
|
+
}, {
|
|
95
|
+
readonly name: "openfairygui_backend_read_resource_bytes";
|
|
96
|
+
readonly backendMethod: "readResourceBytes";
|
|
97
|
+
readonly title: "Read Resource Bytes";
|
|
98
|
+
readonly 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.";
|
|
99
|
+
readonly maxResponseBytes: 16777216;
|
|
100
|
+
readonly annotations: {
|
|
101
|
+
readonly readOnlyHint: true;
|
|
102
|
+
readonly idempotentHint: true;
|
|
103
|
+
readonly openWorldHint: false;
|
|
104
|
+
};
|
|
81
105
|
}, {
|
|
82
106
|
readonly name: "openfairygui_backend_validate_session";
|
|
83
107
|
readonly backendMethod: "validateSession";
|
|
@@ -206,7 +230,7 @@ declare const OPENFAIRYGUI_BACKEND_TOOL_METADATA: readonly [{
|
|
|
206
230
|
//#region src/tool-definitions.d.ts
|
|
207
231
|
declare const OPENFAIRYGUI_BACKEND_TOOL_PREFIX = "openfairygui_backend_";
|
|
208
232
|
type OpenFairyGuiBackendToolName = typeof OPENFAIRYGUI_BACKEND_TOOL_METADATA[number]['name'];
|
|
209
|
-
declare const OPENFAIRYGUI_BACKEND_TOOL_NAMES: ("openfairygui_backend_get_capabilities" | "openfairygui_backend_open_session" | "openfairygui_backend_open_project_session" | "openfairygui_backend_get_session" | "openfairygui_backend_get_project_outline" | "openfairygui_backend_query_entity" | "openfairygui_backend_validate_session" | "openfairygui_backend_preflight_transaction" | "openfairygui_backend_apply_transaction" | "openfairygui_backend_save_session" | "openfairygui_backend_materialize_session" | "openfairygui_backend_close_session" | "openfairygui_backend_get_events" | "openfairygui_backend_get_job" | "openfairygui_backend_list_jobs" | "openfairygui_backend_cancel_job" | "openfairygui_backend_get_cache_snapshot" | "openfairygui_backend_refresh_cache")[];
|
|
233
|
+
declare const OPENFAIRYGUI_BACKEND_TOOL_NAMES: ("openfairygui_backend_get_capabilities" | "openfairygui_backend_open_session" | "openfairygui_backend_open_project_session" | "openfairygui_backend_get_session" | "openfairygui_backend_get_project_outline" | "openfairygui_backend_query_entity" | "openfairygui_backend_read_session_state" | "openfairygui_backend_read_resource_bytes" | "openfairygui_backend_validate_session" | "openfairygui_backend_preflight_transaction" | "openfairygui_backend_apply_transaction" | "openfairygui_backend_save_session" | "openfairygui_backend_materialize_session" | "openfairygui_backend_close_session" | "openfairygui_backend_get_events" | "openfairygui_backend_get_job" | "openfairygui_backend_list_jobs" | "openfairygui_backend_cancel_job" | "openfairygui_backend_get_cache_snapshot" | "openfairygui_backend_refresh_cache")[];
|
|
210
234
|
interface OpenFairyGuiBackendToolDefinition extends BackendToolMetadata {
|
|
211
235
|
name: OpenFairyGuiBackendToolName;
|
|
212
236
|
inputSchema: z.ZodObject;
|
|
@@ -216,7 +240,20 @@ declare const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS: readonly OpenFairyGuiBacken
|
|
|
216
240
|
//#endregion
|
|
217
241
|
//#region src/tool-handler.d.ts
|
|
218
242
|
type OpenFairyGuiBackendRuntime = Pick<BackendRuntime, BackendMethodName$1>;
|
|
219
|
-
|
|
243
|
+
/** Host policy runs after input validation, before the single Backend invocation. */
|
|
244
|
+
interface OpenFairyGuiMcpToolPolicy {
|
|
245
|
+
/** Explicit Host-owned failure envelope, carried in structuredContent.backendResult. */
|
|
246
|
+
failureSchema: z.ZodType<{
|
|
247
|
+
ok: false;
|
|
248
|
+
}>;
|
|
249
|
+
/** Return a declared failure to stop, or undefined to call Backend with the original input. */
|
|
250
|
+
beforeCall(input: Readonly<Record<string, unknown>>): {
|
|
251
|
+
ok: false;
|
|
252
|
+
} | undefined | Promise<{
|
|
253
|
+
ok: false;
|
|
254
|
+
} | undefined>;
|
|
255
|
+
}
|
|
256
|
+
declare function callOpenFairyGuiBackendTool(runtime: OpenFairyGuiBackendRuntime, name: OpenFairyGuiBackendToolName, input: Record<string, unknown>, policy?: OpenFairyGuiMcpToolPolicy): Promise<CallToolResult>;
|
|
220
257
|
//#endregion
|
|
221
258
|
//#region src/server.d.ts
|
|
222
259
|
interface CreateOpenFairyGuiMcpServerOptions {
|
|
@@ -225,6 +262,10 @@ interface CreateOpenFairyGuiMcpServerOptions {
|
|
|
225
262
|
allowedProjectRoots?: readonly string[];
|
|
226
263
|
name?: string;
|
|
227
264
|
version?: string;
|
|
265
|
+
/** Host guidance returned by the SDK initialize handshake. */
|
|
266
|
+
instructions?: string;
|
|
267
|
+
/** Per-tool Host failures do not change the canonical Backend contracts. */
|
|
268
|
+
toolPolicies?: Partial<Record<OpenFairyGuiBackendToolName, OpenFairyGuiMcpToolPolicy>>;
|
|
228
269
|
}
|
|
229
270
|
declare function createOpenFairyGuiMcpServer(options?: CreateOpenFairyGuiMcpServerOptions): McpServer;
|
|
230
271
|
//#endregion
|
|
@@ -267,4 +308,4 @@ declare const OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS: readonly [{
|
|
|
267
308
|
declare const OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI = "openfairygui://backend/capabilities";
|
|
268
309
|
declare const OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES: readonly ["openfairygui://docs/methods/{method}", "openfairygui://docs/cli/{command}", "openfairygui://docs/diagnostics/{code}", "openfairygui://contracts/operations/{kind}", "openfairygui://backend/session/{sessionId}", "openfairygui://backend/session/{sessionId}/outline", "openfairygui://backend/cache/{sessionId}", "openfairygui://backend/job/{sessionId}/{jobId}"];
|
|
269
310
|
//#endregion
|
|
270
|
-
export { type BackendMethodName, type CreateOpenFairyGuiMcpServerOptions, OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS, OPENFAIRYGUI_BACKEND_PROMPT_NAMES, OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES, OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS, OPENFAIRYGUI_BACKEND_TOOL_NAMES, OPENFAIRYGUI_BACKEND_TOOL_PREFIX, OPENFAIRYGUI_OPERATION_CATALOG_URI, OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE, type OpenFairyGuiBackendPromptName, type OpenFairyGuiBackendRuntime, type OpenFairyGuiBackendToolDefinition, type OpenFairyGuiBackendToolName, callOpenFairyGuiBackendTool, connectOpenFairyGuiMcpStdio, createOpenFairyGuiMcpServer, getOpenFairyGuiOperationCatalog, getOpenFairyGuiOperationSchema };
|
|
311
|
+
export { type BackendMethodName, type CreateOpenFairyGuiMcpServerOptions, OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS, OPENFAIRYGUI_BACKEND_PROMPT_NAMES, OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES, OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS, OPENFAIRYGUI_BACKEND_TOOL_NAMES, OPENFAIRYGUI_BACKEND_TOOL_PREFIX, OPENFAIRYGUI_OPERATION_CATALOG_URI, OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE, type OpenFairyGuiBackendPromptName, type OpenFairyGuiBackendRuntime, type OpenFairyGuiBackendToolDefinition, type OpenFairyGuiBackendToolName, type OpenFairyGuiMcpToolPolicy, callOpenFairyGuiBackendTool, connectOpenFairyGuiMcpStdio, createOpenFairyGuiMcpServer, getOpenFairyGuiOperationCatalog, getOpenFairyGuiOperationSchema };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { connectOpenFairyGuiMcpStdio } from "./stdio.mjs";
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { BackendMethodName, BackendMethodName as BackendMethodName$1, BackendRuntime } from "@openfairygui/backend";
|
|
6
5
|
import { OPENFAIRYGUI_OPERATION_CATALOG_URI, OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE, getOpenFairyGuiOperationCatalog, getOpenFairyGuiOperationSchema } from "@openfairygui/backend/docs";
|
|
6
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
7
7
|
|
|
8
8
|
//#region src/tool-metadata.d.ts
|
|
9
9
|
interface BackendToolMetadata {
|
|
@@ -11,6 +11,8 @@ interface BackendToolMetadata {
|
|
|
11
11
|
backendMethod: BackendMethodName$1;
|
|
12
12
|
title: string;
|
|
13
13
|
description: string;
|
|
14
|
+
/** Bound the complete CallToolResult JSON; bounded reads use compact text JSON. */
|
|
15
|
+
maxResponseBytes?: number;
|
|
14
16
|
annotations: {
|
|
15
17
|
readOnlyHint?: boolean;
|
|
16
18
|
destructiveHint?: boolean;
|
|
@@ -78,6 +80,28 @@ declare const OPENFAIRYGUI_BACKEND_TOOL_METADATA: readonly [{
|
|
|
78
80
|
readonly idempotentHint: true;
|
|
79
81
|
readonly openWorldHint: false;
|
|
80
82
|
};
|
|
83
|
+
}, {
|
|
84
|
+
readonly name: "openfairygui_backend_read_session_state";
|
|
85
|
+
readonly backendMethod: "readSessionState";
|
|
86
|
+
readonly title: "Read Session State";
|
|
87
|
+
readonly 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.";
|
|
88
|
+
readonly maxResponseBytes: 16777216;
|
|
89
|
+
readonly annotations: {
|
|
90
|
+
readonly readOnlyHint: true;
|
|
91
|
+
readonly idempotentHint: true;
|
|
92
|
+
readonly openWorldHint: false;
|
|
93
|
+
};
|
|
94
|
+
}, {
|
|
95
|
+
readonly name: "openfairygui_backend_read_resource_bytes";
|
|
96
|
+
readonly backendMethod: "readResourceBytes";
|
|
97
|
+
readonly title: "Read Resource Bytes";
|
|
98
|
+
readonly 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.";
|
|
99
|
+
readonly maxResponseBytes: 16777216;
|
|
100
|
+
readonly annotations: {
|
|
101
|
+
readonly readOnlyHint: true;
|
|
102
|
+
readonly idempotentHint: true;
|
|
103
|
+
readonly openWorldHint: false;
|
|
104
|
+
};
|
|
81
105
|
}, {
|
|
82
106
|
readonly name: "openfairygui_backend_validate_session";
|
|
83
107
|
readonly backendMethod: "validateSession";
|
|
@@ -206,7 +230,7 @@ declare const OPENFAIRYGUI_BACKEND_TOOL_METADATA: readonly [{
|
|
|
206
230
|
//#region src/tool-definitions.d.ts
|
|
207
231
|
declare const OPENFAIRYGUI_BACKEND_TOOL_PREFIX = "openfairygui_backend_";
|
|
208
232
|
type OpenFairyGuiBackendToolName = typeof OPENFAIRYGUI_BACKEND_TOOL_METADATA[number]['name'];
|
|
209
|
-
declare const OPENFAIRYGUI_BACKEND_TOOL_NAMES: ("openfairygui_backend_get_capabilities" | "openfairygui_backend_open_session" | "openfairygui_backend_open_project_session" | "openfairygui_backend_get_session" | "openfairygui_backend_get_project_outline" | "openfairygui_backend_query_entity" | "openfairygui_backend_validate_session" | "openfairygui_backend_preflight_transaction" | "openfairygui_backend_apply_transaction" | "openfairygui_backend_save_session" | "openfairygui_backend_materialize_session" | "openfairygui_backend_close_session" | "openfairygui_backend_get_events" | "openfairygui_backend_get_job" | "openfairygui_backend_list_jobs" | "openfairygui_backend_cancel_job" | "openfairygui_backend_get_cache_snapshot" | "openfairygui_backend_refresh_cache")[];
|
|
233
|
+
declare const OPENFAIRYGUI_BACKEND_TOOL_NAMES: ("openfairygui_backend_get_capabilities" | "openfairygui_backend_open_session" | "openfairygui_backend_open_project_session" | "openfairygui_backend_get_session" | "openfairygui_backend_get_project_outline" | "openfairygui_backend_query_entity" | "openfairygui_backend_read_session_state" | "openfairygui_backend_read_resource_bytes" | "openfairygui_backend_validate_session" | "openfairygui_backend_preflight_transaction" | "openfairygui_backend_apply_transaction" | "openfairygui_backend_save_session" | "openfairygui_backend_materialize_session" | "openfairygui_backend_close_session" | "openfairygui_backend_get_events" | "openfairygui_backend_get_job" | "openfairygui_backend_list_jobs" | "openfairygui_backend_cancel_job" | "openfairygui_backend_get_cache_snapshot" | "openfairygui_backend_refresh_cache")[];
|
|
210
234
|
interface OpenFairyGuiBackendToolDefinition extends BackendToolMetadata {
|
|
211
235
|
name: OpenFairyGuiBackendToolName;
|
|
212
236
|
inputSchema: z.ZodObject;
|
|
@@ -216,7 +240,20 @@ declare const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS: readonly OpenFairyGuiBacken
|
|
|
216
240
|
//#endregion
|
|
217
241
|
//#region src/tool-handler.d.ts
|
|
218
242
|
type OpenFairyGuiBackendRuntime = Pick<BackendRuntime, BackendMethodName$1>;
|
|
219
|
-
|
|
243
|
+
/** Host policy runs after input validation, before the single Backend invocation. */
|
|
244
|
+
interface OpenFairyGuiMcpToolPolicy {
|
|
245
|
+
/** Explicit Host-owned failure envelope, carried in structuredContent.backendResult. */
|
|
246
|
+
failureSchema: z.ZodType<{
|
|
247
|
+
ok: false;
|
|
248
|
+
}>;
|
|
249
|
+
/** Return a declared failure to stop, or undefined to call Backend with the original input. */
|
|
250
|
+
beforeCall(input: Readonly<Record<string, unknown>>): {
|
|
251
|
+
ok: false;
|
|
252
|
+
} | undefined | Promise<{
|
|
253
|
+
ok: false;
|
|
254
|
+
} | undefined>;
|
|
255
|
+
}
|
|
256
|
+
declare function callOpenFairyGuiBackendTool(runtime: OpenFairyGuiBackendRuntime, name: OpenFairyGuiBackendToolName, input: Record<string, unknown>, policy?: OpenFairyGuiMcpToolPolicy): Promise<CallToolResult>;
|
|
220
257
|
//#endregion
|
|
221
258
|
//#region src/server.d.ts
|
|
222
259
|
interface CreateOpenFairyGuiMcpServerOptions {
|
|
@@ -225,6 +262,10 @@ interface CreateOpenFairyGuiMcpServerOptions {
|
|
|
225
262
|
allowedProjectRoots?: readonly string[];
|
|
226
263
|
name?: string;
|
|
227
264
|
version?: string;
|
|
265
|
+
/** Host guidance returned by the SDK initialize handshake. */
|
|
266
|
+
instructions?: string;
|
|
267
|
+
/** Per-tool Host failures do not change the canonical Backend contracts. */
|
|
268
|
+
toolPolicies?: Partial<Record<OpenFairyGuiBackendToolName, OpenFairyGuiMcpToolPolicy>>;
|
|
228
269
|
}
|
|
229
270
|
declare function createOpenFairyGuiMcpServer(options?: CreateOpenFairyGuiMcpServerOptions): McpServer;
|
|
230
271
|
//#endregion
|
|
@@ -267,4 +308,4 @@ declare const OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS: readonly [{
|
|
|
267
308
|
declare const OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI = "openfairygui://backend/capabilities";
|
|
268
309
|
declare const OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES: readonly ["openfairygui://docs/methods/{method}", "openfairygui://docs/cli/{command}", "openfairygui://docs/diagnostics/{code}", "openfairygui://contracts/operations/{kind}", "openfairygui://backend/session/{sessionId}", "openfairygui://backend/session/{sessionId}/outline", "openfairygui://backend/cache/{sessionId}", "openfairygui://backend/job/{sessionId}/{jobId}"];
|
|
269
310
|
//#endregion
|
|
270
|
-
export { type BackendMethodName, type CreateOpenFairyGuiMcpServerOptions, OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS, OPENFAIRYGUI_BACKEND_PROMPT_NAMES, OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES, OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS, OPENFAIRYGUI_BACKEND_TOOL_NAMES, OPENFAIRYGUI_BACKEND_TOOL_PREFIX, OPENFAIRYGUI_OPERATION_CATALOG_URI, OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE, type OpenFairyGuiBackendPromptName, type OpenFairyGuiBackendRuntime, type OpenFairyGuiBackendToolDefinition, type OpenFairyGuiBackendToolName, callOpenFairyGuiBackendTool, connectOpenFairyGuiMcpStdio, createOpenFairyGuiMcpServer, getOpenFairyGuiOperationCatalog, getOpenFairyGuiOperationSchema };
|
|
311
|
+
export { type BackendMethodName, type CreateOpenFairyGuiMcpServerOptions, OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS, OPENFAIRYGUI_BACKEND_PROMPT_NAMES, OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES, OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS, OPENFAIRYGUI_BACKEND_TOOL_NAMES, OPENFAIRYGUI_BACKEND_TOOL_PREFIX, OPENFAIRYGUI_OPERATION_CATALOG_URI, OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE, type OpenFairyGuiBackendPromptName, type OpenFairyGuiBackendRuntime, type OpenFairyGuiBackendToolDefinition, type OpenFairyGuiBackendToolName, type OpenFairyGuiMcpToolPolicy, callOpenFairyGuiBackendTool, connectOpenFairyGuiMcpStdio, createOpenFairyGuiMcpServer, getOpenFairyGuiOperationCatalog, getOpenFairyGuiOperationSchema };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as OPENFAIRYGUI_BACKEND_TOOL_NAMES, c as OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES, d as getOpenFairyGuiOperationCatalog, f as getOpenFairyGuiOperationSchema, i as OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS, l as OPENFAIRYGUI_OPERATION_CATALOG_URI, m as OPENFAIRYGUI_BACKEND_PROMPT_NAMES, n as createOpenFairyGuiMcpServer, o as OPENFAIRYGUI_BACKEND_TOOL_PREFIX, p as OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS, r as callOpenFairyGuiBackendTool, s as OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, t as connectOpenFairyGuiMcpStdio, u as OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE } from "./stdio
|
|
1
|
+
import { a as OPENFAIRYGUI_BACKEND_TOOL_NAMES, c as OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES, d as getOpenFairyGuiOperationCatalog, f as getOpenFairyGuiOperationSchema, i as OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS, l as OPENFAIRYGUI_OPERATION_CATALOG_URI, m as OPENFAIRYGUI_BACKEND_PROMPT_NAMES, n as createOpenFairyGuiMcpServer, o as OPENFAIRYGUI_BACKEND_TOOL_PREFIX, p as OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS, r as callOpenFairyGuiBackendTool, s as OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, t as connectOpenFairyGuiMcpStdio, u as OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE } from "./stdio--4YykL4s.mjs";
|
|
2
2
|
export { OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI, OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS, OPENFAIRYGUI_BACKEND_PROMPT_NAMES, OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES, OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS, OPENFAIRYGUI_BACKEND_TOOL_NAMES, OPENFAIRYGUI_BACKEND_TOOL_PREFIX, OPENFAIRYGUI_OPERATION_CATALOG_URI, OPENFAIRYGUI_OPERATION_SCHEMA_TEMPLATE, callOpenFairyGuiBackendTool, connectOpenFairyGuiMcpStdio, createOpenFairyGuiMcpServer, getOpenFairyGuiOperationCatalog, getOpenFairyGuiOperationSchema };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { ListToolsRequestSchema, ToolSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
3
|
import { createNodeBackendRuntime } from "@openfairygui/backend/node";
|
|
5
4
|
import { z } from "zod";
|
|
6
5
|
import { BACKEND_CAPABILITY_SCHEMA_VERSION, BACKEND_CONTRACT_VERSION, BACKEND_DIAGNOSTICS_URI, BACKEND_DIAGNOSTIC_TEMPLATE, getBackendDiagnosticCatalog, getBackendDiagnosticGuide } from "@openfairygui/backend";
|
|
@@ -113,6 +112,17 @@ function contractObjectSchema(schema) {
|
|
|
113
112
|
if (!(result instanceof z.ZodObject)) throw new TypeError("Tool contract must be an object");
|
|
114
113
|
return result;
|
|
115
114
|
}
|
|
115
|
+
/** Keep SDK discovery dynamic without expanding shared contract definitions. */
|
|
116
|
+
function compactToolSchema(schema, io) {
|
|
117
|
+
return z.looseObject({}).superRefine((value, context) => {
|
|
118
|
+
const parsed = schema.safeParse(value);
|
|
119
|
+
if (!parsed.success) for (const issue of parsed.error.issues) context.addIssue({ ...issue });
|
|
120
|
+
}).meta(z.toJSONSchema(schema, {
|
|
121
|
+
target: "draft-07",
|
|
122
|
+
io,
|
|
123
|
+
reused: "ref"
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
116
126
|
/** Decode only generated Uint8Array locations; arbitrary JSON metadata is not rewritten. */
|
|
117
127
|
function decodeToolBytes(input, paths) {
|
|
118
128
|
if (!paths.length) return input;
|
|
@@ -304,6 +314,30 @@ const OPENFAIRYGUI_BACKEND_TOOL_METADATA = [
|
|
|
304
314
|
openWorldHint: false
|
|
305
315
|
}
|
|
306
316
|
},
|
|
317
|
+
{
|
|
318
|
+
name: "openfairygui_backend_read_session_state",
|
|
319
|
+
backendMethod: "readSessionState",
|
|
320
|
+
title: "Read Session State",
|
|
321
|
+
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.",
|
|
322
|
+
maxResponseBytes: 16777216,
|
|
323
|
+
annotations: {
|
|
324
|
+
readOnlyHint: true,
|
|
325
|
+
idempotentHint: true,
|
|
326
|
+
openWorldHint: false
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: "openfairygui_backend_read_resource_bytes",
|
|
331
|
+
backendMethod: "readResourceBytes",
|
|
332
|
+
title: "Read Resource Bytes",
|
|
333
|
+
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.",
|
|
334
|
+
maxResponseBytes: 16777216,
|
|
335
|
+
annotations: {
|
|
336
|
+
readOnlyHint: true,
|
|
337
|
+
idempotentHint: true,
|
|
338
|
+
openWorldHint: false
|
|
339
|
+
}
|
|
340
|
+
},
|
|
307
341
|
{
|
|
308
342
|
name: "openfairygui_backend_validate_session",
|
|
309
343
|
backendMethod: "validateSession",
|
|
@@ -495,8 +529,8 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = OPENFAIRYGUI_BACKEND_TOOL_METADATA
|
|
|
495
529
|
});
|
|
496
530
|
//#endregion
|
|
497
531
|
//#region src/tool-handler.ts
|
|
498
|
-
function jsonResult(payload, isError = false) {
|
|
499
|
-
const text = JSON.stringify(payload, (_key, value) => value instanceof Uint8Array ? [...value] : value, 2);
|
|
532
|
+
function jsonResult(payload, isError = false, compact = false) {
|
|
533
|
+
const text = JSON.stringify(payload, (_key, value) => value instanceof Uint8Array ? [...value] : value, compact ? void 0 : 2);
|
|
500
534
|
const wirePayload = JSON.parse(text);
|
|
501
535
|
return {
|
|
502
536
|
content: [{
|
|
@@ -528,16 +562,34 @@ function unhandledBackendFailure(startedAt) {
|
|
|
528
562
|
}
|
|
529
563
|
};
|
|
530
564
|
}
|
|
531
|
-
async function callOpenFairyGuiBackendTool(runtime, name, input) {
|
|
565
|
+
async function callOpenFairyGuiBackendTool(runtime, name, input, policy) {
|
|
532
566
|
if (!isOpenFairyGuiMcpPayloadWithinBudget(input)) throw new RangeError("MCP input exceeds the depth, node, key, string, or byte budget.");
|
|
533
567
|
const definition = OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS.find((entry) => entry.name === name);
|
|
534
568
|
if (!definition) throw new RangeError(`Unknown OpenFairyGUI backend MCP tool: ${name}`);
|
|
535
|
-
const
|
|
569
|
+
const parsed = definition.inputSchema.parse(input);
|
|
570
|
+
const decoded = decodeToolBytes(parsed, CONTRACT_SNAPSHOT.tools[definition.backendMethod].bytePaths);
|
|
536
571
|
const startedAt = Date.now();
|
|
537
572
|
try {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
573
|
+
let hostFailure = await policy?.beforeCall(structuredClone(parsed));
|
|
574
|
+
if (hostFailure !== void 0) {
|
|
575
|
+
hostFailure = policy.failureSchema.parse(hostFailure);
|
|
576
|
+
if (!isBackendFailure(hostFailure)) throw new TypeError("Host policy must return a failure or undefined.");
|
|
577
|
+
}
|
|
578
|
+
const result = hostFailure ?? await Reflect.apply(runtime[definition.backendMethod], runtime, definition.backendMethod === "getCapabilities" ? [] : [decoded]);
|
|
579
|
+
let response = jsonResult(result, isBackendFailure(result), definition.maxResponseBytes !== void 0);
|
|
580
|
+
if (definition.maxResponseBytes !== void 0 && new TextEncoder().encode(JSON.stringify(response)).byteLength > definition.maxResponseBytes) {
|
|
581
|
+
response = jsonResult({
|
|
582
|
+
...unhandledBackendFailure(startedAt),
|
|
583
|
+
error: {
|
|
584
|
+
code: "mcp_response_budget_exceeded",
|
|
585
|
+
message: "The complete MCP tool response exceeds its byte limit.",
|
|
586
|
+
maxBytes: definition.maxResponseBytes
|
|
587
|
+
}
|
|
588
|
+
}, true);
|
|
589
|
+
hostFailure = void 0;
|
|
590
|
+
}
|
|
591
|
+
if (hostFailure === void 0) definition.outputSchema.parse(response.structuredContent);
|
|
592
|
+
else policy.failureSchema.parse(response.structuredContent?.backendResult);
|
|
541
593
|
return response;
|
|
542
594
|
} catch {
|
|
543
595
|
return jsonResult(unhandledBackendFailure(startedAt), true);
|
|
@@ -547,7 +599,7 @@ async function callOpenFairyGuiBackendTool(runtime, name, input) {
|
|
|
547
599
|
//#region src/server.ts
|
|
548
600
|
const require = createRequire(import.meta.url);
|
|
549
601
|
function getInjectedPackageVersion() {
|
|
550
|
-
const version = "0.
|
|
602
|
+
const version = "0.5.0-alpha.2";
|
|
551
603
|
return typeof version === "string" && true ? version : null;
|
|
552
604
|
}
|
|
553
605
|
function readPackageVersion() {
|
|
@@ -561,13 +613,15 @@ function readPackageVersion() {
|
|
|
561
613
|
}
|
|
562
614
|
const PACKAGE_VERSION = readPackageVersion();
|
|
563
615
|
function createOpenFairyGuiMcpServer(options = {}) {
|
|
616
|
+
for (const name of Object.keys(options.toolPolicies ?? {})) if (!OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS.some((definition) => definition.name === name)) throw new RangeError(`Unknown OpenFairyGUI backend MCP tool policy: ${name}`);
|
|
564
617
|
const runtime = options.runtime ?? createNodeBackendRuntime({ allowedProjectRoots: options.allowedProjectRoots ?? [process.cwd()] });
|
|
565
618
|
const server = new McpServer({
|
|
566
619
|
name: options.name ?? "openfairygui-mcp",
|
|
567
620
|
version: options.version ?? PACKAGE_VERSION
|
|
568
|
-
});
|
|
569
|
-
const tools = [];
|
|
621
|
+
}, { instructions: options.instructions });
|
|
570
622
|
for (const definition of OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS) {
|
|
623
|
+
const policy = options.toolPolicies?.[definition.name];
|
|
624
|
+
const outputSchema = policy ? definition.outputSchema.extend({ backendResult: z.union([definition.outputSchema.shape.backendResult, policy.failureSchema]) }) : definition.outputSchema;
|
|
571
625
|
const metadata = {
|
|
572
626
|
name: definition.name,
|
|
573
627
|
title: definition.title,
|
|
@@ -576,29 +630,16 @@ function createOpenFairyGuiMcpServer(options = {}) {
|
|
|
576
630
|
_meta: {
|
|
577
631
|
"openfairygui/backendMethod": definition.backendMethod,
|
|
578
632
|
"openfairygui/adapter": "thin-backend-p2",
|
|
579
|
-
"openfairygui/contractDigest": CONTRACT_SNAPSHOT.digest
|
|
633
|
+
"openfairygui/contractDigest": CONTRACT_SNAPSHOT.digest,
|
|
634
|
+
...policy ? { "openfairygui/hostPolicy": true } : {}
|
|
580
635
|
}
|
|
581
636
|
};
|
|
582
637
|
server.registerTool(definition.name, {
|
|
583
638
|
...metadata,
|
|
584
|
-
inputSchema: definition.inputSchema,
|
|
585
|
-
outputSchema:
|
|
586
|
-
}, async (args) => callOpenFairyGuiBackendTool(runtime, definition.name, args));
|
|
587
|
-
tools.push(ToolSchema.parse({
|
|
588
|
-
...metadata,
|
|
589
|
-
inputSchema: z.toJSONSchema(definition.inputSchema, {
|
|
590
|
-
target: "draft-07",
|
|
591
|
-
io: "input",
|
|
592
|
-
reused: "ref"
|
|
593
|
-
}),
|
|
594
|
-
outputSchema: z.toJSONSchema(definition.outputSchema, {
|
|
595
|
-
target: "draft-07",
|
|
596
|
-
io: "output",
|
|
597
|
-
reused: "ref"
|
|
598
|
-
})
|
|
599
|
-
}));
|
|
639
|
+
inputSchema: compactToolSchema(definition.inputSchema, "input"),
|
|
640
|
+
outputSchema: compactToolSchema(outputSchema, "output")
|
|
641
|
+
}, async (args) => callOpenFairyGuiBackendTool(runtime, definition.name, args, policy));
|
|
600
642
|
}
|
|
601
|
-
server.server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: structuredClone(tools) }));
|
|
602
643
|
registerOpenFairyGuiBackendResources(server, runtime);
|
|
603
644
|
registerOpenFairyGuiBackendPrompts(server);
|
|
604
645
|
return server;
|
|
@@ -21,7 +21,6 @@ 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");
|
|
25
24
|
let _openfairygui_backend_node = require("@openfairygui/backend/node");
|
|
26
25
|
let node_module = require("node:module");
|
|
27
26
|
let zod = require("zod");
|
|
@@ -136,6 +135,17 @@ function contractObjectSchema(schema) {
|
|
|
136
135
|
if (!(result instanceof zod.z.ZodObject)) throw new TypeError("Tool contract must be an object");
|
|
137
136
|
return result;
|
|
138
137
|
}
|
|
138
|
+
/** Keep SDK discovery dynamic without expanding shared contract definitions. */
|
|
139
|
+
function compactToolSchema(schema, io) {
|
|
140
|
+
return zod.z.looseObject({}).superRefine((value, context) => {
|
|
141
|
+
const parsed = schema.safeParse(value);
|
|
142
|
+
if (!parsed.success) for (const issue of parsed.error.issues) context.addIssue({ ...issue });
|
|
143
|
+
}).meta(zod.z.toJSONSchema(schema, {
|
|
144
|
+
target: "draft-07",
|
|
145
|
+
io,
|
|
146
|
+
reused: "ref"
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
139
149
|
/** Decode only generated Uint8Array locations; arbitrary JSON metadata is not rewritten. */
|
|
140
150
|
function decodeToolBytes(input, paths) {
|
|
141
151
|
if (!paths.length) return input;
|
|
@@ -327,6 +337,30 @@ const OPENFAIRYGUI_BACKEND_TOOL_METADATA = [
|
|
|
327
337
|
openWorldHint: false
|
|
328
338
|
}
|
|
329
339
|
},
|
|
340
|
+
{
|
|
341
|
+
name: "openfairygui_backend_read_session_state",
|
|
342
|
+
backendMethod: "readSessionState",
|
|
343
|
+
title: "Read Session State",
|
|
344
|
+
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.",
|
|
345
|
+
maxResponseBytes: 16777216,
|
|
346
|
+
annotations: {
|
|
347
|
+
readOnlyHint: true,
|
|
348
|
+
idempotentHint: true,
|
|
349
|
+
openWorldHint: false
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
name: "openfairygui_backend_read_resource_bytes",
|
|
354
|
+
backendMethod: "readResourceBytes",
|
|
355
|
+
title: "Read Resource Bytes",
|
|
356
|
+
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.",
|
|
357
|
+
maxResponseBytes: 16777216,
|
|
358
|
+
annotations: {
|
|
359
|
+
readOnlyHint: true,
|
|
360
|
+
idempotentHint: true,
|
|
361
|
+
openWorldHint: false
|
|
362
|
+
}
|
|
363
|
+
},
|
|
330
364
|
{
|
|
331
365
|
name: "openfairygui_backend_validate_session",
|
|
332
366
|
backendMethod: "validateSession",
|
|
@@ -518,8 +552,8 @@ const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = OPENFAIRYGUI_BACKEND_TOOL_METADATA
|
|
|
518
552
|
});
|
|
519
553
|
//#endregion
|
|
520
554
|
//#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);
|
|
555
|
+
function jsonResult(payload, isError = false, compact = false) {
|
|
556
|
+
const text = JSON.stringify(payload, (_key, value) => value instanceof Uint8Array ? [...value] : value, compact ? void 0 : 2);
|
|
523
557
|
const wirePayload = JSON.parse(text);
|
|
524
558
|
return {
|
|
525
559
|
content: [{
|
|
@@ -551,16 +585,34 @@ function unhandledBackendFailure(startedAt) {
|
|
|
551
585
|
}
|
|
552
586
|
};
|
|
553
587
|
}
|
|
554
|
-
async function callOpenFairyGuiBackendTool(runtime, name, input) {
|
|
588
|
+
async function callOpenFairyGuiBackendTool(runtime, name, input, policy) {
|
|
555
589
|
if (!isOpenFairyGuiMcpPayloadWithinBudget(input)) throw new RangeError("MCP input exceeds the depth, node, key, string, or byte budget.");
|
|
556
590
|
const definition = OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS.find((entry) => entry.name === name);
|
|
557
591
|
if (!definition) throw new RangeError(`Unknown OpenFairyGUI backend MCP tool: ${name}`);
|
|
558
|
-
const
|
|
592
|
+
const parsed = definition.inputSchema.parse(input);
|
|
593
|
+
const decoded = decodeToolBytes(parsed, CONTRACT_SNAPSHOT.tools[definition.backendMethod].bytePaths);
|
|
559
594
|
const startedAt = Date.now();
|
|
560
595
|
try {
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
596
|
+
let hostFailure = await policy?.beforeCall(structuredClone(parsed));
|
|
597
|
+
if (hostFailure !== void 0) {
|
|
598
|
+
hostFailure = policy.failureSchema.parse(hostFailure);
|
|
599
|
+
if (!isBackendFailure(hostFailure)) throw new TypeError("Host policy must return a failure or undefined.");
|
|
600
|
+
}
|
|
601
|
+
const result = hostFailure ?? await Reflect.apply(runtime[definition.backendMethod], runtime, definition.backendMethod === "getCapabilities" ? [] : [decoded]);
|
|
602
|
+
let response = jsonResult(result, isBackendFailure(result), definition.maxResponseBytes !== void 0);
|
|
603
|
+
if (definition.maxResponseBytes !== void 0 && new TextEncoder().encode(JSON.stringify(response)).byteLength > definition.maxResponseBytes) {
|
|
604
|
+
response = jsonResult({
|
|
605
|
+
...unhandledBackendFailure(startedAt),
|
|
606
|
+
error: {
|
|
607
|
+
code: "mcp_response_budget_exceeded",
|
|
608
|
+
message: "The complete MCP tool response exceeds its byte limit.",
|
|
609
|
+
maxBytes: definition.maxResponseBytes
|
|
610
|
+
}
|
|
611
|
+
}, true);
|
|
612
|
+
hostFailure = void 0;
|
|
613
|
+
}
|
|
614
|
+
if (hostFailure === void 0) definition.outputSchema.parse(response.structuredContent);
|
|
615
|
+
else policy.failureSchema.parse(response.structuredContent?.backendResult);
|
|
564
616
|
return response;
|
|
565
617
|
} catch {
|
|
566
618
|
return jsonResult(unhandledBackendFailure(startedAt), true);
|
|
@@ -570,7 +622,7 @@ async function callOpenFairyGuiBackendTool(runtime, name, input) {
|
|
|
570
622
|
//#region src/server.ts
|
|
571
623
|
const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
572
624
|
function getInjectedPackageVersion() {
|
|
573
|
-
const version = "0.
|
|
625
|
+
const version = "0.5.0-alpha.2";
|
|
574
626
|
return typeof version === "string" && true ? version : null;
|
|
575
627
|
}
|
|
576
628
|
function readPackageVersion() {
|
|
@@ -584,13 +636,15 @@ function readPackageVersion() {
|
|
|
584
636
|
}
|
|
585
637
|
const PACKAGE_VERSION = readPackageVersion();
|
|
586
638
|
function createOpenFairyGuiMcpServer(options = {}) {
|
|
639
|
+
for (const name of Object.keys(options.toolPolicies ?? {})) if (!OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS.some((definition) => definition.name === name)) throw new RangeError(`Unknown OpenFairyGUI backend MCP tool policy: ${name}`);
|
|
587
640
|
const runtime = options.runtime ?? (0, _openfairygui_backend_node.createNodeBackendRuntime)({ allowedProjectRoots: options.allowedProjectRoots ?? [process.cwd()] });
|
|
588
641
|
const server = new _modelcontextprotocol_sdk_server_mcp_js.McpServer({
|
|
589
642
|
name: options.name ?? "openfairygui-mcp",
|
|
590
643
|
version: options.version ?? PACKAGE_VERSION
|
|
591
|
-
});
|
|
592
|
-
const tools = [];
|
|
644
|
+
}, { instructions: options.instructions });
|
|
593
645
|
for (const definition of OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS) {
|
|
646
|
+
const policy = options.toolPolicies?.[definition.name];
|
|
647
|
+
const outputSchema = policy ? definition.outputSchema.extend({ backendResult: zod.z.union([definition.outputSchema.shape.backendResult, policy.failureSchema]) }) : definition.outputSchema;
|
|
594
648
|
const metadata = {
|
|
595
649
|
name: definition.name,
|
|
596
650
|
title: definition.title,
|
|
@@ -599,29 +653,16 @@ function createOpenFairyGuiMcpServer(options = {}) {
|
|
|
599
653
|
_meta: {
|
|
600
654
|
"openfairygui/backendMethod": definition.backendMethod,
|
|
601
655
|
"openfairygui/adapter": "thin-backend-p2",
|
|
602
|
-
"openfairygui/contractDigest": CONTRACT_SNAPSHOT.digest
|
|
656
|
+
"openfairygui/contractDigest": CONTRACT_SNAPSHOT.digest,
|
|
657
|
+
...policy ? { "openfairygui/hostPolicy": true } : {}
|
|
603
658
|
}
|
|
604
659
|
};
|
|
605
660
|
server.registerTool(definition.name, {
|
|
606
661
|
...metadata,
|
|
607
|
-
inputSchema: definition.inputSchema,
|
|
608
|
-
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
|
-
}));
|
|
662
|
+
inputSchema: compactToolSchema(definition.inputSchema, "input"),
|
|
663
|
+
outputSchema: compactToolSchema(outputSchema, "output")
|
|
664
|
+
}, async (args) => callOpenFairyGuiBackendTool(runtime, definition.name, args, policy));
|
|
623
665
|
}
|
|
624
|
-
server.server.setRequestHandler(_modelcontextprotocol_sdk_types_js.ListToolsRequestSchema, () => ({ tools: structuredClone(tools) }));
|
|
625
666
|
registerOpenFairyGuiBackendResources(server, runtime);
|
|
626
667
|
registerOpenFairyGuiBackendPrompts(server);
|
|
627
668
|
return server;
|
package/dist/stdio.cjs
CHANGED
package/dist/stdio.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as connectOpenFairyGuiMcpStdio } from "./stdio
|
|
1
|
+
import { t as connectOpenFairyGuiMcpStdio } from "./stdio--4YykL4s.mjs";
|
|
2
2
|
export { connectOpenFairyGuiMcpStdio };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfairygui/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-alpha.2",
|
|
4
4
|
"description": "FairyGUI Headless Authoring SDK - MCP server adapter for the backend runtime.",
|
|
5
5
|
"author": "OpenFairyGUI Contributors",
|
|
6
6
|
"license": "MIT",
|
|
@@ -61,13 +61,13 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
63
63
|
"zod": "^4.3.6",
|
|
64
|
-
"@openfairygui/backend": "0.
|
|
64
|
+
"@openfairygui/backend": "0.5.0-alpha.2"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"ava": "^7.0.0",
|
|
68
68
|
"tsx": "^4.0.0",
|
|
69
|
-
"@openfairygui/
|
|
70
|
-
"@openfairygui/
|
|
69
|
+
"@openfairygui/test-utils": "0.3.0",
|
|
70
|
+
"@openfairygui/core": "0.5.0-alpha.2"
|
|
71
71
|
},
|
|
72
72
|
"ava": {
|
|
73
73
|
"extensions": {
|
package/src/contract-schema.ts
CHANGED
|
@@ -10,6 +10,16 @@ export function contractObjectSchema(schema: ContractSchema): z.ZodObject {
|
|
|
10
10
|
return result;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** Keep SDK discovery dynamic without expanding shared contract definitions. */
|
|
14
|
+
export function compactToolSchema(schema: z.ZodObject, io: 'input' | 'output'): z.ZodObject {
|
|
15
|
+
// Zod metadata supplies the wire schema; validation still delegates to the original schema.
|
|
16
|
+
// A separate object avoids Zod's cycle extraction overwriting the metadata's definitions.
|
|
17
|
+
return z.looseObject({}).superRefine((value, context) => {
|
|
18
|
+
const parsed = schema.safeParse(value);
|
|
19
|
+
if (!parsed.success) for (const issue of parsed.error.issues) context.addIssue({ ...issue });
|
|
20
|
+
}).meta(z.toJSONSchema(schema, { target: 'draft-07', io, reused: 'ref' }));
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
/** Decode only generated Uint8Array locations; arbitrary JSON metadata is not rewritten. */
|
|
14
24
|
export function decodeToolBytes(input: Record<string, unknown>, paths: string[][]): Record<string, unknown> {
|
|
15
25
|
if (!paths.length) return input;
|
package/src/index.ts
CHANGED
package/src/server.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { ListToolsRequestSchema, ToolSchema, type Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
2
|
import { createNodeBackendRuntime } from '@openfairygui/backend/node';
|
|
4
3
|
import { createRequire } from 'node:module';
|
|
5
4
|
import { z } from 'zod';
|
|
6
5
|
import { registerOpenFairyGuiBackendPrompts } from './prompt-definitions.js';
|
|
7
6
|
import { registerOpenFairyGuiBackendResources } from './resource-definitions.js';
|
|
8
|
-
import { CONTRACT_SNAPSHOT } from './contract-schema.js';
|
|
9
|
-
import { callOpenFairyGuiBackendTool, type OpenFairyGuiBackendRuntime } from './tool-handler.js';
|
|
7
|
+
import { compactToolSchema, CONTRACT_SNAPSHOT } from './contract-schema.js';
|
|
8
|
+
import { callOpenFairyGuiBackendTool, type OpenFairyGuiBackendRuntime, type OpenFairyGuiMcpToolPolicy } from './tool-handler.js';
|
|
10
9
|
import {
|
|
11
10
|
OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS,
|
|
12
11
|
type OpenFairyGuiBackendToolName,
|
|
@@ -42,19 +41,31 @@ export interface CreateOpenFairyGuiMcpServerOptions {
|
|
|
42
41
|
allowedProjectRoots?: readonly string[];
|
|
43
42
|
name?: string;
|
|
44
43
|
version?: string;
|
|
44
|
+
/** Host guidance returned by the SDK initialize handshake. */
|
|
45
|
+
instructions?: string;
|
|
46
|
+
/** Per-tool Host failures do not change the canonical Backend contracts. */
|
|
47
|
+
toolPolicies?: Partial<Record<OpenFairyGuiBackendToolName, OpenFairyGuiMcpToolPolicy>>;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
export function createOpenFairyGuiMcpServer(options: CreateOpenFairyGuiMcpServerOptions = {}): McpServer {
|
|
51
|
+
for (const name of Object.keys(options.toolPolicies ?? {})) {
|
|
52
|
+
if (!OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS.some((definition) => definition.name === name)) {
|
|
53
|
+
throw new RangeError(`Unknown OpenFairyGUI backend MCP tool policy: ${name}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
48
56
|
const runtime = options.runtime ?? createNodeBackendRuntime({
|
|
49
57
|
allowedProjectRoots: options.allowedProjectRoots ?? [process.cwd()],
|
|
50
58
|
});
|
|
51
59
|
const server = new McpServer({
|
|
52
60
|
name: options.name ?? 'openfairygui-mcp',
|
|
53
61
|
version: options.version ?? PACKAGE_VERSION,
|
|
54
|
-
});
|
|
62
|
+
}, { instructions: options.instructions });
|
|
55
63
|
|
|
56
|
-
const tools: Tool[] = [];
|
|
57
64
|
for (const definition of OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS) {
|
|
65
|
+
const policy = options.toolPolicies?.[definition.name];
|
|
66
|
+
const outputSchema = policy ? definition.outputSchema.extend({
|
|
67
|
+
backendResult: z.union([definition.outputSchema.shape.backendResult, policy.failureSchema]),
|
|
68
|
+
}) : definition.outputSchema;
|
|
58
69
|
const metadata = {
|
|
59
70
|
name: definition.name, title: definition.title, description: definition.description,
|
|
60
71
|
annotations: definition.annotations,
|
|
@@ -62,26 +73,19 @@ export function createOpenFairyGuiMcpServer(options: CreateOpenFairyGuiMcpServer
|
|
|
62
73
|
'openfairygui/backendMethod': definition.backendMethod,
|
|
63
74
|
'openfairygui/adapter': 'thin-backend-p2',
|
|
64
75
|
'openfairygui/contractDigest': CONTRACT_SNAPSHOT.digest,
|
|
76
|
+
...(policy ? { 'openfairygui/hostPolicy': true } : {}),
|
|
65
77
|
},
|
|
66
78
|
};
|
|
67
79
|
server.registerTool(
|
|
68
80
|
definition.name,
|
|
69
81
|
{
|
|
70
82
|
...metadata,
|
|
71
|
-
inputSchema: definition.inputSchema,
|
|
72
|
-
outputSchema:
|
|
83
|
+
inputSchema: compactToolSchema(definition.inputSchema, 'input'),
|
|
84
|
+
outputSchema: compactToolSchema(outputSchema, 'output'),
|
|
73
85
|
},
|
|
74
|
-
async (args: Record<string, unknown>) => callOpenFairyGuiBackendTool(runtime, definition.name
|
|
86
|
+
async (args: Record<string, unknown>) => callOpenFairyGuiBackendTool(runtime, definition.name, args, policy),
|
|
75
87
|
);
|
|
76
|
-
tools.push(ToolSchema.parse({
|
|
77
|
-
...metadata,
|
|
78
|
-
inputSchema: z.toJSONSchema(definition.inputSchema, { target: 'draft-07', io: 'input', reused: 'ref' }),
|
|
79
|
-
outputSchema: z.toJSONSchema(definition.outputSchema, { target: 'draft-07', io: 'output', reused: 'ref' }),
|
|
80
|
-
}));
|
|
81
88
|
}
|
|
82
|
-
// The installed Backend catalog is fixed. Reuse local definitions in discovery only;
|
|
83
|
-
// registered Zod schemas and the handler's structural/budget validation remain unchanged.
|
|
84
|
-
server.server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: structuredClone(tools) }));
|
|
85
89
|
|
|
86
90
|
registerOpenFairyGuiBackendResources(server, runtime);
|
|
87
91
|
registerOpenFairyGuiBackendPrompts(server);
|
package/src/tool-handler.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import type { z } from 'zod';
|
|
2
3
|
import {
|
|
3
4
|
BACKEND_CAPABILITY_SCHEMA_VERSION,
|
|
4
5
|
BACKEND_CONTRACT_VERSION,
|
|
@@ -12,12 +13,20 @@ import {
|
|
|
12
13
|
} from './tool-definitions.js';
|
|
13
14
|
|
|
14
15
|
import { decodeToolBytes, CONTRACT_SNAPSHOT } from './contract-schema.js';
|
|
15
|
-
import type { McpUnhandledFailure } from './tool-metadata.js';
|
|
16
|
+
import type { McpUnhandledFailure, McpResponseBudgetFailure } from './tool-metadata.js';
|
|
16
17
|
|
|
17
18
|
export type OpenFairyGuiBackendRuntime = Pick<BackendRuntime, BackendMethodName>;
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
/** Host policy runs after input validation, before the single Backend invocation. */
|
|
21
|
+
export interface OpenFairyGuiMcpToolPolicy {
|
|
22
|
+
/** Explicit Host-owned failure envelope, carried in structuredContent.backendResult. */
|
|
23
|
+
failureSchema: z.ZodType<{ ok: false }>;
|
|
24
|
+
/** Return a declared failure to stop, or undefined to call Backend with the original input. */
|
|
25
|
+
beforeCall(input: Readonly<Record<string, unknown>>): { ok: false } | undefined | Promise<{ ok: false } | undefined>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function jsonResult(payload: unknown, isError = false, compact = false): CallToolResult {
|
|
29
|
+
const text = JSON.stringify(payload, (_key, value) => value instanceof Uint8Array ? [...value] : value, compact ? undefined : 2);
|
|
21
30
|
const wirePayload = JSON.parse(text) as unknown;
|
|
22
31
|
return {
|
|
23
32
|
content: [
|
|
@@ -63,6 +72,7 @@ export async function callOpenFairyGuiBackendTool(
|
|
|
63
72
|
runtime: OpenFairyGuiBackendRuntime,
|
|
64
73
|
name: OpenFairyGuiBackendToolName,
|
|
65
74
|
input: Record<string, unknown>,
|
|
75
|
+
policy?: OpenFairyGuiMcpToolPolicy,
|
|
66
76
|
): Promise<CallToolResult> {
|
|
67
77
|
if (!isOpenFairyGuiMcpPayloadWithinBudget(input)) {
|
|
68
78
|
throw new RangeError('MCP input exceeds the depth, node, key, string, or byte budget.');
|
|
@@ -73,9 +83,22 @@ export async function callOpenFairyGuiBackendTool(
|
|
|
73
83
|
const decoded = decodeToolBytes(parsed, CONTRACT_SNAPSHOT.tools[definition.backendMethod].bytePaths);
|
|
74
84
|
const startedAt = Date.now();
|
|
75
85
|
try {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
86
|
+
let hostFailure = await policy?.beforeCall(structuredClone(parsed));
|
|
87
|
+
if (hostFailure !== undefined) {
|
|
88
|
+
hostFailure = policy!.failureSchema.parse(hostFailure);
|
|
89
|
+
if (!isBackendFailure(hostFailure)) throw new TypeError('Host policy must return a failure or undefined.');
|
|
90
|
+
}
|
|
91
|
+
const result = hostFailure ?? await Reflect.apply(runtime[definition.backendMethod], runtime, definition.backendMethod === 'getCapabilities' ? [] : [decoded]);
|
|
92
|
+
let response = jsonResult(result, isBackendFailure(result), definition.maxResponseBytes !== undefined);
|
|
93
|
+
if (definition.maxResponseBytes !== undefined && new TextEncoder().encode(JSON.stringify(response)).byteLength > definition.maxResponseBytes) {
|
|
94
|
+
response = jsonResult({
|
|
95
|
+
...unhandledBackendFailure(startedAt),
|
|
96
|
+
error: { code: 'mcp_response_budget_exceeded', message: 'The complete MCP tool response exceeds its byte limit.', maxBytes: definition.maxResponseBytes },
|
|
97
|
+
} satisfies McpResponseBudgetFailure, true);
|
|
98
|
+
hostFailure = undefined;
|
|
99
|
+
}
|
|
100
|
+
if (hostFailure === undefined) definition.outputSchema.parse(response.structuredContent);
|
|
101
|
+
else policy!.failureSchema.parse(response.structuredContent?.backendResult);
|
|
79
102
|
return response;
|
|
80
103
|
} catch {
|
|
81
104
|
return jsonResult(unhandledBackendFailure(startedAt), true);
|
package/src/tool-metadata.ts
CHANGED
|
@@ -5,6 +5,8 @@ export interface BackendToolMetadata {
|
|
|
5
5
|
backendMethod: BackendMethodName;
|
|
6
6
|
title: string;
|
|
7
7
|
description: string;
|
|
8
|
+
/** Bound the complete CallToolResult JSON; bounded reads use compact text JSON. */
|
|
9
|
+
maxResponseBytes?: number;
|
|
8
10
|
annotations: {
|
|
9
11
|
readOnlyHint?: boolean;
|
|
10
12
|
destructiveHint?: boolean;
|
|
@@ -20,6 +22,12 @@ export interface McpUnhandledFailure {
|
|
|
20
22
|
error: { code: 'backend_unhandled_error'; message: string };
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
export interface McpResponseBudgetFailure {
|
|
26
|
+
ok: false;
|
|
27
|
+
meta: BackendResponseMeta;
|
|
28
|
+
error: { code: 'mcp_response_budget_exceeded'; message: string; maxBytes: number };
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
/** Host objects cannot cross JSON; materialize keeps its existing MCP target boundary. */
|
|
24
32
|
export const MCP_OMITTED_INPUT_FIELDS = {
|
|
25
33
|
openProjectSession: ['storage'],
|
|
@@ -70,6 +78,22 @@ export const OPENFAIRYGUI_BACKEND_TOOL_METADATA = [
|
|
|
70
78
|
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.',
|
|
71
79
|
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
72
80
|
},
|
|
81
|
+
{
|
|
82
|
+
name: 'openfairygui_backend_read_session_state',
|
|
83
|
+
backendMethod: 'readSessionState',
|
|
84
|
+
title: 'Read Session State',
|
|
85
|
+
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.',
|
|
86
|
+
maxResponseBytes: 16777216,
|
|
87
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'openfairygui_backend_read_resource_bytes',
|
|
91
|
+
backendMethod: 'readResourceBytes',
|
|
92
|
+
title: 'Read Resource Bytes',
|
|
93
|
+
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.',
|
|
94
|
+
maxResponseBytes: 16777216,
|
|
95
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
|
|
96
|
+
},
|
|
73
97
|
{
|
|
74
98
|
name: 'openfairygui_backend_validate_session',
|
|
75
99
|
backendMethod: 'validateSession',
|