executable-stories-mcp 0.2.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.
- package/README.md +81 -0
- package/dist/http.cjs +313 -0
- package/dist/http.cjs.map +1 -0
- package/dist/http.d.cts +11 -0
- package/dist/http.d.ts +11 -0
- package/dist/http.js +282 -0
- package/dist/http.js.map +1 -0
- package/dist/index.cjs +254 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +94 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.js +209 -0
- package/dist/index.js.map +1 -0
- package/dist/server.js +332 -0
- package/dist/server.js.map +1 -0
- package/package.json +63 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/server.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
import * as fs from "fs";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
import { spawn } from "child_process";
|
|
12
|
+
import {
|
|
13
|
+
diffStoryReports,
|
|
14
|
+
scenariosCoveringPaths,
|
|
15
|
+
toBehaviorManifest,
|
|
16
|
+
toScenarioIndex
|
|
17
|
+
} from "executable-stories-formatters";
|
|
18
|
+
function loadStoryReport(reportPath) {
|
|
19
|
+
const absolutePath = path.resolve(reportPath);
|
|
20
|
+
const parsed = JSON.parse(fs.readFileSync(absolutePath, "utf8"));
|
|
21
|
+
assertStoryReport(parsed, absolutePath);
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
function listScenarios(report, filters) {
|
|
25
|
+
return toScenarioIndex(report, filters).scenarios;
|
|
26
|
+
}
|
|
27
|
+
function getFailingScenarios(report) {
|
|
28
|
+
return listScenarios(report, { statuses: ["failed"] });
|
|
29
|
+
}
|
|
30
|
+
function getScenariosForPaths(report, paths) {
|
|
31
|
+
return scenariosCoveringPaths(toScenarioIndex(report), paths);
|
|
32
|
+
}
|
|
33
|
+
function getBehaviorDiff(baseline, current) {
|
|
34
|
+
return diffStoryReports(baseline, current);
|
|
35
|
+
}
|
|
36
|
+
function getScenario(report, idOrTitle) {
|
|
37
|
+
for (const feature of report.features) {
|
|
38
|
+
const scenario = feature.scenarios.find(
|
|
39
|
+
(candidate) => candidate.id === idOrTitle || candidate.title === idOrTitle
|
|
40
|
+
);
|
|
41
|
+
if (scenario) return { feature, scenario };
|
|
42
|
+
}
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
function getFeatureSummary(report) {
|
|
46
|
+
return report.features.map((feature) => ({
|
|
47
|
+
id: feature.id,
|
|
48
|
+
title: feature.title,
|
|
49
|
+
sourceFile: feature.sourceFile,
|
|
50
|
+
total: feature.summary.total,
|
|
51
|
+
passed: feature.summary.passed,
|
|
52
|
+
failed: feature.summary.failed,
|
|
53
|
+
skipped: feature.summary.skipped,
|
|
54
|
+
pending: feature.summary.pending,
|
|
55
|
+
durationMs: feature.summary.durationMs
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
function resolveReportPath(reportPath) {
|
|
59
|
+
return path.resolve(reportPath ?? "reports/index.story-report.json");
|
|
60
|
+
}
|
|
61
|
+
function getScenarioIndex(report) {
|
|
62
|
+
return toScenarioIndex(report);
|
|
63
|
+
}
|
|
64
|
+
function getBehaviorManifest(report) {
|
|
65
|
+
return toBehaviorManifest(report);
|
|
66
|
+
}
|
|
67
|
+
var readOnlyTools = [
|
|
68
|
+
{
|
|
69
|
+
name: "get_failing_scenarios",
|
|
70
|
+
title: "Get failing scenarios",
|
|
71
|
+
description: "List failing executable story scenarios from StoryReport JSON.",
|
|
72
|
+
route: "/scenarios/failing",
|
|
73
|
+
run: getFailingScenarios
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "get_feature_summary",
|
|
77
|
+
title: "Get feature summary",
|
|
78
|
+
description: "Summarize features and scenario status counts from StoryReport JSON.",
|
|
79
|
+
route: "/features",
|
|
80
|
+
run: getFeatureSummary
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "get_scenario_index",
|
|
84
|
+
title: "Get scenario index",
|
|
85
|
+
description: "Return the Storybook-like scenario index artifact (schema v1) derived from StoryReport JSON.",
|
|
86
|
+
route: "/scenarios-index",
|
|
87
|
+
run: getScenarioIndex
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "get_behavior_manifest",
|
|
91
|
+
title: "Get behavior manifest",
|
|
92
|
+
description: "Return agent-oriented manifest metadata: source files, tags, doc coverage, debugger warnings.",
|
|
93
|
+
route: "/manifest",
|
|
94
|
+
run: getBehaviorManifest
|
|
95
|
+
}
|
|
96
|
+
];
|
|
97
|
+
var RUNNERS = {
|
|
98
|
+
vitest: {
|
|
99
|
+
framework: "vitest",
|
|
100
|
+
buildCommand: ({ sourceFile, scenarioTitle }) => ({
|
|
101
|
+
command: "pnpm",
|
|
102
|
+
args: ["exec", "vitest", "run", sourceFile, ...scenarioTitle ? ["-t", scenarioTitle] : []]
|
|
103
|
+
})
|
|
104
|
+
},
|
|
105
|
+
jest: {
|
|
106
|
+
framework: "jest",
|
|
107
|
+
buildCommand: ({ sourceFile, scenarioTitle }) => ({
|
|
108
|
+
command: "pnpm",
|
|
109
|
+
args: ["exec", "jest", sourceFile, ...scenarioTitle ? ["-t", scenarioTitle] : [], "--runInBand"]
|
|
110
|
+
})
|
|
111
|
+
},
|
|
112
|
+
playwright: {
|
|
113
|
+
framework: "playwright",
|
|
114
|
+
detect: (sourceFile) => sourceFile.includes(".story.spec."),
|
|
115
|
+
buildCommand: ({ sourceFile, scenarioTitle }) => ({
|
|
116
|
+
command: "pnpm",
|
|
117
|
+
args: ["exec", "playwright", "test", sourceFile, ...scenarioTitle ? ["-g", scenarioTitle] : []]
|
|
118
|
+
})
|
|
119
|
+
},
|
|
120
|
+
cypress: {
|
|
121
|
+
framework: "cypress",
|
|
122
|
+
detect: (sourceFile) => sourceFile.includes(".story.cy."),
|
|
123
|
+
buildCommand: ({ sourceFile }) => ({
|
|
124
|
+
command: "pnpm",
|
|
125
|
+
args: ["exec", "cypress", "run", "--spec", sourceFile]
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
function inferFrameworkFromSourceFile(sourceFile) {
|
|
130
|
+
for (const runner of Object.values(RUNNERS)) {
|
|
131
|
+
if (runner.detect?.(sourceFile)) return runner.framework;
|
|
132
|
+
}
|
|
133
|
+
return void 0;
|
|
134
|
+
}
|
|
135
|
+
function resolveFocusedRunFramework(args) {
|
|
136
|
+
if (args.framework) return args.framework;
|
|
137
|
+
const inferred = inferFrameworkFromSourceFile(args.sourceFile);
|
|
138
|
+
if (inferred) return inferred;
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Could not infer test framework from ${args.sourceFile}. Pass framework: vitest | jest | playwright | cypress.`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
function buildFocusedRunCommand(args) {
|
|
144
|
+
return RUNNERS[args.framework].buildCommand(args);
|
|
145
|
+
}
|
|
146
|
+
async function runFocusedScenario(args) {
|
|
147
|
+
const command = buildFocusedRunCommand(args);
|
|
148
|
+
const spawnFn = args.spawnFn ?? spawn;
|
|
149
|
+
return new Promise((resolve2) => {
|
|
150
|
+
const child = spawnFn(command.command, command.args, {
|
|
151
|
+
cwd: args.cwd,
|
|
152
|
+
env: process.env
|
|
153
|
+
});
|
|
154
|
+
let stdout = "";
|
|
155
|
+
let stderr = "";
|
|
156
|
+
child.stdout.on("data", (chunk) => {
|
|
157
|
+
stdout += String(chunk);
|
|
158
|
+
});
|
|
159
|
+
child.stderr.on("data", (chunk) => {
|
|
160
|
+
stderr += String(chunk);
|
|
161
|
+
});
|
|
162
|
+
child.on("error", (error) => {
|
|
163
|
+
resolve2({
|
|
164
|
+
ok: false,
|
|
165
|
+
exitCode: null,
|
|
166
|
+
command: command.command,
|
|
167
|
+
args: command.args,
|
|
168
|
+
stdout,
|
|
169
|
+
stderr: stderr + error.message
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
child.on("close", (exitCode) => {
|
|
173
|
+
resolve2({
|
|
174
|
+
ok: exitCode === 0,
|
|
175
|
+
exitCode,
|
|
176
|
+
command: command.command,
|
|
177
|
+
args: command.args,
|
|
178
|
+
stdout,
|
|
179
|
+
stderr
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function assertStoryReport(value, reportPath) {
|
|
185
|
+
if (!value || typeof value !== "object") {
|
|
186
|
+
throw new Error(`Invalid StoryReport JSON in ${reportPath}: expected object`);
|
|
187
|
+
}
|
|
188
|
+
const report = value;
|
|
189
|
+
if (typeof report.schemaVersion !== "string" || !report.schemaVersion.startsWith("1.")) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
`Invalid StoryReport JSON in ${reportPath}: expected schemaVersion 1.x`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
if (!Array.isArray(report.features)) {
|
|
195
|
+
throw new Error(`Invalid StoryReport JSON in ${reportPath}: expected features array`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/server.ts
|
|
200
|
+
var reportPathSchema = {
|
|
201
|
+
reportPath: z.string().optional().describe("Path to StoryReport JSON. Defaults to reports/index.story-report.json.")
|
|
202
|
+
};
|
|
203
|
+
var filterSchema = {
|
|
204
|
+
...reportPathSchema,
|
|
205
|
+
statuses: z.array(z.enum(["passed", "failed", "skipped", "pending"])).optional().describe("Filter by scenario status (any match)."),
|
|
206
|
+
tags: z.array(z.string()).optional().describe("Filter by tag (any match)."),
|
|
207
|
+
sourceFiles: z.array(z.string()).optional().describe("Filter by source file substring/glob (any match).")
|
|
208
|
+
};
|
|
209
|
+
var frameworkSchema = z.enum(["vitest", "jest", "playwright", "cypress"]).optional().describe("Host test framework. Inferred from source file when possible.");
|
|
210
|
+
var server = new McpServer({
|
|
211
|
+
name: "executable-stories",
|
|
212
|
+
version: "0.2.0"
|
|
213
|
+
});
|
|
214
|
+
for (const tool of readOnlyTools) {
|
|
215
|
+
server.registerTool(
|
|
216
|
+
tool.name,
|
|
217
|
+
{
|
|
218
|
+
title: tool.title,
|
|
219
|
+
description: tool.description,
|
|
220
|
+
inputSchema: reportPathSchema
|
|
221
|
+
},
|
|
222
|
+
async ({ reportPath }) => json(tool.run(loadStoryReport(resolveReportPath(reportPath))))
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
server.registerTool(
|
|
226
|
+
"list_scenarios",
|
|
227
|
+
{
|
|
228
|
+
title: "List scenarios",
|
|
229
|
+
description: "List executable story scenarios, optionally filtered by status, tag, or source file.",
|
|
230
|
+
inputSchema: filterSchema
|
|
231
|
+
},
|
|
232
|
+
async ({ reportPath, statuses, tags, sourceFiles }) => json(
|
|
233
|
+
listScenarios(loadStoryReport(resolveReportPath(reportPath)), {
|
|
234
|
+
statuses,
|
|
235
|
+
tags,
|
|
236
|
+
sourceFiles
|
|
237
|
+
})
|
|
238
|
+
)
|
|
239
|
+
);
|
|
240
|
+
server.registerTool(
|
|
241
|
+
"get_scenarios_for_paths",
|
|
242
|
+
{
|
|
243
|
+
title: "Get scenarios for paths",
|
|
244
|
+
description: "Find scenarios whose declared `covers` globs match the given product-code paths (e.g. a changed-file list).",
|
|
245
|
+
inputSchema: {
|
|
246
|
+
...reportPathSchema,
|
|
247
|
+
paths: z.array(z.string()).min(1).describe("Product-code paths or globs to look up.")
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
async ({ reportPath, paths }) => json(getScenariosForPaths(loadStoryReport(resolveReportPath(reportPath)), paths))
|
|
251
|
+
);
|
|
252
|
+
server.registerTool(
|
|
253
|
+
"get_behavior_diff",
|
|
254
|
+
{
|
|
255
|
+
title: "Get behavior diff",
|
|
256
|
+
description: "Compare two StoryReports by scenario id: regressed, fixed, added, removed, changed, unchanged.",
|
|
257
|
+
inputSchema: {
|
|
258
|
+
baselineReportPath: z.string().describe("Path to the baseline StoryReport JSON."),
|
|
259
|
+
currentReportPath: z.string().optional().describe("Path to the current StoryReport JSON. Defaults to the standard report path.")
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
async ({ baselineReportPath, currentReportPath }) => json(
|
|
263
|
+
getBehaviorDiff(
|
|
264
|
+
loadStoryReport(resolveReportPath(baselineReportPath)),
|
|
265
|
+
loadStoryReport(resolveReportPath(currentReportPath))
|
|
266
|
+
)
|
|
267
|
+
)
|
|
268
|
+
);
|
|
269
|
+
server.registerTool(
|
|
270
|
+
"get_scenario",
|
|
271
|
+
{
|
|
272
|
+
title: "Get scenario",
|
|
273
|
+
description: "Get one scenario by StoryReport scenario id or exact title.",
|
|
274
|
+
inputSchema: {
|
|
275
|
+
...reportPathSchema,
|
|
276
|
+
idOrTitle: z.string().describe("Scenario id or exact scenario title.")
|
|
277
|
+
}
|
|
278
|
+
},
|
|
279
|
+
async ({ reportPath, idOrTitle }) => {
|
|
280
|
+
const report = loadStoryReport(resolveReportPath(reportPath));
|
|
281
|
+
const lookup = getScenario(report, idOrTitle);
|
|
282
|
+
if (!lookup) {
|
|
283
|
+
return json({ error: `Scenario not found: ${idOrTitle}` });
|
|
284
|
+
}
|
|
285
|
+
return json(lookup);
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
server.registerTool(
|
|
289
|
+
"run_scenario",
|
|
290
|
+
{
|
|
291
|
+
title: "Run scenario",
|
|
292
|
+
description: "Run one scenario through the host test framework (vitest, jest, playwright, or cypress). Executes real tests.",
|
|
293
|
+
inputSchema: {
|
|
294
|
+
...reportPathSchema,
|
|
295
|
+
idOrTitle: z.string().describe("Scenario id or exact scenario title."),
|
|
296
|
+
framework: frameworkSchema,
|
|
297
|
+
cwd: z.string().optional().describe("Working directory for the test command. Defaults to process.cwd().")
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
async ({ reportPath, idOrTitle, framework, cwd }) => {
|
|
301
|
+
const report = loadStoryReport(resolveReportPath(reportPath));
|
|
302
|
+
const lookup = getScenario(report, idOrTitle);
|
|
303
|
+
if (!lookup) {
|
|
304
|
+
return json({ error: `Scenario not found: ${idOrTitle}` });
|
|
305
|
+
}
|
|
306
|
+
const sourceFile = lookup.feature.sourceFile;
|
|
307
|
+
const resolvedFramework = resolveFocusedRunFramework({ sourceFile, framework });
|
|
308
|
+
const result = await runFocusedScenario({
|
|
309
|
+
framework: resolvedFramework,
|
|
310
|
+
sourceFile,
|
|
311
|
+
scenarioTitle: lookup.scenario.title,
|
|
312
|
+
cwd: cwd ?? process.cwd()
|
|
313
|
+
});
|
|
314
|
+
return json({
|
|
315
|
+
scenario: {
|
|
316
|
+
id: lookup.scenario.id,
|
|
317
|
+
title: lookup.scenario.title,
|
|
318
|
+
sourceFile
|
|
319
|
+
},
|
|
320
|
+
framework: resolvedFramework,
|
|
321
|
+
...result
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
);
|
|
325
|
+
var transport = new StdioServerTransport();
|
|
326
|
+
await server.connect(transport);
|
|
327
|
+
function json(value) {
|
|
328
|
+
return {
|
|
329
|
+
content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/index.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\n\nimport {\n getBehaviorDiff,\n getScenario,\n getScenariosForPaths,\n listScenarios,\n loadStoryReport,\n readOnlyTools,\n resolveFocusedRunFramework,\n resolveReportPath,\n runFocusedScenario,\n} from \"./index.js\";\n\nconst reportPathSchema = {\n reportPath: z\n .string()\n .optional()\n .describe(\"Path to StoryReport JSON. Defaults to reports/index.story-report.json.\"),\n};\n\nconst filterSchema = {\n ...reportPathSchema,\n statuses: z\n .array(z.enum([\"passed\", \"failed\", \"skipped\", \"pending\"]))\n .optional()\n .describe(\"Filter by scenario status (any match).\"),\n tags: z.array(z.string()).optional().describe(\"Filter by tag (any match).\"),\n sourceFiles: z\n .array(z.string())\n .optional()\n .describe(\"Filter by source file substring/glob (any match).\"),\n};\n\nconst frameworkSchema = z\n .enum([\"vitest\", \"jest\", \"playwright\", \"cypress\"])\n .optional()\n .describe(\"Host test framework. Inferred from source file when possible.\");\n\nconst server = new McpServer({\n name: \"executable-stories\",\n version: \"0.2.0\",\n});\n\nfor (const tool of readOnlyTools) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: reportPathSchema,\n },\n async ({ reportPath }) =>\n json(tool.run(loadStoryReport(resolveReportPath(reportPath)))),\n );\n}\n\nserver.registerTool(\n \"list_scenarios\",\n {\n title: \"List scenarios\",\n description: \"List executable story scenarios, optionally filtered by status, tag, or source file.\",\n inputSchema: filterSchema,\n },\n async ({ reportPath, statuses, tags, sourceFiles }) =>\n json(\n listScenarios(loadStoryReport(resolveReportPath(reportPath)), {\n statuses,\n tags,\n sourceFiles,\n }),\n ),\n);\n\nserver.registerTool(\n \"get_scenarios_for_paths\",\n {\n title: \"Get scenarios for paths\",\n description:\n \"Find scenarios whose declared `covers` globs match the given product-code paths (e.g. a changed-file list).\",\n inputSchema: {\n ...reportPathSchema,\n paths: z.array(z.string()).min(1).describe(\"Product-code paths or globs to look up.\"),\n },\n },\n async ({ reportPath, paths }) =>\n json(getScenariosForPaths(loadStoryReport(resolveReportPath(reportPath)), paths)),\n);\n\nserver.registerTool(\n \"get_behavior_diff\",\n {\n title: \"Get behavior diff\",\n description:\n \"Compare two StoryReports by scenario id: regressed, fixed, added, removed, changed, unchanged.\",\n inputSchema: {\n baselineReportPath: z.string().describe(\"Path to the baseline StoryReport JSON.\"),\n currentReportPath: z\n .string()\n .optional()\n .describe(\"Path to the current StoryReport JSON. Defaults to the standard report path.\"),\n },\n },\n async ({ baselineReportPath, currentReportPath }) =>\n json(\n getBehaviorDiff(\n loadStoryReport(resolveReportPath(baselineReportPath)),\n loadStoryReport(resolveReportPath(currentReportPath)),\n ),\n ),\n);\n\nserver.registerTool(\n \"get_scenario\",\n {\n title: \"Get scenario\",\n description: \"Get one scenario by StoryReport scenario id or exact title.\",\n inputSchema: {\n ...reportPathSchema,\n idOrTitle: z.string().describe(\"Scenario id or exact scenario title.\"),\n },\n },\n async ({ reportPath, idOrTitle }) => {\n const report = loadStoryReport(resolveReportPath(reportPath));\n const lookup = getScenario(report, idOrTitle);\n if (!lookup) {\n return json({ error: `Scenario not found: ${idOrTitle}` });\n }\n return json(lookup);\n },\n);\n\nserver.registerTool(\n \"run_scenario\",\n {\n title: \"Run scenario\",\n description:\n \"Run one scenario through the host test framework (vitest, jest, playwright, or cypress). Executes real tests.\",\n inputSchema: {\n ...reportPathSchema,\n idOrTitle: z.string().describe(\"Scenario id or exact scenario title.\"),\n framework: frameworkSchema,\n cwd: z\n .string()\n .optional()\n .describe(\"Working directory for the test command. Defaults to process.cwd().\"),\n },\n },\n async ({ reportPath, idOrTitle, framework, cwd }) => {\n const report = loadStoryReport(resolveReportPath(reportPath));\n const lookup = getScenario(report, idOrTitle);\n if (!lookup) {\n return json({ error: `Scenario not found: ${idOrTitle}` });\n }\n\n const sourceFile = lookup.feature.sourceFile;\n const resolvedFramework = resolveFocusedRunFramework({ sourceFile, framework });\n const result = await runFocusedScenario({\n framework: resolvedFramework,\n sourceFile,\n scenarioTitle: lookup.scenario.title,\n cwd: cwd ?? process.cwd(),\n });\n\n return json({\n scenario: {\n id: lookup.scenario.id,\n title: lookup.scenario.title,\n sourceFile,\n },\n framework: resolvedFramework,\n ...result,\n });\n },\n);\n\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n\nfunction json(value: unknown): {\n content: Array<{ type: \"text\"; text: string }>;\n} {\n return {\n content: [{ type: \"text\", text: JSON.stringify(value, null, 2) }],\n };\n}\n","import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport { spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\n\nimport type {\n ReportFeature,\n ReportScenario,\n StoryReport,\n BehaviorManifest,\n BehaviorDiff,\n ScenarioIndex,\n ScenarioIndexFilters,\n ScenarioIndexItem,\n} from \"executable-stories-formatters\";\nimport {\n diffStoryReports,\n scenariosCoveringPaths,\n toBehaviorManifest,\n toScenarioIndex,\n} from \"executable-stories-formatters\";\n\n// Scenario serialization is owned by the formatters package; re-export so MCP\n// consumers get the same shape without a parallel definition to maintain.\nexport type { ScenarioIndexItem, ScenarioIndexFilters };\n\nexport interface FeatureSummaryItem {\n id: string;\n title: string;\n sourceFile: string;\n total: number;\n passed: number;\n failed: number;\n skipped: number;\n pending: number;\n durationMs: number;\n}\n\nexport interface ScenarioLookup {\n feature: ReportFeature;\n scenario: ReportScenario;\n}\n\nexport function loadStoryReport(reportPath: string): StoryReport {\n const absolutePath = path.resolve(reportPath);\n const parsed: unknown = JSON.parse(fs.readFileSync(absolutePath, \"utf8\"));\n assertStoryReport(parsed, absolutePath);\n return parsed;\n}\n\nexport function listScenarios(\n report: StoryReport,\n filters?: ScenarioIndexFilters,\n): ScenarioIndexItem[] {\n return toScenarioIndex(report, filters).scenarios;\n}\n\nexport function getFailingScenarios(report: StoryReport): ScenarioIndexItem[] {\n return listScenarios(report, { statuses: [\"failed\"] });\n}\n\nexport function getScenariosForPaths(report: StoryReport, paths: string[]): ScenarioIndexItem[] {\n return scenariosCoveringPaths(toScenarioIndex(report), paths);\n}\n\nexport function getBehaviorDiff(baseline: StoryReport, current: StoryReport): BehaviorDiff {\n return diffStoryReports(baseline, current);\n}\n\nexport function getScenario(report: StoryReport, idOrTitle: string): ScenarioLookup | undefined {\n for (const feature of report.features) {\n const scenario = feature.scenarios.find(\n (candidate) => candidate.id === idOrTitle || candidate.title === idOrTitle,\n );\n if (scenario) return { feature, scenario };\n }\n return undefined;\n}\n\nexport function getFeatureSummary(report: StoryReport): FeatureSummaryItem[] {\n return report.features.map((feature) => ({\n id: feature.id,\n title: feature.title,\n sourceFile: feature.sourceFile,\n total: feature.summary.total,\n passed: feature.summary.passed,\n failed: feature.summary.failed,\n skipped: feature.summary.skipped,\n pending: feature.summary.pending,\n durationMs: feature.summary.durationMs,\n }));\n}\n\nexport function resolveReportPath(reportPath?: string): string {\n return path.resolve(reportPath ?? \"reports/index.story-report.json\");\n}\n\nexport function getScenarioIndex(report: StoryReport): ScenarioIndex {\n return toScenarioIndex(report);\n}\n\nexport function getBehaviorManifest(report: StoryReport): BehaviorManifest {\n return toBehaviorManifest(report);\n}\n\n/**\n * Single source of truth for the read-only tools, consumed by both the stdio\n * MCP server and the HTTP server so the two transports cannot drift apart.\n * Tools needing extra arguments (get_scenario, run_scenario) are wired up\n * directly in each transport.\n */\nexport interface ReadOnlyTool {\n /** MCP tool name. */\n name: string;\n /** Human-readable MCP tool title. */\n title: string;\n /** Shared description used by both transports. */\n description: string;\n /** HTTP route that exposes the same data. */\n route: string;\n /** Pure projection from a loaded report to its JSON payload. */\n run: (report: StoryReport) => unknown;\n}\n\nexport const readOnlyTools: ReadOnlyTool[] = [\n {\n name: \"get_failing_scenarios\",\n title: \"Get failing scenarios\",\n description: \"List failing executable story scenarios from StoryReport JSON.\",\n route: \"/scenarios/failing\",\n run: getFailingScenarios,\n },\n {\n name: \"get_feature_summary\",\n title: \"Get feature summary\",\n description: \"Summarize features and scenario status counts from StoryReport JSON.\",\n route: \"/features\",\n run: getFeatureSummary,\n },\n {\n name: \"get_scenario_index\",\n title: \"Get scenario index\",\n description:\n \"Return the Storybook-like scenario index artifact (schema v1) derived from StoryReport JSON.\",\n route: \"/scenarios-index\",\n run: getScenarioIndex,\n },\n {\n name: \"get_behavior_manifest\",\n title: \"Get behavior manifest\",\n description:\n \"Return agent-oriented manifest metadata: source files, tags, doc coverage, debugger warnings.\",\n route: \"/manifest\",\n run: getBehaviorManifest,\n },\n];\n\nexport type FocusedRunFramework = \"vitest\" | \"jest\" | \"playwright\" | \"cypress\";\n\nexport interface FocusedRunCommandArgs {\n framework: FocusedRunFramework;\n sourceFile: string;\n scenarioTitle?: string;\n}\n\nexport interface FocusedRunCommand {\n command: string;\n args: string[];\n}\n\nexport interface FocusedRunResult {\n ok: boolean;\n exitCode: number | null;\n command: string;\n args: string[];\n stdout: string;\n stderr: string;\n}\n\n/**\n * One runner per host framework. The seam that keeps `run_scenario` extensible:\n * adding a non-JS framework (go, cargo, pytest, dotnet) later is a single new\n * entry here — no changes to inference, command building, or the transports.\n */\nexport interface RunnerDefinition {\n framework: FocusedRunFramework;\n /** Infer this framework from a source-file path, when unambiguous. */\n detect?: (sourceFile: string) => boolean;\n /** Build the focused-run command for this framework. */\n buildCommand: (args: { sourceFile: string; scenarioTitle?: string }) => FocusedRunCommand;\n}\n\nexport const RUNNERS: Record<FocusedRunFramework, RunnerDefinition> = {\n vitest: {\n framework: \"vitest\",\n buildCommand: ({ sourceFile, scenarioTitle }) => ({\n command: \"pnpm\",\n args: [\"exec\", \"vitest\", \"run\", sourceFile, ...(scenarioTitle ? [\"-t\", scenarioTitle] : [])],\n }),\n },\n jest: {\n framework: \"jest\",\n buildCommand: ({ sourceFile, scenarioTitle }) => ({\n command: \"pnpm\",\n args: [\"exec\", \"jest\", sourceFile, ...(scenarioTitle ? [\"-t\", scenarioTitle] : []), \"--runInBand\"],\n }),\n },\n playwright: {\n framework: \"playwright\",\n detect: (sourceFile) => sourceFile.includes(\".story.spec.\"),\n buildCommand: ({ sourceFile, scenarioTitle }) => ({\n command: \"pnpm\",\n args: [\"exec\", \"playwright\", \"test\", sourceFile, ...(scenarioTitle ? [\"-g\", scenarioTitle] : [])],\n }),\n },\n cypress: {\n framework: \"cypress\",\n detect: (sourceFile) => sourceFile.includes(\".story.cy.\"),\n buildCommand: ({ sourceFile }) => ({\n command: \"pnpm\",\n args: [\"exec\", \"cypress\", \"run\", \"--spec\", sourceFile],\n }),\n },\n};\n\nexport function inferFrameworkFromSourceFile(\n sourceFile: string,\n): FocusedRunFramework | undefined {\n for (const runner of Object.values(RUNNERS)) {\n if (runner.detect?.(sourceFile)) return runner.framework;\n }\n return undefined;\n}\n\nexport function resolveFocusedRunFramework(args: {\n sourceFile: string;\n framework?: FocusedRunFramework;\n}): FocusedRunFramework {\n if (args.framework) return args.framework;\n const inferred = inferFrameworkFromSourceFile(args.sourceFile);\n if (inferred) return inferred;\n throw new Error(\n `Could not infer test framework from ${args.sourceFile}. Pass framework: vitest | jest | playwright | cypress.`,\n );\n}\n\nexport function buildFocusedRunCommand(args: FocusedRunCommandArgs): FocusedRunCommand {\n return RUNNERS[args.framework].buildCommand(args);\n}\n\nexport async function runFocusedScenario(args: FocusedRunCommandArgs & {\n cwd?: string;\n spawnFn?: typeof spawn;\n}): Promise<FocusedRunResult> {\n const command = buildFocusedRunCommand(args);\n const spawnFn = args.spawnFn ?? spawn;\n\n return new Promise((resolve) => {\n const child = spawnFn(command.command, command.args, {\n cwd: args.cwd,\n env: process.env,\n }) as ChildProcessWithoutNullStreams;\n let stdout = \"\";\n let stderr = \"\";\n\n child.stdout.on(\"data\", (chunk) => {\n stdout += String(chunk);\n });\n child.stderr.on(\"data\", (chunk) => {\n stderr += String(chunk);\n });\n child.on(\"error\", (error) => {\n resolve({\n ok: false,\n exitCode: null,\n command: command.command,\n args: command.args,\n stdout,\n stderr: stderr + error.message,\n });\n });\n child.on(\"close\", (exitCode) => {\n resolve({\n ok: exitCode === 0,\n exitCode,\n command: command.command,\n args: command.args,\n stdout,\n stderr,\n });\n });\n });\n}\n\nfunction assertStoryReport(\n value: unknown,\n reportPath: string,\n): asserts value is StoryReport {\n if (!value || typeof value !== \"object\") {\n throw new Error(`Invalid StoryReport JSON in ${reportPath}: expected object`);\n }\n const report = value as Partial<StoryReport>;\n if (typeof report.schemaVersion !== \"string\" || !report.schemaVersion.startsWith(\"1.\")) {\n throw new Error(\n `Invalid StoryReport JSON in ${reportPath}: expected schemaVersion 1.x`,\n );\n }\n if (!Array.isArray(report.features)) {\n throw new Error(`Invalid StoryReport JSON in ${reportPath}: expected features array`);\n }\n}\n"],"mappings":"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","names":["resolve"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "executable-stories-mcp",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Read-only MCP server for executable-stories StoryReport JSON behavior catalogs.",
|
|
5
|
+
"author": "Jag Reehal <jag@jagreehal.com>",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"require": "./dist/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./http": {
|
|
19
|
+
"types": "./dist/http.d.ts",
|
|
20
|
+
"import": "./dist/http.js",
|
|
21
|
+
"require": "./dist/http.cjs"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"executable-stories-mcp": "./dist/server.js"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=22"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"lint": "eslint .",
|
|
37
|
+
"type-check": "tsc --noEmit",
|
|
38
|
+
"test": "vitest run"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
|
+
"executable-stories-formatters": "workspace:*",
|
|
43
|
+
"zod": "^4.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^25.6.0",
|
|
47
|
+
"tsup": "^8.5.1",
|
|
48
|
+
"typescript": "^6.0.3",
|
|
49
|
+
"vitest": "^4.1.5"
|
|
50
|
+
},
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "git+https://github.com/jagreehal/executable-stories.git",
|
|
54
|
+
"directory": "packages/executable-stories-mcp"
|
|
55
|
+
},
|
|
56
|
+
"keywords": [
|
|
57
|
+
"executable-stories",
|
|
58
|
+
"mcp",
|
|
59
|
+
"ai",
|
|
60
|
+
"bdd",
|
|
61
|
+
"living-documentation"
|
|
62
|
+
]
|
|
63
|
+
}
|