Skip to content

Commit be8ec6c

Browse files
revert standalone webhook listener (#44)
v0.4.0で追加した埋め込みwebhook listenerを取り下げ、受信を分離した従来の構成へ戻しました。 Node側のmanifestとREADME、requirementsをsplit receiver前提に揃え、rollback後のpackage versionは0.4.1へ更新しています。 Issue: #43 Co-authored-by: liplus-lin-lay <259586417+liplus-lin-lay@users.noreply.github.com>
1 parent b281e26 commit be8ec6c

9 files changed

Lines changed: 29 additions & 401 deletions

File tree

README.md

Lines changed: 7 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,12 @@ GitHub webhook receiver and local MCP extension for GitHub notification workflow
77
`github-webhook-mcp` receives GitHub webhook events, persists them to a local `events.json`, and exposes them to AI agents through MCP tools.
88
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.
99

10-
The standard mode is standalone: while the local MCP server is running, it can also host a local webhook listener in the same process.
11-
1210
Detailed behavior, event metadata, trigger semantics, and file responsibilities live in [docs/0-requirements.md](docs/0-requirements.md).
1311

1412
## Features
1513

1614
- Receives GitHub webhook events over HTTPS and persists them locally.
1715
- Exposes pending events to MCP clients through lightweight polling tools.
18-
- Can run as a standalone local MCP + webhook listener without a separate Windows service.
1916
- Supports real-time `claude/channel` notifications in Claude Code.
2017
- Supports direct trigger mode for immediate Codex reactions per event.
2118
- Ships as a Node-based `.mcpb` desktop extension and as an `npx` MCP server.
@@ -29,7 +26,6 @@ Download `mcp-server.mcpb` from [Releases](https://github.com/Liplus-Project/git
2926
1. Open Claude Desktop → **Settings****Extensions****Advanced settings****Install Extension...**
3027
2. Select the `.mcpb` file
3128
3. Enter the path to your `events.json` when prompted
32-
4. If you want the extension itself to receive GitHub webhooks, also fill in the local webhook port and webhook secret prompts
3329

3430
### Claude Desktop / Claude Code — npx
3531

@@ -42,10 +38,7 @@ Add to your Claude MCP config (`claude_desktop_config.json` or project settings)
4238
"command": "npx",
4339
"args": ["github-webhook-mcp"],
4440
"env": {
45-
"EVENTS_JSON_PATH": "/path/to/events.json",
46-
"WEBHOOK_PORT": "8080",
47-
"WEBHOOK_SECRET": "your_secret",
48-
"WEBHOOK_EVENT_PROFILE": "notifications"
41+
"EVENTS_JSON_PATH": "/path/to/events.json"
4942
}
5043
}
5144
}
@@ -61,9 +54,6 @@ args = ["github-webhook-mcp"]
6154

6255
[mcp.github-webhook-mcp.env]
6356
EVENTS_JSON_PATH = "/path/to/events.json"
64-
WEBHOOK_PORT = "8080"
65-
WEBHOOK_SECRET = "your_secret"
66-
WEBHOOK_EVENT_PROFILE = "notifications"
6757
```
6858

6959
### Python (legacy)
@@ -81,68 +71,19 @@ WEBHOOK_EVENT_PROFILE = "notifications"
8171

8272
## Configuration
8373

84-
### Standard standalone mode
85-
86-
When `WEBHOOK_PORT` is set, the Node.js MCP server starts a local webhook listener in the same process.
87-
This is the recommended mode for lightweight local use and plugin-style distribution.
88-
89-
### 1. Configure the local MCP server
90-
91-
Set at least:
92-
93-
- `EVENTS_JSON_PATH`
94-
- `WEBHOOK_PORT`
95-
- `WEBHOOK_SECRET`
96-
97-
Optionally set:
98-
99-
- `WEBHOOK_EVENT_PROFILE=notifications`
100-
101-
### 2. Set up Cloudflare Tunnel
102-
103-
```bash
104-
cloudflared tunnel login
105-
cloudflared tunnel create github-webhook-mcp
106-
cp cloudflared/config.yml.example ~/.cloudflared/config.yml
107-
# Edit config.yml with your tunnel ID and domain
108-
cloudflared tunnel run
109-
```
110-
111-
### 3. Configure the GitHub webhook
112-
113-
- Payload URL: `https://webhook.yourdomain.com/webhook`
114-
- Content type: `application/json`
115-
- Secret: same value as `WEBHOOK_SECRET`
116-
- Recommended event profile:
117-
- Issues
118-
- Issue comments
119-
- Pull requests
120-
- Pull request reviews
121-
- Pull request review comments
122-
- Check runs
123-
- Workflow runs
124-
- Discussions
125-
- Discussion comments
126-
127-
If your webhook is temporarily set to `Send me everything`, set `WEBHOOK_EVENT_PROFILE=notifications` and the embedded receiver will ignore noisy events such as `workflow_job` or `check_suite`.
128-
129-
### Legacy Python receiver mode
130-
131-
Use this mode only if you want a separate long-running receiver process or direct trigger queue behavior.
132-
133-
#### 1. Install receiver dependencies
74+
### 1. Install receiver dependencies
13475

