@webhooks-cc/mcp 1.12.1 → 1.13.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 CHANGED
@@ -11,6 +11,16 @@ The server reads `WHK_API_KEY` from the environment. It also supports:
11
11
  - `WHK_BASE_URL` for a custom API base URL
12
12
  - `WHK_WEBHOOK_URL` for a custom receiver base URL
13
13
 
14
+ ### Claude Code plugin
15
+
16
+ The plugin installs the server together with a webhook testing skill that tells Claude when to use it:
17
+
18
+ ```text
19
+ /plugin install webhooks-cc --marketplace kroqdotdev/webhooks-cc
20
+ ```
21
+
22
+ For other agents, `npx skills add kroqdotdev/webhooks-cc` installs the same skill. See [Agent skill](https://webhooks.cc/docs/mcp/skills).
23
+
14
24
  ### One-click
15
25
 
16
26
  - **Cursor**: [Add to Cursor](https://cursor.com/en/install-mcp?name=webhooks-cc&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkB3ZWJob29rcy1jYy9tY3AiXSwiZW52Ijp7IldIS19BUElfS0VZIjoid2hjY18uLi4ifX0=)
@@ -54,11 +64,11 @@ npx @webhooks-cc/mcp setup claude-desktop --api-key whcc_...
54
64
  }
55
65
  ```
56
66
 
57
- Get your API key at [webhooks.cc/account](https://webhooks.cc/account).
67
+ Get your API key at [webhooks.cc/account/api-keys](https://webhooks.cc/account/api-keys).
58
68
 
59
69
  ## What it exposes
60
70
 
61
- The server exposes `38` tools, `3` prompts, and `3` resource surfaces with an API key, and `10` sandbox tools without one.
71
+ The server exposes `39` tools, `3` prompts, and `3` resource surfaces with an API key, and `11` sandbox tools without one.
62
72
 
63
73
  ### Endpoint tools
64
74
 
@@ -80,6 +90,7 @@ The server exposes `38` tools, `3` prompts, and `3` resource surfaces with an AP
80
90
  - `wait_for_requests`
81
91
  - `replay_request`
82
92
  - `compare_requests`
93
+ - `generate_payload_schema` (TypeScript, Zod, Pydantic, or JSON Schema from captured bodies)
83
94
  - `extract_from_request`
84
95
  - `verify_signature`
85
96
  - `clear_requests`
@@ -132,7 +143,7 @@ Endpoints shared with you already appear in `list_endpoints` (with `fromTeam`) a
132
143
 
133
144
  Started without `WHK_API_KEY`, the server registers sandbox tools instead ([auth.md](https://webhooks.cc/auth.md)):
134
145
 
135
- - `create_endpoint`, `list_endpoints`, `get_endpoint`, `delete_endpoint`, `list_requests`, `get_request`, `wait_for_request`: the same names and arguments as the full tools, in a sandbox of up to 3 endpoints, 25 captured requests each and 100 in all, for 24 hours. The first `create_endpoint` registers it (a few seconds of proof of work).
146
+ - `create_endpoint`, `list_endpoints`, `get_endpoint`, `delete_endpoint`, `list_requests`, `get_request`, `wait_for_request`, `generate_payload_schema`: the same names and arguments as the full tools, in a sandbox of up to 3 endpoints, 25 captured requests each and 100 in all, for 24 hours. The first `create_endpoint` registers it (a few seconds of proof of work).
136
147
  - `connect_account`: returns a link and a 6-digit code for a human to connect the agent to their account.
137
148
  - `wait_for_connection`: waits for the human, then replaces the sandbox tools with the full tools for that account (`notifications/tools/list_changed`).
138
149
  - `about_sandbox`: the limits and how to connect, without a network call.
package/dist/bin/mcp.js CHANGED
@@ -76,6 +76,7 @@ function registerPrompts(server) {
76
76
  "If a target URL is provided, use preview_webhook before send_to so the request shape is visible.",
77
77
  targetUrl ? `Target URL: ${targetUrl}.` : null,
78
78
  "Send a realistic provider webhook, wait for capture, and verify the signature if a secret is available.",
79
+ "Once real deliveries are captured, use generate_payload_schema to write the payload types for the handler.",
79
80
  "Return the endpoint URL, the exact tools used, and the next step for the developer."
80
81
  ].filter(Boolean).join(" ")
81
82
  }
@@ -493,6 +494,67 @@ var waitForRequestShape = {
493
494
  timeout: durationOrTimestampSchema.default("30s").describe('How long to wait, for example "30s"'),
494
495
  pollInterval: durationOrTimestampSchema.optional().describe('Interval between polls, for example "500ms" or "1s"')
495
496
  };
497
+ var payloadSchemaShape = {
498
+ endpointSlug: import_zod2.z.string().describe("The endpoint slug"),
499
+ format: import_zod2.z.enum(import_sdk.PAYLOAD_SCHEMA_FORMATS).default("typescript").describe(
500
+ 'Output: "typescript" interfaces, "zod" schemas, "pydantic" models, or "json-schema"'
501
+ ),
502
+ name: import_zod2.z.string().max(100).optional().describe("Root type name (default: from the detected event, else WebhookPayload)"),
503
+ event: import_zod2.z.string().optional().describe(
504
+ 'Only requests whose detected provider event matches, e.g. "invoice.paid" or "push". One event per schema gives the tightest types.'
505
+ ),
506
+ method: import_zod2.z.string().optional().describe("Only requests with this HTTP method"),
507
+ path: import_zod2.z.string().optional().describe("Only requests with this path"),
508
+ requestIds: import_zod2.z.array(import_zod2.z.string()).min(1).max(50).optional().describe("Use exactly these requests instead of the latest ones"),
509
+ limit: import_zod2.z.number().int().min(1).max(100).default(20).describe("How many of the latest requests to read before filtering")
510
+ };
511
+ function eventName(event) {
512
+ const words = event.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);
513
+ const name = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
514
+ if (!name) return "WebhookPayload";
515
+ return /^[0-9]/.test(name) ? `Webhook${name}Event` : `${name}Event`;
516
+ }
517
+ function payloadSchemaResult(requests, args2) {
518
+ const method = args2.method?.toUpperCase();
519
+ const matching = requests.filter(
520
+ (request) => request.kind !== "email" && (!method || request.method.toUpperCase() === method) && (!args2.path || request.path === args2.path) && (!args2.event || (0, import_sdk.detectWebhookInfo)(request)?.event === args2.event)
521
+ );
522
+ if (matching.length === 0) {
523
+ throw new Error(
524
+ `None of the ${requests.length} requests read matched. Capture a request first, or loosen event, method or path.`
525
+ );
526
+ }
527
+ const events = [
528
+ ...new Set(
529
+ matching.map((request) => (0, import_sdk.detectWebhookInfo)(request)?.event).filter((e) => !!e)
530
+ )
531
+ ];
532
+ const name = args2.name ?? (events.length === 1 ? eventName(events[0]) : void 0);
533
+ const result = (0, import_sdk.generateRequestSchema)(matching, { format: args2.format, name });
534
+ if (result.samples === 0) {
535
+ throw new Error(
536
+ `None of the ${matching.length} matching requests has a JSON or form body to learn from.`
537
+ );
538
+ }
539
+ const output = {
540
+ format: result.format,
541
+ name: result.name,
542
+ samples: result.samples,
543
+ skipped: result.skipped,
544
+ ...events.length > 0 ? { events } : {},
545
+ ...events.length > 1 ? {
546
+ warning: `The samples mix ${events.length} events, so fields of one event show up as optional. Pass event for one schema per event.`
547
+ } : {},
548
+ code: result.code
549
+ };
550
+ const size = JSON.stringify(output, null, 2).length;
551
+ if (size > MAX_OUTPUT) {
552
+ throw new Error(
553
+ `The schema is ${result.code.length} characters, more than this tool can return. Narrow it with event or requestIds, or call generateRequestSchema() from @webhooks-cc/sdk in code.`
554
+ );
555
+ }
556
+ return jsonContent(output);
557
+ }
496
558
  function textContent(text) {
497
559
  return { content: [{ type: "text", text }] };
498
560
  }
@@ -1189,6 +1251,15 @@ function registerTools(server, client) {
1189
1251
  return jsonContent(diff);
1190
1252
  })
1191
1253
  );
1254
+ server.tool(
1255
+ "generate_payload_schema",
1256
+ "Generate TypeScript interfaces, Zod schemas, Pydantic models, or JSON Schema from captured request bodies. Several requests of one event teach which fields are optional or nullable, so filter with event (or pass requestIds) and write the result into the codebase instead of hand-typing the payload.",
1257
+ payloadSchemaShape,
1258
+ withErrorHandling(async ({ endpointSlug, requestIds, limit, ...args2 }) => {
1259
+ const requests = requestIds ? await Promise.all(requestIds.map((id) => client.requests.get(id))) : await client.requests.list(endpointSlug, { limit, kind: "http" });
1260
+ return payloadSchemaResult(requests, args2);
1261
+ })
1262
+ );
1192
1263
  server.tool(
1193
1264
  "extract_from_request",
1194
1265
  "Extract specific JSON fields from a captured request body.",
@@ -1879,6 +1950,18 @@ function registerSandboxTools(server, options = {}) {
1879
1950
  })
1880
1951
  )
1881
1952
  );
1953
+ registered.push(
1954
+ server.tool(
1955
+ "generate_payload_schema",
1956
+ "Generate TypeScript interfaces, Zod schemas, Pydantic models, or JSON Schema from the bodies of requests captured by a sandbox endpoint. Several requests of one event teach which fields are optional or nullable.",
1957
+ payloadSchemaShape,
1958
+ withErrorHandling(async ({ endpointSlug, requestIds, limit, ...args2 }) => {
1959
+ const sandbox = await session.requireSandbox();
1960
+ const requests = requestIds ? await Promise.all(requestIds.map((id) => sandbox.requests.get(id))) : await sandbox.requests.list(endpointSlug, { limit });
1961
+ return payloadSchemaResult(requests, args2);
1962
+ })
1963
+ )
1964
+ );
1882
1965
  registered.push(
1883
1966
  server.tool(
1884
1967
  "connect_account",
@@ -2034,7 +2117,7 @@ function registerSandboxTools(server, options = {}) {
2034
2117
  }
2035
2118
 
2036
2119
  // src/index.ts
2037
- var VERSION = true ? "1.12.1" : "0.0.0-dev";
2120
+ var VERSION = true ? "1.13.0" : "0.0.0-dev";
2038
2121
  function createServer(options = {}) {
2039
2122
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
2040
2123
  const webhookUrl = options.webhookUrl ?? process.env.WHK_WEBHOOK_URL;
@@ -2255,7 +2338,7 @@ function runSetup(args2) {
2255
2338
  const apiKey = resolveApiKey(flags);
2256
2339
  if (!apiKey) {
2257
2340
  console.error("Error: API key required. Pass --api-key or set WHK_API_KEY env var.");
2258
- console.error("Get your API key at https://webhooks.cc/account\n");
2341
+ console.error("Get your API key at https://webhooks.cc/account/api-keys\n");
2259
2342
  process.exit(1);
2260
2343
  }
2261
2344
  validateApiKeyFormat(apiKey);
@@ -2310,7 +2393,7 @@ Setup:
2310
2393
  npx @webhooks-cc/mcp setup windsurf --api-key whcc_...
2311
2394
  npx @webhooks-cc/mcp setup claude-desktop --api-key whcc_...
2312
2395
 
2313
- Get your API key at https://webhooks.cc/account
2396
+ Get your API key at https://webhooks.cc/account/api-keys
2314
2397
  `);
