Atlas / MCP servers / hookdeck / mcp-events-bridge

mcp-events-bridgeCAUTION

mcp/hookdeck/mcp-events-bridge

Turn third-party webhooks into MCP Events for AI agents, with Hookdeck Event Gateway receiving and delivering them. Built-in providers: Resend and GitHub.

Verdict
CAUTION
Grade
D
Trust score
65 /100
Exposed tools
9 8r · 1w · 0d
Transport
streamable-http
License
MIT
Stars
1
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

Turn webhooks into MCP Events, so AI agents can act the moment something happens: an email arrives, an issue is opened, a workflow fails. Hookdeck Event Gateway receives, verifies and delivers every event.

For example, an email arrives at a Resend address, and ChatGPT, subscribed through the bridge, acts on it:

Why

MCP Events is an experimental MCP extension that lets an agent subscribe to events instead of polling for them, and ChatGPT supports it. But an agent can only subscribe to MCP servers that implement it, and almost none do yet, while nearly every service already sends webhooks.

The bridge closes that gap:

  • Webhooks become MCP Events. Built-in webhook providers: Resend inbound email, GitHub, and generic webhooks from any HTTP sender, such as a service you've built, verified with HMAC, Standard Webhooks, Basic auth or an API key. Add others with defineProvider.
  • Subscribers choose what wakes them. Events have filters, such as an email's sender, or a GitHub repository and action.
  • Every event verified, delivered and recorded. Event Gateway verifies provider signatures, retries failed deliveries, drops duplicates within an hour, and keeps a record of every event and attempt.
  • Stateless. Each subscription is an Event Gateway connection, so there's no database.

It's for developers who want agents

Read from source at commit 54648c40ef98OBSERVED · 2026-10-11
02

Connect

Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.

claude-code
claude mcp add mcp-events-bridge --env FILLS_WEBHOOK_SECRET=${FILLS_WEBHOOK_SECRET} --env GITHUB_WEBHOOK_SECRET=${GITHUB_WEBHOOK_SECRET} --env HOOKDECK_API_KEY=${HOOKDECK_API_KEY} -- npx -y @hookdeck/[email protected]
claude-desktop
{
  "mcpServers": {
    "mcp-events-bridge": {
      "command": "npx",
      "args": [
        "-y",
        "@hookdeck/[email protected]"
      ],
      "env": {
        "FILLS_WEBHOOK_SECRET": "${FILLS_WEBHOOK_SECRET}",
        "GITHUB_WEBHOOK_SECRET": "${GITHUB_WEBHOOK_SECRET}",
        "HOOKDECK_API_KEY": "${HOOKDECK_API_KEY}"
      }
    }
  }
}
03

Exposed tools (9)

8 read · 1 write · 0 destructive.

