Skip to main content

mcp-webhook-events

MCP Events for Python MCP servers. Let ChatGPT watch something in your app and act when it changes.

On 29 September 2026 OpenAI added MCP Events to ChatGPT. A user can ask ChatGPT to watch something in your app, ChatGPT subscribes through your MCP server, and your server sends a signed webhook when it changes. ChatGPT then does whatever the user asked.

The official Python MCP SDK (mcp 2.2.0) does not support this yet. Neither does the TypeScript SDK (1.32.0). This package adds it to a server built on the official Python SDK, without forking or patching the SDK.

pip install mcp-webhook-events

Proven with the real ChatGPT

Tested on 2 October 2026 against a live server (UK Business Check, which watches UK companies):

  1. After "Refresh tools", ChatGPT listed the server's company.status_changed event next to its tools.
  2. In a Work chat, "watch company 00445790" made ChatGPT call events/subscribe. The callback was verified with a signed single-use challenge, and ChatGPT echoed it correctly.
  3. A test event was signed and posted. ChatGPT's receiver returned 200, and seconds later ChatGPT posted in the chat by itself: "TEST — TESCO PLC (00445790): the service owner sent a test event; nothing about the company actually changed."

Quick start

from mcp.server import MCPServer
from mcp_webhook_events import EventDefinition, McpEvents

mcp = MCPServer("Ticket watcher")

events = McpEvents(mcp, store="events.sqlite3", definitions=[
    EventDefinition(
        name="ticket.updated",
        description="A support ticket you are watching was updated.",
        input_schema={"type": "object", "properties": {"ticket_id": {"type": "string"}},
                      "required": ["ticket_id"], "additionalProperties": False},
        payload_schema={"type": "object",
                        "properties": {"ticket_id": {"type": "string"}, "summary": {"type": "string"}},
                        "required": ["ticket_id", "summary"], "additionalProperties": False},
    ),
])
events.install()   # adds events/list, events/subscribe, events/unsubscribe and the `events` capability

app = mcp.streamable_http_app(stateless_http=True, json_response=True)

When something changes, from anywhere that can open the same store (a cron job, a webhook handler, a worker):

events.publish("ticket.updated", {"ticket_id": "T-1", "summary": "Customer replied"})
events.deliver_pending()   # sends now. Call it again on a timer to retry failures

By default an event goes to every live subscription whose arguments match fields of the same name in the event data. Pass matches= to an EventDefinition for anything cleverer.

A full example is in examples/watch_server.py.

What it handles for you

Everything on OpenAI's MCP Events page that a server has to do:

Requirement How
Advertise events in server/discover Middleware on the result (see below)
events/list, events/subscribe, events/unsubscribe Registered on the SDK's own handler table
whsec_ secrets of 24 to 64 bytes Checked before anything else
Arguments validated against your inputSchema jsonschema
Callback verification with a fresh, signed, single-use challenge, constant-time compare Yes, cached per caller and URL for 24 hours
JSON-RPC -32015 with data.reason when verification fails Yes
Deterministic, idempotent subscription IDs, key order ignored SHA-256 of caller, URL, event name and canonical arguments
Subscriptions survive restarts SQLite (WAL)
Expiry, refreshBefore, ttlMs Default 7 days, minimum 1 hour, never "forever"
Secret rotation Signs with old and new keys during a 24 hour window
Standard Webhooks signing, webhook-id = eventId, X-MCP-Subscription-Id Uses the official standardwebhooks package
Retries with backoff, same event ID, fresh signature 30s, 2m, 10m, 30m, 1h, 2h, then gives up
No retry on 410 or 413, and 410 drops the subscription Yes
One event per request, 256 KiB maximum Enforced
HTTPS only, public addresses only, checked at connect time, no redirects Yes, with TLS still checked against the original hostname
Forget what isn't needed Expired subscriptions, their URLs and secrets, and old delivery history are purged

Not included, because ChatGPT does not support them either: polling, streaming, replay cursors and the draft's gap and terminated notifications.

Three things that had to be worked out

These are the parts that are not obvious from the documentation, in case you are building this yourself.

  1. Custom methods are allowed. The SDK's low-level server has add_request_handler(method, params_type, handler), and it accepts events/* methods over the 2026-07-28 Streamable HTTP transport. No fork is needed.
  2. Clients must send Mcp-Method. On the modern transport, a request without an Mcp-Method header matching the body's method gets rejected with -32020. ChatGPT sends it, but your own test client needs to as well.
  3. The SDK drops unknown capabilities. ServerCapabilities ignores unknown keys, and spec results are cleaned against the schema, so events: {} never reaches the wire if you put it in the result. This package adds it in middleware, after the result is serialised.

Testing

pip install -e ".[test]"
python -m unittest discover -s tests -v
python tests/mutation_check.py     # breaks the code 25 ways, and every one must be caught

The tests run a local receiver that plays ChatGPT. It checks every signature with the official Standard Webhooks verifier rather than this package's own code. mutation_check.py proves the tests catch real faults: unverified callbacks, wrong signatures, ignored filters, retrying a 410, leaking to private addresses and so on.

Status

Version 0.1.0. MCP Events is still a draft extension, so details may change, and the official SDKs will probably add support at some point. Until they do, this is a small, tested way to use it today.

Issues and pull requests are welcome.

Licence

MIT. Made by Shaun Owen.

Metadata

Release files for mcp-webhook-events 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-webhook-events 0.1.0
File Size Uploaded
mcp_webhook_events-0.1.0.tar.gz 19.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-webhook-events 0.1.0
File Interpreter ABI Platform
mcp_webhook_events-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.7 kB

Release files / mcp_webhook_events-0.1.0.tar.gz

Download URL mcp_webhook_events-0.1.0.tar.gz
Size 19.2 kB
Tags Source
SHA-256 checksum
How to use checksums
d10921bae14b9176e2d9e67387f83a3ac87d45ec7ca7dfc28a91ea52b62b8167
BLAKE2b-256 checksum
How to use checksums
5e1257c2c5d9c87f5e62e7689111dd805643d0545cda91f93e22b089752091e0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release files / mcp_webhook_events-0.1.0-py3-none-any.whl

Download URL mcp_webhook_events-0.1.0-py3-none-any.whl
Size 12.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
eb3c16a71de19b0d779e082f45add4b3f533d6e55ddb61e5d821ef010420a164
BLAKE2b-256 checksum
How to use checksums
9e1258a7f9282173100096837e5a9e8cb8b46d5ad0a0ca7654d6146adcfdf0a7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page