@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/index.js
CHANGED
|
@@ -20,131 +20,240 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
ENABLE_TOOLS: () => ENABLE_TOOLS,
|
|
24
|
+
LocalToolGroups: () => LocalToolGroups,
|
|
23
25
|
REMOTE_MAX_WAIT_MS: () => REMOTE_MAX_WAIT_MS,
|
|
26
|
+
TOOL_CATALOG: () => TOOL_CATALOG,
|
|
27
|
+
TOOL_GROUPS: () => TOOL_GROUPS,
|
|
28
|
+
TOOL_GROUP_NAMES: () => TOOL_GROUP_NAMES,
|
|
29
|
+
asToolServer: () => asToolServer,
|
|
30
|
+
clientLoadsEverything: () => clientLoadsEverything,
|
|
24
31
|
createServer: () => createServer,
|
|
32
|
+
defaultToolGroups: () => defaultToolGroups,
|
|
33
|
+
localToolGroups: () => localToolGroups,
|
|
34
|
+
parseToolGroups: () => parseToolGroups,
|
|
25
35
|
registerAgentRegistrationTools: () => registerAgentRegistrationTools,
|
|
26
36
|
registerPrompts: () => registerPrompts,
|
|
27
37
|
registerResources: () => registerResources,
|
|
28
38
|
registerSandboxTools: () => registerSandboxTools,
|
|
29
|
-
registerTools: () => registerTools
|
|
39
|
+
registerTools: () => registerTools,
|
|
40
|
+
toolAnnotations: () => toolAnnotations,
|
|
41
|
+
toolSelection: () => toolSelection
|
|
30
42
|
});
|
|
31
43
|
module.exports = __toCommonJS(src_exports);
|
|
32
|
-
var
|
|
44
|
+
var import_server3 = require("@modelcontextprotocol/server");
|
|
33
45
|
var import_sdk3 = require("@webhooks-cc/sdk");
|
|
34
46
|
|
|
35
|
-
// src/
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
47
|
+
// src/local-tools.ts
|
|
48
|
+
var import_server = require("@modelcontextprotocol/server");
|
|
49
|
+
var import_zod2 = require("zod");
|
|
50
|
+
|
|
51
|
+
// src/catalog.ts
|
|
52
|
+
var TOOL_GROUPS = {
|
|
53
|
+
core: "Endpoints, captured requests and emails, sending test webhooks, signatures, forwarding destinations, deliveries, previews of your local app and their logs, usage and approvals",
|
|
54
|
+
forwarding: "The rest of forwarding: preview what a destination would send, the endpoint's forwarding settings and signing secret, test deliveries",
|
|
55
|
+
layouts: "Email layouts and named fields: try layouts and fields on a captured email, add ready-made layouts, and AI setup",
|
|
56
|
+
extras: "Bulk endpoint changes, favourites, request counts and payload schemas",
|
|
57
|
+
teams: "Teams: members, invites, sharing endpoints with a team, and pricing team changes",
|
|
58
|
+
account: "The account: billing, API keys, connected agents, public share links to requests, and the display name",
|
|
59
|
+
domains: "Custom inbound email domains: add, verify, route and remove them",
|
|
60
|
+
sheets: "Google Sheets destinations: Google accounts, spreadsheets and their tabs",
|
|
61
|
+
names: "Preview names: reserve, release and password-protect them"
|
|
62
|
+
};
|
|
63
|
+
var TOOL_GROUP_NAMES = Object.keys(TOOL_GROUPS);
|
|
64
|
+
var read = { readOnly: true };
|
|
65
|
+
var write = {};
|
|
66
|
+
var update = { idempotent: true };
|
|
67
|
+
var remove = { destructive: true, idempotent: true };
|
|
68
|
+
var replace = { destructive: true, idempotent: true };
|
|
69
|
+
var outward = { openWorld: true };
|
|
70
|
+
function group(name, tools) {
|
|
71
|
+
return Object.fromEntries(
|
|
72
|
+
Object.entries(tools).map(([tool, behaviour]) => [tool, { group: name, ...behaviour }])
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
var TOOL_CATALOG = {
|
|
76
|
+
...group("core", {
|
|
77
|
+
create_endpoint: write,
|
|
78
|
+
list_endpoints: read,
|
|
79
|
+
get_endpoint: read,
|
|
80
|
+
// Replaces what it is given (a mock response, the signing secret).
|
|
81
|
+
update_endpoint: replace,
|
|
82
|
+
delete_endpoint: remove,
|
|
83
|
+
list_requests: read,
|
|
84
|
+
search_requests: read,
|
|
85
|
+
get_request: read,
|
|
86
|
+
wait_for_request: read,
|
|
87
|
+
wait_for_requests: read,
|
|
88
|
+
replay_request: outward,
|
|
89
|
+
compare_requests: read,
|
|
90
|
+
extract_from_request: read,
|
|
91
|
+
clear_requests: remove,
|
|
92
|
+
send_webhook: write,
|
|
93
|
+
send_to: outward,
|
|
94
|
+
test_webhook_flow: outward,
|
|
95
|
+
preview_webhook: read,
|
|
96
|
+
preview_response: read,
|
|
97
|
+
list_provider_templates: read,
|
|
98
|
+
verify_signature: read,
|
|
99
|
+
list_emails: read,
|
|
100
|
+
get_email: read,
|
|
101
|
+
wait_for_email: read,
|
|
102
|
+
send_test_email: write,
|
|
103
|
+
render_email: outward,
|
|
104
|
+
list_destinations: read,
|
|
105
|
+
add_destination: write,
|
|
106
|
+
update_destination: replace,
|
|
107
|
+
remove_destination: remove,
|
|
108
|
+
list_deliveries: read,
|
|
109
|
+
redeliver: outward,
|
|
110
|
+
share_port: outward,
|
|
111
|
+
list_shares: read,
|
|
112
|
+
share_requests: read,
|
|
113
|
+
stop_share: remove,
|
|
114
|
+
get_usage: read,
|
|
115
|
+
// Collecting an approved key hands it over once: not a pure read.
|
|
116
|
+
wait_for_approval: write,
|
|
117
|
+
describe: read
|
|
118
|
+
}),
|
|
119
|
+
...group("forwarding", {
|
|
120
|
+
preview_destination: read,
|
|
121
|
+
configure_forwarding: replace,
|
|
122
|
+
// rotate: true replaces the secret, and receivers holding the old one fail.
|
|
123
|
+
get_forwarding_secret: { destructive: true },
|
|
124
|
+
test_forwarding: outward
|
|
125
|
+
}),
|
|
126
|
+
...group("layouts", {
|
|
127
|
+
preview_layouts: read,
|
|
128
|
+
list_layout_templates: read,
|
|
129
|
+
add_layout_template: write,
|
|
130
|
+
preview_email_fields: read,
|
|
131
|
+
ai_setup_status: read,
|
|
132
|
+
// Sends the chosen emails to Anthropic once the owner approves.
|
|
133
|
+
ai_setup: outward
|
|
134
|
+
}),
|
|
135
|
+
...group("extras", {
|
|
136
|
+
create_endpoints: write,
|
|
137
|
+
delete_endpoints: remove,
|
|
138
|
+
favorite_endpoint: update,
|
|
139
|
+
count_requests: read,
|
|
140
|
+
generate_payload_schema: read
|
|
141
|
+
}),
|
|
142
|
+
...group("teams", {
|
|
143
|
+
list_teams: read,
|
|
144
|
+
list_team_members: read,
|
|
145
|
+
share_endpoint: update,
|
|
146
|
+
unshare_endpoint: remove,
|
|
147
|
+
create_team: write,
|
|
148
|
+
rename_team: update,
|
|
149
|
+
delete_team: remove,
|
|
150
|
+
remove_team_member: remove,
|
|
151
|
+
leave_team: remove,
|
|
152
|
+
invite_to_team: outward,
|
|
153
|
+
list_invites: read,
|
|
154
|
+
answer_invite: write,
|
|
155
|
+
withdraw_invite: remove,
|
|
156
|
+
quote_team_change: read
|
|
157
|
+
}),
|
|
158
|
+
...group("account", {
|
|
159
|
+
get_billing: read,
|
|
160
|
+
list_api_keys: read,
|
|
161
|
+
create_api_key: write,
|
|
162
|
+
revoke_api_key: remove,
|
|
163
|
+
rename_account: update,
|
|
164
|
+
list_connected_agents: read,
|
|
165
|
+
disconnect_agent: remove,
|
|
166
|
+
list_share_links: read,
|
|
167
|
+
create_share_link: write,
|
|
168
|
+
revoke_share_link: remove
|
|
169
|
+
}),
|
|
170
|
+
...group("domains", {
|
|
171
|
+
list_domains: read,
|
|
172
|
+
add_domain: write,
|
|
173
|
+
// A failed check counts toward lapsing a verified domain: not safe to repeat.
|
|
174
|
+
verify_domain: { destructive: true },
|
|
175
|
+
// Replaces the routing; a missing catch-all refuses mail.
|
|
176
|
+
route_domain: replace,
|
|
177
|
+
remove_domain: remove
|
|
178
|
+
}),
|
|
179
|
+
...group("sheets", {
|
|
180
|
+
list_google_connections: read,
|
|
181
|
+
connect_google: outward,
|
|
182
|
+
pick_spreadsheet: outward,
|
|
183
|
+
create_spreadsheet: outward,
|
|
184
|
+
list_sheet_tabs: { readOnly: true, openWorld: true },
|
|
185
|
+
disconnect_google: remove
|
|
186
|
+
}),
|
|
187
|
+
...group("names", {
|
|
188
|
+
// The log of what visitors asked a running preview for.
|
|
189
|
+
list_names: read,
|
|
190
|
+
reserve_name: write,
|
|
191
|
+
release_name: remove,
|
|
192
|
+
set_name_password: update,
|
|
193
|
+
// Opens the name to anyone again.
|
|
194
|
+
clear_name_password: { destructive: true, idempotent: true }
|
|
195
|
+
})
|
|
196
|
+
};
|
|
197
|
+
var ENABLE_TOOLS = "enable_tools";
|
|
198
|
+
function toolAnnotations(name) {
|
|
199
|
+
const entry = TOOL_CATALOG[name];
|
|
200
|
+
const readOnly = entry?.readOnly === true;
|
|
201
|
+
return {
|
|
202
|
+
title: toolTitle(name),
|
|
203
|
+
readOnlyHint: readOnly,
|
|
204
|
+
// MCP reads a missing destructiveHint as true on any tool that writes.
|
|
205
|
+
destructiveHint: !readOnly && entry?.destructive === true,
|
|
206
|
+
idempotentHint: readOnly || entry?.idempotent === true,
|
|
207
|
+
openWorldHint: entry?.openWorld === true
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function toolTitle(name) {
|
|
211
|
+
const words = name.split("_").map((word) => TITLE_WORDS[word] ?? word);
|
|
212
|
+
const first = words[0] ?? "";
|
|
213
|
+
return [first.charAt(0).toUpperCase() + first.slice(1), ...words.slice(1)].join(" ");
|
|
214
|
+
}
|
|
215
|
+
var TITLE_WORDS = {
|
|
216
|
+
api: "API",
|
|
217
|
+
ai: "AI",
|
|
218
|
+
mcp: "MCP"
|
|
219
|
+
};
|
|
220
|
+
function parseToolGroups(setting) {
|
|
221
|
+
if (setting === null || setting === void 0 || setting.trim() === "") return null;
|
|
222
|
+
const groups = /* @__PURE__ */ new Set(["core"]);
|
|
223
|
+
const unknown = [];
|
|
224
|
+
for (const raw of setting.split(/[\s,+]+/)) {
|
|
225
|
+
const name = raw.trim().toLowerCase();
|
|
226
|
+
if (!name) continue;
|
|
227
|
+
if (name === "all") {
|
|
228
|
+
for (const g of TOOL_GROUP_NAMES) groups.add(g);
|
|
229
|
+
} else if (TOOL_GROUP_NAMES.includes(name)) {
|
|
230
|
+
groups.add(name);
|
|
231
|
+
} else {
|
|
232
|
+
unknown.push(raw.trim());
|
|
142
233
|
}
|
|
143
|
-
|
|
234
|
+
}
|
|
235
|
+
return { groups, unknown };
|
|
236
|
+
}
|
|
237
|
+
var LOADS_EVERYTHING = [
|
|
238
|
+
/^claude-code$/i,
|
|
239
|
+
/^claude-ai$/i,
|
|
240
|
+
/^claude$/i,
|
|
241
|
+
/codex/i,
|
|
242
|
+
/^openai-mcp$/i,
|
|
243
|
+
/chatgpt/i
|
|
244
|
+
];
|
|
245
|
+
function clientLoadsEverything(clientName) {
|
|
246
|
+
if (!clientName) return false;
|
|
247
|
+
const name = clientName.trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
248
|
+
return LOADS_EVERYTHING.some((pattern) => pattern.test(name));
|
|
249
|
+
}
|
|
250
|
+
function defaultToolGroups(clientName) {
|
|
251
|
+
return clientLoadsEverything(clientName) ? new Set(TOOL_GROUP_NAMES) : /* @__PURE__ */ new Set(["core"]);
|
|
144
252
|
}
|
|
145
253
|
|
|
146
|
-
// src/
|
|
147
|
-
var
|
|
254
|
+
// src/tools.ts
|
|
255
|
+
var import_zod = require("zod");
|
|
256
|
+
var import_sdk = require("@webhooks-cc/sdk");
|
|
148
257
|
|
|
149
258
|
// src/compact.ts
|
|
150
259
|
var MAX_OUTPUT = 32768;
|
|
@@ -354,142 +463,11 @@ function compactRequest(request) {
|
|
|
354
463
|
return compact;
|
|
355
464
|
}
|
|
356
465
|
|
|
357
|
-
// src/resources.ts
|
|
358
|
-
var ENDPOINTS_RESOURCE_URI = "webhooks://endpoints";
|
|
359
|
-
var ENDPOINT_RECENT_TEMPLATE_URI = "webhooks://endpoint/{slug}/recent";
|
|
360
|
-
var REQUEST_TEMPLATE_URI = "webhooks://request/{id}";
|
|
361
|
-
var MAX_ENDPOINT_RESOURCE_ITEMS = 25;
|
|
362
|
-
var RECENT_REQUEST_LIMIT = 10;
|
|
363
|
-
function jsonResource(uri, value) {
|
|
364
|
-
return {
|
|
365
|
-
contents: [
|
|
366
|
-
{
|
|
367
|
-
uri,
|
|
368
|
-
mimeType: "application/json",
|
|
369
|
-
text: JSON.stringify(value, null, 2)
|
|
370
|
-
}
|
|
371
|
-
]
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
function summarizeRequest(request) {
|
|
375
|
-
return {
|
|
376
|
-
id: request.id,
|
|
377
|
-
method: request.method,
|
|
378
|
-
path: request.path,
|
|
379
|
-
receivedAt: request.receivedAt,
|
|
380
|
-
contentType: request.contentType ?? null
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
function variableToString(value, name) {
|
|
384
|
-
if (typeof value === "string" && value.length > 0) {
|
|
385
|
-
return value;
|
|
386
|
-
}
|
|
387
|
-
throw new Error(`Missing resource variable "${name}"`);
|
|
388
|
-
}
|
|
389
|
-
async function listEndpointSummaries(client) {
|
|
390
|
-
const endpoints = (await client.endpoints.list()).slice().sort((left, right) => right.createdAt - left.createdAt);
|
|
391
|
-
const truncated = endpoints.length > MAX_ENDPOINT_RESOURCE_ITEMS;
|
|
392
|
-
const visible = endpoints.slice(0, MAX_ENDPOINT_RESOURCE_ITEMS);
|
|
393
|
-
const summaries = await Promise.all(
|
|
394
|
-
visible.map(async (endpoint) => {
|
|
395
|
-
const recent = await client.requests.list(endpoint.slug, { limit: 1 });
|
|
396
|
-
return {
|
|
397
|
-
...endpoint,
|
|
398
|
-
lastRequest: recent[0] ? summarizeRequest(recent[0]) : null
|
|
399
|
-
};
|
|
400
|
-
})
|
|
401
|
-
);
|
|
402
|
-
return {
|
|
403
|
-
endpoints: summaries,
|
|
404
|
-
total: endpoints.length,
|
|
405
|
-
truncated
|
|
406
|
-
};
|
|
407
|
-
}
|
|
408
|
-
function registerResources(server, client) {
|
|
409
|
-
server.registerResource(
|
|
410
|
-
"endpoints-overview",
|
|
411
|
-
ENDPOINTS_RESOURCE_URI,
|
|
412
|
-
{
|
|
413
|
-
title: "Endpoints Overview",
|
|
414
|
-
description: "All endpoints with a summary of recent activity.",
|
|
415
|
-
mimeType: "application/json"
|
|
416
|
-
},
|
|
417
|
-
async () => {
|
|
418
|
-
return jsonResource(ENDPOINTS_RESOURCE_URI, await listEndpointSummaries(client));
|
|
419
|
-
}
|
|
420
|
-
);
|
|
421
|
-
server.registerResource(
|
|
422
|
-
"endpoint-recent-requests",
|
|
423
|
-
new import_mcp.ResourceTemplate(ENDPOINT_RECENT_TEMPLATE_URI, {
|
|
424
|
-
list: async () => {
|
|
425
|
-
const endpoints = await client.endpoints.list();
|
|
426
|
-
return {
|
|
427
|
-
resources: endpoints.map((endpoint) => ({
|
|
428
|
-
uri: `webhooks://endpoint/${endpoint.slug}/recent`,
|
|
429
|
-
name: `${endpoint.slug} recent requests`,
|
|
430
|
-
description: `Recent requests for endpoint ${endpoint.slug}`,
|
|
431
|
-
mimeType: "application/json"
|
|
432
|
-
}))
|
|
433
|
-
};
|
|
434
|
-
},
|
|
435
|
-
complete: {
|
|
436
|
-
slug: async (value) => {
|
|
437
|
-
const endpoints = await client.endpoints.list();
|
|
438
|
-
return endpoints.map((endpoint) => endpoint.slug).filter((slug) => slug.startsWith(value)).slice(0, 20);
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}),
|
|
442
|
-
{
|
|
443
|
-
title: "Endpoint Recent Requests",
|
|
444
|
-
description: "Last 10 captured requests for an endpoint.",
|
|
445
|
-
mimeType: "application/json"
|
|
446
|
-
},
|
|
447
|
-
async (uri, variables) => {
|
|
448
|
-
const slug = variableToString(variables.slug, "slug");
|
|
449
|
-
const [endpoint, requests] = await Promise.all([
|
|
450
|
-
client.endpoints.get(slug),
|
|
451
|
-
client.requests.list(slug, { limit: RECENT_REQUEST_LIMIT })
|
|
452
|
-
]);
|
|
453
|
-
return jsonResource(uri.toString(), {
|
|
454
|
-
endpoint,
|
|
455
|
-
requests: requests.map(compactRequest)
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
);
|
|
459
|
-
server.registerResource(
|
|
460
|
-
"request-details",
|
|
461
|
-
new import_mcp.ResourceTemplate(REQUEST_TEMPLATE_URI, {
|
|
462
|
-
list: void 0,
|
|
463
|
-
complete: {}
|
|
464
|
-
}),
|
|
465
|
-
{
|
|
466
|
-
title: "Request Details",
|
|
467
|
-
description: "Full details for a captured request by ID.",
|
|
468
|
-
mimeType: "application/json"
|
|
469
|
-
},
|
|
470
|
-
async (uri, variables) => {
|
|
471
|
-
const id = variableToString(variables.id, "id");
|
|
472
|
-
const request = await client.requests.get(id);
|
|
473
|
-
return jsonResource(uri.toString(), compactRequest(request));
|
|
474
|
-
}
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// src/sandbox-tools.ts
|
|
479
|
-
var import_zod3 = require("zod");
|
|
480
|
-
var import_sdk2 = require("@webhooks-cc/sdk");
|
|
481
|
-
|
|
482
|
-
// src/version.ts
|
|
483
|
-
var VERSION = true ? "1.24.0" : "0.0.0-dev";
|
|
484
|
-
var MCP_USER_AGENT = `webhooks-cc-mcp/${VERSION}`;
|
|
485
|
-
|
|
486
466
|
// src/tools.ts
|
|
487
|
-
var import_zod2 = require("zod");
|
|
488
|
-
var import_sdk = require("@webhooks-cc/sdk");
|
|
489
467
|
var MAX_BODY_SIZE = MAX_OUTPUT;
|
|
490
468
|
var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
491
469
|
var TIME_SEPARATOR = " \u2014 ";
|
|
492
|
-
var httpUrlSchema =
|
|
470
|
+
var httpUrlSchema = import_zod.z.string().url().refine(
|
|
493
471
|
(value) => {
|
|
494
472
|
try {
|
|
495
473
|
const protocol = new URL(value).protocol;
|
|
@@ -500,24 +478,24 @@ var httpUrlSchema = import_zod2.z.string().url().refine(
|
|
|
500
478
|
},
|
|
501
479
|
{ message: "Only http and https URLs are supported" }
|
|
502
480
|
);
|
|
503
|
-
var methodSchema =
|
|
504
|
-
var kindSchema =
|
|
481
|
+
var methodSchema = import_zod.z.enum(HTTP_METHODS).default("POST").describe("HTTP method (default: POST)");
|
|
482
|
+
var kindSchema = import_zod.z.enum(["http", "email"]).optional().describe('Only HTTP requests ("http") or only captured emails ("email")');
|
|
505
483
|
var EMAIL_SCAN_LIMIT = 100;
|
|
506
|
-
var durationOrTimestampSchema =
|
|
484
|
+
var durationOrTimestampSchema = import_zod.z.union([import_zod.z.string(), import_zod.z.number()]);
|
|
507
485
|
var CONDITION_FIELD_NAMES = Object.keys(import_sdk.CONDITION_FIELDS);
|
|
508
486
|
var CONDITION_OP_NAMES = import_sdk.CONDITION_OPS;
|
|
509
487
|
function conditionSchema(use) {
|
|
510
488
|
const fields = use === "response" ? CONDITION_FIELD_NAMES.filter((field) => import_sdk.CONDITION_FIELDS[field].response) : CONDITION_FIELD_NAMES;
|
|
511
|
-
return
|
|
512
|
-
field:
|
|
489
|
+
return import_zod.z.object({
|
|
490
|
+
field: import_zod.z.enum(fields).describe(
|
|
513
491
|
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)"
|
|
514
492
|
),
|
|
515
|
-
op:
|
|
493
|
+
op: import_zod.z.enum(CONDITION_OP_NAMES).describe(
|
|
516
494
|
"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)"
|
|
517
495
|
),
|
|
518
|
-
value:
|
|
519
|
-
name:
|
|
520
|
-
path:
|
|
496
|
+
value: import_zod.z.string().max(import_sdk.MAX_CONDITION_VALUE_LEN).optional().describe(`What to compare with (not for exists and not_exists)`),
|
|
497
|
+
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"),
|
|
498
|
+
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"]')
|
|
521
499
|
}).superRefine((condition, ctx) => {
|
|
522
500
|
for (const issue of (0, import_sdk.conditionIssues)(condition, { use })) {
|
|
523
501
|
ctx.addIssue({ code: "custom", message: issue });
|
|
@@ -525,46 +503,46 @@ function conditionSchema(use) {
|
|
|
525
503
|
});
|
|
526
504
|
}
|
|
527
505
|
var ruleConditionSchema = conditionSchema("response");
|
|
528
|
-
var emailFieldSchema =
|
|
529
|
-
name:
|
|
530
|
-
from:
|
|
531
|
-
header:
|
|
532
|
-
pattern:
|
|
506
|
+
var emailFieldSchema = import_zod.z.object({
|
|
507
|
+
name: import_zod.z.string().describe("Letters, digits and _, starting with a letter: {{ fields.name }}"),
|
|
508
|
+
from: import_zod.z.enum(["subject", "text", "html", "from", "to", "header"]).describe("What it reads; html is the HTML as text"),
|
|
509
|
+
header: import_zod.z.string().optional().describe('The header name, for from "header"'),
|
|
510
|
+
pattern: import_zod.z.string().describe("RE2 regular expression; the first group is the value (no lookarounds)")
|
|
533
511
|
});
|
|
534
|
-
var layoutSchema =
|
|
535
|
-
id:
|
|
536
|
-
name:
|
|
537
|
-
enabled:
|
|
538
|
-
match:
|
|
539
|
-
logic:
|
|
540
|
-
conditions:
|
|
512
|
+
var layoutSchema = import_zod.z.object({
|
|
513
|
+
id: import_zod.z.string().describe("Stable id: 1 to 40 letters, digits, - or _"),
|
|
514
|
+
name: import_zod.z.string().max(100),
|
|
515
|
+
enabled: import_zod.z.boolean().optional().describe("Off: kept but never matched (default on)"),
|
|
516
|
+
match: import_zod.z.object({
|
|
517
|
+
logic: import_zod.z.enum(["and", "or"]).optional(),
|
|
518
|
+
conditions: import_zod.z.array(conditionSchema("filter")).max(10)
|
|
541
519
|
}).optional().describe("When it applies, e.g. the sender or subject; no conditions: every email"),
|
|
542
|
-
fields:
|
|
543
|
-
lineItems:
|
|
544
|
-
from:
|
|
545
|
-
pattern:
|
|
520
|
+
fields: import_zod.z.array(emailFieldSchema).max(20).optional().describe("Named values, as emailFields"),
|
|
521
|
+
lineItems: import_zod.z.object({
|
|
522
|
+
from: import_zod.z.enum(["text", "html", "attachment"]),
|
|
523
|
+
pattern: import_zod.z.string().optional().describe(
|
|
546
524
|
"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+)"
|
|
547
525
|
),
|
|
548
|
-
start:
|
|
549
|
-
end:
|
|
550
|
-
attachment:
|
|
551
|
-
sheet:
|
|
552
|
-
columns:
|
|
526
|
+
start: import_zod.z.string().optional().describe("text/html: search only after this (RE2)"),
|
|
527
|
+
end: import_zod.z.string().optional().describe("text/html: and before this (RE2)"),
|
|
528
|
+
attachment: import_zod.z.string().optional().describe("attachment: file name with * and ? (default: the first spreadsheet or CSV)"),
|
|
529
|
+
sheet: import_zod.z.string().optional().describe("attachment: sheet name (default: the first)"),
|
|
530
|
+
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")
|
|
553
531
|
}).nullable().optional().describe("Rows such as an order's products"),
|
|
554
|
-
parseAttachments:
|
|
532
|
+
parseAttachments: import_zod.z.boolean().optional().describe("Parse CSV, XLSX, ODS, PDF text and text files into the JSON; uses parsed pages")
|
|
555
533
|
});
|
|
556
534
|
var destinationFields = {
|
|
557
|
-
name:
|
|
558
|
-
enabled:
|
|
559
|
-
http:
|
|
560
|
-
email:
|
|
561
|
-
filter:
|
|
562
|
-
logic:
|
|
563
|
-
conditions:
|
|
535
|
+
name: import_zod.z.string().max(100).nullable().optional().describe("A name for the list and the log"),
|
|
536
|
+
enabled: import_zod.z.boolean().optional().describe("Send to it (default true)"),
|
|
537
|
+
http: import_zod.z.boolean().optional().describe("Takes HTTP requests (default true)"),
|
|
538
|
+
email: import_zod.z.boolean().optional().describe("Takes emails (default true)"),
|
|
539
|
+
filter: import_zod.z.object({
|
|
540
|
+
logic: import_zod.z.enum(["and", "or"]).optional().describe("and (default): every condition; or: any"),
|
|
541
|
+
conditions: import_zod.z.array(conditionSchema("filter")).max(10)
|
|
564
542
|
}).nullable().optional().describe(
|
|
565
543
|
"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)"
|
|
566
544
|
),
|
|
567
|
-
format:
|
|
545
|
+
format: import_zod.z.enum([
|
|
568
546
|
"auto",
|
|
569
547
|
"as_received",
|
|
570
548
|
"json",
|
|
@@ -577,103 +555,103 @@ var destinationFields = {
|
|
|
577
555
|
]).optional().describe(
|
|
578
556
|
"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"
|
|
579
557
|
),
|
|
580
|
-
mailgunSigningKey:
|
|
558
|
+
mailgunSigningKey: import_zod.z.string().max(200).nullable().optional().describe(
|
|
581
559
|
"For format mailgun: your Mailgun HTTP webhook signing key, so the form's signature verifies; null removes it. Write-only."
|
|
582
560
|
),
|
|
583
|
-
bodyTemplate:
|
|
561
|
+
bodyTemplate: import_zod.z.unknown().optional().describe(
|
|
584
562
|
`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)`
|
|
585
563
|
),
|
|
586
|
-
messageTemplate:
|
|
564
|
+
messageTemplate: import_zod.z.string().max(4e3).nullable().optional().describe(
|
|
587
565
|
"For format chat: the message text with {{ placeholders }}; null sends the standard message"
|
|
588
566
|
),
|
|
589
|
-
headers:
|
|
590
|
-
|
|
591
|
-
name:
|
|
592
|
-
value:
|
|
567
|
+
headers: import_zod.z.array(
|
|
568
|
+
import_zod.z.object({
|
|
569
|
+
name: import_zod.z.string().describe("Header name, e.g. Authorization"),
|
|
570
|
+
value: import_zod.z.string().nullable().describe("Value, or null to keep the stored one")
|
|
593
571
|
})
|
|
594
572
|
).max(10).optional().describe("Your own headers for every delivery (not chat). Replaces the whole list"),
|
|
595
|
-
appendPath:
|
|
596
|
-
retrySeconds:
|
|
597
|
-
keepOrder:
|
|
598
|
-
sentField:
|
|
599
|
-
throttleSeconds:
|
|
573
|
+
appendPath: import_zod.z.boolean().optional().describe("Append the captured path to the URL (as_received)"),
|
|
574
|
+
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"),
|
|
575
|
+
keepOrder: import_zod.z.boolean().optional().describe("One delivery at a time, in capture order"),
|
|
576
|
+
sentField: import_zod.z.string().max(128).nullable().optional().describe("JSON body field with the sender's own timestamp"),
|
|
577
|
+
throttleSeconds: import_zod.z.number().int().min(0).max(3600).optional().describe(
|
|
600
578
|
"At most one delivery every N seconds; the rest are not sent (1 makes a heads-up channel)"
|
|
601
579
|
),
|
|
602
|
-
delaySeconds:
|
|
580
|
+
delaySeconds: import_zod.z.number().int().min(0).max(3600).optional().describe("Wait this long after the capture before sending, like a callback")
|
|
603
581
|
};
|
|
604
|
-
var responseRuleSchema =
|
|
605
|
-
name:
|
|
606
|
-
enabled:
|
|
607
|
-
logic:
|
|
608
|
-
conditions:
|
|
609
|
-
response:
|
|
582
|
+
var responseRuleSchema = import_zod.z.object({
|
|
583
|
+
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)`),
|
|
584
|
+
enabled: import_zod.z.boolean().optional().default(true).describe("Whether this rule is active"),
|
|
585
|
+
logic: import_zod.z.enum(["and", "or"]).optional().default("and").describe('How to combine conditions: "and" (all match) or "or" (any match)'),
|
|
586
|
+
conditions: import_zod.z.array(ruleConditionSchema).min(1).max(10).describe("Conditions to evaluate (1-10)"),
|
|
587
|
+
response: import_zod.z.lazy(() => mockResponseSchema).describe("Response when conditions match")
|
|
610
588
|
});
|
|
611
|
-
var responseRulesSchema =
|
|
612
|
-
var mockResponseSchema =
|
|
613
|
-
status:
|
|
614
|
-
body:
|
|
589
|
+
var responseRulesSchema = import_zod.z.array(responseRuleSchema).max(50).describe("Ordered conditional response rules (first match wins, max 50)");
|
|
590
|
+
var mockResponseSchema = import_zod.z.object({
|
|
591
|
+
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})`),
|
|
592
|
+
body: import_zod.z.string().default("").describe(
|
|
615
593
|
'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'
|
|
616
594
|
),
|
|
617
|
-
headers:
|
|
618
|
-
delay:
|
|
595
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).default({}).describe("Response headers (default: none); values may use {{ placeholders }} too"),
|
|
596
|
+
delay: import_zod.z.number().int().min(import_sdk.MOCK_RESPONSE_DELAY_MIN).max(import_sdk.MOCK_RESPONSE_DELAY_MAX).optional().describe(
|
|
619
597
|
`Response delay in milliseconds (${import_sdk.MOCK_RESPONSE_DELAY_MIN}-${import_sdk.MOCK_RESPONSE_DELAY_MAX}, default: none)`
|
|
620
598
|
)
|
|
621
599
|
});
|
|
622
|
-
var endpointSiteSchema =
|
|
600
|
+
var endpointSiteSchema = import_zod.z.enum(["webhooks", "mailhooks", "both"]).describe(
|
|
623
601
|
"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."
|
|
624
602
|
);
|
|
625
603
|
var createEndpointShape = {
|
|
626
|
-
name:
|
|
627
|
-
ephemeral:
|
|
604
|
+
name: import_zod.z.string().optional().describe("Display name for the endpoint"),
|
|
605
|
+
ephemeral: import_zod.z.boolean().optional().describe("Create a temporary endpoint that auto-expires"),
|
|
628
606
|
expiresIn: durationOrTimestampSchema.optional().describe('Auto-expire after this duration, for example "12h"'),
|
|
629
607
|
mockResponse: mockResponseSchema.optional().describe("Optional mock response to return when the endpoint receives a request"),
|
|
630
608
|
responseRules: responseRulesSchema.optional().describe(
|
|
631
609
|
"Conditional response rules. Each rule has conditions and a response. First matching rule wins."
|
|
632
610
|
),
|
|
633
|
-
notificationUrl:
|
|
611
|
+
notificationUrl: import_zod.z.string().url().optional().describe(
|
|
634
612
|
"URL to POST a JSON summary to after each captured request (e.g. Slack/Discord webhook)"
|
|
635
613
|
),
|
|
636
614
|
site: endpointSiteSchema.optional(),
|
|
637
|
-
signingProvider:
|
|
615
|
+
signingProvider: import_zod.z.string().optional().describe(
|
|
638
616
|
"Signing provider for automatic signature verification (e.g. stripe, github, shopify)"
|
|
639
617
|
),
|
|
640
|
-
signingSecret:
|
|
618
|
+
signingSecret: import_zod.z.string().optional().describe(
|
|
641
619
|
"Signing secret (encrypted server-side, never returned). Required when signingProvider is set."
|
|
642
620
|
),
|
|
643
|
-
signingHeader:
|
|
621
|
+
signingHeader: import_zod.z.string().optional().describe("Custom signature header name. Only used with generic-hmac provider.")
|
|
644
622
|
};
|
|
645
623
|
var listEndpointsShape = {
|
|
646
|
-
team:
|
|
624
|
+
team: import_zod.z.string().min(1).optional().describe(
|
|
647
625
|
"Team id or name (case-insensitive). Keeps endpoints shared with you from that team and endpoints you own that are shared with it."
|
|
648
626
|
)
|
|
649
627
|
};
|
|
650
|
-
var getEndpointShape = { slug:
|
|
651
|
-
var deleteEndpointShape = { slug:
|
|
628
|
+
var getEndpointShape = { slug: import_zod.z.string().describe("The endpoint slug") };
|
|
629
|
+
var deleteEndpointShape = { slug: import_zod.z.string().describe("The endpoint slug to delete") };
|
|
652
630
|
var listRequestsShape = {
|
|
653
|
-
endpointSlug:
|
|
654
|
-
limit:
|
|
655
|
-
since:
|
|
631
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug"),
|
|
632
|
+
limit: import_zod.z.number().int().min(1).max(100).default(25).describe("Max requests to return"),
|
|
633
|
+
since: import_zod.z.number().optional().describe("Only return requests after this timestamp in ms"),
|
|
656
634
|
kind: kindSchema
|
|
657
635
|
};
|
|
658
|
-
var getRequestShape = { requestId:
|
|
636
|
+
var getRequestShape = { requestId: import_zod.z.string().describe("The request ID") };
|
|
659
637
|
var waitForRequestShape = {
|
|
660
|
-
endpointSlug:
|
|
638
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug to monitor"),
|
|
661
639
|
timeout: durationOrTimestampSchema.default("30s").describe('How long to wait, for example "30s"'),
|
|
662
640
|
pollInterval: durationOrTimestampSchema.optional().describe('Interval between polls, for example "500ms" or "1s"')
|
|
663
641
|
};
|
|
664
642
|
var payloadSchemaShape = {
|
|
665
|
-
endpointSlug:
|
|
666
|
-
format:
|
|
643
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug"),
|
|
644
|
+
format: import_zod.z.enum(import_sdk.PAYLOAD_SCHEMA_FORMATS).default("typescript").describe(
|
|
667
645
|
'Output: "typescript" interfaces, "zod" schemas, "pydantic" models, or "json-schema"'
|
|
668
646
|
),
|
|
669
|
-
name:
|
|
670
|
-
event:
|
|
647
|
+
name: import_zod.z.string().max(100).optional().describe("Root type name (default: from the detected event, else WebhookPayload)"),
|
|
648
|
+
event: import_zod.z.string().optional().describe(
|
|
671
649
|
'Only requests whose detected provider event matches, e.g. "invoice.paid" or "push". One event per schema gives the tightest types.'
|
|
672
650
|
),
|
|
673
|
-
method:
|
|
674
|
-
path:
|
|
675
|
-
requestIds:
|
|
676
|
-
limit:
|
|
651
|
+
method: import_zod.z.string().optional().describe("Only requests with this HTTP method"),
|
|
652
|
+
path: import_zod.z.string().optional().describe("Only requests with this path"),
|
|
653
|
+
requestIds: import_zod.z.array(import_zod.z.string()).min(1).max(50).optional().describe("Use exactly these requests instead of the latest ones"),
|
|
654
|
+
limit: import_zod.z.number().int().min(1).max(100).default(20).describe("How many of the latest requests to read before filtering")
|
|
677
655
|
};
|
|
678
656
|
function eventName(event) {
|
|
679
657
|
const words = event.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean);
|
|
@@ -1171,11 +1149,18 @@ function durationMs(value) {
|
|
|
1171
1149
|
return null;
|
|
1172
1150
|
}
|
|
1173
1151
|
}
|
|
1174
|
-
function toolTarget(target,
|
|
1152
|
+
function toolTarget(target, options, handles) {
|
|
1153
|
+
const remote = options.remote === true;
|
|
1175
1154
|
return {
|
|
1176
1155
|
tool(name, description, shape, handler) {
|
|
1177
1156
|
if (remote && LOCAL_ONLY_TOOLS.has(name)) return { remove() {
|
|
1178
1157
|
} };
|
|
1158
|
+
const entry = TOOL_CATALOG[name];
|
|
1159
|
+
if (!entry) throw new Error(`${name} has no entry in catalog.ts`);
|
|
1160
|
+
if (options.groups && !options.groups.has(entry.group)) return { remove() {
|
|
1161
|
+
} };
|
|
1162
|
+
if (options.readOnly && entry.readOnly !== true) return { remove() {
|
|
1163
|
+
} };
|
|
1179
1164
|
const wrapped = remote ? async (args) => {
|
|
1180
1165
|
const input = args;
|
|
1181
1166
|
if (TARGET_URL_TOOLS.has(name) && input.targetUrl !== void 0) {
|
|
@@ -1209,7 +1194,7 @@ function toolTarget(target, remote, handles) {
|
|
|
1209
1194
|
}
|
|
1210
1195
|
return handler(bounded);
|
|
1211
1196
|
} : handler;
|
|
1212
|
-
const handle = target.tool(name, description, shape, wrapped);
|
|
1197
|
+
const handle = target.tool(name, description, shape, wrapped, toolAnnotations(name));
|
|
1213
1198
|
handles.push(handle);
|
|
1214
1199
|
return handle;
|
|
1215
1200
|
}
|
|
@@ -1285,7 +1270,7 @@ function withShareHints(list, remote = false) {
|
|
|
1285
1270
|
function registerTools(target, client, options = {}) {
|
|
1286
1271
|
const handles = [];
|
|
1287
1272
|
const remote = options.remote === true;
|
|
1288
|
-
const server = toolTarget(target,
|
|
1273
|
+
const server = toolTarget(target, options, handles);
|
|
1289
1274
|
const shareWith = remote ? REMOTE_SHARE : "share_port";
|
|
1290
1275
|
server.tool(
|
|
1291
1276
|
"create_endpoint",
|
|
@@ -1368,7 +1353,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1368
1353
|
server.tool(
|
|
1369
1354
|
"list_team_members",
|
|
1370
1355
|
"List a team's members and pending invites. Any member of the team may call this.",
|
|
1371
|
-
{ teamId:
|
|
1356
|
+
{ teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)") },
|
|
1372
1357
|
withErrorHandling(async ({ teamId }) => {
|
|
1373
1358
|
const result = await client.teams.members(teamId);
|
|
1374
1359
|
return jsonContent(result);
|
|
@@ -1378,8 +1363,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1378
1363
|
"share_endpoint",
|
|
1379
1364
|
"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.",
|
|
1380
1365
|
{
|
|
1381
|
-
slug:
|
|
1382
|
-
teamId:
|
|
1366
|
+
slug: import_zod.z.string().describe("Slug of an endpoint you own"),
|
|
1367
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)")
|
|
1383
1368
|
},
|
|
1384
1369
|
withErrorHandling(async ({ slug, teamId }) => {
|
|
1385
1370
|
await client.teams.share(teamId, slug);
|
|
@@ -1390,8 +1375,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1390
1375
|
"unshare_endpoint",
|
|
1391
1376
|
"Stop sharing an endpoint you own with a team. Works even when the team is suspended.",
|
|
1392
1377
|
{
|
|
1393
|
-
slug:
|
|
1394
|
-
teamId:
|
|
1378
|
+
slug: import_zod.z.string().describe("Slug of an endpoint you own"),
|
|
1379
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)")
|
|
1395
1380
|
},
|
|
1396
1381
|
withErrorHandling(async ({ slug, teamId }) => {
|
|
1397
1382
|
await client.teams.unshare(teamId, slug);
|
|
@@ -1401,15 +1386,15 @@ function registerTools(target, client, options = {}) {
|
|
|
1401
1386
|
server.tool(
|
|
1402
1387
|
"create_team",
|
|
1403
1388
|
"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.",
|
|
1404
|
-
{ name:
|
|
1389
|
+
{ name: import_zod.z.string().trim().min(1).max(100).describe("The team's name") },
|
|
1405
1390
|
withErrorHandling(async ({ name }) => jsonContent(await client.teams.create(name)))
|
|
1406
1391
|
);
|
|
1407
1392
|
server.tool(
|
|
1408
1393
|
"rename_team",
|
|
1409
1394
|
"Rename a team you own.",
|
|
1410
1395
|
{
|
|
1411
|
-
teamId:
|
|
1412
|
-
name:
|
|
1396
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1397
|
+
name: import_zod.z.string().trim().min(1).max(100).describe("The new name")
|
|
1413
1398
|
},
|
|
1414
1399
|
withErrorHandling(async ({ teamId, name }) => {
|
|
1415
1400
|
await client.teams.rename(teamId, name);
|
|
@@ -1419,7 +1404,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1419
1404
|
server.tool(
|
|
1420
1405
|
"delete_team",
|
|
1421
1406
|
"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.",
|
|
1422
|
-
{ teamId:
|
|
1407
|
+
{ teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)") },
|
|
1423
1408
|
withErrorHandling(async ({ teamId }) => {
|
|
1424
1409
|
const pending = await client.teams.delete(teamId);
|
|
1425
1410
|
if (!pending.id) return textContent(`Deleted the team ${teamId}.`);
|
|
@@ -1430,8 +1415,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1430
1415
|
"remove_team_member",
|
|
1431
1416
|
"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.",
|
|
1432
1417
|
{
|
|
1433
|
-
teamId:
|
|
1434
|
-
userId:
|
|
1418
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1419
|
+
userId: import_zod.z.string().min(1).describe("The member's userId (from list_team_members)")
|
|
1435
1420
|
},
|
|
1436
1421
|
withErrorHandling(async ({ teamId, userId }) => {
|
|
1437
1422
|
await client.teams.removeMember(teamId, userId);
|
|
@@ -1441,7 +1426,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1441
1426
|
server.tool(
|
|
1442
1427
|
"leave_team",
|
|
1443
1428
|
"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.",
|
|
1444
|
-
{ teamId:
|
|
1429
|
+
{ teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)") },
|
|
1445
1430
|
withErrorHandling(async ({ teamId }) => {
|
|
1446
1431
|
await client.teams.leave(teamId);
|
|
1447
1432
|
return jsonContent({ left: true, teamId });
|
|
@@ -1451,8 +1436,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1451
1436
|
"invite_to_team",
|
|
1452
1437
|
"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.",
|
|
1453
1438
|
{
|
|
1454
|
-
teamId:
|
|
1455
|
-
email:
|
|
1439
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1440
|
+
email: import_zod.z.string().trim().email().describe("The address to invite")
|
|
1456
1441
|
},
|
|
1457
1442
|
withErrorHandling(async ({ teamId, email }) => {
|
|
1458
1443
|
const pending = await client.teams.invite(teamId, email);
|
|
@@ -1470,8 +1455,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1470
1455
|
"answer_invite",
|
|
1471
1456
|
"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.",
|
|
1472
1457
|
{
|
|
1473
|
-
inviteId:
|
|
1474
|
-
answer:
|
|
1458
|
+
inviteId: import_zod.z.string().min(1).describe("The invite's id, from list_invites"),
|
|
1459
|
+
answer: import_zod.z.enum(["accept", "decline"]).describe("accept joins the team; decline turns it down")
|
|
1475
1460
|
},
|
|
1476
1461
|
withErrorHandling(async ({ inviteId, answer }) => {
|
|
1477
1462
|
if (answer === "accept") {
|
|
@@ -1486,8 +1471,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1486
1471
|
"withdraw_invite",
|
|
1487
1472
|
"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.",
|
|
1488
1473
|
{
|
|
1489
|
-
teamId:
|
|
1490
|
-
inviteId:
|
|
1474
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1475
|
+
inviteId: import_zod.z.string().min(1).describe("The invite's id (from list_team_members' pendingInvites)")
|
|
1491
1476
|
},
|
|
1492
1477
|
withErrorHandling(async ({ teamId, inviteId }) => {
|
|
1493
1478
|
await client.teams.withdrawInvite(teamId, inviteId);
|
|
@@ -1498,10 +1483,10 @@ function registerTools(target, client, options = {}) {
|
|
|
1498
1483
|
"quote_team_change",
|
|
1499
1484
|
"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.",
|
|
1500
1485
|
{
|
|
1501
|
-
teamId:
|
|
1502
|
-
seats:
|
|
1503
|
-
plan:
|
|
1504
|
-
interval:
|
|
1486
|
+
teamId: import_zod.z.string().min(1).describe("Team id (from list_teams)"),
|
|
1487
|
+
seats: import_zod.z.number().int().min(1).optional().describe("Price this seat count"),
|
|
1488
|
+
plan: import_zod.z.enum(["team", "team_business"]).optional().describe("Price a move to this plan: team (Teams) or team_business (Team Business)"),
|
|
1489
|
+
interval: import_zod.z.enum(["month", "year"]).optional().describe("Billing interval for plan (default month; Teams is monthly only)")
|
|
1505
1490
|
},
|
|
1506
1491
|
withErrorHandling(async ({ teamId, seats, plan, interval }) => {
|
|
1507
1492
|
if (seats === void 0 === (plan === void 0)) {
|
|
@@ -1529,8 +1514,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1529
1514
|
"favorite_endpoint",
|
|
1530
1515
|
"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.",
|
|
1531
1516
|
{
|
|
1532
|
-
slug:
|
|
1533
|
-
favorite:
|
|
1517
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1518
|
+
favorite: import_zod.z.boolean().optional().describe("true marks it as a favourite (the default), false unmarks it")
|
|
1534
1519
|
},
|
|
1535
1520
|
withErrorHandling(async ({ slug, favorite }) => {
|
|
1536
1521
|
const result = favorite === false ? await client.endpoints.unfavorite(slug) : await client.endpoints.favorite(slug);
|
|
@@ -1541,24 +1526,24 @@ function registerTools(target, client, options = {}) {
|
|
|
1541
1526
|
"update_endpoint",
|
|
1542
1527
|
"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.",
|
|
1543
1528
|
{
|
|
1544
|
-
slug:
|
|
1545
|
-
name:
|
|
1529
|
+
slug: import_zod.z.string().describe("The endpoint slug to update"),
|
|
1530
|
+
name: import_zod.z.string().optional().describe("New display name"),
|
|
1546
1531
|
mockResponse: mockResponseSchema.nullable().optional().describe("Default mock response (used when no rule matches), or null to clear"),
|
|
1547
1532
|
responseRules: responseRulesSchema.nullable().optional().describe("Conditional response rules (first match wins), or null to clear"),
|
|
1548
|
-
notificationUrl:
|
|
1549
|
-
signingProvider:
|
|
1533
|
+
notificationUrl: import_zod.z.string().url().nullable().optional().describe("Notification webhook URL, or null to clear it"),
|
|
1534
|
+
signingProvider: import_zod.z.string().nullable().optional().describe(
|
|
1550
1535
|
"Signing provider for automatic verification (e.g. stripe, github), or null to disable"
|
|
1551
1536
|
),
|
|
1552
|
-
signingSecret:
|
|
1553
|
-
signingHeader:
|
|
1537
|
+
signingSecret: import_zod.z.string().nullable().optional().describe("New signing secret (encrypted server-side). Pass null to clear."),
|
|
1538
|
+
signingHeader: import_zod.z.string().nullable().optional().describe(
|
|
1554
1539
|
"Custom signature header name (only for generic-hmac provider), or null to clear"
|
|
1555
1540
|
),
|
|
1556
|
-
showEmailExtracts:
|
|
1541
|
+
showEmailExtracts: import_zod.z.boolean().optional().describe("Pick codes and links out of emails (dashboard and forwarded JSON). Owner only."),
|
|
1557
1542
|
site: endpointSiteSchema.optional(),
|
|
1558
|
-
emailFields:
|
|
1543
|
+
emailFields: import_zod.z.array(emailFieldSchema).max(20).nullable().optional().describe(
|
|
1559
1544
|
"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."
|
|
1560
1545
|
),
|
|
1561
|
-
layouts:
|
|
1546
|
+
layouts: import_zod.z.array(layoutSchema).max(50).nullable().optional().describe(
|
|
1562
1547
|
"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."
|
|
1563
1548
|
)
|
|
1564
1549
|
},
|
|
@@ -1598,9 +1583,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1598
1583
|
"preview_layouts",
|
|
1599
1584
|
"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.",
|
|
1600
1585
|
{
|
|
1601
|
-
slug:
|
|
1602
|
-
requestId:
|
|
1603
|
-
layouts:
|
|
1586
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1587
|
+
requestId: import_zod.z.string().optional().describe("A captured email on the endpoint (default: the newest)"),
|
|
1588
|
+
layouts: import_zod.z.array(layoutSchema).max(50).optional().describe("Unsaved layouts to try")
|
|
1604
1589
|
},
|
|
1605
1590
|
withErrorHandling(async ({ slug, requestId, layouts }) => {
|
|
1606
1591
|
const preview = await client.endpoints.previewLayouts(slug, { requestId, layouts });
|
|
@@ -1612,15 +1597,15 @@ function registerTools(target, client, options = {}) {
|
|
|
1612
1597
|
server.tool(
|
|
1613
1598
|
"list_layout_templates",
|
|
1614
1599
|
"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.",
|
|
1615
|
-
{ slug:
|
|
1600
|
+
{ slug: import_zod.z.string().describe("The endpoint slug") },
|
|
1616
1601
|
withErrorHandling(async ({ slug }) => jsonContent(await client.endpoints.layoutTemplates(slug)))
|
|
1617
1602
|
);
|
|
1618
1603
|
server.tool(
|
|
1619
1604
|
"add_layout_template",
|
|
1620
1605
|
"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.",
|
|
1621
1606
|
{
|
|
1622
|
-
slug:
|
|
1623
|
-
template:
|
|
1607
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1608
|
+
template: import_zod.z.string().min(1).describe("The template's id, from list_layout_templates")
|
|
1624
1609
|
},
|
|
1625
1610
|
withErrorHandling(
|
|
1626
1611
|
async ({ slug, template }) => jsonContent(await client.endpoints.addLayoutTemplate(slug, template))
|
|
@@ -1630,9 +1615,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1630
1615
|
"preview_email_fields",
|
|
1631
1616
|
"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.",
|
|
1632
1617
|
{
|
|
1633
|
-
slug:
|
|
1634
|
-
requestId:
|
|
1635
|
-
emailFields:
|
|
1618
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1619
|
+
requestId: import_zod.z.string().optional().describe("A captured email on the endpoint (default: the newest)"),
|
|
1620
|
+
emailFields: import_zod.z.array(emailFieldSchema).max(20).optional().describe("Unsaved fields to try instead of the saved ones")
|
|
1636
1621
|
},
|
|
1637
1622
|
withErrorHandling(
|
|
1638
1623
|
async ({ slug, requestId, emailFields }) => jsonContent(await client.endpoints.previewEmailFields(slug, { requestId, emailFields }))
|
|
@@ -1642,8 +1627,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1642
1627
|
"preview_response",
|
|
1643
1628
|
"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.",
|
|
1644
1629
|
{
|
|
1645
|
-
slug:
|
|
1646
|
-
requestId:
|
|
1630
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
1631
|
+
requestId: import_zod.z.string().optional().describe(
|
|
1647
1632
|
"A captured request on the endpoint (default: the newest HTTP request, or a sample)"
|
|
1648
1633
|
),
|
|
1649
1634
|
mockResponse: mockResponseSchema.nullable().optional().describe("Try this default response instead of the saved one (null: none)"),
|
|
@@ -1671,9 +1656,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1671
1656
|
"create_endpoints",
|
|
1672
1657
|
"Create multiple webhook endpoints in one call.",
|
|
1673
1658
|
{
|
|
1674
|
-
count:
|
|
1675
|
-
namePrefix:
|
|
1676
|
-
ephemeral:
|
|
1659
|
+
count: import_zod.z.number().int().min(1).max(20).describe("Number of endpoints to create"),
|
|
1660
|
+
namePrefix: import_zod.z.string().optional().describe("Optional prefix for endpoint names"),
|
|
1661
|
+
ephemeral: import_zod.z.boolean().optional().describe("Create temporary endpoints that auto-expire"),
|
|
1677
1662
|
expiresIn: durationOrTimestampSchema.optional().describe('Auto-expire after this duration, for example "12h"')
|
|
1678
1663
|
},
|
|
1679
1664
|
withErrorHandling(async ({ count, namePrefix, ephemeral, expiresIn }) => {
|
|
@@ -1694,7 +1679,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1694
1679
|
"delete_endpoints",
|
|
1695
1680
|
"Delete multiple webhook endpoints in one call.",
|
|
1696
1681
|
{
|
|
1697
|
-
slugs:
|
|
1682
|
+
slugs: import_zod.z.array(import_zod.z.string()).min(1).max(100).describe("Endpoint slugs to delete")
|
|
1698
1683
|
},
|
|
1699
1684
|
withErrorHandling(async ({ slugs }) => {
|
|
1700
1685
|
const settled = await Promise.allSettled(
|
|
@@ -1722,14 +1707,14 @@ function registerTools(target, client, options = {}) {
|
|
|
1722
1707
|
"send_webhook",
|
|
1723
1708
|
"Send a test webhook to a hosted endpoint. Supports provider templates and signing.",
|
|
1724
1709
|
{
|
|
1725
|
-
slug:
|
|
1710
|
+
slug: import_zod.z.string().describe("The endpoint slug to send to"),
|
|
1726
1711
|
method: methodSchema,
|
|
1727
|
-
headers:
|
|
1728
|
-
body:
|
|
1729
|
-
provider:
|
|
1730
|
-
template:
|
|
1731
|
-
event:
|
|
1732
|
-
secret:
|
|
1712
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional().describe("HTTP headers to include"),
|
|
1713
|
+
body: import_zod.z.unknown().optional().describe("Request body"),
|
|
1714
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to send with signed headers"),
|
|
1715
|
+
template: import_zod.z.string().optional().describe("Provider-specific template preset"),
|
|
1716
|
+
event: import_zod.z.string().optional().describe("Provider event or topic name"),
|
|
1717
|
+
secret: import_zod.z.string().optional().describe(
|
|
1733
1718
|
"Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
|
|
1734
1719
|
)
|
|
1735
1720
|
},
|
|
@@ -1775,10 +1760,10 @@ function registerTools(target, client, options = {}) {
|
|
|
1775
1760
|
"search_requests",
|
|
1776
1761
|
"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.",
|
|
1777
1762
|
{
|
|
1778
|
-
slug:
|
|
1779
|
-
method:
|
|
1763
|
+
slug: import_zod.z.string().optional().describe("Filter to a specific endpoint slug"),
|
|
1764
|
+
method: import_zod.z.string().optional().describe("Filter by HTTP method"),
|
|
1780
1765
|
kind: kindSchema,
|
|
1781
|
-
q:
|
|
1766
|
+
q: import_zod.z.string().optional().describe(
|
|
1782
1767
|
"Substring to find in the path, headers, body and decoded email fields (up to 1,000 characters)"
|
|
1783
1768
|
),
|
|
1784
1769
|
from: durationOrTimestampSchema.optional().describe(
|
|
@@ -1787,10 +1772,10 @@ function registerTools(target, client, options = {}) {
|
|
|
1787
1772
|
to: durationOrTimestampSchema.optional().describe(
|
|
1788
1773
|
'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.'
|
|
1789
1774
|
),
|
|
1790
|
-
limit:
|
|
1791
|
-
offset:
|
|
1792
|
-
order:
|
|
1793
|
-
cursor:
|
|
1775
|
+
limit: import_zod.z.number().int().min(1).max(200).default(50).describe("Max results to return"),
|
|
1776
|
+
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)"),
|
|
1777
|
+
order: import_zod.z.enum(["asc", "desc"]).default("desc").describe("Sort order by received time"),
|
|
1778
|
+
cursor: import_zod.z.string().optional().describe(
|
|
1794
1779
|
"The cursor from a previous search_requests result, to continue that search. Keep the other inputs the same."
|
|
1795
1780
|
)
|
|
1796
1781
|
},
|
|
@@ -1825,10 +1810,10 @@ function registerTools(target, client, options = {}) {
|
|
|
1825
1810
|
"count_requests",
|
|
1826
1811
|
"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).",
|
|
1827
1812
|
{
|
|
1828
|
-
slug:
|
|
1829
|
-
method:
|
|
1813
|
+
slug: import_zod.z.string().optional().describe("Filter to a specific endpoint slug"),
|
|
1814
|
+
method: import_zod.z.string().optional().describe("Filter by HTTP method"),
|
|
1830
1815
|
kind: kindSchema,
|
|
1831
|
-
q:
|
|
1816
|
+
q: import_zod.z.string().optional().describe(
|
|
1832
1817
|
"Substring to find in the path, headers, body and decoded email fields (up to 1,000 characters)"
|
|
1833
1818
|
),
|
|
1834
1819
|
from: durationOrTimestampSchema.optional().describe(
|
|
@@ -1837,7 +1822,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1837
1822
|
to: durationOrTimestampSchema.optional().describe(
|
|
1838
1823
|
'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.'
|
|
1839
1824
|
),
|
|
1840
|
-
cursor:
|
|
1825
|
+
cursor: import_zod.z.string().optional().describe(
|
|
1841
1826
|
"The cursor from a previous count_requests result, to count the next stretch. Keep the other inputs the same."
|
|
1842
1827
|
)
|
|
1843
1828
|
},
|
|
@@ -1877,11 +1862,11 @@ function registerTools(target, client, options = {}) {
|
|
|
1877
1862
|
"wait_for_requests",
|
|
1878
1863
|
"Wait for multiple requests to arrive at an endpoint.",
|
|
1879
1864
|
{
|
|
1880
|
-
endpointSlug:
|
|
1881
|
-
count:
|
|
1865
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug to monitor"),
|
|
1866
|
+
count: import_zod.z.number().int().min(1).max(20).describe("Number of requests to collect"),
|
|
1882
1867
|
timeout: durationOrTimestampSchema.default("30s").describe('How long to wait, for example "30s"'),
|
|
1883
1868
|
pollInterval: durationOrTimestampSchema.optional().describe('Interval between polls, for example "500ms" or "1s"'),
|
|
1884
|
-
method:
|
|
1869
|
+
method: import_zod.z.string().optional().describe("Only collect requests with this HTTP method")
|
|
1885
1870
|
},
|
|
1886
1871
|
withErrorHandling(async ({ endpointSlug, count, timeout, pollInterval, method }) => {
|
|
1887
1872
|
const result = await waitForMultipleRequests(client, endpointSlug, {
|
|
@@ -1899,7 +1884,7 @@ function registerTools(target, client, options = {}) {
|
|
|
1899
1884
|
"replay_request",
|
|
1900
1885
|
"Replay a previously captured request to a target URL.",
|
|
1901
1886
|
{
|
|
1902
|
-
requestId:
|
|
1887
|
+
requestId: import_zod.z.string().describe("The captured request ID"),
|
|
1903
1888
|
targetUrl: httpUrlSchema.describe("The URL to replay the request to")
|
|
1904
1889
|
},
|
|
1905
1890
|
withErrorHandling(async ({ requestId, targetUrl }) => {
|
|
@@ -1916,9 +1901,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1916
1901
|
"render_email",
|
|
1917
1902
|
"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.",
|
|
1918
1903
|
{
|
|
1919
|
-
requestId:
|
|
1920
|
-
format:
|
|
1921
|
-
mailgunSigningKey:
|
|
1904
|
+
requestId: import_zod.z.string().describe("The captured email's request ID"),
|
|
1905
|
+
format: import_zod.z.enum(["postmark", "sendgrid", "mailgun", "cloudmailin", "json"]).describe("Whose shape; json is webhooks.cc's own email.received JSON (unsigned)"),
|
|
1906
|
+
mailgunSigningKey: import_zod.z.string().optional().describe("For mailgun: the signing key your handler verifies with (a test key)"),
|
|
1922
1907
|
targetUrl: httpUrlSchema.optional().describe("Post it here, e.g. http://localhost:3000/api/inbound/postmark")
|
|
1923
1908
|
},
|
|
1924
1909
|
withErrorHandling(async ({ requestId, format, mailgunSigningKey, targetUrl }) => {
|
|
@@ -1949,9 +1934,9 @@ function registerTools(target, client, options = {}) {
|
|
|
1949
1934
|
"compare_requests",
|
|
1950
1935
|
"Compare two captured requests and show the structured differences.",
|
|
1951
1936
|
{
|
|
1952
|
-
leftRequestId:
|
|
1953
|
-
rightRequestId:
|
|
1954
|
-
ignoreHeaders:
|
|
1937
|
+
leftRequestId: import_zod.z.string().describe("The first request ID"),
|
|
1938
|
+
rightRequestId: import_zod.z.string().describe("The second request ID"),
|
|
1939
|
+
ignoreHeaders: import_zod.z.array(import_zod.z.string()).optional().describe("Headers to ignore during comparison")
|
|
1955
1940
|
},
|
|
1956
1941
|
withErrorHandling(async ({ leftRequestId, rightRequestId, ignoreHeaders }) => {
|
|
1957
1942
|
const [leftRequest, rightRequest] = await Promise.all([
|
|
@@ -1975,8 +1960,8 @@ function registerTools(target, client, options = {}) {
|
|
|
1975
1960
|
"extract_from_request",
|
|
1976
1961
|
"Extract specific JSON fields from a captured request body.",
|
|
1977
1962
|
{
|
|
1978
|
-
requestId:
|
|
1979
|
-
jsonPaths:
|
|
1963
|
+
requestId: import_zod.z.string().describe("The request ID"),
|
|
1964
|
+
jsonPaths: import_zod.z.array(import_zod.z.string()).min(1).max(50).describe("Dot-notation JSON paths to extract")
|
|
1980
1965
|
},
|
|
1981
1966
|
withErrorHandling(async ({ requestId, jsonPaths }) => {
|
|
1982
1967
|
const request = await client.requests.get(requestId);
|
|
@@ -1990,14 +1975,14 @@ function registerTools(target, client, options = {}) {
|
|
|
1990
1975
|
"verify_signature",
|
|
1991
1976
|
"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.",
|
|
1992
1977
|
{
|
|
1993
|
-
requestId:
|
|
1994
|
-
provider:
|
|
1995
|
-
secret:
|
|
1978
|
+
requestId: import_zod.z.string().describe("The captured request ID"),
|
|
1979
|
+
provider: import_zod.z.enum(import_sdk.VERIFY_PROVIDERS).optional().describe("Provider whose signature scheme should be verified. Omit to use stored result."),
|
|
1980
|
+
secret: import_zod.z.string().optional().describe(
|
|
1996
1981
|
"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."
|
|
1997
1982
|
),
|
|
1998
|
-
publicKey:
|
|
1983
|
+
publicKey: import_zod.z.string().optional().describe("Discord application public key. Required for provider=discord."),
|
|
1999
1984
|
url: httpUrlSchema.optional().describe("Original signed URL. Required for Twilio, Square, and HubSpot verification."),
|
|
2000
|
-
method:
|
|
1985
|
+
method: import_zod.z.string().optional().describe(
|
|
2001
1986
|
"Original HTTP method used when the signature was generated. Used by HubSpot v3 verification; the SDK assumes POST when omitted."
|
|
2002
1987
|
)
|
|
2003
1988
|
},
|
|
@@ -2046,7 +2031,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2046
2031
|
"clear_requests",
|
|
2047
2032
|
"Delete captured requests for an endpoint without deleting the endpoint itself.",
|
|
2048
2033
|
{
|
|
2049
|
-
slug:
|
|
2034
|
+
slug: import_zod.z.string().describe("The endpoint slug to clear"),
|
|
2050
2035
|
before: durationOrTimestampSchema.optional().describe('Only clear requests older than this timestamp or duration like "1h"')
|
|
2051
2036
|
},
|
|
2052
2037
|
withErrorHandling(async ({ slug, before }) => {
|
|
@@ -2060,12 +2045,12 @@ function registerTools(target, client, options = {}) {
|
|
|
2060
2045
|
{
|
|
2061
2046
|
url: httpUrlSchema.describe("Target URL"),
|
|
2062
2047
|
method: methodSchema,
|
|
2063
|
-
headers:
|
|
2064
|
-
body:
|
|
2065
|
-
provider:
|
|
2066
|
-
template:
|
|
2067
|
-
event:
|
|
2068
|
-
secret:
|
|
2048
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional().describe("HTTP headers to include"),
|
|
2049
|
+
body: import_zod.z.unknown().optional().describe("Request body"),
|
|
2050
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
|
|
2051
|
+
template: import_zod.z.string().optional().describe("Provider-specific template preset"),
|
|
2052
|
+
event: import_zod.z.string().optional().describe("Provider event or topic name"),
|
|
2053
|
+
secret: import_zod.z.string().optional().describe(
|
|
2069
2054
|
"Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
|
|
2070
2055
|
)
|
|
2071
2056
|
},
|
|
@@ -2093,12 +2078,12 @@ function registerTools(target, client, options = {}) {
|
|
|
2093
2078
|
{
|
|
2094
2079
|
url: httpUrlSchema.describe("Target URL"),
|
|
2095
2080
|
method: methodSchema,
|
|
2096
|
-
headers:
|
|
2097
|
-
body:
|
|
2098
|
-
provider:
|
|
2099
|
-
template:
|
|
2100
|
-
event:
|
|
2101
|
-
secret:
|
|
2081
|
+
headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional().describe("HTTP headers to include"),
|
|
2082
|
+
body: import_zod.z.unknown().optional().describe("Request body"),
|
|
2083
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template for signing"),
|
|
2084
|
+
template: import_zod.z.string().optional().describe("Provider-specific template preset"),
|
|
2085
|
+
event: import_zod.z.string().optional().describe("Provider event or topic name"),
|
|
2086
|
+
secret: import_zod.z.string().optional().describe(
|
|
2102
2087
|
"Signing secret. Required for signed provider templates; omit for secretless providers (sendgrid, discord, plaid)."
|
|
2103
2088
|
)
|
|
2104
2089
|
},
|
|
@@ -2119,7 +2104,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2119
2104
|
"list_provider_templates",
|
|
2120
2105
|
"List supported webhook providers, templates, and signing metadata.",
|
|
2121
2106
|
{
|
|
2122
|
-
provider:
|
|
2107
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Filter to a single provider")
|
|
2123
2108
|
},
|
|
2124
2109
|
withErrorHandling(async ({ provider }) => {
|
|
2125
2110
|
if (provider) {
|
|
@@ -2173,15 +2158,15 @@ function registerTools(target, client, options = {}) {
|
|
|
2173
2158
|
"test_webhook_flow",
|
|
2174
2159
|
"Run a full webhook test flow: create endpoint, optionally mock, send, wait, verify, replay, and clean up.",
|
|
2175
2160
|
{
|
|
2176
|
-
provider:
|
|
2177
|
-
event:
|
|
2178
|
-
secret:
|
|
2161
|
+
provider: import_zod.z.enum(import_sdk.TEMPLATE_PROVIDERS).optional().describe("Optional provider template to use when sending the webhook"),
|
|
2162
|
+
event: import_zod.z.string().optional().describe("Optional provider event or topic name"),
|
|
2163
|
+
secret: import_zod.z.string().optional().describe(
|
|
2179
2164
|
"Signing secret. Required when provider is set or signature verification is enabled."
|
|
2180
2165
|
),
|
|
2181
|
-
mockStatus:
|
|
2166
|
+
mockStatus: import_zod.z.number().int().min(100).max(599).optional().describe("Optional mock response status to configure before sending"),
|
|
2182
2167
|
targetUrl: httpUrlSchema.optional().describe("Optional URL to replay the captured request to after capture"),
|
|
2183
|
-
verifySignature:
|
|
2184
|
-
cleanup:
|
|
2168
|
+
verifySignature: import_zod.z.boolean().default(false).describe("Verify the captured request signature after capture"),
|
|
2169
|
+
cleanup: import_zod.z.boolean().default(true).describe("Delete the created endpoint after the flow completes")
|
|
2185
2170
|
},
|
|
2186
2171
|
withErrorHandling(
|
|
2187
2172
|
async ({
|
|
@@ -2256,14 +2241,14 @@ function registerTools(target, client, options = {}) {
|
|
|
2256
2241
|
"list_emails",
|
|
2257
2242
|
"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.",
|
|
2258
2243
|
{
|
|
2259
|
-
endpointSlug:
|
|
2260
|
-
limit:
|
|
2244
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug"),
|
|
2245
|
+
limit: import_zod.z.number().int().min(1).max(100).default(25).describe(
|
|
2261
2246
|
`Max emails to return. With tag, subject or from, the newest ${EMAIL_SCAN_LIMIT} emails are searched.`
|
|
2262
2247
|
),
|
|
2263
|
-
since:
|
|
2264
|
-
tag:
|
|
2265
|
-
subject:
|
|
2266
|
-
from:
|
|
2248
|
+
since: import_zod.z.number().optional().describe("Only emails received after this timestamp in ms"),
|
|
2249
|
+
tag: import_zod.z.string().optional().describe("Only emails sent to {slug}+{tag}@mailhooks.cc (exact, case-sensitive)"),
|
|
2250
|
+
subject: import_zod.z.string().optional().describe("Only emails whose subject contains this text"),
|
|
2251
|
+
from: import_zod.z.string().optional().describe("Only emails from this address (case-insensitive)")
|
|
2267
2252
|
},
|
|
2268
2253
|
withErrorHandling(async ({ endpointSlug, limit, since, tag, subject, from }) => {
|
|
2269
2254
|
const filtered = tag !== void 0 || subject !== void 0 || from !== void 0;
|
|
@@ -2281,10 +2266,10 @@ function registerTools(target, client, options = {}) {
|
|
|
2281
2266
|
"get_email",
|
|
2282
2267
|
"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.",
|
|
2283
2268
|
{
|
|
2284
|
-
requestId:
|
|
2285
|
-
endpointSlug:
|
|
2286
|
-
tag:
|
|
2287
|
-
includeHtml:
|
|
2269
|
+
requestId: import_zod.z.string().optional().describe("The email's request ID"),
|
|
2270
|
+
endpointSlug: import_zod.z.string().optional().describe("Get the newest email at this endpoint instead (with requestId unset)"),
|
|
2271
|
+
tag: import_zod.z.string().optional().describe("With endpointSlug: only emails sent to this +tag"),
|
|
2272
|
+
includeHtml: import_zod.z.boolean().default(false).describe("Include the HTML part (default: false, only its size)")
|
|
2288
2273
|
},
|
|
2289
2274
|
withErrorHandling(async ({ requestId, endpointSlug, tag, includeHtml }) => {
|
|
2290
2275
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
@@ -2307,15 +2292,15 @@ function registerTools(target, client, options = {}) {
|
|
|
2307
2292
|
"wait_for_email",
|
|
2308
2293
|
"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.",
|
|
2309
2294
|
{
|
|
2310
|
-
endpointSlug:
|
|
2311
|
-
tag:
|
|
2312
|
-
subject:
|
|
2313
|
-
from:
|
|
2295
|
+
endpointSlug: import_zod.z.string().describe("The endpoint slug"),
|
|
2296
|
+
tag: import_zod.z.string().optional().describe("Only emails sent to {slug}+{tag}@mailhooks.cc"),
|
|
2297
|
+
subject: import_zod.z.string().optional().describe("Only emails whose subject contains this text"),
|
|
2298
|
+
from: import_zod.z.string().optional().describe("Only emails from this address (case-insensitive)"),
|
|
2314
2299
|
timeout: durationOrTimestampSchema.default("30s").describe(
|
|
2315
2300
|
`How long to wait (default "30s"). Keep it under your MCP client's request timeout, often 60 seconds.`
|
|
2316
2301
|
),
|
|
2317
|
-
since:
|
|
2318
|
-
includeHtml:
|
|
2302
|
+
since: import_zod.z.number().optional().describe("Only emails received after this timestamp in ms (default: five minutes ago)"),
|
|
2303
|
+
includeHtml: import_zod.z.boolean().default(false).describe("Include the HTML part (default: false)")
|
|
2319
2304
|
},
|
|
2320
2305
|
withErrorHandling(async ({ endpointSlug, tag, subject, from, timeout, since, includeHtml }) => {
|
|
2321
2306
|
const view = await emailEndpointView(client, endpointSlug);
|
|
@@ -2333,8 +2318,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2333
2318
|
"send_test_email",
|
|
2334
2319
|
"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.",
|
|
2335
2320
|
{
|
|
2336
|
-
slug:
|
|
2337
|
-
tag:
|
|
2321
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2322
|
+
tag: import_zod.z.string().optional().describe("Deliver to {slug}+{tag}@mailhooks.cc")
|
|
2338
2323
|
},
|
|
2339
2324
|
withErrorHandling(async ({ slug, tag }) => {
|
|
2340
2325
|
const result = await client.emails.sendTest(slug, { tag });
|
|
@@ -2345,8 +2330,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2345
2330
|
"list_destinations",
|
|
2346
2331
|
"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.",
|
|
2347
2332
|
{
|
|
2348
|
-
slug:
|
|
2349
|
-
summary:
|
|
2333
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2334
|
+
summary: import_zod.z.boolean().default(true).describe("Add each destination's last 24 hours")
|
|
2350
2335
|
},
|
|
2351
2336
|
withErrorHandling(async ({ slug, summary }) => {
|
|
2352
2337
|
return jsonContent(await client.destinations.list(slug, { summary }));
|
|
@@ -2356,7 +2341,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2356
2341
|
"add_destination",
|
|
2357
2342
|
"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.",
|
|
2358
2343
|
{
|
|
2359
|
-
slug:
|
|
2344
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2360
2345
|
url: httpUrlSchema.describe("https URL on a public host name, or a Slack or Discord webhook"),
|
|
2361
2346
|
...destinationFields
|
|
2362
2347
|
},
|
|
@@ -2368,8 +2353,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2368
2353
|
"update_destination",
|
|
2369
2354
|
"Change a destination: any of its fields; only the ones you pass change. Owner only.",
|
|
2370
2355
|
{
|
|
2371
|
-
slug:
|
|
2372
|
-
destinationId:
|
|
2356
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2357
|
+
destinationId: import_zod.z.string().describe("The destination's id (from list_destinations)"),
|
|
2373
2358
|
url: httpUrlSchema.optional().describe("A new URL"),
|
|
2374
2359
|
...destinationFields
|
|
2375
2360
|
},
|
|
@@ -2381,8 +2366,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2381
2366
|
"remove_destination",
|
|
2382
2367
|
"Remove a destination. Deliveries still waiting for it are marked failed; its log stays. Owner only.",
|
|
2383
2368
|
{
|
|
2384
|
-
slug:
|
|
2385
|
-
destinationId:
|
|
2369
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2370
|
+
destinationId: import_zod.z.string().describe("The destination's id")
|
|
2386
2371
|
},
|
|
2387
2372
|
withErrorHandling(async ({ slug, destinationId }) => {
|
|
2388
2373
|
await client.destinations.delete(slug, destinationId);
|
|
@@ -2393,10 +2378,10 @@ function registerTools(target, client, options = {}) {
|
|
|
2393
2378
|
"preview_destination",
|
|
2394
2379
|
"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.",
|
|
2395
2380
|
{
|
|
2396
|
-
slug:
|
|
2397
|
-
destinationId:
|
|
2398
|
-
destination:
|
|
2399
|
-
requestId:
|
|
2381
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2382
|
+
destinationId: import_zod.z.string().optional().describe("A saved destination"),
|
|
2383
|
+
destination: import_zod.z.object({ url: httpUrlSchema.optional(), ...destinationFields }).optional().describe("Unsaved fields to try"),
|
|
2384
|
+
requestId: import_zod.z.string().optional().describe("A captured request (default: the newest of the kind it takes, or a sample)")
|
|
2400
2385
|
},
|
|
2401
2386
|
withErrorHandling(async ({ slug, destinationId, destination, requestId }) => {
|
|
2402
2387
|
if (!destinationId && !destination?.url) {
|
|
@@ -2415,26 +2400,26 @@ function registerTools(target, client, options = {}) {
|
|
|
2415
2400
|
"configure_forwarding",
|
|
2416
2401
|
"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.",
|
|
2417
2402
|
{
|
|
2418
|
-
slug:
|
|
2403
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2419
2404
|
url: httpUrlSchema.nullable().optional().describe("https URL on a public host name, or null to remove it (forwarding must be off)"),
|
|
2420
|
-
enabled:
|
|
2421
|
-
http:
|
|
2422
|
-
email:
|
|
2423
|
-
format:
|
|
2405
|
+
enabled: import_zod.z.boolean().optional().describe("Turn forwarding on (needs a URL) or off (fails deliveries still waiting)"),
|
|
2406
|
+
http: import_zod.z.boolean().optional().describe("Forward HTTP requests"),
|
|
2407
|
+
email: import_zod.z.boolean().optional().describe("Forward emails"),
|
|
2408
|
+
format: import_zod.z.enum(["auto", "as_received", "json", "chat"]).optional().describe(
|
|
2424
2409
|
"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)"
|
|
2425
2410
|
),
|
|
2426
|
-
headers:
|
|
2427
|
-
|
|
2428
|
-
name:
|
|
2429
|
-
value:
|
|
2411
|
+
headers: import_zod.z.array(
|
|
2412
|
+
import_zod.z.object({
|
|
2413
|
+
name: import_zod.z.string().describe("Header name, e.g. Authorization"),
|
|
2414
|
+
value: import_zod.z.string().nullable().describe("Header value, or null to keep the value stored under this name")
|
|
2430
2415
|
})
|
|
2431
2416
|
).max(10).optional().describe(
|
|
2432
2417
|
"Your own headers for every delivery (not chat). Replaces the whole list; [] removes them"
|
|
2433
2418
|
),
|
|
2434
|
-
appendPath:
|
|
2435
|
-
retrySeconds:
|
|
2436
|
-
keepOrder:
|
|
2437
|
-
sentField:
|
|
2419
|
+
appendPath: import_zod.z.boolean().optional().describe("Add the path after the slug to the URL for relayed requests (default true)"),
|
|
2420
|
+
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"),
|
|
2421
|
+
keepOrder: import_zod.z.boolean().optional().describe("One delivery at a time, in capture order (default false)"),
|
|
2422
|
+
sentField: import_zod.z.string().max(128).nullable().optional().describe(
|
|
2438
2423
|
"JSON body field with the sender's own timestamp, e.g. publishedAt or data.publishedAt; null clears it"
|
|
2439
2424
|
)
|
|
2440
2425
|
},
|
|
@@ -2460,9 +2445,9 @@ function registerTools(target, client, options = {}) {
|
|
|
2460
2445
|
"get_forwarding_secret",
|
|
2461
2446
|
"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.",
|
|
2462
2447
|
{
|
|
2463
|
-
slug:
|
|
2464
|
-
destinationId:
|
|
2465
|
-
rotate:
|
|
2448
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2449
|
+
destinationId: import_zod.z.string().optional().describe("A destination (default: the endpoint's primary destination)"),
|
|
2450
|
+
rotate: import_zod.z.boolean().default(false).describe("Replace the secret; every delivery from now on uses the new one")
|
|
2466
2451
|
},
|
|
2467
2452
|
withErrorHandling(async ({ slug, destinationId, rotate }) => {
|
|
2468
2453
|
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);
|
|
@@ -2473,10 +2458,10 @@ function registerTools(target, client, options = {}) {
|
|
|
2473
2458
|
"test_forwarding",
|
|
2474
2459
|
"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.",
|
|
2475
2460
|
{
|
|
2476
|
-
slug:
|
|
2477
|
-
destinationId:
|
|
2478
|
-
kind:
|
|
2479
|
-
requestId:
|
|
2461
|
+
slug: import_zod.z.string().describe("The endpoint slug"),
|
|
2462
|
+
destinationId: import_zod.z.string().optional().describe("A destination (default: the endpoint's primary destination)"),
|
|
2463
|
+
kind: import_zod.z.enum(["http", "email"]).optional().describe("Which kind to send (default: http when the destination takes HTTP requests)"),
|
|
2464
|
+
requestId: import_zod.z.string().optional().describe("With destinationId: a captured request to send")
|
|
2480
2465
|
},
|
|
2481
2466
|
withErrorHandling(async ({ slug, destinationId, kind, requestId }) => {
|
|
2482
2467
|
if (destinationId) {
|
|
@@ -2494,14 +2479,14 @@ function registerTools(target, client, options = {}) {
|
|
|
2494
2479
|
"list_deliveries",
|
|
2495
2480
|
"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.",
|
|
2496
2481
|
{
|
|
2497
|
-
endpointSlug:
|
|
2498
|
-
requestId:
|
|
2499
|
-
limit:
|
|
2500
|
-
status:
|
|
2482
|
+
endpointSlug: import_zod.z.string().optional().describe("List the endpoint's delivery log"),
|
|
2483
|
+
requestId: import_zod.z.string().optional().describe("List every delivery and try of this request"),
|
|
2484
|
+
limit: import_zod.z.number().int().min(1).max(100).default(20).describe("With endpointSlug: max rows"),
|
|
2485
|
+
status: import_zod.z.enum(["all", "pending", "failed", "skipped"]).default("all").describe(
|
|
2501
2486
|
"With endpointSlug: all, pending (queued or retrying), failed or skipped (filtered out)"
|
|
2502
2487
|
),
|
|
2503
|
-
destinationId:
|
|
2504
|
-
before:
|
|
2488
|
+
destinationId: import_zod.z.string().optional().describe("With endpointSlug: only this destination"),
|
|
2489
|
+
before: import_zod.z.string().optional().describe("With endpointSlug: a row's cursor, to list older rows")
|
|
2505
2490
|
},
|
|
2506
2491
|
withErrorHandling(async ({ endpointSlug, requestId, limit, status, destinationId, before }) => {
|
|
2507
2492
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
@@ -2528,9 +2513,9 @@ function registerTools(target, client, options = {}) {
|
|
|
2528
2513
|
"redeliver",
|
|
2529
2514
|
"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.",
|
|
2530
2515
|
{
|
|
2531
|
-
requestId:
|
|
2532
|
-
endpointSlug:
|
|
2533
|
-
destinationId:
|
|
2516
|
+
requestId: import_zod.z.string().optional().describe("The request or email to send again"),
|
|
2517
|
+
endpointSlug: import_zod.z.string().optional().describe("Send every failed delivery of this endpoint again"),
|
|
2518
|
+
destinationId: import_zod.z.string().optional().describe("Only this destination")
|
|
2534
2519
|
},
|
|
2535
2520
|
withErrorHandling(async ({ requestId, endpointSlug, destinationId }) => {
|
|
2536
2521
|
if (requestId === void 0 === (endpointSlug === void 0)) {
|
|
@@ -2547,9 +2532,9 @@ function registerTools(target, client, options = {}) {
|
|
|
2547
2532
|
"share_port",
|
|
2548
2533
|
"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.",
|
|
2549
2534
|
{
|
|
2550
|
-
port:
|
|
2551
|
-
name:
|
|
2552
|
-
password:
|
|
2535
|
+
port: import_zod.z.number().int().min(1).max(65535).describe("The local port the site runs on"),
|
|
2536
|
+
name: import_zod.z.string().optional().describe("A name you reserved (Pro and Business); leave out for a random one"),
|
|
2537
|
+
password: import_zod.z.string().min(8).max(200).optional().describe("What visitors must type (Pro and Business), 8 to 200 characters")
|
|
2553
2538
|
},
|
|
2554
2539
|
withErrorHandling(async ({ port, name, password }) => {
|
|
2555
2540
|
const running = started.get(port);
|
|
@@ -2594,7 +2579,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2594
2579
|
"list_shares",
|
|
2595
2580
|
`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.`,
|
|
2596
2581
|
{
|
|
2597
|
-
state:
|
|
2582
|
+
state: import_zod.z.enum(["live", "ended"]).optional().describe("Only the running ones, or only the ended ones")
|
|
2598
2583
|
},
|
|
2599
2584
|
withErrorHandling(async ({ state }) => {
|
|
2600
2585
|
return jsonContent(withShareHints(await client.shares.list(state ? { state } : {}), remote));
|
|
@@ -2610,8 +2595,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2610
2595
|
"create_api_key",
|
|
2611
2596
|
"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).",
|
|
2612
2597
|
{
|
|
2613
|
-
name:
|
|
2614
|
-
expiresInDays:
|
|
2598
|
+
name: import_zod.z.string().min(1).max(64).describe('A name that says where the key is used, such as "GitHub Actions"'),
|
|
2599
|
+
expiresInDays: import_zod.z.number().int().min(1).max(365).optional().describe("How many days the key works, 1 to 365 (default 90)")
|
|
2615
2600
|
},
|
|
2616
2601
|
withErrorHandling(async ({ name, expiresInDays }) => {
|
|
2617
2602
|
const pending = await client.apiKeys.create({ name, expiresInDays });
|
|
@@ -2626,7 +2611,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2626
2611
|
"wait_for_approval",
|
|
2627
2612
|
"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.",
|
|
2628
2613
|
{
|
|
2629
|
-
id:
|
|
2614
|
+
id: import_zod.z.string().describe("The approval id"),
|
|
2630
2615
|
timeout: durationOrTimestampSchema.default("45s").describe('How long to wait for the person, for example "45s"')
|
|
2631
2616
|
},
|
|
2632
2617
|
withErrorHandling(async ({ id, timeout }) => {
|
|
@@ -2658,7 +2643,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2658
2643
|
"revoke_api_key",
|
|
2659
2644
|
"Revoke one of the account's API keys by its id (from list_api_keys). Anything using it stops working at once.",
|
|
2660
2645
|
{
|
|
2661
|
-
id:
|
|
2646
|
+
id: import_zod.z.string().describe("The key's id, from list_api_keys")
|
|
2662
2647
|
},
|
|
2663
2648
|
withErrorHandling(async ({ id }) => {
|
|
2664
2649
|
await client.apiKeys.revoke(id);
|
|
@@ -2669,7 +2654,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2669
2654
|
"rename_account",
|
|
2670
2655
|
"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.",
|
|
2671
2656
|
{
|
|
2672
|
-
name:
|
|
2657
|
+
name: import_zod.z.string().min(1).max(100).nullable().describe("The new display name, or null to clear it")
|
|
2673
2658
|
},
|
|
2674
2659
|
withErrorHandling(async ({ name }) => {
|
|
2675
2660
|
const pending = await client.account.rename(name);
|
|
@@ -2687,7 +2672,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2687
2672
|
"disconnect_agent",
|
|
2688
2673
|
"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.",
|
|
2689
2674
|
{
|
|
2690
|
-
id:
|
|
2675
|
+
id: import_zod.z.string().describe("The agent's id, from list_connected_agents")
|
|
2691
2676
|
},
|
|
2692
2677
|
withErrorHandling(async ({ id }) => {
|
|
2693
2678
|
await client.agents.disconnect(id);
|
|
@@ -2698,7 +2683,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2698
2683
|
"list_share_links",
|
|
2699
2684
|
"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.",
|
|
2700
2685
|
{
|
|
2701
|
-
requestId:
|
|
2686
|
+
requestId: import_zod.z.string().optional().describe("Only the links to this request")
|
|
2702
2687
|
},
|
|
2703
2688
|
withErrorHandling(async ({ requestId }) => {
|
|
2704
2689
|
const links = requestId ? await client.shareLinks.forRequest(requestId) : await client.shareLinks.list();
|
|
@@ -2709,9 +2694,9 @@ function registerTools(target, client, options = {}) {
|
|
|
2709
2694
|
"create_share_link",
|
|
2710
2695
|
"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.",
|
|
2711
2696
|
{
|
|
2712
|
-
requestId:
|
|
2713
|
-
expiresInDays:
|
|
2714
|
-
hideSensitive:
|
|
2697
|
+
requestId: import_zod.z.string().describe("The request's id"),
|
|
2698
|
+
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)"),
|
|
2699
|
+
hideSensitive: import_zod.z.boolean().optional().describe(
|
|
2715
2700
|
"Also leave out signature and token headers (default true). Credentials are always left out."
|
|
2716
2701
|
)
|
|
2717
2702
|
},
|
|
@@ -2725,14 +2710,14 @@ function registerTools(target, client, options = {}) {
|
|
|
2725
2710
|
"revoke_share_link",
|
|
2726
2711
|
"Revoke a share link by its id (from list_share_links): its page answers 404 from then on.",
|
|
2727
2712
|
{
|
|
2728
|
-
id:
|
|
2713
|
+
id: import_zod.z.string().describe("The link's id, from list_share_links")
|
|
2729
2714
|
},
|
|
2730
2715
|
withErrorHandling(async ({ id }) => {
|
|
2731
2716
|
await client.shareLinks.revoke(id);
|
|
2732
2717
|
return textContent(`Revoked the share link ${id}.`);
|
|
2733
2718
|
})
|
|
2734
2719
|
);
|
|
2735
|
-
const teamIdSchema =
|
|
2720
|
+
const teamIdSchema = import_zod.z.string().optional().describe("A Team Business team's id (its owner only); leave out for your own account");
|
|
2736
2721
|
server.tool(
|
|
2737
2722
|
"list_domains",
|
|
2738
2723
|
"List custom inbound domains (Business): each one's status, DNS records to publish, routes and catch-all endpoint.",
|
|
@@ -2743,7 +2728,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2743
2728
|
"add_domain",
|
|
2744
2729
|
"Add a custom inbound domain (Business) so mail to it is captured. Returns the TXT and MX records to publish; then call verify_domain.",
|
|
2745
2730
|
{
|
|
2746
|
-
domain:
|
|
2731
|
+
domain: import_zod.z.string().min(1).max(253).describe("The domain, such as mail.example.com"),
|
|
2747
2732
|
teamId: teamIdSchema
|
|
2748
2733
|
},
|
|
2749
2734
|
withErrorHandling(
|
|
@@ -2754,7 +2739,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2754
2739
|
"verify_domain",
|
|
2755
2740
|
"Look a custom domain's TXT and MX records up now. It receives mail once both are found.",
|
|
2756
2741
|
{
|
|
2757
|
-
id:
|
|
2742
|
+
id: import_zod.z.string().describe("The domain's id, from list_domains"),
|
|
2758
2743
|
teamId: teamIdSchema
|
|
2759
2744
|
},
|
|
2760
2745
|
withErrorHandling(
|
|
@@ -2765,12 +2750,12 @@ function registerTools(target, client, options = {}) {
|
|
|
2765
2750
|
"route_domain",
|
|
2766
2751
|
"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.",
|
|
2767
2752
|
{
|
|
2768
|
-
id:
|
|
2769
|
-
catchAllEndpointId:
|
|
2770
|
-
routes:
|
|
2771
|
-
|
|
2772
|
-
localPart:
|
|
2773
|
-
endpointId:
|
|
2753
|
+
id: import_zod.z.string().describe("The domain's id, from list_domains"),
|
|
2754
|
+
catchAllEndpointId: import_zod.z.string().nullable().describe("The endpoint id for mail no route matches, or null to refuse it"),
|
|
2755
|
+
routes: import_zod.z.array(
|
|
2756
|
+
import_zod.z.object({
|
|
2757
|
+
localPart: import_zod.z.string().max(64).describe("The part before @"),
|
|
2758
|
+
endpointId: import_zod.z.string().describe("The endpoint's id")
|
|
2774
2759
|
})
|
|
2775
2760
|
).max(100).default([]).describe("Routes by local part"),
|
|
2776
2761
|
teamId: teamIdSchema
|
|
@@ -2783,7 +2768,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2783
2768
|
"remove_domain",
|
|
2784
2769
|
"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.",
|
|
2785
2770
|
{
|
|
2786
|
-
id:
|
|
2771
|
+
id: import_zod.z.string().describe("The domain's id, from list_domains"),
|
|
2787
2772
|
teamId: teamIdSchema
|
|
2788
2773
|
},
|
|
2789
2774
|
withErrorHandling(async ({ id, teamId }) => {
|
|
@@ -2808,7 +2793,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2808
2793
|
"pick_spreadsheet",
|
|
2809
2794
|
"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.",
|
|
2810
2795
|
{
|
|
2811
|
-
connectionId:
|
|
2796
|
+
connectionId: import_zod.z.string().describe("The Google connection's id, from list_google_connections")
|
|
2812
2797
|
},
|
|
2813
2798
|
withErrorHandling(
|
|
2814
2799
|
async ({ connectionId }) => textContent(approvalAskText(await client.google.pick(connectionId)))
|
|
@@ -2818,10 +2803,10 @@ function registerTools(target, client, options = {}) {
|
|
|
2818
2803
|
"create_spreadsheet",
|
|
2819
2804
|
"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.",
|
|
2820
2805
|
{
|
|
2821
|
-
connectionId:
|
|
2822
|
-
title:
|
|
2823
|
-
sheet:
|
|
2824
|
-
headers:
|
|
2806
|
+
connectionId: import_zod.z.string().describe("The Google connection's id, from list_google_connections"),
|
|
2807
|
+
title: import_zod.z.string().max(200).optional().describe("The spreadsheet's title"),
|
|
2808
|
+
sheet: import_zod.z.string().max(100).optional().describe('The tab rows go to (default "Sheet1")'),
|
|
2809
|
+
headers: import_zod.z.array(import_zod.z.string().max(200)).max(50).describe("The header row, one per column")
|
|
2825
2810
|
},
|
|
2826
2811
|
withErrorHandling(
|
|
2827
2812
|
async ({ connectionId, title, sheet, headers }) => jsonContent(await client.google.createSpreadsheet({ connectionId, title, sheet, headers }))
|
|
@@ -2831,8 +2816,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2831
2816
|
"list_sheet_tabs",
|
|
2832
2817
|
"List a spreadsheet's tabs (one created by the app or chosen with pick_spreadsheet), to choose the one a Sheets destination writes to.",
|
|
2833
2818
|
{
|
|
2834
|
-
connectionId:
|
|
2835
|
-
spreadsheetId:
|
|
2819
|
+
connectionId: import_zod.z.string().describe("The Google connection's id"),
|
|
2820
|
+
spreadsheetId: import_zod.z.string().describe("The spreadsheet's id")
|
|
2836
2821
|
},
|
|
2837
2822
|
withErrorHandling(
|
|
2838
2823
|
async ({ connectionId, spreadsheetId }) => jsonContent({ tabs: await client.google.tabs(connectionId, spreadsheetId) })
|
|
@@ -2842,7 +2827,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2842
2827
|
"disconnect_google",
|
|
2843
2828
|
"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.",
|
|
2844
2829
|
{
|
|
2845
|
-
connectionId:
|
|
2830
|
+
connectionId: import_zod.z.string().describe("The Google connection's id")
|
|
2846
2831
|
},
|
|
2847
2832
|
withErrorHandling(async ({ connectionId }) => {
|
|
2848
2833
|
await client.google.disconnect(connectionId);
|
|
@@ -2853,7 +2838,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2853
2838
|
"ai_setup_status",
|
|
2854
2839
|
"Whether AI setup is offered for an endpoint, and how many AI assists the plan has left in a rolling 30 days.",
|
|
2855
2840
|
{
|
|
2856
|
-
slug:
|
|
2841
|
+
slug: import_zod.z.string().describe("The endpoint's slug")
|
|
2857
2842
|
},
|
|
2858
2843
|
withErrorHandling(async ({ slug }) => jsonContent(await client.ai.status(slug)))
|
|
2859
2844
|
);
|
|
@@ -2861,25 +2846,25 @@ function registerTools(target, client, options = {}) {
|
|
|
2861
2846
|
"ai_setup",
|
|
2862
2847
|
"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.",
|
|
2863
2848
|
{
|
|
2864
|
-
slug:
|
|
2865
|
-
requestIds:
|
|
2866
|
-
ask:
|
|
2867
|
-
|
|
2868
|
-
kind:
|
|
2869
|
-
name:
|
|
2870
|
-
description:
|
|
2849
|
+
slug: import_zod.z.string().describe("The endpoint's slug"),
|
|
2850
|
+
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"),
|
|
2851
|
+
ask: import_zod.z.union([
|
|
2852
|
+
import_zod.z.object({
|
|
2853
|
+
kind: import_zod.z.literal("field"),
|
|
2854
|
+
name: import_zod.z.string().describe("The field's name: letters, digits and _"),
|
|
2855
|
+
description: import_zod.z.string().describe('What the field is, such as "the order number"')
|
|
2871
2856
|
}),
|
|
2872
|
-
|
|
2873
|
-
kind:
|
|
2874
|
-
description:
|
|
2857
|
+
import_zod.z.object({
|
|
2858
|
+
kind: import_zod.z.literal("layout"),
|
|
2859
|
+
description: import_zod.z.string().optional().describe("What these emails are")
|
|
2875
2860
|
}),
|
|
2876
|
-
|
|
2877
|
-
kind:
|
|
2878
|
-
field:
|
|
2879
|
-
name:
|
|
2880
|
-
from:
|
|
2881
|
-
header:
|
|
2882
|
-
pattern:
|
|
2861
|
+
import_zod.z.object({
|
|
2862
|
+
kind: import_zod.z.literal("repair"),
|
|
2863
|
+
field: import_zod.z.object({
|
|
2864
|
+
name: import_zod.z.string(),
|
|
2865
|
+
from: import_zod.z.string(),
|
|
2866
|
+
header: import_zod.z.string().optional(),
|
|
2867
|
+
pattern: import_zod.z.string()
|
|
2883
2868
|
}).describe("The field to repair, as the endpoint has it")
|
|
2884
2869
|
})
|
|
2885
2870
|
]).describe("What to propose")
|
|
@@ -2899,7 +2884,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2899
2884
|
"stop_share",
|
|
2900
2885
|
"Stop a public preview or live tunnel by its id (from list_shares). The CLI running it is told why and exits.",
|
|
2901
2886
|
{
|
|
2902
|
-
id:
|
|
2887
|
+
id: import_zod.z.string().describe("The share's id, from list_shares")
|
|
2903
2888
|
},
|
|
2904
2889
|
withErrorHandling(async ({ id }) => {
|
|
2905
2890
|
const result = await client.shares.stop(id);
|
|
@@ -2915,7 +2900,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2915
2900
|
"share_requests",
|
|
2916
2901
|
"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.",
|
|
2917
2902
|
{
|
|
2918
|
-
id:
|
|
2903
|
+
id: import_zod.z.string().describe("The share's id, from list_shares")
|
|
2919
2904
|
},
|
|
2920
2905
|
withErrorHandling(async ({ id }) => {
|
|
2921
2906
|
return jsonContent(await client.shares.requests(id));
|
|
@@ -2933,8 +2918,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2933
2918
|
"reserve_name",
|
|
2934
2919
|
`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.`,
|
|
2935
2920
|
{
|
|
2936
|
-
name:
|
|
2937
|
-
team:
|
|
2921
|
+
name: import_zod.z.string().min(1).max(63).describe("The name: lowercase letters, digits and dashes"),
|
|
2922
|
+
team: import_zod.z.string().optional().describe("A team's id or name (from list_teams), to keep the name inside that team")
|
|
2938
2923
|
},
|
|
2939
2924
|
withErrorHandling(async ({ name, team }) => {
|
|
2940
2925
|
return jsonContent(await client.names.reserve(name, team ? { team } : {}));
|
|
@@ -2944,7 +2929,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2944
2929
|
"release_name",
|
|
2945
2930
|
"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.",
|
|
2946
2931
|
{
|
|
2947
|
-
name:
|
|
2932
|
+
name: import_zod.z.string().min(1).max(63).describe("The name, from list_names")
|
|
2948
2933
|
},
|
|
2949
2934
|
withErrorHandling(async ({ name }) => {
|
|
2950
2935
|
await client.names.release(name);
|
|
@@ -2955,8 +2940,8 @@ function registerTools(target, client, options = {}) {
|
|
|
2955
2940
|
"set_name_password",
|
|
2956
2941
|
"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.",
|
|
2957
2942
|
{
|
|
2958
|
-
name:
|
|
2959
|
-
password:
|
|
2943
|
+
name: import_zod.z.string().min(1).max(63).describe("The name, from list_names"),
|
|
2944
|
+
password: import_zod.z.string().min(8).max(200).describe("What visitors must type, 8 to 200 characters")
|
|
2960
2945
|
},
|
|
2961
2946
|
withErrorHandling(async ({ name, password }) => {
|
|
2962
2947
|
await client.names.setPassword(name, password);
|
|
@@ -2967,7 +2952,7 @@ function registerTools(target, client, options = {}) {
|
|
|
2967
2952
|
"clear_name_password",
|
|
2968
2953
|
"Take the password off a reserved name, so its previews open without one.",
|
|
2969
2954
|
{
|
|
2970
|
-
name:
|
|
2955
|
+
name: import_zod.z.string().min(1).max(63).describe("The name, from list_names")
|
|
2971
2956
|
},
|
|
2972
2957
|
withErrorHandling(async ({ name }) => {
|
|
2973
2958
|
await client.names.clearPassword(name);
|
|
@@ -2986,18 +2971,440 @@ function registerTools(target, client, options = {}) {
|
|
|
2986
2971
|
return handles;
|
|
2987
2972
|
}
|
|
2988
2973
|
|
|
2989
|
-
// src/
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
2974
|
+
// src/local-tools.ts
|
|
2975
|
+
function asToolServer(server) {
|
|
2976
|
+
return {
|
|
2977
|
+
tool(name, description, shape, handler, annotations) {
|
|
2978
|
+
const registered = server.registerTool(
|
|
2979
|
+
name,
|
|
2980
|
+
{
|
|
2981
|
+
title: annotations?.title,
|
|
2982
|
+
description,
|
|
2983
|
+
inputSchema: import_zod2.z.object(shape),
|
|
2984
|
+
annotations
|
|
2985
|
+
},
|
|
2986
|
+
async (args) => handler(args)
|
|
2987
|
+
);
|
|
2988
|
+
return { remove: () => registered.remove() };
|
|
2989
|
+
}
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
var warned = /* @__PURE__ */ new Set();
|
|
2993
|
+
function toolSelection(options = {}) {
|
|
2994
|
+
const fromOption = options.tools !== void 0;
|
|
2995
|
+
const parsed = parseToolGroups(fromOption ? options.tools : process.env.WHK_MCP_TOOLS);
|
|
2996
|
+
const setting = fromOption ? options.tools : process.env.WHK_MCP_TOOLS;
|
|
2997
|
+
if (parsed && parsed.unknown.length > 0 && !warned.has(setting ?? "")) {
|
|
2998
|
+
warned.add(setting ?? "");
|
|
2999
|
+
console.error(
|
|
3000
|
+
`webhooks-cc-mcp: unknown tool groups in ${fromOption ? "tools" : "WHK_MCP_TOOLS"}: ${parsed.unknown.join(", ")} (known: all, ${TOOL_GROUP_NAMES.join(", ")})`
|
|
3001
|
+
);
|
|
3002
|
+
}
|
|
3003
|
+
const readOnly = options.readOnly ?? /^(1|true|yes|on)$/i.test((process.env.WHK_MCP_READ_ONLY ?? "").trim());
|
|
3004
|
+
return { groups: parsed?.groups ?? null, readOnly };
|
|
3005
|
+
}
|
|
3006
|
+
var EXTRA_GROUPS = TOOL_GROUP_NAMES.filter((group2) => group2 !== "core");
|
|
3007
|
+
var ENABLE_TOOLS_ANNOTATIONS = {
|
|
3008
|
+
title: toolTitle(ENABLE_TOOLS),
|
|
3009
|
+
readOnlyHint: false,
|
|
3010
|
+
destructiveHint: false,
|
|
3011
|
+
idempotentHint: true,
|
|
3012
|
+
openWorldHint: false
|
|
3013
|
+
};
|
|
3014
|
+
var LocalToolGroups = class {
|
|
3015
|
+
constructor(server, selection) {
|
|
3016
|
+
this.server = server;
|
|
3017
|
+
this.selection = selection;
|
|
3018
|
+
this.client = null;
|
|
3019
|
+
this.loaded = /* @__PURE__ */ new Set();
|
|
3020
|
+
this.handles = [];
|
|
3021
|
+
this.enableTools = null;
|
|
3022
|
+
this.clientKnown = false;
|
|
3023
|
+
}
|
|
3024
|
+
/** The groups an account's tools start with. */
|
|
3025
|
+
startingGroups() {
|
|
3026
|
+
if (this.selection.groups) return [...this.selection.groups];
|
|
3027
|
+
return this.clientKnown && clientLoadsEverything(this.clientName) ? [...TOOL_GROUP_NAMES] : ["core"];
|
|
3028
|
+
}
|
|
3029
|
+
/** Registers the tools for an account; the handle removes them all again. */
|
|
3030
|
+
show(client) {
|
|
3031
|
+
this.hide();
|
|
3032
|
+
this.client = client;
|
|
3033
|
+
this.load(this.startingGroups());
|
|
3034
|
+
return { remove: () => this.hide() };
|
|
3035
|
+
}
|
|
3036
|
+
/** Removes every account tool, enable_tools included. */
|
|
3037
|
+
hide() {
|
|
3038
|
+
for (const handle of this.handles) handle.remove();
|
|
3039
|
+
this.enableTools?.remove();
|
|
3040
|
+
this.handles = [];
|
|
3041
|
+
this.enableTools = null;
|
|
3042
|
+
this.loaded.clear();
|
|
3043
|
+
this.client = null;
|
|
3044
|
+
}
|
|
3045
|
+
/** The groups loaded now. */
|
|
3046
|
+
loadedGroups() {
|
|
3047
|
+
return TOOL_GROUP_NAMES.filter((group2) => this.loaded.has(group2));
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
3050
|
+
* The client named itself (initialize, or a 2026-07-28 request's _meta).
|
|
3051
|
+
* Without a WHK_MCP_TOOLS setting, a client that takes long tool lists
|
|
3052
|
+
* gets every group. Only the first name counts.
|
|
3053
|
+
*/
|
|
3054
|
+
clientIdentified(name) {
|
|
3055
|
+
if (this.clientKnown) return;
|
|
3056
|
+
this.clientKnown = true;
|
|
3057
|
+
this.clientName = name;
|
|
3058
|
+
if (this.selection.groups || !clientLoadsEverything(name)) return;
|
|
3059
|
+
if (this.client) this.load(TOOL_GROUP_NAMES);
|
|
3060
|
+
}
|
|
3061
|
+
/** Registers the groups not loaded yet; returns the tools each one added. */
|
|
3062
|
+
load(groups) {
|
|
3063
|
+
const client = this.client;
|
|
3064
|
+
const fresh = groups.filter((group2) => !this.loaded.has(group2));
|
|
3065
|
+
if (!client || fresh.length === 0) return {};
|
|
3066
|
+
const added = {};
|
|
3067
|
+
for (const group2 of fresh) added[group2] = [];
|
|
3068
|
+
const server = this.server;
|
|
3069
|
+
const recorder = {
|
|
3070
|
+
tool(name, description, shape, handler, annotations) {
|
|
3071
|
+
added[TOOL_CATALOG[name].group]?.push(name);
|
|
3072
|
+
return server.tool(name, description, shape, handler, annotations);
|
|
3073
|
+
}
|
|
3074
|
+
};
|
|
3075
|
+
this.handles.push(
|
|
3076
|
+
...registerTools(recorder, client, {
|
|
3077
|
+
groups: new Set(fresh),
|
|
3078
|
+
readOnly: this.selection.readOnly
|
|
3079
|
+
})
|
|
3080
|
+
);
|
|
3081
|
+
for (const group2 of fresh) this.loaded.add(group2);
|
|
3082
|
+
this.showEnableTools();
|
|
3083
|
+
return added;
|
|
3084
|
+
}
|
|
3085
|
+
/** (Re)registers enable_tools with the current groups, or drops it once all are loaded. */
|
|
3086
|
+
showEnableTools() {
|
|
3087
|
+
this.enableTools?.remove();
|
|
3088
|
+
this.enableTools = null;
|
|
3089
|
+
const left = EXTRA_GROUPS.filter((group2) => !this.loaded.has(group2));
|
|
3090
|
+
if (!this.client || left.length === 0) return;
|
|
3091
|
+
const lines = EXTRA_GROUPS.map(
|
|
3092
|
+
(group2) => `- ${group2}${this.loaded.has(group2) ? " (loaded)" : ""}: ${TOOL_GROUPS[group2]}`
|
|
3093
|
+
);
|
|
3094
|
+
const description = [
|
|
3095
|
+
"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.",
|
|
3096
|
+
"Groups:",
|
|
3097
|
+
...lines,
|
|
3098
|
+
this.selection.readOnly ? "This server is read-only (WHK_MCP_READ_ONLY): a group brings only its tools that change nothing." : null
|
|
3099
|
+
].filter(Boolean).join("\n");
|
|
3100
|
+
this.enableTools = this.server.tool(
|
|
3101
|
+
ENABLE_TOOLS,
|
|
3102
|
+
description,
|
|
3103
|
+
{
|
|
3104
|
+
groups: import_zod2.z.array(import_zod2.z.enum([...EXTRA_GROUPS, "all"])).min(1).describe('Groups to load, or ["all"]')
|
|
3105
|
+
},
|
|
3106
|
+
withErrorHandling(async ({ groups }) => {
|
|
3107
|
+
const wanted = groups.includes("all") ? [...TOOL_GROUP_NAMES] : groups;
|
|
3108
|
+
const already = wanted.filter((group2) => group2 !== "core" && this.loaded.has(group2));
|
|
3109
|
+
const added = this.load(wanted);
|
|
3110
|
+
const loadedNow = Object.keys(added).length > 0;
|
|
3111
|
+
return jsonContent({
|
|
3112
|
+
added,
|
|
3113
|
+
...already.length > 0 ? { alreadyLoaded: [...new Set(already)] } : {},
|
|
3114
|
+
loaded: this.loadedGroups(),
|
|
3115
|
+
...this.selection.readOnly ? { readOnly: true } : {},
|
|
3116
|
+
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."
|
|
3117
|
+
});
|
|
3118
|
+
}),
|
|
3119
|
+
ENABLE_TOOLS_ANNOTATIONS
|
|
3120
|
+
);
|
|
3121
|
+
}
|
|
3122
|
+
};
|
|
3123
|
+
function modernClientName(message) {
|
|
3124
|
+
if (typeof message !== "object" || message === null) return null;
|
|
3125
|
+
const params = message.params;
|
|
3126
|
+
const info = params?._meta?.[import_server.CLIENT_INFO_META_KEY];
|
|
3127
|
+
if (typeof info !== "object" || info === null) return null;
|
|
3128
|
+
const name = info.name;
|
|
3129
|
+
return typeof name === "string" ? name : void 0;
|
|
3130
|
+
}
|
|
3131
|
+
function watchClient(server, groups) {
|
|
3132
|
+
const low = server.server;
|
|
3133
|
+
const initialized = low.oninitialized;
|
|
3134
|
+
low.oninitialized = () => {
|
|
3135
|
+
initialized?.();
|
|
3136
|
+
groups.clientIdentified(low.getClientVersion()?.name);
|
|
3137
|
+
};
|
|
3138
|
+
const connect = server.connect.bind(server);
|
|
3139
|
+
server.connect = async (transport) => {
|
|
3140
|
+
const previous = transport.onmessage;
|
|
3141
|
+
transport.onmessage = (message, extra) => {
|
|
3142
|
+
const name = modernClientName(message);
|
|
3143
|
+
if (name !== null) groups.clientIdentified(name);
|
|
3144
|
+
previous?.call(transport, message, extra);
|
|
3145
|
+
};
|
|
3146
|
+
return connect(transport);
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
function localToolGroups(server, selection) {
|
|
3150
|
+
const groups = new LocalToolGroups(asToolServer(server), selection);
|
|
3151
|
+
watchClient(server, groups);
|
|
3152
|
+
return groups;
|
|
3153
|
+
}
|
|
3154
|
+
|
|
3155
|
+
// src/prompts.ts
|
|
3156
|
+
var import_zod3 = require("zod");
|
|
3157
|
+
function registerPrompts(server) {
|
|
3158
|
+
server.registerPrompt(
|
|
3159
|
+
"debug_webhook_delivery",
|
|
3160
|
+
{
|
|
3161
|
+
title: "Debug Webhook Delivery",
|
|
3162
|
+
description: "Guide an agent through diagnosing why webhook delivery is failing or missing.",
|
|
3163
|
+
argsSchema: import_zod3.z.object({
|
|
3164
|
+
provider: import_zod3.z.string().optional().describe("Webhook provider, if known"),
|
|
3165
|
+
endpointSlug: import_zod3.z.string().optional().describe("Hosted endpoint slug, if known"),
|
|
3166
|
+
targetUrl: import_zod3.z.string().optional().describe("Your app's receiving URL, if known")
|
|
3167
|
+
})
|
|
3168
|
+
},
|
|
3169
|
+
async ({ provider, endpointSlug, targetUrl }) => {
|
|
3170
|
+
const scope = [
|
|
3171
|
+
provider ? `Provider: ${provider}.` : null,
|
|
3172
|
+
endpointSlug ? `Endpoint slug: ${endpointSlug}.` : null,
|
|
3173
|
+
targetUrl ? `Target URL: ${targetUrl}.` : null
|
|
3174
|
+
].filter(Boolean).join(" ");
|
|
3175
|
+
return {
|
|
3176
|
+
description: "Diagnose a missing or broken webhook delivery.",
|
|
3177
|
+
messages: [
|
|
3178
|
+
{
|
|
3179
|
+
role: "user",
|
|
3180
|
+
content: {
|
|
3181
|
+
type: "text",
|
|
3182
|
+
text: [
|
|
3183
|
+
scope,
|
|
3184
|
+
"Diagnose webhook delivery step by step.",
|
|
3185
|
+
"Use list_endpoints or get_endpoint to confirm the endpoint and URL.",
|
|
3186
|
+
"Use list_requests, wait_for_request, or wait_for_requests to check whether anything arrived.",
|
|
3187
|
+
"If the provider is known, use preview_webhook or send_webhook to reproduce the webhook with realistic signing.",
|
|
3188
|
+
"Use verify_signature when a secret is available.",
|
|
3189
|
+
"Use compare_requests to compare retries or changed payloads.",
|
|
3190
|
+
"Conclude with the most likely cause and the next concrete fix."
|
|
3191
|
+
].filter(Boolean).join(" ")
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3194
|
+
]
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
);
|
|
3198
|
+
server.registerPrompt(
|
|
3199
|
+
"setup_provider_testing",
|
|
3200
|
+
{
|
|
3201
|
+
title: "Setup Provider Testing",
|
|
3202
|
+
description: "Guide an agent through setting up webhook testing for a provider.",
|
|
3203
|
+
argsSchema: import_zod3.z.object({
|
|
3204
|
+
provider: import_zod3.z.string().describe("Webhook provider to test"),
|
|
3205
|
+
targetUrl: import_zod3.z.string().optional().describe("Optional local or remote target URL")
|
|
3206
|
+
})
|
|
3207
|
+
},
|
|
3208
|
+
async ({ provider, targetUrl }) => {
|
|
3209
|
+
return {
|
|
3210
|
+
description: `Set up webhook testing for ${provider}.`,
|
|
3211
|
+
messages: [
|
|
3212
|
+
{
|
|
3213
|
+
role: "user",
|
|
3214
|
+
content: {
|
|
3215
|
+
type: "text",
|
|
3216
|
+
text: [
|
|
3217
|
+
`Set up webhook testing for ${provider}.`,
|
|
3218
|
+
"Use list_provider_templates to inspect supported templates and signing details first.",
|
|
3219
|
+
"Create an endpoint with create_endpoint, preferably ephemeral.",
|
|
3220
|
+
"If a target URL is provided, use preview_webhook before send_to so the request shape is visible.",
|
|
3221
|
+
targetUrl ? `Target URL: ${targetUrl}.` : null,
|
|
3222
|
+
"Send a realistic provider webhook, wait for capture, and verify the signature if a secret is available.",
|
|
3223
|
+
"Once real deliveries are captured, use generate_payload_schema to write the payload types for the handler.",
|
|
3224
|
+
"Return the endpoint URL, the exact tools used, and the next step for the developer."
|
|
3225
|
+
].filter(Boolean).join(" ")
|
|
3226
|
+
}
|
|
3227
|
+
}
|
|
3228
|
+
]
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3231
|
+
);
|
|
3232
|
+
server.registerPrompt(
|
|
3233
|
+
"compare_webhook_attempts",
|
|
3234
|
+
{
|
|
3235
|
+
title: "Compare Webhook Attempts",
|
|
3236
|
+
description: "Guide an agent through comparing two webhook deliveries or retries.",
|
|
3237
|
+
argsSchema: import_zod3.z.object({
|
|
3238
|
+
endpointSlug: import_zod3.z.string().optional().describe("Endpoint slug to inspect for recent attempts"),
|
|
3239
|
+
leftRequestId: import_zod3.z.string().optional().describe("First request ID, if already known"),
|
|
3240
|
+
rightRequestId: import_zod3.z.string().optional().describe("Second request ID, if already known")
|
|
3241
|
+
})
|
|
3242
|
+
},
|
|
3243
|
+
async ({ endpointSlug, leftRequestId, rightRequestId }) => {
|
|
3244
|
+
return {
|
|
3245
|
+
description: "Compare two webhook deliveries and explain the difference.",
|
|
3246
|
+
messages: [
|
|
3247
|
+
{
|
|
3248
|
+
role: "user",
|
|
3249
|
+
content: {
|
|
3250
|
+
type: "text",
|
|
3251
|
+
text: [
|
|
3252
|
+
endpointSlug ? `Endpoint slug: ${endpointSlug}.` : null,
|
|
3253
|
+
leftRequestId && rightRequestId ? `Compare request ${leftRequestId} against ${rightRequestId}.` : "Find the most relevant two webhook attempts first.",
|
|
3254
|
+
"Use compare_requests for the structured diff.",
|
|
3255
|
+
"If request IDs are not provided, use list_requests or the endpoint recent resource to find the last two attempts.",
|
|
3256
|
+
"Explain what changed in the body, headers, or timing, and whether the difference looks expected, retried, or broken."
|
|
3257
|
+
].filter(Boolean).join(" ")
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
]
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
);
|
|
3264
|
+
}
|
|
3265
|
+
|
|
3266
|
+
// src/resources.ts
|
|
3267
|
+
var import_server2 = require("@modelcontextprotocol/server");
|
|
3268
|
+
var ENDPOINTS_RESOURCE_URI = "webhooks://endpoints";
|
|
3269
|
+
var ENDPOINT_RECENT_TEMPLATE_URI = "webhooks://endpoint/{slug}/recent";
|
|
3270
|
+
var REQUEST_TEMPLATE_URI = "webhooks://request/{id}";
|
|
3271
|
+
var MAX_ENDPOINT_RESOURCE_ITEMS = 25;
|
|
3272
|
+
var RECENT_REQUEST_LIMIT = 10;
|
|
3273
|
+
function jsonResource(uri, value) {
|
|
3274
|
+
return {
|
|
3275
|
+
contents: [
|
|
3276
|
+
{
|
|
3277
|
+
uri,
|
|
3278
|
+
mimeType: "application/json",
|
|
3279
|
+
text: JSON.stringify(value, null, 2)
|
|
3280
|
+
}
|
|
3281
|
+
]
|
|
3282
|
+
};
|
|
3283
|
+
}
|
|
3284
|
+
function summarizeRequest(request) {
|
|
3285
|
+
return {
|
|
3286
|
+
id: request.id,
|
|
3287
|
+
method: request.method,
|
|
3288
|
+
path: request.path,
|
|
3289
|
+
receivedAt: request.receivedAt,
|
|
3290
|
+
contentType: request.contentType ?? null
|
|
3291
|
+
};
|
|
3292
|
+
}
|
|
3293
|
+
function variableToString(value, name) {
|
|
3294
|
+
if (typeof value === "string" && value.length > 0) {
|
|
3295
|
+
return value;
|
|
3296
|
+
}
|
|
3297
|
+
throw new Error(`Missing resource variable "${name}"`);
|
|
3298
|
+
}
|
|
3299
|
+
async function listEndpointSummaries(client) {
|
|
3300
|
+
const endpoints = (await client.endpoints.list()).slice().sort((left, right) => right.createdAt - left.createdAt);
|
|
3301
|
+
const truncated = endpoints.length > MAX_ENDPOINT_RESOURCE_ITEMS;
|
|
3302
|
+
const visible = endpoints.slice(0, MAX_ENDPOINT_RESOURCE_ITEMS);
|
|
3303
|
+
const summaries = await Promise.all(
|
|
3304
|
+
visible.map(async (endpoint) => {
|
|
3305
|
+
const recent = await client.requests.list(endpoint.slug, { limit: 1 });
|
|
3306
|
+
return {
|
|
3307
|
+
...endpoint,
|
|
3308
|
+
lastRequest: recent[0] ? summarizeRequest(recent[0]) : null
|
|
3309
|
+
};
|
|
3310
|
+
})
|
|
3311
|
+
);
|
|
3312
|
+
return {
|
|
3313
|
+
endpoints: summaries,
|
|
3314
|
+
total: endpoints.length,
|
|
3315
|
+
truncated
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
function registerResources(server, client) {
|
|
3319
|
+
server.registerResource(
|
|
3320
|
+
"endpoints-overview",
|
|
3321
|
+
ENDPOINTS_RESOURCE_URI,
|
|
3322
|
+
{
|
|
3323
|
+
title: "Endpoints Overview",
|
|
3324
|
+
description: "All endpoints with a summary of recent activity.",
|
|
3325
|
+
mimeType: "application/json"
|
|
3326
|
+
},
|
|
3327
|
+
async () => {
|
|
3328
|
+
return jsonResource(ENDPOINTS_RESOURCE_URI, await listEndpointSummaries(client));
|
|
3329
|
+
}
|
|
3330
|
+
);
|
|
3331
|
+
server.registerResource(
|
|
3332
|
+
"endpoint-recent-requests",
|
|
3333
|
+
new import_server2.ResourceTemplate(ENDPOINT_RECENT_TEMPLATE_URI, {
|
|
3334
|
+
list: async () => {
|
|
3335
|
+
const endpoints = await client.endpoints.list();
|
|
3336
|
+
return {
|
|
3337
|
+
resources: endpoints.map((endpoint) => ({
|
|
3338
|
+
uri: `webhooks://endpoint/${endpoint.slug}/recent`,
|
|
3339
|
+
name: `${endpoint.slug} recent requests`,
|
|
3340
|
+
description: `Recent requests for endpoint ${endpoint.slug}`,
|
|
3341
|
+
mimeType: "application/json"
|
|
3342
|
+
}))
|
|
3343
|
+
};
|
|
3344
|
+
},
|
|
3345
|
+
complete: {
|
|
3346
|
+
slug: async (value) => {
|
|
3347
|
+
const endpoints = await client.endpoints.list();
|
|
3348
|
+
return endpoints.map((endpoint) => endpoint.slug).filter((slug) => slug.startsWith(value)).slice(0, 20);
|
|
3349
|
+
}
|
|
3350
|
+
}
|
|
3351
|
+
}),
|
|
3352
|
+
{
|
|
3353
|
+
title: "Endpoint Recent Requests",
|
|
3354
|
+
description: "Last 10 captured requests for an endpoint.",
|
|
3355
|
+
mimeType: "application/json"
|
|
3356
|
+
},
|
|
3357
|
+
async (uri, variables) => {
|
|
3358
|
+
const slug = variableToString(variables.slug, "slug");
|
|
3359
|
+
const [endpoint, requests] = await Promise.all([
|
|
3360
|
+
client.endpoints.get(slug),
|
|
3361
|
+
client.requests.list(slug, { limit: RECENT_REQUEST_LIMIT })
|
|
3362
|
+
]);
|
|
3363
|
+
return jsonResource(uri.toString(), {
|
|
3364
|
+
endpoint,
|
|
3365
|
+
requests: requests.map(compactRequest)
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
);
|
|
3369
|
+
server.registerResource(
|
|
3370
|
+
"request-details",
|
|
3371
|
+
new import_server2.ResourceTemplate(REQUEST_TEMPLATE_URI, {
|
|
3372
|
+
list: void 0,
|
|
3373
|
+
complete: {}
|
|
3374
|
+
}),
|
|
3375
|
+
{
|
|
3376
|
+
title: "Request Details",
|
|
3377
|
+
description: "Full details for a captured request by ID.",
|
|
3378
|
+
mimeType: "application/json"
|
|
3379
|
+
},
|
|
3380
|
+
async (uri, variables) => {
|
|
3381
|
+
const id = variableToString(variables.id, "id");
|
|
3382
|
+
const request = await client.requests.get(id);
|
|
3383
|
+
return jsonResource(uri.toString(), compactRequest(request));
|
|
3384
|
+
}
|
|
3385
|
+
);
|
|
3386
|
+
}
|
|
3387
|
+
|
|
3388
|
+
// src/sandbox-tools.ts
|
|
3389
|
+
var import_zod4 = require("zod");
|
|
3390
|
+
var import_sdk2 = require("@webhooks-cc/sdk");
|
|
3391
|
+
|
|
3392
|
+
// src/version.ts
|
|
3393
|
+
var VERSION = true ? "2.0.0" : "0.0.0-dev";
|
|
3394
|
+
var MCP_USER_AGENT = `webhooks-cc-mcp/${VERSION}`;
|
|
3395
|
+
|
|
3396
|
+
// src/sandbox-tools.ts
|
|
3397
|
+
var SANDBOX = {
|
|
3398
|
+
maxEndpoints: 3,
|
|
3399
|
+
requestsPerEndpoint: 25,
|
|
3400
|
+
requests: 100,
|
|
3401
|
+
lifetimeHours: 24
|
|
3402
|
+
};
|
|
3403
|
+
var DEFAULT_WAIT_SECONDS = 45;
|
|
3404
|
+
var MAX_WAIT_SECONDS = 600;
|
|
3405
|
+
var DOCS_URL = "https://webhooks.cc/docs/agents";
|
|
3406
|
+
var ACCOUNT_ONLY_OPTIONS = [
|
|
3407
|
+
"mockResponse",
|
|
3001
3408
|
"responseRules",
|
|
3002
3409
|
"notificationUrl",
|
|
3003
3410
|
"signingProvider",
|
|
@@ -3100,6 +3507,8 @@ function registerSandboxTools(server, options = {}) {
|
|
|
3100
3507
|
options.clientName ?? "webhooks-cc-mcp",
|
|
3101
3508
|
options.credentialsFile
|
|
3102
3509
|
);
|
|
3510
|
+
const target = asToolServer(server);
|
|
3511
|
+
const groups = options.toolGroups ?? localToolGroups(server, toolSelection(options));
|
|
3103
3512
|
let handles = [];
|
|
3104
3513
|
let shown = 0;
|
|
3105
3514
|
const swap = (register) => {
|
|
@@ -3107,7 +3516,9 @@ function registerSandboxTools(server, options = {}) {
|
|
|
3107
3516
|
for (const handle of handles) handle.remove();
|
|
3108
3517
|
handles = register(id);
|
|
3109
3518
|
};
|
|
3110
|
-
const showSandbox = () => swap(
|
|
3519
|
+
const showSandbox = () => swap(
|
|
3520
|
+
() => sandboxToolSet(sandboxTarget(target, groups.selection.readOnly), session, showAccount, groups)
|
|
3521
|
+
);
|
|
3111
3522
|
const savedAccount = (id) => options.credentialsFile === false ? null : import_sdk2.WebhooksCC.fromSavedConnection({
|
|
3112
3523
|
baseUrl,
|
|
3113
3524
|
webhookUrl,
|
|
@@ -3120,18 +3531,54 @@ function registerSandboxTools(server, options = {}) {
|
|
|
3120
3531
|
})
|
|
3121
3532
|
});
|
|
3122
3533
|
function showAccount(client) {
|
|
3123
|
-
swap((id) =>
|
|
3534
|
+
swap((id) => [groups.show(savedAccount(id) ?? client)]);
|
|
3124
3535
|
options.onConnected?.(client);
|
|
3125
3536
|
}
|
|
3126
3537
|
const saved = savedAccount(1);
|
|
3127
3538
|
if (saved) {
|
|
3128
|
-
swap(() =>
|
|
3539
|
+
swap(() => [groups.show(saved)]);
|
|
3129
3540
|
options.onConnected?.(saved);
|
|
3130
3541
|
} else {
|
|
3131
3542
|
showSandbox();
|
|
3132
3543
|
}
|
|
3133
3544
|
}
|
|
3134
|
-
|
|
3545
|
+
var SANDBOX_ONLY_TOOLS = {
|
|
3546
|
+
// A new code each time.
|
|
3547
|
+
connect_account: {
|
|
3548
|
+
readOnlyHint: false,
|
|
3549
|
+
destructiveHint: false,
|
|
3550
|
+
idempotentHint: false,
|
|
3551
|
+
openWorldHint: false
|
|
3552
|
+
},
|
|
3553
|
+
// Changes this session's tools once the human has connected.
|
|
3554
|
+
wait_for_connection: {
|
|
3555
|
+
readOnlyHint: false,
|
|
3556
|
+
destructiveHint: false,
|
|
3557
|
+
idempotentHint: true,
|
|
3558
|
+
openWorldHint: false
|
|
3559
|
+
},
|
|
3560
|
+
about_sandbox: {
|
|
3561
|
+
readOnlyHint: true,
|
|
3562
|
+
destructiveHint: false,
|
|
3563
|
+
idempotentHint: true,
|
|
3564
|
+
openWorldHint: false
|
|
3565
|
+
}
|
|
3566
|
+
};
|
|
3567
|
+
var CONNECT_TOOLS = /* @__PURE__ */ new Set(["connect_account", "wait_for_connection"]);
|
|
3568
|
+
function sandboxTarget(target, readOnly) {
|
|
3569
|
+
return {
|
|
3570
|
+
tool(name, description, shape, handler) {
|
|
3571
|
+
const own = SANDBOX_ONLY_TOOLS[name];
|
|
3572
|
+
const annotations = own ? { title: toolTitle(name), ...own } : toolAnnotations(name);
|
|
3573
|
+
if (readOnly && !annotations.readOnlyHint && !CONNECT_TOOLS.has(name)) {
|
|
3574
|
+
return { remove() {
|
|
3575
|
+
} };
|
|
3576
|
+
}
|
|
3577
|
+
return target.tool(name, description, shape, handler, annotations);
|
|
3578
|
+
}
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
function sandboxToolSet(server, session, showAccount, groups) {
|
|
3135
3582
|
const tools = [];
|
|
3136
3583
|
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`;
|
|
3137
3584
|
const file = savedTo(session.credentialsFile);
|
|
@@ -3246,7 +3693,7 @@ function sandboxToolSet(server, session, showAccount) {
|
|
|
3246
3693
|
"connect_account",
|
|
3247
3694
|
"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.",
|
|
3248
3695
|
{
|
|
3249
|
-
email:
|
|
3696
|
+
email: import_zod4.z.string().email({ message: "Invalid email address" }).describe(
|
|
3250
3697
|
"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"
|
|
3251
3698
|
)
|
|
3252
3699
|
},
|
|
@@ -3299,7 +3746,7 @@ function sandboxToolSet(server, session, showAccount) {
|
|
|
3299
3746
|
"wait_for_connection",
|
|
3300
3747
|
`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.`,
|
|
3301
3748
|
{
|
|
3302
|
-
timeoutSeconds:
|
|
3749
|
+
timeoutSeconds: import_zod4.z.number().int().min(1).max(MAX_WAIT_SECONDS).optional().describe(`How long to wait, in seconds (default ${DEFAULT_WAIT_SECONDS})`)
|
|
3303
3750
|
},
|
|
3304
3751
|
withErrorHandling(async ({ timeoutSeconds }) => {
|
|
3305
3752
|
const pending = session.connect;
|
|
@@ -3342,7 +3789,7 @@ function sandboxToolSet(server, session, showAccount) {
|
|
|
3342
3789
|
return jsonContent({
|
|
3343
3790
|
connected: true,
|
|
3344
3791
|
email: pending.email,
|
|
3345
|
-
message: `Connected to the webhooks.cc account of ${pending.email}. The
|
|
3792
|
+
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."}`
|
|
3346
3793
|
});
|
|
3347
3794
|
})
|
|
3348
3795
|
)
|
|
@@ -3384,33 +3831,57 @@ function registerAgentRegistrationTools(server) {
|
|
|
3384
3831
|
}
|
|
3385
3832
|
|
|
3386
3833
|
// src/index.ts
|
|
3834
|
+
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).";
|
|
3387
3835
|
function createServer(options = {}) {
|
|
3388
3836
|
const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
|
|
3389
3837
|
const webhookUrl = options.webhookUrl ?? process.env.WHK_WEBHOOK_URL;
|
|
3390
3838
|
const baseUrl = options.baseUrl ?? process.env.WHK_BASE_URL;
|
|
3391
|
-
const server = new
|
|
3392
|
-
{ name: "webhooks-cc", version: VERSION },
|
|
3393
|
-
|
|
3394
|
-
|
|
3839
|
+
const server = new import_server3.McpServer(
|
|
3840
|
+
{ name: "webhooks-cc", title: "webhooks.cc", version: VERSION },
|
|
3841
|
+
{
|
|
3842
|
+
capabilities: { tools: { listChanged: true } },
|
|
3843
|
+
instructions: INSTRUCTIONS,
|
|
3844
|
+
// Connecting an account or loading groups changes many tools at once:
|
|
3845
|
+
// one list_changed, not dozens.
|
|
3846
|
+
debouncedNotificationMethods: ["notifications/tools/list_changed"]
|
|
3847
|
+
}
|
|
3395
3848
|
);
|
|
3849
|
+
const groups = localToolGroups(server, toolSelection(options));
|
|
3396
3850
|
if (!apiKey) {
|
|
3397
|
-
registerSandboxTools(server, {
|
|
3851
|
+
registerSandboxTools(server, {
|
|
3852
|
+
baseUrl,
|
|
3853
|
+
webhookUrl,
|
|
3854
|
+
credentialsFile: options.credentialsFile,
|
|
3855
|
+
toolGroups: groups
|
|
3856
|
+
});
|
|
3398
3857
|
registerPrompts(server);
|
|
3399
3858
|
return server;
|
|
3400
3859
|
}
|
|
3401
3860
|
const client = new import_sdk3.WebhooksCC({ apiKey, webhookUrl, baseUrl, userAgent: MCP_USER_AGENT });
|
|
3402
|
-
|
|
3861
|
+
groups.show(client);
|
|
3403
3862
|
registerPrompts(server);
|
|
3404
3863
|
registerResources(server, client);
|
|
3405
3864
|
return server;
|
|
3406
3865
|
}
|
|
3407
3866
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3408
3867
|
0 && (module.exports = {
|
|
3868
|
+
ENABLE_TOOLS,
|
|
3869
|
+
LocalToolGroups,
|
|
3409
3870
|
REMOTE_MAX_WAIT_MS,
|
|
3871
|
+
TOOL_CATALOG,
|
|
3872
|
+
TOOL_GROUPS,
|
|
3873
|
+
TOOL_GROUP_NAMES,
|
|
3874
|
+
asToolServer,
|
|
3875
|
+
clientLoadsEverything,
|
|
3410
3876
|
createServer,
|
|
3877
|
+
defaultToolGroups,
|
|
3878
|
+
localToolGroups,
|
|
3879
|
+
parseToolGroups,
|
|
3411
3880
|
registerAgentRegistrationTools,
|
|
3412
3881
|
registerPrompts,
|
|
3413
3882
|
registerResources,
|
|
3414
3883
|
registerSandboxTools,
|
|
3415
|
-
registerTools
|
|
3884
|
+
registerTools,
|
|
3885
|
+
toolAnnotations,
|
|
3886
|
+
toolSelection
|
|
3416
3887
|
});
|