@webhooks-cc/mcp 1.6.0 → 1.8.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
@@ -58,7 +58,7 @@ Get your API key at [webhooks.cc/account](https://webhooks.cc/account).
58
58
 
59
59
  ## What it exposes
60
60
 
61
- The server exposes `25` tools, `3` prompts, and `3` resource surfaces.
61
+ The server exposes `35` tools, `3` prompts, and `3` resource surfaces.
62
62
 
63
63
  ### Endpoint tools
64
64
 
@@ -91,12 +91,32 @@ The server exposes `25` tools, `3` prompts, and `3` resource surfaces.
91
91
  - `preview_webhook`
92
92
  - `test_webhook_flow`
93
93
 
94
+ ### Team tools
95
+
96
+ - `list_teams`
97
+ - `list_team_members`
98
+ - `share_endpoint`
99
+ - `unshare_endpoint`
100
+
101
+ Endpoints shared with you already appear in `list_endpoints` (with `fromTeam`) and work with every request tool except `clear_requests`, which stays with the owner. Invites are not exposed: accepting one claims a paid seat.
102
+
94
103
  ### Discovery and account tools
95
104
 
96
105
  - `list_provider_templates`
97
- - `get_usage`
106
+ - `get_usage` (personal plan plus each subscribed team's pool)
98
107
  - `describe`
99
108
 
109
+ ### Agent registration tools
110
+
111
+ Registered on every server, with or without an API key, so an agent that has no credential yet can obtain one (auth.md):
112
+
113
+ - `how_to_register`
114
+ - `register_agent`
115
+ - `check_claim`
116
+ - `register_agent_with_email`
117
+ - `verify_agent_otp`
118
+ - `register_agent_with_idjag`
119
+
100
120
  ## Prompts
101
121
 
102
122
  The server exposes MCP prompts for common workflows:
@@ -149,10 +169,14 @@ Provider template and signing support includes:
149
169
  - `mux`
150
170
  - `sentry`
151
171
  - `bitbucket`
172
+ - `docusign`
173
+ - `adyen`
174
+ - `paypal`
175
+ - `plaid`
152
176
 
153
177
  Signature verification also supports:
154
178
 
155
- - all template providers except `sendgrid`, which uses IP allowlisting instead of cryptographic signatures
179
+ - all template providers except `sendgrid` (IP allowlisting) and `plaid` (JWT/JWK verification requires Plaid API credentials)
156
180
 
157
181
  Use `list_provider_templates` to inspect templates, default events, signature headers, and signing requirements from the agent.
158
182
 
package/dist/bin/mcp.js CHANGED
@@ -559,11 +559,62 @@ function registerTools(server, client) {
559
559
  );
560
560
  server.tool(
561
561
  "list_endpoints",
562
- "List all webhook endpoints for the authenticated user.",
562
+ "List webhook endpoints: those you own (with sharedWith) and those shared with you through teams (with fromTeam). Optionally keep only one team's endpoints.",
563
+ {
564
+ team: import_zod2.z.string().min(1).optional().describe(
565
+ "Team id or name (case-insensitive). Keeps endpoints shared with you from that team and endpoints you own that are shared with it."
566
+ )
567
+ },
568
+ withErrorHandling(async ({ team }) => {
569
+ const endpoints = await client.endpoints.list({ team });
570
+ return jsonContent(endpoints);
571
+ })
572
+ );
573
+ server.tool(
574
+ "list_teams",
575
+ "List the teams you own or belong to, with role, seats, pooled request usage (requestsUsed/requestLimit), period end, and whether the team is suspended for lack of a subscription.",
563
576
  {},
564
577
  withErrorHandling(async () => {
565
- const endpoints = await client.endpoints.list();
566
- return jsonContent(endpoints);
578
+ const teams = await client.teams.list();
579
+ return jsonContent(
580
+ teams.map((team) => ({
581
+ ...team,
582
+ periodEnd: team.periodEnd ? new Date(team.periodEnd).toISOString() : null
583
+ }))
584
+ );
585
+ })
586
+ );
587
+ server.tool(
588
+ "list_team_members",
589
+ "List a team's members and pending invites. Any member of the team may call this.",
590
+ { teamId: import_zod2.z.string().min(1).describe("Team id (from list_teams)") },
591
+ withErrorHandling(async ({ teamId }) => {
592
+ const result = await client.teams.members(teamId);
593
+ return jsonContent(result);
594
+ })
595
+ );
596
+ server.tool(
597
+ "share_endpoint",
598
+ "Share an endpoint you own with a team so its members can inspect, stream, and edit it. The team needs an active subscription; the endpoint's requests then bill the team's pooled quota.",
599
+ {
600
+ slug: import_zod2.z.string().describe("Slug of an endpoint you own"),
601
+ teamId: import_zod2.z.string().min(1).describe("Team id (from list_teams)")
602
+ },
603
+ withErrorHandling(async ({ slug, teamId }) => {
604
+ await client.teams.share(teamId, slug);
605
+ return jsonContent({ shared: true, slug, teamId });
606
+ })
607
+ );
608
+ server.tool(
609
+ "unshare_endpoint",
610
+ "Stop sharing an endpoint you own with a team. Works even when the team is suspended.",
611
+ {
612
+ slug: import_zod2.z.string().describe("Slug of an endpoint you own"),
613
+ teamId: import_zod2.z.string().min(1).describe("Team id (from list_teams)")
614
+ },
615
+ withErrorHandling(async ({ slug, teamId }) => {
616
+ await client.teams.unshare(teamId, slug);
617
+ return jsonContent({ shared: false, slug, teamId });
567
618
  })
568
619
  );
569
620
  server.tool(
@@ -687,15 +738,17 @@ function registerTools(server, client) {
687
738
  provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to send with signed headers"),
688
739
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
689
740
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
690
- secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
741
+ secret: import_zod2.z.string().optional().describe(
742
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
743
+ )
691
744
  },
692
745
  withErrorHandling(
693
746
  async ({ slug, method, headers, body, provider, template, event, secret }) => {
694
747
  let response;
695
748
  if (provider) {
696
749
  const templateSecret = secret?.trim();
697
- if (!templateSecret) {
698
- throw new Error("send_webhook with provider templates requires a non-empty secret");
750
+ if (!templateSecret && import_sdk.TEMPLATE_METADATA[provider]?.secretRequired !== false) {
751
+ throw new Error(`send_webhook with provider "${provider}" requires a non-empty secret`);
699
752
  }
700
753
  response = await client.endpoints.sendTemplate(slug, {
701
754
  provider,
@@ -871,7 +924,7 @@ function registerTools(server, client) {
871
924
  requestId: import_zod2.z.string().describe("The captured request ID"),
872
925
  provider: import_zod2.z.enum(import_sdk.VERIFY_PROVIDERS).optional().describe("Provider whose signature scheme should be verified. Omit to use stored result."),
873
926
  secret: import_zod2.z.string().optional().describe(
874
- "Shared signing secret. Required for non-Discord providers when doing client-side verification."
927
+ "Provider credential for client-side verification (for example a shared signing secret, Adyen hex HMAC key, or PayPal webhook ID). Required for non-Discord providers."
875
928
  ),
876
929
  publicKey: import_zod2.z.string().optional().describe("Discord application public key. Required for provider=discord."),
877
930
  url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio, Square, and HubSpot verification."),
@@ -943,7 +996,9 @@ function registerTools(server, client) {
943
996
  provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
944
997
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
945
998
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
946
- secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
999
+ secret: import_zod2.z.string().optional().describe(
1000
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
1001
+ )
947
1002
  },
948
1003
  withErrorHandling(async ({ url, method, headers, body, provider, template, event, secret }) => {
949
1004
  const response = await client.sendTo(url, {
@@ -974,7 +1029,9 @@ function registerTools(server, client) {
974
1029
  provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
975
1030
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
976
1031
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
977
- secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
1032
+ secret: import_zod2.z.string().optional().describe(
1033
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
1034
+ )
978
1035
  },
979
1036
  withErrorHandling(async ({ url, method, headers, body, provider, template, event, secret }) => {
980
1037
  const preview = await client.buildRequest(url, {
@@ -1006,13 +1063,31 @@ function registerTools(server, client) {
1006
1063
  );
1007
1064
  server.tool(
1008
1065
  "get_usage",
1009
- "Check current request usage, remaining quota, plan, and period end.",
1066
+ "Check current request usage, remaining quota, plan, and period end, plus the pooled quota of every subscribed team you belong to.",
1010
1067
  {},
1011
1068
  withErrorHandling(async () => {
1012
- const usage = await client.usage();
1069
+ const [usageResult, teamsResult] = await Promise.allSettled([
1070
+ client.usage(),
1071
+ client.teams.list()
1072
+ ]);
1073
+ if (usageResult.status === "rejected") throw usageResult.reason;
1074
+ const usage = usageResult.value;
1075
+ const teams = teamsResult.status === "fulfilled" ? teamsResult.value : [];
1076
+ const teamsError = teamsResult.status === "rejected" ? teamsResult.reason instanceof Error ? teamsResult.reason.message : String(teamsResult.reason) : void 0;
1013
1077
  return jsonContent({
1014
1078
  ...usage,
1015
- periodEnd: usage.periodEnd ? new Date(usage.periodEnd).toISOString() : null
1079
+ periodEnd: usage.periodEnd ? new Date(usage.periodEnd).toISOString() : null,
1080
+ ...teamsError ? { teamsError } : {},
1081
+ teams: teams.filter((team) => !team.suspended).map((team) => ({
1082
+ id: team.id,
1083
+ name: team.name,
1084
+ role: team.role,
1085
+ seats: team.seats,
1086
+ used: team.requestsUsed,
1087
+ limit: team.requestLimit,
1088
+ remaining: Math.max(0, team.requestLimit - team.requestsUsed),
1089
+ periodEnd: team.periodEnd ? new Date(team.periodEnd).toISOString() : null
1090
+ }))
1016
1091
  });
1017
1092
  })
1018
1093
  );
@@ -1050,9 +1125,9 @@ function registerTools(server, client) {
1050
1125
  }
1051
1126
  if (provider) {
1052
1127
  const templateSecret = secret?.trim();
1053
- if (!templateSecret) {
1128
+ if (!templateSecret && import_sdk.TEMPLATE_METADATA[provider]?.secretRequired !== false) {
1054
1129
  throw new Error(
1055
- "test_webhook_flow with provider templates requires a non-empty secret"
1130
+ `test_webhook_flow with provider "${provider}" requires a non-empty secret`
1056
1131
  );
1057
1132
  }
1058
1133
  flow.sendTemplate({
@@ -1066,6 +1141,11 @@ function registerTools(server, client) {
1066
1141
  "test_webhook_flow cannot verify Discord signatures (Ed25519 requires a public key, not a secret)"
1067
1142
  );
1068
1143
  }
1144
+ if (!templateSecret) {
1145
+ throw new Error(
1146
+ `test_webhook_flow cannot verify "${provider}" signatures \u2014 this provider's templates are unsigned`
1147
+ );
1148
+ }
1069
1149
  flow.verifySignature({
1070
1150
  provider,
1071
1151
  secret: templateSecret
@@ -1208,7 +1288,7 @@ function registerAgentRegistrationTools(server) {
1208
1288
  }
1209
1289
 
1210
1290
  // src/index.ts
1211
- var VERSION = true ? "1.6.0" : "0.0.0-dev";
1291
+ var VERSION = true ? "1.8.0" : "0.0.0-dev";
1212
1292
  function createServer(options = {}) {
1213
1293
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
1214
1294
  const server = new import_mcp2.McpServer({
package/dist/index.js CHANGED
@@ -581,11 +581,62 @@ function registerTools(server, client) {
581
581
  );
582
582
  server.tool(
583
583
  "list_endpoints",
584
- "List all webhook endpoints for the authenticated user.",
584
+ "List webhook endpoints: those you own (with sharedWith) and those shared with you through teams (with fromTeam). Optionally keep only one team's endpoints.",
585
+ {
586
+ team: import_zod2.z.string().min(1).optional().describe(
587
+ "Team id or name (case-insensitive). Keeps endpoints shared with you from that team and endpoints you own that are shared with it."
588
+ )
589
+ },
590
+ withErrorHandling(async ({ team }) => {
591
+ const endpoints = await client.endpoints.list({ team });
592
+ return jsonContent(endpoints);
593
+ })
594
+ );
595
+ server.tool(
596
+ "list_teams",
597
+ "List the teams you own or belong to, with role, seats, pooled request usage (requestsUsed/requestLimit), period end, and whether the team is suspended for lack of a subscription.",
585
598
  {},
586
599
  withErrorHandling(async () => {
587
- const endpoints = await client.endpoints.list();
588
- return jsonContent(endpoints);
600
+ const teams = await client.teams.list();
601
+ return jsonContent(
602
+ teams.map((team) => ({
603
+ ...team,
604
+ periodEnd: team.periodEnd ? new Date(team.periodEnd).toISOString() : null
605
+ }))
606
+ );
607
+ })
608
+ );
609
+ server.tool(
610
+ "list_team_members",
611
+ "List a team's members and pending invites. Any member of the team may call this.",
612
+ { teamId: import_zod2.z.string().min(1).describe("Team id (from list_teams)") },
613
+ withErrorHandling(async ({ teamId }) => {
614
+ const result = await client.teams.members(teamId);
615
+ return jsonContent(result);
616
+ })
617
+ );
618
+ server.tool(
619
+ "share_endpoint",
620
+ "Share an endpoint you own with a team so its members can inspect, stream, and edit it. The team needs an active subscription; the endpoint's requests then bill the team's pooled quota.",
621
+ {
622
+ slug: import_zod2.z.string().describe("Slug of an endpoint you own"),
623
+ teamId: import_zod2.z.string().min(1).describe("Team id (from list_teams)")
624
+ },
625
+ withErrorHandling(async ({ slug, teamId }) => {
626
+ await client.teams.share(teamId, slug);
627
+ return jsonContent({ shared: true, slug, teamId });
628
+ })
629
+ );
630
+ server.tool(
631
+ "unshare_endpoint",
632
+ "Stop sharing an endpoint you own with a team. Works even when the team is suspended.",
633
+ {
634
+ slug: import_zod2.z.string().describe("Slug of an endpoint you own"),
635
+ teamId: import_zod2.z.string().min(1).describe("Team id (from list_teams)")
636
+ },
637
+ withErrorHandling(async ({ slug, teamId }) => {
638
+ await client.teams.unshare(teamId, slug);
639
+ return jsonContent({ shared: false, slug, teamId });
589
640
  })
590
641
  );
591
642
  server.tool(
@@ -709,15 +760,17 @@ function registerTools(server, client) {
709
760
  provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to send with signed headers"),
710
761
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
711
762
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
712
- secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
763
+ secret: import_zod2.z.string().optional().describe(
764
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
765
+ )
713
766
  },
714
767
  withErrorHandling(
715
768
  async ({ slug, method, headers, body, provider, template, event, secret }) => {
716
769
  let response;
717
770
  if (provider) {
718
771
  const templateSecret = secret?.trim();
719
- if (!templateSecret) {
720
- throw new Error("send_webhook with provider templates requires a non-empty secret");
772
+ if (!templateSecret && import_sdk.TEMPLATE_METADATA[provider]?.secretRequired !== false) {
773
+ throw new Error(`send_webhook with provider "${provider}" requires a non-empty secret`);
721
774
  }
722
775
  response = await client.endpoints.sendTemplate(slug, {
723
776
  provider,
@@ -893,7 +946,7 @@ function registerTools(server, client) {
893
946
  requestId: import_zod2.z.string().describe("The captured request ID"),
894
947
  provider: import_zod2.z.enum(import_sdk.VERIFY_PROVIDERS).optional().describe("Provider whose signature scheme should be verified. Omit to use stored result."),
895
948
  secret: import_zod2.z.string().optional().describe(
896
- "Shared signing secret. Required for non-Discord providers when doing client-side verification."
949
+ "Provider credential for client-side verification (for example a shared signing secret, Adyen hex HMAC key, or PayPal webhook ID). Required for non-Discord providers."
897
950
  ),
898
951
  publicKey: import_zod2.z.string().optional().describe("Discord application public key. Required for provider=discord."),
899
952
  url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio, Square, and HubSpot verification."),
@@ -965,7 +1018,9 @@ function registerTools(server, client) {
965
1018
  provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
966
1019
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
967
1020
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
968
- secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
1021
+ secret: import_zod2.z.string().optional().describe(
1022
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
1023
+ )
969
1024
  },
970
1025
  withErrorHandling(async ({ url, method, headers, body, provider, template, event, secret }) => {
971
1026
  const response = await client.sendTo(url, {
@@ -996,7 +1051,9 @@ function registerTools(server, client) {
996
1051
  provider: import_zod2.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
997
1052
  template: import_zod2.z.string().optional().describe("Provider-specific template preset"),
998
1053
  event: import_zod2.z.string().optional().describe("Provider event or topic name"),
999
- secret: import_zod2.z.string().optional().describe("Signing secret. Required when provider is set.")
1054
+ secret: import_zod2.z.string().optional().describe(
1055
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
1056
+ )
1000
1057
  },
1001
1058
  withErrorHandling(async ({ url, method, headers, body, provider, template, event, secret }) => {
1002
1059
  const preview = await client.buildRequest(url, {
@@ -1028,13 +1085,31 @@ function registerTools(server, client) {
1028
1085
  );
1029
1086
  server.tool(
1030
1087
  "get_usage",
1031
- "Check current request usage, remaining quota, plan, and period end.",
1088
+ "Check current request usage, remaining quota, plan, and period end, plus the pooled quota of every subscribed team you belong to.",
1032
1089
  {},
1033
1090
  withErrorHandling(async () => {
1034
- const usage = await client.usage();
1091
+ const [usageResult, teamsResult] = await Promise.allSettled([
1092
+ client.usage(),
1093
+ client.teams.list()
1094
+ ]);
1095
+ if (usageResult.status === "rejected") throw usageResult.reason;
1096
+ const usage = usageResult.value;
1097
+ const teams = teamsResult.status === "fulfilled" ? teamsResult.value : [];
1098
+ const teamsError = teamsResult.status === "rejected" ? teamsResult.reason instanceof Error ? teamsResult.reason.message : String(teamsResult.reason) : void 0;
1035
1099
  return jsonContent({
1036
1100
  ...usage,
1037
- periodEnd: usage.periodEnd ? new Date(usage.periodEnd).toISOString() : null
1101
+ periodEnd: usage.periodEnd ? new Date(usage.periodEnd).toISOString() : null,
1102
+ ...teamsError ? { teamsError } : {},
1103
+ teams: teams.filter((team) => !team.suspended).map((team) => ({
1104
+ id: team.id,
1105
+ name: team.name,
1106
+ role: team.role,
1107
+ seats: team.seats,
1108
+ used: team.requestsUsed,
1109
+ limit: team.requestLimit,
1110
+ remaining: Math.max(0, team.requestLimit - team.requestsUsed),
1111
+ periodEnd: team.periodEnd ? new Date(team.periodEnd).toISOString() : null
1112
+ }))
1038
1113
  });
1039
1114
  })
1040
1115
  );
@@ -1072,9 +1147,9 @@ function registerTools(server, client) {
1072
1147
  }
1073
1148
  if (provider) {
1074
1149
  const templateSecret = secret?.trim();
1075
- if (!templateSecret) {
1150
+ if (!templateSecret && import_sdk.TEMPLATE_METADATA[provider]?.secretRequired !== false) {
1076
1151
  throw new Error(
1077
- "test_webhook_flow with provider templates requires a non-empty secret"
1152
+ `test_webhook_flow with provider "${provider}" requires a non-empty secret`
1078
1153
  );
1079
1154
  }
1080
1155
  flow.sendTemplate({
@@ -1088,6 +1163,11 @@ function registerTools(server, client) {
1088
1163
  "test_webhook_flow cannot verify Discord signatures (Ed25519 requires a public key, not a secret)"
1089
1164
  );
1090
1165
  }
1166
+ if (!templateSecret) {
1167
+ throw new Error(
1168
+ `test_webhook_flow cannot verify "${provider}" signatures \u2014 this provider's templates are unsigned`
1169
+ );
1170
+ }
1091
1171
  flow.verifySignature({
1092
1172
  provider,
1093
1173
  secret: templateSecret
@@ -1230,7 +1310,7 @@ function registerAgentRegistrationTools(server) {
1230
1310
  }
1231
1311
 
1232
1312
  // src/index.ts
1233
- var VERSION = true ? "1.6.0" : "0.0.0-dev";
1313
+ var VERSION = true ? "1.8.0" : "0.0.0-dev";
1234
1314
  function createServer(options = {}) {
1235
1315
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
1236
1316
  const server = new import_mcp2.McpServer({
package/dist/index.mjs CHANGED
@@ -250,6 +250,7 @@ import {
250
250
  MAX_GLOB_PATTERN_LEN,
251
251
  NotFoundError,
252
252
  RateLimitError,
253
+ TEMPLATE_METADATA,
253
254
  TEMPLATE_PROVIDERS,
254
255
  TimeoutError,
255
256
  UnauthorizedError,
@@ -574,11 +575,62 @@ function registerTools(server, client) {
574
575
  );
575
576
  server.tool(
576
577
  "list_endpoints",
577
- "List all webhook endpoints for the authenticated user.",
578
+ "List webhook endpoints: those you own (with sharedWith) and those shared with you through teams (with fromTeam). Optionally keep only one team's endpoints.",
579
+ {
580
+ team: z2.string().min(1).optional().describe(
581
+ "Team id or name (case-insensitive). Keeps endpoints shared with you from that team and endpoints you own that are shared with it."
582
+ )
583
+ },
584
+ withErrorHandling(async ({ team }) => {
585
+ const endpoints = await client.endpoints.list({ team });
586
+ return jsonContent(endpoints);
587
+ })
588
+ );
589
+ server.tool(
590
+ "list_teams",
591
+ "List the teams you own or belong to, with role, seats, pooled request usage (requestsUsed/requestLimit), period end, and whether the team is suspended for lack of a subscription.",
578
592
  {},
579
593
  withErrorHandling(async () => {
580
- const endpoints = await client.endpoints.list();
581
- return jsonContent(endpoints);
594
+ const teams = await client.teams.list();
595
+ return jsonContent(
596
+ teams.map((team) => ({
597
+ ...team,
598
+ periodEnd: team.periodEnd ? new Date(team.periodEnd).toISOString() : null
599
+ }))
600
+ );
601
+ })
602
+ );
603
+ server.tool(
604
+ "list_team_members",
605
+ "List a team's members and pending invites. Any member of the team may call this.",
606
+ { teamId: z2.string().min(1).describe("Team id (from list_teams)") },
607
+ withErrorHandling(async ({ teamId }) => {
608
+ const result = await client.teams.members(teamId);
609
+ return jsonContent(result);
610
+ })
611
+ );
612
+ server.tool(
613
+ "share_endpoint",
614
+ "Share an endpoint you own with a team so its members can inspect, stream, and edit it. The team needs an active subscription; the endpoint's requests then bill the team's pooled quota.",
615
+ {
616
+ slug: z2.string().describe("Slug of an endpoint you own"),
617
+ teamId: z2.string().min(1).describe("Team id (from list_teams)")
618
+ },
619
+ withErrorHandling(async ({ slug, teamId }) => {
620
+ await client.teams.share(teamId, slug);
621
+ return jsonContent({ shared: true, slug, teamId });
622
+ })
623
+ );
624
+ server.tool(
625
+ "unshare_endpoint",
626
+ "Stop sharing an endpoint you own with a team. Works even when the team is suspended.",
627
+ {
628
+ slug: z2.string().describe("Slug of an endpoint you own"),
629
+ teamId: z2.string().min(1).describe("Team id (from list_teams)")
630
+ },
631
+ withErrorHandling(async ({ slug, teamId }) => {
632
+ await client.teams.unshare(teamId, slug);
633
+ return jsonContent({ shared: false, slug, teamId });
582
634
  })
583
635
  );
584
636
  server.tool(
@@ -702,15 +754,17 @@ function registerTools(server, client) {
702
754
  provider: z2.enum(TEMPLATE_PROVIDERS).optional().describe("Optional provider template to send with signed headers"),
703
755
  template: z2.string().optional().describe("Provider-specific template preset"),
704
756
  event: z2.string().optional().describe("Provider event or topic name"),
705
- secret: z2.string().optional().describe("Signing secret. Required when provider is set.")
757
+ secret: z2.string().optional().describe(
758
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
759
+ )
706
760
  },
707
761
  withErrorHandling(
708
762
  async ({ slug, method, headers, body, provider, template, event, secret }) => {
709
763
  let response;
710
764
  if (provider) {
711
765
  const templateSecret = secret?.trim();
712
- if (!templateSecret) {
713
- throw new Error("send_webhook with provider templates requires a non-empty secret");
766
+ if (!templateSecret && TEMPLATE_METADATA[provider]?.secretRequired !== false) {
767
+ throw new Error(`send_webhook with provider "${provider}" requires a non-empty secret`);
714
768
  }
715
769
  response = await client.endpoints.sendTemplate(slug, {
716
770
  provider,
@@ -886,7 +940,7 @@ function registerTools(server, client) {
886
940
  requestId: z2.string().describe("The captured request ID"),
887
941
  provider: z2.enum(VERIFY_PROVIDERS).optional().describe("Provider whose signature scheme should be verified. Omit to use stored result."),
888
942
  secret: z2.string().optional().describe(
889
- "Shared signing secret. Required for non-Discord providers when doing client-side verification."
943
+ "Provider credential for client-side verification (for example a shared signing secret, Adyen hex HMAC key, or PayPal webhook ID). Required for non-Discord providers."
890
944
  ),
891
945
  publicKey: z2.string().optional().describe("Discord application public key. Required for provider=discord."),
892
946
  url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio, Square, and HubSpot verification."),
@@ -958,7 +1012,9 @@ function registerTools(server, client) {
958
1012
  provider: z2.enum(TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
959
1013
  template: z2.string().optional().describe("Provider-specific template preset"),
960
1014
  event: z2.string().optional().describe("Provider event or topic name"),
961
- secret: z2.string().optional().describe("Signing secret. Required when provider is set.")
1015
+ secret: z2.string().optional().describe(
1016
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
1017
+ )
962
1018
  },
963
1019
  withErrorHandling(async ({ url, method, headers, body, provider, template, event, secret }) => {
964
1020
  const response = await client.sendTo(url, {
@@ -989,7 +1045,9 @@ function registerTools(server, client) {
989
1045
  provider: z2.enum(TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
990
1046
  template: z2.string().optional().describe("Provider-specific template preset"),
991
1047
  event: z2.string().optional().describe("Provider event or topic name"),
992
- secret: z2.string().optional().describe("Signing secret. Required when provider is set.")
1048
+ secret: z2.string().optional().describe(
1049
+ "Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
1050
+ )
993
1051
  },
994
1052
  withErrorHandling(async ({ url, method, headers, body, provider, template, event, secret }) => {
995
1053
  const preview = await client.buildRequest(url, {
@@ -1021,13 +1079,31 @@ function registerTools(server, client) {
1021
1079
  );
1022
1080
  server.tool(
1023
1081
  "get_usage",
1024
- "Check current request usage, remaining quota, plan, and period end.",
1082
+ "Check current request usage, remaining quota, plan, and period end, plus the pooled quota of every subscribed team you belong to.",
1025
1083
  {},
1026
1084
  withErrorHandling(async () => {
1027
- const usage = await client.usage();
1085
+ const [usageResult, teamsResult] = await Promise.allSettled([
1086
+ client.usage(),
1087
+ client.teams.list()
1088
+ ]);
1089
+ if (usageResult.status === "rejected") throw usageResult.reason;
1090
+ const usage = usageResult.value;
1091
+ const teams = teamsResult.status === "fulfilled" ? teamsResult.value : [];
1092
+ const teamsError = teamsResult.status === "rejected" ? teamsResult.reason instanceof Error ? teamsResult.reason.message : String(teamsResult.reason) : void 0;
1028
1093
  return jsonContent({
1029
1094
  ...usage,
1030
- periodEnd: usage.periodEnd ? new Date(usage.periodEnd).toISOString() : null
1095
+ periodEnd: usage.periodEnd ? new Date(usage.periodEnd).toISOString() : null,
1096
+ ...teamsError ? { teamsError } : {},
1097
+ teams: teams.filter((team) => !team.suspended).map((team) => ({
1098
+ id: team.id,
1099
+ name: team.name,
1100
+ role: team.role,
1101
+ seats: team.seats,
1102
+ used: team.requestsUsed,
1103
+ limit: team.requestLimit,
1104
+ remaining: Math.max(0, team.requestLimit - team.requestsUsed),
1105
+ periodEnd: team.periodEnd ? new Date(team.periodEnd).toISOString() : null
1106
+ }))
1031
1107
  });
1032
1108
  })
1033
1109
  );
@@ -1065,9 +1141,9 @@ function registerTools(server, client) {
1065
1141
  }
1066
1142
  if (provider) {
1067
1143
  const templateSecret = secret?.trim();
1068
- if (!templateSecret) {
1144
+ if (!templateSecret && TEMPLATE_METADATA[provider]?.secretRequired !== false) {
1069
1145
  throw new Error(
1070
- "test_webhook_flow with provider templates requires a non-empty secret"
1146
+ `test_webhook_flow with provider "${provider}" requires a non-empty secret`
1071
1147
  );
1072
1148
  }
1073
1149
  flow.sendTemplate({
@@ -1081,6 +1157,11 @@ function registerTools(server, client) {
1081
1157
  "test_webhook_flow cannot verify Discord signatures (Ed25519 requires a public key, not a secret)"
1082
1158
  );
1083
1159
  }
1160
+ if (!templateSecret) {
1161
+ throw new Error(
1162
+ `test_webhook_flow cannot verify "${provider}" signatures \u2014 this provider's templates are unsigned`
1163
+ );
1164
+ }
1084
1165
  flow.verifySignature({
1085
1166
  provider,
1086
1167
  secret: templateSecret
@@ -1223,7 +1304,7 @@ function registerAgentRegistrationTools(server) {
1223
1304
  }
1224
1305
 
1225
1306
  // src/index.ts
1226
- var VERSION = true ? "1.6.0" : "0.0.0-dev";
1307
+ var VERSION = true ? "1.8.0" : "0.0.0-dev";
1227
1308
  function createServer(options = {}) {
1228
1309
  const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
1229
1310
  const server = new McpServer2({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webhooks-cc/mcp",
3
- "version": "1.6.0",
3
+ "version": "1.8.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,15 +36,15 @@
36
36
  },
37
37
  "homepage": "https://webhooks.cc",
38
38
  "dependencies": {
39
- "@modelcontextprotocol/sdk": "^1.29.0",
39
+ "@modelcontextprotocol/sdk": "^1.30.0",
40
40
  "zod": "^4.4.3",
41
- "@webhooks-cc/sdk": "1.8.0"
41
+ "@webhooks-cc/sdk": "1.10.0"
42
42
  },
43
43
  "devDependencies": {
44
- "@types/node": "^25.9.1",
44
+ "@types/node": "^26.4.0",
45
45
  "tsup": "^8.5.1",
46
46
  "typescript": "^6.0.3",
47
- "vitest": "^4.1.7"
47
+ "vitest": "^4.1.11"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "tsup",