Skip to content

feat: add webhook support for push notifications - #101

Merged
evansenter merged 3 commits into
mainfrom
feat/webhooks
Feb 6, 2026
Merged

feat: add webhook support for push notifications#101
evansenter merged 3 commits into
mainfrom
feat/webhooks

Conversation

@evansenter

Copy link
Copy Markdown
Owner

Summary

Add HTTP webhook support for real-time event delivery instead of polling.

Features

  • register_webhook MCP tool with channel/event_type filtering
  • Optional HMAC-SHA256 signing with shared secret for verification
  • Async non-blocking dispatch with retries (2 retries, exponential backoff)
  • CLI commands: webhook register/list/unregister
  • Prefix matching for channels (e.g., session: matches all session events)

Usage

MCP

# Register a webhook for all events
register_webhook(url="https://your-server.com/events")

# Filter by channel (prefix matching)
register_webhook(url="https://...", channel="session:")

# With HMAC signature
register_webhook(url="https://...", secret="shared-secret")

CLI

agent-event-bus-cli webhook register --url https://example.com/hook
agent-event-bus-cli webhook list
agent-event-bus-cli webhook unregister 1

Payload

{
  "event_id": 123,
  "event_type": "task_completed",
  "payload": "...",
  "session_id": "abc",
  "timestamp": "2026-01-31T08:00:00",
  "channel": "all"
}

With secret configured, includes X-Event-Bus-Signature: sha256=<hmac> header.

Changes

  • storage.py: Added Webhook dataclass, webhooks table, CRUD methods
  • server.py: Added webhook MCP tools + async dispatch on publish
  • cli.py: Added webhook subcommand group
  • pyproject.toml: Added httpx dependency
  • 16 new tests, 279 total passing

Use Case

This enables agents like Clawdbot to receive real-time notifications when events are published to the bus, instead of polling.

@claude

claude Bot commented Jan 31, 2026

Copy link
Copy Markdown

Prompt: evansenter/dotfiles/.../claude-review.md

Code Review

Summary

This PR adds HTTP webhook support for real-time event delivery, including register_webhook, list_webhooks, and unregister_webhook MCP tools, CLI commands, storage layer with schema migration, and HMAC-SHA256 signature verification. The implementation includes async non-blocking dispatch with retries and channel/event type filtering.

Issues Found

Critical

None

Important

  • src/agent_event_bus/guide.md - Missing webhook documentation. Per CLAUDE.md: "Usage guide: agent-event-bus://guide resource. Keep it updated when changing APIs." The guide.md should be updated to document the new webhook tools, matching the detailed README additions.

  • src/agent_event_bus/server.py:352-355 - The verbose docstring for register_webhook includes a Returns: section and example payload, which violates CLAUDE.md guidelines: "Exclude (put in guide.md instead): Returns: sections (JSON results are self-documenting), Usage examples and patterns." Keep docstrings minimal and move details to guide.md.

Suggestions

  • src/agent_event_bus/storage.py:368-381 - The webhooks table is created both in _init_db() and in the migration migrate_v3. While idempotent (CREATE TABLE IF NOT EXISTS), this duplication is inconsistent with how sessions/events tables are handled - they are only in _init_db(). Consider removing the table creation from _init_db() to let the migration handle it, matching the pattern used for other v2+ additions.

  • src/agent_event_bus/server.py:293-296 - Creating a new httpx.AsyncClient for every webhook dispatch is inefficient. Consider creating a module-level client or lazily-initialized singleton that can be reused across dispatches, benefiting from connection pooling.

  • tests/test_webhooks.py - The tests cover storage operations, matching logic, and dispatch mocking, but there is no integration test for the full publish_event -> _schedule_webhook_dispatch flow. Consider adding a test that verifies webhooks are actually scheduled when publish_event is called.

Verdict

REQUEST_CHANGES - Missing guide.md documentation for the new API (required per CLAUDE.md), and verbose docstring violates project conventions. The webhook implementation itself is well-structured with good test coverage for the core functionality.


Automated review by Claude Code

evansenter and others added 2 commits January 31, 2026 08:23
Add HTTP webhook support for real-time event delivery instead of polling.

Features:
- register_webhook MCP tool with channel/event_type filtering
- Optional HMAC-SHA256 signing with shared secret
- Async non-blocking dispatch with retries
- CLI commands: webhook register/list/unregister
- Prefix matching for channels (e.g., 'session:' matches all sessions)

Storage:
- New webhooks table (schema version 3)
- Webhook CRUD operations
- Smart matching for channel and event type filters

