@openfairygui/mcp 0.2.0-alpha.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.
@@ -0,0 +1,106 @@
1
+ import type { GetPromptResult } from '@modelcontextprotocol/sdk/types.js';
2
+
3
+ export const OPENFAIRYGUI_BACKEND_PROMPT_NAMES = [
4
+ 'openfairygui_inspect_capabilities',
5
+ 'openfairygui_open_and_inspect_session',
6
+ 'openfairygui_plan_revision_checked_transaction',
7
+ 'openfairygui_save_session',
8
+ 'openfairygui_poll_runtime_state',
9
+ ] as const;
10
+
11
+ export type OpenFairyGuiBackendPromptName = typeof OPENFAIRYGUI_BACKEND_PROMPT_NAMES[number];
12
+
13
+ interface OpenFairyGuiBackendPromptDefinition {
14
+ name: OpenFairyGuiBackendPromptName;
15
+ title: string;
16
+ description: string;
17
+ text: string;
18
+ }
19
+
20
+ export const OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS = [
21
+ {
22
+ name: 'openfairygui_inspect_capabilities',
23
+ title: 'Inspect OpenFairyGUI Backend Capabilities',
24
+ description: 'Guide a client through the capability and version discovery tool.',
25
+ text: [
26
+ 'Use openfairygui_backend_get_capabilities first.',
27
+ 'Read contractVersion, capabilitySchemaVersion, capability planes, methods, and runtime non-goals from the backend envelope.',
28
+ 'Do not infer artifact publish/restore, subscriptions, persistent jobs, or cache source-of-truth support when the backend marks them unsupported.',
29
+ ].join('\n'),
30
+ },
31
+ {
32
+ name: 'openfairygui_open_and_inspect_session',
33
+ title: 'Open and Inspect an OpenFairyGUI Session',
34
+ description: 'Guide a client through opening and reading a backend session.',
35
+ text: [
36
+ 'Use openfairygui_backend_open_session with a projectPath, then use openfairygui_backend_get_session with the returned sessionId.',
37
+ 'Backend path policy remains authoritative for project paths and save targets; MCP roots are only client context in this package.',
38
+ 'Close the session with openfairygui_backend_close_session when finished.',
39
+ ].join('\n'),
40
+ },
41
+ {
42
+ name: 'openfairygui_plan_revision_checked_transaction',
43
+ title: 'Plan a Revision-Checked OpenFairyGUI Transaction',
44
+ description: 'Guide a client through backend-owned revision checks without inventing operation grammar.',
45
+ text: [
46
+ 'Use openfairygui_backend_get_session to read the current revision before mutation.',
47
+ 'Call openfairygui_backend_apply_transaction with sessionId, expectedRevision, and backend/UAM-owned operations.',
48
+ 'If the backend returns a stale revision error, refresh the session snapshot and re-plan against the new revision.',
49
+ 'Do not invent selector grammar, transaction grammar, or operation payload semantics at the MCP layer.',
50
+ ].join('\n'),
51
+ },
52
+ {
53
+ name: 'openfairygui_save_session',
54
+ title: 'Save an OpenFairyGUI Backend Session',
55
+ description: 'Guide a client through coordinated backend save semantics.',
56
+ text: [
57
+ 'Use openfairygui_backend_save_session with sessionId and expectedRevision when available.',
58
+ 'Backend path policy remains authoritative for targetPath; MCP does not canonicalize or authorize paths.',
59
+ 'Handle stale revision and partial save failure envelopes from the backend without rewriting their error semantics.',
60
+ ].join('\n'),
61
+ },
62
+ {
63
+ name: 'openfairygui_poll_runtime_state',
64
+ title: 'Poll OpenFairyGUI Runtime State',
65
+ description: 'Guide a client through event, job, and cache polling tools.',
66
+ text: [
67
+ 'Use openfairygui_backend_get_events for polling events with the backend cursor contract.',
68
+ 'Use openfairygui_backend_list_jobs and openfairygui_backend_get_job for in-memory job snapshots.',
69
+ 'Use openfairygui_backend_get_cache_snapshot and openfairygui_backend_refresh_cache for derived read-only cache state.',
70
+ 'Subscriptions, persistent jobs, artifact jobs, and cache-as-source-of-truth behavior are not supported by backend P2.',
71
+ ].join('\n'),
72
+ },
73
+ ] as const satisfies readonly OpenFairyGuiBackendPromptDefinition[];
74
+
75
+ function promptResult(text: string): GetPromptResult {
76
+ return {
77
+ messages: [
78
+ {
79
+ role: 'user',
80
+ content: {
81
+ type: 'text',
82
+ text,
83
+ },
84
+ },
85
+ ],
86
+ };
87
+ }
88
+
89
+ export function registerOpenFairyGuiBackendPrompts(server: {
90
+ registerPrompt: (
91
+ name: string,
92
+ config: { title?: string; description?: string },
93
+ callback: () => GetPromptResult,
94
+ ) => unknown;
95
+ }): void {
96
+ for (const definition of OPENFAIRYGUI_BACKEND_PROMPT_DEFINITIONS) {
97
+ server.registerPrompt(
98
+ definition.name,
99
+ {
100
+ title: definition.title,
101
+ description: definition.description,
102
+ },
103
+ () => promptResult(definition.text),
104
+ );
105
+ }
106
+ }
@@ -0,0 +1,82 @@
1
+ import { ResourceTemplate, type McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';
3
+ import type { BackendRuntime } from '@openfairygui/backend';
4
+
5
+ const JSON_MIME_TYPE = 'application/json';
6
+
7
+ function firstVariable(value: string | string[] | undefined): string {
8
+ return Array.isArray(value) ? value[0] ?? '' : value ?? '';
9
+ }
10
+
11
+ function jsonResource(uri: URL, backendResult: unknown): ReadResourceResult {
12
+ return {
13
+ contents: [
14
+ {
15
+ uri: uri.toString(),
16
+ mimeType: JSON_MIME_TYPE,
17
+ text: JSON.stringify(backendResult, null, 2),
18
+ },
19
+ ],
20
+ };
21
+ }
22
+
23
+ export const OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI = 'openfairygui://backend/capabilities';
24
+
25
+ export const OPENFAIRYGUI_BACKEND_RESOURCE_TEMPLATES = [
26
+ 'openfairygui://backend/session/{sessionId}',
27
+ 'openfairygui://backend/cache/{sessionId}',
28
+ 'openfairygui://backend/job/{sessionId}/{jobId}',
29
+ ] as const;
30
+
31
+ export function registerOpenFairyGuiBackendResources(server: McpServer, runtime: BackendRuntime): void {
32
+ server.registerResource(
33
+ 'openfairygui_backend_capabilities',
34
+ OPENFAIRYGUI_BACKEND_CAPABILITIES_RESOURCE_URI,
35
+ {
36
+ title: 'OpenFairyGUI Backend Capabilities',
37
+ description: 'Read the backend capability and version envelope as JSON.',
38
+ mimeType: JSON_MIME_TYPE,
39
+ },
40
+ (uri: URL) => jsonResource(uri, runtime.getCapabilities()),
41
+ );
42
+
43
+ server.registerResource(
44
+ 'openfairygui_backend_session',
45
+ new ResourceTemplate('openfairygui://backend/session/{sessionId}', { list: undefined }),
46
+ {
47
+ title: 'OpenFairyGUI Backend Session Snapshot',
48
+ description: 'Read a backend session envelope by backend-local session id.',
49
+ mimeType: JSON_MIME_TYPE,
50
+ },
51
+ (uri: URL, variables) => jsonResource(uri, runtime.getSession({
52
+ sessionId: firstVariable(variables.sessionId),
53
+ })),
54
+ );
55
+
56
+ server.registerResource(
57
+ 'openfairygui_backend_cache',
58
+ new ResourceTemplate('openfairygui://backend/cache/{sessionId}', { list: undefined }),
59
+ {
60
+ title: 'OpenFairyGUI Backend Cache Snapshot',
61
+ description: 'Read a derived backend cache envelope by backend-local session id.',
62
+ mimeType: JSON_MIME_TYPE,
63
+ },
64
+ (uri: URL, variables) => jsonResource(uri, runtime.getCacheSnapshot({
65
+ sessionId: firstVariable(variables.sessionId),
66
+ })),
67
+ );
68
+
69
+ server.registerResource(
70
+ 'openfairygui_backend_job',
71
+ new ResourceTemplate('openfairygui://backend/job/{sessionId}/{jobId}', { list: undefined }),
72
+ {
73
+ title: 'OpenFairyGUI Backend Job Snapshot',
74
+ description: 'Read a backend runtime job envelope by session id and job id.',
75
+ mimeType: JSON_MIME_TYPE,
76
+ },
77
+ (uri: URL, variables) => jsonResource(uri, runtime.getJob({
78
+ sessionId: firstVariable(variables.sessionId),
79
+ jobId: firstVariable(variables.jobId),
80
+ })),
81
+ );
82
+ }
package/src/server.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { BackendRuntime } from '@openfairygui/backend';
3
+ import { registerOpenFairyGuiBackendPrompts } from './prompt-definitions.js';
4
+ import { registerOpenFairyGuiBackendResources } from './resource-definitions.js';
5
+ import { callOpenFairyGuiBackendTool } from './tool-handler.js';
6
+ import {
7
+ OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS,
8
+ type OpenFairyGuiBackendToolName,
9
+ } from './tool-definitions.js';
10
+
11
+ const PACKAGE_VERSION = process.env.npm_package_version ?? '0.2.0-alpha.0';
12
+
13
+ export interface CreateOpenFairyGuiMcpServerOptions {
14
+ runtime?: BackendRuntime;
15
+ name?: string;
16
+ version?: string;
17
+ }
18
+
19
+ export function createOpenFairyGuiMcpServer(options: CreateOpenFairyGuiMcpServerOptions = {}): McpServer {
20
+ const runtime = options.runtime ?? new BackendRuntime();
21
+ const server = new McpServer({
22
+ name: options.name ?? 'openfairygui-mcp',
23
+ version: options.version ?? PACKAGE_VERSION,
24
+ });
25
+
26
+ for (const definition of OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS) {
27
+ server.registerTool(
28
+ definition.name,
29
+ {
30
+ title: definition.title,
31
+ description: definition.description,
32
+ inputSchema: definition.inputSchema,
33
+ outputSchema: definition.outputSchema,
34
+ annotations: definition.annotations,
35
+ _meta: {
36
+ 'openfairygui/backendMethod': definition.backendMethod,
37
+ 'openfairygui/adapter': 'thin-backend-p2',
38
+ },
39
+ },
40
+ async (args) => callOpenFairyGuiBackendTool(runtime, definition.name as OpenFairyGuiBackendToolName, args as Record<string, unknown>),
41
+ );
42
+ }
43
+
44
+ registerOpenFairyGuiBackendResources(server, runtime);
45
+ registerOpenFairyGuiBackendPrompts(server);
46
+
47
+ return server;
48
+ }
package/src/stdio.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
2
+ import { pathToFileURL } from 'node:url';
3
+ import { createOpenFairyGuiMcpServer } from './server.js';
4
+
5
+ export async function connectOpenFairyGuiMcpStdio(): Promise<void> {
6
+ const server = createOpenFairyGuiMcpServer();
7
+ await server.connect(new StdioServerTransport());
8
+ }
9
+
10
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
11
+ connectOpenFairyGuiMcpStdio().catch((error: unknown) => {
12
+ console.error(error instanceof Error ? error.stack ?? error.message : String(error));
13
+ process.exitCode = 1;
14
+ });
15
+ }
@@ -0,0 +1,196 @@
1
+ import { z } from 'zod';
2
+
3
+ export const OPENFAIRYGUI_BACKEND_TOOL_PREFIX = 'openfairygui_backend_';
4
+
5
+ export const OPENFAIRYGUI_BACKEND_TOOL_NAMES = [
6
+ 'openfairygui_backend_get_capabilities',
7
+ 'openfairygui_backend_open_session',
8
+ 'openfairygui_backend_get_session',
9
+ 'openfairygui_backend_apply_transaction',
10
+ 'openfairygui_backend_save_session',
11
+ 'openfairygui_backend_close_session',
12
+ 'openfairygui_backend_get_events',
13
+ 'openfairygui_backend_get_job',
14
+ 'openfairygui_backend_list_jobs',
15
+ 'openfairygui_backend_cancel_job',
16
+ 'openfairygui_backend_get_cache_snapshot',
17
+ 'openfairygui_backend_refresh_cache',
18
+ ] as const;
19
+
20
+ export type OpenFairyGuiBackendToolName = typeof OPENFAIRYGUI_BACKEND_TOOL_NAMES[number];
21
+
22
+ export type BackendMethodName =
23
+ | 'getCapabilities'
24
+ | 'openSession'
25
+ | 'getSession'
26
+ | 'applyTransaction'
27
+ | 'saveSession'
28
+ | 'closeSession'
29
+ | 'getEvents'
30
+ | 'getJob'
31
+ | 'listJobs'
32
+ | 'cancelJob'
33
+ | 'getCacheSnapshot'
34
+ | 'refreshCache';
35
+
36
+ export interface OpenFairyGuiBackendToolDefinition {
37
+ name: OpenFairyGuiBackendToolName;
38
+ backendMethod: BackendMethodName;
39
+ title: string;
40
+ description: string;
41
+ inputSchema: z.ZodObject;
42
+ outputSchema: z.ZodObject;
43
+ annotations: {
44
+ readOnlyHint?: boolean;
45
+ destructiveHint?: boolean;
46
+ idempotentHint?: boolean;
47
+ openWorldHint?: boolean;
48
+ };
49
+ }
50
+
51
+ const sessionId = z.string().min(1);
52
+ const jobId = z.string().min(1);
53
+ const expectedRevision = z.number().int().nonnegative();
54
+ const limit = z.number().int().nonnegative().optional();
55
+
56
+ export const OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA = z.object({
57
+ backendResult: z.object({
58
+ ok: z.boolean(),
59
+ data: z.unknown().optional(),
60
+ error: z.unknown().optional(),
61
+ meta: z.unknown().optional(),
62
+ }).passthrough(),
63
+ });
64
+
65
+ export const OPENFAIRYGUI_BACKEND_TOOL_DEFINITIONS = [
66
+ {
67
+ name: 'openfairygui_backend_get_capabilities',
68
+ backendMethod: 'getCapabilities',
69
+ title: 'Get Backend Capabilities',
70
+ description: 'Return the OpenFairyGUI backend capability, version, and service-plane snapshot.',
71
+ inputSchema: z.object({}),
72
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
73
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
74
+ },
75
+ {
76
+ name: 'openfairygui_backend_open_session',
77
+ backendMethod: 'openSession',
78
+ title: 'Open Backend Session',
79
+ description: 'Open a FairyGUI project through BackendRuntime and acquire its backend-local session lock.',
80
+ inputSchema: z.object({
81
+ projectPath: z.string().min(1),
82
+ }),
83
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
84
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false },
85
+ },
86
+ {
87
+ name: 'openfairygui_backend_get_session',
88
+ backendMethod: 'getSession',
89
+ title: 'Get Backend Session',
90
+ description: 'Return a backend session snapshot by session id.',
91
+ inputSchema: z.object({ sessionId }),
92
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
93
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
94
+ },
95
+ {
96
+ name: 'openfairygui_backend_apply_transaction',
97
+ backendMethod: 'applyTransaction',
98
+ title: 'Apply UAM Transaction',
99
+ description: 'Apply a backend revision-checked UAM operation batch without redefining selector or operation grammar.',
100
+ inputSchema: z.object({
101
+ sessionId,
102
+ expectedRevision,
103
+ operations: z.array(z.unknown()),
104
+ }),
105
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
106
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
107
+ },
108
+ {
109
+ name: 'openfairygui_backend_save_session',
110
+ backendMethod: 'saveSession',
111
+ title: 'Save Backend Session',
112
+ description: 'Write the current backend session back through the backend coordinated non-atomic save path.',
113
+ inputSchema: z.object({
114
+ sessionId,
115
+ expectedRevision: expectedRevision.optional(),
116
+ targetPath: z.string().min(1).optional(),
117
+ }),
118
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
119
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
120
+ },
121
+ {
122
+ name: 'openfairygui_backend_close_session',
123
+ backendMethod: 'closeSession',
124
+ title: 'Close Backend Session',
125
+ description: 'Close a backend session and release its backend-local advisory lock.',
126
+ inputSchema: z.object({ sessionId }),
127
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
128
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false },
129
+ },
130
+ {
131
+ name: 'openfairygui_backend_get_events',
132
+ backendMethod: 'getEvents',
133
+ title: 'Get Runtime Events',
134
+ description: 'Poll backend runtime events for a session using the backend P2 event cursor contract.',
135
+ inputSchema: z.object({
136
+ sessionId,
137
+ after: z.string().optional(),
138
+ limit,
139
+ }),
140
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
141
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
142
+ },
143
+ {
144
+ name: 'openfairygui_backend_get_job',
145
+ backendMethod: 'getJob',
146
+ title: 'Get Runtime Job',
147
+ description: 'Return a backend runtime job snapshot by session and backend-local job id.',
148
+ inputSchema: z.object({ sessionId, jobId }),
149
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
150
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
151
+ },
152
+ {
153
+ name: 'openfairygui_backend_list_jobs',
154
+ backendMethod: 'listJobs',
155
+ title: 'List Runtime Jobs',
156
+ description: 'List backend runtime jobs for a session with backend P2 status/kind filters.',
157
+ inputSchema: z.object({
158
+ sessionId,
159
+ status: z.enum(['queued', 'running', 'completed', 'failed', 'cancelled', 'active', 'terminal']).optional(),
160
+ kind: z.literal('cache.refresh').optional(),
161
+ limit,
162
+ }),
163
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
164
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
165
+ },
166
+ {
167
+ name: 'openfairygui_backend_cancel_job',
168
+ backendMethod: 'cancelJob',
169
+ title: 'Cancel Runtime Job',
170
+ description: 'Request cooperative cancellation for a backend runtime job.',
171
+ inputSchema: z.object({ sessionId, jobId }),
172
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
173
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false },
174
+ },
175
+ {
176
+ name: 'openfairygui_backend_get_cache_snapshot',
177
+ backendMethod: 'getCacheSnapshot',
178
+ title: 'Get Cache Snapshot',
179
+ description: 'Return the backend P2 derived read-only cache snapshot for a session.',
180
+ inputSchema: z.object({ sessionId }),
181
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
182
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
183
+ },
184
+ {
185
+ name: 'openfairygui_backend_refresh_cache',
186
+ backendMethod: 'refreshCache',
187
+ title: 'Refresh Cache',
188
+ description: 'Create a backend P2 cache.refresh job for the session cache snapshot.',
189
+ inputSchema: z.object({
190
+ sessionId,
191
+ reason: z.enum(['manual', 'session_open', 'after_save']).optional(),
192
+ }),
193
+ outputSchema: OPENFAIRYGUI_BACKEND_TOOL_OUTPUT_SCHEMA,
194
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false },
195
+ },
196
+ ] as const satisfies readonly OpenFairyGuiBackendToolDefinition[];
@@ -0,0 +1,110 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
2
+ import type { BackendRuntime } from '@openfairygui/backend';
3
+ import type { OpenFairyGuiBackendToolName } from './tool-definitions.js';
4
+
5
+ function jsonResult(payload: unknown, isError = false): CallToolResult {
6
+ return {
7
+ content: [
8
+ {
9
+ type: 'text',
10
+ text: JSON.stringify(payload, null, 2),
11
+ },
12
+ ],
13
+ structuredContent: {
14
+ backendResult: payload,
15
+ },
16
+ isError,
17
+ };
18
+ }
19
+
20
+ function isBackendFailure(value: unknown): boolean {
21
+ return typeof value === 'object'
22
+ && value !== null
23
+ && 'ok' in value
24
+ && (value as { ok?: unknown }).ok === false;
25
+ }
26
+
27
+ export async function callOpenFairyGuiBackendTool(
28
+ runtime: BackendRuntime,
29
+ name: OpenFairyGuiBackendToolName,
30
+ input: Record<string, unknown>,
31
+ ): Promise<CallToolResult> {
32
+ let result: unknown;
33
+ switch (name) {
34
+ case 'openfairygui_backend_get_capabilities':
35
+ result = runtime.getCapabilities();
36
+ break;
37
+ case 'openfairygui_backend_open_session':
38
+ result = await runtime.openSession({
39
+ projectPath: String(input.projectPath),
40
+ });
41
+ break;
42
+ case 'openfairygui_backend_get_session':
43
+ result = runtime.getSession({
44
+ sessionId: String(input.sessionId),
45
+ });
46
+ break;
47
+ case 'openfairygui_backend_apply_transaction':
48
+ result = await runtime.applyTransaction({
49
+ sessionId: String(input.sessionId),
50
+ expectedRevision: Number(input.expectedRevision),
51
+ operations: input.operations as Parameters<BackendRuntime['applyTransaction']>[0]['operations'],
52
+ });
53
+ break;
54
+ case 'openfairygui_backend_save_session':
55
+ result = await runtime.saveSession({
56
+ sessionId: String(input.sessionId),
57
+ expectedRevision: input.expectedRevision === undefined ? undefined : Number(input.expectedRevision),
58
+ targetPath: input.targetPath === undefined ? undefined : String(input.targetPath),
59
+ });
60
+ break;
61
+ case 'openfairygui_backend_close_session':
62
+ result = await runtime.closeSession({
63
+ sessionId: String(input.sessionId),
64
+ });
65
+ break;
66
+ case 'openfairygui_backend_get_events':
67
+ result = runtime.getEvents({
68
+ sessionId: String(input.sessionId),
69
+ after: input.after === undefined ? undefined : String(input.after),
70
+ limit: input.limit === undefined ? undefined : Number(input.limit),
71
+ });
72
+ break;
73
+ case 'openfairygui_backend_get_job':
74
+ result = runtime.getJob({
75
+ sessionId: String(input.sessionId),
76
+ jobId: String(input.jobId),
77
+ });
78
+ break;
79
+ case 'openfairygui_backend_list_jobs':
80
+ result = runtime.listJobs({
81
+ sessionId: String(input.sessionId),
82
+ status: input.status as Parameters<BackendRuntime['listJobs']>[0]['status'],
83
+ kind: input.kind as Parameters<BackendRuntime['listJobs']>[0]['kind'],
84
+ limit: input.limit === undefined ? undefined : Number(input.limit),
85
+ });
86
+ break;
87
+ case 'openfairygui_backend_cancel_job':
88
+ result = runtime.cancelJob({
89
+ sessionId: String(input.sessionId),
90
+ jobId: String(input.jobId),
91
+ });
92
+ break;
93
+ case 'openfairygui_backend_get_cache_snapshot':
94
+ result = runtime.getCacheSnapshot({
95
+ sessionId: String(input.sessionId),
96
+ });
97
+ break;
98
+ case 'openfairygui_backend_refresh_cache':
99
+ result = runtime.refreshCache({
100
+ sessionId: String(input.sessionId),
101
+ reason: input.reason as Parameters<BackendRuntime['refreshCache']>[0]['reason'],
102
+ });
103
+ break;
104
+ default: {
105
+ const exhaustive: never = name;
106
+ throw new Error(`Unknown OpenFairyGUI backend MCP tool: ${exhaustive}`);
107
+ }
108
+ }
109
+ return jsonResult(result, isBackendFailure(result));
110
+ }