@webhooks-cc/mcp 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mads Sauer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # @webhooks-cc/mcp
2
+
3
+ MCP server for [webhooks.cc](https://webhooks.cc). Connects AI coding agents to webhook endpoints — create, inspect, test, and replay webhooks through natural language.
4
+
5
+ Works with Claude Code, Cursor, VS Code, Codex, Windsurf, and Claude Desktop.
6
+
7
+ ## Install
8
+
9
+ ### One-click
10
+
11
+ - **Cursor**: [Add to Cursor](https://cursor.com/en/install-mcp?name=webhooks-cc&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkB3ZWJob29rcy1jYy9tY3AiXSwiZW52Ijp7IldIS19BUElfS0VZIjoid2hjY18uLi4ifX0=) (paste your API key after install)
12
+ - **VS Code**: [Add to VS Code](https://insiders.vscode.dev/redirect/mcp/install?name=webhooks-cc&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40webhooks-cc%2Fmcp%22%5D%2C%22env%22%3A%7B%22WHK_API_KEY%22%3A%22%24%7Binput%3Awhk_api_key%7D%22%7D%7D&inputs=%5B%7B%22id%22%3A%22whk_api_key%22%2C%22type%22%3A%22promptString%22%2C%22description%22%3A%22webhooks.cc%20API%20key%20%28get%20yours%20at%20webhooks.cc%2Faccount%29%22%2C%22password%22%3Atrue%7D%5D) (prompts for your key)
13
+
14
+ ### CLI
15
+
16
+ ```bash
17
+ # Claude Code
18
+ claude mcp add -s user --transport stdio webhooks-cc -e WHK_API_KEY=whcc_... -- npx -y @webhooks-cc/mcp
19
+
20
+ # Cursor
21
+ npx @webhooks-cc/mcp setup cursor --api-key whcc_...
22
+
23
+ # VS Code
24
+ npx @webhooks-cc/mcp setup vscode --api-key whcc_...
25
+
26
+ # OpenAI Codex
27
+ codex mcp add webhooks-cc -e WHK_API_KEY=whcc_... -- npx -y @webhooks-cc/mcp
28
+
29
+ # Windsurf
30
+ npx @webhooks-cc/mcp setup windsurf --api-key whcc_...
31
+
32
+ # Claude Desktop
33
+ npx @webhooks-cc/mcp setup claude-desktop --api-key whcc_...
34
+ ```
35
+
36
+ ### Manual JSON config
37
+
38
+ For any tool that reads an MCP config file:
39
+
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "webhooks-cc": {
44
+ "command": "npx",
45
+ "args": ["-y", "@webhooks-cc/mcp"],
46
+ "env": {
47
+ "WHK_API_KEY": "whcc_..."
48
+ }
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ Get your API key at [webhooks.cc/account](https://webhooks.cc/account).
55
+
56
+ ## Tools
57
+
58
+ The MCP server exposes 11 tools:
59
+
60
+ | Tool | Description |
61
+ | ------------------ | ----------------------------------------- |
62
+ | `create_endpoint` | Create a new webhook endpoint |
63
+ | `list_endpoints` | List all your endpoints |
64
+ | `get_endpoint` | Get details for an endpoint by slug |
65
+ | `update_endpoint` | Update an endpoint name or mock response |
66
+ | `delete_endpoint` | Delete an endpoint and its requests |
67
+ | `list_requests` | List captured requests for an endpoint |
68
+ | `get_request` | Get full details of a captured request |
69
+ | `send_webhook` | Send a test webhook to an endpoint |
70
+ | `wait_for_request` | Wait for an incoming request (polling) |
71
+ | `replay_request` | Replay a captured request to a target URL |
72
+ | `describe` | Describe all available SDK operations |
73
+
74
+ ## Example conversation
75
+
76
+ ```
77
+ You: "Create a webhook endpoint for testing Stripe"
78
+ Agent: Created endpoint "stripe-test" at https://go.webhooks.cc/w/abc123
79
+
80
+ You: "Set it to return 201 with {"received": true}"
81
+ Agent: Updated mock response for stripe-test
82
+
83
+ You: "Send a test POST with a checkout.session.completed event"
84
+ Agent: Sent POST to stripe-test with event payload
85
+
86
+ You: "Show me what was captured"
87
+ Agent: 1 request captured:
88
+ POST /w/abc123 — {"event": "checkout.session.completed", ...}
89
+
90
+ You: "Replay that to my local server"
91
+ Agent: Replayed to http://localhost:3000/webhooks — got 200 OK
92
+ ```
93
+
94
+ ## Documentation
95
+
96
+ Full setup guide with interactive API key filling: [webhooks.cc/docs/mcp](https://webhooks.cc/docs/mcp)
97
+
98
+ ## License
99
+
100
+ MIT
@@ -0,0 +1,469 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // bin/mcp.ts
5
+ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+
7
+ // src/index.ts
8
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
9
+ var import_sdk = require("@webhooks-cc/sdk");
10
+
11
+ // src/tools.ts
12
+ var import_zod = require("zod");
13
+ var MAX_BODY_SIZE = 32768;
14
+ function textContent(text) {
15
+ return { content: [{ type: "text", text }] };
16
+ }
17
+ async function readBodyTruncated(response, limit = MAX_BODY_SIZE) {
18
+ const text = await response.text();
19
+ if (text.length <= limit) return text;
20
+ return text.slice(0, limit) + `
21
+ ... [truncated, ${text.length} chars total]`;
22
+ }
23
+ function withErrorHandling(handler) {
24
+ return async (args2) => {
25
+ try {
26
+ return await handler(args2);
27
+ } catch (error) {
28
+ const message = error instanceof Error ? error.message : String(error);
29
+ return { ...textContent(`Error: ${message}`), isError: true };
30
+ }
31
+ };
32
+ }
33
+ function registerTools(server, client) {
34
+ server.tool(
35
+ "create_endpoint",
36
+ "Create a new webhook endpoint. Returns the endpoint URL and slug.",
37
+ { name: import_zod.z.string().optional().describe("Display name for the endpoint") },
38
+ withErrorHandling(async ({ name }) => {
39
+ const endpoint = await client.endpoints.create({ name });
40
+ return textContent(JSON.stringify(endpoint, null, 2));
41
+ })
42
+ );
43
+ server.tool(
44
+ "list_endpoints",
45
+ "List all webhook endpoints for the authenticated user. Returns an array of endpoints with their slugs, names, and URLs.",
46
+ {},
47
+ withErrorHandling(async () => {
48
+ const endpoints = await client.endpoints.list();
49
+ return textContent(JSON.stringify(endpoints, null, 2));
50
+ })
51
+ );
52
+ server.tool(
53
+ "get_endpoint",
54
+ "Get details for a specific webhook endpoint by its slug.",
55
+ { slug: import_zod.z.string().describe("The endpoint slug (from the URL)") },
56
+ withErrorHandling(async ({ slug }) => {
57
+ const endpoint = await client.endpoints.get(slug);
58
+ return textContent(JSON.stringify(endpoint, null, 2));
59
+ })
60
+ );
61
+ server.tool(
62
+ "update_endpoint",
63
+ "Update an endpoint's name or mock response configuration.",
64
+ {
65
+ slug: import_zod.z.string().describe("The endpoint slug to update"),
66
+ name: import_zod.z.string().optional().describe("New display name"),
67
+ mockResponse: import_zod.z.object({
68
+ status: import_zod.z.number().min(100).max(599).describe("HTTP status code (100-599)"),
69
+ body: import_zod.z.string().default("").describe("Response body string (default: empty)"),
70
+ headers: import_zod.z.record(import_zod.z.string()).default({}).describe("Response headers (default: none)")
71
+ }).nullable().optional().describe("Mock response config, or null to clear it")
72
+ },
73
+ withErrorHandling(async ({ slug, name, mockResponse }) => {
74
+ const endpoint = await client.endpoints.update(slug, { name, mockResponse });
75
+ return textContent(JSON.stringify(endpoint, null, 2));
76
+ })
77
+ );
78
+ server.tool(
79
+ "delete_endpoint",
80
+ "Delete a webhook endpoint and all its captured requests.",
81
+ { slug: import_zod.z.string().describe("The endpoint slug to delete") },
82
+ withErrorHandling(async ({ slug }) => {
83
+ await client.endpoints.delete(slug);
84
+ return textContent(`Endpoint "${slug}" deleted.`);
85
+ })
86
+ );
87
+ server.tool(
88
+ "send_webhook",
89
+ "Send a test webhook to an endpoint. Useful for testing webhook handling code.",
90
+ {
91
+ slug: import_zod.z.string().describe("The endpoint slug to send to"),
92
+ method: import_zod.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).default("POST").describe("HTTP method (default: POST)"),
93
+ headers: import_zod.z.record(import_zod.z.string()).optional().describe("HTTP headers to include"),
94
+ body: import_zod.z.unknown().optional().describe("Request body (will be JSON-serialized)")
95
+ },
96
+ withErrorHandling(async ({ slug, method, headers, body }) => {
97
+ const response = await client.endpoints.send(slug, { method, headers, body });
98
+ const responseBody = await readBodyTruncated(response);
99
+ return textContent(
100
+ JSON.stringify(
101
+ { status: response.status, statusText: response.statusText, body: responseBody },
102
+ null,
103
+ 2
104
+ )
105
+ );
106
+ })
107
+ );
108
+ server.tool(
109
+ "list_requests",
110
+ "List captured webhook requests for an endpoint. Returns the most recent requests (default: 25).",
111
+ {
112
+ endpointSlug: import_zod.z.string().describe("The endpoint slug"),
113
+ limit: import_zod.z.number().default(25).describe("Max number of requests to return (default: 25)"),
114
+ since: import_zod.z.number().optional().describe("Only return requests after this timestamp (ms)")
115
+ },
116
+ withErrorHandling(async ({ endpointSlug, limit, since }) => {
117
+ const requests = await client.requests.list(endpointSlug, { limit, since });
118
+ return textContent(JSON.stringify(requests, null, 2));
119
+ })
120
+ );
121
+ server.tool(
122
+ "get_request",
123
+ "Get full details of a specific captured webhook request by its ID. Includes method, headers, body, path, and timestamp.",
124
+ { requestId: import_zod.z.string().describe("The request ID") },
125
+ withErrorHandling(async ({ requestId }) => {
126
+ const request = await client.requests.get(requestId);
127
+ return textContent(JSON.stringify(request, null, 2));
128
+ })
129
+ );
130
+ server.tool(
131
+ "wait_for_request",
132
+ "Wait for a webhook request to arrive at an endpoint. Polls until a request is captured or timeout expires. Use this after sending a webhook to verify it was received.",
133
+ {
134
+ endpointSlug: import_zod.z.string().describe("The endpoint slug to monitor"),
135
+ timeout: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).default("30s").describe('How long to wait (e.g. "30s", "5m", or milliseconds as number)'),
136
+ pollInterval: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional().describe('Interval between polls (e.g. "1s", "500", or milliseconds). Default: 500ms')
137
+ },
138
+ withErrorHandling(async ({ endpointSlug, timeout, pollInterval }) => {
139
+ const request = await client.requests.waitFor(endpointSlug, { timeout, pollInterval });
140
+ return textContent(JSON.stringify(request, null, 2));
141
+ })
142
+ );
143
+ server.tool(
144
+ "replay_request",
145
+ "Replay a previously captured webhook request to a target URL. Sends the original method, headers, and body to the specified URL. Only use with URLs you trust \u2014 the original request data is forwarded.",
146
+ {
147
+ requestId: import_zod.z.string().describe("The ID of the captured request to replay"),
148
+ targetUrl: import_zod.z.string().url().refine(
149
+ (u) => {
150
+ try {
151
+ const p = new URL(u).protocol;
152
+ return p === "http:" || p === "https:";
153
+ } catch {
154
+ return false;
155
+ }
156
+ },
157
+ { message: "Only http and https URLs are supported" }
158
+ ).describe("The URL to send the replayed request to (http or https only)")
159
+ },
160
+ withErrorHandling(async ({ requestId, targetUrl }) => {
161
+ const response = await client.requests.replay(requestId, targetUrl);
162
+ const responseBody = await readBodyTruncated(response);
163
+ return textContent(
164
+ JSON.stringify(
165
+ { status: response.status, statusText: response.statusText, body: responseBody },
166
+ null,
167
+ 2
168
+ )
169
+ );
170
+ })
171
+ );
172
+ server.tool(
173
+ "describe",
174
+ "Describe all available SDK operations, their parameters, and types. Useful for discovering what actions are possible.",
175
+ {},
176
+ withErrorHandling(async () => {
177
+ const description = client.describe();
178
+ return textContent(JSON.stringify(description, null, 2));
179
+ })
180
+ );
181
+ }
182
+
183
+ // src/index.ts
184
+ var VERSION = true ? "0.1.0" : "0.0.0-dev";
185
+ function createServer(options = {}) {
186
+ const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
187
+ if (!apiKey) {
188
+ throw new Error("Missing API key. Set WHK_API_KEY environment variable or pass apiKey option.");
189
+ }
190
+ const client = new import_sdk.WebhooksCC({
191
+ apiKey,
192
+ webhookUrl: options.webhookUrl ?? process.env.WHK_WEBHOOK_URL,
193
+ baseUrl: options.baseUrl ?? process.env.WHK_BASE_URL
194
+ });
195
+ const server = new import_mcp.McpServer({
196
+ name: "webhooks-cc",
197
+ version: VERSION
198
+ });
199
+ registerTools(server, client);
200
+ return server;
201
+ }
202
+
203
+ // src/setup.ts
204
+ var import_fs = require("fs");
205
+ var import_path = require("path");
206
+ var import_child_process = require("child_process");
207
+ var import_os = require("os");
208
+ var TOOLS = ["claude-code", "claude-desktop", "cursor", "vscode", "codex", "windsurf"];
209
+ var API_KEY_REGEX = /^whcc_[a-zA-Z0-9_-]+$/;
210
+ function validateApiKeyFormat(key) {
211
+ if (!API_KEY_REGEX.test(key)) {
212
+ console.error("Error: Invalid API key format. Keys must start with 'whcc_' and contain only");
213
+ console.error("alphanumeric characters, hyphens, and underscores.");
214
+ process.exit(1);
215
+ }
216
+ }
217
+ function mcpServerConfig(apiKey) {
218
+ return {
219
+ command: "npx",
220
+ args: ["-y", "@webhooks-cc/mcp"],
221
+ env: { WHK_API_KEY: apiKey }
222
+ };
223
+ }
224
+ function resolveApiKey(flags) {
225
+ return flags["api-key"] ?? process.env.WHK_API_KEY ?? null;
226
+ }
227
+ function mergeJsonConfig(filePath, serverName, serverConfig) {
228
+ let existing = {};
229
+ if ((0, import_fs.existsSync)(filePath)) {
230
+ try {
231
+ existing = JSON.parse((0, import_fs.readFileSync)(filePath, "utf-8"));
232
+ } catch {
233
+ console.warn(`Warning: ${filePath} contains invalid JSON and will be overwritten.`);
234
+ console.warn(`A backup has been saved to ${filePath}.bak`);
235
+ (0, import_fs.copyFileSync)(filePath, `${filePath}.bak`);
236
+ }
237
+ }
238
+ const mcpServers = existing.mcpServers ?? {};
239
+ mcpServers[serverName] = serverConfig;
240
+ existing.mcpServers = mcpServers;
241
+ const dir = (0, import_path.dirname)(filePath);
242
+ if (!(0, import_fs.existsSync)(dir)) (0, import_fs.mkdirSync)(dir, { recursive: true });
243
+ (0, import_fs.writeFileSync)(filePath, JSON.stringify(existing, null, 2) + "\n");
244
+ }
245
+ function getClaudeDesktopConfigPath() {
246
+ const os = (0, import_os.platform)();
247
+ if (os === "darwin") {
248
+ return (0, import_path.join)(
249
+ (0, import_os.homedir)(),
250
+ "Library",
251
+ "Application Support",
252
+ "Claude",
253
+ "claude_desktop_config.json"
254
+ );
255
+ }
256
+ if (os === "win32") {
257
+ return (0, import_path.join)(
258
+ process.env.APPDATA ?? (0, import_path.join)((0, import_os.homedir)(), "AppData", "Roaming"),
259
+ "Claude",
260
+ "claude_desktop_config.json"
261
+ );
262
+ }
263
+ return (0, import_path.join)((0, import_os.homedir)(), ".config", "Claude", "claude_desktop_config.json");
264
+ }
265
+ function setupClaudeCode(apiKey) {
266
+ try {
267
+ (0, import_child_process.execFileSync)(
268
+ "claude",
269
+ [
270
+ "mcp",
271
+ "add",
272
+ "-s",
273
+ "user",
274
+ "--transport",
275
+ "stdio",
276
+ "webhooks-cc",
277
+ "-e",
278
+ `WHK_API_KEY=${apiKey}`,
279
+ "--",
280
+ "npx",
281
+ "-y",
282
+ "@webhooks-cc/mcp"
283
+ ],
284
+ { stdio: "inherit" }
285
+ );
286
+ console.log("\nDone! webhooks-cc MCP server added to Claude Code (user scope).");
287
+ console.log("It will be available in all your Claude Code sessions.");
288
+ } catch {
289
+ console.error("\nFailed to run 'claude mcp add'. Is Claude Code CLI installed?");
290
+ console.error("Install it: npm install -g @anthropic-ai/claude-code");
291
+ console.error("\nManual alternative \u2014 run this command:");
292
+ console.error(
293
+ ` claude mcp add -s user --transport stdio webhooks-cc -e WHK_API_KEY=<your-key> -- npx -y @webhooks-cc/mcp`
294
+ );
295
+ process.exit(1);
296
+ }
297
+ }
298
+ function setupClaudeDesktop(apiKey) {
299
+ const configPath = getClaudeDesktopConfigPath();
300
+ mergeJsonConfig(configPath, "webhooks-cc", mcpServerConfig(apiKey));
301
+ console.log(`Done! Config written to ${configPath}`);
302
+ console.log("Restart Claude Desktop for changes to take effect.");
303
+ }
304
+ function setupCursor(apiKey) {
305
+ const configPath = (0, import_path.join)(process.cwd(), ".cursor", "mcp.json");
306
+ mergeJsonConfig(configPath, "webhooks-cc", mcpServerConfig(apiKey));
307
+ console.log(`Done! Config written to ${configPath}`);
308
+ console.log("Restart Cursor or reload the window for changes to take effect.");
309
+ }
310
+ function setupVSCode(apiKey) {
311
+ const configPath = (0, import_path.join)(process.cwd(), ".vscode", "mcp.json");
312
+ mergeJsonConfig(configPath, "webhooks-cc", mcpServerConfig(apiKey));
313
+ console.log(`Done! Config written to ${configPath}`);
314
+ console.log("Reload VS Code window for changes to take effect.");
315
+ }
316
+ function setupCodex(apiKey) {
317
+ try {
318
+ (0, import_child_process.execFileSync)(
319
+ "codex",
320
+ [
321
+ "mcp",
322
+ "add",
323
+ "webhooks-cc",
324
+ "-e",
325
+ `WHK_API_KEY=${apiKey}`,
326
+ "--",
327
+ "npx",
328
+ "-y",
329
+ "@webhooks-cc/mcp"
330
+ ],
331
+ { stdio: "inherit" }
332
+ );
333
+ console.log("\nDone! webhooks-cc MCP server added to Codex.");
334
+ } catch {
335
+ console.error("\nFailed to run 'codex mcp add'. Is OpenAI Codex CLI installed?");
336
+ console.error("\nManual alternative \u2014 add to ~/.codex/config.toml:");
337
+ console.error(`
338
+ [mcp.webhooks-cc]
339
+ command = "npx"
340
+ args = ["-y", "@webhooks-cc/mcp"]
341
+
342
+ [mcp.webhooks-cc.env]
343
+ WHK_API_KEY = "<your-key>"
344
+ `);
345
+ process.exit(1);
346
+ }
347
+ }
348
+ function setupWindsurf(apiKey) {
349
+ const configPath = (0, import_path.join)((0, import_os.homedir)(), ".codeium", "windsurf", "mcp_config.json");
350
+ mergeJsonConfig(configPath, "webhooks-cc", mcpServerConfig(apiKey));
351
+ console.log(`Done! Config written to ${configPath}`);
352
+ console.log("Restart Windsurf for changes to take effect.");
353
+ }
354
+ function printUsage() {
355
+ console.log(`
356
+ @webhooks-cc/mcp setup \u2014 Configure the MCP server for your AI tool
357
+
358
+ Usage:
359
+ npx @webhooks-cc/mcp setup <tool> [--api-key <key>]
360
+
361
+ Tools:
362
+ claude-code Claude Code CLI (runs 'claude mcp add')
363
+ claude-desktop Claude Desktop app
364
+ cursor Cursor editor (writes .cursor/mcp.json)
365
+ vscode VS Code Copilot (writes .vscode/mcp.json)
366
+ codex OpenAI Codex CLI (runs 'codex mcp add')
367
+ windsurf Windsurf editor
368
+
369
+ Options:
370
+ --api-key <key> Your webhooks.cc API key (or set WHK_API_KEY env var)
371
+
372
+ Examples:
373
+ npx @webhooks-cc/mcp setup claude-code --api-key whcc_abc123
374
+ WHK_API_KEY=whcc_abc123 npx @webhooks-cc/mcp setup cursor
375
+ npx @webhooks-cc/mcp setup vscode --api-key whcc_abc123
376
+ `);
377
+ }
378
+ function runSetup(args2) {
379
+ const flags = {};
380
+ const positional = [];
381
+ for (let i = 0; i < args2.length; i++) {
382
+ if (args2[i] === "--api-key" && i + 1 < args2.length) {
383
+ flags["api-key"] = args2[++i];
384
+ } else if (args2[i].startsWith("--api-key=")) {
385
+ flags["api-key"] = args2[i].slice("--api-key=".length);
386
+ } else if (!args2[i].startsWith("-")) {
387
+ positional.push(args2[i]);
388
+ }
389
+ }
390
+ const tool = positional[0];
391
+ if (!tool || !TOOLS.includes(tool)) {
392
+ printUsage();
393
+ if (tool) {
394
+ console.error(`Unknown tool: "${tool}"
395
+ `);
396
+ }
397
+ process.exit(tool ? 1 : 0);
398
+ }
399
+ const apiKey = resolveApiKey(flags);
400
+ if (!apiKey) {
401
+ console.error("Error: API key required. Pass --api-key or set WHK_API_KEY env var.");
402
+ console.error("Get your API key at https://webhooks.cc/account\n");
403
+ process.exit(1);
404
+ }
405
+ validateApiKeyFormat(apiKey);
406
+ console.log(`Setting up webhooks-cc MCP server for ${tool}...
407
+ `);
408
+ switch (tool) {
409
+ case "claude-code":
410
+ setupClaudeCode(apiKey);
411
+ break;
412
+ case "claude-desktop":
413
+ setupClaudeDesktop(apiKey);
414
+ break;
415
+ case "cursor":
416
+ setupCursor(apiKey);
417
+ break;
418
+ case "vscode":
419
+ setupVSCode(apiKey);
420
+ break;
421
+ case "codex":
422
+ setupCodex(apiKey);
423
+ break;
424
+ case "windsurf":
425
+ setupWindsurf(apiKey);
426
+ break;
427
+ }
428
+ }
429
+
430
+ // bin/mcp.ts
431
+ var args = process.argv.slice(2);
432
+ var command = args[0];
433
+ if (command === "setup") {
434
+ runSetup(args.slice(1));
435
+ } else if (command === "--help" || command === "-h") {
436
+ console.log(`
437
+ @webhooks-cc/mcp \u2014 MCP server for webhooks.cc
438
+
439
+ Usage:
440
+ npx @webhooks-cc/mcp Start stdio MCP server
441
+ npx @webhooks-cc/mcp setup <tool> Configure for an AI tool
442
+ npx @webhooks-cc/mcp --help Show this help
443
+
444
+ Environment:
445
+ WHK_API_KEY Your webhooks.cc API key (required)
446
+ WHK_WEBHOOK_URL Custom webhook receiver URL (optional)
447
+ WHK_BASE_URL Custom API base URL (optional)
448
+
449
+ Setup:
450
+ npx @webhooks-cc/mcp setup claude-code --api-key whcc_...
451
+ npx @webhooks-cc/mcp setup cursor --api-key whcc_...
452
+ npx @webhooks-cc/mcp setup vscode --api-key whcc_...
453
+ npx @webhooks-cc/mcp setup codex --api-key whcc_...
454
+ npx @webhooks-cc/mcp setup windsurf --api-key whcc_...
455
+ npx @webhooks-cc/mcp setup claude-desktop --api-key whcc_...
456
+
457
+ Get your API key at https://webhooks.cc/account
458
+ `);
459
+ } else if (command && command !== "--") {
460
+ console.error(`Unknown command: "${command}". Run with --help for usage.`);
461
+ process.exit(1);
462
+ } else {
463
+ const server = createServer();
464
+ const transport = new import_stdio.StdioServerTransport();
465
+ server.connect(transport).catch((error) => {
466
+ console.error("Fatal:", error instanceof Error ? error.message : error);
467
+ process.exit(1);
468
+ });
469
+ }
@@ -0,0 +1,29 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { WebhooksCC } from '@webhooks-cc/sdk';
3
+
4
+ /** Register all 11 webhook tools on an MCP server instance. */
5
+ declare function registerTools(server: McpServer, client: WebhooksCC): void;
6
+
7
+ interface CreateServerOptions {
8
+ /** API key for webhooks.cc (default: reads WHK_API_KEY env var) */
9
+ apiKey?: string;
10
+ /** Custom webhook receiver URL (default: reads WHK_WEBHOOK_URL or https://go.webhooks.cc) */
11
+ webhookUrl?: string;
12
+ /** Custom API base URL (default: https://webhooks.cc) */
13
+ baseUrl?: string;
14
+ }
15
+ /**
16
+ * Create an MCP server with all webhooks.cc tools registered.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { createServer } from "@webhooks-cc/mcp";
21
+ * import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
22
+ *
23
+ * const server = createServer({ apiKey: "whcc_..." });
24
+ * await server.connect(new StdioServerTransport());
25
+ * ```
26
+ */
27
+ declare function createServer(options?: CreateServerOptions): McpServer;
28
+
29
+ export { type CreateServerOptions, createServer, registerTools };
@@ -0,0 +1,29 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { WebhooksCC } from '@webhooks-cc/sdk';
3
+
4
+ /** Register all 11 webhook tools on an MCP server instance. */
5
+ declare function registerTools(server: McpServer, client: WebhooksCC): void;
6
+
7
+ interface CreateServerOptions {
8
+ /** API key for webhooks.cc (default: reads WHK_API_KEY env var) */
9
+ apiKey?: string;
10
+ /** Custom webhook receiver URL (default: reads WHK_WEBHOOK_URL or https://go.webhooks.cc) */
11
+ webhookUrl?: string;
12
+ /** Custom API base URL (default: https://webhooks.cc) */
13
+ baseUrl?: string;
14
+ }
15
+ /**
16
+ * Create an MCP server with all webhooks.cc tools registered.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { createServer } from "@webhooks-cc/mcp";
21
+ * import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
22
+ *
23
+ * const server = createServer({ apiKey: "whcc_..." });
24
+ * await server.connect(new StdioServerTransport());
25
+ * ```
26
+ */
27
+ declare function createServer(options?: CreateServerOptions): McpServer;
28
+
29
+ export { type CreateServerOptions, createServer, registerTools };
package/dist/index.js ADDED
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createServer: () => createServer,
24
+ registerTools: () => registerTools
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
28
+ var import_sdk = require("@webhooks-cc/sdk");
29
+
30
+ // src/tools.ts
31
+ var import_zod = require("zod");
32
+ var MAX_BODY_SIZE = 32768;
33
+ function textContent(text) {
34
+ return { content: [{ type: "text", text }] };
35
+ }
36
+ async function readBodyTruncated(response, limit = MAX_BODY_SIZE) {
37
+ const text = await response.text();
38
+ if (text.length <= limit) return text;
39
+ return text.slice(0, limit) + `
40
+ ... [truncated, ${text.length} chars total]`;
41
+ }
42
+ function withErrorHandling(handler) {
43
+ return async (args) => {
44
+ try {
45
+ return await handler(args);
46
+ } catch (error) {
47
+ const message = error instanceof Error ? error.message : String(error);
48
+ return { ...textContent(`Error: ${message}`), isError: true };
49
+ }
50
+ };
51
+ }
52
+ function registerTools(server, client) {
53
+ server.tool(
54
+ "create_endpoint",
55
+ "Create a new webhook endpoint. Returns the endpoint URL and slug.",
56
+ { name: import_zod.z.string().optional().describe("Display name for the endpoint") },
57
+ withErrorHandling(async ({ name }) => {
58
+ const endpoint = await client.endpoints.create({ name });
59
+ return textContent(JSON.stringify(endpoint, null, 2));
60
+ })
61
+ );
62
+ server.tool(
63
+ "list_endpoints",
64
+ "List all webhook endpoints for the authenticated user. Returns an array of endpoints with their slugs, names, and URLs.",
65
+ {},
66
+ withErrorHandling(async () => {
67
+ const endpoints = await client.endpoints.list();
68
+ return textContent(JSON.stringify(endpoints, null, 2));
69
+ })
70
+ );
71
+ server.tool(
72
+ "get_endpoint",
73
+ "Get details for a specific webhook endpoint by its slug.",
74
+ { slug: import_zod.z.string().describe("The endpoint slug (from the URL)") },
75
+ withErrorHandling(async ({ slug }) => {
76
+ const endpoint = await client.endpoints.get(slug);
77
+ return textContent(JSON.stringify(endpoint, null, 2));
78
+ })
79
+ );
80
+ server.tool(
81
+ "update_endpoint",
82
+ "Update an endpoint's name or mock response configuration.",
83
+ {
84
+ slug: import_zod.z.string().describe("The endpoint slug to update"),
85
+ name: import_zod.z.string().optional().describe("New display name"),
86
+ mockResponse: import_zod.z.object({
87
+ status: import_zod.z.number().min(100).max(599).describe("HTTP status code (100-599)"),
88
+ body: import_zod.z.string().default("").describe("Response body string (default: empty)"),
89
+ headers: import_zod.z.record(import_zod.z.string()).default({}).describe("Response headers (default: none)")
90
+ }).nullable().optional().describe("Mock response config, or null to clear it")
91
+ },
92
+ withErrorHandling(async ({ slug, name, mockResponse }) => {
93
+ const endpoint = await client.endpoints.update(slug, { name, mockResponse });
94
+ return textContent(JSON.stringify(endpoint, null, 2));
95
+ })
96
+ );
97
+ server.tool(
98
+ "delete_endpoint",
99
+ "Delete a webhook endpoint and all its captured requests.",
100
+ { slug: import_zod.z.string().describe("The endpoint slug to delete") },
101
+ withErrorHandling(async ({ slug }) => {
102
+ await client.endpoints.delete(slug);
103
+ return textContent(`Endpoint "${slug}" deleted.`);
104
+ })
105
+ );
106
+ server.tool(
107
+ "send_webhook",
108
+ "Send a test webhook to an endpoint. Useful for testing webhook handling code.",
109
+ {
110
+ slug: import_zod.z.string().describe("The endpoint slug to send to"),
111
+ method: import_zod.z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).default("POST").describe("HTTP method (default: POST)"),
112
+ headers: import_zod.z.record(import_zod.z.string()).optional().describe("HTTP headers to include"),
113
+ body: import_zod.z.unknown().optional().describe("Request body (will be JSON-serialized)")
114
+ },
115
+ withErrorHandling(async ({ slug, method, headers, body }) => {
116
+ const response = await client.endpoints.send(slug, { method, headers, body });
117
+ const responseBody = await readBodyTruncated(response);
118
+ return textContent(
119
+ JSON.stringify(
120
+ { status: response.status, statusText: response.statusText, body: responseBody },
121
+ null,
122
+ 2
123
+ )
124
+ );
125
+ })
126
+ );
127
+ server.tool(
128
+ "list_requests",
129
+ "List captured webhook requests for an endpoint. Returns the most recent requests (default: 25).",
130
+ {
131
+ endpointSlug: import_zod.z.string().describe("The endpoint slug"),
132
+ limit: import_zod.z.number().default(25).describe("Max number of requests to return (default: 25)"),
133
+ since: import_zod.z.number().optional().describe("Only return requests after this timestamp (ms)")
134
+ },
135
+ withErrorHandling(async ({ endpointSlug, limit, since }) => {
136
+ const requests = await client.requests.list(endpointSlug, { limit, since });
137
+ return textContent(JSON.stringify(requests, null, 2));
138
+ })
139
+ );
140
+ server.tool(
141
+ "get_request",
142
+ "Get full details of a specific captured webhook request by its ID. Includes method, headers, body, path, and timestamp.",
143
+ { requestId: import_zod.z.string().describe("The request ID") },
144
+ withErrorHandling(async ({ requestId }) => {
145
+ const request = await client.requests.get(requestId);
146
+ return textContent(JSON.stringify(request, null, 2));
147
+ })
148
+ );
149
+ server.tool(
150
+ "wait_for_request",
151
+ "Wait for a webhook request to arrive at an endpoint. Polls until a request is captured or timeout expires. Use this after sending a webhook to verify it was received.",
152
+ {
153
+ endpointSlug: import_zod.z.string().describe("The endpoint slug to monitor"),
154
+ timeout: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).default("30s").describe('How long to wait (e.g. "30s", "5m", or milliseconds as number)'),
155
+ pollInterval: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional().describe('Interval between polls (e.g. "1s", "500", or milliseconds). Default: 500ms')
156
+ },
157
+ withErrorHandling(async ({ endpointSlug, timeout, pollInterval }) => {
158
+ const request = await client.requests.waitFor(endpointSlug, { timeout, pollInterval });
159
+ return textContent(JSON.stringify(request, null, 2));
160
+ })
161
+ );
162
+ server.tool(
163
+ "replay_request",
164
+ "Replay a previously captured webhook request to a target URL. Sends the original method, headers, and body to the specified URL. Only use with URLs you trust \u2014 the original request data is forwarded.",
165
+ {
166
+ requestId: import_zod.z.string().describe("The ID of the captured request to replay"),
167
+ targetUrl: import_zod.z.string().url().refine(
168
+ (u) => {
169
+ try {
170
+ const p = new URL(u).protocol;
171
+ return p === "http:" || p === "https:";
172
+ } catch {
173
+ return false;
174
+ }
175
+ },
176
+ { message: "Only http and https URLs are supported" }
177
+ ).describe("The URL to send the replayed request to (http or https only)")
178
+ },
179
+ withErrorHandling(async ({ requestId, targetUrl }) => {
180
+ const response = await client.requests.replay(requestId, targetUrl);
181
+ const responseBody = await readBodyTruncated(response);
182
+ return textContent(
183
+ JSON.stringify(
184
+ { status: response.status, statusText: response.statusText, body: responseBody },
185
+ null,
186
+ 2
187
+ )
188
+ );
189
+ })
190
+ );
191
+ server.tool(
192
+ "describe",
193
+ "Describe all available SDK operations, their parameters, and types. Useful for discovering what actions are possible.",
194
+ {},
195
+ withErrorHandling(async () => {
196
+ const description = client.describe();
197
+ return textContent(JSON.stringify(description, null, 2));
198
+ })
199
+ );
200
+ }
201
+
202
+ // src/index.ts
203
+ var VERSION = true ? "0.1.0" : "0.0.0-dev";
204
+ function createServer(options = {}) {
205
+ const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
206
+ if (!apiKey) {
207
+ throw new Error("Missing API key. Set WHK_API_KEY environment variable or pass apiKey option.");
208
+ }
209
+ const client = new import_sdk.WebhooksCC({
210
+ apiKey,
211
+ webhookUrl: options.webhookUrl ?? process.env.WHK_WEBHOOK_URL,
212
+ baseUrl: options.baseUrl ?? process.env.WHK_BASE_URL
213
+ });
214
+ const server = new import_mcp.McpServer({
215
+ name: "webhooks-cc",
216
+ version: VERSION
217
+ });
218
+ registerTools(server, client);
219
+ return server;
220
+ }
221
+ // Annotate the CommonJS export names for ESM import in node:
222
+ 0 && (module.exports = {
223
+ createServer,
224
+ registerTools
225
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,199 @@
1
+ // src/index.ts
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { WebhooksCC } from "@webhooks-cc/sdk";
4
+
5
+ // src/tools.ts
6
+ import { z } from "zod";
7
+ var MAX_BODY_SIZE = 32768;
8
+ function textContent(text) {
9
+ return { content: [{ type: "text", text }] };
10
+ }
11
+ async function readBodyTruncated(response, limit = MAX_BODY_SIZE) {
12
+ const text = await response.text();
13
+ if (text.length <= limit) return text;
14
+ return text.slice(0, limit) + `
15
+ ... [truncated, ${text.length} chars total]`;
16
+ }
17
+ function withErrorHandling(handler) {
18
+ return async (args) => {
19
+ try {
20
+ return await handler(args);
21
+ } catch (error) {
22
+ const message = error instanceof Error ? error.message : String(error);
23
+ return { ...textContent(`Error: ${message}`), isError: true };
24
+ }
25
+ };
26
+ }
27
+ function registerTools(server, client) {
28
+ server.tool(
29
+ "create_endpoint",
30
+ "Create a new webhook endpoint. Returns the endpoint URL and slug.",
31
+ { name: z.string().optional().describe("Display name for the endpoint") },
32
+ withErrorHandling(async ({ name }) => {
33
+ const endpoint = await client.endpoints.create({ name });
34
+ return textContent(JSON.stringify(endpoint, null, 2));
35
+ })
36
+ );
37
+ server.tool(
38
+ "list_endpoints",
39
+ "List all webhook endpoints for the authenticated user. Returns an array of endpoints with their slugs, names, and URLs.",
40
+ {},
41
+ withErrorHandling(async () => {
42
+ const endpoints = await client.endpoints.list();
43
+ return textContent(JSON.stringify(endpoints, null, 2));
44
+ })
45
+ );
46
+ server.tool(
47
+ "get_endpoint",
48
+ "Get details for a specific webhook endpoint by its slug.",
49
+ { slug: z.string().describe("The endpoint slug (from the URL)") },
50
+ withErrorHandling(async ({ slug }) => {
51
+ const endpoint = await client.endpoints.get(slug);
52
+ return textContent(JSON.stringify(endpoint, null, 2));
53
+ })
54
+ );
55
+ server.tool(
56
+ "update_endpoint",
57
+ "Update an endpoint's name or mock response configuration.",
58
+ {
59
+ slug: z.string().describe("The endpoint slug to update"),
60
+ name: z.string().optional().describe("New display name"),
61
+ mockResponse: z.object({
62
+ status: z.number().min(100).max(599).describe("HTTP status code (100-599)"),
63
+ body: z.string().default("").describe("Response body string (default: empty)"),
64
+ headers: z.record(z.string()).default({}).describe("Response headers (default: none)")
65
+ }).nullable().optional().describe("Mock response config, or null to clear it")
66
+ },
67
+ withErrorHandling(async ({ slug, name, mockResponse }) => {
68
+ const endpoint = await client.endpoints.update(slug, { name, mockResponse });
69
+ return textContent(JSON.stringify(endpoint, null, 2));
70
+ })
71
+ );
72
+ server.tool(
73
+ "delete_endpoint",
74
+ "Delete a webhook endpoint and all its captured requests.",
75
+ { slug: z.string().describe("The endpoint slug to delete") },
76
+ withErrorHandling(async ({ slug }) => {
77
+ await client.endpoints.delete(slug);
78
+ return textContent(`Endpoint "${slug}" deleted.`);
79
+ })
80
+ );
81
+ server.tool(
82
+ "send_webhook",
83
+ "Send a test webhook to an endpoint. Useful for testing webhook handling code.",
84
+ {
85
+ slug: z.string().describe("The endpoint slug to send to"),
86
+ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]).default("POST").describe("HTTP method (default: POST)"),
87
+ headers: z.record(z.string()).optional().describe("HTTP headers to include"),
88
+ body: z.unknown().optional().describe("Request body (will be JSON-serialized)")
89
+ },
90
+ withErrorHandling(async ({ slug, method, headers, body }) => {
91
+ const response = await client.endpoints.send(slug, { method, headers, body });
92
+ const responseBody = await readBodyTruncated(response);
93
+ return textContent(
94
+ JSON.stringify(
95
+ { status: response.status, statusText: response.statusText, body: responseBody },
96
+ null,
97
+ 2
98
+ )
99
+ );
100
+ })
101
+ );
102
+ server.tool(
103
+ "list_requests",
104
+ "List captured webhook requests for an endpoint. Returns the most recent requests (default: 25).",
105
+ {
106
+ endpointSlug: z.string().describe("The endpoint slug"),
107
+ limit: z.number().default(25).describe("Max number of requests to return (default: 25)"),
108
+ since: z.number().optional().describe("Only return requests after this timestamp (ms)")
109
+ },
110
+ withErrorHandling(async ({ endpointSlug, limit, since }) => {
111
+ const requests = await client.requests.list(endpointSlug, { limit, since });
112
+ return textContent(JSON.stringify(requests, null, 2));
113
+ })
114
+ );
115
+ server.tool(
116
+ "get_request",
117
+ "Get full details of a specific captured webhook request by its ID. Includes method, headers, body, path, and timestamp.",
118
+ { requestId: z.string().describe("The request ID") },
119
+ withErrorHandling(async ({ requestId }) => {
120
+ const request = await client.requests.get(requestId);
121
+ return textContent(JSON.stringify(request, null, 2));
122
+ })
123
+ );
124
+ server.tool(
125
+ "wait_for_request",
126
+ "Wait for a webhook request to arrive at an endpoint. Polls until a request is captured or timeout expires. Use this after sending a webhook to verify it was received.",
127
+ {
128
+ endpointSlug: z.string().describe("The endpoint slug to monitor"),
129
+ timeout: z.union([z.string(), z.number()]).default("30s").describe('How long to wait (e.g. "30s", "5m", or milliseconds as number)'),
130
+ pollInterval: z.union([z.string(), z.number()]).optional().describe('Interval between polls (e.g. "1s", "500", or milliseconds). Default: 500ms')
131
+ },
132
+ withErrorHandling(async ({ endpointSlug, timeout, pollInterval }) => {
133
+ const request = await client.requests.waitFor(endpointSlug, { timeout, pollInterval });
134
+ return textContent(JSON.stringify(request, null, 2));
135
+ })
136
+ );
137
+ server.tool(
138
+ "replay_request",
139
+ "Replay a previously captured webhook request to a target URL. Sends the original method, headers, and body to the specified URL. Only use with URLs you trust \u2014 the original request data is forwarded.",
140
+ {
141
+ requestId: z.string().describe("The ID of the captured request to replay"),
142
+ targetUrl: z.string().url().refine(
143
+ (u) => {
144
+ try {
145
+ const p = new URL(u).protocol;
146
+ return p === "http:" || p === "https:";
147
+ } catch {
148
+ return false;
149
+ }
150
+ },
151
+ { message: "Only http and https URLs are supported" }
152
+ ).describe("The URL to send the replayed request to (http or https only)")
153
+ },
154
+ withErrorHandling(async ({ requestId, targetUrl }) => {
155
+ const response = await client.requests.replay(requestId, targetUrl);
156
+ const responseBody = await readBodyTruncated(response);
157
+ return textContent(
158
+ JSON.stringify(
159
+ { status: response.status, statusText: response.statusText, body: responseBody },
160
+ null,
161
+ 2
162
+ )
163
+ );
164
+ })
165
+ );
166
+ server.tool(
167
+ "describe",
168
+ "Describe all available SDK operations, their parameters, and types. Useful for discovering what actions are possible.",
169
+ {},
170
+ withErrorHandling(async () => {
171
+ const description = client.describe();
172
+ return textContent(JSON.stringify(description, null, 2));
173
+ })
174
+ );
175
+ }
176
+
177
+ // src/index.ts
178
+ var VERSION = true ? "0.1.0" : "0.0.0-dev";
179
+ function createServer(options = {}) {
180
+ const apiKey = options.apiKey ?? process.env.WHK_API_KEY;
181
+ if (!apiKey) {
182
+ throw new Error("Missing API key. Set WHK_API_KEY environment variable or pass apiKey option.");
183
+ }
184
+ const client = new WebhooksCC({
185
+ apiKey,
186
+ webhookUrl: options.webhookUrl ?? process.env.WHK_WEBHOOK_URL,
187
+ baseUrl: options.baseUrl ?? process.env.WHK_BASE_URL
188
+ });
189
+ const server = new McpServer({
190
+ name: "webhooks-cc",
191
+ version: VERSION
192
+ });
193
+ registerTools(server, client);
194
+ return server;
195
+ }
196
+ export {
197
+ createServer,
198
+ registerTools
199
+ };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@webhooks-cc/mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for webhooks.cc — AI agent integration for webhook testing",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "webhooks-cc-mcp": "dist/bin/mcp.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "keywords": [
23
+ "webhook",
24
+ "mcp",
25
+ "model-context-protocol",
26
+ "ai",
27
+ "agent",
28
+ "webhooks-cc"
29
+ ],
30
+ "author": "",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/webhooks-cc/webhooks-cc",
35
+ "directory": "packages/mcp"
36
+ },
37
+ "homepage": "https://webhooks.cc",
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.12.1",
40
+ "zod": "^3.25.0",
41
+ "@webhooks-cc/sdk": "0.3.0"
42
+ },
43
+ "devDependencies": {
44
+ "tsup": "^8.5.1",
45
+ "typescript": "^5.9.3",
46
+ "vitest": "^3.0.0"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsup --watch",
51
+ "typecheck": "tsc --noEmit",
52
+ "test": "vitest run"
53
+ }
54
+ }