@webhooks-cc/mcp 1.2.0 → 1.4.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 +7 -1
- package/dist/bin/mcp.js +104 -14
- package/dist/index.js +104 -14
- package/dist/index.mjs +104 -14
- package/package.json +13 -12
package/README.md
CHANGED
|
@@ -128,11 +128,17 @@ Provider template and signing support includes:
|
|
|
128
128
|
- `slack`
|
|
129
129
|
- `paddle`
|
|
130
130
|
- `linear`
|
|
131
|
+
- `sendgrid`
|
|
132
|
+
- `clerk`
|
|
133
|
+
- `discord`
|
|
134
|
+
- `vercel`
|
|
135
|
+
- `gitlab`
|
|
136
|
+
- `typeform`
|
|
131
137
|
- `standard-webhooks`
|
|
132
138
|
|
|
133
139
|
Signature verification also supports:
|
|
134
140
|
|
|
135
|
-
- `
|
|
141
|
+
- all template providers except `sendgrid`, which uses IP allowlisting instead of cryptographic signatures
|
|
136
142
|
|
|
137
143
|
Use `list_provider_templates` to inspect templates, default events, signature headers, and signing requirements from the agent.
|
|
138
144
|
|
package/dist/bin/mcp.js
CHANGED
|
@@ -498,10 +498,38 @@ function registerTools(server, client) {
|
|
|
498
498
|
),
|
|
499
499
|
notificationUrl: import_zod2.z.string().url().optional().describe(
|
|
500
500
|
"URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook)"
|
|
501
|
-
)
|
|
501
|
+
),
|
|
502
|
+
signingProvider: import_zod2.z.string().optional().describe(
|
|
503
|
+
"Signing provider for automatic signature verification (e.g. stripe, github, shopify)"
|
|
504
|
+
),
|
|
505
|
+
signingSecret: import_zod2.z.string().optional().describe(
|
|
506
|
+
"Signing secret (encrypted server-side, never returned). Required when signingProvider is set."
|
|
507
|
+
),
|
|
508
|
+
signingHeader: import_zod2.z.string().optional().describe("Custom signature header name. Only used with generic-hmac provider.")
|
|
502
509
|
},
|
|
503
510
|
withErrorHandling(
|
|
504
|
-
async ({
|
|
511
|
+
async ({
|
|
512
|
+
name,
|
|
513
|
+
ephemeral,
|
|
514
|
+
expiresIn,
|
|
515
|
+
mockResponse,
|
|
516
|
+
responseRules,
|
|
517
|
+
notificationUrl,
|
|
518
|
+
signingProvider,
|
|
519
|
+
signingSecret,
|
|
520
|
+
signingHeader
|
|
521
|
+
}) => {
|
|
522
|
+
if (signingProvider) {
|
|
523
|
+
if (signingProvider !== "discord" && !signingSecret) {
|
|
524
|
+
throw new Error(`signingSecret is required for provider "${signingProvider}"`);
|
|
525
|
+
}
|
|
526
|
+
if (signingProvider === "generic-hmac" && !signingHeader) {
|
|
527
|
+
throw new Error("signingHeader is required for generic-hmac provider");
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (signingSecret && !signingProvider) {
|
|
531
|
+
throw new Error("signingProvider is required when signingSecret is set");
|
|
532
|
+
}
|
|
505
533
|
const endpoint = await client.endpoints.create({
|
|
506
534
|
name,
|
|
507
535
|
ephemeral,
|
|
@@ -510,6 +538,20 @@ function registerTools(server, client) {
|
|
|
510
538
|
responseRules,
|
|
511
539
|
notificationUrl
|
|
512
540
|
});
|
|
541
|
+
if (signingProvider && signingSecret) {
|
|
542
|
+
try {
|
|
543
|
+
await client.endpoints.update(endpoint.slug, {
|
|
544
|
+
signingProvider,
|
|
545
|
+
signingSecret,
|
|
546
|
+
signingHeader
|
|
547
|
+
});
|
|
548
|
+
} catch (err) {
|
|
549
|
+
return jsonContent({
|
|
550
|
+
...endpoint,
|
|
551
|
+
warning: `Endpoint created but signing configuration failed: ${err instanceof Error ? err.message : String(err)}. Update endpoint "${endpoint.slug}" to add signing.`
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
}
|
|
513
555
|
return jsonContent(endpoint);
|
|
514
556
|
}
|
|
515
557
|
)
|
|
@@ -534,23 +576,44 @@ function registerTools(server, client) {
|
|
|
534
576
|
);
|
|
535
577
|
server.tool(
|
|
536
578
|
"update_endpoint",
|
|
537
|
-
"Update an endpoint name, mock response,
|
|
579
|
+
"Update an endpoint name, mock response, conditional response rules, or signing configuration.",
|
|
538
580
|
{
|
|
539
581
|
slug: import_zod2.z.string().describe("The endpoint slug to update"),
|
|
540
582
|
name: import_zod2.z.string().optional().describe("New display name"),
|
|
541
583
|
mockResponse: mockResponseSchema.nullable().optional().describe("Default mock response (used when no rule matches), or null to clear"),
|
|
542
584
|
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")
|
|
585
|
+
notificationUrl: import_zod2.z.string().url().nullable().optional().describe("Notification webhook URL, or null to clear it"),
|
|
586
|
+
signingProvider: import_zod2.z.string().nullable().optional().describe(
|
|
587
|
+
"Signing provider for automatic verification (e.g. stripe, github), or null to disable"
|
|
588
|
+
),
|
|
589
|
+
signingSecret: import_zod2.z.string().nullable().optional().describe("New signing secret (encrypted server-side). Pass null to clear."),
|
|
590
|
+
signingHeader: import_zod2.z.string().nullable().optional().describe(
|
|
591
|
+
"Custom signature header name (only for generic-hmac provider), or null to clear"
|
|
592
|
+
)
|
|
544
593
|
},
|
|
545
|
-
withErrorHandling(
|
|
546
|
-
|
|
594
|
+
withErrorHandling(
|
|
595
|
+
async ({
|
|
596
|
+
slug,
|
|
547
597
|
name,
|
|
548
598
|
mockResponse,
|
|
549
599
|
responseRules,
|
|
550
|
-
notificationUrl
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
600
|
+
notificationUrl,
|
|
601
|
+
signingProvider,
|
|
602
|
+
signingSecret,
|
|
603
|
+
signingHeader
|
|
604
|
+
}) => {
|
|
605
|
+
const endpoint = await client.endpoints.update(slug, {
|
|
606
|
+
name,
|
|
607
|
+
mockResponse,
|
|
608
|
+
responseRules,
|
|
609
|
+
notificationUrl,
|
|
610
|
+
signingProvider,
|
|
611
|
+
signingSecret,
|
|
612
|
+
signingHeader
|
|
613
|
+
});
|
|
614
|
+
return jsonContent(endpoint);
|
|
615
|
+
}
|
|
616
|
+
)
|
|
554
617
|
);
|
|
555
618
|
server.tool(
|
|
556
619
|
"delete_endpoint",
|
|
@@ -802,16 +865,43 @@ function registerTools(server, client) {
|
|
|
802
865
|
);
|
|
803
866
|
server.tool(
|
|
804
867
|
"verify_signature",
|
|
805
|
-
"Verify the webhook signature on a captured request.",
|
|
868
|
+
"Verify the webhook signature on a captured request. When called without provider/secret, returns the server-side pre-computed result (if signing is configured on the endpoint). When called with provider+secret, performs client-side verification.",
|
|
806
869
|
{
|
|
807
870
|
requestId: import_zod2.z.string().describe("The captured request ID"),
|
|
808
|
-
provider: import_zod2.z.enum(import_sdk.VERIFY_PROVIDERS).describe("Provider whose signature scheme should be verified"),
|
|
809
|
-
secret: import_zod2.z.string().optional().describe(
|
|
871
|
+
provider: import_zod2.z.enum(import_sdk.VERIFY_PROVIDERS).optional().describe("Provider whose signature scheme should be verified. Omit to use stored result."),
|
|
872
|
+
secret: import_zod2.z.string().optional().describe(
|
|
873
|
+
"Shared signing secret. Required for non-Discord providers when doing client-side verification."
|
|
874
|
+
),
|
|
810
875
|
publicKey: import_zod2.z.string().optional().describe("Discord application public key. Required for provider=discord."),
|
|
811
876
|
url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio verification.")
|
|
812
877
|
},
|
|
813
878
|
withErrorHandling(async ({ requestId, provider, secret, publicKey, url }) => {
|
|
814
879
|
const request = await client.requests.get(requestId);
|
|
880
|
+
if (!provider && !secret && !publicKey) {
|
|
881
|
+
const verified = request.signatureVerified;
|
|
882
|
+
if (verified === null || verified === void 0) {
|
|
883
|
+
return jsonContent({
|
|
884
|
+
valid: null,
|
|
885
|
+
details: "No signature verification configured on this endpoint. Pass provider and secret to verify client-side."
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
let errorData = null;
|
|
889
|
+
if (request.signatureError) {
|
|
890
|
+
try {
|
|
891
|
+
errorData = JSON.parse(request.signatureError);
|
|
892
|
+
} catch {
|
|
893
|
+
errorData = null;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
const isSkipped = !verified && errorData?.code && (errorData.code === "missing_header" || errorData.code === "unsupported");
|
|
897
|
+
return jsonContent({
|
|
898
|
+
valid: isSkipped ? null : verified,
|
|
899
|
+
provider: request.signingProvider ?? null,
|
|
900
|
+
details: verified ? "Signature verified by server." : isSkipped ? `Verification skipped: ${errorData?.message ?? errorData?.code}` : `Signature invalid: ${request.signatureError ?? "unknown error"}`,
|
|
901
|
+
error: request.signatureError ?? null,
|
|
902
|
+
skipped: isSkipped ?? false
|
|
903
|
+
});
|
|
904
|
+
}
|
|
815
905
|
const verificationOptions = ensureVerifyArgs({ provider, secret, publicKey, url });
|
|
816
906
|
const result = await (0, import_sdk.verifySignature)(request, verificationOptions);
|
|
817
907
|
return jsonContent({
|
|
@@ -1006,7 +1096,7 @@ function registerTools(server, client) {
|
|
|
1006
1096
|
}
|
|
1007
1097
|
|
|
1008
1098
|
// src/index.ts
|
|
1009
|
-
var VERSION = true ? "1.
|
|
1099
|
+
var VERSION = true ? "1.4.0" : "0.0.0-dev";
|
|
1010
1100
|
function createServer(options = {}) {
|
|
1011
1101
|
const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
|
|
1012
1102
|
if (!apiKey) {
|
package/dist/index.js
CHANGED
|
@@ -519,10 +519,38 @@ function registerTools(server, client) {
|
|
|
519
519
|
),
|
|
520
520
|
notificationUrl: import_zod2.z.string().url().optional().describe(
|
|
521
521
|
"URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook)"
|
|
522
|
-
)
|
|
522
|
+
),
|
|
523
|
+
signingProvider: import_zod2.z.string().optional().describe(
|
|
524
|
+
"Signing provider for automatic signature verification (e.g. stripe, github, shopify)"
|
|
525
|
+
),
|
|
526
|
+
signingSecret: import_zod2.z.string().optional().describe(
|
|
527
|
+
"Signing secret (encrypted server-side, never returned). Required when signingProvider is set."
|
|
528
|
+
),
|
|
529
|
+
signingHeader: import_zod2.z.string().optional().describe("Custom signature header name. Only used with generic-hmac provider.")
|
|
523
530
|
},
|
|
524
531
|
withErrorHandling(
|
|
525
|
-
async ({
|
|
532
|
+
async ({
|
|
533
|
+
name,
|
|
534
|
+
ephemeral,
|
|
535
|
+
expiresIn,
|
|
536
|
+
mockResponse,
|
|
537
|
+
responseRules,
|
|
538
|
+
notificationUrl,
|
|
539
|
+
signingProvider,
|
|
540
|
+
signingSecret,
|
|
541
|
+
signingHeader
|
|
542
|
+
}) => {
|
|
543
|
+
if (signingProvider) {
|
|
544
|
+
if (signingProvider !== "discord" && !signingSecret) {
|
|
545
|
+
throw new Error(`signingSecret is required for provider "${signingProvider}"`);
|
|
546
|
+
}
|
|
547
|
+
if (signingProvider === "generic-hmac" && !signingHeader) {
|
|
548
|
+
throw new Error("signingHeader is required for generic-hmac provider");
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (signingSecret && !signingProvider) {
|
|
552
|
+
throw new Error("signingProvider is required when signingSecret is set");
|
|
553
|
+
}
|
|
526
554
|
const endpoint = await client.endpoints.create({
|
|
527
555
|
name,
|
|
528
556
|
ephemeral,
|
|
@@ -531,6 +559,20 @@ function registerTools(server, client) {
|
|
|
531
559
|
responseRules,
|
|
532
560
|
notificationUrl
|
|
533
561
|
});
|
|
562
|
+
if (signingProvider && signingSecret) {
|
|
563
|
+
try {
|
|
564
|
+
await client.endpoints.update(endpoint.slug, {
|
|
565
|
+
signingProvider,
|
|
566
|
+
signingSecret,
|
|
567
|
+
signingHeader
|
|
568
|
+
});
|
|
569
|
+
} catch (err) {
|
|
570
|
+
return jsonContent({
|
|
571
|
+
...endpoint,
|
|
572
|
+
warning: `Endpoint created but signing configuration failed: ${err instanceof Error ? err.message : String(err)}. Update endpoint "${endpoint.slug}" to add signing.`
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
}
|
|
534
576
|
return jsonContent(endpoint);
|
|
535
577
|
}
|
|
536
578
|
)
|
|
@@ -555,23 +597,44 @@ function registerTools(server, client) {
|
|
|
555
597
|
);
|
|
556
598
|
server.tool(
|
|
557
599
|
"update_endpoint",
|
|
558
|
-
"Update an endpoint name, mock response,
|
|
600
|
+
"Update an endpoint name, mock response, conditional response rules, or signing configuration.",
|
|
559
601
|
{
|
|
560
602
|
slug: import_zod2.z.string().describe("The endpoint slug to update"),
|
|
561
603
|
name: import_zod2.z.string().optional().describe("New display name"),
|
|
562
604
|
mockResponse: mockResponseSchema.nullable().optional().describe("Default mock response (used when no rule matches), or null to clear"),
|
|
563
605
|
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")
|
|
606
|
+
notificationUrl: import_zod2.z.string().url().nullable().optional().describe("Notification webhook URL, or null to clear it"),
|
|
607
|
+
signingProvider: import_zod2.z.string().nullable().optional().describe(
|
|
608
|
+
"Signing provider for automatic verification (e.g. stripe, github), or null to disable"
|
|
609
|
+
),
|
|
610
|
+
signingSecret: import_zod2.z.string().nullable().optional().describe("New signing secret (encrypted server-side). Pass null to clear."),
|
|
611
|
+
signingHeader: import_zod2.z.string().nullable().optional().describe(
|
|
612
|
+
"Custom signature header name (only for generic-hmac provider), or null to clear"
|
|
613
|
+
)
|
|
565
614
|
},
|
|
566
|
-
withErrorHandling(
|
|
567
|
-
|
|
615
|
+
withErrorHandling(
|
|
616
|
+
async ({
|
|
617
|
+
slug,
|
|
568
618
|
name,
|
|
569
619
|
mockResponse,
|
|
570
620
|
responseRules,
|
|
571
|
-
notificationUrl
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
621
|
+
notificationUrl,
|
|
622
|
+
signingProvider,
|
|
623
|
+
signingSecret,
|
|
624
|
+
signingHeader
|
|
625
|
+
}) => {
|
|
626
|
+
const endpoint = await client.endpoints.update(slug, {
|
|
627
|
+
name,
|
|
628
|
+
mockResponse,
|
|
629
|
+
responseRules,
|
|
630
|
+
notificationUrl,
|
|
631
|
+
signingProvider,
|
|
632
|
+
signingSecret,
|
|
633
|
+
signingHeader
|
|
634
|
+
});
|
|
635
|
+
return jsonContent(endpoint);
|
|
636
|
+
}
|
|
637
|
+
)
|
|
575
638
|
);
|
|
576
639
|
server.tool(
|
|
577
640
|
"delete_endpoint",
|
|
@@ -823,16 +886,43 @@ function registerTools(server, client) {
|
|
|
823
886
|
);
|
|
824
887
|
server.tool(
|
|
825
888
|
"verify_signature",
|
|
826
|
-
"Verify the webhook signature on a captured request.",
|
|
889
|
+
"Verify the webhook signature on a captured request. When called without provider/secret, returns the server-side pre-computed result (if signing is configured on the endpoint). When called with provider+secret, performs client-side verification.",
|
|
827
890
|
{
|
|
828
891
|
requestId: import_zod2.z.string().describe("The captured request ID"),
|
|
829
|
-
provider: import_zod2.z.enum(import_sdk.VERIFY_PROVIDERS).describe("Provider whose signature scheme should be verified"),
|
|
830
|
-
secret: import_zod2.z.string().optional().describe(
|
|
892
|
+
provider: import_zod2.z.enum(import_sdk.VERIFY_PROVIDERS).optional().describe("Provider whose signature scheme should be verified. Omit to use stored result."),
|
|
893
|
+
secret: import_zod2.z.string().optional().describe(
|
|
894
|
+
"Shared signing secret. Required for non-Discord providers when doing client-side verification."
|
|
895
|
+
),
|
|
831
896
|
publicKey: import_zod2.z.string().optional().describe("Discord application public key. Required for provider=discord."),
|
|
832
897
|
url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio verification.")
|
|
833
898
|
},
|
|
834
899
|
withErrorHandling(async ({ requestId, provider, secret, publicKey, url }) => {
|
|
835
900
|
const request = await client.requests.get(requestId);
|
|
901
|
+
if (!provider && !secret && !publicKey) {
|
|
902
|
+
const verified = request.signatureVerified;
|
|
903
|
+
if (verified === null || verified === void 0) {
|
|
904
|
+
return jsonContent({
|
|
905
|
+
valid: null,
|
|
906
|
+
details: "No signature verification configured on this endpoint. Pass provider and secret to verify client-side."
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
let errorData = null;
|
|
910
|
+
if (request.signatureError) {
|
|
911
|
+
try {
|
|
912
|
+
errorData = JSON.parse(request.signatureError);
|
|
913
|
+
} catch {
|
|
914
|
+
errorData = null;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
const isSkipped = !verified && errorData?.code && (errorData.code === "missing_header" || errorData.code === "unsupported");
|
|
918
|
+
return jsonContent({
|
|
919
|
+
valid: isSkipped ? null : verified,
|
|
920
|
+
provider: request.signingProvider ?? null,
|
|
921
|
+
details: verified ? "Signature verified by server." : isSkipped ? `Verification skipped: ${errorData?.message ?? errorData?.code}` : `Signature invalid: ${request.signatureError ?? "unknown error"}`,
|
|
922
|
+
error: request.signatureError ?? null,
|
|
923
|
+
skipped: isSkipped ?? false
|
|
924
|
+
});
|
|
925
|
+
}
|
|
836
926
|
const verificationOptions = ensureVerifyArgs({ provider, secret, publicKey, url });
|
|
837
927
|
const result = await (0, import_sdk.verifySignature)(request, verificationOptions);
|
|
838
928
|
return jsonContent({
|
|
@@ -1027,7 +1117,7 @@ function registerTools(server, client) {
|
|
|
1027
1117
|
}
|
|
1028
1118
|
|
|
1029
1119
|
// src/index.ts
|
|
1030
|
-
var VERSION = true ? "1.
|
|
1120
|
+
var VERSION = true ? "1.4.0" : "0.0.0-dev";
|
|
1031
1121
|
function createServer(options = {}) {
|
|
1032
1122
|
const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
|
|
1033
1123
|
if (!apiKey) {
|
package/dist/index.mjs
CHANGED
|
@@ -512,10 +512,38 @@ function registerTools(server, client) {
|
|
|
512
512
|
),
|
|
513
513
|
notificationUrl: z2.string().url().optional().describe(
|
|
514
514
|
"URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook)"
|
|
515
|
-
)
|
|
515
|
+
),
|
|
516
|
+
signingProvider: z2.string().optional().describe(
|
|
517
|
+
"Signing provider for automatic signature verification (e.g. stripe, github, shopify)"
|
|
518
|
+
),
|
|
519
|
+
signingSecret: z2.string().optional().describe(
|
|
520
|
+
"Signing secret (encrypted server-side, never returned). Required when signingProvider is set."
|
|
521
|
+
),
|
|
522
|
+
signingHeader: z2.string().optional().describe("Custom signature header name. Only used with generic-hmac provider.")
|
|
516
523
|
},
|
|
517
524
|
withErrorHandling(
|
|
518
|
-
async ({
|
|
525
|
+
async ({
|
|
526
|
+
name,
|
|
527
|
+
ephemeral,
|
|
528
|
+
expiresIn,
|
|
529
|
+
mockResponse,
|
|
530
|
+
responseRules,
|
|
531
|
+
notificationUrl,
|
|
532
|
+
signingProvider,
|
|
533
|
+
signingSecret,
|
|
534
|
+
signingHeader
|
|
535
|
+
}) => {
|
|
536
|
+
if (signingProvider) {
|
|
537
|
+
if (signingProvider !== "discord" && !signingSecret) {
|
|
538
|
+
throw new Error(`signingSecret is required for provider "${signingProvider}"`);
|
|
539
|
+
}
|
|
540
|
+
if (signingProvider === "generic-hmac" && !signingHeader) {
|
|
541
|
+
throw new Error("signingHeader is required for generic-hmac provider");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (signingSecret && !signingProvider) {
|
|
545
|
+
throw new Error("signingProvider is required when signingSecret is set");
|
|
546
|
+
}
|
|
519
547
|
const endpoint = await client.endpoints.create({
|
|
520
548
|
name,
|
|
521
549
|
ephemeral,
|
|
@@ -524,6 +552,20 @@ function registerTools(server, client) {
|
|
|
524
552
|
responseRules,
|
|
525
553
|
notificationUrl
|
|
526
554
|
});
|
|
555
|
+
if (signingProvider && signingSecret) {
|
|
556
|
+
try {
|
|
557
|
+
await client.endpoints.update(endpoint.slug, {
|
|
558
|
+
signingProvider,
|
|
559
|
+
signingSecret,
|
|
560
|
+
signingHeader
|
|
561
|
+
});
|
|
562
|
+
} catch (err) {
|
|
563
|
+
return jsonContent({
|
|
564
|
+
...endpoint,
|
|
565
|
+
warning: `Endpoint created but signing configuration failed: ${err instanceof Error ? err.message : String(err)}. Update endpoint "${endpoint.slug}" to add signing.`
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
}
|
|
527
569
|
return jsonContent(endpoint);
|
|
528
570
|
}
|
|
529
571
|
)
|
|
@@ -548,23 +590,44 @@ function registerTools(server, client) {
|
|
|
548
590
|
);
|
|
549
591
|
server.tool(
|
|
550
592
|
"update_endpoint",
|
|
551
|
-
"Update an endpoint name, mock response,
|
|
593
|
+
"Update an endpoint name, mock response, conditional response rules, or signing configuration.",
|
|
552
594
|
{
|
|
553
595
|
slug: z2.string().describe("The endpoint slug to update"),
|
|
554
596
|
name: z2.string().optional().describe("New display name"),
|
|
555
597
|
mockResponse: mockResponseSchema.nullable().optional().describe("Default mock response (used when no rule matches), or null to clear"),
|
|
556
598
|
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")
|
|
599
|
+
notificationUrl: z2.string().url().nullable().optional().describe("Notification webhook URL, or null to clear it"),
|
|
600
|
+
signingProvider: z2.string().nullable().optional().describe(
|
|
601
|
+
"Signing provider for automatic verification (e.g. stripe, github), or null to disable"
|
|
602
|
+
),
|
|
603
|
+
signingSecret: z2.string().nullable().optional().describe("New signing secret (encrypted server-side). Pass null to clear."),
|
|
604
|
+
signingHeader: z2.string().nullable().optional().describe(
|
|
605
|
+
"Custom signature header name (only for generic-hmac provider), or null to clear"
|
|
606
|
+
)
|
|
558
607
|
},
|
|
559
|
-
withErrorHandling(
|
|
560
|
-
|
|
608
|
+
withErrorHandling(
|
|
609
|
+
async ({
|
|
610
|
+
slug,
|
|
561
611
|
name,
|
|
562
612
|
mockResponse,
|
|
563
613
|
responseRules,
|
|
564
|
-
notificationUrl
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
614
|
+
notificationUrl,
|
|
615
|
+
signingProvider,
|
|
616
|
+
signingSecret,
|
|
617
|
+
signingHeader
|
|
618
|
+
}) => {
|
|
619
|
+
const endpoint = await client.endpoints.update(slug, {
|
|
620
|
+
name,
|
|
621
|
+
mockResponse,
|
|
622
|
+
responseRules,
|
|
623
|
+
notificationUrl,
|
|
624
|
+
signingProvider,
|
|
625
|
+
signingSecret,
|
|
626
|
+
signingHeader
|
|
627
|
+
});
|
|
628
|
+
return jsonContent(endpoint);
|
|
629
|
+
}
|
|
630
|
+
)
|
|
568
631
|
);
|
|
569
632
|
server.tool(
|
|
570
633
|
"delete_endpoint",
|
|
@@ -816,16 +879,43 @@ function registerTools(server, client) {
|
|
|
816
879
|
);
|
|
817
880
|
server.tool(
|
|
818
881
|
"verify_signature",
|
|
819
|
-
"Verify the webhook signature on a captured request.",
|
|
882
|
+
"Verify the webhook signature on a captured request. When called without provider/secret, returns the server-side pre-computed result (if signing is configured on the endpoint). When called with provider+secret, performs client-side verification.",
|
|
820
883
|
{
|
|
821
884
|
requestId: z2.string().describe("The captured request ID"),
|
|
822
|
-
provider: z2.enum(VERIFY_PROVIDERS).describe("Provider whose signature scheme should be verified"),
|
|
823
|
-
secret: z2.string().optional().describe(
|
|
885
|
+
provider: z2.enum(VERIFY_PROVIDERS).optional().describe("Provider whose signature scheme should be verified. Omit to use stored result."),
|
|
886
|
+
secret: z2.string().optional().describe(
|
|
887
|
+
"Shared signing secret. Required for non-Discord providers when doing client-side verification."
|
|
888
|
+
),
|
|
824
889
|
publicKey: z2.string().optional().describe("Discord application public key. Required for provider=discord."),
|
|
825
890
|
url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio verification.")
|
|
826
891
|
},
|
|
827
892
|
withErrorHandling(async ({ requestId, provider, secret, publicKey, url }) => {
|
|
828
893
|
const request = await client.requests.get(requestId);
|
|
894
|
+
if (!provider && !secret && !publicKey) {
|
|
895
|
+
const verified = request.signatureVerified;
|
|
896
|
+
if (verified === null || verified === void 0) {
|
|
897
|
+
return jsonContent({
|
|
898
|
+
valid: null,
|
|
899
|
+
details: "No signature verification configured on this endpoint. Pass provider and secret to verify client-side."
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
let errorData = null;
|
|
903
|
+
if (request.signatureError) {
|
|
904
|
+
try {
|
|
905
|
+
errorData = JSON.parse(request.signatureError);
|
|
906
|
+
} catch {
|
|
907
|
+
errorData = null;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
const isSkipped = !verified && errorData?.code && (errorData.code === "missing_header" || errorData.code === "unsupported");
|
|
911
|
+
return jsonContent({
|
|
912
|
+
valid: isSkipped ? null : verified,
|
|
913
|
+
provider: request.signingProvider ?? null,
|
|
914
|
+
details: verified ? "Signature verified by server." : isSkipped ? `Verification skipped: ${errorData?.message ?? errorData?.code}` : `Signature invalid: ${request.signatureError ?? "unknown error"}`,
|
|
915
|
+
error: request.signatureError ?? null,
|
|
916
|
+
skipped: isSkipped ?? false
|
|
917
|
+
});
|
|
918
|
+
}
|
|
829
919
|
const verificationOptions = ensureVerifyArgs({ provider, secret, publicKey, url });
|
|
830
920
|
const result = await verifySignature(request, verificationOptions);
|
|
831
921
|
return jsonContent({
|
|
@@ -1020,7 +1110,7 @@ function registerTools(server, client) {
|
|
|
1020
1110
|
}
|
|
1021
1111
|
|
|
1022
1112
|
// src/index.ts
|
|
1023
|
-
var VERSION = true ? "1.
|
|
1113
|
+
var VERSION = true ? "1.4.0" : "0.0.0-dev";
|
|
1024
1114
|
function createServer(options = {}) {
|
|
1025
1115
|
const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
|
|
1026
1116
|
if (!apiKey) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webhooks-cc/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|
|
@@ -19,6 +19,13 @@
|
|
|
19
19
|
"dist",
|
|
20
20
|
"README.md"
|
|
21
21
|
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsup",
|
|
24
|
+
"dev": "tsup --watch",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
22
29
|
"keywords": [
|
|
23
30
|
"webhook",
|
|
24
31
|
"mcp",
|
|
@@ -37,19 +44,13 @@
|
|
|
37
44
|
"homepage": "https://webhooks.cc",
|
|
38
45
|
"dependencies": {
|
|
39
46
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
47
|
+
"@webhooks-cc/sdk": "workspace:*",
|
|
48
|
+
"zod": "^3.25.76"
|
|
42
49
|
},
|
|
43
50
|
"devDependencies": {
|
|
44
|
-
"@types/node": "^
|
|
51
|
+
"@types/node": "^25.6.0",
|
|
45
52
|
"tsup": "^8.5.1",
|
|
46
53
|
"typescript": "^6.0.2",
|
|
47
|
-
"vitest": "^4.1.
|
|
48
|
-
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"build": "tsup",
|
|
51
|
-
"dev": "tsup --watch",
|
|
52
|
-
"typecheck": "tsc --noEmit",
|
|
53
|
-
"test": "vitest run"
|
|
54
|
+
"vitest": "^4.1.4"
|
|
54
55
|
}
|
|
55
|
-
}
|
|
56
|
+
}
|