@webhooks-cc/mcp 1.18.0 → 1.20.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
@@ -456,7 +456,7 @@ var import_zod3 = require("zod");
456
456
  var import_sdk2 = require("@webhooks-cc/sdk");
457
457
 
458
458
  // src/version.ts
459
- var VERSION = true ? "1.18.0" : "0.0.0-dev";
459
+ var VERSION = true ? "1.20.0" : "0.0.0-dev";
460
460
  var MCP_USER_AGENT = `webhooks-cc-mcp/${VERSION}`;
461
461
 
462
462
  // src/tools.ts
@@ -698,6 +698,13 @@ function payloadSchemaResult(requests, args2) {
698
698
  }
699
699
  return jsonContent(output);
700
700
  }
701
+ function keyText(key) {
702
+ return [
703
+ `Approved. The API key ${key.name ? `"${key.name}" ` : ""}is: ${key.key ?? "(not returned)"}`,
704
+ `It is shown only this once: store it now (for example as a CI secret), never in code.`,
705
+ key.expiresAt ? `It works until ${key.expiresAt}.` : ""
706
+ ].filter(Boolean).join("\n");
707
+ }
701
708
  function textContent(text) {
702
709
  return { content: [{ type: "text", text }] };
703
710
  }
@@ -1799,7 +1806,7 @@ function registerTools(target, client, options = {}) {
1799
1806
  );
