@webhooks-cc/mcp 1.24.0 → 2.0.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 +41 -14
- package/dist/bin/mcp.js +1030 -583
- package/dist/{chunk-SOQIUGLA.mjs → chunk-VANGXSK7.mjs} +222 -3
- package/dist/index.d.mts +84 -10
- package/dist/index.d.ts +84 -10
- package/dist/index.js +1051 -580
- package/dist/index.mjs +290 -34
- package/dist/tools-Bg9zP80n.d.mts +276 -0
- package/dist/tools-Bg9zP80n.d.ts +276 -0
- package/dist/tools.d.mts +3 -200
- package/dist/tools.d.ts +3 -200
- package/dist/tools.js +226 -3
- package/dist/tools.mjs +15 -1
- package/package.json +4 -3
package/dist/bin/mcp.js
CHANGED
|
@@ -2,125 +2,219 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
// bin/mcp.ts
|
|
5
|
-
var import_stdio = require("@modelcontextprotocol/
|
|
5
|
+
var import_stdio = require("@modelcontextprotocol/server/stdio");
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
|
-
var
|
|
8
|
+
var import_server3 = require("@modelcontextprotocol/server");
|
|
9
9
|
var import_sdk3 = require("@webhooks-cc/sdk");
|
|
10
10
|
|
|
11
|
-
// src/
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
type: "text",
|
|
38
|
-
text: [
|
|
39
|
-
scope,
|
|
40
|
-
"Diagnose webhook delivery step by step.",
|
|
41
|
-
"Use list_endpoints or get_endpoint to confirm the endpoint and URL.",
|
|
42
|
-
"Use list_requests, wait_for_request, or wait_for_requests to check whether anything arrived.",
|
|
43
|
-
"If the provider is known, use preview_webhook or send_webhook to reproduce the webhook with realistic signing.",
|
|
44
|
-
"Use verify_signature when a secret is available.",
|
|
45
|
-
"Use compare_requests to compare retries or changed payloads.",
|
|
46
|
-
"Conclude with the most likely cause and the next concrete fix."
|
|
47
|
-
].filter(Boolean).join(" ")
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
);
|
|
54
|
-
server.registerPrompt(
|
|
55
|
-
"setup_provider_testing",
|
|
56
|
-
{
|
|
57
|
-
title: "Setup Provider Testing",
|
|
58
|
-
description: "Guide an agent through setting up webhook testing for a provider.",
|
|
59
|
-
argsSchema: {
|
|
60
|
-
provider: import_zod.z.string().describe("Webhook provider to test"),
|
|
61
|
-
targetUrl: import_zod.z.string().optional().describe("Optional local or remote target URL")
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
async ({ provider, targetUrl }) => {
|
|
65
|
-
return {
|
|
66
|
-
description: `Set up webhook testing for ${provider}.`,
|
|
67
|
-
messages: [
|
|
68
|
-
{
|
|
69
|
-
role: "user",
|
|
70
|
-
content: {
|
|
71
|
-
type: "text",
|
|
72
|
-
text: [
|
|
73
|
-
`Set up webhook testing for ${provider}.`,
|
|
74
|
-
"Use list_provider_templates to inspect supported templates and signing details first.",
|
|
75
|
-
"Create an endpoint with create_endpoint, preferably ephemeral.",
|
|
76
|
-
"If a target URL is provided, use preview_webhook before send_to so the request shape is visible.",
|
|
77
|
-
targetUrl ? `Target URL: ${targetUrl}.` : null,
|
|
78
|
-
"Send a realistic provider webhook, wait for capture, and verify the signature if a secret is available.",
|
|
79
|
-
"Once real deliveries are captured, use generate_payload_schema to write the payload types for the handler.",
|
|
80
|
-
"Return the endpoint URL, the exact tools used, and the next step for the developer."
|
|
81
|
-
].filter(Boolean).join(" ")
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
]
|
|
85
|
-
};
|
|
86
|
-
}
|
|
11
|
+
// src/local-tools.ts
|
|
12
|
+
var import_server = require("@modelcontextprotocol/server");
|
|
13
|
+
var import_zod2 = require("zod");
|
|
14
|
+
|
|
15
|
+
// src/catalog.ts
|
|
16
|
+
var TOOL_GROUPS = {
|
|
17
|
+
core: "Endpoints, captured requests and emails, sending test webhooks, signatures, forwarding destinations, deliveries, previews of your local app and their logs, usage and approvals",
|
|
18
|
+
forwarding: "The rest of forwarding: preview what a destination would send, the endpoint's forwarding settings and signing secret, test deliveries",
|
|
19
|
+
layouts: "Email layouts and named fields: try layouts and fields on a captured email, add ready-made layouts, and AI setup",
|
|
20
|
+
extras: "Bulk endpoint changes, favourites, request counts and payload schemas",
|
|
21
|
+
teams: "Teams: members, invites, sharing endpoints with a team, and pricing team changes",
|
|
22
|
+
account: "The account: billing, API keys, connected agents, public share links to requests, and the display name",
|
|
23
|
+
domains: "Custom inbound email domains: add, verify, route and remove them",
|
|
24
|
+
sheets: "Google Sheets destinations: Google accounts, spreadsheets and their tabs",
|
|
25
|
+
names: "Preview names: reserve, release and password-protect them"
|
|
26
|
+
};
|
|
27
|
+
var TOOL_GROUP_NAMES = Object.keys(TOOL_GROUPS);
|
|
28
|
+
var read = { readOnly: true };
|
|
29
|
+
var write = {};
|
|
30
|
+
var update = { idempotent: true };
|
|
31
|
+
var remove = { destructive: true, idempotent: true };
|
|
32
|
+
var replace = { destructive: true, idempotent: true };
|
|
33
|
+
var outward = { openWorld: true };
|
|
34
|
+
function group(name, tools) {
|
|
35
|
+
return Object.fromEntries(
|
|
36
|
+
Object.entries(tools).map(([tool, behaviour]) => [tool, { group: name, ...behaviour }])
|
|
87
37
|
);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
38
|
+
}
|
|
39
|
+
var TOOL_CATALOG = {
|
|
40
|
+
...group("core", {
|
|
41
|
+
create_endpoint: write,
|
|
42
|
+
list_endpoints: read,
|
|
43
|
+
get_endpoint: read,
|
|
44
|
+
// Replaces what it is given (a mock response, the signing secret).
|
|
45
|
+
update_endpoint: replace,
|
|
46
|
+
delete_endpoint: remove,
|
|
47
|
+
list_requests: read,
|
|
48
|
+
search_requests: read,
|
|
49
|
+
get_request: read,
|
|
50
|
+
wait_for_request: read,
|
|
51
|
+
wait_for_requests: read,
|
|
52
|
+
replay_request: outward,
|
|
53
|
+
compare_requests: read,
|
|
54
|
+
extract_from_request: read,
|
|
55
|
+
clear_requests: remove,
|
|
56
|
+
send_webhook: write,
|
|
57
|
+
send_to: outward,
|
|
58
|
+
test_webhook_flow: outward,
|
|
59
|
+
preview_webhook: read,
|
|
60
|
+
preview_response: read,
|
|
61
|
+
list_provider_templates: read,
|
|
62
|
+
verify_signature: read,
|
|
63
|
+
list_emails: read,
|
|
64
|
+
get_email: read,
|
|
65
|
+
wait_for_email: read,
|
|
66
|
+
send_test_email: write,
|
|
67
|
+
render_email: outward,
|
|
68
|
+
list_destinations: read,
|
|
69
|
+
add_destination: write,
|
|
70
|
+
update_destination: replace,
|
|
71
|
+
remove_destination: remove,
|
|
72
|
+
list_deliveries: read,
|
|
73
|
+
redeliver: outward,
|
|
74
|
+
share_port: outward,
|
|
75
|
+
list_shares: read,
|
|
76
|
+
share_requests: read,
|
|
77
|
+
stop_share: remove,
|
|
78
|
+
get_usage: read,
|
|
79
|
+
// Collecting an approved key hands it over once: not a pure read.
|
|
80
|
+
wait_for_approval: write,
|
|
81
|
+
describe: read
|
|
82
|
+
}),
|
|
83
|
+
...group("forwarding", {
|
|
84
|
+
preview_destination: read,
|
|
85
|
+
configure_forwarding: replace,
|
|
86
|
+
// rotate: true replaces the secret, and receivers holding the old one fail.
|
|
87
|
+
get_forwarding_secret: { destructive: true },
|
|
88
|
+
test_forwarding: outward
|
|
89
|
+
}),
|
|
90
|
+
...group("layouts", {
|
|
91
|
+
preview_layouts: read,
|
|
92
|
+
list_layout_templates: read,
|
|
93
|
+
add_layout_template: write,
|
|
94
|
+
preview_email_fields: read,
|
|
95
|
+
ai_setup_status: read,
|
|
96
|
+
// Sends the chosen emails to Anthropic once the owner approves.
|
|
97
|
+
ai_setup: outward
|
|
98
|
+
}),
|
|
99
|
+
...group("extras", {
|
|
100
|
+
create_endpoints: write,
|
|
101
|
+
delete_endpoints: remove,
|
|
102
|
+
favorite_endpoint: update,
|
|
103
|
+
count_requests: read,
|
|
104
|
+
generate_payload_schema: read
|
|
105
|
+
}),
|
|
106
|
+
...group("teams", {
|
|
107
|
+
list_teams: read,
|
|
108
|
+
list_team_members: read,
|
|
109
|
+
share_endpoint: update,
|
|
110
|
+
unshare_endpoint: remove,
|
|
111
|
+
create_team: write,
|
|
112
|
+
rename_team: update,
|
|
113
|
+
delete_team: remove,
|
|
114
|
+
remove_team_member: remove,
|
|
115
|
+
leave_team: remove,
|
|
116
|
+
invite_to_team: outward,
|
|
117
|
+
list_invites: read,
|
|
118
|
+
answer_invite: write,
|
|
119
|
+
withdraw_invite: remove,
|
|
120
|
+
quote_team_change: read
|
|
121
|
+
}),
|
|
122
|
+
...group("account", {
|
|
123
|
+
get_billing: read,
|
|
124
|
+
list_api_keys: read,
|
|
125
|
+
create_api_key: write,
|
|
126
|
+
revoke_api_key: remove,
|
|
127
|
+
rename_account: update,
|
|
128
|
+
list_connected_agents: read,
|
|
129
|
+
disconnect_agent: remove,
|
|
130
|
+
list_share_links: read,
|
|
131
|
+
create_share_link: write,
|
|
132
|
+
revoke_share_link: remove
|
|
133
|
+
}),
|
|
134
|
+
...group("domains", {
|
|
135
|
+
list_domains: read,
|
|
136
|
+
add_domain: write,
|
|
137
|
+
// A failed check counts toward lapsing a verified domain: not safe to repeat.
|
|
138
|
+
verify_domain: { destructive: true },
|
|
139
|
+
// Replaces the routing; a missing catch-all refuses mail.
|
|
140
|
+
route_domain: replace,
|
|
141
|
+
remove_domain: remove
|
|
142
|
+
}),
|
|
143
|
+
...group("sheets", {
|
|
144
|
+
list_google_connections: read,
|
|
145
|
+
connect_google: outward,
|
|
146
|
+
pick_spreadsheet: outward,
|
|
147
|
+
create_spreadsheet: outward,
|
|
148
|
+
list_sheet_tabs: { readOnly: true, openWorld: true },
|
|
149
|
+
disconnect_google: remove
|
|
150
|
+
}),
|
|
151
|
+
...group("names", {
|
|
152
|
+
// The log of what visitors asked a running preview for.
|
|
153
|
+
list_names: read,
|
|
154
|
+
reserve_name: write,
|
|
155
|
+
release_name: remove,
|
|
156
|
+
set_name_password: update,
|
|
157
|
+
// Opens the name to anyone again.
|
|
158
|
+
clear_name_password: { destructive: true, idempotent: true }
|
|
159
|
+
})
|
|
160
|
+
};
|
|
161
|
+
var ENABLE_TOOLS = "enable_tools";
|
|
162
|
+
function toolAnnotations(name) {
|
|
163
|
+
const entry = TOOL_CATALOG[name];
|
|
164
|
+
const readOnly = entry?.readOnly === true;
|
|
165
|
+
return {
|
|
166
|
+
title: toolTitle(name),
|
|
167
|
+
readOnlyHint: readOnly,
|
|
168
|
+
// MCP reads a missing destructiveHint as true on any tool that writes.
|
|
169
|
+
destructiveHint: !readOnly && entry?.destructive === true,
|
|
170
|
+
idempotentHint: readOnly || entry?.idempotent === true,
|
|
171
|
+
openWorldHint: entry?.openWorld === true
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function toolTitle(name) {
|
|
175
|
+
const words = name.split("_").map((word) => TITLE_WORDS[word] ?? word);
|
|
176
|
+
const first = words[0] ?? "";
|
|
177
|
+
return [first.charAt(0).toUpperCase() + first.slice(1), ...words.slice(1)].join(" ");
|
|
178
|
+
}
|
|
179
|
+
var TITLE_WORDS = {
|
|
180
|
+
api: "API",
|
|
181
|
+
ai: "AI",
|
|
182
|
+
mcp: "MCP"
|
|
183
|
+
};
|
|
184
|
+
function parseToolGroups(setting) {
|
|
185
|
+
if (setting === null || setting === void 0 || setting.trim() === "") return null;
|
|
186
|
+
const groups = /* @__PURE__ */ new Set(["core"]);
|
|
187
|
+
const unknown = [];
|
|
188
|
+
for (const raw of setting.split(/[\s,+]+/)) {
|
|
189
|
+
const name = raw.trim().toLowerCase();
|
|
190
|
+
if (!name) continue;
|
|
191
|
+
if (name === "all") {
|
|
192
|
+
for (const g of TOOL_GROUP_NAMES) groups.add(g);
|
|
193
|
+
} else if (TOOL_GROUP_NAMES.includes(name)) {
|
|
194
|
+
groups.add(name);
|
|
195
|
+
} else {
|
|
196
|
+
unknown.push(raw.trim());
|
|
118
197
|
}
|
|
119
|
-
|
|
198
|
+
}
|
|
199
|
+
return { groups, unknown };
|
|
200
|
+
}
|
|
201
|
+
var LOADS_EVERYTHING = [
|
|
202
|
+
/^claude-code$/i,
|
|
203
|
+
/^claude-ai$/i,
|
|
204
|
+
/^claude$/i,
|
|
205
|
+
/codex/i,
|
|
206
|
+
/^openai-mcp$/i,
|
|
207
|
+
/chatgpt/i
|
|
208
|
+
];
|
|
209
|
+
function clientLoadsEverything(clientName) {
|
|
210
|
+
if (!clientName) return false;
|
|
211
|
+
const name = clientName.trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
212
|
+
return LOADS_EVERYTHING.some((pattern) => pattern.test(name));
|
|
120
213
|
}
|
|
121
214
|
|
|
122
|
-
// src/
|
|
123
|
-
var
|
|
215
|
+
// src/tools.ts
|
|
216
|
+
var import_zod = require("zod");
|
|
217
|
+
var import_sdk = require("@webhooks-cc/sdk");
|
|
124
218
|
|
|
125
219
|
// src/compact.ts
|
|
126
220
|
var MAX_OUTPUT = 32768;
|
|
@@ -330,142 +424,11 @@ function compactRequest(request) {
|
|
|
330
424
|
return compact;
|
|
331
425
|
}
|
|
332
426
|
|
|
333
|
-
// src/resources.ts
|
|
334
|
-
var ENDPOINTS_RESOURCE_URI = "webhooks://endpoints";
|
|
335
|
-
var ENDPOINT_RECENT_TEMPLATE_URI = "webhooks://endpoint/{slug}/recent";
|
|
336
|
-
var REQUEST_TEMPLATE_URI = "webhooks://request/{id}";
|
|
337
|
-
var MAX_ENDPOINT_RESOURCE_ITEMS = 25;
|
|
338
|
-
var RECENT_REQUEST_LIMIT = 10;
|
|
339
|
-
function jsonResource(uri, value) {
|
|
340
|
-
return {
|
|
341
|
-
contents: [
|
|
342
|
-
{
|
|
343
|
-
uri,
|
|
344
|
-
mimeType: "application/json",
|
|
345
|
-
text: JSON.stringify(value, null, 2)
|
|
346
|
-
}
|
|
347
|
-
]
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
function summarizeRequest(request) {
|
|
351
|
-
return {
|
|
352
|
-
id: request.id,
|
|
353
|
-
method: request.method,
|
|
354
|
-
path: request.path,
|
|
355
|
-
receivedAt: request.receivedAt,
|
|
356
|
-
contentType: request.contentType ?? null
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
function variableToString(value, name) {
|
|
360
|
-
if (typeof value === "string" && value.length > 0) {
|
|
361
|
-
return value;
|
|
362
|
-
}
|
|
363
|
-
throw new Error(`Missing resource variable "${name}"`);
|
|
364
|
-
}
|
|
365
|
-
async function listEndpointSummaries(client) {
|
|
366
|
-
const endpoints = (await client.endpoints.list()).slice().sort((left, right) => right.createdAt - left.createdAt);
|
|
367
|
-
const truncated = endpoints.length > MAX_ENDPOINT_RESOURCE_ITEMS;
|
|
368
|
-
const visible = endpoints.slice(0, MAX_ENDPOINT_RESOURCE_ITEMS);
|
|
369
|
-
const summaries = await Promise.all(
|
|
370
|
-
visible.map(async (endpoint) => {
|
|
371
|
-
const recent = await client.requests.list(endpoint.slug, { limit: 1 });
|
|
372
|
-
return {
|
|
373
|
-
...endpoint,
|
|
374
|
-
lastRequest: recent[0] ? summarizeRequest(recent[0]) : null
|
|
375
|
-
};
|
|
376
|
-
})
|
|
377
|
-
);
|
|
378
|
-
return {
|
|
379
|
-
endpoints: summaries,
|
|
380
|
-
total: endpoints.length,
|
|
381
|
-
truncated
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
function registerResources(server, client) {
|
|
385
|
-
server.registerResource(
|
|
386
|
-
"endpoints-overview",
|
|
387
|
-
ENDPOINTS_RESOURCE_URI,
|
|
388
|
-
{
|
|
389
|
-
title: "Endpoints Overview",
|
|
390
|
-
description: "All endpoints with a summary of recent activity.",
|
|
391
|
-
mimeType: "application/json"
|
|
392
|
-
},
|
|
393
|
-
async () => {
|
|
394
|
-
return jsonResource(ENDPOINTS_RESOURCE_URI, await listEndpointSummaries(client));
|
|
395
|
-
}
|
|
396
|
-
);
|
|
397
|
-
server.registerResource(
|
|
398
|
-
"endpoint-recent-requests",
|
|
399
|
-
new import_mcp.ResourceTemplate(ENDPOINT_RECENT_TEMPLATE_URI, {
|
|
400
|
-
list: async () => {
|
|
401
|
-
const endpoints = await client.endpoints.list();
|
|
402
|
-
return {
|
|
403
|
-
resources: endpoints.map((endpoint) => ({
|
|
404
|
-
uri: `webhooks://endpoint/${endpoint.slug}/recent`,
|
|
405
|
-
name: `${endpoint.slug} recent requests`,
|
|
406
|
-
description: `Recent requests for endpoint ${endpoint.slug}`,
|
|
407
|
-
mimeType: "application/json"
|
|
408
|
-
}))
|
|
409
|
-
};
|
|
410
|
-
},
|
|
411
|
-
complete: {
|
|
412
|
-
slug: async (value) => {
|
|
413
|
-
const endpoints = await client.endpoints.list();
|
|
414
|
-
return endpoints.map((endpoint) => endpoint.slug).filter((slug) => slug.startsWith(value)).slice(0, 20);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}),
|
|
418
|
-
{
|
|
419
|
-
title: "Endpoint Recent Requests",
|
|
420
|
-
description: "Last 10 captured requests for an endpoint.",
|
|
421
|
-
mimeType: "application/json"
|
|
422
|
-
},
|
|
423
|
-
async (uri, variables) => {
|
|
424
|
-
const slug = variableToString(variables.slug, "slug");
|
|
425
|
-
const [endpoint, requests] = await Promise.all([
|
|
426
|
-
client.endpoints.get(slug),
|
|
427
|
-
client.requests.list(slug, { limit: RECENT_REQUEST_LIMIT })
|
|
428
|
-
]);
|
|
429
|
-
return jsonResource(uri.toString(), {
|
|
430
|
-
endpoint,
|
|
431
|
-
requests: requests.map(compactRequest)
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
);
|
|
435
|
-
server.registerResource(
|
|
436
|
-
"request-details",
|
|
437
|
-
new import_mcp.ResourceTemplate(REQUEST_TEMPLATE_URI, {
|
|
438
|
-
list: void 0,
|
|
439
|
-
complete: {}
|
|
440
|
-
}),
|
|
441
|
-
{
|
|
442
|
-
title: "Request Details",
|
|
443
|
-
description: "Full details for a captured request by ID.",
|
|
444
|
-
mimeType: "application/json"
|
|
445
|
-
},
|
|
446
|
-
async (uri, variables) => {
|
|
447
|
-
const id = variableToString(variables.id, "id");
|
|
448
|
-
const request = await client.requests.get(id);
|
|
449
|
-
return jsonResource(uri.toString(), compactRequest(request));
|
|
450
|
-
}
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// src/sandbox-tools.ts
|
|
455
|
-
var import_zod3 = require("zod");
|
|
456
|
-
var import_sdk2 = require("@webhooks-cc/sdk");
|
|
457
|
-
|
|
458
|
-
// src/version.ts
|
|
459
|
-
var VERSION = true ? "1.24.0" : "0.0.0-dev";
|
|
460
|
-
var MCP_USER_AGENT = `webhooks-cc-mcp/${VERSION}`;
|
|
461
|
-
|
|
462
427
|
// src/tools.ts
|
|
463
|
-
var import_zod2 = require("zod");
|
|
464
|
-
var import_sdk = require("@webhooks-cc/sdk");
|
|
465
428
|
var MAX_BODY_SIZE = MAX_OUTPUT;
|
|
466
429
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
467
430
|
var TIME_SEPARATOR = " \u2014 ";
|
|
468
|
-
var httpUrlSchema =
|
|
431
|
+
var httpUrlSchema = import_zod.z.string().url().refine(
|
|
469
432
|
(value) => {
|
|
470
433
|
try {
|
|
471
434
|
const protocol = new URL(value).protocol;
|
|
@@ -476,24 +439,24 @@ var httpUrlSchema = import_zod2.z.string().url().refine(
|
|
|
476
439
|
},
|
|
477
440
|
{ message: "Only http and https URLs are supported" }
|
|
478
441
|
);
|
|
479
|
-
var methodSchema =
|
|
480
|
-
var kindSchema =
|
|
442
|
+
var methodSchema = import_zod.z.enum(HTTP_METHODS).default("POST").describe("HTTP method (default: POST)");
|
|
443
|
+
var kindSchema = import_zod.z.enum(["http", "email"]).optional().describe('Only HTTP requests ("http") or only captured emails ("email")');
|
|
481
444
|
var EMAIL_SCAN_LIMIT = 100;
|
|
482
|
-
var durationOrTimestampSchema =
|
|
445
|
+
var durationOrTimestampSchema = import_zod.z.union([import_zod.z.string(), import_zod.z.number()]);
|
|
483
446
|
var CONDITION_FIELD_NAMES = Object.keys(import_sdk.CONDITION_FIELDS);
|
|
484
447
|
var CONDITION_OP_NAMES = import_sdk.CONDITION_OPS;
|
|
485
448
|
function conditionSchema(use) {
|
|
486
449
|
const fields = use === "response" ? CONDITION_FIELD_NAMES.filter((field) => import_sdk.CONDITION_FIELDS[field].response) : CONDITION_FIELD_NAMES;
|
|
487
|
-
return
|
|
488
|
-
field:
|
|
450
|
+
return import_zod.z.object({
|
|
451
|
+
field: import_zod.z.enum(fields).describe(
|
|
489
452
|
use === "response" ? "What to read: method, path, header (name), query (name), body (text), body_path (path into a JSON or form body), content_type, size" : "What to read: method, path, header (name), query (name), body, body_path (path), content_type, size, kind (http or email), email (name: subject, from, to, text or tag), spam_score, email_auth (name: spf, dkim or dmarc), signature (verified, failed or missing)"
|
|
490
453
|
),
|
|
491
|
-
op:
|
|
454
|
+
op: import_zod.z.enum(CONDITION_OP_NAMES).describe(
|
|
492
455
|
"eq, neq, contains, not_contains, starts_with, ends_with, matches (glob: * one path segment, ** any, ? one character), regex (RE2, searched anywhere; (?i) ignores case), exists, not_exists, gt, gte, lt, lte (numbers)"
|
|
493
456
|
),
|
|
494
|
-
value:
|
|
495
|
-
name:
|
|
496
|
-
path:
|
|
457
|
+
value: import_zod.z.string().max(import_sdk.MAX_CONDITION_VALUE_LEN).optional().describe(`What to compare with (not for exists and not_exists)`),
|
|
458
|
+
name: import_zod.z.string().max(import_sdk.MAX_CONDITION_NAME_LEN).optional().describe("Header or query parameter name; the email part; spf, dkim or dmarc"),
|
|
459
|
+
path: import_zod.z.string().max(import_sdk.MAX_CONDITION_PATH_LEN).optional().describe('Dot path for body_path, e.g. data.object.status or items.0.sku or a["odd.key"]')
|
|
497
460
|
}).superRefine((condition, ctx) => {
|
|
498
461
|
for (const issue of (0, import_sdk.conditionIssues)(condition, { use })) {
|
|
499
462
|
ctx.addIssue({ code: "custom", message: issue });
|
|
@@ -501,46 +464,46 @@ function conditionSchema(use) {
|
|
|
501
464
|
});
|
|
502
465
|
}
|
|
503
466
|
var ruleConditionSchema = conditionSchema("response");
|
|
504
|
-
var emailFieldSchema =
|
|
505
|
-
name:
|
|
506
|
-
from:
|
|
507
|
-
header:
|
|
508
|
-
pattern:
|
|
467
|
+
var emailFieldSchema = import_zod.z.object({
|
|
468
|
+
name: import_zod.z.string().describe("Letters, digits and _, starting with a letter: {{ fields.name }}"),
|
|
469
|
+
from: import_zod.z.enum(["subject", "text", "html", "from", "to", "header"]).describe("What it reads; html is the HTML as text"),
|
|
470
|
+
header: import_zod.z.string().optional().describe('The header name, for from "header"'),
|
|
471
|
+
pattern: import_zod.z.string().describe("RE2 regular expression; the first group is the value (no lookarounds)")
|
|
509
472
|
});
|
|
510
|
-
var layoutSchema =
|
|
511
|
-
id:
|
|
512
|
-
name:
|
|
513
|
-
enabled:
|
|
514
|
-
match:
|
|
515
|
-
logic:
|
|
516
|
-
conditions:
|
|
473
|
+
var layoutSchema = import_zod.z.object({
|
|
474
|
+
id: import_zod.z.string().describe("Stable id: 1 to 40 letters, digits, - or _"),
|
|
475
|
+
name: import_zod.z.string().max(100),
|
|
476
|
+
enabled: import_zod.z.boolean().optional().describe("Off: kept but never matched (default on)"),
|
|
477
|
+
match: import_zod.z.object({
|
|
478
|
+
logic: import_zod.z.enum(["and", "or"]).optional(),
|
|
479
|
+
conditions: import_zod.z.array(conditionSchema("filter")).max(10)
|
|
517
480
|
}).optional().describe("When it applies, e.g. the sender or subject; no conditions: every email"),
|
|
518
|
-
fields:
|
|
519
|
-
lineItems:
|
|
520
|
-
from:
|
|
521
|
-
pattern:
|
|
481
|
+
fields: import_zod.z.array(emailFieldSchema).max(20).optional().describe("Named values, as emailFields"),
|
|
482
|
+
lineItems: import_zod.z.object({
|
|
483
|
+
from: import_zod.z.enum(["text", "html", "attachment"]),
|
|
484
|
+
pattern: import_zod.z.string().optional().describe(
|
|
522
485
|
"text/html: RE2 with named groups found again and again, one row each, e.g. (?P<sku>[A-Z]+-\\d+)\\s+x(?P<qty>\\d+)"
|
|
523
486
|
),
|
|
524
|
-
start:
|
|
525
|
-
end:
|
|
526
|
-
attachment:
|
|
527
|
-
sheet:
|
|
528
|
-
columns:
|
|
487
|
+
start: import_zod.z.string().optional().describe("text/html: search only after this (RE2)"),
|
|
488
|
+
end: import_zod.z.string().optional().describe("text/html: and before this (RE2)"),
|
|
489
|
+
attachment: import_zod.z.string().optional().describe("attachment: file name with * and ? (default: the first spreadsheet or CSV)"),
|
|
490
|
+
sheet: import_zod.z.string().optional().describe("attachment: sheet name (default: the first)"),
|
|
491
|
+
columns: import_zod.z.array(import_zod.z.object({ name: import_zod.z.string(), column: import_zod.z.string() })).max(30).optional().describe("attachment: { name, column: header text or letter }; default: every column")
|
|
529
492
|
}).nullable().optional().describe("Rows such as an order's products"),
|
|
530
|
-
parseAttachments:
|
|
493
|
+
parseAttachments: import_zod.z.boolean().optional().describe("Parse CSV, XLSX, ODS, PDF text and text files into the JSON; uses parsed pages")
|
|
531
494
|
});
|
|
532
495
|
var destinationFields = {
|
|
533
|
-
name:
|
|
534
|
-
enabled:
|
|
535
|
-
http:
|
|
536
|
-
email:
|
|
537
|
-
filter:
|
|
538
|
-
logic:
|
|
539
|
-
conditions:
|
|
496
|
+
name: import_zod.z.string().max(100).nullable().optional().describe("A name for the list and the log"),
|
|
497
|
+
enabled: import_zod.z.boolean().optional().describe("Send to it (default true)"),
|
|
498
|
+
http: import_zod.z.boolean().optional().describe("Takes HTTP requests (default true)"),
|
|
499
|
+
email: import_zod.z.boolean().optional().describe("Takes emails (default true)"),
|
|
500
|
+
filter: import_zod.z.object({
|
|
501
|
+
logic: import_zod.z.enum(["and", "or"]).optional().describe("and (default): every condition; or: any"),
|
|
502
|
+
conditions: import_zod.z.array(conditionSchema("filter")).max(10)
|
|
540
503
|
}).nullable().optional().describe(
|
|
541
504
|
"Send only captures that match (null: everything it takes). Conditions like response rules plus kind, email (name: subject, from, to, text, tag), spam_score, email_auth (name: spf, dkim, dmarc) and signature (verified, failed, missing)"
|
|
542
505
|
),
|
|
543
|
-
format:
|
|
506
|
+
format: import_zod.z.enum([
|
|
544
507
|
"auto",
|
|
545
508
|
"as_received",
|
|
546
509
|
"json",
|
|
@@ -553,103 +516,103 @@ var destinationFields = {
|
|
|
553
516
|
]).optional().describe(
|
|
554
517
|
"auto (pick from the URL), as_received (relay HTTP requests unchanged; emails as signed JSON), json (signed JSON envelope), template (your bodyTemplate, signed like json), chat (Slack or Discord message), or an inbound mail provider's shape for emails (postmark, sendgrid, mailgun, cloudmailin; HTTP requests then go as received), to test the inbound handler you wrote for that provider"
|
|
555
518
|
),
|
|
556
|
-
mailgunSigningKey:
|
|
519
|
+
mailgunSigningKey: import_zod.z.string().max(200).nullable().optional().describe(
|
|
557
520
|
"For format mailgun: your Mailgun HTTP webhook signing key, so the form's signature verifies; null removes it. Write-only."
|
|
558
521
|
),
|
|
559
|
-
bodyTemplate:
|
|
522
|
+
bodyTemplate: import_zod.z.unknown().optional().describe(
|
|
560
523
|
`For format template: any JSON value whose strings hold {{ placeholders }}, e.g. {"event": "{{ json.type }}", "amount": "{{ json.data.amount }}", "code": "{{ codes.0 }}"}. A string that is only a placeholder keeps the value's type; a missing field becomes null. HTTP requests read method, path, query.x, headers.x, body, json.path, id, receivedAt, signature; emails read the email.received fields (subject, from.address, to, text, codes, links, auth, spam, tag)`
|
|
561
524
|
),
|
|
562
|
-
messageTemplate:
|
|
525
|
+
messageTemplate: import_zod.z.string().max(4e3).nullable().optional().describe(
|
|
563
526
|
"For format chat: the message text with {{ placeholders }}; null sends the standard message"
|
|
564
527
|
),
|
|
565
|
-
headers:
|
|
566
|
-
|
|
567
|
-
name:
|
|
568
|
-
value:
|
|
528
|
+
headers: import_zod.z.array(
|
|
529
|
+
import_zod.z.object({
|
|
530
|
+
name: import_zod.z.string().describe("Header name, e.g. Authorization"),
|
|
531
|
+
value: import_zod.z.string().nullable().describe("Value, or null to keep the stored one")
|
|
569
532
|
})
|
|
570
533
|
).max(10).optional().describe("Your own headers for every delivery (not chat). Replaces the whole list"),
|
|
571
|
-
appendPath:
|
|
572
|
-
retrySeconds:
|
|
573
|
-
keepOrder:
|
|
574
|
-
sentField:
|
|
575
|
-
throttleSeconds:
|
|
534
|
+
appendPath: import_zod.z.boolean().optional().describe("Append the captured path to the URL (as_received)"),
|
|
535
|
+
retrySeconds: import_zod.z.union([import_zod.z.literal(0), import_zod.z.literal(3600), import_zod.z.literal(86400)]).optional().describe("Retry failed deliveries for 86400 (default), 3600 or 0 seconds"),
|
|
536
|
+
keepOrder: import_zod.z.boolean().optional().describe("One delivery at a time, in capture order"),
|
|
537
|
+
sentField: import_zod.z.string().max(128).nullable().optional().describe("JSON body field with the sender's own timestamp"),
|
|
538
|
+
throttleSeconds: import_zod.z.number().int().min(0).max(3600).optional().describe(
|
|
576
539
|
"At most one delivery every N seconds; the rest are not sent (1 makes a heads-up channel)"
|
|
577
540
|
),
|
|
578
|
-
delaySeconds:
|
|
541
|
+
delaySeconds: import_zod.z.number().int().min(0).max(3600).optional().describe("Wait this long after the capture before sending, like a callback")
|
|
579
542
|
};
|
|
580
|
-
var responseRuleSchema =
|
|
581
|
-
name:
|
|
582
|
-
enabled:
|
|
583
|
-
logic:
|
|
584
|
-
conditions:
|
|
585
|
-
response:
|
|
543
|
+
var responseRuleSchema = import_zod.z.object({
|
|
544
|
+
name: import_zod.z.string().max(import_sdk.MAX_RULE_NAME_LEN).optional().describe(`Human-readable rule name (max ${import_sdk.MAX_RULE_NAME_LEN} chars)`),
|
|
545
|
+
enabled: import_zod.z.boolean().optional().default(true).describe("Whether this rule is active"),
|
|
546
|
+
logic: import_zod.z.enum(["and", "or"]).optional().default("and").describe('How to combine conditions: "and" (all match) or "or" (any match)'),
|
|
547
|
+
conditions: import_zod.z.array(ruleConditionSchema).min(1).max(10).describe("Conditions to evaluate (1-10)"),
|
|
548
|
+
response: import_zod.z.lazy(() => mockResponseSchema).describe("Response when conditions match")
|
|
586
549
|
});
|
|
587
|
-
var responseRulesSchema =
|
|
588
|
-
var mockResponseSchema =
|
|
589
|
-
status:
|
|
590
|
-
body:
|
|
550
|
+
var responseRulesSchema = import_zod.z.array(responseRuleSchema).max(50).describe("Ordered conditional response rules (first match wins, max 50)");
|
|
551
|
+
var mockResponseSchema = import_zod.z.object({
|
|
552
|
+
status: import_zod.z.number().int().min(import_sdk.MOCK_RESPONSE_STATUS_MIN).max(import_sdk.MOCK_RESPONSE_STATUS_MAX).describe(`HTTP status code (${import_sdk.MOCK_RESPONSE_STATUS_MIN}-${import_sdk.MOCK_RESPONSE_STATUS_MAX})`),
|
|
553
|
+
body: import_zod.z.string().default("").describe(
|
|
591
554
|
'Response body (default: empty). {{ placeholders }} are filled in from the request: {{ json.id }} (a JSON or form body field), {{ body }}, {{ headers.user-agent }}, {{ query.page }}, {{ method }}, {{ path }}, {{ id }}, {{ uuid }}, {{ now }}; filters: | default: "x", | json, | lower, | upper, | truncate: 80'
|
|
592
555
|
),
|
|
593
|
-
headers:
|
|
594
|
-
delay:
|
|
556
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).default({}).describe("Response headers (default: none); values may use {{ placeholders }} too"),
|
|
557
|
+
delay: import_zod.z.number().int().min(import_sdk.MOCK_RESPONSE_DELAY_MIN).max(import_sdk.MOCK_RESPONSE_DELAY_MAX).optional().describe(
|
|
595
558
|
`Response delay in milliseconds (${import_sdk.MOCK_RESPONSE_DELAY_MIN}-${import_sdk.MOCK_RESPONSE_DELAY_MAX}, default: none)`
|
|
596
559
|
)
|
|
597
560
|
});
|
|
598
|
-
var endpointSiteSchema =
|
|
561
|
+
var endpointSiteSchema = import_zod.z.enum(["webhooks", "mailhooks", "both"]).describe(
|
|
599
562
|
"Which site's dashboard lists the endpoint: webhooks (webhooks.cc), mailhooks (mailhooks.cc) or both (the default when creating). Only the listing: its HTTP URL and email address work either way. Owner only to change."
|
|
600
563
|
);
|
|
601
564
|
var createEndpointShape = {
|
|
602
|
-
name:
|
|
603
|
-
ephemeral:
|
|
565
|
+
name: import_zod.z.string().optional().describe("Display name for the endpoint"),
|
|
566
|
+
ephemeral: import_zod.z.boolean().optional().describe("Create a temporary endpoint that auto-expires"),
|
|
604
567
|
expiresIn: durationOrTimestampSchema.optional().describe('Auto-expire after this duration, for example "12h"'),
|
|
605
568
|
mockResponse: mockResponseSchema.optional().describe("Optional mock response to return when the endpoint receives a request"),
|
|
606
569
|
responseRules: responseRulesSchema.optional().describe(
|
|
607
570
|
"Conditional response rules. Each rule has conditions and a response. First matching rule wins."
|
|
608
571
|
),
|
|
609
|
-
notificationUrl:
|
|
572
|
+
notificationUrl: import_zod.z.string().url().optional().describe(
|
|
610
573
|
"URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook)"
|
|
611
574
|
),
|
|
612
575
|
site: endpointSiteSchema.optional(),
|
|
613
|
-
signingProvider:
|
|
576
|
+
signingProvider: import_zod.z.string().optional().describe(
|
|
614
577
|
"Signing provider for automatic signature verification (e.g. stripe, github, shopify)"
|
|
615
578
|
),
|
|
616
|
-
signingSecret:
|
|
579
|
+
signingSecret: import_zod.z.string().optional().describe(
|
|
617
580
|
"Signing secret (encrypted server-side, never returned). Required when signingProvider is set."
|
|
618
581
|
),
|
|
619
|
-
signingHeader:
|
|
582
|
+
signingHeader: import_zod.z.string().optional().describe("Custom signature header name. Only used with generic-hmac provider.")
|
|
620
583
|
};
|
|
621
584
|
var listEndpointsShape = {
|
|
622
|
-
team:
|
|
585
|
+
team: import_zod.z.string().min(1).optional().describe(
|
|
623
586
|
"Team id or name (case-insensitive). Keeps endpoints shared with you from that team and endpoints you own that are shared with it."
|
|
624
587
|
)
|
|
625
588
|
};
|
|
626
|
-
var getEndpointShape = { slug:
|
|
627
|
-
var deleteEndpointShape = { slug:
|
|
589
|
+
var getEndpointShape = { slug: import_zod.z.string().describe("The endpoint slug") };
|
|
590
|
+
var deleteEndpointShape = { slug: import_zod.z.string().describe("The endpoint slug to delete") };
|
|
628
591
|
var listRequestsShape = {
|
|
629
|
-
endpointSlug:
|
|
630
|
-
limit:
|
|
631
|
-
since:
|
|
592
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug"),
|
|
593
|
+
limit: import_zod.z.number().int().min(1).max(100).default(25).describe("Max requests to return"),
|
|
594
|
+
since: import_zod.z.number().optional().describe("Only return requests after this timestamp in ms"),
|
|
632
595
|
kind: kindSchema
|
|
633
596
|
};
|
|
634
|
-
var getRequestShape = { requestId:
|
|
597
|
+
var getRequestShape = { requestId: import_zod.z.string().describe("The request ID") };
|
|
635
598
|
var waitForRequestShape = {
|
|
636
|
-
endpointSlug:
|
|
599
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug to monitor"),
|
|
637
600
|
timeout: durationOrTimestampSchema.default("30s").describe('How long to wait, for example "30s"'),
|
|
638
601
|
pollInterval: durationOrTimestampSchema.optional().describe('Interval between polls, for example "500ms" or "1s"')
|
|
639
602
|
};
|
|
640
603
|
var payloadSchemaShape = {
|
|
641
|
-
endpointSlug:
|
|
642
|
-
format:
|
|
604
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug"),
|
|
605
|
+
format: import_zod.z.enum(import_sdk.PAYLOAD_SCHEMA_FORMATS).default("typescript").describe(
|
|
643
606
|
'Output: "typescript" interfaces, "zod" schemas, "pydantic" models, or "json-schema"'
|
|
644
607
|
),
|
|
645
|
-
name:
|
|
646
|
-
event:
|
|
608
|
+
name: import_zod.z.string().max(100).optional().describe("Root type name (default: from the detected event, else WebhookPayload)"),
|
|
609
|
+
event: import_zod.z.string().optional().describe(
|
|
647
610
|
'Only requests whose detected provider event matches, e.g. "invoice.paid" or "push". One event per schema gives the tightest types.'
|
|
648
611
|
),
|
|
649
|
-
method:
|
|
650
|
-
path:
|
|
651
|
-
requestIds:
|
|
652
|
-
limit:
|
|
612
|
+
method: import_zod.z.string().optional().describe("Only requests with this HTTP method"),
|
|
613
|
+
path: import_zod.z.string().optional().describe("Only requests with this path"),
|
|
614
|
+
requestIds: import_zod.z.array(import_zod.z.string()).min(1).max(50).optional().describe("Use exactly these requests instead of the latest ones"),
|
|
615
|
+
limit: import_zod.z.number().int().min(1).max(100).default(20).describe("How many of the latest requests to read before filtering")
|
|
653
616
|
};
|
|
654
617
|
function eventName(event) {
|
|
655
618
|
const words = event.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
@@ -1147,11 +1110,18 @@ function durationMs(value) {
|
|
|
1147
1110
|
return null;
|
|
1148
1111
|
}
|
|
1149
1112
|
}
|
|
1150
|
-
function toolTarget(target,
|
|
1113
|
+
function toolTarget(target, options, handles) {
|
|
1114
|
+
const remote = options.remote === true;
|
|
1151
1115
|
return {
|
|
1152
1116
|
tool(name, description, shape, handler) {
|
|
1153
1117
|
if (remote && LOCAL_ONLY_TOOLS.has(name)) return { remove() {
|
|
1154
1118
|
} };
|
|
1119
|
+
const entry = TOOL_CATALOG[name];
|
|
1120
|
+
if (!entry) throw new Error(`${name} has no entry in catalog.ts`);
|
|
1121
|
+
if (options.groups && !options.groups.has(entry.group)) return { remove() {
|
|
1122
|
+
} };
|
|
1123
|
+
if (options.readOnly && entry.readOnly !== true) return { remove() {
|
|
1124
|
+
} };
|
|
1155
1125
|
const wrapped = remote ? async (args2) => {
|
|
1156
1126
|
const input = args2;
|
|
1157
1127
|
if (TARGET_URL_TOOLS.has(name) && input.targetUrl !== void 0) {
|
|
@@ -1185,7 +1155,7 @@ function toolTarget(target, remote, handles) {
|
|
|
1185
1155
|
}
|
|
1186
1156
|
return handler(bounded);
|
|
1187
1157
|
} : handler;
|
|
1188
|
-
const handle = target.tool(name, description, shape, wrapped);
|
|
1158
|
+
const handle = target.tool(name, description, shape, wrapped, toolAnnotations(name));
|
|
1189
1159
|
handles.push(handle);
|
|
1190
1160
|
return handle;
|
|
1191
1161
|
}
|
|
@@ -1261,7 +1231,7 @@ function withShareHints(list, remote = false) {
|
|
|
1261
1231
|
function registerTools(target, client, options = {}) {
|
|
1262
1232
|
const handles = [];
|
|
1263
1233
|
const remote = options.remote === true;
|
|
1264
|
-
const server = toolTarget(target,
|
|
1234
|
+
const server = toolTarget(target, options, handles);
|
|
1265
1235
|
const shareWith = remote ? REMOTE_SHARE : "share_port";
|
|
1266
1236
|
server.tool(
|
|
1267
1237
|
"create_endpoint",
|
|
@@ -1344,7 +1314,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1344
1314
|
server.tool(
|
|
1345
1315
|
"list_team_members",
|
|
1346
1316
|
"List a team's members and pending invites. Any member of the team may call this.",
|
|
1347
|
-
{ teamId:
|
|
1317
|
+
{ teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)") },
|
|
1348
1318
|
withErrorHandling(async ({ teamId }) => {
|
|
1349
1319
|
const result = await client.teams.members(teamId);
|
|
1350
1320
|
return jsonContent(result);
|
|
@@ -1354,8 +1324,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1354
1324
|
"share_endpoint",
|
|
1355
1325
|
"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.",
|
|
1356
1326
|
{
|
|
1357
|
-
slug:
|
|
1358
|
-
teamId:
|
|
1327
|
+
slug: import_zod.z.string().describe("Slug of an endpoint you own"),
|
|
1328
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)")
|
|
1359
1329
|
},
|
|
1360
1330
|
withErrorHandling(async ({ slug, teamId }) => {
|
|
1361
1331
|
await client.teams.share(teamId, slug);
|
|
@@ -1366,8 +1336,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1366
1336
|
"unshare_endpoint",
|
|
1367
1337
|
"Stop sharing an endpoint you own with a team. Works even when the team is suspended.",
|
|
1368
1338
|
{
|
|
1369
|
-
slug:
|
|
1370
|
-
teamId:
|
|
1339
|
+
slug: import_zod.z.string().describe("Slug of an endpoint you own"),
|
|
1340
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)")
|
|
1371
1341
|
},
|
|
1372
1342
|
withErrorHandling(async ({ slug, teamId }) => {
|
|
1373
1343
|
await client.teams.unshare(teamId, slug);
|
|
@@ -1377,15 +1347,15 @@ function registerTools(target, client, options = {}) {
|
|
|
1377
1347
|
server.tool(
|
|
1378
1348
|
"create_team",
|
|
1379
1349
|
"Create a team you own, to share endpoints with its members under a pooled quota. It does nothing until it has a subscription, which the person starts in the browser (Account > Teams); until then it is suspended.",
|
|
1380
|
-
{ name:
|
|
1350
|
+
{ name: import_zod.z.string().trim().min(1).max(100).describe("The team's name") },
|
|
1381
1351
|
withErrorHandling(async ({ name }) => jsonContent(await client.teams.create(name)))
|
|
1382
1352
|
);
|
|
1383
1353
|
server.tool(
|
|
1384
1354
|
"rename_team",
|
|
1385
1355
|
"Rename a team you own.",
|
|
1386
1356
|
{
|
|
1387
|
-
teamId:
|
|
1388
|
-
name:
|
|
1357
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1358
|
+
name: import_zod.z.string().trim().min(1).max(100).describe("The new name")
|
|
1389
1359
|
},
|
|
1390
1360
|
withErrorHandling(async ({ teamId, name }) => {
|
|
1391
1361
|
await client.teams.rename(teamId, name);
|
|
@@ -1395,7 +1365,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1395
1365
|
server.tool(
|
|
1396
1366
|
"delete_team",
|
|
1397
1367
|
"Delete a team you own: its subscription ends at once, every member loses the endpoints shared with it, and it cannot be undone. Only when the person asks for it. The team's owner must approve it: this returns a link and a code to show them, then call wait_for_approval.",
|
|
1398
|
-
{ teamId:
|
|
1368
|
+
{ teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)") },
|
|
1399
1369
|
withErrorHandling(async ({ teamId }) => {
|
|
1400
1370
|
const pending = await client.teams.delete(teamId);
|
|
1401
1371
|
if (!pending.id) return textContent(`Deleted the team ${teamId}.`);
|
|
@@ -1406,8 +1376,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1406
1376
|
"remove_team_member",
|
|
1407
1377
|
"Remove a member from a team you own: the endpoints they shared with the team stop being shared, and their seat is free for someone else. Only when the person asks for it.",
|
|
1408
1378
|
{
|
|
1409
|
-
teamId:
|
|
1410
|
-
userId:
|
|
1379
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1380
|
+
userId: import_zod.z.string().min(1).describe("The member's userId (from list_team_members)")
|
|
1411
1381
|
},
|
|
1412
1382
|
withErrorHandling(async ({ teamId, userId }) => {
|
|
1413
1383
|
await client.teams.removeMember(teamId, userId);
|
|
@@ -1417,7 +1387,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1417
1387
|
server.tool(
|
|
1418
1388
|
"leave_team",
|
|
1419
1389
|
"Leave a team you belong to: the endpoints you shared with it stop being shared, and you lose access to the ones shared with you through it. An owner cannot leave; they delete the team instead (delete_team). Only when the person asks for it.",
|
|
1420
|
-
{ teamId:
|
|
1390
|
+
{ teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)") },
|
|
1421
1391
|
withErrorHandling(async ({ teamId }) => {
|
|
1422
1392
|
await client.teams.leave(teamId);
|
|
1423
1393
|
return jsonContent({ left: true, teamId });
|
|
@@ -1427,8 +1397,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1427
1397
|
"invite_to_team",
|
|
1428
1398
|
"Invite an email address to a team you own. It emails that address, so the team's owner must approve it: this returns a link and a code to show them, then call wait_for_approval, which returns the invite. Accepting the invite claims one of the team's paid seats. Only when the person asks for it.",
|
|
1429
1399
|
{
|
|
1430
|
-
teamId:
|
|
1431
|
-
email:
|
|
1400
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1401
|
+
email: import_zod.z.string().trim().email().describe("The address to invite")
|
|
1432
1402
|
},
|
|
1433
1403
|
withErrorHandling(async ({ teamId, email }) => {
|
|
1434
1404
|
const pending = await client.teams.invite(teamId, email);
|
|
@@ -1446,8 +1416,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1446
1416
|
"answer_invite",
|
|
1447
1417
|
"Accept or decline a team invite waiting for you (from list_invites). Accepting claims one of the team's paid seats and you join the team, seeing the endpoints shared with it; it fails when the team is full or suspended. Only when the person says which.",
|
|
1448
1418
|
{
|
|
1449
|
-
inviteId:
|
|
1450
|
-
answer:
|
|
1419
|
+
inviteId: import_zod.z.string().min(1).describe("The invite's id, from list_invites"),
|
|
1420
|
+
answer: import_zod.z.enum(["accept", "decline"]).describe("accept joins the team; decline turns it down")
|
|
1451
1421
|
},
|
|
1452
1422
|
withErrorHandling(async ({ inviteId, answer }) => {
|
|
1453
1423
|
if (answer === "accept") {
|
|
@@ -1462,8 +1432,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1462
1432
|
"withdraw_invite",
|
|
1463
1433
|
"Withdraw an invite to a team you own before it is answered (pending invites and their ids are in list_team_members). The invite link stops working.",
|
|
1464
1434
|
{
|
|
1465
|
-
teamId:
|
|
1466
|
-
inviteId:
|
|
1435
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1436
|
+
inviteId: import_zod.z.string().min(1).describe("The invite's id (from list_team_members' pendingInvites)")
|
|
1467
1437
|
},
|
|
1468
1438
|
withErrorHandling(async ({ teamId, inviteId }) => {
|
|
1469
1439
|
await client.teams.withdrawInvite(teamId, inviteId);
|
|
@@ -1474,10 +1444,10 @@ function registerTools(target, client, options = {}) {
|
|
|
1474
1444
|
"quote_team_change",
|
|
1475
1445
|
"What changing a team's seat count or plan would cost, read only (owner only): what is due now, any credit, and the price from the renewal. Give seats, or plan (with interval). The change itself is made by the person in the browser.",
|
|
1476
1446
|
{
|
|
1477
|
-
teamId:
|
|
1478
|
-
seats:
|
|
1479
|
-
plan:
|
|
1480
|
-
interval:
|
|
1447
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1448
|
+
seats: import_zod.z.number().int().min(1).optional().describe("Price this seat count"),
|
|
1449
|
+
plan: import_zod.z.enum(["team", "team_business"]).optional().describe("Price a move to this plan: team (Teams) or team_business (Team Business)"),
|
|
1450
|
+
interval: import_zod.z.enum(["month", "year"]).optional().describe("Billing interval for plan (default month; Teams is monthly only)")
|
|
1481
1451
|
},
|
|
1482
1452
|
withErrorHandling(async ({ teamId, seats, plan, interval }) => {
|
|
1483
1453
|
if (seats === void 0 === (plan === void 0)) {
|
|
@@ -1505,8 +1475,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1505
1475
|
"favorite_endpoint",
|
|
1506
1476
|
"Mark an endpoint as one of your favourites, or unmark it with favorite: false. A favourite is your own choice, so endpoints shared with you through teams can be marked too; list_endpoints and get_endpoint show it as favorite. Idempotent.",
|
|
1507
1477
|
{
|
|
1508
|
-
slug:
|
|
1509
|
-
favorite:
|
|
1478
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1479
|
+
favorite: import_zod.z.boolean().optional().describe("true marks it as a favourite (the default), false unmarks it")
|
|
1510
1480
|
},
|
|
1511
1481
|
withErrorHandling(async ({ slug, favorite }) => {
|
|
1512
1482
|
const result = favorite === false ? await client.endpoints.unfavorite(slug) : await client.endpoints.favorite(slug);
|
|
@@ -1517,24 +1487,24 @@ function registerTools(target, client, options = {}) {
|
|
|
1517
1487
|
"update_endpoint",
|
|
1518
1488
|
"Update an endpoint name, mock response, conditional response rules, signing configuration, whether codes and links are picked out of its emails, or the named fields picked out of them.",
|
|
1519
1489
|
{
|
|
1520
|
-
slug:
|
|
1521
|
-
name:
|
|
1490
|
+
slug: import_zod.z.string().describe("The endpoint slug to update"),
|
|
1491
|
+
name: import_zod.z.string().optional().describe("New display name"),
|
|
1522
1492
|
mockResponse: mockResponseSchema.nullable().optional().describe("Default mock response (used when no rule matches), or null to clear"),
|
|
1523
1493
|
responseRules: responseRulesSchema.nullable().optional().describe("Conditional response rules (first match wins), or null to clear"),
|
|
1524
|
-
notificationUrl:
|
|
1525
|
-
signingProvider:
|
|
1494
|
+
notificationUrl: import_zod.z.string().url().nullable().optional().describe("Notification webhook URL, or null to clear it"),
|
|
1495
|
+
signingProvider: import_zod.z.string().nullable().optional().describe(
|
|
1526
1496
|
"Signing provider for automatic verification (e.g. stripe, github), or null to disable"
|
|
1527
1497
|
),
|
|
1528
|
-
signingSecret:
|
|
1529
|
-
signingHeader:
|
|
1498
|
+
signingSecret: import_zod.z.string().nullable().optional().describe("New signing secret (encrypted server-side). Pass null to clear."),
|
|
1499
|
+
signingHeader: import_zod.z.string().nullable().optional().describe(
|
|
1530
1500
|
"Custom signature header name (only for generic-hmac provider), or null to clear"
|
|
1531
1501
|
),
|
|
1532
|
-
showEmailExtracts:
|
|
1502
|
+
showEmailExtracts: import_zod.z.boolean().optional().describe("Pick codes and links out of emails (dashboard and forwarded JSON). Owner only."),
|
|
1533
1503
|
site: endpointSiteSchema.optional(),
|
|
1534
|
-
emailFields:
|
|
1504
|
+
emailFields: import_zod.z.array(emailFieldSchema).max(20).nullable().optional().describe(
|
|
1535
1505
|
"Named fields picked out of every email: shown with it, `fields` in its JSON, read by forwarding templates and filters (email_field). Replaces the list; null or [] removes them. Owner only."
|
|
1536
1506
|
),
|
|
1537
|
-
layouts:
|
|
1507
|
+
layouts: import_zod.z.array(layoutSchema).max(50).nullable().optional().describe(
|
|
1538
1508
|
"How the endpoint reads each sender's emails: the first layout whose match holds applies its fields, line items and attachment parsing; the result is `parsed` in the email's JSON, and deliveries wait for it. The plan uses the first 2 (Business 50). Replaces the list; null or [] removes them. Try them with preview_layouts first. Owner only."
|
|
1539
1509
|
)
|
|
1540
1510
|
},
|
|
@@ -1574,9 +1544,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1574
1544
|
"preview_layouts",
|
|
1575
1545
|
"Dry run of an endpoint's layouts on a captured email: which layout matches, the fields and line items it finds, and what earlier parses of its attachments held. Nothing is saved and no pages are used. Pass layouts to try them before saving them with update_endpoint.",
|
|
1576
1546
|
{
|
|
1577
|
-
slug:
|
|
1578
|
-
requestId:
|
|
1579
|
-
layouts:
|
|
1547
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1548
|
+
requestId: import_zod.z.string().optional().describe("A captured email on the endpoint (default: the newest)"),
|
|
1549
|
+
layouts: import_zod.z.array(layoutSchema).max(50).optional().describe("Unsaved layouts to try")
|
|
1580
1550
|
},
|
|
1581
1551
|
withErrorHandling(async ({ slug, requestId, layouts }) => {
|
|
1582
1552
|
const preview = await client.endpoints.previewLayouts(slug, { requestId, layouts });
|
|
@@ -1588,15 +1558,15 @@ function registerTools(target, client, options = {}) {
|
|
|
1588
1558
|
server.tool(
|
|
1589
1559
|
"list_layout_templates",
|
|
1590
1560
|
"List the ready-made email layouts (order emails from Shopify, WooCommerce, Etsy and more, payments, bookings, attachments): each with its id, sender, kind, description, the fields it reads and whether it reads line items. Add one with add_layout_template.",
|
|
1591
|
-
{ slug:
|
|
1561
|
+
{ slug: import_zod.z.string().describe("The endpoint slug") },
|
|
1592
1562
|
withErrorHandling(async ({ slug }) => jsonContent(await client.endpoints.layoutTemplates(slug)))
|
|
1593
1563
|
);
|
|
1594
1564
|
server.tool(
|
|
1595
1565
|
"add_layout_template",
|
|
1596
1566
|
"Add a ready-made layout (an id from list_layout_templates) to the end of an endpoint's layouts. Emails it matches are parsed from the next one on; check it on a captured email with preview_layouts. Owner only.",
|
|
1597
1567
|
{
|
|
1598
|
-
slug:
|
|
1599
|
-
template:
|
|
1568
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1569
|
+
template: import_zod.z.string().min(1).describe("The template's id, from list_layout_templates")
|
|
1600
1570
|
},
|
|
1601
1571
|
withErrorHandling(
|
|
1602
1572
|
async ({ slug, template }) => jsonContent(await client.endpoints.addLayoutTemplate(slug, template))
|
|
@@ -1606,9 +1576,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1606
1576
|
"preview_email_fields",
|
|
1607
1577
|
"Dry run of an endpoint's named email fields on a captured email: each field's value (null where it finds nothing), and problems with fields you pass. Nothing is saved. Pass emailFields to try fields before saving them with update_endpoint. Owner only.",
|
|
1608
1578
|
{
|
|
1609
|
-
slug:
|
|
1610
|
-
requestId:
|
|
1611
|
-
emailFields:
|
|
1579
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1580
|
+
requestId: import_zod.z.string().optional().describe("A captured email on the endpoint (default: the newest)"),
|
|
1581
|
+
emailFields: import_zod.z.array(emailFieldSchema).max(20).optional().describe("Unsaved fields to try instead of the saved ones")
|
|
1612
1582
|
},
|
|
1613
1583
|
withErrorHandling(
|
|
1614
1584
|
async ({ slug, requestId, emailFields }) => jsonContent(await client.endpoints.previewEmailFields(slug, { requestId, emailFields }))
|
|
@@ -1618,8 +1588,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1618
1588
|
"preview_response",
|
|
1619
1589
|
"Dry run of an endpoint's responses: which response rule answers a captured request, and the response with its {{ placeholders }} filled in, exactly as the receiver would send it. Nothing is sent. Pass mockResponse or responseRules to try settings before saving them with update_endpoint.",
|
|
1620
1590
|
{
|
|
1621
|
-
slug:
|
|
1622
|
-
requestId:
|
|
1591
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1592
|
+
requestId: import_zod.z.string().optional().describe(
|
|
1623
1593
|
"A captured request on the endpoint (default: the newest HTTP request, or a sample)"
|
|
1624
1594
|
),
|
|
1625
1595
|
mockResponse: mockResponseSchema.nullable().optional().describe("Try this default response instead of the saved one (null: none)"),
|
|
@@ -1647,9 +1617,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1647
1617
|
"create_endpoints",
|
|
1648
1618
|
"Create multiple webhook endpoints in one call.",
|
|
1649
1619
|
{
|
|
1650
|
-
count:
|
|
1651
|
-
namePrefix:
|
|
1652
|
-
ephemeral:
|
|
1620
|
+
count: import_zod.z.number().int().min(1).max(20).describe("Number of endpoints to create"),
|
|
1621
|
+
namePrefix: import_zod.z.string().optional().describe("Optional prefix for endpoint names"),
|
|
1622
|
+
ephemeral: import_zod.z.boolean().optional().describe("Create temporary endpoints that auto-expire"),
|
|
1653
1623
|
expiresIn: durationOrTimestampSchema.optional().describe('Auto-expire after this duration, for example "12h"')
|
|
1654
1624
|
},
|
|
1655
1625
|
withErrorHandling(async ({ count, namePrefix, ephemeral, expiresIn }) => {
|
|
@@ -1670,7 +1640,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1670
1640
|
"delete_endpoints",
|
|
1671
1641
|
"Delete multiple webhook endpoints in one call.",
|
|
1672
1642
|
{
|
|
1673
|
-
slugs:
|
|
1643
|
+
slugs: import_zod.z.array(import_zod.z.string()).min(1).max(100).describe("Endpoint slugs to delete")
|
|
1674
1644
|
},
|
|
1675
1645
|
withErrorHandling(async ({ slugs }) => {
|
|
1676
1646
|
const settled = await Promise.allSettled(
|
|
@@ -1698,14 +1668,14 @@ function registerTools(target, client, options = {}) {
|
|
|
1698
1668
|
"send_webhook",
|
|
1699
1669
|
"Send a test webhook to a hosted endpoint. Supports provider templates and signing.",
|
|
1700
1670
|
{
|
|
1701
|
-
slug:
|
|
1671
|
+
slug: import_zod.z.string().describe("The endpoint slug to send to"),
|
|
1702
1672
|
method: methodSchema,
|
|
1703
|
-
headers:
|
|
1704
|
-
body:
|
|
1705
|
-
provider:
|
|
1706
|
-
template:
|
|
1707
|
-
event:
|
|
1708
|
-
secret:
|
|
1673
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional().describe("HTTP headers to include"),
|
|
1674
|
+
body: import_zod.z.unknown().optional().describe("Request body"),
|
|
1675
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to send with signed headers"),
|
|
1676
|
+
template: import_zod.z.string().optional().describe("Provider-specific template preset"),
|
|
1677
|
+
event: import_zod.z.string().optional().describe("Provider event or topic name"),
|
|
1678
|
+
secret: import_zod.z.string().optional().describe(
|
|
1709
1679
|
"Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
|
|
1710
1680
|
)
|
|
1711
1681
|
},
|
|
@@ -1751,10 +1721,10 @@ function registerTools(target, client, options = {}) {
|
|
|
1751
1721
|
"search_requests",
|
|
1752
1722
|
"Search captured requests and emails across endpoints: a substring match on path, headers, body and decoded email fields. One call reads back through history for about 2 seconds and returns { requests, complete, searchedUntil, cursor }. When complete is false, older requests were not searched yet: call again with the cursor and the same filters to continue.",
|
|
1753
1723
|
{
|
|
1754
|
-
slug:
|
|
1755
|
-
method:
|
|
1724
|
+
slug: import_zod.z.string().optional().describe("Filter to a specific endpoint slug"),
|
|
1725
|
+
method: import_zod.z.string().optional().describe("Filter by HTTP method"),
|
|
1756
1726
|
kind: kindSchema,
|
|
1757
|
-
q:
|
|
1727
|
+
q: import_zod.z.string().optional().describe(
|
|
1758
1728
|
"Substring to find in the path, headers, body and decoded email fields (up to 1,000 characters)"
|
|
1759
1729
|
),
|
|
1760
1730
|
from: durationOrTimestampSchema.optional().describe(
|
|
@@ -1763,10 +1733,10 @@ function registerTools(target, client, options = {}) {
|
|
|
1763
1733
|
to: durationOrTimestampSchema.optional().describe(
|
|
1764
1734
|
'End time as a timestamp (Unix ms) or a duration back from now like "1h" or "7d". A duration is counted from each call, so when you continue with a cursor pass a timestamp to keep the same range.'
|
|
1765
1735
|
),
|
|
1766
|
-
limit:
|
|
1767
|
-
offset:
|
|
1768
|
-
order:
|
|
1769
|
-
cursor:
|
|
1736
|
+
limit: import_zod.z.number().int().min(1).max(200).default(50).describe("Max results to return"),
|
|
1737
|
+
offset: import_zod.z.number().int().min(0).max(1e4).default(0).describe("Matches to skip (first call only; a cursor carries what is left of it)"),
|
|
1738
|
+
order: import_zod.z.enum(["asc", "desc"]).default("desc").describe("Sort order by received time"),
|
|
1739
|
+
cursor: import_zod.z.string().optional().describe(
|
|
1770
1740
|
"The cursor from a previous search_requests result, to continue that search. Keep the other inputs the same."
|
|
1771
1741
|
)
|
|
1772
1742
|
},
|
|
@@ -1801,10 +1771,10 @@ function registerTools(target, client, options = {}) {
|
|
|
1801
1771
|
"count_requests",
|
|
1802
1772
|
"Count captured requests and emails that match the given filters (q is a substring match on path, headers, body and decoded email fields). Returns { count, exact, searchedUntil, cursor }. With q or method a call counts back through history for about 2 seconds: when exact is false, count covers history back to searchedUntil only, and calling again with the cursor counts the next stretch (add the counts up).",
|
|
1803
1773
|
{
|
|
1804
|
-
slug:
|
|
1805
|
-
method:
|
|
1774
|
+
slug: import_zod.z.string().optional().describe("Filter to a specific endpoint slug"),
|
|
1775
|
+
method: import_zod.z.string().optional().describe("Filter by HTTP method"),
|
|
1806
1776
|
kind: kindSchema,
|
|
1807
|
-
q:
|
|
1777
|
+
q: import_zod.z.string().optional().describe(
|
|
1808
1778
|
"Substring to find in the path, headers, body and decoded email fields (up to 1,000 characters)"
|
|
1809
1779
|
),
|
|
1810
1780
|
from: durationOrTimestampSchema.optional().describe(
|
|
@@ -1813,7 +1783,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1813
1783
|
to: durationOrTimestampSchema.optional().describe(
|
|
1814
1784
|
'End time as a timestamp (Unix ms) or a duration back from now like "1h" or "7d". A duration is counted from each call, so when you continue with a cursor pass a timestamp to keep the same range.'
|
|
1815
1785
|
),
|
|
1816
|
-
cursor:
|
|
1786
|
+
cursor: import_zod.z.string().optional().describe(
|
|
1817
1787
|
"The cursor from a previous count_requests result, to count the next stretch. Keep the other inputs the same."
|
|
1818
1788
|
)
|
|
1819
1789
|
},
|
|
@@ -1853,11 +1823,11 @@ function registerTools(target, client, options = {}) {
|
|
|
1853
1823
|
"wait_for_requests",
|
|
1854
1824
|
"Wait for multiple requests to arrive at an endpoint.",
|
|
1855
1825
|
{
|
|
1856
|
-
endpointSlug:
|
|
1857
|
-
count:
|
|
1826
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug to monitor"),
|
|
1827
|
+
count: import_zod.z.number().int().min(1).max(20).describe("Number of requests to collect"),
|
|
1858
1828
|
timeout: durationOrTimestampSchema.default("30s").describe('How long to wait, for example "30s"'),
|
|
1859
1829
|
pollInterval: durationOrTimestampSchema.optional().describe('Interval between polls, for example "500ms" or "1s"'),
|
|
1860
|
-
method:
|
|
1830
|
+
method: import_zod.z.string().optional().describe("Only collect requests with this HTTP method")
|
|
1861
1831
|
},
|
|
1862
1832
|
withErrorHandling(async ({ endpointSlug, count, timeout, pollInterval, method }) => {
|
|
1863
1833
|
const result = await waitForMultipleRequests(client, endpointSlug, {
|
|
@@ -1875,7 +1845,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1875
1845
|
"replay_request",
|
|
1876
1846
|
"Replay a previously captured request to a target URL.",
|
|
1877
1847
|
{
|
|
1878
|
-
requestId:
|
|
1848
|
+
requestId: import_zod.z.string().describe("The captured request ID"),
|
|
1879
1849
|
targetUrl: httpUrlSchema.describe("The URL to replay the request to")
|
|
1880
1850
|
},
|
|
1881
1851
|
withErrorHandling(async ({ requestId, targetUrl }) => {
|
|
@@ -1892,9 +1862,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1892
1862
|
"render_email",
|
|
1893
1863
|
"A captured email in the shape an inbound mail provider posts it (postmark, sendgrid, mailgun, cloudmailin), to test the inbound handler you wrote for that provider against real mail. Returns the method, headers and body; with targetUrl (your local handler), posts it there and returns the answer. A Mailgun form is signed when you pass the signing key your handler checks.",
|
|
1894
1864
|
{
|
|
1895
|
-
requestId:
|
|
1896
|
-
format:
|
|
1897
|
-
mailgunSigningKey:
|
|
1865
|
+
requestId: import_zod.z.string().describe("The captured email's request ID"),
|
|
1866
|
+
format: import_zod.z.enum(["postmark", "sendgrid", "mailgun", "cloudmailin", "json"]).describe("Whose shape; json is webhooks.cc's own email.received JSON (unsigned)"),
|
|
1867
|
+
mailgunSigningKey: import_zod.z.string().optional().describe("For mailgun: the signing key your handler verifies with (a test key)"),
|
|
1898
1868
|
targetUrl: httpUrlSchema.optional().describe("Post it here, e.g. http://localhost:3000/api/inbound/postmark")
|
|
1899
1869
|
},
|
|
1900
1870
|
withErrorHandling(async ({ requestId, format, mailgunSigningKey, targetUrl }) => {
|
|
@@ -1925,9 +1895,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1925
1895
|
"compare_requests",
|
|
1926
1896
|
"Compare two captured requests and show the structured differences.",
|
|
1927
1897
|
{
|
|
1928
|
-
leftRequestId:
|
|
1929
|
-
rightRequestId:
|
|
1930
|
-
ignoreHeaders:
|
|
1898
|
+
leftRequestId: import_zod.z.string().describe("The first request ID"),
|
|
1899
|
+
rightRequestId: import_zod.z.string().describe("The second request ID"),
|
|
1900
|
+
ignoreHeaders: import_zod.z.array(import_zod.z.string()).optional().describe("Headers to ignore during comparison")
|
|
1931
1901
|
},
|
|
1932
1902
|
withErrorHandling(async ({ leftRequestId, rightRequestId, ignoreHeaders }) => {
|
|
1933
1903
|
const [leftRequest, rightRequest] = await Promise.all([
|
|
@@ -1951,8 +1921,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1951
1921
|
"extract_from_request",
|
|
1952
1922
|
"Extract specific JSON fields from a captured request body.",
|
|
1953
1923
|
{
|
|
1954
|
-
requestId:
|
|
1955
|
-
jsonPaths:
|
|
1924
|
+
requestId: import_zod.z.string().describe("The request ID"),
|
|
1925
|
+
jsonPaths: import_zod.z.array(import_zod.z.string()).min(1).max(50).describe("Dot-notation JSON paths to extract")
|
|
1956
1926
|
},
|
|
1957
1927
|
withErrorHandling(async ({ requestId, jsonPaths }) => {
|
|
1958
1928
|
const request = await client.requests.get(requestId);
|
|
@@ -1966,14 +1936,14 @@ function registerTools(target, client, options = {}) {
|
|
|
1966
1936
|
"verify_signature",
|
|
1967
1937
|
"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.",
|
|
1968
1938
|
{
|
|
1969
|
-
requestId:
|
|
1970
|
-
provider:
|
|
1971
|
-
secret:
|
|
1939
|
+
requestId: import_zod.z.string().describe("The captured request ID"),
|
|
1940
|
+
provider: import_zod.z.enum(import_sdk.VERIFY_PROVIDERS).optional().describe("Provider whose signature scheme should be verified. Omit to use stored result."),
|
|
1941
|
+
secret: import_zod.z.string().optional().describe(
|
|
1972
1942
|
"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."
|
|
1973
1943
|
),
|
|
1974
|
-
publicKey:
|
|
1944
|
+
publicKey: import_zod.z.string().optional().describe("Discord application public key. Required for provider=discord."),
|
|
1975
1945
|
url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio, Square, and HubSpot verification."),
|
|
1976
|
-
method:
|
|
1946
|
+
method: import_zod.z.string().optional().describe(
|
|
1977
1947
|
"Original HTTP method used when the signature was generated. Used by HubSpot v3 verification; the SDK assumes POST when omitted."
|
|
1978
1948
|
)
|
|
1979
1949
|
},
|
|
@@ -2022,7 +1992,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2022
1992
|
"clear_requests",
|
|
2023
1993
|
"Delete captured requests for an endpoint without deleting the endpoint itself.",
|
|
2024
1994
|
{
|
|
2025
|
-
slug:
|
|
1995
|
+
slug: import_zod.z.string().describe("The endpoint slug to clear"),
|
|
2026
1996
|
before: durationOrTimestampSchema.optional().describe('Only clear requests older than this timestamp or duration like "1h"')
|
|
2027
1997
|
},
|
|
2028
1998
|
withErrorHandling(async ({ slug, before }) => {
|
|
@@ -2036,12 +2006,12 @@ function registerTools(target, client, options = {}) {
|
|
|
2036
2006
|
{
|
|
2037
2007
|
url: httpUrlSchema.describe("Target URL"),
|
|
2038
2008
|
method: methodSchema,
|
|
2039
|
-
headers:
|
|
2040
|
-
body:
|
|
2041
|
-
provider:
|
|
2042
|
-
template:
|
|
2043
|
-
event:
|
|
2044
|
-
secret:
|
|
2009
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional().describe("HTTP headers to include"),
|
|
2010
|
+
body: import_zod.z.unknown().optional().describe("Request body"),
|
|
2011
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
|
|
2012
|
+
template: import_zod.z.string().optional().describe("Provider-specific template preset"),
|
|
2013
|
+
event: import_zod.z.string().optional().describe("Provider event or topic name"),
|
|
2014
|
+
secret: import_zod.z.string().optional().describe(
|
|
2045
2015
|
"Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
|
|
2046
2016
|
)
|
|
2047
2017
|
},
|
|
@@ -2069,12 +2039,12 @@ function registerTools(target, client, options = {}) {
|
|
|
2069
2039
|
{
|
|
2070
2040
|
url: httpUrlSchema.describe("Target URL"),
|
|
2071
2041
|
method: methodSchema,
|
|
2072
|
-
headers:
|
|
2073
|
-
body:
|
|
2074
|
-
provider:
|
|
2075
|
-
template:
|
|
2076
|
-
event:
|
|
2077
|
-
secret:
|
|
2042
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional().describe("HTTP headers to include"),
|
|
2043
|
+
body: import_zod.z.unknown().optional().describe("Request body"),
|
|
2044
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
|
|
2045
|
+
template: import_zod.z.string().optional().describe("Provider-specific template preset"),
|
|
2046
|
+
event: import_zod.z.string().optional().describe("Provider event or topic name"),
|
|
2047
|
+
secret: import_zod.z.string().optional().describe(
|
|
2078
2048
|
"Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
|
|
2079
2049
|
)
|
|
2080
2050
|
},
|
|
@@ -2095,7 +2065,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2095
2065
|
"list_provider_templates",
|
|
2096
2066
|
"List supported webhook providers, templates, and signing metadata.",
|
|
2097
2067
|
{
|
|
2098
|
-
provider:
|
|
2068
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Filter to a single provider")
|
|
2099
2069
|
},
|
|
2100
2070
|
withErrorHandling(async ({ provider }) => {
|
|
2101
2071
|
if (provider) {
|
|
@@ -2149,15 +2119,15 @@ function registerTools(target, client, options = {}) {
|
|
|
2149
2119
|
"test_webhook_flow",
|
|
2150
2120
|
"Run a full webhook test flow: create endpoint, optionally mock, send, wait, verify, replay, and clean up.",
|
|
2151
2121
|
{
|
|
2152
|
-
provider:
|
|
2153
|
-
event:
|
|
2154
|
-
secret:
|
|
2122
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to use when sending the webhook"),
|
|
2123
|
+
event: import_zod.z.string().optional().describe("Optional provider event or topic name"),
|
|
2124
|
+
secret: import_zod.z.string().optional().describe(
|
|
2155
2125
|
"Signing secret. Required when provider is set or signature verification is enabled."
|
|
2156
2126
|
),
|
|
2157
|
-
mockStatus:
|
|
2127
|
+
mockStatus: import_zod.z.number().int().min(100).max(599).optional().describe("Optional mock response status to configure before sending"),
|
|
2158
2128
|
targetUrl: httpUrlSchema.optional().describe("Optional URL to replay the captured request to after capture"),
|
|
2159
|
-
verifySignature:
|
|
2160
|
-
cleanup:
|
|
2129
|
+
verifySignature: import_zod.z.boolean().default(false).describe("Verify the captured request signature after capture"),
|
|
2130
|
+
cleanup: import_zod.z.boolean().default(true).describe("Delete the created endpoint after the flow completes")
|
|
2161
2131
|
},
|
|
2162
2132
|
withErrorHandling(
|
|
2163
2133
|
async ({
|
|
@@ -2232,14 +2202,14 @@ function registerTools(target, client, options = {}) {
|
|
|
2232
2202
|
"list_emails",
|
|
2233
2203
|
"List the emails an endpoint received at its mailhooks.cc address, newest first: subject, sender, tag, and the one-time code and main link found in each.",
|
|
2234
2204
|
{
|
|
2235
|
-
endpointSlug:
|
|
2236
|
-
limit:
|
|
2205
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug"),
|
|
2206
|
+
limit: import_zod.z.number().int().min(1).max(100).default(25).describe(
|
|
2237
2207
|
`Max emails to return. With tag, subject or from, the newest ${EMAIL_SCAN_LIMIT} emails are searched.`
|
|
2238
2208
|
),
|
|
2239
|
-
since:
|
|
2240
|
-
tag:
|
|
2241
|
-
subject:
|
|
2242
|
-
from:
|
|
2209
|
+
since: import_zod.z.number().optional().describe("Only emails received after this timestamp in ms"),
|
|
2210
|
+
tag: import_zod.z.string().optional().describe("Only emails sent to {slug}+{tag}@mailhooks.cc (exact, case-sensitive)"),
|
|
2211
|
+
subject: import_zod.z.string().optional().describe("Only emails whose subject contains this text"),
|
|
2212
|
+
from: import_zod.z.string().optional().describe("Only emails from this address (case-insensitive)")
|
|
2243
2213
|
},
|
|
2244
2214
|
withErrorHandling(async ({ endpointSlug, limit, since, tag, subject, from }) => {
|
|
2245
2215
|
const filtered = tag !== void 0 || subject !== void 0 || from !== void 0;
|
|
@@ -2257,10 +2227,10 @@ function registerTools(target, client, options = {}) {
|
|
|
2257
2227
|
"get_email",
|
|
2258
2228
|
"Get one captured email: by requestId, or the newest at an endpoint (optionally with a tag). Returns the parsed message with its one-time code and main link.",
|
|
2259
2229
|
{
|
|
2260
|
-
requestId:
|
|
2261
|
-
endpointSlug:
|
|
2262
|
-
tag:
|
|
2263
|
-
includeHtml:
|
|
2230
|
+
requestId: import_zod.z.string().optional().describe("The email's request ID"),
|
|
2231
|
+
endpointSlug: import_zod.z.string().optional().describe("Get the newest email at this endpoint instead (with requestId unset)"),
|
|
2232
|
+
tag: import_zod.z.string().optional().describe("With endpointSlug: only emails sent to this +tag"),
|
|
2233
|
+
includeHtml: import_zod.z.boolean().default(false).describe("Include the HTML part (default: false, only its size)")
|
|
2264
2234
|
},
|
|
2265
2235
|
withErrorHandling(async ({ requestId, endpointSlug, tag, includeHtml }) => {
|
|
2266
2236
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
@@ -2283,15 +2253,15 @@ function registerTools(target, client, options = {}) {
|
|
|
2283
2253
|
"wait_for_email",
|
|
2284
2254
|
"Wait for an email to arrive at an endpoint (for example after triggering a signup), then return it with its one-time code and main link. Use a tag per run to find your own email.",
|
|
2285
2255
|
{
|
|
2286
|
-
endpointSlug:
|
|
2287
|
-
tag:
|
|
2288
|
-
subject:
|
|
2289
|
-
from:
|
|
2256
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug"),
|
|
2257
|
+
tag: import_zod.z.string().optional().describe("Only emails sent to {slug}+{tag}@mailhooks.cc"),
|
|
2258
|
+
subject: import_zod.z.string().optional().describe("Only emails whose subject contains this text"),
|
|
2259
|
+
from: import_zod.z.string().optional().describe("Only emails from this address (case-insensitive)"),
|
|
2290
2260
|
timeout: durationOrTimestampSchema.default("30s").describe(
|
|
2291
2261
|
`How long to wait (default "30s"). Keep it under your MCP client's request timeout, often 60 seconds.`
|
|
2292
2262
|
),
|
|
2293
|
-
since:
|
|
2294
|
-
includeHtml:
|
|
2263
|
+
since: import_zod.z.number().optional().describe("Only emails received after this timestamp in ms (default: five minutes ago)"),
|
|
2264
|
+
includeHtml: import_zod.z.boolean().default(false).describe("Include the HTML part (default: false)")
|
|
2295
2265
|
},
|
|
2296
2266
|
withErrorHandling(async ({ endpointSlug, tag, subject, from, timeout, since, includeHtml }) => {
|
|
2297
2267
|
const view = await emailEndpointView(client, endpointSlug);
|
|
@@ -2309,8 +2279,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2309
2279
|
"send_test_email",
|
|
2310
2280
|
"Deliver a sample email (with a 6-digit code and a link) to an endpoint, optionally to a +tag address. It counts as one request and skips SMTP, so sender checks do not run on it.",
|
|
2311
2281
|
{
|
|
2312
|
-
slug:
|
|
2313
|
-
tag:
|
|
2282
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2283
|
+
tag: import_zod.z.string().optional().describe("Deliver to {slug}+{tag}@mailhooks.cc")
|
|
2314
2284
|
},
|
|
2315
2285
|
withErrorHandling(async ({ slug, tag }) => {
|
|
2316
2286
|
const result = await client.emails.sendTest(slug, { tag });
|
|
@@ -2321,8 +2291,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2321
2291
|
"list_destinations",
|
|
2322
2292
|
"List where an endpoint's captures go: its destinations (up to 10) with their URL, kinds, filter, format, templates and timing, and with summary: true their last 24 hours. Owner only.",
|
|
2323
2293
|
{
|
|
2324
|
-
slug:
|
|
2325
|
-
summary:
|
|
2294
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2295
|
+
summary: import_zod.z.boolean().default(true).describe("Add each destination's last 24 hours")
|
|
2326
2296
|
},
|
|
2327
2297
|
withErrorHandling(async ({ slug, summary }) => {
|
|
2328
2298
|
return jsonContent(await client.destinations.list(slug, { summary }));
|
|
@@ -2332,7 +2302,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2332
2302
|
"add_destination",
|
|
2333
2303
|
"Add a destination for an endpoint's captures: your server (as received or signed JSON), a Slack or Discord channel (chat, optionally your own messageTemplate, throttleSeconds 1 for a heads-up), or a JSON body you shape with bodyTemplate (for Zapier, n8n or your API). filter sends only matching captures; delaySeconds makes it a callback. Each destination gets its own signing secret. Check it with preview_destination before relying on it. Owner only.",
|
|
2334
2304
|
{
|
|
2335
|
-
slug:
|
|
2305
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2336
2306
|
url: httpUrlSchema.describe("https URL on a public host name, or a Slack or Discord webhook"),
|
|
2337
2307
|
...destinationFields
|
|
2338
2308
|
},
|
|
@@ -2344,8 +2314,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2344
2314
|
"update_destination",
|
|
2345
2315
|
"Change a destination: any of its fields; only the ones you pass change. Owner only.",
|
|
2346
2316
|
{
|
|
2347
|
-
slug:
|
|
2348
|
-
destinationId:
|
|
2317
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2318
|
+
destinationId: import_zod.z.string().describe("The destination's id (from list_destinations)"),
|
|
2349
2319
|
url: httpUrlSchema.optional().describe("A new URL"),
|
|
2350
2320
|
...destinationFields
|
|
2351
2321
|
},
|
|
@@ -2357,8 +2327,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2357
2327
|
"remove_destination",
|
|
2358
2328
|
"Remove a destination. Deliveries still waiting for it are marked failed; its log stays. Owner only.",
|
|
2359
2329
|
{
|
|
2360
|
-
slug:
|
|
2361
|
-
destinationId:
|
|
2330
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2331
|
+
destinationId: import_zod.z.string().describe("The destination's id")
|
|
2362
2332
|
},
|
|
2363
2333
|
withErrorHandling(async ({ slug, destinationId }) => {
|
|
2364
2334
|
await client.destinations.delete(slug, destinationId);
|
|
@@ -2369,10 +2339,10 @@ function registerTools(target, client, options = {}) {
|
|
|
2369
2339
|
"preview_destination",
|
|
2370
2340
|
"Dry run of a destination against a captured request: whether its filter lets the request through and the exact request it would send (method, URL, headers, body with placeholders filled in). Nothing is sent. Use destinationId for a saved destination, destination for unsaved fields (on top of the saved ones, or with a url on their own), or both.",
|
|
2371
2341
|
{
|
|
2372
|
-
slug:
|
|
2373
|
-
destinationId:
|
|
2374
|
-
destination:
|
|
2375
|
-
requestId:
|
|
2342
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2343
|
+
destinationId: import_zod.z.string().optional().describe("A saved destination"),
|
|
2344
|
+
destination: import_zod.z.object({ url: httpUrlSchema.optional(), ...destinationFields }).optional().describe("Unsaved fields to try"),
|
|
2345
|
+
requestId: import_zod.z.string().optional().describe("A captured request (default: the newest of the kind it takes, or a sample)")
|
|
2376
2346
|
},
|
|
2377
2347
|
withErrorHandling(async ({ slug, destinationId, destination, requestId }) => {
|
|
2378
2348
|
if (!destinationId && !destination?.url) {
|
|
@@ -2391,26 +2361,26 @@ function registerTools(target, client, options = {}) {
|
|
|
2391
2361
|
"configure_forwarding",
|
|
2392
2362
|
"Change an endpoint's primary forwarding destination (the one the endpoint's forward* fields stand for): the URL, on or off, which kinds, the format, your own headers, and the advanced options. Only the arguments you pass change. Owner only. For several destinations, filters, templates, throttling or delays, use add_destination and update_destination.",
|
|
2393
2363
|
{
|
|
2394
|
-
slug:
|
|
2364
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2395
2365
|
url: httpUrlSchema.nullable().optional().describe("https URL on a public host name, or null to remove it (forwarding must be off)"),
|
|
2396
|
-
enabled:
|
|
2397
|
-
http:
|
|
2398
|
-
email:
|
|
2399
|
-
format:
|
|
2366
|
+
enabled: import_zod.z.boolean().optional().describe("Turn forwarding on (needs a URL) or off (fails deliveries still waiting)"),
|
|
2367
|
+
http: import_zod.z.boolean().optional().describe("Forward HTTP requests"),
|
|
2368
|
+
email: import_zod.z.boolean().optional().describe("Forward emails"),
|
|
2369
|
+
format: import_zod.z.enum(["auto", "as_received", "json", "chat"]).optional().describe(
|
|
2400
2370
|
"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)"
|
|
2401
2371
|
),
|
|
2402
|
-
headers:
|
|
2403
|
-
|
|
2404
|
-
name:
|
|
2405
|
-
value:
|
|
2372
|
+
headers: import_zod.z.array(
|
|
2373
|
+
import_zod.z.object({
|
|
2374
|
+
name: import_zod.z.string().describe("Header name, e.g. Authorization"),
|
|
2375
|
+
value: import_zod.z.string().nullable().describe("Header value, or null to keep the value stored under this name")
|
|
2406
2376
|
})
|
|
2407
2377
|
).max(10).optional().describe(
|
|
2408
2378
|
"Your own headers for every delivery (not chat). Replaces the whole list; [] removes them"
|
|
2409
2379
|
),
|
|
2410
|
-
appendPath:
|
|
2411
|
-
retrySeconds:
|
|
2412
|
-
keepOrder:
|
|
2413
|
-
sentField:
|
|
2380
|
+
appendPath: import_zod.z.boolean().optional().describe("Add the path after the slug to the URL for relayed requests (default true)"),
|
|
2381
|
+
retrySeconds: import_zod.z.union([import_zod.z.literal(0), import_zod.z.literal(3600), import_zod.z.literal(86400)]).optional().describe("Retry failed deliveries for 86400 (default), 3600 or 0 seconds"),
|
|
2382
|
+
keepOrder: import_zod.z.boolean().optional().describe("One delivery at a time, in capture order (default false)"),
|
|
2383
|
+
sentField: import_zod.z.string().max(128).nullable().optional().describe(
|
|
2414
2384
|
"JSON body field with the sender's own timestamp, e.g. publishedAt or data.publishedAt; null clears it"
|
|
2415
2385
|
)
|
|
2416
2386
|
},
|
|
@@ -2436,9 +2406,9 @@ function registerTools(target, client, options = {}) {
|
|
|
2436
2406
|
"get_forwarding_secret",
|
|
2437
2407
|
"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.",
|
|
2438
2408
|
{
|
|
2439
|
-
slug:
|
|
2440
|
-
destinationId:
|
|
2441
|
-
rotate:
|
|
2409
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2410
|
+
destinationId: import_zod.z.string().optional().describe("A destination (default: the endpoint's primary destination)"),
|
|
2411
|
+
rotate: import_zod.z.boolean().default(false).describe("Replace the secret; every delivery from now on uses the new one")
|
|
2442
2412
|
},
|
|
2443
2413
|
withErrorHandling(async ({ slug, destinationId, rotate }) => {
|
|
2444
2414
|
const secret = destinationId ? rotate ? await client.destinations.rotateSecret(slug, destinationId) : await client.destinations.secret(slug, destinationId) : rotate ? await client.forwarding.rotateSecret(slug) : await client.forwarding.secret(slug);
|
|
@@ -2449,10 +2419,10 @@ function registerTools(target, client, options = {}) {
|
|
|
2449
2419
|
"test_forwarding",
|
|
2450
2420
|
"Send the endpoint's newest captured request of a kind (or a sample, or requestId) to a destination once, exactly as a delivery would go out, and report the answer. Works whether it is on or off, and whatever its filter says; nothing is retried or logged.",
|
|
2451
2421
|
{
|
|
2452
|
-
slug:
|
|
2453
|
-
destinationId:
|
|
2454
|
-
kind:
|
|
2455
|
-
requestId:
|
|
2422
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2423
|
+
destinationId: import_zod.z.string().optional().describe("A destination (default: the endpoint's primary destination)"),
|
|
2424
|
+
kind: import_zod.z.enum(["http", "email"]).optional().describe("Which kind to send (default: http when the destination takes HTTP requests)"),
|
|
2425
|
+
requestId: import_zod.z.string().optional().describe("With destinationId: a captured request to send")
|
|
2456
2426
|
},
|
|
2457
2427
|
withErrorHandling(async ({ slug, destinationId, kind, requestId }) => {
|
|
2458
2428
|
if (destinationId) {
|
|
@@ -2470,14 +2440,14 @@ function registerTools(target, client, options = {}) {
|
|
|
2470
2440
|
"list_deliveries",
|
|
2471
2441
|
"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.",
|
|
2472
2442
|
{
|
|
2473
|
-
endpointSlug:
|
|
2474
|
-
requestId:
|
|
2475
|
-
limit:
|
|
2476
|
-
status:
|
|
2443
|
+
endpointSlug: import_zod.z.string().optional().describe("List the endpoint's delivery log"),
|
|
2444
|
+
requestId: import_zod.z.string().optional().describe("List every delivery and try of this request"),
|
|
2445
|
+
limit: import_zod.z.number().int().min(1).max(100).default(20).describe("With endpointSlug: max rows"),
|
|
2446
|
+
status: import_zod.z.enum(["all", "pending", "failed", "skipped"]).default("all").describe(
|
|
2477
2447
|
"With endpointSlug: all, pending (queued or retrying), failed or skipped (filtered out)"
|
|
2478
2448
|
),
|
|
2479
|
-
destinationId:
|
|
2480
|
-
before:
|
|
2449
|
+
destinationId: import_zod.z.string().optional().describe("With endpointSlug: only this destination"),
|
|
2450
|
+
before: import_zod.z.string().optional().describe("With endpointSlug: a row's cursor, to list older rows")
|
|
2481
2451
|
},
|
|
2482
2452
|
withErrorHandling(async ({ endpointSlug, requestId, limit, status, destinationId, before }) => {
|
|
2483
2453
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
@@ -2504,9 +2474,9 @@ function registerTools(target, client, options = {}) {
|
|
|
2504
2474
|
"redeliver",
|
|
2505
2475
|
"Forward again with the current settings: one captured request or email (requestId; to every destination that takes it, or with destinationId to that one whatever its filter says), or every request whose latest delivery failed (endpointSlug, optionally only destinationId, oldest first, up to 1,000 at a time per destination). Owner only.",
|
|
2506
2476
|
{
|
|
2507
|
-
requestId:
|
|
2508
|
-
endpointSlug:
|
|
2509
|
-
destinationId:
|
|
2477
|
+
requestId: import_zod.z.string().optional().describe("The request or email to send again"),
|
|
2478
|
+
endpointSlug: import_zod.z.string().optional().describe("Send every failed delivery of this endpoint again"),
|
|
2479
|
+
destinationId: import_zod.z.string().optional().describe("Only this destination")
|
|
2510
2480
|
},
|
|
2511
2481
|
withErrorHandling(async ({ requestId, endpointSlug, destinationId }) => {
|
|
2512
2482
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
@@ -2523,9 +2493,9 @@ function registerTools(target, client, options = {}) {
|
|
|
2523
2493
|
"share_port",
|
|
2524
2494
|
"Share a port on this computer at a public link (https://<name>.sharehooks.cc) by running the webhooks.cc CLI (whk share), so a person can open the site you are building. Returns the link. It keeps running until stop_share, the plan's session limit, or the end of this MCP server. Asking again for the same port without a name or password returns the running link; with a name or password, the running share on that port stops and a new one starts. Free accounts get a random name ending in -free, a 2-hour limit and a notice page before the site; Pro and Business can use a reserved name and a password. Needs whk installed on this computer. For a webhook provider that must get your local app's real answer, use `whk tunnel <port> --live` instead.",
|
|
2525
2495
|
{
|
|
2526
|
-
port:
|
|
2527
|
-
name:
|
|
2528
|
-
password:
|
|
2496
|
+
port: import_zod.z.number().int().min(1).max(65535).describe("The local port the site runs on"),
|
|
2497
|
+
name: import_zod.z.string().optional().describe("A name you reserved (Pro and Business); leave out for a random one"),
|
|
2498
|
+
password: import_zod.z.string().min(8).max(200).optional().describe("What visitors must type (Pro and Business), 8 to 200 characters")
|
|
2529
2499
|
},
|
|
2530
2500
|
withErrorHandling(async ({ port, name, password }) => {
|
|
2531
2501
|
const running = started.get(port);
|
|
@@ -2570,7 +2540,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2570
2540
|
"list_shares",
|
|
2571
2541
|
`List your public previews of local ports (whk share${remote ? "" : ", share_port"}) and live tunnels (whk tunnel --live): live ones first, then the latest that ended, with this month's shared data and your plan's sharing limits. Not the same as share_endpoint, which shares an endpoint with a team.`,
|
|
2572
2542
|
{
|
|
2573
|
-
state:
|
|
2543
|
+
state: import_zod.z.enum(["live", "ended"]).optional().describe("Only the running ones, or only the ended ones")
|
|
2574
2544
|
},
|
|
2575
2545
|
withErrorHandling(async ({ state }) => {
|
|
2576
2546
|
return jsonContent(withShareHints(await client.shares.list(state ? { state } : {}), remote));
|
|
@@ -2586,8 +2556,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2586
2556
|
"create_api_key",
|
|
2587
2557
|
"Ask for a new API key for the account, for CI or another tool. The account's owner must approve it: this returns at once with a link and a code. Show the person the link and tell them to check that the page shows the same code, then call wait_for_approval with the approval id to get the key (shown once).",
|
|
2588
2558
|
{
|
|
2589
|
-
name:
|
|
2590
|
-
expiresInDays:
|
|
2559
|
+
name: import_zod.z.string().min(1).max(64).describe('A name that says where the key is used, such as "GitHub Actions"'),
|
|
2560
|
+
expiresInDays: import_zod.z.number().int().min(1).max(365).optional().describe("How many days the key works, 1 to 365 (default 90)")
|
|
2591
2561
|
},
|
|
2592
2562
|
withErrorHandling(async ({ name, expiresInDays }) => {
|
|
2593
2563
|
const pending = await client.apiKeys.create({ name, expiresInDays });
|
|
@@ -2602,7 +2572,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2602
2572
|
"wait_for_approval",
|
|
2603
2573
|
"Wait for the account's owner to answer an approval (from create_api_key, create_share_link, remove_domain, rename_account, delete_team, invite_to_team, connect_google, pick_spreadsheet or ai_setup) and collect what it grants or did, such as the new API key, the share link or AI setup's proposals. If they have not answered yet, call it again.",
|
|
2604
2574
|
{
|
|
2605
|
-
id:
|
|
2575
|
+
id: import_zod.z.string().describe("The approval id"),
|
|
2606
2576
|
timeout: durationOrTimestampSchema.default("45s").describe('How long to wait for the person, for example "45s"')
|
|
2607
2577
|
},
|
|
2608
2578
|
withErrorHandling(async ({ id, timeout }) => {
|
|
@@ -2634,7 +2604,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2634
2604
|
"revoke_api_key",
|
|
2635
2605
|
"Revoke one of the account's API keys by its id (from list_api_keys). Anything using it stops working at once.",
|
|
2636
2606
|
{
|
|
2637
|
-
id:
|
|
2607
|
+
id: import_zod.z.string().describe("The key's id, from list_api_keys")
|
|
2638
2608
|
},
|
|
2639
2609
|
withErrorHandling(async ({ id }) => {
|
|
2640
2610
|
await client.apiKeys.revoke(id);
|
|
@@ -2645,7 +2615,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2645
2615
|
"rename_account",
|
|
2646
2616
|
"Set the account's display name, or clear it with null. The account's owner must approve it: this returns a link and a code to show them, then call wait_for_approval.",
|
|
2647
2617
|
{
|
|
2648
|
-
name:
|
|
2618
|
+
name: import_zod.z.string().min(1).max(100).nullable().describe("The new display name, or null to clear it")
|
|
2649
2619
|
},
|
|
2650
2620
|
withErrorHandling(async ({ name }) => {
|
|
2651
2621
|
const pending = await client.account.rename(name);
|
|
@@ -2663,7 +2633,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2663
2633
|
"disconnect_agent",
|
|
2664
2634
|
"Disconnect an agent by its id (from list_connected_agents): its tokens stop working at once. The endpoints it made stay in the account. Disconnecting the agent you are running as ends your own access.",
|
|
2665
2635
|
{
|
|
2666
|
-
id:
|
|
2636
|
+
id: import_zod.z.string().describe("The agent's id, from list_connected_agents")
|
|
2667
2637
|
},
|
|
2668
2638
|
withErrorHandling(async ({ id }) => {
|
|
2669
2639
|
await client.agents.disconnect(id);
|
|
@@ -2674,7 +2644,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2674
2644
|
"list_share_links",
|
|
2675
2645
|
"List live public links to frozen copies of requests (/s/<token>), the account's or one request's. Not the same as list_shares, which lists port previews and live tunnels.",
|
|
2676
2646
|
{
|
|
2677
|
-
requestId:
|
|
2647
|
+
requestId: import_zod.z.string().optional().describe("Only the links to this request")
|
|
2678
2648
|
},
|
|
2679
2649
|
withErrorHandling(async ({ requestId }) => {
|
|
2680
2650
|
const links = requestId ? await client.shareLinks.forRequest(requestId) : await client.shareLinks.list();
|
|
@@ -2685,9 +2655,9 @@ function registerTools(target, client, options = {}) {
|
|
|
2685
2655
|
"create_share_link",
|
|
2686
2656
|
"Make a public link to a read-only copy of one HTTP request (not an email), for a teammate or a bug report. Anyone with the link can read it, so the account's owner must approve: this returns a link and a code to show them, then call wait_for_approval for the share link.",
|
|
2687
2657
|
{
|
|
2688
|
-
requestId:
|
|
2689
|
-
expiresInDays:
|
|
2690
|
-
hideSensitive:
|
|
2658
|
+
requestId: import_zod.z.string().describe("The request's id"),
|
|
2659
|
+
expiresInDays: import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(7), import_zod.z.literal(30)]).optional().describe("How long the link works: 1, 7 or 30 days (default 7)"),
|
|
2660
|
+
hideSensitive: import_zod.z.boolean().optional().describe(
|
|
2691
2661
|
"Also leave out signature and token headers (default true). Credentials are always left out."
|
|
2692
2662
|
)
|
|
2693
2663
|
},
|
|
@@ -2701,14 +2671,14 @@ function registerTools(target, client, options = {}) {
|
|
|
2701
2671
|
"revoke_share_link",
|
|
2702
2672
|
"Revoke a share link by its id (from list_share_links): its page answers 404 from then on.",
|
|
2703
2673
|
{
|
|
2704
|
-
id:
|
|
2674
|
+
id: import_zod.z.string().describe("The link's id, from list_share_links")
|
|
2705
2675
|
},
|
|
2706
2676
|
withErrorHandling(async ({ id }) => {
|
|
2707
2677
|
await client.shareLinks.revoke(id);
|
|
2708
2678
|
return textContent(`Revoked the share link ${id}.`);
|
|
2709
2679
|
})
|
|
2710
2680
|
);
|
|
2711
|
-
const teamIdSchema =
|
|
2681
|
+
const teamIdSchema = import_zod.z.string().optional().describe("A Team Business team's id (its owner only); leave out for your own account");
|
|
2712
2682
|
server.tool(
|
|
2713
2683
|
"list_domains",
|
|
2714
2684
|
"List custom inbound domains (Business): each one's status, DNS records to publish, routes and catch-all endpoint.",
|
|
@@ -2719,7 +2689,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2719
2689
|
"add_domain",
|
|
2720
2690
|
"Add a custom inbound domain (Business) so mail to it is captured. Returns the TXT and MX records to publish; then call verify_domain.",
|
|
2721
2691
|
{
|
|
2722
|
-
domain:
|
|
2692
|
+
domain: import_zod.z.string().min(1).max(253).describe("The domain, such as mail.example.com"),
|
|
2723
2693
|
teamId: teamIdSchema
|
|
2724
2694
|
},
|
|
2725
2695
|
withErrorHandling(
|
|
@@ -2730,7 +2700,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2730
2700
|
"verify_domain",
|
|
2731
2701
|
"Look a custom domain's TXT and MX records up now. It receives mail once both are found.",
|
|
2732
2702
|
{
|
|
2733
|
-
id:
|
|
2703
|
+
id: import_zod.z.string().describe("The domain's id, from list_domains"),
|
|
2734
2704
|
teamId: teamIdSchema
|
|
2735
2705
|
},
|
|
2736
2706
|
withErrorHandling(
|
|
@@ -2741,12 +2711,12 @@ function registerTools(target, client, options = {}) {
|
|
|
2741
2711
|
"route_domain",
|
|
2742
2712
|
"Set where a custom domain's mail goes: routes by local part (the part before @) to endpoints, everything else to a catch-all endpoint, or refused when the catch-all is null. Replaces the domain's routing.",
|
|
2743
2713
|
{
|
|
2744
|
-
id:
|
|
2745
|
-
catchAllEndpointId:
|
|
2746
|
-
routes:
|
|
2747
|
-
|
|
2748
|
-
localPart:
|
|
2749
|
-
endpointId:
|
|
2714
|
+
id: import_zod.z.string().describe("The domain's id, from list_domains"),
|
|
2715
|
+
catchAllEndpointId: import_zod.z.string().nullable().describe("The endpoint id for mail no route matches, or null to refuse it"),
|
|
2716
|
+
routes: import_zod.z.array(
|
|
2717
|
+
import_zod.z.object({
|
|
2718
|
+
localPart: import_zod.z.string().max(64).describe("The part before @"),
|
|
2719
|
+
endpointId: import_zod.z.string().describe("The endpoint's id")
|
|
2750
2720
|
})
|
|
2751
2721
|
).max(100).default([]).describe("Routes by local part"),
|
|
2752
2722
|
teamId: teamIdSchema
|
|
@@ -2759,7 +2729,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2759
2729
|
"remove_domain",
|
|
2760
2730
|
"Remove a custom inbound domain: mail to it is refused from then on. The account's owner must approve it: this returns a link and a code to show them, then call wait_for_approval.",
|
|
2761
2731
|
{
|
|
2762
|
-
id:
|
|
2732
|
+
id: import_zod.z.string().describe("The domain's id, from list_domains"),
|
|
2763
2733
|
teamId: teamIdSchema
|
|
2764
2734
|
},
|
|
2765
2735
|
withErrorHandling(async ({ id, teamId }) => {
|
|
@@ -2784,7 +2754,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2784
2754
|
"pick_spreadsheet",
|
|
2785
2755
|
"Have the account's owner choose an existing spreadsheet from Google Drive with a connected account (to add rows to it from a Sheets destination). Returns a link and a code to show them; the page opens Google's picker. Then call wait_for_approval for the spreadsheet's id and tabs. To use a new spreadsheet instead, call create_spreadsheet.",
|
|
2786
2756
|
{
|
|
2787
|
-
connectionId:
|
|
2757
|
+
connectionId: import_zod.z.string().describe("The Google connection's id, from list_google_connections")
|
|
2788
2758
|
},
|
|
2789
2759
|
withErrorHandling(
|
|
2790
2760
|
async ({ connectionId }) => textContent(approvalAskText(await client.google.pick(connectionId)))
|
|
@@ -2794,10 +2764,10 @@ function registerTools(target, client, options = {}) {
|
|
|
2794
2764
|
"create_spreadsheet",
|
|
2795
2765
|
"Create a new spreadsheet with a header row in a connected Google account, for a Sheets destination. Needs no approval: the app reaches only spreadsheets it created or that were chosen for it.",
|
|
2796
2766
|
{
|
|
2797
|
-
connectionId:
|
|
2798
|
-
title:
|
|
2799
|
-
sheet:
|
|
2800
|
-
headers:
|
|
2767
|
+
connectionId: import_zod.z.string().describe("The Google connection's id, from list_google_connections"),
|
|
2768
|
+
title: import_zod.z.string().max(200).optional().describe("The spreadsheet's title"),
|
|
2769
|
+
sheet: import_zod.z.string().max(100).optional().describe('The tab rows go to (default "Sheet1")'),
|
|
2770
|
+
headers: import_zod.z.array(import_zod.z.string().max(200)).max(50).describe("The header row, one per column")
|
|
2801
2771
|
},
|
|
2802
2772
|
withErrorHandling(
|
|
2803
2773
|
async ({ connectionId, title, sheet, headers }) => jsonContent(await client.google.createSpreadsheet({ connectionId, title, sheet, headers }))
|
|
@@ -2807,8 +2777,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2807
2777
|
"list_sheet_tabs",
|
|
2808
2778
|
"List a spreadsheet's tabs (one created by the app or chosen with pick_spreadsheet), to choose the one a Sheets destination writes to.",
|
|
2809
2779
|
{
|
|
2810
|
-
connectionId:
|
|
2811
|
-
spreadsheetId:
|
|
2780
|
+
connectionId: import_zod.z.string().describe("The Google connection's id"),
|
|
2781
|
+
spreadsheetId: import_zod.z.string().describe("The spreadsheet's id")
|
|
2812
2782
|
},
|
|
2813
2783
|
withErrorHandling(
|
|
2814
2784
|
async ({ connectionId, spreadsheetId }) => jsonContent({ tabs: await client.google.tabs(connectionId, spreadsheetId) })
|
|
@@ -2818,7 +2788,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2818
2788
|
"disconnect_google",
|
|
2819
2789
|
"Disconnect a Google account (id from list_google_connections) and revoke its access at Google. Sheets destinations using it fail until another account is chosen.",
|
|
2820
2790
|
{
|
|
2821
|
-
connectionId:
|
|
2791
|
+
connectionId: import_zod.z.string().describe("The Google connection's id")
|
|
2822
2792
|
},
|
|
2823
2793
|
withErrorHandling(async ({ connectionId }) => {
|
|
2824
2794
|
await client.google.disconnect(connectionId);
|
|
@@ -2829,7 +2799,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2829
2799
|
"ai_setup_status",
|
|
2830
2800
|
"Whether AI setup is offered for an endpoint, and how many AI assists the plan has left in a rolling 30 days.",
|
|
2831
2801
|
{
|
|
2832
|
-
slug:
|
|
2802
|
+
slug: import_zod.z.string().describe("The endpoint's slug")
|
|
2833
2803
|
},
|
|
2834
2804
|
withErrorHandling(async ({ slug }) => jsonContent(await client.ai.status(slug)))
|
|
2835
2805
|
);
|
|
@@ -2837,25 +2807,25 @@ function registerTools(target, client, options = {}) {
|
|
|
2837
2807
|
"ai_setup",
|
|
2838
2808
|
"Have AI propose email fields (or a layout, or a repaired field) from 1 to 5 of an endpoint's captured emails. It sends those emails (subject, sender, recipients, text, headers; never attachments) to Anthropic's model, so the account's owner must approve it: this returns a link and a code to show them, and the page lists the emails. Then call wait_for_approval for the proposals, each with the value it found in every email. Nothing is saved: apply the fields with update_endpoint. Uses one AI assist.",
|
|
2839
2809
|
{
|
|
2840
|
-
slug:
|
|
2841
|
-
requestIds:
|
|
2842
|
-
ask:
|
|
2843
|
-
|
|
2844
|
-
kind:
|
|
2845
|
-
name:
|
|
2846
|
-
description:
|
|
2810
|
+
slug: import_zod.z.string().describe("The endpoint's slug"),
|
|
2811
|
+
requestIds: import_zod.z.array(import_zod.z.string()).min(1).max(5).describe("1 to 5 of the endpoint's email request ids, from list_emails"),
|
|
2812
|
+
ask: import_zod.z.union([
|
|
2813
|
+
import_zod.z.object({
|
|
2814
|
+
kind: import_zod.z.literal("field"),
|
|
2815
|
+
name: import_zod.z.string().describe("The field's name: letters, digits and _"),
|
|
2816
|
+
description: import_zod.z.string().describe('What the field is, such as "the order number"')
|
|
2847
2817
|
}),
|
|
2848
|
-
|
|
2849
|
-
kind:
|
|
2850
|
-
description:
|
|
2818
|
+
import_zod.z.object({
|
|
2819
|
+
kind: import_zod.z.literal("layout"),
|
|
2820
|
+
description: import_zod.z.string().optional().describe("What these emails are")
|
|
2851
2821
|
}),
|
|
2852
|
-
|
|
2853
|
-
kind:
|
|
2854
|
-
field:
|
|
2855
|
-
name:
|
|
2856
|
-
from:
|
|
2857
|
-
header:
|
|
2858
|
-
pattern:
|
|
2822
|
+
import_zod.z.object({
|
|
2823
|
+
kind: import_zod.z.literal("repair"),
|
|
2824
|
+
field: import_zod.z.object({
|
|
2825
|
+
name: import_zod.z.string(),
|
|
2826
|
+
from: import_zod.z.string(),
|
|
2827
|
+
header: import_zod.z.string().optional(),
|
|
2828
|
+
pattern: import_zod.z.string()
|
|
2859
2829
|
}).describe("The field to repair, as the endpoint has it")
|
|
2860
2830
|
})
|
|
2861
2831
|
]).describe("What to propose")
|
|
@@ -2875,7 +2845,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2875
2845
|
"stop_share",
|
|
2876
2846
|
"Stop a public preview or live tunnel by its id (from list_shares). The CLI running it is told why and exits.",
|
|
2877
2847
|
{
|
|
2878
|
-
id:
|
|
2848
|
+
id: import_zod.z.string().describe("The share's id, from list_shares")
|
|
2879
2849
|
},
|
|
2880
2850
|
withErrorHandling(async ({ id }) => {
|
|
2881
2851
|
const result = await client.shares.stop(id);
|
|
@@ -2891,7 +2861,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2891
2861
|
"share_requests",
|
|
2892
2862
|
"The last requests visitors made to a running preview (from list_shares), newest first: method, path, status, time, size, country, device and a number per visitor, never an address. Use it to see whether the person opened the link and what failed for them. `available` is false when the log could not be read just now.",
|
|
2893
2863
|
{
|
|
2894
|
-
id:
|
|
2864
|
+
id: import_zod.z.string().describe("The share's id, from list_shares")
|
|
2895
2865
|
},
|
|
2896
2866
|
withErrorHandling(async ({ id }) => {
|
|
2897
2867
|
return jsonContent(await client.shares.requests(id));
|
|
@@ -2909,8 +2879,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2909
2879
|
"reserve_name",
|
|
2910
2880
|
`Reserve a preview name, so ${shareWith} can use the link https://<name>.sharehooks.cc (Pro and Business). Lowercase letters, digits and dashes; names that look like random ones or end in -free are refused. With team, the name belongs to that team and every member may share under it.`,
|
|
2911
2881
|
{
|
|
2912
|
-
name:
|
|
2913
|
-
team:
|
|
2882
|
+
name: import_zod.z.string().min(1).max(63).describe("The name: lowercase letters, digits and dashes"),
|
|
2883
|
+
team: import_zod.z.string().optional().describe("A team's id or name (from list_teams), to keep the name inside that team")
|
|
2914
2884
|
},
|
|
2915
2885
|
withErrorHandling(async ({ name, team }) => {
|
|
2916
2886
|
return jsonContent(await client.names.reserve(name, team ? { team } : {}));
|
|
@@ -2920,7 +2890,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2920
2890
|
"release_name",
|
|
2921
2891
|
"Release a reserved preview name you kept (or, for a team name, as the team's owner). A preview running under it stops, and the link stops working.",
|
|
2922
2892
|
{
|
|
2923
|
-
name:
|
|
2893
|
+
name: import_zod.z.string().min(1).max(63).describe("The name, from list_names")
|
|
2924
2894
|
},
|
|
2925
2895
|
withErrorHandling(async ({ name }) => {
|
|
2926
2896
|
await client.names.release(name);
|
|
@@ -2931,8 +2901,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2931
2901
|
"set_name_password",
|
|
2932
2902
|
"Put a password on every preview under a reserved name (Pro and Business): visitors must type it before the page opens. The password is not shown back; give it to the person yourself.",
|
|
2933
2903
|
{
|
|
2934
|
-
name:
|
|
2935
|
-
password:
|
|
2904
|
+
name: import_zod.z.string().min(1).max(63).describe("The name, from list_names"),
|
|
2905
|
+
password: import_zod.z.string().min(8).max(200).describe("What visitors must type, 8 to 200 characters")
|
|
2936
2906
|
},
|
|
2937
2907
|
withErrorHandling(async ({ name, password }) => {
|
|
2938
2908
|
await client.names.setPassword(name, password);
|
|
@@ -2943,7 +2913,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2943
2913
|
"clear_name_password",
|
|
2944
2914
|
"Take the password off a reserved name, so its previews open without one.",
|
|
2945
2915
|
{
|
|
2946
|
-
name:
|
|
2916
|
+
name: import_zod.z.string().min(1).max(63).describe("The name, from list_names")
|
|
2947
2917
|
},
|
|
2948
2918
|
withErrorHandling(async ({ name }) => {
|
|
2949
2919
|
await client.names.clearPassword(name);
|
|
@@ -2962,17 +2932,439 @@ function registerTools(target, client, options = {}) {
|
|
|
2962
2932
|
return handles;
|
|
2963
2933
|
}
|
|
2964
2934
|
|
|
2965
|
-
// src/
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2935
|
+
// src/local-tools.ts
|
|
2936
|
+
function asToolServer(server) {
|
|
2937
|
+
return {
|
|
2938
|
+
tool(name, description, shape, handler, annotations) {
|
|
2939
|
+
const registered = server.registerTool(
|
|
2940
|
+
name,
|
|
2941
|
+
{
|
|
2942
|
+
title: annotations?.title,
|
|
2943
|
+
description,
|
|
2944
|
+
inputSchema: import_zod2.z.object(shape),
|
|
2945
|
+
annotations
|
|
2946
|
+
},
|
|
2947
|
+
async (args2) => handler(args2)
|
|
2948
|
+
);
|
|
2949
|
+
return { remove: () => registered.remove() };
|
|
2950
|
+
}
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
var warned = /* @__PURE__ */ new Set();
|
|
2954
|
+
function toolSelection(options = {}) {
|
|
2955
|
+
const fromOption = options.tools !== void 0;
|
|
2956
|
+
const parsed = parseToolGroups(fromOption ? options.tools : process.env.WHK_MCP_TOOLS);
|
|
2957
|
+
const setting = fromOption ? options.tools : process.env.WHK_MCP_TOOLS;
|
|
2958
|
+
if (parsed && parsed.unknown.length > 0 && !warned.has(setting ?? "")) {
|
|
2959
|
+
warned.add(setting ?? "");
|
|
2960
|
+
console.error(
|
|
2961
|
+
`webhooks-cc-mcp: unknown tool groups in ${fromOption ? "tools" : "WHK_MCP_TOOLS"}: ${parsed.unknown.join(", ")} (known: all, ${TOOL_GROUP_NAMES.join(", ")})`
|
|
2962
|
+
);
|
|
2963
|
+
}
|
|
2964
|
+
const readOnly = options.readOnly ?? /^(1|true|yes|on)$/i.test((process.env.WHK_MCP_READ_ONLY ?? "").trim());
|
|
2965
|
+
return { groups: parsed?.groups ?? null, readOnly };
|
|
2966
|
+
}
|
|
2967
|
+
var EXTRA_GROUPS = TOOL_GROUP_NAMES.filter((group2) => group2 !== "core");
|
|
2968
|
+
var ENABLE_TOOLS_ANNOTATIONS = {
|
|
2969
|
+
title: toolTitle(ENABLE_TOOLS),
|
|
2970
|
+
readOnlyHint: false,
|
|
2971
|
+
destructiveHint: false,
|
|
2972
|
+
idempotentHint: true,
|
|
2973
|
+
openWorldHint: false
|
|
2974
|
+
};
|
|
2975
|
+
var LocalToolGroups = class {
|
|
2976
|
+
constructor(server, selection) {
|
|
2977
|
+
this.server = server;
|
|
2978
|
+
this.selection = selection;
|
|
2979
|
+
this.client = null;
|
|
2980
|
+
this.loaded = /* @__PURE__ */ new Set();
|
|
2981
|
+
this.handles = [];
|
|
2982
|
+
this.enableTools = null;
|
|
2983
|
+
this.clientKnown = false;
|
|
2984
|
+
}
|
|
2985
|
+
/** The groups an account's tools start with. */
|
|
2986
|
+
startingGroups() {
|
|
2987
|
+
if (this.selection.groups) return [...this.selection.groups];
|
|
2988
|
+
return this.clientKnown && clientLoadsEverything(this.clientName) ? [...TOOL_GROUP_NAMES] : ["core"];
|
|
2989
|
+
}
|
|
2990
|
+
/** Registers the tools for an account; the handle removes them all again. */
|
|
2991
|
+
show(client) {
|
|
2992
|
+
this.hide();
|
|
2993
|
+
this.client = client;
|
|
2994
|
+
this.load(this.startingGroups());
|
|
2995
|
+
return { remove: () => this.hide() };
|
|
2996
|
+
}
|
|
2997
|
+
/** Removes every account tool, enable_tools included. */
|
|
2998
|
+
hide() {
|
|
2999
|
+
for (const handle of this.handles) handle.remove();
|
|
3000
|
+
this.enableTools?.remove();
|
|
3001
|
+
this.handles = [];
|
|
3002
|
+
this.enableTools = null;
|
|
3003
|
+
this.loaded.clear();
|
|
3004
|
+
this.client = null;
|
|
3005
|
+
}
|
|
3006
|
+
/** The groups loaded now. */
|
|
3007
|
+
loadedGroups() {
|
|
3008
|
+
return TOOL_GROUP_NAMES.filter((group2) => this.loaded.has(group2));
|
|
3009
|
+
}
|
|
3010
|
+
/**
|
|
3011
|
+
* The client named itself (initialize, or a 2026-07-28 request's _meta).
|
|
3012
|
+
* Without a WHK_MCP_TOOLS setting, a client that takes long tool lists
|
|
3013
|
+
* gets every group. Only the first name counts.
|
|
3014
|
+
*/
|
|
3015
|
+
clientIdentified(name) {
|
|
3016
|
+
if (this.clientKnown) return;
|
|
3017
|
+
this.clientKnown = true;
|
|
3018
|
+
this.clientName = name;
|
|
3019
|
+
if (this.selection.groups || !clientLoadsEverything(name)) return;
|
|
3020
|
+
if (this.client) this.load(TOOL_GROUP_NAMES);
|
|
3021
|
+
}
|
|
3022
|
+
/** Registers the groups not loaded yet; returns the tools each one added. */
|
|
3023
|
+
load(groups) {
|
|
3024
|
+
const client = this.client;
|
|
3025
|
+
const fresh = groups.filter((group2) => !this.loaded.has(group2));
|
|
3026
|
+
if (!client || fresh.length === 0) return {};
|
|
3027
|
+
const added = {};
|
|
3028
|
+
for (const group2 of fresh) added[group2] = [];
|
|
3029
|
+
const server = this.server;
|
|
3030
|
+
const recorder = {
|
|
3031
|
+
tool(name, description, shape, handler, annotations) {
|
|
3032
|
+
added[TOOL_CATALOG[name].group]?.push(name);
|
|
3033
|
+
return server.tool(name, description, shape, handler, annotations);
|
|
3034
|
+
}
|
|
3035
|
+
};
|
|
3036
|
+
this.handles.push(
|
|
3037
|
+
...registerTools(recorder, client, {
|
|
3038
|
+
groups: new Set(fresh),
|
|
3039
|
+
readOnly: this.selection.readOnly
|
|
3040
|
+
})
|
|
3041
|
+
);
|
|
3042
|
+
for (const group2 of fresh) this.loaded.add(group2);
|
|
3043
|
+
this.showEnableTools();
|
|
3044
|
+
return added;
|
|
3045
|
+
}
|
|
3046
|
+
/** (Re)registers enable_tools with the current groups, or drops it once all are loaded. */
|
|
3047
|
+
showEnableTools() {
|
|
3048
|
+
this.enableTools?.remove();
|
|
3049
|
+
this.enableTools = null;
|
|
3050
|
+
const left = EXTRA_GROUPS.filter((group2) => !this.loaded.has(group2));
|
|
3051
|
+
if (!this.client || left.length === 0) return;
|
|
3052
|
+
const lines = EXTRA_GROUPS.map(
|
|
3053
|
+
(group2) => `- ${group2}${this.loaded.has(group2) ? " (loaded)" : ""}: ${TOOL_GROUPS[group2]}`
|
|
3054
|
+
);
|
|
3055
|
+
const description = [
|
|
3056
|
+
"Load more webhooks.cc tools into this session. This server lists its core tools by default; the rest come in groups. If a tool you need is missing, load its group, then look at the tool list again.",
|
|
3057
|
+
"Groups:",
|
|
3058
|
+
...lines,
|
|
3059
|
+
this.selection.readOnly ? "This server is read-only (WHK_MCP_READ_ONLY): a group brings only its tools that change nothing." : null
|
|
3060
|
+
].filter(Boolean).join("\n");
|
|
3061
|
+
this.enableTools = this.server.tool(
|
|
3062
|
+
ENABLE_TOOLS,
|
|
3063
|
+
description,
|
|
3064
|
+
{
|
|
3065
|
+
groups: import_zod2.z.array(import_zod2.z.enum([...EXTRA_GROUPS, "all"])).min(1).describe('Groups to load, or ["all"]')
|
|
3066
|
+
},
|
|
3067
|
+
withErrorHandling(async ({ groups }) => {
|
|
3068
|
+
const wanted = groups.includes("all") ? [...TOOL_GROUP_NAMES] : groups;
|
|
3069
|
+
const already = wanted.filter((group2) => group2 !== "core" && this.loaded.has(group2));
|
|
3070
|
+
const added = this.load(wanted);
|
|
3071
|
+
const loadedNow = Object.keys(added).length > 0;
|
|
3072
|
+
return jsonContent({
|
|
3073
|
+
added,
|
|
3074
|
+
...already.length > 0 ? { alreadyLoaded: [...new Set(already)] } : {},
|
|
3075
|
+
loaded: this.loadedGroups(),
|
|
3076
|
+
...this.selection.readOnly ? { readOnly: true } : {},
|
|
3077
|
+
note: loadedNow ? "The tool list changed. If your client does not show the new tools, ask the user to set WHK_MCP_TOOLS (for example all) and restart the server." : "Nothing new to load."
|
|
3078
|
+
});
|
|
3079
|
+
}),
|
|
3080
|
+
ENABLE_TOOLS_ANNOTATIONS
|
|
3081
|
+
);
|
|
3082
|
+
}
|
|
3083
|
+
};
|
|
3084
|
+
function modernClientName(message) {
|
|
3085
|
+
if (typeof message !== "object" || message === null) return null;
|
|
3086
|
+
const params = message.params;
|
|
3087
|
+
const info = params?._meta?.[import_server.CLIENT_INFO_META_KEY];
|
|
3088
|
+
if (typeof info !== "object" || info === null) return null;
|
|
3089
|
+
const name = info.name;
|
|
3090
|
+
return typeof name === "string" ? name : void 0;
|
|
3091
|
+
}
|
|
3092
|
+
function watchClient(server, groups) {
|
|
3093
|
+
const low = server.server;
|
|
3094
|
+
const initialized = low.oninitialized;
|
|
3095
|
+
low.oninitialized = () => {
|
|
3096
|
+
initialized?.();
|
|
3097
|
+
groups.clientIdentified(low.getClientVersion()?.name);
|
|
3098
|
+
};
|
|
3099
|
+
const connect = server.connect.bind(server);
|
|
3100
|
+
server.connect = async (transport) => {
|
|
3101
|
+
const previous = transport.onmessage;
|
|
3102
|
+
transport.onmessage = (message, extra) => {
|
|
3103
|
+
const name = modernClientName(message);
|
|
3104
|
+
if (name !== null) groups.clientIdentified(name);
|
|
3105
|
+
previous?.call(transport, message, extra);
|
|
3106
|
+
};
|
|
3107
|
+
return connect(transport);
|
|
3108
|
+
};
|
|
3109
|
+
}
|
|
3110
|
+
function localToolGroups(server, selection) {
|
|
3111
|
+
const groups = new LocalToolGroups(asToolServer(server), selection);
|
|
3112
|
+
watchClient(server, groups);
|
|
3113
|
+
return groups;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
// src/prompts.ts
|
|
3117
|
+
var import_zod3 = require("zod");
|
|
3118
|
+
function registerPrompts(server) {
|
|
3119
|
+
server.registerPrompt(
|
|
3120
|
+
"debug_webhook_delivery",
|
|
3121
|
+
{
|
|
3122
|
+
title: "Debug Webhook Delivery",
|
|
3123
|
+
description: "Guide an agent through diagnosing why webhook delivery is failing or missing.",
|
|
3124
|
+
argsSchema: import_zod3.z.object({
|
|
3125
|
+
provider: import_zod3.z.string().optional().describe("Webhook provider, if known"),
|
|
3126
|
+
endpointSlug: import_zod3.z.string().optional().describe("Hosted endpoint slug, if known"),
|
|
3127
|
+
targetUrl: import_zod3.z.string().optional().describe("Your app's receiving URL, if known")
|
|
3128
|
+
})
|
|
3129
|
+
},
|
|
3130
|
+
async ({ provider, endpointSlug, targetUrl }) => {
|
|
3131
|
+
const scope = [
|
|
3132
|
+
provider ? `Provider: ${provider}.` : null,
|
|
3133
|
+
endpointSlug ? `Endpoint slug: ${endpointSlug}.` : null,
|
|
3134
|
+
targetUrl ? `Target URL: ${targetUrl}.` : null
|
|
3135
|
+
].filter(Boolean).join(" ");
|
|
3136
|
+
return {
|
|
3137
|
+
description: "Diagnose a missing or broken webhook delivery.",
|
|
3138
|
+
messages: [
|
|
3139
|
+
{
|
|
3140
|
+
role: "user",
|
|
3141
|
+
content: {
|
|
3142
|
+
type: "text",
|
|
3143
|
+
text: [
|
|
3144
|
+
scope,
|
|
3145
|
+
"Diagnose webhook delivery step by step.",
|
|
3146
|
+
"Use list_endpoints or get_endpoint to confirm the endpoint and URL.",
|
|
3147
|
+
"Use list_requests, wait_for_request, or wait_for_requests to check whether anything arrived.",
|
|
3148
|
+
"If the provider is known, use preview_webhook or send_webhook to reproduce the webhook with realistic signing.",
|
|
3149
|
+
"Use verify_signature when a secret is available.",
|
|
3150
|
+
"Use compare_requests to compare retries or changed payloads.",
|
|
3151
|
+
"Conclude with the most likely cause and the next concrete fix."
|
|
3152
|
+
].filter(Boolean).join(" ")
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
]
|
|
3156
|
+
};
|
|
3157
|
+
}
|
|
3158
|
+
);
|
|
3159
|
+
server.registerPrompt(
|
|
3160
|
+
"setup_provider_testing",
|
|
3161
|
+
{
|
|
3162
|
+
title: "Setup Provider Testing",
|
|
3163
|
+
description: "Guide an agent through setting up webhook testing for a provider.",
|
|
3164
|
+
argsSchema: import_zod3.z.object({
|
|
3165
|
+
provider: import_zod3.z.string().describe("Webhook provider to test"),
|
|
3166
|
+
targetUrl: import_zod3.z.string().optional().describe("Optional local or remote target URL")
|
|
3167
|
+
})
|
|
3168
|
+
},
|
|
3169
|
+
async ({ provider, targetUrl }) => {
|
|
3170
|
+
return {
|
|
3171
|
+
description: `Set up webhook testing for ${provider}.`,
|
|
3172
|
+
messages: [
|
|
3173
|
+
{
|
|
3174
|
+
role: "user",
|
|
3175
|
+
content: {
|
|
3176
|
+
type: "text",
|
|
3177
|
+
text: [
|
|
3178
|
+
`Set up webhook testing for ${provider}.`,
|
|
3179
|
+
"Use list_provider_templates to inspect supported templates and signing details first.",
|
|
3180
|
+
"Create an endpoint with create_endpoint, preferably ephemeral.",
|
|
3181
|
+
"If a target URL is provided, use preview_webhook before send_to so the request shape is visible.",
|
|
3182
|
+
targetUrl ? `Target URL: ${targetUrl}.` : null,
|
|
3183
|
+
"Send a realistic provider webhook, wait for capture, and verify the signature if a secret is available.",
|
|
3184
|
+
"Once real deliveries are captured, use generate_payload_schema to write the payload types for the handler.",
|
|
3185
|
+
"Return the endpoint URL, the exact tools used, and the next step for the developer."
|
|
3186
|
+
].filter(Boolean).join(" ")
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
]
|
|
3190
|
+
};
|
|
3191
|
+
}
|
|
3192
|
+
);
|
|
3193
|
+
server.registerPrompt(
|
|
3194
|
+
"compare_webhook_attempts",
|
|
3195
|
+
{
|
|
3196
|
+
title: "Compare Webhook Attempts",
|
|
3197
|
+
description: "Guide an agent through comparing two webhook deliveries or retries.",
|
|
3198
|
+
argsSchema: import_zod3.z.object({
|
|
3199
|
+
endpointSlug: import_zod3.z.string().optional().describe("Endpoint slug to inspect for recent attempts"),
|
|
3200
|
+
leftRequestId: import_zod3.z.string().optional().describe("First request ID, if already known"),
|
|
3201
|
+
rightRequestId: import_zod3.z.string().optional().describe("Second request ID, if already known")
|
|
3202
|
+
})
|
|
3203
|
+
},
|
|
3204
|
+
async ({ endpointSlug, leftRequestId, rightRequestId }) => {
|
|
3205
|
+
return {
|
|
3206
|
+
description: "Compare two webhook deliveries and explain the difference.",
|
|
3207
|
+
messages: [
|
|
3208
|
+
{
|
|
3209
|
+
role: "user",
|
|
3210
|
+
content: {
|
|
3211
|
+
type: "text",
|
|
3212
|
+
text: [
|
|
3213
|
+
endpointSlug ? `Endpoint slug: ${endpointSlug}.` : null,
|
|
3214
|
+
leftRequestId && rightRequestId ? `Compare request ${leftRequestId} against ${rightRequestId}.` : "Find the most relevant two webhook attempts first.",
|
|
3215
|
+
"Use compare_requests for the structured diff.",
|
|
3216
|
+
"If request IDs are not provided, use list_requests or the endpoint recent resource to find the last two attempts.",
|
|
3217
|
+
"Explain what changed in the body, headers, or timing, and whether the difference looks expected, retried, or broken."
|
|
3218
|
+
].filter(Boolean).join(" ")
|
|
3219
|
+
}
|
|
3220
|
+
}
|
|
3221
|
+
]
|
|
3222
|
+
};
|
|
3223
|
+
}
|
|
3224
|
+
);
|
|
3225
|
+
}
|
|
3226
|
+
|
|
3227
|
+
// src/resources.ts
|
|
3228
|
+
var import_server2 = require("@modelcontextprotocol/server");
|
|
3229
|
+
var ENDPOINTS_RESOURCE_URI = "webhooks://endpoints";
|
|
3230
|
+
var ENDPOINT_RECENT_TEMPLATE_URI = "webhooks://endpoint/{slug}/recent";
|
|
3231
|
+
var REQUEST_TEMPLATE_URI = "webhooks://request/{id}";
|
|
3232
|
+
var MAX_ENDPOINT_RESOURCE_ITEMS = 25;
|
|
3233
|
+
var RECENT_REQUEST_LIMIT = 10;
|
|
3234
|
+
function jsonResource(uri, value) {
|
|
3235
|
+
return {
|
|
3236
|
+
contents: [
|
|
3237
|
+
{
|
|
3238
|
+
uri,
|
|
3239
|
+
mimeType: "application/json",
|
|
3240
|
+
text: JSON.stringify(value, null, 2)
|
|
3241
|
+
}
|
|
3242
|
+
]
|
|
3243
|
+
};
|
|
3244
|
+
}
|
|
3245
|
+
function summarizeRequest(request) {
|
|
3246
|
+
return {
|
|
3247
|
+
id: request.id,
|
|
3248
|
+
method: request.method,
|
|
3249
|
+
path: request.path,
|
|
3250
|
+
receivedAt: request.receivedAt,
|
|
3251
|
+
contentType: request.contentType ?? null
|
|
3252
|
+
};
|
|
3253
|
+
}
|
|
3254
|
+
function variableToString(value, name) {
|
|
3255
|
+
if (typeof value === "string" && value.length > 0) {
|
|
3256
|
+
return value;
|
|
3257
|
+
}
|
|
3258
|
+
throw new Error(`Missing resource variable "${name}"`);
|
|
3259
|
+
}
|
|
3260
|
+
async function listEndpointSummaries(client) {
|
|
3261
|
+
const endpoints = (await client.endpoints.list()).slice().sort((left, right) => right.createdAt - left.createdAt);
|
|
3262
|
+
const truncated = endpoints.length > MAX_ENDPOINT_RESOURCE_ITEMS;
|
|
3263
|
+
const visible = endpoints.slice(0, MAX_ENDPOINT_RESOURCE_ITEMS);
|
|
3264
|
+
const summaries = await Promise.all(
|
|
3265
|
+
visible.map(async (endpoint) => {
|
|
3266
|
+
const recent = await client.requests.list(endpoint.slug, { limit: 1 });
|
|
3267
|
+
return {
|
|
3268
|
+
...endpoint,
|
|
3269
|
+
lastRequest: recent[0] ? summarizeRequest(recent[0]) : null
|
|
3270
|
+
};
|
|
3271
|
+
})
|
|
3272
|
+
);
|
|
3273
|
+
return {
|
|
3274
|
+
endpoints: summaries,
|
|
3275
|
+
total: endpoints.length,
|
|
3276
|
+
truncated
|
|
3277
|
+
};
|
|
3278
|
+
}
|
|
3279
|
+
function registerResources(server, client) {
|
|
3280
|
+
server.registerResource(
|
|
3281
|
+
"endpoints-overview",
|
|
3282
|
+
ENDPOINTS_RESOURCE_URI,
|
|
3283
|
+
{
|
|
3284
|
+
title: "Endpoints Overview",
|
|
3285
|
+
description: "All endpoints with a summary of recent activity.",
|
|
3286
|
+
mimeType: "application/json"
|
|
3287
|
+
},
|
|
3288
|
+
async () => {
|
|
3289
|
+
return jsonResource(ENDPOINTS_RESOURCE_URI, await listEndpointSummaries(client));
|
|
3290
|
+
}
|
|
3291
|
+
);
|
|
3292
|
+
server.registerResource(
|
|
3293
|
+
"endpoint-recent-requests",
|
|
3294
|
+
new import_server2.ResourceTemplate(ENDPOINT_RECENT_TEMPLATE_URI, {
|
|
3295
|
+
list: async () => {
|
|
3296
|
+
const endpoints = await client.endpoints.list();
|
|
3297
|
+
return {
|
|
3298
|
+
resources: endpoints.map((endpoint) => ({
|
|
3299
|
+
uri: `webhooks://endpoint/${endpoint.slug}/recent`,
|
|
3300
|
+
name: `${endpoint.slug} recent requests`,
|
|
3301
|
+
description: `Recent requests for endpoint ${endpoint.slug}`,
|
|
3302
|
+
mimeType: "application/json"
|
|
3303
|
+
}))
|
|
3304
|
+
};
|
|
3305
|
+
},
|
|
3306
|
+
complete: {
|
|
3307
|
+
slug: async (value) => {
|
|
3308
|
+
const endpoints = await client.endpoints.list();
|
|
3309
|
+
return endpoints.map((endpoint) => endpoint.slug).filter((slug) => slug.startsWith(value)).slice(0, 20);
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
}),
|
|
3313
|
+
{
|
|
3314
|
+
title: "Endpoint Recent Requests",
|
|
3315
|
+
description: "Last 10 captured requests for an endpoint.",
|
|
3316
|
+
mimeType: "application/json"
|
|
3317
|
+
},
|
|
3318
|
+
async (uri, variables) => {
|
|
3319
|
+
const slug = variableToString(variables.slug, "slug");
|
|
3320
|
+
const [endpoint, requests] = await Promise.all([
|
|
3321
|
+
client.endpoints.get(slug),
|
|
3322
|
+
client.requests.list(slug, { limit: RECENT_REQUEST_LIMIT })
|
|
3323
|
+
]);
|
|
3324
|
+
return jsonResource(uri.toString(), {
|
|
3325
|
+
endpoint,
|
|
3326
|
+
requests: requests.map(compactRequest)
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
);
|
|
3330
|
+
server.registerResource(
|
|
3331
|
+
"request-details",
|
|
3332
|
+
new import_server2.ResourceTemplate(REQUEST_TEMPLATE_URI, {
|
|
3333
|
+
list: void 0,
|
|
3334
|
+
complete: {}
|
|
3335
|
+
}),
|
|
3336
|
+
{
|
|
3337
|
+
title: "Request Details",
|
|
3338
|
+
description: "Full details for a captured request by ID.",
|
|
3339
|
+
mimeType: "application/json"
|
|
3340
|
+
},
|
|
3341
|
+
async (uri, variables) => {
|
|
3342
|
+
const id = variableToString(variables.id, "id");
|
|
3343
|
+
const request = await client.requests.get(id);
|
|
3344
|
+
return jsonResource(uri.toString(), compactRequest(request));
|
|
3345
|
+
}
|
|
3346
|
+
);
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
// src/sandbox-tools.ts
|
|
3350
|
+
var import_zod4 = require("zod");
|
|
3351
|
+
var import_sdk2 = require("@webhooks-cc/sdk");
|
|
3352
|
+
|
|
3353
|
+
// src/version.ts
|
|
3354
|
+
var VERSION = true ? "2.0.0" : "0.0.0-dev";
|
|
3355
|
+
var MCP_USER_AGENT = `webhooks-cc-mcp/${VERSION}`;
|
|
3356
|
+
|
|
3357
|
+
// src/sandbox-tools.ts
|
|
3358
|
+
var SANDBOX = {
|
|
3359
|
+
maxEndpoints: 3,
|
|
3360
|
+
requestsPerEndpoint: 25,
|
|
3361
|
+
requests: 100,
|
|
3362
|
+
lifetimeHours: 24
|
|
3363
|
+
};
|
|
3364
|
+
var DEFAULT_WAIT_SECONDS = 45;
|
|
3365
|
+
var MAX_WAIT_SECONDS = 600;
|
|
3366
|
+
var DOCS_URL = "https://webhooks.cc/docs/agents";
|
|
3367
|
+
var ACCOUNT_ONLY_OPTIONS = [
|
|
2976
3368
|
"mockResponse",
|
|
2977
3369
|
"responseRules",
|
|
2978
3370
|
"notificationUrl",
|
|
@@ -3076,6 +3468,8 @@ function registerSandboxTools(server, options = {}) {
|
|
|
3076
3468
|
options.clientName ?? "webhooks-cc-mcp",
|
|
3077
3469
|
options.credentialsFile
|
|
3078
3470
|
);
|
|
3471
|
+
const target = asToolServer(server);
|
|
3472
|
+
const groups = options.toolGroups ?? localToolGroups(server, toolSelection(options));
|
|
3079
3473
|
let handles = [];
|
|
3080
3474
|
let shown = 0;
|
|
3081
3475
|
const swap = (register) => {
|
|
@@ -3083,7 +3477,9 @@ function registerSandboxTools(server, options = {}) {
|
|
|
3083
3477
|
for (const handle of handles) handle.remove();
|
|
3084
3478
|
handles = register(id);
|
|
3085
3479
|
};
|
|
3086
|
-
const showSandbox = () => swap(
|
|
3480
|
+
const showSandbox = () => swap(
|
|
3481
|
+
() => sandboxToolSet(sandboxTarget(target, groups.selection.readOnly), session, showAccount, groups)
|
|
3482
|
+
);
|
|
3087
3483
|
const savedAccount = (id) => options.credentialsFile === false ? null : import_sdk2.WebhooksCC.fromSavedConnection({
|
|
3088
3484
|
baseUrl,
|
|
3089
3485
|
webhookUrl,
|
|
@@ -3096,18 +3492,54 @@ function registerSandboxTools(server, options = {}) {
|
|
|
3096
3492
|
})
|
|
3097
3493
|
});
|
|
3098
3494
|
function showAccount(client) {
|
|
3099
|
-
swap((id) =>
|
|
3495
|
+
swap((id) => [groups.show(savedAccount(id) ?? client)]);
|
|
3100
3496
|
options.onConnected?.(client);
|
|
3101
3497
|
}
|
|
3102
3498
|
const saved = savedAccount(1);
|
|
3103
3499
|
if (saved) {
|
|
3104
|
-
swap(() =>
|
|
3500
|
+
swap(() => [groups.show(saved)]);
|
|
3105
3501
|
options.onConnected?.(saved);
|
|
3106
3502
|
} else {
|
|
3107
3503
|
showSandbox();
|
|
3108
3504
|
}
|
|
3109
3505
|
}
|
|
3110
|
-
|
|
3506
|
+
var SANDBOX_ONLY_TOOLS = {
|
|
3507
|
+
// A new code each time.
|
|
3508
|
+
connect_account: {
|
|
3509
|
+
readOnlyHint: false,
|
|
3510
|
+
destructiveHint: false,
|
|
3511
|
+
idempotentHint: false,
|
|
3512
|
+
openWorldHint: false
|
|
3513
|
+
},
|
|
3514
|
+
// Changes this session's tools once the human has connected.
|
|
3515
|
+
wait_for_connection: {
|
|
3516
|
+
readOnlyHint: false,
|
|
3517
|
+
destructiveHint: false,
|
|
3518
|
+
idempotentHint: true,
|
|
3519
|
+
openWorldHint: false
|
|
3520
|
+
},
|
|
3521
|
+
about_sandbox: {
|
|
3522
|
+
readOnlyHint: true,
|
|
3523
|
+
destructiveHint: false,
|
|
3524
|
+
idempotentHint: true,
|
|
3525
|
+
openWorldHint: false
|
|
3526
|
+
}
|
|
3527
|
+
};
|
|
3528
|
+
var CONNECT_TOOLS = /* @__PURE__ */ new Set(["connect_account", "wait_for_connection"]);
|
|
3529
|
+
function sandboxTarget(target, readOnly) {
|
|
3530
|
+
return {
|
|
3531
|
+
tool(name, description, shape, handler) {
|
|
3532
|
+
const own = SANDBOX_ONLY_TOOLS[name];
|
|
3533
|
+
const annotations = own ? { title: toolTitle(name), ...own } : toolAnnotations(name);
|
|
3534
|
+
if (readOnly && !annotations.readOnlyHint && !CONNECT_TOOLS.has(name)) {
|
|
3535
|
+
return { remove() {
|
|
3536
|
+
} };
|
|
3537
|
+
}
|
|
3538
|
+
return target.tool(name, description, shape, handler, annotations);
|
|
3539
|
+
}
|
|
3540
|
+
};
|
|
3541
|
+
}
|
|
3542
|
+
function sandboxToolSet(server, session, showAccount, groups) {
|
|
3111
3543
|
const tools = [];
|
|
3112
3544
|
const limits = `up to ${SANDBOX.maxEndpoints} endpoints at a time, ${SANDBOX.requestsPerEndpoint} captured requests each and ${SANDBOX.requests} in all, deleted ${SANDBOX.lifetimeHours} hours after the sandbox was created`;
|
|
3113
3545
|
const file = savedTo(session.credentialsFile);
|
|
@@ -3222,7 +3654,7 @@ function sandboxToolSet(server, session, showAccount) {
|
|
|
3222
3654
|
"connect_account",
|
|
3223
3655
|
"Connect this agent to a human's webhooks.cc account, for the full tool set (mock responses, signatures, email, teams) and to keep the sandbox endpoints. Returns a link and a 6-digit code: show both to the human, who signs in or signs up with that email and enters the code. Then call wait_for_connection. Calling again gives a new code.",
|
|
3224
3656
|
{
|
|
3225
|
-
email:
|
|
3657
|
+
email: import_zod4.z.string().email({ message: "Invalid email address" }).describe(
|
|
3226
3658
|
"The email the human signs in to webhooks.cc with (their GitHub or Google primary email if they sign in that way); only someone signed in as it can connect"
|
|
3227
3659
|
)
|
|
3228
3660
|
},
|
|
@@ -3275,7 +3707,7 @@ function sandboxToolSet(server, session, showAccount) {
|
|
|
3275
3707
|
"wait_for_connection",
|
|
3276
3708
|
`Wait for the human to enter the code from connect_account. On success the full webhooks.cc tools for their account replace these sandbox tools. Returns after timeoutSeconds (default ${DEFAULT_WAIT_SECONDS}) if they have not finished yet; call it again to keep waiting.`,
|
|
3277
3709
|
{
|
|
3278
|
-
timeoutSeconds:
|
|
3710
|
+
timeoutSeconds: import_zod4.z.number().int().min(1).max(MAX_WAIT_SECONDS).optional().describe(`How long to wait, in seconds (default ${DEFAULT_WAIT_SECONDS})`)
|
|
3279
3711
|
},
|
|
3280
3712
|
withErrorHandling(async ({ timeoutSeconds }) => {
|
|
3281
3713
|
const pending = session.connect;
|
|
@@ -3318,7 +3750,7 @@ function sandboxToolSet(server, session, showAccount) {
|
|
|
3318
3750
|
return jsonContent({
|
|
3319
3751
|
connected: true,
|
|
3320
3752
|
email: pending.email,
|
|
3321
|
-
message: `Connected to the webhooks.cc account of ${pending.email}. The
|
|
3753
|
+
message: `Connected to the webhooks.cc account of ${pending.email}. The account's tools have replaced the sandbox tools (the tool list changed; if your client does not pick that up, ask the user to restart it).${groups.loadedGroups().length < TOOL_GROUP_NAMES.length ? " More tools load in groups: see enable_tools." : ""}${movedEndpoints ? " The sandbox endpoints are now that account's endpoints, with what they captured." : ""} ${file ? `The connection is saved in ${file}, so later sessions, the whk CLI and the SDKs start connected; it lasts 90 days, and the human can remove it under Account, Connected agents.` : "This connection lasts while this MCP server runs; for a lasting setup, the human can create an API key under Account and set WHK_API_KEY."}`
|
|
3322
3754
|
});
|
|
3323
3755
|
})
|
|
3324
3756
|
)
|
|
@@ -3357,22 +3789,34 @@ function sandboxToolSet(server, session, showAccount) {
|
|
|
3357
3789
|
}
|
|
3358
3790
|
|
|
3359
3791
|
// src/index.ts
|
|
3792
|
+
var INSTRUCTIONS = "webhooks.cc captures webhooks and inbound email for testing. Start with create_endpoint, send to its URL (or point a provider at it), then wait_for_request and get_request to inspect what arrived. send_webhook sends a signed test webhook to an endpoint; verify_signature checks a captured request. With an account the tools come in groups: when a tool you need is missing, enable_tools loads its group (teams, domains, Google Sheets, billing and keys, and more).";
|
|
3360
3793
|
function createServer(options = {}) {
|
|
3361
3794
|
const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
|
|
3362
3795
|
const webhookUrl = options.webhookUrl ?? process.env.WHK_WEBHOOK_URL;
|
|
3363
3796
|
const baseUrl = options.baseUrl ?? process.env.WHK_BASE_URL;
|
|
3364
|
-
const server = new
|
|
3365
|
-
{ name: "webhooks-cc", version: VERSION },
|
|
3366
|
-
|
|
3367
|
-
|
|
3797
|
+
const server = new import_server3.McpServer(
|
|
3798
|
+
{ name: "webhooks-cc", title: "webhooks.cc", version: VERSION },
|
|
3799
|
+
{
|
|
3800
|
+
capabilities: { tools: { listChanged: true } },
|
|
3801
|
+
instructions: INSTRUCTIONS,
|
|
3802
|
+
// Connecting an account or loading groups changes many tools at once:
|
|
3803
|
+
// one list_changed, not dozens.
|
|
3804
|
+
debouncedNotificationMethods: ["notifications/tools/list_changed"]
|
|
3805
|
+
}
|
|
3368
3806
|
);
|
|
3807
|
+
const groups = localToolGroups(server, toolSelection(options));
|
|
3369
3808
|
if (!apiKey) {
|
|
3370
|
-
registerSandboxTools(server, {
|
|
3809
|
+
registerSandboxTools(server, {
|
|
3810
|
+
baseUrl,
|
|
3811
|
+
webhookUrl,
|
|
3812
|
+
credentialsFile: options.credentialsFile,
|
|
3813
|
+
toolGroups: groups
|
|
3814
|
+
});
|
|
3371
3815
|
registerPrompts(server);
|
|
3372
3816
|
return server;
|
|
3373
3817
|
}
|
|
3374
3818
|
const client = new import_sdk3.WebhooksCC({ apiKey, webhookUrl, baseUrl, userAgent: MCP_USER_AGENT });
|
|
3375
|
-
|
|
3819
|
+
groups.show(client);
|
|
3376
3820
|
registerPrompts(server);
|
|
3377
3821
|
registerResources(server, client);
|
|
3378
3822
|
return server;
|
|
@@ -3658,7 +4102,7 @@ if (command === "setup") {
|
|
|
3658
4102
|
runSetup(args.slice(1));
|
|
3659
4103
|
} else if (command === "--help" || command === "-h") {
|
|
3660
4104
|
console.log(`
|
|
3661
|
-
@webhooks-cc/mcp
|
|
4105
|
+
@webhooks-cc/mcp: MCP server for webhooks.cc
|
|
3662
4106
|
|
|
3663
4107
|
Usage:
|
|
3664
4108
|
npx @webhooks-cc/mcp Start stdio MCP server
|
|
@@ -3673,6 +4117,11 @@ Environment:
|
|
|
3673
4117
|
~/.config/whk/agent.json, shared with the whk CLI and SDKs)
|
|
3674
4118
|
WHK_WEBHOOK_URL Custom webhook receiver URL (optional)
|
|
3675
4119
|
WHK_BASE_URL Custom API base URL (optional)
|
|
4120
|
+
WHK_MCP_TOOLS Tool groups to list: all, or core plus any of forwarding,
|
|
4121
|
+
layouts, extras, teams, account, domains, sheets, names
|
|
4122
|
+
(default: core, and all for Claude Code, Codex and
|
|
4123
|
+
ChatGPT; enable_tools loads more during a session)
|
|
4124
|
+
WHK_MCP_READ_ONLY 1 to list only the tools that change nothing
|
|
3676
4125
|
|
|
3677
4126
|
Setup:
|
|
3678
4127
|
npx @webhooks-cc/mcp setup claude-code
|
|
@@ -3692,10 +4141,8 @@ Get your API key at https://webhooks.cc/account/api-keys
|
|
|
3692
4141
|
console.error(`Unknown command: "${command}". Run with --help for usage.`);
|
|
3693
4142
|
process.exit(1);
|
|
3694
4143
|
} else {
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
console.error("Fatal:", error instanceof Error ? error.message : error);
|
|
3699
|
-
process.exit(1);
|
|
4144
|
+
(0, import_stdio.serveStdio)(() => createServer(), {
|
|
4145
|
+
// Out-of-band reports only, never what was sent; stdout is the protocol.
|
|
4146
|
+
onerror: (error) => console.error(`webhooks-cc-mcp: ${error.message}`)
|
|
3700
4147
|
});
|
|
3701
4148
|
}
|