webhook-crash-lab-mcp 0.1.0 → 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.
@@ -12,7 +12,7 @@ business state (for example "credits granted exactly once").
12
12
  ## Loop
13
13
 
14
14
  1. **Find the handler** (`/webhook` route, `app/api/webhooks/*/route.ts`, `constructEvent` call).
15
- 2. **Config.** If `crash-lab.config.mjs` does not exist, run `npx webhook-crash-lab init` and fill it in:
15
+ 2. **Config.** If no `crash-lab.config.{mjs,js,ts}` exists, run `npx webhook-crash-lab init` and fill it in:
16
16
  - `target`: the local dev URL of the webhook route, or an in-process `(req: Request) => Response` handler.
17
17
  - `secret`: a **test** signing secret the handler verifies with (set it in the handler's env too).
18
18
  - `event`: a realistic event (for example `checkout.session.completed`).
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webhook-crash-lab",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Test webhook handlers against duplicate, concurrent, out-of-order and forged deliveries, then fix and re-run until they pass.",
5
5
  "author": { "name": "Krrish Kumar", "url": "https://github.com/Krrishkumar125" },
6
6
  "homepage": "https://github.com/Krrishkumar125/webhook-crash-lab",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 (2026-10-22)
4
+
5
+ - `run_crash_lab` accepts TypeScript configs (`.ts`/`.mts`/`.cts`) by delegating config loading to
6
+ webhook-crash-lab 0.3.0; jiti is bundled so this works on Node 20 too.
7
+
3
8
  ## 0.1.0 (2026-10-22)
4
9
 
5
10
  - MCP server with `run_crash_lab`, `run_demo`, `explain_report`, `list_scenarios`.
package/README.md CHANGED
@@ -59,7 +59,7 @@ Paste [`docs/AGENTS-snippet.md`](docs/AGENTS-snippet.md) into `AGENTS.md` / `CLA
59
59
 
60
60
  ## Safety
61
61
 
62
- `run_crash_lab` imports your `crash-lab.config.mjs` (it is code, like any config file) and sends requests to
62
+ `run_crash_lab` imports your crash-lab config (`.mjs`/`.js`/`.cjs`, or `.ts`/`.mts`/`.cts` via the bundled jiti) (it is code, like any config file) and sends requests to
63
63
  its `target`. Point configs at a local server and a **test** database and secret only. The skill tells
64
64
  agents never to use production URLs or live secrets.
65
65
 
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createServer
4
- } from "./chunk-MEQIW5WQ.js";
4
+ } from "./chunk-UR6UXUAN.js";
5
5
 
6
6
  // src/bin.ts
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -1,9 +1,9 @@
1
1
  // src/core.ts
2
2
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
3
- import { dirname, extname, isAbsolute, resolve } from "path";
4
- import { pathToFileURL } from "url";
3
+ import { dirname, resolve } from "path";
5
4
  import {
6
5
  demoConfig,
6
+ loadConfig as loadLabConfig,
7
7
  runCrashLab,
8
8
  SCENARIOS,
9
9
  toJson
@@ -63,17 +63,7 @@ function writeReport(result, reportPath) {
63
63
  writeFileSync(reportPath, toJson(result));
64
64
  }
65
65
  async function loadConfig(configPath, cwd = process.cwd()) {
66
- const path = isAbsolute(configPath) ? configPath : resolve(cwd, configPath);
67
- if ([".ts", ".mts", ".cts"].includes(extname(path))) {
68
- throw new Error(".ts configs are not supported yet; use crash-lab.config.mjs");
69
- }
70
- if (!existsSync(path)) throw new Error(`Config not found: ${path}`);
71
- const mod = await import(`${pathToFileURL(path).href}?t=${Date.now()}`);
72
- const config = mod.default;
73
- if (!config || !config.target || typeof config.secret !== "string" || !config.event?.id) {
74
- throw new Error(`Invalid config ${path}: must export default { target, secret, event }`);
75
- }
76
- return config;
66
+ return loadLabConfig(configPath, cwd);
77
67
  }
78
68
  async function runFromConfig(opts) {
79
69
  for (const id of opts.scenarios ?? []) {
@@ -138,7 +128,7 @@ function renderSummary(s) {
138
128
  // src/server.ts
139
129
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
140
130
  import { z } from "zod";
141
- var VERSION = "0.1.0";
131
+ var VERSION = "0.2.0";
142
132
  var summaryShape = {
143
133
  name: z.string(),
144
134
  ok: z.boolean(),
@@ -177,7 +167,9 @@ function createServer() {
177
167
  title: "Run webhook-crash-lab",
178
168
  description: "Replay hostile webhook deliveries (duplicates, concurrent duplicates, out-of-order events, forged/missing/stale signatures, malformed JSON) against the handler described by a crash-lab config file, and return structured failures with fix hints. Use a TEST database and test secret only. Create a config with `npx webhook-crash-lab init` if none exists.",
179
169
  inputSchema: {
180
- configPath: z.string().describe("Path to crash-lab.config.mjs (relative to cwd)"),
170
+ configPath: z.string().describe(
171
+ "Path to a crash-lab config (.mjs, .js, .cjs, .ts, .mts or .cts), relative to cwd"
172
+ ),
181
173
  cwd: z.string().optional().describe("Project directory; defaults to the server working directory"),
182
174
  scenarios: z.array(z.enum(SCENARIO_IDS)).optional().describe("Run only these scenarios"),
183
175
  reportPath: z.string().optional().describe("Also write the full JSON report here")
@@ -261,4 +253,4 @@ export {
261
253
  VERSION,
262
254
  createServer
263
255
  };
264
- //# sourceMappingURL=chunk-MEQIW5WQ.js.map
256
+ //# sourceMappingURL=chunk-UR6UXUAN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core.ts","../src/server.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport {\n demoConfig,\n loadConfig as loadLabConfig,\n runCrashLab,\n SCENARIOS,\n toJson,\n type CrashLabConfig,\n type CrashLabResult,\n type ScenarioId,\n} from 'webhook-crash-lab';\n\nexport type { CrashLabResult, ScenarioId };\n\n/** Fix security first, then duplicates, then ordering: later fixes depend on earlier ones. */\nconst PRIORITY: ScenarioId[] = [\n 'missing-signature',\n 'invalid-signature',\n 'stale-timestamp',\n 'malformed-json',\n 'single-valid',\n 'duplicate-sequential',\n 'duplicate-concurrent',\n 'out-of-order',\n];\nexport const SCENARIO_IDS = SCENARIOS.map((s) => s.id) as ScenarioId[];\n\nexport interface Failure {\n id: ScenarioId;\n title: string;\n why: string;\n fix: string;\n failedChecks: { name: string; detail: string }[];\n responses: number[];\n}\n\nexport interface Summary {\n name: string;\n ok: boolean;\n summary: { pass: number; fail: number; notTested: number };\n /** Failing scenarios, most important first. */\n failures: Failure[];\n notTested: ScenarioId[];\n scenarios: { id: ScenarioId; status: string }[];\n nextSteps: string[];\n reportPath?: string;\n}\n\nexport function summarize(result: CrashLabResult, reportPath?: string): Summary {\n const rank = (id: ScenarioId) => PRIORITY.indexOf(id);\n const failures = result.scenarios\n .filter((s) => s.status === 'fail')\n .sort((a, b) => rank(a.id) - rank(b.id))\n .map<Failure>((s) => ({\n id: s.id,\n title: s.title,\n why: s.why,\n fix: s.fix,\n failedChecks: s.checks\n .filter((c) => c.status === 'fail')\n .map(({ name, detail }) => ({ name, detail })),\n responses: s.responses,\n }));\n const notTested = result.scenarios.filter((s) => s.status === 'not-tested').map((s) => s.id);\n\n const nextSteps: string[] = [];\n if (failures.length) {\n nextSteps.push(\n `Fix in this order: ${failures.map((f) => f.id).join(' → ')}. Each failure has a \"fix\" hint.`,\n 'Change the webhook handler (not the test config), then re-run the same tool with the same arguments. Stop when ok is true.',\n 'Static checks for the same bugs: eslint-plugin-webhook-safety and eslint-plugin-bullmq.',\n );\n } else {\n nextSteps.push(\n 'All scenarios pass. Keep `npx webhook-crash-lab run` in CI so this stays true.',\n );\n }\n if (notTested.length) {\n nextSteps.push(\n `${notTested.length} scenario(s) not tested (${notTested.join(', ')}): duplicate checks need an \\`adapter\\` + \\`expect.once\\`; out-of-order needs \\`sequence\\` in the config.`,\n );\n }\n return {\n name: result.name,\n ok: result.ok,\n summary: result.summary,\n failures,\n notTested,\n scenarios: result.scenarios.map((s) => ({ id: s.id, status: s.status })),\n nextSteps,\n ...(reportPath ? { reportPath } : {}),\n };\n}\n\nfunction writeReport(result: CrashLabResult, reportPath: string): void {\n mkdirSync(dirname(reportPath), { recursive: true });\n writeFileSync(reportPath, toJson(result));\n}\n\nexport interface RunOptions {\n configPath: string;\n /** Directory relative paths resolve against. Defaults to process.cwd(). */\n cwd?: string;\n scenarios?: ScenarioId[];\n /** If set, the JSON report is written here too. */\n reportPath?: string;\n}\n\n/**\n * Load a crash-lab config (.mjs/.js/.cjs, or .ts/.mts/.cts via jiti or Node type stripping).\n * Delegates to webhook-crash-lab so the CLI and the MCP server accept exactly the same files,\n * and re-reads the file each call so an agent re-running after edits gets the new config.\n */\nexport async function loadConfig(configPath: string, cwd = process.cwd()): Promise<CrashLabConfig> {\n return loadLabConfig(configPath, cwd);\n}\n\nexport async function runFromConfig(opts: RunOptions): Promise<Summary> {\n for (const id of opts.scenarios ?? []) {\n if (!SCENARIO_IDS.includes(id)) {\n throw new Error(`Unknown scenario \"${id}\". Known: ${SCENARIO_IDS.join(', ')}`);\n }\n }\n const config = await loadConfig(opts.configPath, opts.cwd);\n const result = await runCrashLab(\n opts.scenarios?.length ? { ...config, scenarios: opts.scenarios } : config,\n );\n const reportPath = opts.reportPath && resolve(opts.cwd ?? process.cwd(), opts.reportPath);\n if (reportPath) writeReport(result, reportPath);\n return summarize(result, reportPath);\n}\n\nexport async function runDemo(mode: 'broken' | 'fixed'): Promise<Summary> {\n return summarize(await runCrashLab(demoConfig(mode)));\n}\n\nfunction isReport(x: unknown): x is CrashLabResult {\n const r = x as CrashLabResult;\n return (\n !!r &&\n typeof r === 'object' &&\n typeof r.ok === 'boolean' &&\n Array.isArray(r.scenarios) &&\n !!r.summary\n );\n}\n\nexport function explainReport(input: {\n reportPath?: string;\n report?: unknown;\n cwd?: string;\n}): Summary {\n let report: unknown = input.report;\n if (input.reportPath) {\n const path = resolve(input.cwd ?? process.cwd(), input.reportPath);\n if (!existsSync(path)) throw new Error(`Report not found: ${path}`);\n report = readFileSync(path, 'utf8');\n }\n if (report === undefined)\n throw new Error('Pass reportPath or report (the JSON from `webhook-crash-lab run --json`).');\n if (typeof report === 'string') {\n try {\n report = JSON.parse(report);\n } catch {\n throw new Error('Report is not valid JSON.');\n }\n }\n if (!isReport(report))\n throw new Error(\n 'This is not a webhook-crash-lab JSON report (expected ok, summary, scenarios).',\n );\n return summarize(report, input.reportPath);\n}\n\nexport function listScenarios() {\n return SCENARIOS.map(({ id, title, what, why, fix }) => ({ id, title, what, why, fix }));\n}\n\n/** Short human/agent-readable rendering of a summary. */\nexport function renderSummary(s: Summary): string {\n const lines = [\n `${s.ok ? 'PASS' : 'FAIL'} ${s.name}: ${s.summary.pass} pass, ${s.summary.fail} fail, ${s.summary.notTested} not tested`,\n ];\n for (const f of s.failures) {\n lines.push('', `✗ ${f.id}: ${f.title}`);\n for (const c of f.failedChecks) lines.push(` ${c.name}: ${c.detail}`);\n lines.push(` why: ${f.why}`, ` fix: ${f.fix}`);\n }\n lines.push('', 'Next steps:', ...s.nextSteps.map((n) => `- ${n}`));\n if (s.reportPath) lines.push(`Report written to ${s.reportPath}`);\n return lines.join('\\n');\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n explainReport,\n listScenarios,\n renderSummary,\n runDemo,\n runFromConfig,\n SCENARIO_IDS,\n type Summary,\n} from './core.js';\n\nexport const VERSION = '0.2.0';\n\nconst summaryShape = {\n name: z.string(),\n ok: z.boolean(),\n summary: z.object({ pass: z.number(), fail: z.number(), notTested: z.number() }),\n failures: z.array(\n z.object({\n id: z.string(),\n title: z.string(),\n why: z.string(),\n fix: z.string(),\n failedChecks: z.array(z.object({ name: z.string(), detail: z.string() })),\n responses: z.array(z.number()),\n }),\n ),\n notTested: z.array(z.string()),\n scenarios: z.array(z.object({ id: z.string(), status: z.string() })),\n nextSteps: z.array(z.string()),\n reportPath: z.string().optional(),\n};\n\nasync function respond(work: () => Promise<Summary> | Summary) {\n try {\n const s = await work();\n return {\n content: [{ type: 'text' as const, text: renderSummary(s) }],\n structuredContent: s as unknown as Record<string, unknown>,\n };\n } catch (err) {\n return { isError: true, content: [{ type: 'text' as const, text: (err as Error).message }] };\n }\n}\n\nexport function createServer(): McpServer {\n const server = new McpServer({ name: 'webhook-crash-lab', version: VERSION });\n\n server.registerTool(\n 'run_crash_lab',\n {\n title: 'Run webhook-crash-lab',\n description:\n 'Replay hostile webhook deliveries (duplicates, concurrent duplicates, out-of-order events, forged/missing/stale signatures, malformed JSON) against the handler described by a crash-lab config file, and return structured failures with fix hints. Use a TEST database and test secret only. Create a config with `npx webhook-crash-lab init` if none exists.',\n inputSchema: {\n configPath: z\n .string()\n .describe(\n 'Path to a crash-lab config (.mjs, .js, .cjs, .ts, .mts or .cts), relative to cwd',\n ),\n cwd: z\n .string()\n .optional()\n .describe('Project directory; defaults to the server working directory'),\n scenarios: z\n .array(z.enum(SCENARIO_IDS as [string, ...string[]]))\n .optional()\n .describe('Run only these scenarios'),\n reportPath: z.string().optional().describe('Also write the full JSON report here'),\n },\n outputSchema: summaryShape,\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n (args) => respond(() => runFromConfig(args as Parameters<typeof runFromConfig>[0])),\n );\n\n server.registerTool(\n 'run_demo',\n {\n title: 'Run the built-in demo',\n description:\n 'Run crash-lab against its built-in demo credits app. \"broken\" shows the typical AI-generated Stripe handler failing; \"fixed\" shows the same app done right. Needs no config. Use it to show what crash-lab checks before setting up a real config.',\n inputSchema: { mode: z.enum(['broken', 'fixed']).default('broken') },\n outputSchema: summaryShape,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n ({ mode }) => respond(() => runDemo(mode)),\n );\n\n server.registerTool(\n 'explain_report',\n {\n title: 'Explain a crash-lab report',\n description:\n 'Read a JSON report produced by `webhook-crash-lab run --json <file>` (e.g. from CI) and return the failures in fix order, with why/fix hints and next steps.',\n inputSchema: {\n reportPath: z.string().optional(),\n report: z\n .union([z.string(), z.record(z.string(), z.unknown())])\n .optional()\n .describe('Report JSON, as text or object'),\n cwd: z.string().optional(),\n },\n outputSchema: summaryShape,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n (args) => respond(() => explainReport(args)),\n );\n\n server.registerTool(\n 'list_scenarios',\n {\n title: 'List crash-lab scenarios',\n description:\n 'List every scenario crash-lab can run, with what it sends, why it matters in production, and how to fix a failure.',\n inputSchema: {},\n outputSchema: {\n scenarios: z.array(\n z.object({\n id: z.string(),\n title: z.string(),\n what: z.string(),\n why: z.string(),\n fix: z.string(),\n }),\n ),\n },\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n () => {\n const scenarios = listScenarios();\n return {\n content: [\n {\n type: 'text',\n text: scenarios.map((s) => `${s.id}: ${s.what}\\n fix: ${s.fix}`).join('\\n'),\n },\n ],\n structuredContent: { scenarios },\n };\n },\n );\n\n return server;\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -41,6 +41,11 @@ interface RunOptions {
41
41
  /** If set, the JSON report is written here too. */
42
42
  reportPath?: string;
43
43
  }
