@webhooks-cc/mcp 1.0.1 → 1.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/dist/bin/mcp.js CHANGED
@@ -245,20 +245,6 @@ var import_zod2 = require("zod");
245
245
  var import_sdk = require("@webhooks-cc/sdk");
246
246
  var MAX_BODY_SIZE = 32768;
247
247
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
248
- var TEMPLATE_PROVIDER_VALUES = [
249
- "stripe",
250
- "github",
251
- "shopify",
252
- "twilio",
253
- "slack",
254
- "paddle",
255
- "linear",
256
- "standard-webhooks"
257
- ];
258
- var VERIFY_PROVIDER_VALUES = [
259
- ...TEMPLATE_PROVIDER_VALUES,
260
- "discord"
261
- ];
262
248
  var TIME_SEPARATOR = " \u2014 ";
263
249
  var httpUrlSchema = import_zod2.z.string().url().refine(
264
250
  (value) => {
@@ -273,11 +259,43 @@ var httpUrlSchema = import_zod2.z.string().url().refine(
273
259
  );
274
260
  var methodSchema = import_zod2.z.enum(HTTP_METHODS).default("POST").describe("HTTP method (default: POST)");
275
261
  var durationOrTimestampSchema = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]);
262
+ var ruleConditionSchema = import_zod2.z.object({
263
+ field: import_zod2.z.enum(["method", "path", "header", "body_contains", "body_path", "query"]).describe("Request field to match against"),
264
+ op: import_zod2.z.enum(["eq", "contains", "starts_with", "matches", "exists"]).describe("Comparison operator"),
265
+ value: import_zod2.z.string().max(import_sdk.MAX_CONDITION_VALUE_LEN).optional().describe(
266
+ `Value to compare against (required unless op is "exists", max ${import_sdk.MAX_CONDITION_VALUE_LEN} chars)`
267
+ ),
268
+ name: import_zod2.z.string().max(import_sdk.MAX_CONDITION_NAME_LEN).optional().describe(
269
+ `Header name or query param name (required for header/query, max ${import_sdk.MAX_CONDITION_NAME_LEN} chars)`
270
+ ),
271
+ path: import_zod2.z.string().max(import_sdk.MAX_CONDITION_PATH_LEN).optional().describe(
272
+ `JSON dot-notation path (required for body_path, max ${import_sdk.MAX_CONDITION_PATH_LEN} chars)`
273
+ )
274
+ }).refine((c) => c.op === "exists" || c.value !== void 0 && c.value.length > 0, {
275
+ message: 'value is required when op is not "exists"'
276
+ }).refine(
277
+ (c) => !(c.field === "header" || c.field === "query") || c.name !== void 0 && c.name.length > 0,
278
+ { message: "name is required for header and query conditions" }
279
+ ).refine((c) => c.field !== "body_path" || c.path !== void 0 && c.path.length > 0, {
280
+ message: "path is required for body_path conditions"
281
+ }).refine((c) => c.op !== "matches" || !c.value || c.value.length <= import_sdk.MAX_GLOB_PATTERN_LEN, {
282
+ message: `matches pattern must be ${import_sdk.MAX_GLOB_PATTERN_LEN} chars or less`
283
+ });
284
+ var responseRuleSchema = import_zod2.z.object({
285
+ name: import_zod2.z.string().max(import_sdk.MAX_RULE_NAME_LEN).optional().describe(`Human-readable rule name (max ${import_sdk.MAX_RULE_NAME_LEN} chars)`),
286
+ enabled: import_zod2.z.boolean().optional().default(true).describe("Whether this rule is active"),
287
+ logic: import_zod2.z.enum(["and", "or"]).optional().default("and").describe('How to combine conditions: "and" (all match) or "or" (any match)'),
288
+ conditions: import_zod2.z.array(ruleConditionSchema).min(1).max(10).describe("Conditions to evaluate (1-10)"),
289
+ response: import_zod2.z.lazy(() => mockResponseSchema).describe("Response when conditions match")
290
+ });
291
+ var responseRulesSchema = import_zod2.z.array(responseRuleSchema).max(50).describe("Ordered conditional response rules (first match wins, max 50)");
276
292
  var mockResponseSchema = import_zod2.z.object({
277
- status: import_zod2.z.number().int().min(100).max(599).describe("HTTP status code (100-599)"),
293
+ status: import_zod2.z.number().int().min(import_sdk.MOCK_RESPONSE_STATUS_MIN).max(import_sdk.MOCK_RESPONSE_STATUS_MAX).describe(`HTTP status code (${import_sdk.MOCK_RESPONSE_STATUS_MIN}-${import_sdk.MOCK_RESPONSE_STATUS_MAX})`),
278
294
  body: import_zod2.z.string().default("").describe("Response body string (default: empty)"),
279
295
  headers: import_zod2.z.record(import_zod2.z.string()).default({}).describe("Response headers (default: none)"),
280
- delay: import_zod2.z.number().int().min(0).max(3e4).optional().describe("Response delay in milliseconds (0-30000, default: none)")
296
+ delay: import_zod2.z.number().int().min(import_sdk.MOCK_RESPONSE_DELAY_MIN).max(import_sdk.MOCK_RESPONSE_DELAY_MAX).optional().describe(
297
+ `Response delay in milliseconds (${import_sdk.MOCK_RESPONSE_DELAY_MIN}-${import_sdk.MOCK_RESPONSE_DELAY_MAX}, default: none)`
298
+ )
281
299
  });
