@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 +27 -3
- package/dist/bin/mcp.js +95 -15
- package/dist/index.js +95 -15
- package/dist/index.mjs +96 -15
- package/package.json +5 -5
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 `
|
|
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
|
|
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
|
|
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
|
|
566
|
-
return jsonContent(
|
|
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(
|
|
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(
|
|
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
|
-
"
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
588
|
-
return jsonContent(
|
|
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(
|
|
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(
|
|
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
|
-
"
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
581
|
-
return jsonContent(
|
|
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(
|
|
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(
|
|
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
|
-
"
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
40
40
|
"zod": "^4.4.3",
|
|
41
|
-
"@webhooks-cc/sdk": "1.
|
|
41
|
+
"@webhooks-cc/sdk": "1.10.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@types/node": "^
|
|
44
|
+
"@types/node": "^26.4.0",
|
|
45
45
|
"tsup": "^8.5.1",
|
|
46
46
|
"typescript": "^6.0.3",
|
|
47
|
-
"vitest": "^4.1.
|
|
47
|
+
"vitest": "^4.1.11"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsup",
|