2315
2398
  } else if (command && command !== "--") {
2316
2399
  console.error(`Unknown command: "${command}". Run with --help for usage.`);
package/dist/index.d.mts CHANGED
@@ -53,4 +53,5 @@ interface CreateServerOptions {
53
53
  */
54
54
  declare function createServer(options?: CreateServerOptions): McpServer;
55
55
 
56
- export { type CreateServerOptions, type SandboxToolOptions, createServer, registerAgentRegistrationTools, registerPrompts, registerResources, registerSandboxTools, registerTools };
56
+ export { createServer, registerAgentRegistrationTools, registerPrompts, registerResources, registerSandboxTools, registerTools };
57
+ export type { CreateServerOptions, SandboxToolOptions };
package/dist/index.d.ts CHANGED
@@ -53,4 +53,5 @@ interface CreateServerOptions {
53
53
  */
54
54
  declare function createServer(options?: CreateServerOptions): McpServer;
55
55
 
56
- export { type CreateServerOptions, type SandboxToolOptions, createServer, registerAgentRegistrationTools, registerPrompts, registerResources, registerSandboxTools, registerTools };
56
+ export { createServer, registerAgentRegistrationTools, registerPrompts, registerResources, registerSandboxTools, registerTools };
57
+ export type { CreateServerOptions, SandboxToolOptions };
package/dist/index.js CHANGED
@@ -99,6 +99,7 @@ function registerPrompts(server) {
99
99
  "If a target URL is provided, use preview_webhook before send_to so the request shape is visible.",
100
100
  targetUrl ? `Target URL: ${targetUrl}.` : null,
101
101
  "Send a realistic provider webhook, wait for capture, and verify the signature if a secret is available.",
102
+ "Once real deliveries are captured, use generate_payload_schema to write the payload types for the handler.",
102
103
  "Return the endpoint URL, the exact tools used, and the next step for the developer."
103
104
  ].filter(Boolean).join(" ")
104
105
  }
@@ -516,6 +517,67 @@ var waitForRequestShape = {
516
517
  timeout: durationOrTimestampSchema.default("30s").describe('How long to wait, for example "30s"'),
517
518
  pollInterval: durationOrTimestampSchema.optional().describe('Interval between polls, for example "500ms" or "1s"')
518
519
  };
520
+ var payloadSchemaShape = {
521
+ endpointSlug: import_zod2.z.string().describe("The endpoint slug"),
522
+ format: import_zod2.z.enum(import_sdk.PAYLOAD_SCHEMA_FORMATS).default("typescript").describe(
523
+ 'Output: "typescript" interfaces, "zod" schemas, "pydantic" models, or "json-schema"'
524
+ ),
525
+ name: import_zod2.z.string().max(100).optional().describe("Root type name (default: from the detected event, else WebhookPayload)"),
526
+ event: import_zod2.z.string().optional().describe(
527
+ 'Only requests whose detected provider event matches, e.g. "invoice.paid" or "push". One event per schema gives the tightest types.'
528
+ ),
529
+ method: import_zod2.z.string().optional().describe("Only requests with this HTTP method"),
530
+ path: import_zod2.z.string().optional().describe("Only requests with this path"),
531
+ requestIds: import_zod2.z.array(import_zod2.z.string()).min(1).max(50).optional().describe("Use exactly these requests instead of the latest ones"),
532
+ limit: import_zod2.z.number().int().min(1).max(100).default(20).describe("How many of the latest requests to read before filtering")
533
+ };
534
+ function eventName(event) {
535
+ const words = event.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);
536
+ const name = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
537
+ if (!name) return "WebhookPayload";
538
+ return /^[0-9]/.test(name) ? `Webhook${name}Event` : `${name}Event`;
539
+ }
540
+ function payloadSchemaResult(requests, args) {
541
+ const method = args.method?.toUpperCase();
542
+ const matching = requests.filter(
543
+ (request) => request.kind !== "email" && (!method || request.method.toUpperCase() === method) && (!args.path || request.path === args.path) && (!args.event || (0, import_sdk.detectWebhookInfo)(request)?.event === args.event)
544
+ );
545
+ if (matching.length === 0) {
546
+ throw new Error(
547
+ `None of the ${requests.length} requests read matched. Capture a request first, or loosen event, method or path.`
548
+ );
549
+ }
550
+ const events = [
551
+ ...new Set(
552
+ matching.map((request) => (0, import_sdk.detectWebhookInfo)(request)?.event).filter((e) => !!e)
553
+ )
554
+ ];
555
+ const name = args.name ?? (events.length === 1 ? eventName(events[0]) : void 0);
556
+ const result = (0, import_sdk.generateRequestSchema)(matching, { format: args.format, name });
557
+ if (result.samples === 0) {
558
+ throw new Error(
559
+ `None of the ${matching.length} matching requests has a JSON or form body to learn from.`
560
+ );
561
+ }
562
+ const output = {
563
+ format: result.format,
564
+ name: result.name,
565
+ samples: result.samples,
566
+ skipped: result.skipped,
567
+ ...events.length > 0 ? { events } : {},
568
+ ...events.length > 1 ? {
569
+ warning: `The samples mix ${events.length} events, so fields of one event show up as optional. Pass event for one schema per event.`
570
+ } : {},
571
+ code: result.code
572
+ };
573
+ const size = JSON.stringify(output, null, 2).length;
574
+ if (size > MAX_OUTPUT) {
575
+ throw new Error(
576
+ `The schema is ${result.code.length} characters, more than this tool can return. Narrow it with event or requestIds, or call generateRequestSchema() from @webhooks-cc/sdk in code.`
577
+ );
578
+ }
579
+ return jsonContent(output);
580
+ }
519
581
  function textContent(text) {
520
582
  return { content: [{ type: "text", text }] };
521
583
  }
@@ -1212,6 +1274,15 @@ function registerTools(server, client) {
1212
1274
  return jsonContent(diff);
1213
1275
  })
