@webhooks-cc/mcp 1.11.0 → 1.12.1
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 +5 -4
- package/dist/bin/mcp.js +85 -25
- package/dist/index.js +85 -25
- package/dist/index.mjs +85 -25
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -97,13 +97,14 @@ The request tools and resources return emails too (filter with `kind`), without
|
|
|
97
97
|
|
|
98
98
|
### Forwarding tools
|
|
99
99
|
|
|
100
|
-
Forward every captured email to a URL
|
|
100
|
+
Forward every captured HTTP request and email to a URL or a Slack or Discord channel, as received,
|
|
101
|
+
as signed JSON or as a chat message, retried and logged (owner only):
|
|
101
102
|
|
|
102
|
-
- `configure_forwarding`
|
|
103
|
+
- `configure_forwarding` (URL, on/off, kinds, format, your headers, path, retries, order, sent time field)
|
|
103
104
|
- `get_forwarding_secret` (returns the `whsec_` secret; with `rotate: true`, replaces it)
|
|
104
105
|
- `test_forwarding`
|
|
105
|
-
- `list_deliveries`
|
|
106
|
-
- `
|
|
106
|
+
- `list_deliveries` (the delivery log with its counts, or every try of one request)
|
|
107
|
+
- `redeliver` (one request, or every failed delivery of an endpoint)
|
|
107
108
|
|
|
108
109
|
### Send and test tools
|
|
109
110
|
|
package/dist/bin/mcp.js
CHANGED
|
@@ -157,6 +157,13 @@ function omitHeaders(holder) {
|
|
|
157
157
|
holder.headers = {};
|
|
158
158
|
holder.headersOmitted = `${count} headers left out to fit the output`;
|
|
159
159
|
}
|
|
160
|
+
function trimSpam(holder) {
|
|
161
|
+
const spam = holder.spam;
|
|
162
|
+
if (spam && typeof spam === "object" && !Array.isArray(spam)) {
|
|
163
|
+
const { score, required, action, error } = spam;
|
|
164
|
+
holder.spam = { score, required, action, error };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
160
167
|
function capExtracts(holder) {
|
|
161
168
|
if (Array.isArray(holder.codes) && holder.codes.length > MAX_EXTRACTS) {
|
|
162
169
|
holder.codes = holder.codes.slice(0, MAX_EXTRACTS);
|
|
@@ -221,7 +228,8 @@ var ESSENTIAL_EMAIL_FIELDS = [
|
|
|
221
228
|
"date",
|
|
222
229
|
"text",
|
|
223
230
|
"textTruncated",
|
|
224
|
-
"htmlSize"
|
|
231
|
+
"htmlSize",
|
|
232
|
+
"spam"
|
|
225
233
|
];
|
|
226
234
|
function compactRequest(request) {
|
|
227
235
|
if (request.kind !== "email") return request;
|
|
@@ -248,6 +256,7 @@ function compactRequest(request) {
|
|
|
248
256
|
const budget = MAX_OUTPUT - 128;
|
|
249
257
|
shrinkToFit(root, budget, [
|
|
250
258
|
() => omitHeaders(compact),
|
|
259
|
+
() => trimSpam(email),
|
|
251
260
|
() => capLists(email),
|
|
252
261
|
() => cutStringToFit(root, email, "text", "textTruncated", budget, TEXT_FLOOR),
|
|
253
262
|
// Down to the essentials there may be room for more of the text again.
|
|
@@ -737,6 +746,7 @@ var ESSENTIAL_DETAIL_FIELDS = [
|
|
|
737
746
|
"from",
|
|
738
747
|
"size",
|
|
739
748
|
"test",
|
|
749
|
+
"spam",
|
|
740
750
|
"text",
|
|
741
751
|
"textTruncated",
|
|
742
752
|
"htmlSize",
|
|
@@ -765,6 +775,7 @@ function emailDetail(client, email, view, includeHtml) {
|
|
|
765
775
|
};
|
|
766
776
|
shrinkToFit(detail, MAX_BODY_SIZE, [
|
|
767
777
|
() => omitHeaders(detail),
|
|
778
|
+
() => trimSpam(detail),
|
|
768
779
|
() => capExtracts(detail),
|
|
769
780
|
() => capLists(detail),
|
|
770
781
|
() => cutStringToFit(detail, detail, "html", "htmlTruncated", MAX_BODY_SIZE),
|
|
@@ -1537,25 +1548,52 @@ function registerTools(server, client) {
|
|
|
1537
1548
|
);
|
|
1538
1549
|
server.tool(
|
|
1539
1550
|
"configure_forwarding",
|
|
1540
|
-
"
|
|
1551
|
+
"Change an endpoint's forwarding: the URL, on or off, which kinds (HTTP requests, emails), the format, your own headers, and the advanced options. Only the arguments you pass change. Owner only. Saving the first URL creates the signing secret. A Slack or Discord webhook URL gets a chat message automatically.",
|
|
1541
1552
|
{
|
|
1542
1553
|
slug: import_zod2.z.string().describe("The endpoint slug"),
|
|
1543
1554
|
url: httpUrlSchema.nullable().optional().describe("https URL on a public host name, or null to remove it (forwarding must be off)"),
|
|
1544
|
-
enabled: import_zod2.z.boolean().optional().describe("Turn forwarding on (needs a URL) or off (fails deliveries still waiting)")
|
|
1555
|
+
enabled: import_zod2.z.boolean().optional().describe("Turn forwarding on (needs a URL) or off (fails deliveries still waiting)"),
|
|
1556
|
+
http: import_zod2.z.boolean().optional().describe("Forward HTTP requests"),
|
|
1557
|
+
email: import_zod2.z.boolean().optional().describe("Forward emails"),
|
|
1558
|
+
format: import_zod2.z.enum(["auto", "as_received", "json", "chat"]).optional().describe(
|
|
1559
|
+
"auto (pick from the URL), as_received (relay HTTP requests unchanged; emails as signed JSON), json (signed JSON envelope for both kinds) or chat (Slack or Discord message)"
|
|
1560
|
+
),
|
|
1561
|
+
headers: import_zod2.z.array(
|
|
1562
|
+
import_zod2.z.object({
|
|
1563
|
+
name: import_zod2.z.string().describe("Header name, e.g. Authorization"),
|
|
1564
|
+
value: import_zod2.z.string().nullable().describe("Header value, or null to keep the value stored under this name")
|
|
1565
|
+
})
|
|
1566
|
+
).max(10).optional().describe(
|
|
1567
|
+
"Your own headers for every delivery (not chat). Replaces the whole list; [] removes them"
|
|
1568
|
+
),
|
|
1569
|
+
appendPath: import_zod2.z.boolean().optional().describe("Add the path after the slug to the URL for relayed requests (default true)"),
|
|
1570
|
+
retrySeconds: import_zod2.z.union([import_zod2.z.literal(0), import_zod2.z.literal(3600), import_zod2.z.literal(86400)]).optional().describe("Retry failed deliveries for 86400 (default), 3600 or 0 seconds"),
|
|
1571
|
+
keepOrder: import_zod2.z.boolean().optional().describe("One delivery at a time, in capture order (default false)"),
|
|
1572
|
+
sentField: import_zod2.z.string().max(128).nullable().optional().describe(
|
|
1573
|
+
"JSON body field with the sender's own timestamp, e.g. publishedAt or data.publishedAt; null clears it"
|
|
1574
|
+
)
|
|
1545
1575
|
},
|
|
1546
|
-
withErrorHandling(async ({ slug,
|
|
1547
|
-
const endpoint = await client.forwarding.configure(slug,
|
|
1576
|
+
withErrorHandling(async ({ slug, ...options }) => {
|
|
1577
|
+
const endpoint = await client.forwarding.configure(slug, options);
|
|
1548
1578
|
return jsonContent({
|
|
1549
1579
|
slug: endpoint.slug,
|
|
1550
1580
|
forwardEnabled: endpoint.forwardEnabled ?? false,
|
|
1551
1581
|
forwardUrl: endpoint.forwardUrl ?? null,
|
|
1552
|
-
hasForwardSecret: endpoint.hasForwardSecret ?? false
|
|
1582
|
+
hasForwardSecret: endpoint.hasForwardSecret ?? false,
|
|
1583
|
+
forwardHttp: endpoint.forwardHttp ?? false,
|
|
1584
|
+
forwardEmail: endpoint.forwardEmail ?? false,
|
|
1585
|
+
forwardFormat: endpoint.forwardFormat ?? "auto",
|
|
1586
|
+
forwardHeaders: endpoint.forwardHeaders ?? [],
|
|
1587
|
+
forwardAppendPath: endpoint.forwardAppendPath ?? true,
|
|
1588
|
+
forwardRetrySeconds: endpoint.forwardRetrySeconds ?? 86400,
|
|
1589
|
+
forwardKeepOrder: endpoint.forwardKeepOrder ?? false,
|
|
1590
|
+
forwardSentField: endpoint.forwardSentField ?? null
|
|
1553
1591
|
});
|
|
1554
1592
|
})
|
|
1555
1593
|
);
|
|
1556
1594
|
server.tool(
|
|
1557
1595
|
"get_forwarding_secret",
|
|
1558
|
-
"Read (or with rotate: true, replace) the whsec_ secret forwarded
|
|
1596
|
+
"Read (or with rotate: true, replace) the whsec_ secret forwarded deliveries are signed with. Owner only. Treat the value as a credential: put it in the handler's environment, not in code.",
|
|
1559
1597
|
{
|
|
1560
1598
|
slug: import_zod2.z.string().describe("The endpoint slug"),
|
|
1561
1599
|
rotate: import_zod2.z.boolean().default(false).describe("Replace the secret; every delivery from now on uses the new one")
|
|
@@ -1567,35 +1605,57 @@ function registerTools(server, client) {
|
|
|
1567
1605
|
);
|
|
1568
1606
|
server.tool(
|
|
1569
1607
|
"test_forwarding",
|
|
1570
|
-
"
|
|
1571
|
-
{
|
|
1572
|
-
|
|
1573
|
-
|
|
1608
|
+
"Send the endpoint's newest captured request of a kind (or a sample) to its forwarding URL once, exactly as a delivery would go out, and report the answer. Works with forwarding on or off; nothing is retried or logged.",
|
|
1609
|
+
{
|
|
1610
|
+
slug: import_zod2.z.string().describe("The endpoint slug"),
|
|
1611
|
+
kind: import_zod2.z.enum(["http", "email"]).optional().describe(
|
|
1612
|
+
"Which kind to send (default: http when HTTP requests are forwarded, else email)"
|
|
1613
|
+
)
|
|
1614
|
+
},
|
|
1615
|
+
withErrorHandling(async ({ slug, kind }) => {
|
|
1616
|
+
return jsonContent(await client.forwarding.test(slug, kind ? { kind } : {}));
|
|
1574
1617
|
})
|
|
1575
1618
|
);
|
|
1576
1619
|
server.tool(
|
|
1577
1620
|
"list_deliveries",
|
|
1578
|
-
"List forwarding deliveries: an endpoint's
|
|
1621
|
+
"List forwarding deliveries: an endpoint's delivery log with its counts (endpointSlug), or every delivery and try of one captured request or email (requestId). Rows carry senderAt when the request had the sender's own timestamp, so receivedAt - senderAt is the time to reach webhooks.cc.",
|
|
1579
1622
|
{
|
|
1580
|
-
endpointSlug: import_zod2.z.string().optional().describe("List the endpoint's
|
|
1581
|
-
requestId: import_zod2.z.string().optional().describe("List every delivery and try of this
|
|
1582
|
-
limit: import_zod2.z.number().int().min(1).max(
|
|
1623
|
+
endpointSlug: import_zod2.z.string().optional().describe("List the endpoint's delivery log"),
|
|
1624
|
+
requestId: import_zod2.z.string().optional().describe("List every delivery and try of this request"),
|
|
1625
|
+
limit: import_zod2.z.number().int().min(1).max(100).default(20).describe("With endpointSlug: max rows"),
|
|
1626
|
+
status: import_zod2.z.enum(["all", "pending", "failed"]).default("all").describe("With endpointSlug: all, pending (queued or retrying) or failed"),
|
|
1627
|
+
before: import_zod2.z.string().optional().describe("With endpointSlug: a row's cursor, to list older rows")
|
|
1583
1628
|
},
|
|
1584
|
-
withErrorHandling(async ({ endpointSlug, requestId, limit }) => {
|
|
1629
|
+
withErrorHandling(async ({ endpointSlug, requestId, limit, status, before }) => {
|
|
1585
1630
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
1586
1631
|
throw new Error("Pass exactly one of endpointSlug or requestId");
|
|
1587
1632
|
}
|
|
1588
|
-
return jsonContent(
|
|
1589
|
-
|
|
1590
|
-
|
|
1633
|
+
if (requestId) return jsonContent(await client.forwarding.requestDeliveries(requestId));
|
|
1634
|
+
const [summary, deliveries] = await Promise.all([
|
|
1635
|
+
client.forwarding.summary(endpointSlug),
|
|
1636
|
+
client.forwarding.deliveries(endpointSlug, { limit, status, before })
|
|
1637
|
+
]);
|
|
1638
|
+
return jsonContent({
|
|
1639
|
+
summary,
|
|
1640
|
+
deliveries,
|
|
1641
|
+
nextCursor: deliveries.length === limit ? deliveries[deliveries.length - 1]?.cursor ?? null : null
|
|
1642
|
+
});
|
|
1591
1643
|
})
|
|
1592
1644
|
);
|
|
1593
1645
|
server.tool(
|
|
1594
|
-
"
|
|
1595
|
-
"Forward
|
|
1596
|
-
{
|
|
1597
|
-
|
|
1598
|
-
|
|
1646
|
+
"redeliver",
|
|
1647
|
+
"Forward again with the endpoint's current settings: one captured request or email (requestId; it also sends one that was never forwarded), or every request whose latest delivery failed (endpointSlug, oldest first, up to 1,000 at a time). Owner only; fails while forwarding is off.",
|
|
1648
|
+
{
|
|
1649
|
+
requestId: import_zod2.z.string().optional().describe("The request or email to send again"),
|
|
1650
|
+
endpointSlug: import_zod2.z.string().optional().describe("Send every failed delivery of this endpoint again")
|
|
1651
|
+
},
|
|
1652
|
+
withErrorHandling(async ({ requestId, endpointSlug }) => {
|
|
1653
|
+
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
1654
|
+
throw new Error("Pass exactly one of requestId or endpointSlug");
|
|
1655
|
+
}
|
|
1656
|
+
return jsonContent(
|
|
1657
|
+
requestId ? await client.forwarding.redeliver(requestId) : await client.forwarding.redeliverFailed(endpointSlug)
|
|
1658
|
+
);
|
|
1599
1659
|
})
|
|
1600
1660
|
);
|
|
1601
1661
|
server.tool(
|
|
@@ -1974,7 +2034,7 @@ function registerSandboxTools(server, options = {}) {
|
|
|
1974
2034
|
}
|
|
1975
2035
|
|
|
1976
2036
|
// src/index.ts
|
|
1977
|
-
var VERSION = true ? "1.
|
|
2037
|
+
var VERSION = true ? "1.12.1" : "0.0.0-dev";
|
|
1978
2038
|
function createServer(options = {}) {
|
|
1979
2039
|
const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
|
|
1980
2040
|
const webhookUrl = options.webhookUrl ?? process.env.WHK_WEBHOOK_URL;
|
package/dist/index.js
CHANGED
|
@@ -180,6 +180,13 @@ function omitHeaders(holder) {
|
|
|
180
180
|
holder.headers = {};
|
|
181
181
|
holder.headersOmitted = `${count} headers left out to fit the output`;
|
|
182
182
|
}
|
|
183
|
+
function trimSpam(holder) {
|
|
184
|
+
const spam = holder.spam;
|
|
185
|
+
if (spam && typeof spam === "object" && !Array.isArray(spam)) {
|
|
186
|
+
const { score, required, action, error } = spam;
|
|
187
|
+
holder.spam = { score, required, action, error };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
183
190
|
function capExtracts(holder) {
|
|
184
191
|
if (Array.isArray(holder.codes) && holder.codes.length > MAX_EXTRACTS) {
|
|
185
192
|
holder.codes = holder.codes.slice(0, MAX_EXTRACTS);
|
|
@@ -244,7 +251,8 @@ var ESSENTIAL_EMAIL_FIELDS = [
|
|
|
244
251
|
"date",
|
|
245
252
|
"text",
|
|
246
253
|
"textTruncated",
|
|
247
|
-
"htmlSize"
|
|
254
|
+
"htmlSize",
|
|
255
|
+
"spam"
|
|
248
256
|
];
|
|
249
257
|
function compactRequest(request) {
|
|
250
258
|
if (request.kind !== "email") return request;
|
|
@@ -271,6 +279,7 @@ function compactRequest(request) {
|
|
|
271
279
|
const budget = MAX_OUTPUT - 128;
|
|
272
280
|
shrinkToFit(root, budget, [
|
|
273
281
|
() => omitHeaders(compact),
|
|
282
|
+
() => trimSpam(email),
|
|
274
283
|
() => capLists(email),
|
|
275
284
|
() => cutStringToFit(root, email, "text", "textTruncated", budget, TEXT_FLOOR),
|
|
276
285
|
// Down to the essentials there may be room for more of the text again.
|
|
@@ -760,6 +769,7 @@ var ESSENTIAL_DETAIL_FIELDS = [
|
|
|
760
769
|
"from",
|
|
761
770
|
"size",
|
|
762
771
|
"test",
|
|
772
|
+
"spam",
|
|
763
773
|
"text",
|
|
764
774
|
"textTruncated",
|
|
765
775
|
"htmlSize",
|
|
@@ -788,6 +798,7 @@ function emailDetail(client, email, view, includeHtml) {
|
|
|
788
798
|
};
|
|
789
799
|
shrinkToFit(detail, MAX_BODY_SIZE, [
|
|
790
800
|
() => omitHeaders(detail),
|
|
801
|
+
() => trimSpam(detail),
|
|
791
802
|
() => capExtracts(detail),
|
|
792
803
|
() => capLists(detail),
|
|
793
804
|
() => cutStringToFit(detail, detail, "html", "htmlTruncated", MAX_BODY_SIZE),
|
|
@@ -1560,25 +1571,52 @@ function registerTools(server, client) {
|
|
|
1560
1571
|
);
|
|
1561
1572
|
server.tool(
|
|
1562
1573
|
"configure_forwarding",
|
|
1563
|
-
"
|
|
1574
|
+
"Change an endpoint's forwarding: the URL, on or off, which kinds (HTTP requests, emails), the format, your own headers, and the advanced options. Only the arguments you pass change. Owner only. Saving the first URL creates the signing secret. A Slack or Discord webhook URL gets a chat message automatically.",
|
|
1564
1575
|
{
|
|
1565
1576
|
slug: import_zod2.z.string().describe("The endpoint slug"),
|
|
1566
1577
|
url: httpUrlSchema.nullable().optional().describe("https URL on a public host name, or null to remove it (forwarding must be off)"),
|
|
1567
|
-
enabled: import_zod2.z.boolean().optional().describe("Turn forwarding on (needs a URL) or off (fails deliveries still waiting)")
|
|
1578
|
+
enabled: import_zod2.z.boolean().optional().describe("Turn forwarding on (needs a URL) or off (fails deliveries still waiting)"),
|
|
1579
|
+
http: import_zod2.z.boolean().optional().describe("Forward HTTP requests"),
|
|
1580
|
+
email: import_zod2.z.boolean().optional().describe("Forward emails"),
|
|
1581
|
+
format: import_zod2.z.enum(["auto", "as_received", "json", "chat"]).optional().describe(
|
|
1582
|
+
"auto (pick from the URL), as_received (relay HTTP requests unchanged; emails as signed JSON), json (signed JSON envelope for both kinds) or chat (Slack or Discord message)"
|
|
1583
|
+
),
|
|
1584
|
+
headers: import_zod2.z.array(
|
|
1585
|
+
import_zod2.z.object({
|
|
1586
|
+
name: import_zod2.z.string().describe("Header name, e.g. Authorization"),
|
|
1587
|
+
value: import_zod2.z.string().nullable().describe("Header value, or null to keep the value stored under this name")
|
|
1588
|
+
})
|
|
1589
|
+
).max(10).optional().describe(
|
|
1590
|
+
"Your own headers for every delivery (not chat). Replaces the whole list; [] removes them"
|
|
1591
|
+
),
|
|
1592
|
+
appendPath: import_zod2.z.boolean().optional().describe("Add the path after the slug to the URL for relayed requests (default true)"),
|
|
1593
|
+
retrySeconds: import_zod2.z.union([import_zod2.z.literal(0), import_zod2.z.literal(3600), import_zod2.z.literal(86400)]).optional().describe("Retry failed deliveries for 86400 (default), 3600 or 0 seconds"),
|
|
1594
|
+
keepOrder: import_zod2.z.boolean().optional().describe("One delivery at a time, in capture order (default false)"),
|
|
1595
|
+
sentField: import_zod2.z.string().max(128).nullable().optional().describe(
|
|
1596
|
+
"JSON body field with the sender's own timestamp, e.g. publishedAt or data.publishedAt; null clears it"
|
|
1597
|
+
)
|
|
1568
1598
|
},
|
|
1569
|
-
withErrorHandling(async ({ slug,
|
|
1570
|
-
const endpoint = await client.forwarding.configure(slug,
|
|
1599
|
+
withErrorHandling(async ({ slug, ...options }) => {
|
|
1600
|
+
const endpoint = await client.forwarding.configure(slug, options);
|
|
1571
1601
|
return jsonContent({
|
|
1572
1602
|
slug: endpoint.slug,
|
|
1573
1603
|
forwardEnabled: endpoint.forwardEnabled ?? false,
|
|
1574
1604
|
forwardUrl: endpoint.forwardUrl ?? null,
|
|
1575
|
-
hasForwardSecret: endpoint.hasForwardSecret ?? false
|
|
1605
|
+
hasForwardSecret: endpoint.hasForwardSecret ?? false,
|
|
1606
|
+
forwardHttp: endpoint.forwardHttp ?? false,
|
|
1607
|
+
forwardEmail: endpoint.forwardEmail ?? false,
|
|
1608
|
+
forwardFormat: endpoint.forwardFormat ?? "auto",
|
|
1609
|
+
forwardHeaders: endpoint.forwardHeaders ?? [],
|
|
1610
|
+
forwardAppendPath: endpoint.forwardAppendPath ?? true,
|
|
1611
|
+
forwardRetrySeconds: endpoint.forwardRetrySeconds ?? 86400,
|
|
1612
|
+
forwardKeepOrder: endpoint.forwardKeepOrder ?? false,
|
|
1613
|
+
forwardSentField: endpoint.forwardSentField ?? null
|
|
1576
1614
|
});
|
|
1577
1615
|
})
|
|
1578
1616
|
);
|
|
1579
1617
|
server.tool(
|
|
1580
1618
|
"get_forwarding_secret",
|
|
1581
|
-
"Read (or with rotate: true, replace) the whsec_ secret forwarded
|
|
1619
|
+
"Read (or with rotate: true, replace) the whsec_ secret forwarded deliveries are signed with. Owner only. Treat the value as a credential: put it in the handler's environment, not in code.",
|
|
1582
1620
|
{
|
|
1583
1621
|
slug: import_zod2.z.string().describe("The endpoint slug"),
|
|
1584
1622
|
rotate: import_zod2.z.boolean().default(false).describe("Replace the secret; every delivery from now on uses the new one")
|
|
@@ -1590,35 +1628,57 @@ function registerTools(server, client) {
|
|
|
1590
1628
|
);
|
|
1591
1629
|
server.tool(
|
|
1592
1630
|
"test_forwarding",
|
|
1593
|
-
"
|
|
1594
|
-
{
|
|
1595
|
-
|
|
1596
|
-
|
|
1631
|
+
"Send the endpoint's newest captured request of a kind (or a sample) to its forwarding URL once, exactly as a delivery would go out, and report the answer. Works with forwarding on or off; nothing is retried or logged.",
|
|
1632
|
+
{
|
|
1633
|
+
slug: import_zod2.z.string().describe("The endpoint slug"),
|
|
1634
|
+
kind: import_zod2.z.enum(["http", "email"]).optional().describe(
|
|
1635
|
+
"Which kind to send (default: http when HTTP requests are forwarded, else email)"
|
|
1636
|
+
)
|
|
1637
|
+
},
|
|
1638
|
+
withErrorHandling(async ({ slug, kind }) => {
|
|
1639
|
+
return jsonContent(await client.forwarding.test(slug, kind ? { kind } : {}));
|
|
1597
1640
|
})
|
|
1598
1641
|
);
|
|
1599
1642
|
server.tool(
|
|
1600
1643
|
"list_deliveries",
|
|
1601
|
-
"List forwarding deliveries: an endpoint's
|
|
1644
|
+
"List forwarding deliveries: an endpoint's delivery log with its counts (endpointSlug), or every delivery and try of one captured request or email (requestId). Rows carry senderAt when the request had the sender's own timestamp, so receivedAt - senderAt is the time to reach webhooks.cc.",
|
|
1602
1645
|
{
|
|
1603
|
-
endpointSlug: import_zod2.z.string().optional().describe("List the endpoint's
|
|
1604
|
-
requestId: import_zod2.z.string().optional().describe("List every delivery and try of this
|
|
1605
|
-
limit: import_zod2.z.number().int().min(1).max(
|
|
1646
|
+
endpointSlug: import_zod2.z.string().optional().describe("List the endpoint's delivery log"),
|
|
1647
|
+
requestId: import_zod2.z.string().optional().describe("List every delivery and try of this request"),
|
|
1648
|
+
limit: import_zod2.z.number().int().min(1).max(100).default(20).describe("With endpointSlug: max rows"),
|
|
1649
|
+
status: import_zod2.z.enum(["all", "pending", "failed"]).default("all").describe("With endpointSlug: all, pending (queued or retrying) or failed"),
|
|
1650
|
+
before: import_zod2.z.string().optional().describe("With endpointSlug: a row's cursor, to list older rows")
|
|
1606
1651
|
},
|
|
1607
|
-
withErrorHandling(async ({ endpointSlug, requestId, limit }) => {
|
|
1652
|
+
withErrorHandling(async ({ endpointSlug, requestId, limit, status, before }) => {
|
|
1608
1653
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
1609
1654
|
throw new Error("Pass exactly one of endpointSlug or requestId");
|
|
1610
1655
|
}
|
|
1611
|
-
return jsonContent(
|
|
1612
|
-
|
|
1613
|
-
|
|
1656
|
+
if (requestId) return jsonContent(await client.forwarding.requestDeliveries(requestId));
|
|
1657
|
+
const [summary, deliveries] = await Promise.all([
|
|
1658
|
+
client.forwarding.summary(endpointSlug),
|
|
1659
|
+
client.forwarding.deliveries(endpointSlug, { limit, status, before })
|
|
1660
|
+
]);
|
|
1661
|
+
return jsonContent({
|
|
1662
|
+
summary,
|
|
1663
|
+
deliveries,
|
|
1664
|
+
nextCursor: deliveries.length === limit ? deliveries[deliveries.length - 1]?.cursor ?? null : null
|
|
1665
|
+
});
|
|
1614
1666
|
})
|
|
1615
1667
|
);
|
|
1616
1668
|
server.tool(
|
|
1617
|
-
"
|
|
1618
|
-
"Forward
|
|
1619
|
-
{
|
|
1620
|
-
|
|
1621
|
-
|
|
1669
|
+
"redeliver",
|
|
1670
|
+
"Forward again with the endpoint's current settings: one captured request or email (requestId; it also sends one that was never forwarded), or every request whose latest delivery failed (endpointSlug, oldest first, up to 1,000 at a time). Owner only; fails while forwarding is off.",
|
|
1671
|
+
{
|
|
1672
|
+
requestId: import_zod2.z.string().optional().describe("The request or email to send again"),
|
|
1673
|
+
endpointSlug: import_zod2.z.string().optional().describe("Send every failed delivery of this endpoint again")
|
|
1674
|
+
},
|
|
1675
|
+
withErrorHandling(async ({ requestId, endpointSlug }) => {
|
|
1676
|
+
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
1677
|
+
throw new Error("Pass exactly one of requestId or endpointSlug");
|
|
1678
|
+
}
|
|
1679
|
+
return jsonContent(
|
|
1680
|
+
requestId ? await client.forwarding.redeliver(requestId) : await client.forwarding.redeliverFailed(endpointSlug)
|
|
1681
|
+
);
|
|
1622
1682
|
})
|
|
1623
1683
|
);
|
|
1624
1684
|
server.tool(
|
|
@@ -2000,7 +2060,7 @@ function registerAgentRegistrationTools(server) {
|
|
|
2000
2060
|
}
|
|
2001
2061
|
|
|
2002
2062
|
// src/index.ts
|
|
2003
|
-
var VERSION = true ? "1.
|
|
2063
|
+
var VERSION = true ? "1.12.1" : "0.0.0-dev";
|
|
2004
2064
|
function createServer(options = {}) {
|
|
2005
2065
|
const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
|
|
2006
2066
|
const webhookUrl = options.webhookUrl ?? process.env.WHK_WEBHOOK_URL;
|
package/dist/index.mjs
CHANGED
|
@@ -151,6 +151,13 @@ function omitHeaders(holder) {
|
|
|
151
151
|
holder.headers = {};
|
|
152
152
|
holder.headersOmitted = `${count} headers left out to fit the output`;
|
|
153
153
|
}
|
|
154
|
+
function trimSpam(holder) {
|
|
155
|
+
const spam = holder.spam;
|
|
156
|
+
if (spam && typeof spam === "object" && !Array.isArray(spam)) {
|
|
157
|
+
const { score, required, action, error } = spam;
|
|
158
|
+
holder.spam = { score, required, action, error };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
154
161
|
function capExtracts(holder) {
|
|
155
162
|
if (Array.isArray(holder.codes) && holder.codes.length > MAX_EXTRACTS) {
|
|
156
163
|
holder.codes = holder.codes.slice(0, MAX_EXTRACTS);
|
|
@@ -215,7 +222,8 @@ var ESSENTIAL_EMAIL_FIELDS = [
|
|
|
215
222
|
"date",
|
|
216
223
|
"text",
|
|
217
224
|
"textTruncated",
|
|
218
|
-
"htmlSize"
|
|
225
|
+
"htmlSize",
|
|
226
|
+
"spam"
|
|
219
227
|
];
|
|
220
228
|
function compactRequest(request) {
|
|
221
229
|
if (request.kind !== "email") return request;
|
|
@@ -242,6 +250,7 @@ function compactRequest(request) {
|
|
|
242
250
|
const budget = MAX_OUTPUT - 128;
|
|
243
251
|
shrinkToFit(root, budget, [
|
|
244
252
|
() => omitHeaders(compact),
|
|
253
|
+
() => trimSpam(email),
|
|
245
254
|
() => capLists(email),
|
|
246
255
|
() => cutStringToFit(root, email, "text", "textTruncated", budget, TEXT_FLOOR),
|
|
247
256
|
// Down to the essentials there may be room for more of the text again.
|
|
@@ -757,6 +766,7 @@ var ESSENTIAL_DETAIL_FIELDS = [
|
|
|
757
766
|
"from",
|
|
758
767
|
"size",
|
|
759
768
|
"test",
|
|
769
|
+
"spam",
|
|
760
770
|
"text",
|
|
761
771
|
"textTruncated",
|
|
762
772
|
"htmlSize",
|
|
@@ -785,6 +795,7 @@ function emailDetail(client, email, view, includeHtml) {
|
|
|
785
795
|
};
|
|
786
796
|
shrinkToFit(detail, MAX_BODY_SIZE, [
|
|
787
797
|
() => omitHeaders(detail),
|
|
798
|
+
() => trimSpam(detail),
|
|
788
799
|
() => capExtracts(detail),
|
|
789
800
|
() => capLists(detail),
|
|
790
801
|
() => cutStringToFit(detail, detail, "html", "htmlTruncated", MAX_BODY_SIZE),
|
|
@@ -1557,25 +1568,52 @@ function registerTools(server, client) {
|
|
|
1557
1568
|
);
|
|
1558
1569
|
server.tool(
|
|
1559
1570
|
"configure_forwarding",
|
|
1560
|
-
"
|
|
1571
|
+
"Change an endpoint's forwarding: the URL, on or off, which kinds (HTTP requests, emails), the format, your own headers, and the advanced options. Only the arguments you pass change. Owner only. Saving the first URL creates the signing secret. A Slack or Discord webhook URL gets a chat message automatically.",
|
|
1561
1572
|
{
|
|
1562
1573
|
slug: z2.string().describe("The endpoint slug"),
|
|
1563
1574
|
url: httpUrlSchema.nullable().optional().describe("https URL on a public host name, or null to remove it (forwarding must be off)"),
|
|
1564
|
-
enabled: z2.boolean().optional().describe("Turn forwarding on (needs a URL) or off (fails deliveries still waiting)")
|
|
1575
|
+
enabled: z2.boolean().optional().describe("Turn forwarding on (needs a URL) or off (fails deliveries still waiting)"),
|
|
1576
|
+
http: z2.boolean().optional().describe("Forward HTTP requests"),
|
|
1577
|
+
email: z2.boolean().optional().describe("Forward emails"),
|
|
1578
|
+
format: z2.enum(["auto", "as_received", "json", "chat"]).optional().describe(
|
|
1579
|
+
"auto (pick from the URL), as_received (relay HTTP requests unchanged; emails as signed JSON), json (signed JSON envelope for both kinds) or chat (Slack or Discord message)"
|
|
1580
|
+
),
|
|
1581
|
+
headers: z2.array(
|
|
1582
|
+
z2.object({
|
|
1583
|
+
name: z2.string().describe("Header name, e.g. Authorization"),
|
|
1584
|
+
value: z2.string().nullable().describe("Header value, or null to keep the value stored under this name")
|
|
1585
|
+
})
|
|
1586
|
+
).max(10).optional().describe(
|
|
1587
|
+
"Your own headers for every delivery (not chat). Replaces the whole list; [] removes them"
|
|
1588
|
+
),
|
|
1589
|
+
appendPath: z2.boolean().optional().describe("Add the path after the slug to the URL for relayed requests (default true)"),
|
|
1590
|
+
retrySeconds: z2.union([z2.literal(0), z2.literal(3600), z2.literal(86400)]).optional().describe("Retry failed deliveries for 86400 (default), 3600 or 0 seconds"),
|
|
1591
|
+
keepOrder: z2.boolean().optional().describe("One delivery at a time, in capture order (default false)"),
|
|
1592
|
+
sentField: z2.string().max(128).nullable().optional().describe(
|
|
1593
|
+
"JSON body field with the sender's own timestamp, e.g. publishedAt or data.publishedAt; null clears it"
|
|
1594
|
+
)
|
|
1565
1595
|
},
|
|
1566
|
-
withErrorHandling(async ({ slug,
|
|
1567
|
-
const endpoint = await client.forwarding.configure(slug,
|
|
1596
|
+
withErrorHandling(async ({ slug, ...options }) => {
|
|
1597
|
+
const endpoint = await client.forwarding.configure(slug, options);
|
|
1568
1598
|
return jsonContent({
|
|
1569
1599
|
slug: endpoint.slug,
|
|
1570
1600
|
forwardEnabled: endpoint.forwardEnabled ?? false,
|
|
1571
1601
|
forwardUrl: endpoint.forwardUrl ?? null,
|
|
1572
|
-
hasForwardSecret: endpoint.hasForwardSecret ?? false
|
|
1602
|
+
hasForwardSecret: endpoint.hasForwardSecret ?? false,
|
|
1603
|
+
forwardHttp: endpoint.forwardHttp ?? false,
|
|
1604
|
+
forwardEmail: endpoint.forwardEmail ?? false,
|
|
1605
|
+
forwardFormat: endpoint.forwardFormat ?? "auto",
|
|
1606
|
+
forwardHeaders: endpoint.forwardHeaders ?? [],
|
|
1607
|
+
forwardAppendPath: endpoint.forwardAppendPath ?? true,
|
|
1608
|
+
forwardRetrySeconds: endpoint.forwardRetrySeconds ?? 86400,
|
|
1609
|
+
forwardKeepOrder: endpoint.forwardKeepOrder ?? false,
|
|
1610
|
+
forwardSentField: endpoint.forwardSentField ?? null
|
|
1573
1611
|
});
|
|
1574
1612
|
})
|
|
1575
1613
|
);
|
|
1576
1614
|
server.tool(
|
|
1577
1615
|
"get_forwarding_secret",
|
|
1578
|
-
"Read (or with rotate: true, replace) the whsec_ secret forwarded
|
|
1616
|
+
"Read (or with rotate: true, replace) the whsec_ secret forwarded deliveries are signed with. Owner only. Treat the value as a credential: put it in the handler's environment, not in code.",
|
|
1579
1617
|
{
|
|
1580
1618
|
slug: z2.string().describe("The endpoint slug"),
|
|
1581
1619
|
rotate: z2.boolean().default(false).describe("Replace the secret; every delivery from now on uses the new one")
|
|
@@ -1587,35 +1625,57 @@ function registerTools(server, client) {
|
|
|
1587
1625
|
);
|
|
1588
1626
|
server.tool(
|
|
1589
1627
|
"test_forwarding",
|
|
1590
|
-
"
|
|
1591
|
-
{
|
|
1592
|
-
|
|
1593
|
-
|
|
1628
|
+
"Send the endpoint's newest captured request of a kind (or a sample) to its forwarding URL once, exactly as a delivery would go out, and report the answer. Works with forwarding on or off; nothing is retried or logged.",
|
|
1629
|
+
{
|
|
1630
|
+
slug: z2.string().describe("The endpoint slug"),
|
|
1631
|
+
kind: z2.enum(["http", "email"]).optional().describe(
|
|
1632
|
+
"Which kind to send (default: http when HTTP requests are forwarded, else email)"
|
|
1633
|
+
)
|
|
1634
|
+
},
|
|
1635
|
+
withErrorHandling(async ({ slug, kind }) => {
|
|
1636
|
+
return jsonContent(await client.forwarding.test(slug, kind ? { kind } : {}));
|
|
1594
1637
|
})
|
|
1595
1638
|
);
|
|
1596
1639
|
server.tool(
|
|
1597
1640
|
"list_deliveries",
|
|
1598
|
-
"List forwarding deliveries: an endpoint's
|
|
1641
|
+
"List forwarding deliveries: an endpoint's delivery log with its counts (endpointSlug), or every delivery and try of one captured request or email (requestId). Rows carry senderAt when the request had the sender's own timestamp, so receivedAt - senderAt is the time to reach webhooks.cc.",
|
|
1599
1642
|
{
|
|
1600
|
-
endpointSlug: z2.string().optional().describe("List the endpoint's
|
|
1601
|
-
requestId: z2.string().optional().describe("List every delivery and try of this
|
|
1602
|
-
limit: z2.number().int().min(1).max(
|
|
1643
|
+
endpointSlug: z2.string().optional().describe("List the endpoint's delivery log"),
|
|
1644
|
+
requestId: z2.string().optional().describe("List every delivery and try of this request"),
|
|
1645
|
+
limit: z2.number().int().min(1).max(100).default(20).describe("With endpointSlug: max rows"),
|
|
1646
|
+
status: z2.enum(["all", "pending", "failed"]).default("all").describe("With endpointSlug: all, pending (queued or retrying) or failed"),
|
|
1647
|
+
before: z2.string().optional().describe("With endpointSlug: a row's cursor, to list older rows")
|
|
1603
1648
|
},
|
|
1604
|
-
withErrorHandling(async ({ endpointSlug, requestId, limit }) => {
|
|
1649
|
+
withErrorHandling(async ({ endpointSlug, requestId, limit, status, before }) => {
|
|
1605
1650
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
1606
1651
|
throw new Error("Pass exactly one of endpointSlug or requestId");
|
|
1607
1652
|
}
|
|
1608
|
-
return jsonContent(
|
|
1609
|
-
|
|
1610
|
-
|
|
1653
|
+
if (requestId) return jsonContent(await client.forwarding.requestDeliveries(requestId));
|
|
1654
|
+
const [summary, deliveries] = await Promise.all([
|
|
1655
|
+
client.forwarding.summary(endpointSlug),
|
|
1656
|
+
client.forwarding.deliveries(endpointSlug, { limit, status, before })
|
|
1657
|
+
]);
|
|
1658
|
+
return jsonContent({
|
|
1659
|
+
summary,
|
|
1660
|
+
deliveries,
|
|
1661
|
+
nextCursor: deliveries.length === limit ? deliveries[deliveries.length - 1]?.cursor ?? null : null
|
|
1662
|
+
});
|
|
1611
1663
|
})
|
|
1612
1664
|
);
|
|
1613
1665
|
server.tool(
|
|
1614
|
-
"
|
|
1615
|
-
"Forward
|
|
1616
|
-
{
|
|
1617
|
-
|
|
1618
|
-
|
|
1666
|
+
"redeliver",
|
|
1667
|
+
"Forward again with the endpoint's current settings: one captured request or email (requestId; it also sends one that was never forwarded), or every request whose latest delivery failed (endpointSlug, oldest first, up to 1,000 at a time). Owner only; fails while forwarding is off.",
|
|
1668
|
+
{
|
|
1669
|
+
requestId: z2.string().optional().describe("The request or email to send again"),
|
|
1670
|
+
endpointSlug: z2.string().optional().describe("Send every failed delivery of this endpoint again")
|
|
1671
|
+
},
|
|
1672
|
+
withErrorHandling(async ({ requestId, endpointSlug }) => {
|
|
1673
|
+
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
1674
|
+
throw new Error("Pass exactly one of requestId or endpointSlug");
|
|
1675
|
+
}
|
|
1676
|
+
return jsonContent(
|
|
1677
|
+
requestId ? await client.forwarding.redeliver(requestId) : await client.forwarding.redeliverFailed(endpointSlug)
|
|
1678
|
+
);
|
|
1619
1679
|
})
|
|
1620
1680
|
);
|
|
1621
1681
|
server.tool(
|
|
@@ -1997,7 +2057,7 @@ function registerAgentRegistrationTools(server) {
|
|
|
1997
2057
|
}
|
|
1998
2058
|
|
|
1999
2059
|
// src/index.ts
|
|
2000
|
-
var VERSION = true ? "1.
|
|
2060
|
+
var VERSION = true ? "1.12.1" : "0.0.0-dev";
|
|
2001
2061
|
function createServer(options = {}) {
|
|
2002
2062
|
const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
|
|
2003
2063
|
const webhookUrl = options.webhookUrl ?? process.env.WHK_WEBHOOK_URL;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webhooks-cc/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.1",
|
|
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",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@modelcontextprotocol/sdk": "^1.31.0",
|
|
40
40
|
"zod": "^4.6.5",
|
|
41
|
-
"@webhooks-cc/sdk": "1.
|
|
41
|
+
"@webhooks-cc/sdk": "1.15.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node": "^26.6.4",
|