13576
```bash
13677
pip install -r requirements.txt
13778
```
13879

139-
#### 2. Start the webhook receiver
80+
### 2. Start the webhook receiver
14081

14182
```bash
14283
WEBHOOK_SECRET=your_secret python main.py webhook --port 8080 --event-profile notifications
14384
```
14485

145-
#### 3. Set up Cloudflare Tunnel
86+
### 3. Set up Cloudflare Tunnel
14687

14788
```bash
14889
cloudflared tunnel login
@@ -152,7 +93,7 @@ cp cloudflared/config.yml.example ~/.cloudflared/config.yml
15293
cloudflared tunnel run
15394
```
15495

155-
#### 4. Configure the GitHub webhook
96+
### 4. Configure the GitHub webhook
15697

15798
- Payload URL: `https://webhook.yourdomain.com/webhook`
15899
- Content type: `application/json`
@@ -170,7 +111,7 @@ cloudflared tunnel run
170111

171112
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`.
172113

173-
#### 5. Optional direct trigger mode
114+
### 5. Optional direct trigger mode
174115

175116
Use the bundled Codex wrapper if you want the webhook to launch `codex exec` immediately.
176117

@@ -190,7 +131,7 @@ python codex_reaction.py --workspace /path/to/workspace --resume-session <thread
190131
If you want webhook delivery to stay notification-only for a workspace, create a `.codex-webhook-notify-only`
191132
file in that workspace. The bundled wrapper will skip direct Codex execution and leave the event pending.
192133

193-
### Optional channel push notifications
134+
### 6. Optional channel push notifications
194135

195136
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.
196137

docs/0-requirements.md

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,9 @@ GitHub Webhook イベントを受信・永続化し、MCP プロトコル経由
1111
- AI エージェントは MCP stdio トランスポートで接続する
1212
- 単一マシン上でローカル動作する(分散構成は対象外)
1313
- イベント永続化は JSON ファイルベース(DB 不要)
14-
- 標準モードは、MCP サーバー起動中のみ受信するスタンドアロン構成とする
1514

1615
## Architecture
1716

18-
### Standard standalone mode
19-
20-
```
21-
GitHub ──POST──> Cloudflare Tunnel ──> local webhook event server (:8080, embedded)
22-
|
23-
v
24-
events.json
25-
^
26-
|
27-
Node.js MCP server (stdio, same process)
28-
^
29-
|
30-
AI Agent (Codex / Claude)
31-
```
32-
33-
標準モードでは、Node.js MCP サーバーが起動中のみローカル webhook event server を同居起動する。
34-
Webhook 受信と MCP 提供は同一実体で兼任してよく、イベントストアは既存の `events.json` をそのまま使う。
35-
36-
### Legacy service-style mode
37-
3817
```
3918
GitHub ──POST──> Cloudflare Tunnel ──> FastAPI :8080 ──persist──> events.json
4019
^
@@ -51,21 +30,19 @@ GitHub ──POST──> Cloudflare Tunnel ──> FastAPI :8080 ──persist
5130
AI Agent (Codex / Claude) optional trigger command
5231
```
5332

54-
常駐サービスや direct trigger queue が必要な場合は、従来どおり Python webhook receiver を別プロセスで起動できる。
55-
56-
### Components
33+
システムは二つのコンポーネントで構成される:
5734

58-
1. **Node.js standalone MCP server(標準)**`npx github-webhook-mcp` または MCPB 経由
59-
- MCP stdio サーバーとして起動する
60-
- `WEBHOOK_PORT` が設定されている場合、同一プロセスでローカル webhook event server を起動する
61-
- `events.json` の読み書きを担当する
62-
63-
2. **Legacy webhook receiver(Python)**`python main.py webhook`
35+
1. **Webhook 受信サーバー(Python)**`python main.py webhook`
6436
- HTTP 受信、イベント永続化、optional な direct trigger queue を担当
6537
- trigger command が設定されている場合、保存済みイベントごとに直列実行される
6638

67-
3. **Legacy MCP server(Python 互換)**`python main.py mcp`
68-
- 従来互換用の stdio MCP 実装
39+
2. **MCP ツールサーバー** — 二つの実装が存在する:
40+
- **Python 実装:** `python main.py mcp`
41+
- **Node.js 実装:** `mcp-server/` ディレクトリ(`npx github-webhook-mcp` または MCPB 経由)
42+
43+
両 MCP 実装は同一の events.json を読み書きし、同一の5ツールを提供する。
44+
Python 実装は webhook 受信サーバーと同一エントリポイントで起動する。
45+
Node.js 実装は独立パッケージとして配布される(MCPB: Claude Desktop 向け、npx: Codex 向け)。
6946

7047
## Functional Requirements
7148

@@ -78,7 +55,6 @@ GitHub ──POST──> Cloudflare Tunnel ──> FastAPI :8080 ──persist
7855
| F1.3 | 署名不一致時は HTTP 401 を返す |
7956
| F1.4 | シークレット未設定時は署名検証をスキップする |
8057
| F1.5 | `GET /health` でヘルスチェックを提供する(`{"status": "ok"}`|
81-
| F1.6 | Node.js standalone MCP server は、`WEBHOOK_PORT` 設定時に同一プロセスで `POST /webhook``GET /health` を提供できる |
8258

8359
### F2. イベントフィルタリング
8460

@@ -162,7 +138,7 @@ Python 実装と Node.js 実装の両方が、以下の同一ツールセット
162138
}
163139
```
164140

