Receive, forward, sign and verify webhooks from your terminal, your tests and your AI agent. CLI + library + MCP server.
- ✓Open-source license (MIT)
- ✓Actively maintained (<30d)
- ✓Clear description
- ✓Topics declared
- ✓Documented (README)
claude mcp add webhook-toolkit -- npx -y webhook-toolkit{
"mcpServers": {
"webhook-toolkit": {
"command": "npx",
"args": ["-y", "webhook-toolkit"],
"env": {
"WEBHOOK_TOOLKIT_KEY": "<webhook_toolkit_key>"
}
}
}
}WEBHOOK_TOOLKIT_KEYMCP Servers overview
# webhook-toolkit
**Receive, forward, sign and verify webhooks from your terminal, your test suite and your AI agent.**
One package: a CLI (`webhook-toolkit`, alias `whtk`), a typed library and an MCP server, backed by [webhook-toolkit.com](https://webhook-toolkit.com).
[](https://www.npmjs.com/package/webhook-toolkit)
[](./LICENSE)
[](https://nodejs.org)
[](https://github.com/THE-KIPDEV/webhook-toolkit/actions/workflows/ci.yml)
## Quick start
```sh
npx webhook-toolkit listen --forward http://localhost:3000/webhooks
```
That's it: no account, no config. You get a public URL; paste it into Stripe, GitHub, Shopify or any other sender, and every webhook shows up in your terminal and is re-sent to your local handler:
```
webhook-toolkit · listening
Webhook URL https://webhook-toolkit.com/r/l90c9MKIdo0o
Inspector https://webhook-toolkit.com/e/l90c9MKIdo0o
Forwarding → http://localhost:3000/webhooks
Expires in 7 days (2026-09-25 15:42)
Anonymous URL. Run `webhook-toolkit login` to get a permanent one.
Waiting for webhooks. Ctrl+C to stop.
15:42:11 POST /stripe stripe checkout.session.completed 248 B
↳ 200 OK · 3 ms
15:42:11 POST /github github push 269 B
↳ 500 Internal Server Error · 1 ms {"error":"handler crashed: cannot read properties of undefined"}
```
Every request is also kept in a web inspector (headers, raw body, replay), so nothing is lost when your handler crashes.
## Contents
- [CLI](#cli)
- [Test webhooks in your test suite](#test-webhooks-in-your-test-suite)
- [Use it from AI agents (MCP)](#use-it-from-ai-agents-mcp)
- [Signing and verifying](#signing-and-verifying)
- [Relay: a real tunnel (paid)](#relay-a-real-tunnel-paid)
- [Free vs paid](#free-vs-paid)
- [How it compares](#how-it-compares)
- [Library reference](#library-reference)
- [Configuration](#configuration)
## CLI
```sh
npm i -g webhook-toolkit # or run any command with npx webhook-toolkit …
```
| Command | What it does |
|---|---|
| `whtk listen [--forward <url>]` | Public URL + live stream of incoming requests, optionally re-sent to localhost. Reuses your last URL while it is alive (`--new` for a fresh one). |
| `whtk relay --to <url\|port>` | Real tunnel: callers get your local app's response. [Paid](#relay-a-real-tunnel-paid). |
| `whtk sign <provider> --secret <s>` | Build a validly signed webhook: prints headers + a ready-to-run `curl`, or sends it with `--send`. |
| `whtk verify <provider> --secret <s> …` | Check a signature, and when it fails, say why. |
| `whtk replay <token> <id> --to <url>` | Re-send a captured request from your machine (localhost works). |
| `whtk requests <token>` | List captured requests (`--json` for scripts). |
| `whtk endpoints` | List your account's URLs. |
| `whtk login` / `logout` / `whoami` | Save, remove or inspect your API key. |
| `whtk mcp` | Start the MCP server on stdio. |
A few things worth knowing:
- **Sub-paths and query strings are kept.** A request to `…/r/<token>/stripe?x=1` is forwarded to `http://localhost:3000/webhooks/stripe?x=1`, so one URL can feed several handlers.
- **Headers are forwarded as received** (minus hop-by-hop headers, `host` and `content-length`), so signature checks in your handler pass exactly as in production.
- **`--json` prints NDJSON** (`listening`, `request`, `forward` events) for scripting: `whtk listen -f 3000 --json | jq .request.event`.
- Colors follow `NO_COLOR`, `FORCE_COLOR` and are off when output is not a terminal.
### Send a signed test event
```sh
# Prints the headers and a curl command, with a realistic sample event
whtk sign stripe --secret whsec_… --event checkout.session.completed
# Or send it straight to your handler
whtk sign github --secret my-secret --file push.json --send http://localhost:3000/api/github
whtk sign twilio --secret <auth token> --url https://example.com/sms --send 3000
```
### Debug "invalid signature"
```sh
whtk verify stripe --secret whsec_test_secret --body-file body.json \
-H "Stripe-Signature: t=1726000000,v1=a530ce3d81556a0eced506e1d1cad777bc55bc1e408e54dd6bfef08e1dde216c"
```
```
✗ Invalid Stripe signature (body_trailing_newline_added)
The signature matches the body without its trailing newline: a newline was appended after signing (proxy, logger, copy-paste). Verify the raw bytes exactly as received.
expected t=1726000000,v1=1e0085bf1054f89ac8882e2e5fcbfdf301b1d6445e8ed7a316120cd5dc651ba9
received t=1726000000,v1=a530ce3d81556a0eced506e1d1cad777bc55bc1e408e54dd6bfef08e1dde216c
timestamp 1726000000 (738 days ago)
```
The verifier replays the usual mistakes (whitespace in the secret, added or stripped trailing newline, CRLF conversion, expired timestamp, and for Twilio: http vs https, port, query string, trailing slash, repeated parameters) and tells you which one matches. The same logic powers the [online signature validator](https://webhook-toolkit.com/webhook-signature-validator).
## Test webhooks in your test suite
The library gives your tests a real public URL and a way to wait for what lands on it. It works anonymously; set `WEBHOOK_TOOLKIT_KEY` in CI for permanent URLs and higher limits.
### Vitest (or Jest in ESM mode)
```ts
import { expect, test } from "vitest";
import { WebhookToolkit, verify } from "webhook-toolkit";
const wt = new WebhookToolkit(); // anonymous, or reads WEBHOOK_TOOLKIT_KEY
test("creating an order notifies the customer's webhook", async () => {
const endpoint = await wt.createEndpoint({ name: "ci-orders" });
// The app under test sends its outgoing webhooks to the capture URL.
await api.post("/settings/webhooks", { url: endpoint.url, secret: "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw" });
await api.post("/orders", { sku: "tshirt", quantity: 2 });
const req = await wt.waitForRequest(endpoint.token, {
timeoutMs: 30_000,
filter: (r) => JSON.parse(r.body).type === "order.created",
});
expect(JSON.parse(req.body).data.quantity).toBe(2);
// Your outgoing signatures are correct, too:
expect(verify("svix", { secret: "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw", rawBody: req.body, headers: req.headers }).valid).toBe(true);
});
```
`waitForRequest` checks requests that already arrived since the endpoint was created, then long-polls until `timeoutMs`, so the order of "trigger" and "wait" does not matter. It rejects with a `WebhookToolkitError` (`code: "timeout"`) when nothing matched.
To test **your handler** without any network at all, sign the payload locally:
```ts
import { sign } from "webhook-toolkit";
const { headers, body } = sign("stripe", { secret: process.env.STRIPE_WEBHOOK_SECRET!, event: "checkout.session.completed" });
const res = await fetch("http://localhost:3000/api/webhooks/stripe", { method: "POST", headers, body });
expect(res.status).toBe(200);
```
### Playwright
```ts
import { expect, test } from "@playwright/test";
import { WebhookToolkit } from "webhook-toolkit";
const wt = new WebhookToolkit();
test("'Send test event' reaches the configured URL", async ({ page }) => {
const endpoint = await wt.createEndpoint();
await page.goto("/settings/integrations");
await page.getByLabel("Webhook URL").fill(endpoint.url);
await page.getByRole("button", { name: "Save" }).click();
await page.getByRole("button", { name: "Send test event" }).click();
const req = await wt.waitForRequest(endpoint.token, { timeoutMs: 15_000 });
expect(req.method).toBe("POST");
expect(req.headers["content-type"]).toContain("application/json");
});
```
> The package is ESM-only. CommonJS test runners work on Node ≥ 20.19 / 22.12 (native `require(esm)`); Jest needs its [ESM mode](https://jestjs.io/docs/ecmascript-modules).
## Use it from AI agents (MCP)
`webhook-toolkit mcp` is a [Model Context Protocol](https://modelcontextprotocol.io) server. Your coding agent can create a webhook URL, wait for the delivery after triggering it, read the payload, then replay it or send freshly signed events to your **localhost** handler while it fixes the code.
The API key is optional: everything except `list_webhook_urls` and `explain_webhook_request` works anonymously.
**Claude Code**
```sh
claude mcp add webhook-toolkit -- npx -y webhook-toolkit mcp
# with a key:
claude mcp add webhook-toolkit --env WEBHOOK_TOOLKIT_KEY=whk_… -- npx -y webhook-toolkit mcp
```
**Cursor** (`.cursor/mcp.json`), **Windsurf** (`~/.codeium/windsurf/mcp_config.json`), **Claude Desktop** (`claude_desktop_config.json`)
```json
{
"mcpServers": {
"webhook-toolkit": {
"command": "npx",
"args": ["-y", "webhook-toolkit", "mcp"],
"env": { "WEBHOOK_TOOLKIT_KEY": "whk_…" }
}
}
}
```
**VS Code** (`.vscode/mcp.json`)
```json
{
"inputs": [
{ "type": "promptString", "id": "webhook-toolkit-key", "description": "webhook-toolkit.com API key (optional)", "password": true }
],
"servers": {
"webhook-toolkit": {
"type": "stdio",
"command": "npx",
"args": ["-y", "webhook-toolkit", "mcp"],
"env": { "WEBHOOK_TOOLKIT_KEY": "${input:webhook-toolkit-key}" }
}
}
}
```
**Codex CLI** (`~/.codex/config.toml`)
```toml
[mcp_servers.webhook-toolkit]
command = "npx"
args = ["-y", "webhook-toolkit", "mcp"]
env = { WEBHOOK_TOOLKIT_KEY = "whk_…" }
```
**ChatGPT, Claude.ai and other remote-capable clients**: add a custom connector pointing at the hosted server, nothing to install:
```
https://webhook-toolkit.com/mcp
```
(Streamable HTTP, optional `Authorization: Bearer whk_…`.) The hosted server cannot reach your machine, so replaying to `localhost` and `send_signed_webhook` need the local server above.
| Tool | Use it to |
|---|---|
| `create_webhook_url` | Get a public URL to receiWhat people ask about webhook-toolkit
What is THE-KIPDEV/webhook-toolkit?
+
THE-KIPDEV/webhook-toolkit is mcp servers for the Claude AI ecosystem. Receive, forward, sign and verify webhooks from your terminal, your tests and your AI agent. CLI + library + MCP server. It has 0 GitHub stars and its last recorded update is dated 2026-09-18.
How do I install webhook-toolkit?
+
You can install webhook-toolkit by cloning the repository (https://github.com/THE-KIPDEV/webhook-toolkit) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is THE-KIPDEV/webhook-toolkit safe to use?
+
Our security agent has analyzed THE-KIPDEV/webhook-toolkit and assigned a Trust Score of 95/100 (tier: Verified). See the full breakdown of passed checks and flags on this page.
Who maintains THE-KIPDEV/webhook-toolkit?
+
THE-KIPDEV/webhook-toolkit is maintained by THE-KIPDEV. The last recorded GitHub activity is dated 2026-09-18, with 0 open issues.
Are there alternatives to webhook-toolkit?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy webhook-toolkit to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/the-kipdev-webhook-toolkit)<a href="https://claudewave.com/repo/the-kipdev-webhook-toolkit"><img src="https://claudewave.com/api/badge/the-kipdev-webhook-toolkit" alt="Featured on ClaudeWave: THE-KIPDEV/webhook-toolkit" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ
The fastest path to AI-powered full stack observability, even for lean teams.