|
| 1 | +# github-webhook-mcp |
| 2 | + |
| 3 | +Stdio MCP proxy that bridges local MCP clients (Claude Desktop, Claude Code, Codex, etc.) to a remote [github-webhook-mcp](https://github.com/Liplus-Project/github-webhook-mcp) Cloudflare Worker. The Worker receives GitHub webhook deliveries, persists them in a per-tenant Durable Object (SQLite), and exposes them through MCP tools and a real-time WebSocket stream. |
| 4 | + |
| 5 | +This package is the **client-side proxy only**. Webhook ingestion, tenant routing, persistence, and the MCP server itself run on the Worker. See the [main repository](https://github.com/Liplus-Project/github-webhook-mcp) for architecture and self-hosting instructions. |
| 6 | + |
| 7 | +## What this proxy does |
| 8 | + |
| 9 | +- Speaks stdio MCP locally to your client. |
| 10 | +- Forwards `tools/call` to the Worker's Streamable HTTP MCP endpoint (`/mcp`). |
| 11 | +- Optionally maintains a WebSocket connection to the Worker's `/events` endpoint and re-emits incoming webhook events as Claude Code `claude/channel` notifications (real-time push, no polling). |
| 12 | +- Handles OAuth 2.1 with PKCE against the Worker (browser-based localhost callback) and Dynamic Client Registration (RFC 7591). |
| 13 | +- Caches access and refresh tokens under `~/.github-webhook-mcp/` (mode `0600`) and refreshes them silently before expiry. |
| 14 | + |
| 15 | +## Requirements |
| 16 | + |
| 17 | +- Node.js >= 18 |
| 18 | +- A reachable github-webhook-mcp Worker (the public preview default is `https://github-webhook.smgjp.com`; you can also point at your own deployment) |
| 19 | +- A web browser on the same machine (used once for OAuth authorization) |
| 20 | +- A GitHub App installed on the accounts/organizations whose events you want to receive (the Worker resolves your accessible installations automatically after OAuth) |
| 21 | + |
| 22 | +## Install / Run |
| 23 | + |
| 24 | +The proxy is published to npm and exposes a `github-webhook-mcp` binary. |
| 25 | + |
| 26 | +Run directly with `npx` (no global install required): |
| 27 | + |
| 28 | +```bash |
| 29 | +npx github-webhook-mcp |
| 30 | +``` |
| 31 | + |
| 32 | +Or install globally: |
| 33 | + |
| 34 | +```bash |
| 35 | +npm install -g github-webhook-mcp |
| 36 | +github-webhook-mcp |
| 37 | +``` |
| 38 | + |
| 39 | +The first run opens a browser window to complete OAuth against the Worker. After authorization, tokens are stored under `~/.github-webhook-mcp/` and refreshed automatically. |
| 40 | + |
| 41 | +## Client configuration |
| 42 | + |
| 43 | +### Claude Desktop / Claude Code |
| 44 | + |
| 45 | +Add the server to your MCP client configuration. Example for Claude Desktop (`claude_desktop_config.json`): |
| 46 | + |
| 47 | +```json |
| 48 | +{ |
| 49 | + "mcpServers": { |
| 50 | + "github-webhook": { |
| 51 | + "command": "npx", |
| 52 | + "args": ["-y", "github-webhook-mcp"] |
| 53 | + } |
| 54 | + } |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +To target a self-hosted Worker, set the `WEBHOOK_WORKER_URL` environment variable: |
| 59 | + |
| 60 | +```json |
| 61 | +{ |
| 62 | + "mcpServers": { |
| 63 | + "github-webhook": { |
| 64 | + "command": "npx", |
| 65 | + "args": ["-y", "github-webhook-mcp"], |
| 66 | + "env": { |
| 67 | + "WEBHOOK_WORKER_URL": "https://your-worker.example.workers.dev" |
| 68 | + } |
| 69 | + } |
| 70 | + } |
| 71 | +} |
| 72 | +``` |
| 73 | + |
| 74 | +### Codex (`config.toml`) |
| 75 | + |
| 76 | +```toml |
| 77 | +[mcp.github-webhook-mcp] |
| 78 | +command = "npx" |
| 79 | +args = ["-y", "github-webhook-mcp"] |
| 80 | + |
| 81 | +[mcp.github-webhook-mcp.env] |
| 82 | +WEBHOOK_WORKER_URL = "https://your-worker.example.workers.dev" |
| 83 | +WEBHOOK_CHANNEL = "0" |
| 84 | +``` |
| 85 | + |
| 86 | +`WEBHOOK_CHANNEL=0` disables the WebSocket real-time channel. Set it to `0` for clients that do not support `claude/channel` notifications (Claude Desktop, Codex). Leave it at the default to enable real-time push for Claude Code. |
| 87 | + |
| 88 | +### Real-time channel notifications (Claude Code only) |
| 89 | + |
| 90 | +When `WEBHOOK_CHANNEL` is enabled (the default), the proxy declares the `claude/channel` experimental capability and re-emits new webhook events as channel notifications. To make them visible in a Claude Code session, load the channel: |
| 91 | + |
| 92 | +```bash |
| 93 | +claude --dangerously-load-development-channels server:github-webhook-mcp |
| 94 | +``` |
| 95 | + |
| 96 | +Notifications are one-way: they include event type, repo, action, title, sender, and URL. There is no reply tool. |
| 97 | + |
| 98 | +## Configuration |
| 99 | + |
| 100 | +| Variable | Required | Default | Description | |
| 101 | +|---|---|---|---| |
| 102 | +| `WEBHOOK_WORKER_URL` | No | `https://github-webhook.smgjp.com` | Base URL of the Cloudflare Worker that exposes the MCP endpoint, the WebSocket stream, and OAuth metadata. | |
| 103 | +| `WEBHOOK_CHANNEL` | No | `1` (enabled) | Set to `0` to disable the WebSocket connection and `claude/channel` notifications. | |
| 104 | + |
| 105 | +OAuth client registration and tokens are stored in: |
| 106 | + |
| 107 | +- `~/.github-webhook-mcp/oauth-client.json` (dynamic client registration) |
| 108 | +- `~/.github-webhook-mcp/oauth-tokens.json` (access + refresh tokens) |
| 109 | + |
| 110 | +Delete these files to force a fresh authorization flow. |
| 111 | + |
| 112 | +> **Note on the default Worker URL.** `https://github-webhook.smgjp.com` is a preview instance offered for evaluation. It has no SLA and may change or stop without notice. For production use, deploy your own Worker (see the main repository's [installation guide](https://github.com/Liplus-Project/github-webhook-mcp/blob/main/docs/installation.md)) and set `WEBHOOK_WORKER_URL` accordingly. |
| 113 | +
|
| 114 | +## Tools exposed |
| 115 | + |
| 116 | +All tools are read-only except `mark_processed`. |
| 117 | + |
| 118 | +| Tool | Description | |
| 119 | +|---|---| |
| 120 | +| `get_pending_status` | Lightweight snapshot of pending (unprocessed) webhook events: pending count, latest received timestamp, and event types. Use this for periodic polling before requesting details. | |
| 121 | +| `list_pending_events` | Summary list of pending events (`limit`: 1-100, default 20). Returns metadata only — `id`, `type`, `action`, `repo`, `sender`, `number`, `title`, `url`, `received_at` — without the full payload. | |
| 122 | +| `get_event` | Full payload for a single webhook event by `event_id`. | |
| 123 | +| `get_webhook_events` | Pending events with full payloads. Prefer `get_pending_status` or `list_pending_events` for polling and only fall back to this when you really need everything. | |
| 124 | +| `mark_processed` | Mark an event as processed by `event_id` so it will no longer appear in pending queries. Required to keep the pending queue from growing unbounded. | |
| 125 | + |
| 126 | +### Recommended polling flow |
| 127 | + |
| 128 | +1. Poll `get_pending_status()` periodically (e.g. every 60 seconds). |
| 129 | +2. If `pending_count > 0`, call `list_pending_events()` for summaries. |
| 130 | +3. Call `get_event(event_id)` only for events that need the full payload. |
| 131 | +4. Call `mark_processed(event_id)` after handling each event. |
| 132 | + |
| 133 | +If real-time channel notifications are enabled (Claude Code), step 1 can be skipped — the proxy will push event summaries as soon as the Worker receives them. You still need to call `mark_processed` to clear the queue. |
| 134 | + |
| 135 | +## Authentication flow |
| 136 | + |
| 137 | +1. On first tool call (or on startup if cached tokens exist), the proxy discovers OAuth metadata at `${WEBHOOK_WORKER_URL}/.well-known/oauth-authorization-server`. |
| 138 | +2. It performs Dynamic Client Registration (RFC 7591) if no client is cached. |
| 139 | +3. It starts a one-shot localhost HTTP listener on a random port and opens the browser to the Worker's authorization endpoint. |
| 140 | +4. After you approve, the Worker redirects to `http://127.0.0.1:<port>/callback` with an authorization code. |
| 141 | +5. The proxy exchanges the code for tokens (PKCE S256) and saves them. |
| 142 | +6. The Worker resolves your accessible GitHub installations (your user account plus any organizations where the GitHub App is installed) and binds them to the OAuth session, so events from any of those tenants surface through the same MCP session. |
| 143 | +7. Subsequent calls reuse the access token and silently refresh it five minutes before expiry. On `401` from the Worker, the proxy invalidates its cached tokens and re-authenticates automatically. |
| 144 | + |
| 145 | +The authorization code is delivered directly to the local listener; it never leaves your machine. |
| 146 | + |
| 147 | +## Troubleshooting |
| 148 | + |
| 149 | +- **Browser does not open.** The proxy logs the authorization URL to stderr; copy it into a browser manually. |
| 150 | +- **`OAuth callback timed out after 5 minutes`.** Re-invoke any tool to restart the flow. |
| 151 | +- **`Failed to reach worker`.** Check that `WEBHOOK_WORKER_URL` is correct and reachable from your machine. |
| 152 | +- **`Authentication failed after retry`.** Cached tokens were rejected and re-authentication did not succeed. Remove `~/.github-webhook-mcp/oauth-tokens.json` and retry. |
| 153 | +- **No events arriving.** Confirm that the GitHub App is installed on the target account/organization and that webhook deliveries are succeeding on the GitHub App's *Advanced* → *Recent Deliveries* page. The Worker only sees events for installations linked to your authenticated account. |
| 154 | +- **`429` from the Worker.** The per-tenant event quota (default 10,000) has been exceeded. Process the backlog with `mark_processed` to free space. |
| 155 | +- **Real-time notifications not showing in Claude Code.** Make sure `WEBHOOK_CHANNEL` is not set to `0` and that Claude Code was launched with `--dangerously-load-development-channels server:github-webhook-mcp`. |
| 156 | +- **Stale credentials.** Remove `~/.github-webhook-mcp/oauth-tokens.json` (and optionally `oauth-client.json`) and retry. |
| 157 | + |
| 158 | +## Links |
| 159 | + |
| 160 | +- Source and architecture: <https://github.com/Liplus-Project/github-webhook-mcp> |
| 161 | +- Self-hosting the Worker: [docs/installation.md](https://github.com/Liplus-Project/github-webhook-mcp/blob/main/docs/installation.md) |
| 162 | +- Requirements spec: [docs/0-requirements.md](https://github.com/Liplus-Project/github-webhook-mcp/blob/main/docs/0-requirements.md) |
| 163 | +- Issue tracker: <https://github.com/Liplus-Project/github-webhook-mcp/issues> |
| 164 | + |
| 165 | +## License |
| 166 | + |
| 167 | +Apache-2.0. See the [LICENSE](https://github.com/Liplus-Project/github-webhook-mcp/blob/main/LICENSE) and [NOTICE](https://github.com/Liplus-Project/github-webhook-mcp/blob/main/NOTICE) files in the main repository. |
0 commit comments