165-
### F5. Direct Trigger Execution(legacy Python receiver)
141+
### F5. Direct Trigger Execution
166142

167143
| ID | 要件 |
168144
|----|------|
@@ -247,12 +223,8 @@ Python 実装と Node.js 実装の両方が、以下の同一ツールセット
247223
| N2.5 | trigger working directory | `--trigger-cwd` / `WEBHOOK_TRIGGER_CWD` | なし |
248224
| N2.6 | success 時に pending を維持するか | `--keep-pending-on-trigger-success` | false |
249225
| N2.7 | events.json パス(Node.js MCP) | `EVENTS_JSON_PATH` | `mcp-server/../events.json` |
250-
| N2.8 | 埋め込み webhook listener の bind host(Node.js standalone) | `WEBHOOK_HOST` | `127.0.0.1` |
251-
| N2.9 | 埋め込み webhook listener のポート(Node.js standalone) | `WEBHOOK_PORT` | 未設定(無効) |
252-
| N2.10 | 埋め込み webhook listener のシークレット(Node.js standalone) | `WEBHOOK_SECRET` | なし(検証スキップ) |
253-
| N2.11 | 埋め込み webhook listener のイベントプロファイル(Node.js standalone) | `WEBHOOK_EVENT_PROFILE` | `all` |
254-
| N2.12 | 処理済みイベント保持日数 | `PURGE_AFTER_DAYS` | 1 |
255-
| N2.13 | チャンネル通知の有効/無効 | `WEBHOOK_CHANNEL` | 有効(`0` で無効) |
226+
| N2.8 | 処理済みイベント保持日数 | `PURGE_AFTER_DAYS` | 1 |
227+
| N2.9 | チャンネル通知の有効/無効 | `WEBHOOK_CHANNEL` | 有効(`0` で無効) |
256228