282
300
  function textContent(text) {
283
301
  return { content: [{ type: "text", text }] };
@@ -474,12 +492,27 @@ function registerTools(server, client) {
474
492
  name: import_zod2.z.string().optional().describe("Display name for the endpoint"),
475
493
  ephemeral: import_zod2.z.boolean().optional().describe("Create a temporary endpoint that auto-expires"),
476
494
  expiresIn: durationOrTimestampSchema.optional().describe('Auto-expire after this duration, for example "12h"'),
477
- mockResponse: mockResponseSchema.optional().describe("Optional mock response to return when the endpoint receives a request")
495
+ mockResponse: mockResponseSchema.optional().describe("Optional mock response to return when the endpoint receives a request"),
496
+ responseRules: responseRulesSchema.optional().describe(
497
+ "Conditional response rules. Each rule has conditions and a response. First matching rule wins."
498
+ ),
499
+ notificationUrl: import_zod2.z.string().url().optional().describe(
500
+ "URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook)"
501
+ )
478
502
  },
479
- withErrorHandling(async ({ name, ephemeral, expiresIn, mockResponse }) => {
480
- const endpoint = await client.endpoints.create({ name, ephemeral, expiresIn, mockResponse });
481
- return jsonContent(endpoint);
482
- })
503
+ withErrorHandling(
504
+ async ({ name, ephemeral, expiresIn, mockResponse, responseRules, notificationUrl }) => {
505
+ const endpoint = await client.endpoints.create({
506
+ name,
507
+ ephemeral,
508
+ expiresIn,
509
+ mockResponse,
510
+ responseRules,
511
+ notificationUrl
512
+ });
513
+ return jsonContent(endpoint);
514
+ }
515
+ )
483
516
  );
