diff --git a/.gitignore b/.gitignore index d0426f9..b906d95 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ codex-runs/ __pycache__/ *.pyc mcp-server/node_modules/ +mcp-server/*.mcpb diff --git a/README.md b/README.md index e71ee31..1478065 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,89 @@ # github-webhook-mcp -GitHub webhook receiver as an MCP server. +GitHub webhook receiver and local MCP extension for GitHub notification workflows. -Receives GitHub webhook events and enables Lin and Lay to autonomously handle PR reviews and issue management. -It supports two operating styles: +## Description -- MCP polling: store webhook events and let an AI poll lightweight summaries. -- Channel push: push new events into a Claude Code session in real-time via `claude/channel`. -- Direct trigger: run a command immediately for each stored event. +`github-webhook-mcp` receives GitHub webhook events, persists them to a local `events.json`, and exposes them to AI agents through MCP tools. +It is designed for notification-style workflows where an AI can poll lightweight summaries, inspect a single event in detail, and mark handled events as processed. Detailed behavior, event metadata, trigger semantics, and file responsibilities live in [docs/0-requirements.md](docs/0-requirements.md). -## Quick Start +## Features -### 1. Install dependencies +- Receives GitHub webhook events over HTTPS and persists them locally. +- Exposes pending events to MCP clients through lightweight polling tools. +- Supports real-time `claude/channel` notifications in Claude Code. +- Supports direct trigger mode for immediate Codex reactions per event. +- Ships as a Node-based `.mcpb` desktop extension and as an `npx` MCP server. -```bash -pip install -r requirements.txt +## Installation + +### Claude Desktop — Desktop Extension (.mcpb) + +Download `mcp-server.mcpb` from [Releases](https://github.com/Liplus-Project/github-webhook-mcp/releases), then: + +1. Open Claude Desktop → **Settings** → **Extensions** → **Advanced settings** → **Install Extension...** +2. Select the `.mcpb` file +3. Enter the path to your `events.json` when prompted + +### Claude Desktop / Claude Code — npx + +Add to your Claude MCP config (`claude_desktop_config.json` or project settings): + +```json +{ + "mcpServers": { + "github-webhook-mcp": { + "command": "npx", + "args": ["github-webhook-mcp"], + "env": { + "EVENTS_JSON_PATH": "/path/to/events.json" + } + } + } +} ``` -### 2. Start webhook receiver for MCP polling +### Codex — config.toml -```bash -WEBHOOK_SECRET=your_secret python main.py webhook --port 8080 --event-profile notifications +```toml +[mcp.github-webhook-mcp] +command = "npx" +args = ["github-webhook-mcp"] + +[mcp.github-webhook-mcp.env] +EVENTS_JSON_PATH = "/path/to/events.json" ``` -### 3. Optional: start webhook receiver with direct Codex reactions +### Python (legacy) -Use the bundled Codex wrapper if you want the webhook to launch `codex exec` immediately. -Put `--trigger-command` last when a service manager splits the remaining tokens for you. +```json +{ + "mcpServers": { + "github-webhook-mcp": { + "command": "python", + "args": ["/path/to/github-webhook-mcp/main.py", "mcp"] + } + } +} +``` + +## Configuration + +### 1. Install receiver dependencies ```bash -python main.py webhook \ - --port 8080 \ - --event-profile notifications \ - --trigger-command "python codex_reaction.py --workspace /path/to/workspace --output-dir /path/to/github-webhook-mcp/codex-runs" +pip install -r requirements.txt ``` -If you want Codex to resume an existing app thread instead of writing markdown output: +### 2. Start the webhook receiver -```text -python codex_reaction.py --workspace /path/to/workspace --resume-session +```bash +WEBHOOK_SECRET=your_secret python main.py webhook --port 8080 --event-profile notifications ``` -If you want webhook delivery to stay notification-only for a workspace, create a `.codex-webhook-notify-only` -file in that workspace. The bundled wrapper will skip direct Codex execution and leave the event pending. - -### 4. Set up Cloudflare Tunnel +### 3. Set up Cloudflare Tunnel ```bash cloudflared tunnel login @@ -56,12 +93,12 @@ cp cloudflared/config.yml.example ~/.cloudflared/config.yml cloudflared tunnel run ``` -### 5. Configure GitHub webhook +### 4. Configure the GitHub webhook - Payload URL: `https://webhook.yourdomain.com/webhook` - Content type: `application/json` - Secret: same value as `WEBHOOK_SECRET` -- Recommended event profile: choose these events to stay close to GitHub Notifications +- Recommended event profile: - Issues - Issue comments - Pull requests @@ -74,73 +111,93 @@ cloudflared tunnel run If your webhook is temporarily set to `Send me everything`, start the receiver with `--event-profile notifications` and it will ignore noisy events such as `workflow_job` or `check_suite`. -### 6. Enable channel push notifications (optional) +### 5. Optional direct trigger mode -The Node.js MCP server supports Claude Code's `claude/channel` capability (research preview, v2.1.80+). When enabled, new webhook events are pushed into your session automatically — no polling needed. +Use the bundled Codex wrapper if you want the webhook to launch `codex exec` immediately. + +```bash +python main.py webhook \ + --port 8080 \ + --event-profile notifications \ + --trigger-command "python codex_reaction.py --workspace /path/to/workspace --output-dir /path/to/github-webhook-mcp/codex-runs" +``` + +If you want Codex to resume an existing app thread instead of writing markdown output: + +```text +python codex_reaction.py --workspace /path/to/workspace --resume-session +``` + +If you want webhook delivery to stay notification-only for a workspace, create a `.codex-webhook-notify-only` +file in that workspace. The bundled wrapper will skip direct Codex execution and leave the event pending. + +### 6. Optional channel push notifications + +The Node.js MCP server supports Claude Code's `claude/channel` capability (research preview, v2.1.80+). When enabled, new webhook events are pushed into your session automatically. ```bash claude --dangerously-load-development-channels server:github-webhook-mcp ``` -Channel notifications are enabled by default. To disable, set `WEBHOOK_CHANNEL=0` in the MCP server env. +Channel notifications are enabled by default. To disable, set `WEBHOOK_CHANNEL=0` in the MCP server environment. -### 7. Configure MCP server +## Examples -#### Option A: Claude Desktop — Desktop Extension (.mcpb) +### Example 1: Check whether any GitHub notifications are pending -Download `mcp-server.mcpb` from [Releases](https://github.com/Liplus-Project/github-webhook-mcp/releases), then: +**User prompt:** "Do I have any pending GitHub webhook notifications right now?" -1. Open Claude Desktop → **Settings** → **Extensions** → **Advanced settings** → **Install Extension...** -2. Select the `.mcpb` file -3. Enter the path to your `events.json` when prompted +**Expected behavior:** -#### Option B: Claude Desktop / Claude Code — npx +- Calls `get_pending_status` +- Returns pending count, latest event time, and event types +- Uses that summary to decide whether more detail is needed -Add to your Claude MCP config (`claude_desktop_config.json` or project settings): +### Example 2: Review the latest pending PR-related event -```json -{ - "mcpServers": { - "github-webhook-mcp": { - "command": "npx", - "args": ["github-webhook-mcp"], - "env": { - "EVENTS_JSON_PATH": "/path/to/events.json" - } - } - } -} -``` +**User prompt:** "Show me the latest pending pull request event and explain what changed." -#### Option C: Codex — config.toml +**Expected behavior:** -```toml -[mcp.github-webhook-mcp] -command = "npx" -args = ["github-webhook-mcp"] +- Calls `list_pending_events` to find the newest relevant event +- Calls `get_event` only for the selected event +- Summarizes the PR metadata and payload without dumping every event -[mcp.github-webhook-mcp.env] -EVENTS_JSON_PATH = "/path/to/events.json" -``` +### Example 3: Mark an event as handled after triage -#### Option D: Python (legacy) +**User prompt:** "I already handled event `EVENT_ID`. Mark it processed so it stops appearing." -```json -{ - "mcpServers": { - "github-webhook-mcp": { - "command": "python", - "args": ["/path/to/github-webhook-mcp/main.py", "mcp"] - } - } -} -``` +**Expected behavior:** + +- Calls `mark_processed` with the event ID +- Marks the event as processed in the local event store +- Confirms success and, if applicable, reports how many processed events were purged + +## Privacy Policy + +This extension works with GitHub webhook event payloads that you choose to persist locally in `events.json`. +It may include issue titles, pull request metadata, discussion text, sender identities, and repository URLs inside that local event store. + +### Data Collection + +- Reads the local `events.json` file configured by the user +- Surfaces webhook metadata and payloads to the connected MCP client +- Can mark events as processed in the same local event store +- Does not send event contents to third-party services by itself beyond the webhook receiver and infrastructure you configure + +### Submission Note + +The extension-specific privacy policy is published at: + +- https://smgjp.com/privacy-policy-github-webhook-mcp/ + +Keep the policy URL in `mcp-server/manifest.json` aligned with that public page before directory submission. -## Operator Notes +## Support -- For the recommended polling flow, MCP tool contracts, event profiles, and trigger metadata, see [docs/0-requirements.md](docs/0-requirements.md). -- For environment variable examples, see [.env.example](.env.example). -- For direct trigger usage, `codex_reaction.py` is the bundled helper. +- GitHub Issues: https://github.com/Liplus-Project/github-webhook-mcp/issues +- Requirements/specification: [docs/0-requirements.md](docs/0-requirements.md) +- Environment variable examples: [.env.example](.env.example) ## Related diff --git a/mcp-server/manifest.json b/mcp-server/manifest.json index daa2ab3..70bb1be 100644 --- a/mcp-server/manifest.json +++ b/mcp-server/manifest.json @@ -1,11 +1,21 @@ { "manifest_version": "0.3", "name": "github-webhook-mcp", - "version": "0.3.0", + "display_name": "GitHub Webhook MCP", + "version": "0.3.1", "description": "Browse pending GitHub webhook events. Pairs with a webhook receiver that writes events.json.", + "long_description": "GitHub Webhook MCP helps Claude review and react to GitHub notifications from a local event store. It surfaces lightweight pending-event summaries, exposes full webhook payloads on demand, and lets users mark handled events as processed without exposing the event file directly.", "author": { - "name": "Liplus Project" + "name": "Liplus Project", + "url": "https://github.com/Liplus-Project" }, + "repository": { + "type": "git", + "url": "https://github.com/Liplus-Project/github-webhook-mcp.git" + }, + "homepage": "https://github.com/Liplus-Project/github-webhook-mcp", + "documentation": "https://github.com/Liplus-Project/github-webhook-mcp#readme", + "support": "https://github.com/Liplus-Project/github-webhook-mcp/issues", "license": "MIT", "server": { "type": "node", @@ -51,10 +61,24 @@ } ], "compatibility": { + "claude_desktop": ">=0.11.0", "platforms": [ "win32", "darwin", "linux" - ] - } + ], + "runtimes": { + "node": ">=18.0.0" + } + }, + "keywords": [ + "github", + "webhook", + "notifications", + "mcp", + "claude" + ], + "privacy_policies": [ + "https://smgjp.com/privacy-policy-github-webhook-mcp/" + ] } diff --git a/mcp-server/package-lock.json b/mcp-server/package-lock.json index 8761232..eb58fb7 100644 --- a/mcp-server/package-lock.json +++ b/mcp-server/package-lock.json @@ -1,12 +1,12 @@ { "name": "github-webhook-mcp", - "version": "0.2.1", + "version": "0.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "github-webhook-mcp", - "version": "0.2.1", + "version": "0.3.1", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0", diff --git a/mcp-server/package.json b/mcp-server/package.json index 9c64cd9..3b1fb2f 100644 --- a/mcp-server/package.json +++ b/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "github-webhook-mcp", - "version": "0.3.0", + "version": "0.3.1", "description": "MCP server for browsing GitHub webhook events", "type": "module", "bin": { diff --git a/mcp-server/server/index.js b/mcp-server/server/index.js index c74c0dd..3c0f40f 100644 --- a/mcp-server/server/index.js +++ b/mcp-server/server/index.js @@ -40,12 +40,18 @@ const server = new Server( const TOOLS = [ { name: "get_pending_status", + title: "Get Pending Status", description: "Get a lightweight snapshot of pending GitHub webhook events. Use this for periodic polling before requesting details.", inputSchema: { type: "object", properties: {} }, + annotations: { + title: "Get Pending Status", + readOnlyHint: true, + }, }, { name: "list_pending_events", + title: "List Pending Events", description: "List lightweight summaries for pending GitHub webhook events. Returns metadata only, without full payloads.", inputSchema: { @@ -57,9 +63,14 @@ const TOOLS = [ }, }, }, + annotations: { + title: "List Pending Events", + readOnlyHint: true, + }, }, { name: "get_event", + title: "Get Event Payload", description: "Get the full payload for a single webhook event by ID.", inputSchema: { type: "object", @@ -68,15 +79,25 @@ const TOOLS = [ }, required: ["event_id"], }, + annotations: { + title: "Get Event Payload", + readOnlyHint: true, + }, }, { name: "get_webhook_events", + title: "Get Webhook Events", description: "Get pending (unprocessed) GitHub webhook events with full payloads. Prefer get_pending_status or list_pending_events for polling.", inputSchema: { type: "object", properties: {} }, + annotations: { + title: "Get Webhook Events", + readOnlyHint: true, + }, }, { name: "mark_processed", + title: "Mark Event Processed", description: "Mark a webhook event as processed so it won't appear again.", inputSchema: { type: "object", @@ -88,6 +109,10 @@ const TOOLS = [ }, required: ["event_id"], }, + annotations: { + title: "Mark Event Processed", + destructiveHint: true, + }, }, ];