1214
1276
  );
1277
+ server.tool(
1278
+ "generate_payload_schema",
1279
+ "Generate TypeScript interfaces, Zod schemas, Pydantic models, or JSON Schema from captured request bodies. Several requests of one event teach which fields are optional or nullable, so filter with event (or pass requestIds) and write the result into the codebase instead of hand-typing the payload.",
1280
+ payloadSchemaShape,
1281
+ withErrorHandling(async ({ endpointSlug, requestIds, limit, ...args }) => {
1282
+ const requests = requestIds ? await Promise.all(requestIds.map((id) => client.requests.get(id))) : await client.requests.list(endpointSlug, { limit, kind: "http" });
1283
+ return payloadSchemaResult(requests, args);
1284
+ })
1285
+ );
1215
1286
  server.tool(
1216
1287
  "extract_from_request",
1217
1288
  "Extract specific JSON fields from a captured request body.",
@@ -1902,6 +1973,18 @@ function registerSandboxTools(server, options = {}) {
1902
1973
  })
1903
1974
  )
1904
1975
  );
1976
+ registered.push(
1977
+ server.tool(
1978
+ "generate_payload_schema",
1979
+ "Generate TypeScript interfaces, Zod schemas, Pydantic models, or JSON Schema from the bodies of requests captured by a sandbox endpoint. Several requests of one event teach which fields are optional or nullable.",
1980
+ payloadSchemaShape,
1981
+ withErrorHandling(async ({ endpointSlug, requestIds, limit, ...args }) => {
1982
+ const sandbox = await session.requireSandbox();
1983
+ const requests = requestIds ? await Promise.all(requestIds.map((id) => sandbox.requests.get(id))) : await sandbox.requests.list(endpointSlug, { limit });
1984
+ return payloadSchemaResult(requests, args);
1985
+ })
1986
+ )
1987
+ );
1905
1988
  registered.push(
1906
1989
  server.tool(
1907
1990
  "connect_account",
@@ -2060,7 +2143,7 @@ function registerAgentRegistrationTools(server) {
2060
2143
  }
2061
2144
 
2062
2145
  // src/index.ts
2063
- var VERSION = true ? "1.12.1" : "0.0.0-dev";
2146
+ var VERSION = true ? "1.13.0" : "0.0.0-dev";
2064
2147
  function createServer(options = {}) {
2065
2148
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
2066
2149
  const webhookUrl = options.webhookUrl ?? process.env.WHK_WEBHOOK_URL;
package/dist/index.mjs CHANGED
@@ -70,6 +70,7 @@ function registerPrompts(server) {
70
70
  "If a target URL is provided, use preview_webhook before send_to so the request shape is visible.",
71
71
  targetUrl ? `Target URL: ${targetUrl}.` : null,
72
72
  "Send a realistic provider webhook, wait for capture, and verify the signature if a secret is available.",
73
+ "Once real deliveries are captured, use generate_payload_schema to write the payload types for the handler.",
73
74
  "Return the endpoint URL, the exact tools used, and the next step for the developer."
74
75
  ].filter(Boolean).join(" ")
75
76
  }
@@ -417,7 +418,10 @@ import {
417
418
  verifySignature,
418
419
  WebhooksCCError,
419
420
  extractCode,
420
- extractLink
421
+ extractLink,
422
+ detectWebhookInfo,
423
+ generateRequestSchema,
424
+ PAYLOAD_SCHEMA_FORMATS
421
425
  } from "@webhooks-cc/sdk";
422
426
  var MAX_BODY_SIZE = MAX_OUTPUT;
423
427
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
@@ -513,6 +517,67 @@ var waitForRequestShape = {
513
517
  timeout: durationOrTimestampSchema.default("30s").describe('How long to wait, for example "30s"'),
514
518
  pollInterval: durationOrTimestampSchema.optional().describe('Interval between polls, for example "500ms" or "1s"')
515
519
  };
520
+ var payloadSchemaShape = {
521
+ endpointSlug: z2.string().describe("The endpoint slug"),
522
+ format: z2.enum(PAYLOAD_SCHEMA_FORMATS).default("typescript").describe(
523
+ 'Output: "typescript" interfaces, "zod" schemas, "pydantic" models, or "json-schema"'
524
+ ),
525
+ name: z2.string().max(100).optional().describe("Root type name (default: from the detected event, else WebhookPayload)"),
526
+ event: z2.string().optional().describe(
527
+ 'Only requests whose detected provider event matches, e.g. "invoice.paid" or "push". One event per schema gives the tightest types.'
528
+ ),
529
+ method: z2.string().optional().describe("Only requests with this HTTP method"),
530
+ path: z2.string().optional().describe("Only requests with this path"),
531
+ requestIds: z2.array(z2.string()).min(1).max(50).optional().describe("Use exactly these requests instead of the latest ones"),
532
+ limit: z2.number().int().min(1).max(100).default(20).describe("How many of the latest requests to read before filtering")
533
+ };
534
+ function eventName(event) {
535
+ const words = event.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);
536
+ const name = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
537
+ if (!name) return "WebhookPayload";
538
+ return /^[0-9]/.test(name) ? `Webhook${name}Event` : `${name}Event`;
539
+ }
540
+ function payloadSchemaResult(requests, args) {
541
+ const method = args.method?.toUpperCase();
542
+ const matching = requests.filter(
543
+ (request) => request.kind !== "email" && (!method || request.method.toUpperCase() === method) && (!args.path || request.path === args.path) && (!args.event || detectWebhookInfo(request)?.event === args.event)
544
+ );
545
+ if (matching.length === 0) {
546
+ throw new Error(
547
+ `None of the ${requests.length} requests read matched. Capture a request first, or loosen event, method or path.`
548
+ );
549
+ }
550
+ const events = [
551
+ ...new Set(
552
+ matching.map((request) => detectWebhookInfo(request)?.event).filter((e) => !!e)
553
+ )
554
+ ];
555
+ const name = args.name ?? (events.length === 1 ? eventName(events[0]) : void 0);
556
+ const result = generateRequestSchema(matching, { format: args.format, name });
557
+ if (result.samples === 0) {
558
+ throw new Error(
559
+ `None of the ${matching.length} matching requests has a JSON or form body to learn from.`
560
+ );
561
+ }
562
+ const output = {
563
+ format: result.format,
564
+ name: result.name,
565
+ samples: result.samples,
566
+ skipped: result.skipped,
567
+ ...events.length > 0 ? { events } : {},
568
+ ...events.length > 1 ? {
569
+ warning: `The samples mix ${events.length} events, so fields of one event show up as optional. Pass event for one schema per event.`
570
+ } : {},
571
+ code: result.code
572
+ };
573
+ const size = JSON.stringify(output, null, 2).length;
574
+ if (size > MAX_OUTPUT) {
575
+ throw new Error(
576
+ `The schema is ${result.code.length} characters, more than this tool can return. Narrow it with event or requestIds, or call generateRequestSchema() from @webhooks-cc/sdk in code.`
577
+ );
578
+ }
579
+ return jsonContent(output);
580
+ }
516
581
  function textContent(text) {
517
582
  return { content: [{ type: "text", text }] };
518
583
  }
@@ -1209,6 +1274,15 @@ function registerTools(server, client) {
1209
1274
  return jsonContent(diff);
1210
1275
  })