1800
1807
  server.tool(
1801
1808
  "get_usage",
1802
- "Check current request usage, remaining quota, plan, and period end, plus the pooled quota of every subscribed team you belong to.",
1809
+ "Check current request usage, remaining quota, plan, and period end; parsed pages, preview data (whk share) and AI assists, each with when it resets; and the pooled quota of every subscribed team you belong to.",
1803
1810
  {},
1804
1811
  withErrorHandling(async () => {
1805
1812
  const [usageResult, teamsResult] = await Promise.allSettled([
@@ -2258,6 +2265,76 @@ function registerTools(target, client, options = {}) {
2258
2265
  return jsonContent(withShareHints(await client.shares.list(state ? { state } : {}), remote));
2259
2266
  })
2260
2267
  );
2268
+ server.tool(
2269
+ "list_api_keys",
2270
+ "List the account's API keys: id, name, prefix, when they were made, when they expire and when they were last used. The keys themselves are never listed.",
2271
+ {},
2272
+ withErrorHandling(async () => jsonContent(await client.apiKeys.list()))
2273
+ );
2274
+ server.tool(
2275
+ "create_api_key",
2276
+ "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).",
2277
+ {
2278
+ name: import_zod2.z.string().min(1).max(64).describe('A name that says where the key is used, such as "GitHub Actions"'),
2279
+ expiresInDays: import_zod2.z.number().int().min(1).max(365).optional().describe("How many days the key works, 1 to 365 (default 90)")
2280
+ },
2281
+ withErrorHandling(async ({ name, expiresInDays }) => {
2282
+ const pending = await client.apiKeys.create({ name, expiresInDays });
2283
+ if (!pending.id) {
2284
+ const key = await pending.wait();
2285
+ return textContent(keyText(key));
2286
+ }
2287
+ return textContent(
2288
+ [
2289
+ `Asked for approval: ${pending.summary}`,
2290
+ `Show the person this link: ${pending.url}`,
2291
+ `The page shows the code ${pending.code}; ask them to approve only if it matches.`,
2292
+ `Then call wait_for_approval with id ${pending.id}. The request expires at ${pending.expiresAt}.`
2293
+ ].join("\n")
2294
+ );
2295
+ })
2296
+ );
2297
+ server.tool(
2298
+ "wait_for_approval",
2299
+ "Wait for the account's owner to answer an approval (from create_api_key) and collect what it grants, such as the new API key. If they have not answered yet, call it again.",
2300
+ {
2301
+ id: import_zod2.z.string().describe("The approval id"),
2302
+ timeout: durationOrTimestampSchema.default("45s").describe('How long to wait for the person, for example "45s"')
2303
+ },
2304
+ withErrorHandling(async ({ id, timeout }) => {
2305
+ const ms = durationMs(timeout) ?? 45e3;
2306
+ try {
2307
+ const result = await client.approvals.wait(id, { timeout: ms });
2308
+ return textContent(keyText(result));
2309
+ } catch (error) {
2310
+ if (error instanceof import_sdk.ApprovalDeniedError) {
2311
+ return textContent(
2312
+ "The person refused it on the approval page. Do not ask again unless they tell you to."
2313
+ );
2314
+ }
2315
+ if (error instanceof import_sdk.ApprovalExpiredError) {
2316
+ return textContent("The approval expired. Ask again if the person still wants it.");
2317
+ }
2318
+ if (error instanceof import_sdk.TimeoutError) {
2319
+ return textContent(
2320
+ `Not answered yet. Remind the person of the link if needed, and call wait_for_approval with id ${id} again.`
2321
+ );
2322
+ }
2323
+ throw error;
2324
+ }
2325
+ })
2326
+ );
2327
+ server.tool(
2328
+ "revoke_api_key",
2329
+ "Revoke one of the account's API keys by its id (from list_api_keys). Anything using it stops working at once.",
2330
+ {
2331
+ id: import_zod2.z.string().describe("The key's id, from list_api_keys")
2332
+ },
2333
+ withErrorHandling(async ({ id }) => {
2334
+ await client.apiKeys.revoke(id);
2335
+ return textContent(`Revoked the API key ${id}, if the account had it.`);
2336
+ })
2337
+ );
2261
2338
  server.tool(
2262
2339
  "stop_share",
2263
2340
  "Stop a public preview or live tunnel by its id (from list_shares). The CLI running it is told why and exits.",
@@ -12,6 +12,8 @@ import {
12
12
  MAX_CONDITION_NAME_LEN,
13
13
  MAX_CONDITION_PATH_LEN,
14
14
  MAX_RULE_NAME_LEN,
15
+ ApprovalDeniedError,
16
+ ApprovalExpiredError,
15
17
  NotFoundError,
16
18
  RateLimitError,
17
19
  TEMPLATE_METADATA,
@@ -476,6 +478,13 @@ function payloadSchemaResult(requests, args) {
476
478
  }
477
479
  return jsonContent(output);
478
480
  }
481
+ function keyText(key) {
482
+ return [
483
+ `Approved. The API key ${key.name ? `"${key.name}" ` : ""}is: ${key.key ?? "(not returned)"}`,
484
+ `It is shown only this once: store it now (for example as a CI secret), never in code.`,
485
+ key.expiresAt ? `It works until ${key.expiresAt}.` : ""
486
+ ].filter(Boolean).join("\n");
487
+ }
479
488
  function textContent(text) {
480
489
  return { content: [{ type: "text", text }] };
481
490
  }
@@ -1577,7 +1586,7 @@ function registerTools(target, client, options = {}) {
1577
1586
  );
1578
1587
  server.tool(
1579
1588
  "get_usage",
1580
- "Check current request usage, remaining quota, plan, and period end, plus the pooled quota of every subscribed team you belong to.",
1589
+ "Check current request usage, remaining quota, plan, and period end; parsed pages, preview data (whk share) and AI assists, each with when it resets; and the pooled quota of every subscribed team you belong to.",
1581
1590
  {},
1582
1591
  withErrorHandling(async () => {
1583
1592
  const [usageResult, teamsResult] = await Promise.allSettled([
@@ -2036,6 +2045,76 @@ function registerTools(target, client, options = {}) {
2036
2045
  return jsonContent(withShareHints(await client.shares.list(state ? { state } : {}), remote));
2037
2046
  })
2038
2047
  );
2048
+ server.tool(
2049
+ "list_api_keys",
2050
+ "List the account's API keys: id, name, prefix, when they were made, when they expire and when they were last used. The keys themselves are never listed.",
2051
+ {},
2052
+ withErrorHandling(async () => jsonContent(await client.apiKeys.list()))
2053
+ );
2054
+ server.tool(
2055
+ "create_api_key",
2056
+ "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).",
2057
+ {
2058
+ name: z.string().min(1).max(64).describe('A name that says where the key is used, such as "GitHub Actions"'),
2059
+ expiresInDays: z.number().int().min(1).max(365).optional().describe("How many days the key works, 1 to 365 (default 90)")
2060
+ },
2061
+ withErrorHandling(async ({ name, expiresInDays }) => {
2062
+ const pending = await client.apiKeys.create({ name, expiresInDays });
2063
+ if (!pending.id) {
2064
+ const key = await pending.wait();
2065
+ return textContent(keyText(key));
2066
+ }
2067
+ return textContent(
2068
+ [
2069
+ `Asked for approval: ${pending.summary}`,
2070
+ `Show the person this link: ${pending.url}`,
2071
+ `The page shows the code ${pending.code}; ask them to approve only if it matches.`,
2072
+ `Then call wait_for_approval with id ${pending.id}. The request expires at ${pending.expiresAt}.`
2073
+ ].join("\n")
2074
+ );
2075
+ })
2076
+ );
2077
+ server.tool(
2078
+ "wait_for_approval",
2079
+ "Wait for the account's owner to answer an approval (from create_api_key) and collect what it grants, such as the new API key. If they have not answered yet, call it again.",
2080
+ {
2081
+ id: z.string().describe("The approval id"),
2082
+ timeout: durationOrTimestampSchema.default("45s").describe('How long to wait for the person, for example "45s"')
2083
+ },
2084
+ withErrorHandling(async ({ id, timeout }) => {
2085
+ const ms = durationMs(timeout) ?? 45e3;
2086
+ try {
2087
+ const result = await client.approvals.wait(id, { timeout: ms });
2088
+ return textContent(keyText(result));
2089
+ } catch (error) {
2090
+ if (error instanceof ApprovalDeniedError) {
2091
+ return textContent(
2092
+ "The person refused it on the approval page. Do not ask again unless they tell you to."
2093
+ );
2094
+ }
2095
+ if (error instanceof ApprovalExpiredError) {
2096
+ return textContent("The approval expired. Ask again if the person still wants it.");
2097
+ }
2098
+ if (error instanceof TimeoutError) {
2099
+ return textContent(
2100
+ `Not answered yet. Remind the person of the link if needed, and call wait_for_approval with id ${id} again.`
2101
+ );
2102
+ }
2103
+ throw error;
2104
+ }
2105
+ })
2106
+ );
2107
+ server.tool(
2108
+ "revoke_api_key",
2109
+ "Revoke one of the account's API keys by its id (from list_api_keys). Anything using it stops working at once.",
2110
+ {
2111
+ id: z.string().describe("The key's id, from list_api_keys")
2112
+ },
2113
+ withErrorHandling(async ({ id }) => {
2114
+ await client.apiKeys.revoke(id);
2115
+ return textContent(`Revoked the API key ${id}, if the account had it.`);
2116
+ })
2117
+ );
2039
2118
  server.tool(
2040
2119
  "stop_share",
2041
2120
  "Stop a public preview or live tunnel by its id (from list_shares). The CLI running it is told why and exits.",
@@ -2141,6 +2220,7 @@ export {
2141
2220
  textContent,
2142
2221
  jsonContent,
2143
2222
  withErrorHandling,
2223
+ LOCAL_ONLY_TOOLS,
2144
2224
  REMOTE_MAX_WAIT_MS,
2145
2225
  REMOTE_MIN_POLL_MS,
2146
2226
  shareHints,
package/dist/index.js CHANGED
@@ -480,7 +480,7 @@ var import_zod3 = require("zod");
480
480
  var import_sdk2 = require("@webhooks-cc/sdk");
481
481
 
482
482
  // src/version.ts
483
- var VERSION = true ? "1.18.0" : "0.0.0-dev";
483
+ var VERSION = true ? "1.20.0" : "0.0.0-dev";
484
484
  var MCP_USER_AGENT = `webhooks-cc-mcp/${VERSION}`;
485
485
 
486
486
  // src/tools.ts
@@ -722,6 +722,13 @@ function payloadSchemaResult(requests, args) {
722
722
  }
723
723
  return jsonContent(output);
724
724
  }
725
+ function keyText(key) {
726
+ return [
727
+ `Approved. The API key ${key.name ? `"${key.name}" ` : ""}is: ${key.key ?? "(not returned)"}`,
728
+ `It is shown only this once: store it now (for example as a CI secret), never in code.`,
729
+ key.expiresAt ? `It works until ${key.expiresAt}.` : ""
730
+ ].filter(Boolean).join("\n");
731
+ }
725
732
  function textContent(text) {
726
733
  return { content: [{ type: "text", text }] };
727
734
  }
@@ -1823,7 +1830,7 @@ function registerTools(target, client, options = {}) {
1823
1830
  );
1824
1831
  server.tool(
1825
1832
  "get_usage",
1826
- "Check current request usage, remaining quota, plan, and period end, plus the pooled quota of every subscribed team you belong to.",
1833
+ "Check current request usage, remaining quota, plan, and period end; parsed pages, preview data (whk share) and AI assists, each with when it resets; and the pooled quota of every subscribed team you belong to.",
1827
1834
  {},
1828
1835
  withErrorHandling(async () => {
1829
1836
  const [usageResult, teamsResult] = await Promise.allSettled([
@@ -2282,6 +2289,76 @@ function registerTools(target, client, options = {}) {
2282
2289
  return jsonContent(withShareHints(await client.shares.list(state ? { state } : {}), remote));
2283
2290
  })
2284
2291
  );
2292
+ server.tool(
2293
+ "list_api_keys",
2294
+ "List the account's API keys: id, name, prefix, when they were made, when they expire and when they were last used. The keys themselves are never listed.",
2295
+ {},
2296
+ withErrorHandling(async () => jsonContent(await client.apiKeys.list()))
2297
+ );
2298
+ server.tool(
2299
+ "create_api_key",
2300
+ "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).",
2301
+ {
2302
+ name: import_zod2.z.string().min(1).max(64).describe('A name that says where the key is used, such as "GitHub Actions"'),
2303
+ expiresInDays: import_zod2.z.number().int().min(1).max(365).optional().describe("How many days the key works, 1 to 365 (default 90)")
2304
+ },
2305
+ withErrorHandling(async ({ name, expiresInDays }) => {
2306
+ const pending = await client.apiKeys.create({ name, expiresInDays });
2307
+ if (!pending.id) {
2308
+ const key = await pending.wait();
2309
+ return textContent(keyText(key));
2310
+ }
2311
+ return textContent(
2312
+ [
2313
+ `Asked for approval: ${pending.summary}`,
2314
+ `Show the person this link: ${pending.url}`,
2315
+ `The page shows the code ${pending.code}; ask them to approve only if it matches.`,
2316
+ `Then call wait_for_approval with id ${pending.id}. The request expires at ${pending.expiresAt}.`
2317
+ ].join("\n")
2318
+ );
2319
+ })
2320
+ );
2321
+ server.tool(
2322
+ "wait_for_approval",
2323
+ "Wait for the account's owner to answer an approval (from create_api_key) and collect what it grants, such as the new API key. If they have not answered yet, call it again.",
2324
+ {
2325
+ id: import_zod2.z.string().describe("The approval id"),
2326
+ timeout: durationOrTimestampSchema.default("45s").describe('How long to wait for the person, for example "45s"')
2327
+ },
2328
+ withErrorHandling(async ({ id, timeout }) => {
2329
+ const ms = durationMs(timeout) ?? 45e3;
2330
+ try {
2331
+ const result = await client.approvals.wait(id, { timeout: ms });
2332
+ return textContent(keyText(result));
2333
+ } catch (error) {
2334
+ if (error instanceof import_sdk.ApprovalDeniedError) {
2335
+ return textContent(
2336
+ "The person refused it on the approval page. Do not ask again unless they tell you to."
2337
+ );
2338
+ }
2339
+ if (error instanceof import_sdk.ApprovalExpiredError) {
2340
+ return textContent("The approval expired. Ask again if the person still wants it.");
2341
+ }
2342
+ if (error instanceof import_sdk.TimeoutError) {
2343
+ return textContent(
2344
+ `Not answered yet. Remind the person of the link if needed, and call wait_for_approval with id ${id} again.`
2345
+ );
2346
+ }
2347
+ throw error;
2348
+ }
2349
+ })
2350
+ );
2351
+ server.tool(
2352
+ "revoke_api_key",
2353
+ "Revoke one of the account's API keys by its id (from list_api_keys). Anything using it stops working at once.",
2354
+ {
2355
+ id: import_zod2.z.string().describe("The key's id, from list_api_keys")
2356
+ },
2357
+ withErrorHandling(async ({ id }) => {
2358
+ await client.apiKeys.revoke(id);
2359
+ return textContent(`Revoked the API key ${id}, if the account had it.`);
2360
+ })
2361
+ );
2285
2362
  server.tool(
2286
2363
  "stop_share",
2287
2364
  "Stop a public preview or live tunnel by its id (from list_shares). The CLI running it is told why and exits.",
package/dist/index.mjs CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  registerTools,
14
14
  waitForRequestShape,
15
15
  withErrorHandling
16
- } from "./chunk-GTEE7L2Y.mjs";
16
+ } from "./chunk-KLP2TU7I.mjs";
17
17
 
18
18
  // src/index.ts
19
19
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -262,7 +262,7 @@ import {
262
262
  } from "@webhooks-cc/sdk";
263
263
 
264
264
  // src/version.ts
265
- var VERSION = true ? "1.18.0" : "0.0.0-dev";
265
+ var VERSION = true ? "1.20.0" : "0.0.0-dev";
266
266
  var MCP_USER_AGENT = `webhooks-cc-mcp/${VERSION}`;
267
267
 
268
268
  // src/sandbox-tools.ts
package/dist/tools.d.mts CHANGED
@@ -135,7 +135,6 @@ type ToolResult = {
135
135
  content: TextContent[];
136
136
  isError?: boolean;
137
137
  };
138
- /** Create a text content response for MCP tools. */
139
138
  declare function textContent(text: string): ToolResult;
140
139
  declare function jsonContent(value: unknown): ToolResult;
141
140
  /** Wrap a tool handler with error handling that returns structured MCP errors. */
@@ -162,6 +161,11 @@ interface RegisterToolsOptions {
162
161
  */
163
162
  remote?: boolean;
164
163
  }
164
+ /**
165
+ * Tools that only make sense next to the caller: they post to a URL of the
166
+ * caller's choosing, or (share_port) run the whk CLI on the caller's machine.
167
+ */
168
+ declare const LOCAL_ONLY_TOOLS: Set<string>;
165
169
  /** Longest wait on the hosted server; Cloudflare ends a request after 100 seconds. */
166
170
  declare const REMOTE_MAX_WAIT_MS = 50000;
167
171
  /** Shortest poll interval on the hosted server: each poll is an API request. */
@@ -192,5 +196,5 @@ declare function withShareHints(list: TunnelShareList, remote?: boolean): Record
192
196
  */
193
197
  declare function registerTools(target: ToolServer, client: WebhooksCC, options?: RegisterToolsOptions): RegisteredToolHandle[];
194
198
 
195
- export { REMOTE_MAX_WAIT_MS, REMOTE_MIN_POLL_MS, createEndpointShape, deleteEndpointShape, getEndpointShape, getRequestShape, jsonContent, listEndpointsShape, listRequestsShape, payloadSchemaResult, payloadSchemaShape, registerTools, shareHints, textContent, toolWords, waitForRequestShape, withErrorHandling, withShareHints };
199
+ export { LOCAL_ONLY_TOOLS, REMOTE_MAX_WAIT_MS, REMOTE_MIN_POLL_MS, createEndpointShape, deleteEndpointShape, getEndpointShape, getRequestShape, jsonContent, listEndpointsShape, listRequestsShape, payloadSchemaResult, payloadSchemaShape, registerTools, shareHints, textContent, toolWords, waitForRequestShape, withErrorHandling, withShareHints };
196
200
  export type { RegisterToolsOptions, RegisteredToolHandle, ToolResult, ToolServer };
package/dist/tools.d.ts CHANGED
@@ -135,7 +135,6 @@ type ToolResult = {
135
135
  content: TextContent[];
136
136
  isError?: boolean;
137
137
  };
138
- /** Create a text content response for MCP tools. */
139
138
  declare function textContent(text: string): ToolResult;
140
139
  declare function jsonContent(value: unknown): ToolResult;
141
140
  /** Wrap a tool handler with error handling that returns structured MCP errors. */
@@ -162,6 +161,11 @@ interface RegisterToolsOptions {
162
161
  */
163
162
  remote?: boolean;
164
163
  }
164
+ /**
165
+ * Tools that only make sense next to the caller: they post to a URL of the
166
+ * caller's choosing, or (share_port) run the whk CLI on the caller's machine.
167
+ */
168
+ declare const LOCAL_ONLY_TOOLS: Set<string>;
165
169
  /** Longest wait on the hosted server; Cloudflare ends a request after 100 seconds. */
166
170
  declare const REMOTE_MAX_WAIT_MS = 50000;
167
171
  /** Shortest poll interval on the hosted server: each poll is an API request. */
@@ -192,5 +196,5 @@ declare function withShareHints(list: TunnelShareList, remote?: boolean): Record
192
196
  */
193
197
  declare function registerTools(target: ToolServer, client: WebhooksCC, options?: RegisterToolsOptions): RegisteredToolHandle[];
194
198
 
195
- export { REMOTE_MAX_WAIT_MS, REMOTE_MIN_POLL_MS, createEndpointShape, deleteEndpointShape, getEndpointShape, getRequestShape, jsonContent, listEndpointsShape, listRequestsShape, payloadSchemaResult, payloadSchemaShape, registerTools, shareHints, textContent, toolWords, waitForRequestShape, withErrorHandling, withShareHints };
199
+ export { LOCAL_ONLY_TOOLS, REMOTE_MAX_WAIT_MS, REMOTE_MIN_POLL_MS, createEndpointShape, deleteEndpointShape, getEndpointShape, getRequestShape, jsonContent, listEndpointsShape, listRequestsShape, payloadSchemaResult, payloadSchemaShape, registerTools, shareHints, textContent, toolWords, waitForRequestShape, withErrorHandling, withShareHints };
196
200
  export type { RegisterToolsOptions, RegisteredToolHandle, ToolResult, ToolServer };
package/dist/tools.js CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/tools.ts
21
21
  var tools_exports = {};
22
22
  __export(tools_exports, {
23
+ LOCAL_ONLY_TOOLS: () => LOCAL_ONLY_TOOLS,
23
24
  REMOTE_MAX_WAIT_MS: () => REMOTE_MAX_WAIT_MS,
24
25
  REMOTE_MIN_POLL_MS: () => REMOTE_MIN_POLL_MS,
25
26
  createEndpointShape: () => createEndpointShape,
@@ -488,6 +489,13 @@ function payloadSchemaResult(requests, args) {
488
489
  }
489
490
  return jsonContent(output);
490
491
  }
492
+ function keyText(key) {
493
+ return [
494
+ `Approved. The API key ${key.name ? `"${key.name}" ` : ""}is: ${key.key ?? "(not returned)"}`,
495
+ `It is shown only this once: store it now (for example as a CI secret), never in code.`,
496
+ key.expiresAt ? `It works until ${key.expiresAt}.` : ""
497
+ ].filter(Boolean).join("\n");
498
+ }
491
499
  function textContent(text) {
492
500
  return { content: [{ type: "text", text }] };
493
501
  }
@@ -1589,7 +1597,7 @@ function registerTools(target, client, options = {}) {
1589
1597
  );
1590
1598
  server.tool(
1591
1599
  "get_usage",
1592
- "Check current request usage, remaining quota, plan, and period end, plus the pooled quota of every subscribed team you belong to.",
1600
+ "Check current request usage, remaining quota, plan, and period end; parsed pages, preview data (whk share) and AI assists, each with when it resets; and the pooled quota of every subscribed team you belong to.",
1593
1601
  {},
1594
1602
  withErrorHandling(async () => {
1595
1603
  const [usageResult, teamsResult] = await Promise.allSettled([
@@ -2048,6 +2056,76 @@ function registerTools(target, client, options = {}) {
2048
2056
  return jsonContent(withShareHints(await client.shares.list(state ? { state } : {}), remote));
2049
2057
  })
2050
2058
  );
2059
+ server.tool(
2060
+ "list_api_keys",
2061
+ "List the account's API keys: id, name, prefix, when they were made, when they expire and when they were last used. The keys themselves are never listed.",
2062
+ {},
2063
+ withErrorHandling(async () => jsonContent(await client.apiKeys.list()))
2064
+ );
2065
+ server.tool(
2066
+ "create_api_key",
2067
+ "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).",
2068
+ {
2069
+ name: import_zod.z.string().min(1).max(64).describe('A name that says where the key is used, such as "GitHub Actions"'),
2070
+ expiresInDays: import_zod.z.number().int().min(1).max(365).optional().describe("How many days the key works, 1 to 365 (default 90)")
2071
+ },
2072
+ withErrorHandling(async ({ name, expiresInDays }) => {
2073
+ const pending = await client.apiKeys.create({ name, expiresInDays });
2074
+ if (!pending.id) {
2075
+ const key = await pending.wait();
2076
+ return textContent(keyText(key));
2077
+ }
2078
+ return textContent(
2079
+ [
2080
+ `Asked for approval: ${pending.summary}`,
2081
+ `Show the person this link: ${pending.url}`,
2082
+ `The page shows the code ${pending.code}; ask them to approve only if it matches.`,
2083
+ `Then call wait_for_approval with id ${pending.id}. The request expires at ${pending.expiresAt}.`
2084
+ ].join("\n")
2085
+ );
2086
+ })
2087
+ );
2088
+ server.tool(
2089
+ "wait_for_approval",
2090
+ "Wait for the account's owner to answer an approval (from create_api_key) and collect what it grants, such as the new API key. If they have not answered yet, call it again.",
2091
+ {
2092
+ id: import_zod.z.string().describe("The approval id"),
2093
+ timeout: durationOrTimestampSchema.default("45s").describe('How long to wait for the person, for example "45s"')
2094
+ },
2095
+ withErrorHandling(async ({ id, timeout }) => {
2096
+ const ms = durationMs(timeout) ?? 45e3;
2097
+ try {
2098
+ const result = await client.approvals.wait(id, { timeout: ms });
2099
+ return textContent(keyText(result));
2100
+ } catch (error) {
2101
+ if (error instanceof import_sdk.ApprovalDeniedError) {
2102
+ return textContent(
2103
+ "The person refused it on the approval page. Do not ask again unless they tell you to."
2104
+ );
2105
+ }
2106
+ if (error instanceof import_sdk.ApprovalExpiredError) {
2107
+ return textContent("The approval expired. Ask again if the person still wants it.");
2108
+ }
2109
+ if (error instanceof import_sdk.TimeoutError) {
2110
+ return textContent(
2111
+ `Not answered yet. Remind the person of the link if needed, and call wait_for_approval with id ${id} again.`
2112
+ );
2113
+ }
2114
+ throw error;
2115
+ }
2116
+ })
2117
+ );
2118
+ server.tool(
2119
+ "revoke_api_key",
2120
+ "Revoke one of the account's API keys by its id (from list_api_keys). Anything using it stops working at once.",
2121
+ {
2122
+ id: import_zod.z.string().describe("The key's id, from list_api_keys")
2123
+ },
2124
+ withErrorHandling(async ({ id }) => {
2125
+ await client.apiKeys.revoke(id);
2126
+ return textContent(`Revoked the API key ${id}, if the account had it.`);
2127
+ })
2128
+ );
2051
2129
  server.tool(
2052
2130
  "stop_share",
2053
2131
  "Stop a public preview or live tunnel by its id (from list_shares). The CLI running it is told why and exits.",
@@ -2140,6 +2218,7 @@ function registerTools(target, client, options = {}) {
2140
2218
  }
2141
2219
  // Annotate the CommonJS export names for ESM import in node:
2142
2220
  0 && (module.exports = {
2221
+ LOCAL_ONLY_TOOLS,
2143
2222
  REMOTE_MAX_WAIT_MS,
2144
2223
  REMOTE_MIN_POLL_MS,
2145
2224
  createEndpointShape,
package/dist/tools.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ LOCAL_ONLY_TOOLS,
2
3
  REMOTE_MAX_WAIT_MS,
3
4
  REMOTE_MIN_POLL_MS,
4
5
  createEndpointShape,
@@ -17,8 +18,9 @@ import {
17
18
  waitForRequestShape,
18
19
  withErrorHandling,
19
20
  withShareHints
20
- } from "./chunk-GTEE7L2Y.mjs";
21
+ } from "./chunk-KLP2TU7I.mjs";
21
22
  export {
23
+ LOCAL_ONLY_TOOLS,
22
24
  REMOTE_MAX_WAIT_MS,
23
25
  REMOTE_MIN_POLL_MS,
24
26
  createEndpointShape,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webhooks-cc/mcp",
3
- "version": "1.18.0",
3
+ "version": "1.20.0",
4
4
  "mcpName": "io.github.kroqdotdev/webhooks-cc",
5
5
  "description": "MCP server for webhooks.cc — AI agent integration for webhook testing",
6
6
  "main": "dist/index.js",
@@ -44,7 +44,7 @@
44
44
  "dependencies": {
45
45
  "@modelcontextprotocol/sdk": "^1.32.2",
46
46
  "zod": "^4.6.7",
47
- "@webhooks-cc/sdk": "1.22.0"
47
+ "@webhooks-cc/sdk": "1.24.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/node": "^26.6.6",