44
+ /**
45
+ * Load a crash-lab config (.mjs/.js/.cjs, or .ts/.mts/.cts via jiti or Node type stripping).
46
+ * Delegates to webhook-crash-lab so the CLI and the MCP server accept exactly the same files,
47
+ * and re-reads the file each call so an agent re-running after edits gets the new config.
48
+ */
44
49
  declare function loadConfig(configPath: string, cwd?: string): Promise<CrashLabConfig>;
45
50
  declare function runFromConfig(opts: RunOptions): Promise<Summary>;
46
51
  declare function runDemo(mode: 'broken' | 'fixed'): Promise<Summary>;
@@ -59,7 +64,7 @@ declare function listScenarios(): {
59
64
  /** Short human/agent-readable rendering of a summary. */
60
65
  declare function renderSummary(s: Summary): string;
61
66
 
62
- declare const VERSION = "0.1.0";
67
+ declare const VERSION = "0.2.0";
63
68
  declare function createServer(): McpServer;
64
69
 
65
70
  export { type Failure, type RunOptions, SCENARIO_IDS, type Summary, VERSION, createServer, explainReport, listScenarios, loadConfig, renderSummary, runDemo, runFromConfig, summarize };
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  runDemo,
10
10
  runFromConfig,
11
11
  summarize
12
- } from "./chunk-MEQIW5WQ.js";
12
+ } from "./chunk-UR6UXUAN.js";
13
13
  export {
14
14
  SCENARIO_IDS,
15
15
  VERSION,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webhook-crash-lab-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server (plus Claude Code plugin and Codex skill) that lets coding agents run webhook-crash-lab, read structured failures with fix hints, and re-run until the webhook handler survives retries, duplicates and forged signatures.",
5
5
  "license": "MIT",
6
6
  "author": "Krrish Kumar (https://github.com/Krrishkumar125)",
@@ -23,7 +23,7 @@
23
23
  ],