Testing:
- 16 new tests for webhook functionality
- All 279 tests passing
- Add exception callback for background webhook dispatch tasks
- Use threading instead of blocking asyncio.run() in fallback path
- Narrow exception catching to httpx-specific errors (TimeoutException, RequestError)
- Log exception details from asyncio.gather results
- Add webhook documentation to guide.md
- Add MCP tool tests, integration tests, and retry behavior tests
- Clean up webhooks between tests in conftest

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@claude

claude Bot commented Jan 31, 2026

Copy link
Copy Markdown

Prompt: evansenter/dotfiles/.../claude-review.md

Code Review

Summary

This PR adds HTTP webhook support for real-time event push notifications. It includes register_webhook, list_webhooks, and unregister_webhook MCP tools and CLI commands, storage layer with schema migration, HMAC-SHA256 signature verification, and async non-blocking dispatch with retries. The implementation is well-structured with comprehensive test coverage (16 new tests).

Issues Found

Critical

None

Important

  • src/agent_event_bus/server.py:486-509 - The register_webhook docstring includes a payload example and describes return values in detail. Per CLAUDE.md: "Exclude (put in guide.md instead): Returns: sections (JSON results are self-documenting), Usage examples and patterns." The docstring should be minimal like:
    """Register a webhook to receive event notifications via HTTP POST.
    
    Args:
        url: HTTP(S) endpoint to POST events to
        channel: Filter to specific channel (None = all). Supports prefix matching.
        event_types: Filter to specific event types (None = all)
        secret: Shared secret for HMAC signing (optional)
    """

Suggestions

  • src/agent_event_bus/storage.py:368-381 vs storage.py:104-115 - The webhooks table is created both in _init_db() and in migrate_v3. While idempotent (CREATE TABLE IF NOT EXISTS), this is inconsistent with other tables - sessions/events are only in _init_db(). For consistency, consider either: (a) removing from _init_db() and letting the migration handle it, or (b) documenting why webhooks differs.

  • src/agent_event_bus/server.py:381 - Creating a new httpx.AsyncClient for every webhook dispatch misses connection pooling benefits. Consider a module-level client or lazily-initialized singleton that can be reused across dispatches. This is a minor optimization but could matter at scale.

  • src/agent_event_bus/server.py:529,537,565 - The list_webhooks and unregister_webhook docstrings include Returns: sections. These should be removed per CLAUDE.md conventions.

Verdict

REQUEST_CHANGES - The verbose docstring in register_webhook violates project conventions documented in CLAUDE.md. The suggestions are minor improvements.


Automated review by Claude Code

Address PR reviewer feedback:
- Use module-level httpx.AsyncClient with _get_webhook_client() for connection pooling
- Trim verbose docstrings (remove Returns sections, keep minimal arg descriptions)
- Fix test mocks to use new _get_webhook_client pattern

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@evansenter

Copy link
Copy Markdown
Owner Author

Feedback Addressed

Implemented

  • [Important] Verbose register_webhook docstring - Trimmed to minimal args description, removed Returns/examples
  • [Suggestion] Returns sections in list_webhooks/unregister_webhook - Removed Returns sections from both docstrings
  • [Suggestion] Connection pooling for httpx client - Added module-level _get_webhook_client() for shared AsyncClient

Skipped

  • [Suggestion] Webhooks table in guide.md - Kept for discoverability; this is the canonical documentation

🤖 Generated with Claude Code

@claude

claude Bot commented Jan 31, 2026

Copy link
Copy Markdown

Prompt: evansenter/dotfiles/.../claude-review.md

Code Review

Summary

This PR adds HTTP webhook support for real-time event push notifications, including register_webhook, list_webhooks, and unregister_webhook MCP tools, CLI commands, storage layer with schema migration, HMAC-SHA256 signature verification, and async non-blocking dispatch with retries. The implementation is well-structured with comprehensive test coverage (16 new tests covering storage, matching, dispatch, and integration).

Issues Found

Critical

None

Important

None

Suggestions

None

Previously Addressed (Filtered)

  • server.py - Verbose register_webhook docstring (Implemented: trimmed to minimal args description)
  • server.py - Returns sections in list_webhooks/unregister_webhook (Implemented: removed)
  • server.py - Connection pooling for httpx client (Implemented: added _get_webhook_client())
  • storage.py - Webhooks table in both _init_db() and migration (Skipped: kept for discoverability)
  • guide.md - Missing webhook documentation (Implemented: comprehensive docs added)

5 items from prior feedback rounds were not re-raised.

Verdict

APPROVE - Code looks good, no issues found. All prior feedback has been addressed. The implementation is clean, well-documented, and thoroughly tested.


Automated review by Claude Code

@evansenter
evansenter merged commit 26107be into main Feb 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant