@statelyai/mcp 0.1.0 → 0.1.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/LICENSE +10 -0
- package/README.md +409 -39
- package/package.json +7 -7
- package/dist/index.cjs +0 -483
- package/dist/index.d.cts +0 -72
- package/dist/index.d.mts +0 -72
- package/dist/index.mjs +0 -472
package/dist/index.mjs
DELETED
|
@@ -1,472 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
|
|
6
|
-
//#region src/server.ts
|
|
7
|
-
const readOnlyAnnotations = {
|
|
8
|
-
readOnlyHint: true,
|
|
9
|
-
destructiveHint: false,
|
|
10
|
-
idempotentHint: true,
|
|
11
|
-
openWorldHint: false
|
|
12
|
-
};
|
|
13
|
-
const apiFormats = [
|
|
14
|
-
"d2",
|
|
15
|
-
"scxml",
|
|
16
|
-
"xgraph",
|
|
17
|
-
"xstate"
|
|
18
|
-
];
|
|
19
|
-
const validationFormats = [
|
|
20
|
-
...apiFormats,
|
|
21
|
-
"json",
|
|
22
|
-
"yaml",
|
|
23
|
-
"mermaid"
|
|
24
|
-
];
|
|
25
|
-
const apiFormatSchema = z.enum(apiFormats);
|
|
26
|
-
const validationFormatSchema = z.enum(validationFormats);
|
|
27
|
-
const machineSchema = z.union([z.record(z.string(), z.unknown()), z.string()]);
|
|
28
|
-
const sourceSchema = z.object({
|
|
29
|
-
format: validationFormatSchema.optional(),
|
|
30
|
-
content: machineSchema
|
|
31
|
-
});
|
|
32
|
-
const patchSchema = z.object({
|
|
33
|
-
op: z.string(),
|
|
34
|
-
path: z.string(),
|
|
35
|
-
value: z.unknown().optional()
|
|
36
|
-
});
|
|
37
|
-
function generateTestCode(machineCode, paths) {
|
|
38
|
-
return [
|
|
39
|
-
[
|
|
40
|
-
"// Generated by generate_test_code (Stately MCP).",
|
|
41
|
-
"// Guards and actions are stubbed; assertions use state paths.",
|
|
42
|
-
"import { createActor } from 'xstate';",
|
|
43
|
-
"import { describe, expect, it } from 'vitest';",
|
|
44
|
-
"",
|
|
45
|
-
machineCode.trim(),
|
|
46
|
-
""
|
|
47
|
-
].join("\n"),
|
|
48
|
-
"describe('generated paths', () => {",
|
|
49
|
-
paths.map((path) => {
|
|
50
|
-
const events = path.steps.filter((step) => step.eventType !== null && step.eventType !== "" && !step.eventType.startsWith("xstate.")).map((step) => ` actor.send({ type: ${JSON.stringify(step.eventType)} });`);
|
|
51
|
-
return [
|
|
52
|
-
` it(${JSON.stringify(path.name)}, () => {`,
|
|
53
|
-
" const actor = createActor(machine).start();",
|
|
54
|
-
...events,
|
|
55
|
-
` expect(actor.getSnapshot().matches(${JSON.stringify(path.finalStatePath)})).toBe(true);`,
|
|
56
|
-
" });"
|
|
57
|
-
].join("\n");
|
|
58
|
-
}).join("\n\n"),
|
|
59
|
-
"});",
|
|
60
|
-
""
|
|
61
|
-
].join("\n");
|
|
62
|
-
}
|
|
63
|
-
function toolText$1(body) {
|
|
64
|
-
return JSON.stringify(body, null, 2);
|
|
65
|
-
}
|
|
66
|
-
function toolResult(body) {
|
|
67
|
-
return {
|
|
68
|
-
structuredContent: body,
|
|
69
|
-
content: [{
|
|
70
|
-
type: "text",
|
|
71
|
-
text: toolText$1(body)
|
|
72
|
-
}]
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
function toolError(body) {
|
|
76
|
-
return {
|
|
77
|
-
isError: true,
|
|
78
|
-
...toolResult(body)
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function normalizeApiBaseUrl(apiBaseUrl) {
|
|
82
|
-
return new URL(String(apiBaseUrl ?? "https://editor.stately.ai/api")).toString().replace(/\/$/, "");
|
|
83
|
-
}
|
|
84
|
-
function normalizeSource(input) {
|
|
85
|
-
const content = input.source?.content ?? input.machine;
|
|
86
|
-
if (content === void 0) throw new Error("Expected machine or source.content.");
|
|
87
|
-
return {
|
|
88
|
-
format: input.source?.format ?? input.format ?? (typeof content === "string" ? "xstate" : "json"),
|
|
89
|
-
content
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
function toConversionSource(source) {
|
|
93
|
-
if (!apiFormats.includes(source.format)) {
|
|
94
|
-
if (source.format === "json" && typeof source.content === "object") return {
|
|
95
|
-
format: "xstate",
|
|
96
|
-
content: `createMachine(${JSON.stringify(source.content, null, 2)})`
|
|
97
|
-
};
|
|
98
|
-
if (source.format === "json" && typeof source.content === "string") return {
|
|
99
|
-
format: "xstate",
|
|
100
|
-
content: source.content
|
|
101
|
-
};
|
|
102
|
-
throw new Error(`Format ${source.format} is only supported by validate_machine.`);
|
|
103
|
-
}
|
|
104
|
-
return source;
|
|
105
|
-
}
|
|
106
|
-
async function callApi(options, pathname, body) {
|
|
107
|
-
const configuredHeaders = typeof options.apiHeaders === "function" ? await options.apiHeaders() : options.apiHeaders;
|
|
108
|
-
const headers = new Headers(configuredHeaders);
|
|
109
|
-
const authMode = options.authMode ?? "auto";
|
|
110
|
-
if (authMode !== "none" && !headers.has("authorization")) {
|
|
111
|
-
const credential = await options.credentialProvider?.();
|
|
112
|
-
const token = credential?.type === "oauth" ? credential.accessToken : credential?.token;
|
|
113
|
-
if (token) headers.set("authorization", `Bearer ${token}`);
|
|
114
|
-
else if (authMode === "bearer") throw new Error("No Stately credential configured. Run `statelyai login` or set STATELY_ACCESS_TOKEN.");
|
|
115
|
-
}
|
|
116
|
-
const response = await fetch(`${normalizeApiBaseUrl(options.apiBaseUrl)}${pathname}`, {
|
|
117
|
-
method: "POST",
|
|
118
|
-
headers: {
|
|
119
|
-
"content-type": "application/json",
|
|
120
|
-
...Object.fromEntries(headers.entries())
|
|
121
|
-
},
|
|
122
|
-
body: JSON.stringify(body)
|
|
123
|
-
});
|
|
124
|
-
const result = await response.json().catch(() => ({ error: `Invalid JSON response from ${pathname}.` }));
|
|
125
|
-
if (!response.ok) throw new Error(typeof result.error === "string" ? result.error : `${pathname} failed with HTTP ${response.status}`);
|
|
126
|
-
return result;
|
|
127
|
-
}
|
|
128
|
-
function registerTool(server, name, config, handler) {
|
|
129
|
-
server.registerTool(name, {
|
|
130
|
-
...config,
|
|
131
|
-
annotations: readOnlyAnnotations
|
|
132
|
-
}, async (args) => {
|
|
133
|
-
try {
|
|
134
|
-
return toolResult(await handler(args));
|
|
135
|
-
} catch (error) {
|
|
136
|
-
return toolError({ error: error instanceof Error ? error.message : String(error) });
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
function registerStatelyMcpTools(server, options = {}) {
|
|
141
|
-
registerTool(server, "validate_machine", {
|
|
142
|
-
title: "Validate machine",
|
|
143
|
-
description: "Validate an XState machine config or source string through the Stately API.",
|
|
144
|
-
inputSchema: {
|
|
145
|
-
machine: machineSchema.optional(),
|
|
146
|
-
format: validationFormatSchema.optional(),
|
|
147
|
-
source: sourceSchema.optional()
|
|
148
|
-
}
|
|
149
|
-
}, (input) => callApi(options, "/v1/validate", { source: normalizeSource(input) }));
|
|
150
|
-
registerTool(server, "convert_machine", {
|
|
151
|
-
title: "Convert machine",
|
|
152
|
-
description: "Convert between XState, SCXML, XGraph, and D2 through the Stately API.",
|
|
153
|
-
inputSchema: {
|
|
154
|
-
machine: machineSchema.optional(),
|
|
155
|
-
format: apiFormatSchema.optional(),
|
|
156
|
-
source: sourceSchema.optional(),
|
|
157
|
-
to: apiFormatSchema
|
|
158
|
-
}
|
|
159
|
-
}, (input) => callApi(options, "/v1/convert", {
|
|
160
|
-
source: toConversionSource(normalizeSource(input)),
|
|
161
|
-
target: { format: input.to }
|
|
162
|
-
}));
|
|
163
|
-
registerTool(server, "diff_machines", {
|
|
164
|
-
title: "Diff machines",
|
|
165
|
-
description: "Compute a semantic diff between two machine documents through the Stately API.",
|
|
166
|
-
inputSchema: {
|
|
167
|
-
base: sourceSchema,
|
|
168
|
-
compare: sourceSchema,
|
|
169
|
-
includePatches: z.boolean().optional()
|
|
170
|
-
}
|
|
171
|
-
}, (input) => callApi(options, "/v1/diff", {
|
|
172
|
-
base: toConversionSource(normalizeSource({ source: input.base })),
|
|
173
|
-
compare: toConversionSource(normalizeSource({ source: input.compare })),
|
|
174
|
-
includePatches: input.includePatches
|
|
175
|
-
}));
|
|
176
|
-
registerTool(server, "apply_machine_patches", {
|
|
177
|
-
title: "Apply machine patches",
|
|
178
|
-
description: "Apply semantic machine patches through the Stately API.",
|
|
179
|
-
inputSchema: {
|
|
180
|
-
machine: machineSchema.optional(),
|
|
181
|
-
format: apiFormatSchema.optional(),
|
|
182
|
-
source: sourceSchema.optional(),
|
|
183
|
-
patches: z.array(patchSchema),
|
|
184
|
-
to: apiFormatSchema.optional()
|
|
185
|
-
}
|
|
186
|
-
}, (input) => callApi(options, "/v1/apply-patches", {
|
|
187
|
-
source: toConversionSource(normalizeSource(input)),
|
|
188
|
-
patches: input.patches,
|
|
189
|
-
target: input.to ? { format: input.to } : void 0
|
|
190
|
-
}));
|
|
191
|
-
registerTool(server, "generate_graph_paths", {
|
|
192
|
-
title: "Generate graph paths",
|
|
193
|
-
description: "Generate shortest or simple paths through the Stately API.",
|
|
194
|
-
inputSchema: {
|
|
195
|
-
machine: machineSchema.optional(),
|
|
196
|
-
format: apiFormatSchema.optional(),
|
|
197
|
-
source: sourceSchema.optional(),
|
|
198
|
-
graph: z.record(z.string(), z.unknown()).optional(),
|
|
199
|
-
kind: z.enum(["shortest", "simple"]).default("shortest"),
|
|
200
|
-
max: z.number().int().positive().optional()
|
|
201
|
-
}
|
|
202
|
-
}, async (input) => {
|
|
203
|
-
return callApi(options, "/v1/graph-paths", {
|
|
204
|
-
graph: input.graph ?? (await callApi(options, "/v1/convert", {
|
|
205
|
-
source: toConversionSource(normalizeSource(input)),
|
|
206
|
-
target: { format: "xgraph" }
|
|
207
|
-
})).content,
|
|
208
|
-
options: {
|
|
209
|
-
strategy: input.kind,
|
|
210
|
-
limit: Math.min(input.max ?? 5, options.anonymousGraphPathLimit ?? 5)
|
|
211
|
-
}
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
registerTool(server, "simulate_machine", {
|
|
215
|
-
title: "Simulate machine",
|
|
216
|
-
description: "Simulate an XState machine through a sequence of events; returns per-step state, context, and executed actions. Deterministic; guards are stubbed.",
|
|
217
|
-
inputSchema: {
|
|
218
|
-
machine: machineSchema.optional(),
|
|
219
|
-
format: apiFormatSchema.optional(),
|
|
220
|
-
source: sourceSchema.optional(),
|
|
221
|
-
events: z.array(z.object({ type: z.string() }).passthrough()).min(1).max(100)
|
|
222
|
-
}
|
|
223
|
-
}, (input) => callApi(options, "/v1/simulate", {
|
|
224
|
-
source: toConversionSource(normalizeSource(input)),
|
|
225
|
-
events: input.events
|
|
226
|
-
}));
|
|
227
|
-
registerTool(server, "visualize_machine", {
|
|
228
|
-
title: "Visualize machine",
|
|
229
|
-
description: "Render the machine as an SVG diagram (returns svg string + dimensions).",
|
|
230
|
-
inputSchema: {
|
|
231
|
-
machine: machineSchema.optional(),
|
|
232
|
-
format: apiFormatSchema.optional(),
|
|
233
|
-
source: sourceSchema.optional(),
|
|
234
|
-
direction: z.enum(["right", "down"]).optional()
|
|
235
|
-
}
|
|
236
|
-
}, (input) => callApi(options, "/v1/visualize", {
|
|
237
|
-
source: toConversionSource(normalizeSource(input)),
|
|
238
|
-
options: input.direction ? { direction: input.direction } : void 0
|
|
239
|
-
}));
|
|
240
|
-
registerTool(server, "analyze_machine", {
|
|
241
|
-
title: "Analyze machine",
|
|
242
|
-
description: "Deep analysis of machine logic (merges deterministic graph checks with AI review); may require an API key — configure via server options; for purely deterministic checks use validate_machine.",
|
|
243
|
-
inputSchema: {
|
|
244
|
-
machine: machineSchema.optional(),
|
|
245
|
-
format: apiFormatSchema.optional(),
|
|
246
|
-
source: sourceSchema.optional(),
|
|
247
|
-
intent: z.string().optional()
|
|
248
|
-
}
|
|
249
|
-
}, (input) => callApi(options, "/v1/analyze", {
|
|
250
|
-
source: toConversionSource(normalizeSource(input)),
|
|
251
|
-
intent: input.intent
|
|
252
|
-
}));
|
|
253
|
-
registerTool(server, "generate_test_code", {
|
|
254
|
-
title: "Generate test code",
|
|
255
|
-
description: "Generate a runnable vitest test file from the machine, one test per generated path. Guards and actions are stubbed; assertions use state paths.",
|
|
256
|
-
inputSchema: {
|
|
257
|
-
machine: machineSchema.optional(),
|
|
258
|
-
format: apiFormatSchema.optional(),
|
|
259
|
-
source: sourceSchema.optional(),
|
|
260
|
-
kind: z.enum(["shortest", "simple"]).default("shortest"),
|
|
261
|
-
max: z.number().int().positive().optional()
|
|
262
|
-
}
|
|
263
|
-
}, async (input) => {
|
|
264
|
-
const source = toConversionSource(normalizeSource(input));
|
|
265
|
-
const graph = (await callApi(options, "/v1/convert", {
|
|
266
|
-
source,
|
|
267
|
-
target: { format: "xgraph" }
|
|
268
|
-
})).content;
|
|
269
|
-
const pathsResponse = await callApi(options, "/v1/graph-paths", {
|
|
270
|
-
graph,
|
|
271
|
-
options: {
|
|
272
|
-
strategy: input.kind,
|
|
273
|
-
limit: Math.min(input.max ?? 5, options.anonymousGraphPathLimit ?? 5)
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
return {
|
|
277
|
-
code: generateTestCode(String((await callApi(options, "/v1/convert", {
|
|
278
|
-
source,
|
|
279
|
-
target: { format: "xstate" }
|
|
280
|
-
})).content), pathsResponse.paths),
|
|
281
|
-
paths: pathsResponse.paths,
|
|
282
|
-
coverage: pathsResponse.coverage
|
|
283
|
-
};
|
|
284
|
-
});
|
|
285
|
-
registerTool(server, "extract_machines_from_code", {
|
|
286
|
-
title: "Extract machines from code",
|
|
287
|
-
description: "Parse XState source code into XGraph through the Stately API.",
|
|
288
|
-
inputSchema: { code: z.string().min(1) }
|
|
289
|
-
}, async (input) => {
|
|
290
|
-
return { machines: [{
|
|
291
|
-
format: "xgraph",
|
|
292
|
-
graph: (await callApi(options, "/v1/convert", {
|
|
293
|
-
source: {
|
|
294
|
-
format: "xstate",
|
|
295
|
-
content: input.code
|
|
296
|
-
},
|
|
297
|
-
target: { format: "xgraph" }
|
|
298
|
-
})).content
|
|
299
|
-
}] };
|
|
300
|
-
});
|
|
301
|
-
return server;
|
|
302
|
-
}
|
|
303
|
-
function createStatelyMcpServer(options = {}) {
|
|
304
|
-
const server = new McpServer({
|
|
305
|
-
name: "stately",
|
|
306
|
-
version: "0.1.0"
|
|
307
|
-
});
|
|
308
|
-
registerStatelyMcpTools(server, options);
|
|
309
|
-
return server;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
//#endregion
|
|
313
|
-
//#region src/index.ts
|
|
314
|
-
async function loadNode() {
|
|
315
|
-
const [fs, os, path] = await Promise.all([
|
|
316
|
-
import("node:fs/promises"),
|
|
317
|
-
import("node:os"),
|
|
318
|
-
import("node:path")
|
|
319
|
-
]);
|
|
320
|
-
return {
|
|
321
|
-
fs,
|
|
322
|
-
os,
|
|
323
|
-
path
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
function workspaceFromArgv() {
|
|
327
|
-
const index = process.argv.indexOf("--workspace");
|
|
328
|
-
return index >= 0 ? process.argv[index + 1] : process.cwd();
|
|
329
|
-
}
|
|
330
|
-
function isCandidate(pathname) {
|
|
331
|
-
return /\.(?:[cm]?[jt]sx?|json|ya?ml|scxml|mmd|mermaid)$/i.test(pathname);
|
|
332
|
-
}
|
|
333
|
-
function formatForPath(pathname, text) {
|
|
334
|
-
if (/\.(?:mmd|mermaid)$/i.test(pathname)) return "mermaid";
|
|
335
|
-
if (/\.scxml$/i.test(pathname)) return "scxml";
|
|
336
|
-
if (/\.ya?ml$/i.test(pathname)) return "yaml";
|
|
337
|
-
if (/\.json$/i.test(pathname)) return "json";
|
|
338
|
-
if (/^\s*</.test(text)) return "scxml";
|
|
339
|
-
return "xstate";
|
|
340
|
-
}
|
|
341
|
-
async function walkWorkspace(root, glob) {
|
|
342
|
-
const { fs, path } = await loadNode();
|
|
343
|
-
const results = [];
|
|
344
|
-
async function walk(dir) {
|
|
345
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
346
|
-
for (const entry of entries) {
|
|
347
|
-
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist") continue;
|
|
348
|
-
const full = path.join(dir, entry.name);
|
|
349
|
-
if (entry.isDirectory()) await walk(full);
|
|
350
|
-
else if (isCandidate(full)) {
|
|
351
|
-
const relative = path.relative(root, full);
|
|
352
|
-
if (!glob || relative.includes(glob)) results.push(relative);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
await walk(root);
|
|
357
|
-
return results;
|
|
358
|
-
}
|
|
359
|
-
async function readWorkspaceMachine(input) {
|
|
360
|
-
const { fs, path } = await loadNode();
|
|
361
|
-
const root = workspaceFromArgv();
|
|
362
|
-
const fullPath = path.resolve(root, input.path);
|
|
363
|
-
const relative = path.relative(root, fullPath);
|
|
364
|
-
if (relative.startsWith("..") || path.isAbsolute(relative)) throw new Error("Path must stay inside workspace.");
|
|
365
|
-
const sourceText = await fs.readFile(fullPath, "utf8");
|
|
366
|
-
return {
|
|
367
|
-
machine: sourceText,
|
|
368
|
-
format: formatForPath(fullPath, sourceText),
|
|
369
|
-
source: { path: relative },
|
|
370
|
-
...input.includeSourceText ? { sourceText } : {}
|
|
371
|
-
};
|
|
372
|
-
}
|
|
373
|
-
async function readStoredCliCredential() {
|
|
374
|
-
const { fs, os, path } = await loadNode();
|
|
375
|
-
const configDir = process.env.STATELYAI_CONFIG_DIR ? path.resolve(process.env.STATELYAI_CONFIG_DIR) : process.platform === "darwin" ? path.join(os.homedir(), "Library", "Application Support", "statelyai") : process.platform === "win32" ? path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "statelyai") : path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "statelyai");
|
|
376
|
-
try {
|
|
377
|
-
const raw = await fs.readFile(path.join(configDir, "credentials.json"), "utf8");
|
|
378
|
-
const parsed = JSON.parse(raw);
|
|
379
|
-
if (typeof parsed.apiKey === "string" && parsed.apiKey) return {
|
|
380
|
-
type: "api_key",
|
|
381
|
-
token: parsed.apiKey
|
|
382
|
-
};
|
|
383
|
-
if (typeof parsed.credential === "object" && parsed.credential !== null && "type" in parsed.credential) {
|
|
384
|
-
const credential = parsed.credential;
|
|
385
|
-
if (credential.type === "api_key" && typeof credential.token === "string") return {
|
|
386
|
-
type: "api_key",
|
|
387
|
-
token: credential.token
|
|
388
|
-
};
|
|
389
|
-
if (credential.type === "oauth" && typeof credential.accessToken === "string") return {
|
|
390
|
-
type: "oauth",
|
|
391
|
-
accessToken: credential.accessToken,
|
|
392
|
-
...typeof credential.refreshToken === "string" ? { refreshToken: credential.refreshToken } : {},
|
|
393
|
-
...typeof credential.expiresAt === "number" ? { expiresAt: credential.expiresAt } : {}
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
} catch {
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
async function getDefaultCredential() {
|
|
401
|
-
if (process.env.STATELY_ACCESS_TOKEN) return {
|
|
402
|
-
type: "oauth",
|
|
403
|
-
accessToken: process.env.STATELY_ACCESS_TOKEN
|
|
404
|
-
};
|
|
405
|
-
if (process.env.STATELY_API_KEY) return {
|
|
406
|
-
type: "api_key",
|
|
407
|
-
token: process.env.STATELY_API_KEY
|
|
408
|
-
};
|
|
409
|
-
return readStoredCliCredential();
|
|
410
|
-
}
|
|
411
|
-
function toolText(body) {
|
|
412
|
-
return JSON.stringify(body, null, 2);
|
|
413
|
-
}
|
|
414
|
-
function createStatelyMcpServer$1(options = {}) {
|
|
415
|
-
const server = new McpServer({
|
|
416
|
-
name: "stately",
|
|
417
|
-
version: "0.1.0"
|
|
418
|
-
});
|
|
419
|
-
if (options.includeWorkspaceTools !== false) {
|
|
420
|
-
server.registerTool("list_machines", {
|
|
421
|
-
title: "list_machines",
|
|
422
|
-
description: "List candidate machine sources in the workspace.",
|
|
423
|
-
inputSchema: { glob: z.string().optional() },
|
|
424
|
-
annotations: readOnlyAnnotations
|
|
425
|
-
}, async (request) => {
|
|
426
|
-
const machines = (await walkWorkspace(workspaceFromArgv(), request.glob)).map((pathname) => ({
|
|
427
|
-
source: { path: pathname },
|
|
428
|
-
format: "xstate"
|
|
429
|
-
}));
|
|
430
|
-
return {
|
|
431
|
-
structuredContent: { machines },
|
|
432
|
-
content: [{
|
|
433
|
-
type: "text",
|
|
434
|
-
text: toolText({ machines })
|
|
435
|
-
}]
|
|
436
|
-
};
|
|
437
|
-
});
|
|
438
|
-
server.registerTool("get_machine", {
|
|
439
|
-
title: "get_machine",
|
|
440
|
-
description: "Read a machine source from the workspace and return it.",
|
|
441
|
-
inputSchema: {
|
|
442
|
-
path: z.string(),
|
|
443
|
-
includeSourceText: z.boolean().optional()
|
|
444
|
-
},
|
|
445
|
-
annotations: readOnlyAnnotations
|
|
446
|
-
}, async (request) => {
|
|
447
|
-
const result = await readWorkspaceMachine(request);
|
|
448
|
-
return {
|
|
449
|
-
structuredContent: result,
|
|
450
|
-
content: [{
|
|
451
|
-
type: "text",
|
|
452
|
-
text: toolText(result)
|
|
453
|
-
}]
|
|
454
|
-
};
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
registerStatelyMcpTools(server, {
|
|
458
|
-
...options,
|
|
459
|
-
authMode: options.authMode ?? (process.env.STATELY_AUTH_MODE === "none" ? "none" : void 0),
|
|
460
|
-
credentialProvider: options.credentialProvider ?? getDefaultCredential
|
|
461
|
-
});
|
|
462
|
-
return server;
|
|
463
|
-
}
|
|
464
|
-
async function main() {
|
|
465
|
-
const server = createStatelyMcpServer$1();
|
|
466
|
-
const transport = new StdioServerTransport();
|
|
467
|
-
await server.connect(transport);
|
|
468
|
-
}
|
|
469
|
-
if (import.meta.url === `file://${process.argv[1]}`) main();
|
|
470
|
-
|
|
471
|
-
//#endregion
|
|
472
|
-
export { createStatelyMcpServer as createHostedStatelyMcpServer, createStatelyMcpServer$1 as createStatelyMcpServer, formatForPath, generateTestCode, getDefaultCredential, isCandidate, readOnlyAnnotations, readWorkspaceMachine, registerStatelyMcpTools, walkWorkspace, workspaceFromArgv };
|