24
24
  "type": "module",
25
25
  "bin": {
26
- "webhook-crash-lab-mcp": "./dist/bin.js"
26
+ "webhook-crash-lab-mcp": "dist/bin.js"
27
27
  },
28
28
  "main": "./dist/index.js",
29
29
  "types": "./dist/index.d.ts",
@@ -60,7 +60,8 @@
60
60
  },
61
61
  "dependencies": {
62
62
  "@modelcontextprotocol/sdk": "^1.32.1",
63
- "webhook-crash-lab": "^0.2.0",
63
+ "jiti": "^2.7.0",
64
+ "webhook-crash-lab": "^0.3.0",
64
65
  "zod": "^4.6.7"
65
66
  },
66
67
  "devDependencies": {
@@ -12,7 +12,7 @@ business state (for example "credits granted exactly once").
12
12
  ## Loop
13
13
 
14
14
  1. **Find the handler** (`/webhook` route, `app/api/webhooks/*/route.ts`, `constructEvent` call).
15
- 2. **Config.** If `crash-lab.config.mjs` does not exist, run `npx webhook-crash-lab init` and fill it in:
15
+ 2. **Config.** If no `crash-lab.config.{mjs,js,ts}` exists, run `npx webhook-crash-lab init` and fill it in:
16
16
  - `target`: the local dev URL of the webhook route, or an in-process `(req: Request) => Response` handler.
17
17
  - `secret`: a **test** signing secret the handler verifies with (set it in the handler's env too).
18
18
  - `event`: a realistic event (for example `checkout.session.completed`).
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core.ts","../src/server.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, extname, isAbsolute, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n demoConfig,\n runCrashLab,\n SCENARIOS,\n toJson,\n type CrashLabConfig,\n type CrashLabResult,\n type ScenarioId,\n} from 'webhook-crash-lab';\n\nexport type { CrashLabResult, ScenarioId };\n\n/** Fix security first, then duplicates, then ordering: later fixes depend on earlier ones. */\nconst PRIORITY: ScenarioId[] = [\n 'missing-signature',\n 'invalid-signature',\n 'stale-timestamp',\n 'malformed-json',\n 'single-valid',\n 'duplicate-sequential',\n 'duplicate-concurrent',\n 'out-of-order',\n];\nexport const SCENARIO_IDS = SCENARIOS.map((s) => s.id) as ScenarioId[];\n\nexport interface Failure {\n id: ScenarioId;\n title: string;\n why: string;\n fix: string;\n failedChecks: { name: string; detail: string }[];\n responses: number[];\n}\n\nexport interface Summary {\n name: string;\n ok: boolean;\n summary: { pass: number; fail: number; notTested: number };\n /** Failing scenarios, most important first. */\n failures: Failure[];\n notTested: ScenarioId[];\n scenarios: { id: ScenarioId; status: string }[];\n nextSteps: string[];\n reportPath?: string;\n}\n\nexport function summarize(result: CrashLabResult, reportPath?: string): Summary {\n const rank = (id: ScenarioId) => PRIORITY.indexOf(id);\n const failures = result.scenarios\n .filter((s) => s.status === 'fail')\n .sort((a, b) => rank(a.id) - rank(b.id))\n .map<Failure>((s) => ({\n id: s.id,\n title: s.title,\n why: s.why,\n fix: s.fix,\n failedChecks: s.checks\n .filter((c) => c.status === 'fail')\n .map(({ name, detail }) => ({ name, detail })),\n responses: s.responses,\n }));\n const notTested = result.scenarios.filter((s) => s.status === 'not-tested').map((s) => s.id);\n\n const nextSteps: string[] = [];\n if (failures.length) {\n nextSteps.push(\n `Fix in this order: ${failures.map((f) => f.id).join(' → ')}. Each failure has a \"fix\" hint.`,\n 'Change the webhook handler (not the test config), then re-run the same tool with the same arguments. Stop when ok is true.',\n 'Static checks for the same bugs: eslint-plugin-webhook-safety and eslint-plugin-bullmq.',\n );\n } else {\n nextSteps.push(\n 'All scenarios pass. Keep `npx webhook-crash-lab run` in CI so this stays true.',\n );\n }\n if (notTested.length) {\n nextSteps.push(\n `${notTested.length} scenario(s) not tested (${notTested.join(', ')}): duplicate checks need an \\`adapter\\` + \\`expect.once\\`; out-of-order needs \\`sequence\\` in the config.`,\n );\n }\n return {\n name: result.name,\n ok: result.ok,\n summary: result.summary,\n failures,\n notTested,\n scenarios: result.scenarios.map((s) => ({ id: s.id, status: s.status })),\n nextSteps,\n ...(reportPath ? { reportPath } : {}),\n };\n}\n\nfunction writeReport(result: CrashLabResult, reportPath: string): void {\n mkdirSync(dirname(reportPath), { recursive: true });\n writeFileSync(reportPath, toJson(result));\n}\n\nexport interface RunOptions {\n configPath: string;\n /** Directory relative paths resolve against. Defaults to process.cwd(). */\n cwd?: string;\n scenarios?: ScenarioId[];\n /** If set, the JSON report is written here too. */\n reportPath?: string;\n}\n\nexport async function loadConfig(configPath: string, cwd = process.cwd()): Promise<CrashLabConfig> {\n const path = isAbsolute(configPath) ? configPath : resolve(cwd, configPath);\n if (['.ts', '.mts', '.cts'].includes(extname(path))) {\n throw new Error('.ts configs are not supported yet; use crash-lab.config.mjs');\n }\n if (!existsSync(path)) throw new Error(`Config not found: ${path}`);\n // Cache-bust so an agent re-running after edits gets the new config.\n const mod = (await import(`${pathToFileURL(path).href}?t=${Date.now()}`)) as {\n default?: CrashLabConfig;\n };\n const config = mod.default;\n if (!config || !config.target || typeof config.secret !== 'string' || !config.event?.id) {\n throw new Error(`Invalid config ${path}: must export default { target, secret, event }`);\n }\n return config;\n}\n\nexport async function runFromConfig(opts: RunOptions): Promise<Summary> {\n for (const id of opts.scenarios ?? []) {\n if (!SCENARIO_IDS.includes(id)) {\n throw new Error(`Unknown scenario \"${id}\". Known: ${SCENARIO_IDS.join(', ')}`);\n }\n }\n const config = await loadConfig(opts.configPath, opts.cwd);\n const result = await runCrashLab(\n opts.scenarios?.length ? { ...config, scenarios: opts.scenarios } : config,\n );\n const reportPath = opts.reportPath && resolve(opts.cwd ?? process.cwd(), opts.reportPath);\n if (reportPath) writeReport(result, reportPath);\n return summarize(result, reportPath);\n}\n\nexport async function runDemo(mode: 'broken' | 'fixed'): Promise<Summary> {\n return summarize(await runCrashLab(demoConfig(mode)));\n}\n\nfunction isReport(x: unknown): x is CrashLabResult {\n const r = x as CrashLabResult;\n return (\n !!r &&\n typeof r === 'object' &&\n typeof r.ok === 'boolean' &&\n Array.isArray(r.scenarios) &&\n !!r.summary\n );\n}\n\nexport function explainReport(input: {\n reportPath?: string;\n report?: unknown;\n cwd?: string;\n}): Summary {\n let report: unknown = input.report;\n if (input.reportPath) {\n const path = resolve(input.cwd ?? process.cwd(), input.reportPath);\n if (!existsSync(path)) throw new Error(`Report not found: ${path}`);\n report = readFileSync(path, 'utf8');\n }\n if (report === undefined)\n throw new Error('Pass reportPath or report (the JSON from `webhook-crash-lab run --json`).');\n if (typeof report === 'string') {\n try {\n report = JSON.parse(report);\n } catch {\n throw new Error('Report is not valid JSON.');\n }\n }\n if (!isReport(report))\n throw new Error(\n 'This is not a webhook-crash-lab JSON report (expected ok, summary, scenarios).',\n );\n return summarize(report, input.reportPath);\n}\n\nexport function listScenarios() {\n return SCENARIOS.map(({ id, title, what, why, fix }) => ({ id, title, what, why, fix }));\n}\n\n/** Short human/agent-readable rendering of a summary. */\nexport function renderSummary(s: Summary): string {\n const lines = [\n `${s.ok ? 'PASS' : 'FAIL'} ${s.name}: ${s.summary.pass} pass, ${s.summary.fail} fail, ${s.summary.notTested} not tested`,\n ];\n for (const f of s.failures) {\n lines.push('', `✗ ${f.id}: ${f.title}`);\n for (const c of f.failedChecks) lines.push(` ${c.name}: ${c.detail}`);\n lines.push(` why: ${f.why}`, ` fix: ${f.fix}`);\n }\n lines.push('', 'Next steps:', ...s.nextSteps.map((n) => `- ${n}`));\n if (s.reportPath) lines.push(`Report written to ${s.reportPath}`);\n return lines.join('\\n');\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n explainReport,\n listScenarios,\n renderSummary,\n runDemo,\n runFromConfig,\n SCENARIO_IDS,\n type Summary,\n} from './core.js';\n\nexport const VERSION = '0.1.0';\n\nconst summaryShape = {\n name: z.string(),\n ok: z.boolean(),\n summary: z.object({ pass: z.number(), fail: z.number(), notTested: z.number() }),\n failures: z.array(\n z.object({\n id: z.string(),\n title: z.string(),\n why: z.string(),\n fix: z.string(),\n failedChecks: z.array(z.object({ name: z.string(), detail: z.string() })),\n responses: z.array(z.number()),\n }),\n ),\n notTested: z.array(z.string()),\n scenarios: z.array(z.object({ id: z.string(), status: z.string() })),\n nextSteps: z.array(z.string()),\n reportPath: z.string().optional(),\n};\n\nasync function respond(work: () => Promise<Summary> | Summary) {\n try {\n const s = await work();\n return {\n content: [{ type: 'text' as const, text: renderSummary(s) }],\n structuredContent: s as unknown as Record<string, unknown>,\n };\n } catch (err) {\n return { isError: true, content: [{ type: 'text' as const, text: (err as Error).message }] };\n }\n}\n\nexport function createServer(): McpServer {\n const server = new McpServer({ name: 'webhook-crash-lab', version: VERSION });\n\n server.registerTool(\n 'run_crash_lab',\n {\n title: 'Run webhook-crash-lab',\n description:\n 'Replay hostile webhook deliveries (duplicates, concurrent duplicates, out-of-order events, forged/missing/stale signatures, malformed JSON) against the handler described by a crash-lab config file, and return structured failures with fix hints. Use a TEST database and test secret only. Create a config with `npx webhook-crash-lab init` if none exists.',\n inputSchema: {\n configPath: z.string().describe('Path to crash-lab.config.mjs (relative to cwd)'),\n cwd: z\n .string()\n .optional()\n .describe('Project directory; defaults to the server working directory'),\n scenarios: z\n .array(z.enum(SCENARIO_IDS as [string, ...string[]]))\n .optional()\n .describe('Run only these scenarios'),\n reportPath: z.string().optional().describe('Also write the full JSON report here'),\n },\n outputSchema: summaryShape,\n annotations: { readOnlyHint: false, openWorldHint: true },\n },\n (args) => respond(() => runFromConfig(args as Parameters<typeof runFromConfig>[0])),\n );\n\n server.registerTool(\n 'run_demo',\n {\n title: 'Run the built-in demo',\n description:\n 'Run crash-lab against its built-in demo credits app. \"broken\" shows the typical AI-generated Stripe handler failing; \"fixed\" shows the same app done right. Needs no config. Use it to show what crash-lab checks before setting up a real config.',\n inputSchema: { mode: z.enum(['broken', 'fixed']).default('broken') },\n outputSchema: summaryShape,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n ({ mode }) => respond(() => runDemo(mode)),\n );\n\n server.registerTool(\n 'explain_report',\n {\n title: 'Explain a crash-lab report',\n description:\n 'Read a JSON report produced by `webhook-crash-lab run --json <file>` (e.g. from CI) and return the failures in fix order, with why/fix hints and next steps.',\n inputSchema: {\n reportPath: z.string().optional(),\n report: z\n .union([z.string(), z.record(z.string(), z.unknown())])\n .optional()\n .describe('Report JSON, as text or object'),\n cwd: z.string().optional(),\n },\n outputSchema: summaryShape,\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n (args) => respond(() => explainReport(args)),\n );\n\n server.registerTool(\n 'list_scenarios',\n {\n title: 'List crash-lab scenarios',\n description:\n 'List every scenario crash-lab can run, with what it sends, why it matters in production, and how to fix a failure.',\n inputSchema: {},\n outputSchema: {\n scenarios: z.array(\n z.object({\n id: z.string(),\n title: z.string(),\n what: z.string(),\n why: z.string(),\n fix: z.string(),\n }),\n ),\n },\n annotations: { readOnlyHint: true, openWorldHint: false },\n },\n () => {\n const scenarios = listScenarios();\n return {\n content: [\n {\n type: 'text',\n text: scenarios.map((s) => `${s.id}: ${s.what}\\n fix: ${s.fix}`).join('\\n'),\n },\n ],\n structuredContent: { scenarios },\n };\n },\n );\n\n return server;\n}\n"],"mappings":"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","names":[]}