484
517
  server.tool(
485
518
  "list_endpoints",
@@ -501,14 +534,21 @@ function registerTools(server, client) {
501
534
  );
502
535
  server.tool(
503
536
  "update_endpoint",
504
- "Update an endpoint name or mock response configuration.",
537
+ "Update an endpoint name, mock response, or conditional response rules.",
505
538
  {
506
539
  slug: import_zod2.z.string().describe("The endpoint slug to update"),
507
540
  name: import_zod2.z.string().optional().describe("New display name"),
508
- mockResponse: mockResponseSchema.nullable().optional().describe("Mock response config, or null to clear it")
541
+ mockResponse: mockResponseSchema.nullable().optional().describe("Default mock response (used when no rule matches), or null to clear"),
542
+ responseRules: responseRulesSchema.nullable().optional().describe("Conditional response rules (first match wins), or null to clear"),
543
+ notificationUrl: import_zod2.z.string().url().nullable().optional().describe("Notification webhook URL, or null to clear it")
509
544
  },
510
- withErrorHandling(async ({ slug, name, mockResponse }) => {
511
- const endpoint = await client.endpoints.update(slug, { name, mockResponse });
545
+ withErrorHandling(async ({ slug, name, mockResponse, responseRules, notificationUrl }) => {
546
+ const endpoint = await client.endpoints.update(slug, {
547
+ name,
548
+ mockResponse,
549
+ responseRules,
550
+ notificationUrl
551
+ });
512
552
  return jsonContent(endpoint);
513
553
  })
514
554
  );
@@ -580,7 +620,7 @@ function registerTools(server, client) {
580
620
  method: methodSchema,
581
621
  headers: import_zod2.z.record(import_zod2.z.string()).optional().describe("HTTP headers to include"),
582
622
  body: import_zod2.z.unknown().optional().describe("Request body"),
583
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template to send with signed headers"),
623
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to send with signed headers"),
584
624
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
585
625
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
586
626
  secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
@@ -765,7 +805,7 @@ function registerTools(server, client) {
765
805
  "Verify the webhook signature on a captured request.",
766
806
  {
767
807
  requestId: import_zod2.z.string().describe("The captured request ID"),
768
- provider: import_zod2.z.enum(VERIFY_PROVIDER_VALUES).describe("Provider whose signature scheme should be verified"),
808
+ provider: import_zod2.z.enum(import_sdk.VERIFY_PROVIDERS).describe("Provider whose signature scheme should be verified"),
769
809
  secret: import_zod2.z.string().optional().describe("Shared signing secret. Required for non-Discord providers."),
770
810
  publicKey: import_zod2.z.string().optional().describe("Discord application public key. Required for provider=discord."),
771
811
  url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio verification.")
@@ -800,7 +840,7 @@ function registerTools(server, client) {
800
840
  method: methodSchema,
801
841
  headers: import_zod2.z.record(import_zod2.z.string()).optional().describe("HTTP headers to include"),
802
842
  body: import_zod2.z.unknown().optional().describe("Request body"),
803
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template for signing"),
843
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
804
844
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
805
845
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
806
846
  secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
@@ -831,7 +871,7 @@ function registerTools(server, client) {
831
871
  method: methodSchema,
832
872
  headers: import_zod2.z.record(import_zod2.z.string()).optional().describe("HTTP headers to include"),
833
873
  body: import_zod2.z.unknown().optional().describe("Request body"),
834
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template for signing"),
874
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
835
875
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
836
876
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
837
877
  secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
@@ -853,7 +893,7 @@ function registerTools(server, client) {
853
893
  "list_provider_templates",
854
894
  "List supported webhook providers, templates, and signing metadata.",
855
895
  {
856
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Filter to a single provider")
896
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Filter to a single provider")
857
897
  },
858
898
  withErrorHandling(async ({ provider }) => {
859
899
  if (provider) {
@@ -880,7 +920,7 @@ function registerTools(server, client) {
880
920
  "test_webhook_flow",
881
921
  "Run a full webhook test flow: create endpoint, optionally mock, send, wait, verify, replay, and clean up.",
882
922
  {
883
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template to use when sending the webhook"),
923
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to use when sending the webhook"),
884
924
  event: import_zod2.z.string().optional().describe("Optional provider event or topic name"),
885
925
  secret: import_zod2.z.string().optional().describe(
886
926
  "Signing secret. Required when provider is set or signature verification is enabled."
@@ -921,6 +961,11 @@ function registerTools(server, client) {
921
961
  secret: templateSecret
922
962
  });
923
963
  if (shouldVerify) {
964
+ if (provider === "discord") {
965
+ throw new Error(
966
+ "test_webhook_flow cannot verify Discord signatures (Ed25519 requires a public key, not a secret)"
967
+ );
968
+ }
924
969
  flow.verifySignature({
925
970
  provider,
926
971
  secret: templateSecret
@@ -961,7 +1006,7 @@ function registerTools(server, client) {
961
1006
  }
962
1007
 
963
1008
  // src/index.ts
964
- var VERSION = true ? "1.0.1" : "0.0.0-dev";
1009
+ var VERSION = true ? "1.2.0" : "0.0.0-dev";
965
1010
  function createServer(options = {}) {
966
1011
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
967
1012
  if (!apiKey) {
package/dist/index.js CHANGED
@@ -266,20 +266,6 @@ var import_zod2 = require("zod");
266
266
  var import_sdk = require("@webhooks-cc/sdk");
267
267
  var MAX_BODY_SIZE = 32768;
268
268
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
269
- var TEMPLATE_PROVIDER_VALUES = [
270
- "stripe",
271
- "github",
272
- "shopify",
273
- "twilio",
274
- "slack",
275
- "paddle",
276
- "linear",
277
- "standard-webhooks"
278
- ];
279
- var VERIFY_PROVIDER_VALUES = [
280
- ...TEMPLATE_PROVIDER_VALUES,
281
- "discord"
282
- ];
283
269
  var TIME_SEPARATOR = " \u2014 ";
284
270
  var httpUrlSchema = import_zod2.z.string().url().refine(
285
271
  (value) => {
@@ -294,11 +280,43 @@ var httpUrlSchema = import_zod2.z.string().url().refine(
294
280
  );
295
281
  var methodSchema = import_zod2.z.enum(HTTP_METHODS).default("POST").describe("HTTP method (default: POST)");
296
282
  var durationOrTimestampSchema = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]);
283
+ var ruleConditionSchema = import_zod2.z.object({
284
+ field: import_zod2.z.enum(["method", "path", "header", "body_contains", "body_path", "query"]).describe("Request field to match against"),
285
+ op: import_zod2.z.enum(["eq", "contains", "starts_with", "matches", "exists"]).describe("Comparison operator"),
286
+ value: import_zod2.z.string().max(import_sdk.MAX_CONDITION_VALUE_LEN).optional().describe(
287
+ `Value to compare against (required unless op is "exists", max ${import_sdk.MAX_CONDITION_VALUE_LEN} chars)`
288
+ ),
289
+ name: import_zod2.z.string().max(import_sdk.MAX_CONDITION_NAME_LEN).optional().describe(
290
+ `Header name or query param name (required for header/query, max ${import_sdk.MAX_CONDITION_NAME_LEN} chars)`
291
+ ),
292
+ path: import_zod2.z.string().max(import_sdk.MAX_CONDITION_PATH_LEN).optional().describe(
293
+ `JSON dot-notation path (required for body_path, max ${import_sdk.MAX_CONDITION_PATH_LEN} chars)`
294
+ )
295
+ }).refine((c) => c.op === "exists" || c.value !== void 0 && c.value.length > 0, {
296
+ message: 'value is required when op is not "exists"'
297
+ }).refine(
298
+ (c) => !(c.field === "header" || c.field === "query") || c.name !== void 0 && c.name.length > 0,
299
+ { message: "name is required for header and query conditions" }
300
+ ).refine((c) => c.field !== "body_path" || c.path !== void 0 && c.path.length > 0, {
301
+ message: "path is required for body_path conditions"
302
+ }).refine((c) => c.op !== "matches" || !c.value || c.value.length <= import_sdk.MAX_GLOB_PATTERN_LEN, {
303
+ message: `matches pattern must be ${import_sdk.MAX_GLOB_PATTERN_LEN} chars or less`
304
+ });
305
+ var responseRuleSchema = import_zod2.z.object({
306
+ name: import_zod2.z.string().max(import_sdk.MAX_RULE_NAME_LEN).optional().describe(`Human-readable rule name (max ${import_sdk.MAX_RULE_NAME_LEN} chars)`),
307
+ enabled: import_zod2.z.boolean().optional().default(true).describe("Whether this rule is active"),
308
+ logic: import_zod2.z.enum(["and", "or"]).optional().default("and").describe('How to combine conditions: "and" (all match) or "or" (any match)'),
309
+ conditions: import_zod2.z.array(ruleConditionSchema).min(1).max(10).describe("Conditions to evaluate (1-10)"),
310
+ response: import_zod2.z.lazy(() => mockResponseSchema).describe("Response when conditions match")
311
+ });
312
+ var responseRulesSchema = import_zod2.z.array(responseRuleSchema).max(50).describe("Ordered conditional response rules (first match wins, max 50)");
297
313
  var mockResponseSchema = import_zod2.z.object({
298
- status: import_zod2.z.number().int().min(100).max(599).describe("HTTP status code (100-599)"),
314
+ status: import_zod2.z.number().int().min(import_sdk.MOCK_RESPONSE_STATUS_MIN).max(import_sdk.MOCK_RESPONSE_STATUS_MAX).describe(`HTTP status code (${import_sdk.MOCK_RESPONSE_STATUS_MIN}-${import_sdk.MOCK_RESPONSE_STATUS_MAX})`),
299
315
  body: import_zod2.z.string().default("").describe("Response body string (default: empty)"),
300
316
  headers: import_zod2.z.record(import_zod2.z.string()).default({}).describe("Response headers (default: none)"),
301
- delay: import_zod2.z.number().int().min(0).max(3e4).optional().describe("Response delay in milliseconds (0-30000, default: none)")
317
+ delay: import_zod2.z.number().int().min(import_sdk.MOCK_RESPONSE_DELAY_MIN).max(import_sdk.MOCK_RESPONSE_DELAY_MAX).optional().describe(
318
+ `Response delay in milliseconds (${import_sdk.MOCK_RESPONSE_DELAY_MIN}-${import_sdk.MOCK_RESPONSE_DELAY_MAX}, default: none)`
319
+ )
302
320
  });
303
321
  function textContent(text) {
304
322
  return { content: [{ type: "text", text }] };
@@ -495,12 +513,27 @@ function registerTools(server, client) {
495
513
  name: import_zod2.z.string().optional().describe("Display name for the endpoint"),
496
514
  ephemeral: import_zod2.z.boolean().optional().describe("Create a temporary endpoint that auto-expires"),
497
515
  expiresIn: durationOrTimestampSchema.optional().describe('Auto-expire after this duration, for example "12h"'),
498
- mockResponse: mockResponseSchema.optional().describe("Optional mock response to return when the endpoint receives a request")
516
+ mockResponse: mockResponseSchema.optional().describe("Optional mock response to return when the endpoint receives a request"),
517
+ responseRules: responseRulesSchema.optional().describe(
518
+ "Conditional response rules. Each rule has conditions and a response. First matching rule wins."
519
+ ),
520
+ notificationUrl: import_zod2.z.string().url().optional().describe(
521
+ "URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook)"
522
+ )
499
523
  },
500
- withErrorHandling(async ({ name, ephemeral, expiresIn, mockResponse }) => {
501
- const endpoint = await client.endpoints.create({ name, ephemeral, expiresIn, mockResponse });
502
- return jsonContent(endpoint);
503
- })
524
+ withErrorHandling(
525
+ async ({ name, ephemeral, expiresIn, mockResponse, responseRules, notificationUrl }) => {
526
+ const endpoint = await client.endpoints.create({
527
+ name,
528
+ ephemeral,
529
+ expiresIn,
530
+ mockResponse,
531
+ responseRules,
532
+ notificationUrl
533
+ });
534
+ return jsonContent(endpoint);
535
+ }
536
+ )
504
537
  );
505
538
  server.tool(
506
539
  "list_endpoints",
@@ -522,14 +555,21 @@ function registerTools(server, client) {
522
555
  );
523
556
  server.tool(
524
557
  "update_endpoint",
525
- "Update an endpoint name or mock response configuration.",
558
+ "Update an endpoint name, mock response, or conditional response rules.",
526
559
  {
527
560
  slug: import_zod2.z.string().describe("The endpoint slug to update"),
528
561
  name: import_zod2.z.string().optional().describe("New display name"),
529
- mockResponse: mockResponseSchema.nullable().optional().describe("Mock response config, or null to clear it")
562
+ mockResponse: mockResponseSchema.nullable().optional().describe("Default mock response (used when no rule matches), or null to clear"),
563
+ responseRules: responseRulesSchema.nullable().optional().describe("Conditional response rules (first match wins), or null to clear"),
564
+ notificationUrl: import_zod2.z.string().url().nullable().optional().describe("Notification webhook URL, or null to clear it")
530
565
  },
531
- withErrorHandling(async ({ slug, name, mockResponse }) => {
532
- const endpoint = await client.endpoints.update(slug, { name, mockResponse });
566
+ withErrorHandling(async ({ slug, name, mockResponse, responseRules, notificationUrl }) => {
567
+ const endpoint = await client.endpoints.update(slug, {
568
+ name,
569
+ mockResponse,
570
+ responseRules,
571
+ notificationUrl
572
+ });
533
573
  return jsonContent(endpoint);
534
574
  })
535
575
  );
@@ -601,7 +641,7 @@ function registerTools(server, client) {
601
641
  method: methodSchema,
602
642
  headers: import_zod2.z.record(import_zod2.z.string()).optional().describe("HTTP headers to include"),
603
643
  body: import_zod2.z.unknown().optional().describe("Request body"),
604
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template to send with signed headers"),
644
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to send with signed headers"),
605
645
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
606
646
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
607
647
  secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
@@ -786,7 +826,7 @@ function registerTools(server, client) {
786
826
  "Verify the webhook signature on a captured request.",
787
827
  {
788
828
  requestId: import_zod2.z.string().describe("The captured request ID"),
789
- provider: import_zod2.z.enum(VERIFY_PROVIDER_VALUES).describe("Provider whose signature scheme should be verified"),
829
+ provider: import_zod2.z.enum(import_sdk.VERIFY_PROVIDERS).describe("Provider whose signature scheme should be verified"),
790
830
  secret: import_zod2.z.string().optional().describe("Shared signing secret. Required for non-Discord providers."),
791
831
  publicKey: import_zod2.z.string().optional().describe("Discord application public key. Required for provider=discord."),
792
832
  url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio verification.")
@@ -821,7 +861,7 @@ function registerTools(server, client) {
821
861
  method: methodSchema,
822
862
  headers: import_zod2.z.record(import_zod2.z.string()).optional().describe("HTTP headers to include"),
823
863
  body: import_zod2.z.unknown().optional().describe("Request body"),
824
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template for signing"),
864
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
825
865
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
826
866
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
827
867
  secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
@@ -852,7 +892,7 @@ function registerTools(server, client) {
852
892
  method: methodSchema,
853
893
  headers: import_zod2.z.record(import_zod2.z.string()).optional().describe("HTTP headers to include"),
854
894
  body: import_zod2.z.unknown().optional().describe("Request body"),
855
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template for signing"),
895
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
856
896
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
857
897
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
858
898
  secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
@@ -874,7 +914,7 @@ function registerTools(server, client) {
874
914
  "list_provider_templates",
875
915
  "List supported webhook providers, templates, and signing metadata.",
876
916
  {
877
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Filter to a single provider")
917
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Filter to a single provider")
878
918
  },
879
919
  withErrorHandling(async ({ provider }) => {
880
920
  if (provider) {
@@ -901,7 +941,7 @@ function registerTools(server, client) {
901
941
  "test_webhook_flow",
902
942
  "Run a full webhook test flow: create endpoint, optionally mock, send, wait, verify, replay, and clean up.",
903
943
  {
904
- provider: import_zod2.z.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template to use when sending the webhook"),
944
+ provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to use when sending the webhook"),
905
945
  event: import_zod2.z.string().optional().describe("Optional provider event or topic name"),
906
946
  secret: import_zod2.z.string().optional().describe(
907
947
  "Signing secret. Required when provider is set or signature verification is enabled."
@@ -942,6 +982,11 @@ function registerTools(server, client) {
942
982
  secret: templateSecret
943
983
  });
944
984
  if (shouldVerify) {
985
+ if (provider === "discord") {
986
+ throw new Error(
987
+ "test_webhook_flow cannot verify Discord signatures (Ed25519 requires a public key, not a secret)"
988
+ );
989
+ }
945
990
  flow.verifySignature({
946
991
  provider,
947
992
  secret: templateSecret
@@ -982,7 +1027,7 @@ function registerTools(server, client) {
982
1027
  }
983
1028
 
984
1029
  // src/index.ts
985
- var VERSION = true ? "1.0.1" : "0.0.0-dev";
1030
+ var VERSION = true ? "1.2.0" : "0.0.0-dev";
986
1031
  function createServer(options = {}) {
987
1032
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
988
1033
  if (!apiKey) {
package/dist/index.mjs CHANGED
@@ -239,29 +239,26 @@ import { z as z2 } from "zod";
239
239
  import {
240
240
  diffRequests,
241
241
  extractJsonField,
242
+ MOCK_RESPONSE_DELAY_MAX,
243
+ MOCK_RESPONSE_DELAY_MIN,
244
+ MOCK_RESPONSE_STATUS_MAX,
245
+ MOCK_RESPONSE_STATUS_MIN,
246
+ MAX_CONDITION_VALUE_LEN,
247
+ MAX_CONDITION_NAME_LEN,
248
+ MAX_CONDITION_PATH_LEN,
249
+ MAX_RULE_NAME_LEN,
250
+ MAX_GLOB_PATTERN_LEN,
242
251
  NotFoundError,
243
252
  RateLimitError,
253
+ TEMPLATE_PROVIDERS,
244
254
  TimeoutError,
245
255
  UnauthorizedError,
256
+ VERIFY_PROVIDERS,
246
257
  verifySignature,
247
258
  WebhooksCCError
248
259
  } from "@webhooks-cc/sdk";
249
260
  var MAX_BODY_SIZE = 32768;
250
261
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
251
- var TEMPLATE_PROVIDER_VALUES = [
252
- "stripe",
253
- "github",
254
- "shopify",
255
- "twilio",
256
- "slack",
257
- "paddle",
258
- "linear",
259
- "standard-webhooks"
260
- ];
261
- var VERIFY_PROVIDER_VALUES = [
262
- ...TEMPLATE_PROVIDER_VALUES,
263
- "discord"
264
- ];
265
262
  var TIME_SEPARATOR = " \u2014 ";
266
263
  var httpUrlSchema = z2.string().url().refine(
267
264
  (value) => {
@@ -276,11 +273,43 @@ var httpUrlSchema = z2.string().url().refine(
276
273
  );
277
274
  var methodSchema = z2.enum(HTTP_METHODS).default("POST").describe("HTTP method (default: POST)");
278
275
  var durationOrTimestampSchema = z2.union([z2.string(), z2.number()]);
276
+ var ruleConditionSchema = z2.object({
277
+ field: z2.enum(["method", "path", "header", "body_contains", "body_path", "query"]).describe("Request field to match against"),
278
+ op: z2.enum(["eq", "contains", "starts_with", "matches", "exists"]).describe("Comparison operator"),
279
+ value: z2.string().max(MAX_CONDITION_VALUE_LEN).optional().describe(
280
+ `Value to compare against (required unless op is "exists", max ${MAX_CONDITION_VALUE_LEN} chars)`
281
+ ),
282
+ name: z2.string().max(MAX_CONDITION_NAME_LEN).optional().describe(
283
+ `Header name or query param name (required for header/query, max ${MAX_CONDITION_NAME_LEN} chars)`
284
+ ),
285
+ path: z2.string().max(MAX_CONDITION_PATH_LEN).optional().describe(
286
+ `JSON dot-notation path (required for body_path, max ${MAX_CONDITION_PATH_LEN} chars)`
287
+ )
288
+ }).refine((c) => c.op === "exists" || c.value !== void 0 && c.value.length > 0, {
289
+ message: 'value is required when op is not "exists"'
290
+ }).refine(
291
+ (c) => !(c.field === "header" || c.field === "query") || c.name !== void 0 && c.name.length > 0,
292
+ { message: "name is required for header and query conditions" }
293
+ ).refine((c) => c.field !== "body_path" || c.path !== void 0 && c.path.length > 0, {
294
+ message: "path is required for body_path conditions"
295
+ }).refine((c) => c.op !== "matches" || !c.value || c.value.length <= MAX_GLOB_PATTERN_LEN, {
296
+ message: `matches pattern must be ${MAX_GLOB_PATTERN_LEN} chars or less`
297
+ });
298
+ var responseRuleSchema = z2.object({
299
+ name: z2.string().max(MAX_RULE_NAME_LEN).optional().describe(`Human-readable rule name (max ${MAX_RULE_NAME_LEN} chars)`),
300
+ enabled: z2.boolean().optional().default(true).describe("Whether this rule is active"),
301
+ logic: z2.enum(["and", "or"]).optional().default("and").describe('How to combine conditions: "and" (all match) or "or" (any match)'),
302
+ conditions: z2.array(ruleConditionSchema).min(1).max(10).describe("Conditions to evaluate (1-10)"),
303
+ response: z2.lazy(() => mockResponseSchema).describe("Response when conditions match")
304
+ });
305
+ var responseRulesSchema = z2.array(responseRuleSchema).max(50).describe("Ordered conditional response rules (first match wins, max 50)");
279
306
  var mockResponseSchema = z2.object({
280
- status: z2.number().int().min(100).max(599).describe("HTTP status code (100-599)"),
307
+ status: z2.number().int().min(MOCK_RESPONSE_STATUS_MIN).max(MOCK_RESPONSE_STATUS_MAX).describe(`HTTP status code (${MOCK_RESPONSE_STATUS_MIN}-${MOCK_RESPONSE_STATUS_MAX})`),
281
308
  body: z2.string().default("").describe("Response body string (default: empty)"),
282
309
  headers: z2.record(z2.string()).default({}).describe("Response headers (default: none)"),
283
- delay: z2.number().int().min(0).max(3e4).optional().describe("Response delay in milliseconds (0-30000, default: none)")
310
+ delay: z2.number().int().min(MOCK_RESPONSE_DELAY_MIN).max(MOCK_RESPONSE_DELAY_MAX).optional().describe(
311
+ `Response delay in milliseconds (${MOCK_RESPONSE_DELAY_MIN}-${MOCK_RESPONSE_DELAY_MAX}, default: none)`
312
+ )
284
313
  });
285
314
  function textContent(text) {
286
315
  return { content: [{ type: "text", text }] };
@@ -477,12 +506,27 @@ function registerTools(server, client) {
477
506
  name: z2.string().optional().describe("Display name for the endpoint"),
478
507
  ephemeral: z2.boolean().optional().describe("Create a temporary endpoint that auto-expires"),
479
508
  expiresIn: durationOrTimestampSchema.optional().describe('Auto-expire after this duration, for example "12h"'),
480
- mockResponse: mockResponseSchema.optional().describe("Optional mock response to return when the endpoint receives a request")
509
+ mockResponse: mockResponseSchema.optional().describe("Optional mock response to return when the endpoint receives a request"),
510
+ responseRules: responseRulesSchema.optional().describe(
511
+ "Conditional response rules. Each rule has conditions and a response. First matching rule wins."
512
+ ),
513
+ notificationUrl: z2.string().url().optional().describe(
514
+ "URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook)"
515
+ )
481
516
  },
482
- withErrorHandling(async ({ name, ephemeral, expiresIn, mockResponse }) => {
483
- const endpoint = await client.endpoints.create({ name, ephemeral, expiresIn, mockResponse });
484
- return jsonContent(endpoint);
485
- })
517
+ withErrorHandling(
518
+ async ({ name, ephemeral, expiresIn, mockResponse, responseRules, notificationUrl }) => {
519
+ const endpoint = await client.endpoints.create({
520
+ name,
521
+ ephemeral,
522
+ expiresIn,
523
+ mockResponse,
524
+ responseRules,
525
+ notificationUrl
526
+ });
527
+ return jsonContent(endpoint);
528
+ }
529
+ )
486
530
  );
487
531
  server.tool(
488
532
  "list_endpoints",
@@ -504,14 +548,21 @@ function registerTools(server, client) {
504
548
  );
505
549
  server.tool(
506
550
  "update_endpoint",
507
- "Update an endpoint name or mock response configuration.",
551
+ "Update an endpoint name, mock response, or conditional response rules.",
508
552
  {
509
553
  slug: z2.string().describe("The endpoint slug to update"),
510
554
  name: z2.string().optional().describe("New display name"),
511
- mockResponse: mockResponseSchema.nullable().optional().describe("Mock response config, or null to clear it")
555
+ mockResponse: mockResponseSchema.nullable().optional().describe("Default mock response (used when no rule matches), or null to clear"),
556
+ responseRules: responseRulesSchema.nullable().optional().describe("Conditional response rules (first match wins), or null to clear"),
557
+ notificationUrl: z2.string().url().nullable().optional().describe("Notification webhook URL, or null to clear it")
512
558
  },
513
- withErrorHandling(async ({ slug, name, mockResponse }) => {
514
- const endpoint = await client.endpoints.update(slug, { name, mockResponse });
559
+ withErrorHandling(async ({ slug, name, mockResponse, responseRules, notificationUrl }) => {
560
+ const endpoint = await client.endpoints.update(slug, {
561
+ name,
562
+ mockResponse,
563
+ responseRules,
564
+ notificationUrl
565
+ });
515
566
  return jsonContent(endpoint);
516
567
  })
517
568
  );
@@ -583,7 +634,7 @@ function registerTools(server, client) {
583
634
  method: methodSchema,
584
635
  headers: z2.record(z2.string()).optional().describe("HTTP headers to include"),
585
636
  body: z2.unknown().optional().describe("Request body"),
586
- provider: z2.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template to send with signed headers"),
637
+ provider: z2.enum(TEMPLATE_PROVIDERS).optional().describe("Optional provider template to send with signed headers"),
587
638
  template: z2.string().optional().describe("Provider-specific template preset"),
588
639
  event: z2.string().optional().describe("Provider event or topic name"),
589
640
  secret: z2.string().optional().describe("Signing secret. Required when provider is set.")
@@ -768,7 +819,7 @@ function registerTools(server, client) {
768
819
  "Verify the webhook signature on a captured request.",
769
820
  {
770
821
  requestId: z2.string().describe("The captured request ID"),
771
- provider: z2.enum(VERIFY_PROVIDER_VALUES).describe("Provider whose signature scheme should be verified"),
822
+ provider: z2.enum(VERIFY_PROVIDERS).describe("Provider whose signature scheme should be verified"),
772
823
  secret: z2.string().optional().describe("Shared signing secret. Required for non-Discord providers."),
773
824
  publicKey: z2.string().optional().describe("Discord application public key. Required for provider=discord."),
774
825
  url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio verification.")
@@ -803,7 +854,7 @@ function registerTools(server, client) {
803
854
  method: methodSchema,
804
855
  headers: z2.record(z2.string()).optional().describe("HTTP headers to include"),
805
856
  body: z2.unknown().optional().describe("Request body"),
806
- provider: z2.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template for signing"),
857
+ provider: z2.enum(TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
807
858
  template: z2.string().optional().describe("Provider-specific template preset"),
808
859
  event: z2.string().optional().describe("Provider event or topic name"),
809
860
  secret: z2.string().optional().describe("Signing secret. Required when provider is set.")
@@ -834,7 +885,7 @@ function registerTools(server, client) {
834
885
  method: methodSchema,
835
886
  headers: z2.record(z2.string()).optional().describe("HTTP headers to include"),
836
887
  body: z2.unknown().optional().describe("Request body"),
837
- provider: z2.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template for signing"),
888
+ provider: z2.enum(TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
838
889
  template: z2.string().optional().describe("Provider-specific template preset"),
839
890
  event: z2.string().optional().describe("Provider event or topic name"),
840
891
  secret: z2.string().optional().describe("Signing secret. Required when provider is set.")
@@ -856,7 +907,7 @@ function registerTools(server, client) {
856
907
  "list_provider_templates",
857
908
  "List supported webhook providers, templates, and signing metadata.",
858
909
  {
859
- provider: z2.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Filter to a single provider")
910
+ provider: z2.enum(TEMPLATE_PROVIDERS).optional().describe("Filter to a single provider")
860
911
  },
861
912
  withErrorHandling(async ({ provider }) => {
862
913
  if (provider) {
@@ -883,7 +934,7 @@ function registerTools(server, client) {
883
934
  "test_webhook_flow",
884
935
  "Run a full webhook test flow: create endpoint, optionally mock, send, wait, verify, replay, and clean up.",
885
936
  {
886
- provider: z2.enum(TEMPLATE_PROVIDER_VALUES).optional().describe("Optional provider template to use when sending the webhook"),
937
+ provider: z2.enum(TEMPLATE_PROVIDERS).optional().describe("Optional provider template to use when sending the webhook"),
887
938
  event: z2.string().optional().describe("Optional provider event or topic name"),
888
939
  secret: z2.string().optional().describe(
889
940
  "Signing secret. Required when provider is set or signature verification is enabled."
@@ -924,6 +975,11 @@ function registerTools(server, client) {
924
975
  secret: templateSecret
925
976
  });
926
977
  if (shouldVerify) {
978
+ if (provider === "discord") {
979
+ throw new Error(
980
+ "test_webhook_flow cannot verify Discord signatures (Ed25519 requires a public key, not a secret)"
981
+ );
982
+ }
927
983
  flow.verifySignature({
928
984
  provider,
929
985
  secret: templateSecret
@@ -964,7 +1020,7 @@ function registerTools(server, client) {
964
1020
  }
965
1021
 
966
1022
  // src/index.ts
967
- var VERSION = true ? "1.0.1" : "0.0.0-dev";
1023
+ var VERSION = true ? "1.2.0" : "0.0.0-dev";
968
1024
  function createServer(options = {}) {
969
1025
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
970
1026
  if (!apiKey) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webhooks-cc/mcp",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "MCP server for webhooks.cc — AI agent integration for webhook testing",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -36,14 +36,15 @@
36
36
  },
37
37
  "homepage": "https://webhooks.cc",
38
38
  "dependencies": {
39
- "@modelcontextprotocol/sdk": "^1.27.1",
39
+ "@modelcontextprotocol/sdk": "^1.29.0",
40
40
  "zod": "^3.25.76",
41
- "@webhooks-cc/sdk": "1.0.1"
41
+ "@webhooks-cc/sdk": "1.3.0"
42
42
  },
43
43
  "devDependencies": {
44
+ "@types/node": "^24.0.0",
44
45
  "tsup": "^8.5.1",
45
- "typescript": "^5.9.3",
46
- "vitest": "^3.2.4"
46
+ "typescript": "^6.0.2",
47
+ "vitest": "^4.1.2"
47
48
  },
48
49
  "scripts": {
49
50
  "build": "tsup",