257229
優先順位: CLI 引数 > 環境変数 > デフォルト
258230
Node.js MCP サーバーは環境変数のみで構成する(CLI 引数なし)。

mcp-server/manifest.json

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
"manifest_version": "0.3",
33
"name": "github-webhook-mcp",
44
"display_name": "GitHub Webhook MCP",
5-
"version": "0.4.0",
6-
"description": "Browse pending GitHub webhook events and optionally receive them locally in standalone mode.",
7-
"long_description": "GitHub Webhook MCP helps Claude review and react to GitHub notifications from a local event store. In standalone mode it can also host a local webhook listener in the same process, so the extension can receive and persist GitHub events while Claude is running.",
5+
"version": "0.4.1",
6+
"description": "Browse pending GitHub webhook events. Pairs with a webhook receiver that writes events.json.",
7+
"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.",
88
"author": {
99
"name": "Liplus Project",
1010
"url": "https://github.com/Liplus-Project"
@@ -26,11 +26,7 @@
2626
"${__dirname}/server/index.js"
2727
],
2828
"env": {
29-
"EVENTS_JSON_PATH": "${user_config.events_json_path}",
30-
"WEBHOOK_PORT": "${user_config.webhook_port}",
31-
"WEBHOOK_SECRET": "${user_config.webhook_secret}",
32-
"WEBHOOK_EVENT_PROFILE": "notifications",
33-
"WEBHOOK_HOST": "127.0.0.1"
29+
"EVENTS_JSON_PATH": "${user_config.events_json_path}"
3430
}
3531
}
3632
},
@@ -40,18 +36,6 @@
4036
"type": "string",
4137
"required": true,
4238
"title": "Events JSON Path"
43-
},
44-
"webhook_port": {
45-
"description": "Optional local port for the embedded webhook listener. Leave empty to disable receiving.",
46-
"type": "string",
47-
"required": false,
48-
"title": "Webhook Port"
49-
},
50-
"webhook_secret": {
51-
"description": "Optional GitHub webhook secret used to verify incoming requests.",
52-
"type": "string",
53-
"required": false,
54-
"title": "Webhook Secret"
5539
}
5640
},
5741
"tools": [

mcp-server/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mcp-server/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "github-webhook-mcp",
3-
"version": "0.4.0",
3+
"version": "0.4.1",
44
"description": "MCP server for browsing GitHub webhook events",
55
"type": "module",
66
"bin": {
@@ -13,7 +13,7 @@
1313
],
1414
"scripts": {
1515
"start": "node server/index.js",
16-
"test": "node --test test/*.test.js",
16+
"test": "node --check server/index.js && node --check server/event-store.js",
1717
"pack:mcpb": "mcpb pack"
1818
},
1919
"dependencies": {

mcp-server/server/event-store.js

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
2-
import { randomUUID } from "node:crypto";
1+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
32
import { resolve, dirname } from "node:path";
43
import { fileURLToPath } from "node:url";
54
import iconv from "iconv-lite";
@@ -59,7 +58,6 @@ export function load() {
5958

6059
export function save(events) {
6160
const filePath = dataFilePath();
62-
mkdirSync(dirname(filePath), { recursive: true });
6361
writeFileSync(filePath, JSON.stringify(events, null, 2), PRIMARY_ENCODING);
6462
}
6563

@@ -84,20 +82,6 @@ export function getPending() {
8482
return load().filter((e) => !e.processed);
8583
}
8684

87-
export function addEvent(eventType, payload) {
88-
const events = load();
89-
const event = {
90-
id: randomUUID(),
91-
type: eventType,
92-
payload,
93-
received_at: new Date().toISOString(),
94-
processed: false,
95-
};
96-
events.push(event);
97-
save(events);
98-
return event;
99-
}
100-
10185
export function getEvent(eventId) {
10286
for (const event of load()) {
10387
if (event.id === eventId) return event;

0 commit comments

Comments
 (0)