1211
1276
  );
1277
+ server.tool(
1278
+ "generate_payload_schema",
1279
+ "Generate TypeScript interfaces, Zod schemas, Pydantic models, or JSON Schema from captured request bodies. Several requests of one event teach which fields are optional or nullable, so filter with event (or pass requestIds) and write the result into the codebase instead of hand-typing the payload.",
1280
+ payloadSchemaShape,
1281
+ withErrorHandling(async ({ endpointSlug, requestIds, limit, ...args }) => {
1282
+ const requests = requestIds ? await Promise.all(requestIds.map((id) => client.requests.get(id))) : await client.requests.list(endpointSlug, { limit, kind: "http" });
1283
+ return payloadSchemaResult(requests, args);
1284
+ })
1285
+ );
1212
1286
  server.tool(
1213
1287
  "extract_from_request",
1214
1288
  "Extract specific JSON fields from a captured request body.",
@@ -1899,6 +1973,18 @@ function registerSandboxTools(server, options = {}) {
1899
1973
  })
1900
1974
  )
1901
1975
  );
1976
+ registered.push(
1977
+ server.tool(
1978
+ "generate_payload_schema",
1979
+ "Generate TypeScript interfaces, Zod schemas, Pydantic models, or JSON Schema from the bodies of requests captured by a sandbox endpoint. Several requests of one event teach which fields are optional or nullable.",
1980
+ payloadSchemaShape,
1981
+ withErrorHandling(async ({ endpointSlug, requestIds, limit, ...args }) => {
1982
+ const sandbox = await session.requireSandbox();
1983
+ const requests = requestIds ? await Promise.all(requestIds.map((id) => sandbox.requests.get(id))) : await sandbox.requests.list(endpointSlug, { limit });
1984
+ return payloadSchemaResult(requests, args);
1985
+ })
1986
+ )
1987
+ );
1902
1988
  registered.push(
1903
1989
  server.tool(
1904
1990
  "connect_account",
@@ -2057,7 +2143,7 @@ function registerAgentRegistrationTools(server) {
2057
2143
  }
2058
2144
 
2059
2145
  // src/index.ts
2060
- var VERSION = true ? "1.12.1" : "0.0.0-dev";
2146
+ var VERSION = true ? "1.13.0" : "0.0.0-dev";
2061
2147
  function createServer(options = {}) {
2062
2148
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
2063
2149
  const webhookUrl = options.webhookUrl ?? process.env.WHK_WEBHOOK_URL;
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@webhooks-cc/mcp",
3
- "version": "1.12.1",
3
+ "version": "1.13.0",
4
+ "mcpName": "io.github.kroqdotdev/webhooks-cc",
4
5
  "description": "MCP server for webhooks.cc — AI agent integration for webhook testing",
5
6
  "main": "dist/index.js",
6
7
  "module": "dist/index.mjs",
@@ -31,20 +32,20 @@
31
32
  "license": "MIT",
32
33
  "repository": {
33
34
  "type": "git",
34
- "url": "https://github.com/webhooks-cc/webhooks-cc",
35
+ "url": "https://github.com/kroqdotdev/webhooks-cc",
35
36
  "directory": "packages/mcp"
36
37
  },
37
38
  "homepage": "https://webhooks.cc",
38
39
  "dependencies": {
39
- "@modelcontextprotocol/sdk": "^1.31.0",
40
- "zod": "^4.6.5",
41
- "@webhooks-cc/sdk": "1.15.0"
40
+ "@modelcontextprotocol/sdk": "^1.32.0",
41
+ "zod": "^4.6.7",
42
+ "@webhooks-cc/sdk": "1.17.0"
42
43
  },
43
44
  "devDependencies": {
44
- "@types/node": "^26.6.4",
45
- "tsup": "^8.5.1",
45
+ "@types/node": "^26.6.5",
46
+ "tsup": "^9.0.0",
46
47
  "typescript": "^6.0.3",
47
- "vitest": "^5.0.2"
48
+ "vitest": "^5.0.5"
48
49
  },
49
50
  "scripts": {
50
51
  "build": "tsup",