ToolRiskDescription
create_tunnel_urlwrite
email.receivedreadAn email arrived at a Resend receiving address. Carries metadata only (sender, recipients, subject); fetch the body with Resend\
get_eventread
list_eventsread
list_providersread
list_tunnel_urlsread
otherread{
poll_eventsread
wait_for_eventread
04

Trust audit

CAUTIONgrade D · trust 65/100 Install with care. The audit found things worth knowing before you trust its output.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeWARN
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (3 observation(s))
Network
declared (6 observation(s))
Shell
declared (3 observation(s))
Dependencies
not all pinned
Secrets in source
found

Findings (25)

MEDIUMInformation disclosure · disclose.log_secret · CWE-209, CWE-532
src/cli.ts:94
console.log(`\nGenerated an MCP secret. Set this line ${where} before running serve, and keep it private (the URL is a credential):`);
MEDIUMInformation disclosure · disclose.log_secret · CWE-209, CWE-532
src/cli.ts:95
console.log(`BRIDGE_MCP_SECRET=${report.mcp.secret}`);
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
scripts/e2e.ts:283
bridgeUrl = `http://127.0.0.1:${port}`;
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
skills/mcp-events-bridge/SKILL.md:67
- **Success:** `[bridge] hookdeck listen connected: ...` and `[bridge] MCP endpoint: http://127.0.0.1:8080/mcp/<BRIDGE_MCP_SECRET>`. Keep it running; stop it with Ctrl-C (or `kill` its PID), which als
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
test/core/event-history.test.ts:9
webhook({ id, verification: { type: 'hmac', algorithm: 'sha256', encoding: 'hex', header: 'x-signature', secret: 'a-long-enough-secret' }, events: ['order.filled'], eventId: { header: 'x-delivery-id'
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
test/core/event-history.test.ts:72
const noHeader = webhook({ id: 'plain', verification: { type: 'hmac', algorithm: 'sha256', encoding: 'hex', header: 'x-signature', secret: 'a-long-enough-secret' }, events: ['ping'], eventId: { field:
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
test/core/poll.test.ts:13
verification: { type: 'hmac', algorithm: 'sha256', encoding: 'hex', header: 'x-signature', secret: 'a-long-enough-secret' },
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
test/core/providers/webhook.test.ts:8
const SECRET = 'fills-secret-0123456789';
MEDIUMHard-coded secrets · secret.generic · CWE-798, CWE-321
test/core/relay.test.ts:165
verification: { type: 'hmac', algorithm: 'sha256', encoding: 'hex', header: 'x-signature', secret: 'fills-secret-0123456789' },
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
test/core/callback.test.ts:3
import { CallbackUrlError, parseCallbackUrl, verifyEndpoint, type CallbackTransport, type PostResult } from '../../src/core/callback.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
test/core/callback.test.ts:4
import { generateWebhookSecret } from '../../src/core/secret.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
test/core/callbacks.test.ts:6
import { verifyEndpoint } from '../../src/core/callback.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
test/core/callbacks.test.ts:7
import { CallbackInputError, CallbackRegistry, RESEND_ID_HEADER, RETRY_OF_HEADER, type CallbacksHookdeck } from '../../src/core/callbacks.js';
LOWFilesystem / path · fs.traversal · CWE-22, CWE-59
test/core/callbacks.test.ts:8
import { Catalog } from '../../src/core/catalog.js';
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
test/host/callback-transport.test.ts:17
'127.0.0.1', '10.1.2.3', '172.16.0.1', '172.31.255.255', '192.168.1.1', '169.254.169.254', '100.64.0.1',
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.metadata_ip · CWE-200, CWE-319
test/host/callback-transport.test.ts:27
it.each(['https://127.0.0.1/x', 'https://[::1]/x', 'https://10.0.0.5/x', 'https://169.254.169.254/latest', 'https://localhost/x'])(
Why it matters. cloud metadata endpoint: the classic SSRF credential grab
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
README.md:106
6. **Connect an agent:** see [Connect ChatGPT](#connect-chatgpt) or [Connect Claude Code](#connect-claude-code). ChatGPT has to reach the bridge's MCP endpoint, so [deploy](#deploy-to-flyio) the bridg
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
skills/mcp-events-bridge/references/claude-code.md:16
For a local bridge, use the URL `serve` prints (`http://127.0.0.1:8080/mcp/<BRIDGE_MCP_SECRET>`).
LOWNetwork egress · net.raw_ip · CWE-200, CWE-319
skills/mcp-events-bridge/references/hermes-agent.md:51
npx -y @modelcontextprotocol/inspector --cli "http://127.0.0.1:8080/mcp/$BRIDGE_MCP_SECRET" --transport http \
LOWSupply chain · supply.unpinned · CWE-829, CWE-1357
package.json
@modelcontextprotocol/client, @modelcontextprotocol/node, @modelcontextprotocol/server, magicast, standardwebhooks, tsx, zod, @types/node
Why it matters. 11 dependency range(s) float
Fix. pin exact versions or ship a lockfile
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
AGENTS.md:19
| The e2e tests | A verified Resend sending domain, and a key that can send email; or, with `E2E_SOURCE=webhook`, the `fills` secret instead (no email sent) | `RESEND_INBOUND_ADDRESS`, `RESEND_TEST_FR
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
README.md:17
- **Webhooks become MCP Events.** Built-in webhook providers: Resend inbound email, GitHub, and generic webhooks from any HTTP sender, such as a service you've built, verified with HMAC, Standard Webh
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
docs/ARCHITECTURE.md:224
- **Paths under a tunnel URL.** A tunnel URL also covers any path under it: the bridge treats `<tunnel URL>/<path>` as that tunnel's, signing the challenge and deliveries with the source's secret, re-
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
docs/ARCHITECTURE.md:335
| [Hermes Agent](https://github.com/NousResearch/hermes-agent/pull/132908) | Draft PR, 4 Oct; not in a release | Webhook, to a local receiver (`POST /mcp/events/webhook/<local id>`, one public base UR
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine
LOWPrompt injection · prompt.transfer_instruction · CWE-94, CWE-1427
docs/ARCHITECTURE.md:366
- **One HTTP listener** (`BRIDGE_PORT`): `POST /inbound/<instance id>` (Hookdeck-signed only, bodies up to 10 MiB, else `413`; runs the relay; `200` or `5xx`) and the MCP endpoint at `/mcp/<secret>` (
Why it matters. an instruction to move sensitive data to an outside destination
Fix. remove; a skill never needs the user's secrets off the machine

Gates applied: no_behavioural_pass.

Audited 2026-10-11 · audit v0.4.1 · source sha 54648c40ef98full audit observations/trust-audit/mcp-server/hookdeck__mcp-events-bridge.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-1154648c40ef98CAUTIOND65first audit
06

Questions

What is the mcp-events-bridge MCP server?

Turn third-party webhooks into MCP Events for AI agents, with Hookdeck Event Gateway receiving and delivering them. Built-in providers: Resend and GitHub.

What tools does mcp-events-bridge expose?

9 in total: 8 read-only, 1 that write, and 0 that can delete or overwrite. Every one is listed on this page with its risk.

Is mcp-events-bridge safe to connect to an agent?

With care. The audit graded it D (65/100) and found 25 things worth knowing before you trust this server, listed below with the exact line each was found on.

What credentials does mcp-events-bridge need?

It reads FILLS_WEBHOOK_SECRET, GITHUB_WEBHOOK_SECRET and HOOKDECK_API_KEY from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.

How does mcp-events-bridge run?

It speaks streamable-http, so it runs as a service you connect to over the network. It is published on npm as @hookdeck/mcp-events-bridge at 0.4.0.

How current is this page?

The grade is for one exact copy of the source (54648c40ef98), read on 2026-10-11. The repository is watched and re-audited when it changes.

Advertisement