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