ghark
You can monitor GitHub pull requests and let a coding session wake on review activity and other PR updates.
Subscribe to a PR by URL or
OWNER/REPO#NUMBER.Unsubscribe from a watched PR.
List this process’s active PR subscriptions.
Read unread pending events, including any dropped at the queue limit.
Receive coalesced updates for commits, reviews, comments, check statuses, merge state, and PR close/merge/reopen activity.
Wake a Claude Code session for actionable reviewer comments/reviews, with merged and closed PRs removed from the watch list.
Use
pending_eventsfor clients that do not receive channel pushes.Rediscover likely owned open PRs on startup from the repo’s remotes and checked-out branch, unless discovery is disabled.
Watches GitHub pull requests and reports new PR activity, including commits, reviews, comments, check status changes, merge state changes, and PR closure, merge, or reopen events. Supports subscribing to PRs by URL or OWNER/REPO#N, unsubscribing, listing active subscriptions, and retrieving pending events.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ghark watch acme/web#42 and report new commits, reviews, and checks"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
ghark lets a Claude Code session respond to review on the pull requests it
opens. When the reviewer comments, the session wakes, makes the changes,
pushes, and replies, without being told to. It runs as a local stdio MCP
server.
Each server process owns its subscriptions. No webhook endpoint, public host, or shared service is required. The process polls GitHub while the session is open. It stops when the session ends.
Requirements
Python 3.11 or later
A GitHub personal access token in
GH_TOKENorGITHUB_TOKENuvto run the serverClaude Code for channel delivery; other clients can use
pending_events
The token needs read access to the repositories and pull requests you watch. Fine-grained tokens need Pull requests: read-only, Contents: read-only, and read access to checks and commit statuses.
Related MCP server: GitHub PR Reviewer MCP
Run
Install the project in editable mode:
uv syncAdd it to Claude Code as a local MCP server:
claude mcp add --scope user ghark -- uv run --directory /path/to/checkout gharkTo enable channel delivery in Claude Code, start the session with the channel server enabled:
claude --dangerously-load-development-channels server:gharkCustom channels are a Claude Code preview feature. Without the channel flag,
the tools still work and events remain available through pending_events.
For Codex, add a stdio server entry to ~/.codex/config.toml:
[mcp_servers.ghark]
command = "uv"
args = ["run", "--directory", "/path/to/checkout", "ghark"]Codex does not receive MCP channel pushes. Ask it to call pending_events when
you want to check for activity.
Tools
subscribewatches itsprargument. It accepts a GitHub PR URL orOWNER/REPO#N.unsubscribetakes the sameprargument and stops watching it.subscriptionslists this process's active PRs. It waits up to 30 seconds for startup discovery to finish.pending_eventsreturns unread events and reports any events dropped at the queue limit.
The server reports commits, reviews, comments, check status changes, merge state changes, and PR closure, merge, or reopen activity. It combines events from a PR into a short notification. Merged and closed PRs leave the watch list.
Review requests
An event is actionable when it is a review, issue comment, or review-thread
comment by a reviewer, other than a bare approval. The reviewers are the logins
in GHARK_REVIEWERS (comma-separated), or by default the token's owner.
Claude posts with the reviewer's token, so its replies carry the reviewer's
login. The server instructions tell it to end every comment with Claude Code's
signature, 🤖 Generated with [Claude Code](https://claude.com/claude-code).
Signed comments are never actionable, so Claude's replies do not wake it.
Actionable events wake the session through the wake hook (see
Hooks), which works where channels are disabled by policy. While
review requests are unread, the server keeps a flag file,
<state directory>/<session ID>.pending, naming their PRs; pending_events
removes it. The hook waits for the flag and then wakes Claude, even when the
session is idle. The flag is only a signal: pending_events remains the
durable read path. A session with no review requests costs one model turn a
day, when the waiter re-arms.
Restarted sessions
Subscriptions and unread events live in memory, and they end with the stdio server process. A resumed or restarted Claude Code session starts a new process. That process has no subscriptions.
On startup the server rediscovers the PRs that the session most plausibly owns.
Nothing is written to disk. In the background, it finds the git checkout of the
project directory and reads the GitHub repositories named by its remotes. It
then subscribes to every open PR by the token's owner in one of those
repositories whose head branch is the checked-out branch. Other local branches
are ignored: in a busy repository they name many PRs, some stale, and every
session would wake on every review. A session in a worktree finds the main
checkout's branch, since CLAUDE_PROJECT_DIR names the main checkout; the
subscribe hook covers PRs it opens. Each PR gets the normal baseline, so
old activity is not replayed. The wake hook re-arms at every session start,
so nothing is asked of the model.
The project directory is CLAUDE_PROJECT_DIR, then PWD, then the working
directory. Claude Code sets CLAUDE_PROJECT_DIR for stdio servers. The
working directory alone does not identify the project:
uv run --directory /path/to/checkout moves it to the ghark checkout.
Discovery is best effort. Failures are logged, and the server serves without
the missing subscriptions. Set GHARK_DISCOVER=0 to turn it off.
Two sessions in the same repository both discover the same PRs. Each session
wakes on review of the other session's PRs. Set GHARK_DISCOVER=0 for one of
the sessions, or unsubscribe PRs that the session does not own.
Channel delivery is best effort. The pending queue holds up to 1,000 events; pending_events reports
how many older events were dropped when that limit was reached.
Hooks
Three Claude Code hooks do what the model could forget, with nothing to report the miss.
ghark-hook-wake(SessionStart and PostToolUse onpending_events, bothasyncRewake) waits for review requests and wakes Claude when they arrive. It starts with every session and again after everypending_eventscall, so the model only has to callpending_eventswhen woken, as the wake message says. A second waiter for the same session exits at once and silently. Claude Code kills a hook at itstimeoutwithout telling Claude, so the waiter wakes Claude a few minutes before it: about once a day, Claude callspending_events, which arms the next waiter. The waiter exits quietly five minutes after the session's ghark server is gone.ghark-hook-signature(PreToolUse) denies Bash commands that post an unsigned PR comment or review. An unsigned reply from Claude counts as a review request, so Claude could wake itself and reply to its own comment in a loop. The denial tells Claude to append the signature line and retry.ghark-hook-subscribe(PostToolUse) runs after a successfulgh pr create(orgh pr new). It tells Claude to callsubscribewith the URL the command printed.
Add them to ~/.claude/settings.json or to a project's .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(gh *)",
"command": "uv run --directory /path/to/checkout ghark-hook-signature",
"timeout": 30
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "uv run --quiet --directory /path/to/checkout ghark-hook-wake 86400",
"asyncRewake": true,
"timeout": 86400
}
]
}
],
"PostToolUse": [
{
"matcher": "mcp__ghark__pending_events",
"hooks": [
{
"type": "command",
"command": "uv run --quiet --directory /path/to/checkout ghark-hook-wake 86400",
"asyncRewake": true,
"timeout": 86400
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(gh *)",
"command": "uv run --directory /path/to/checkout ghark-hook-subscribe",
"timeout": 30
}
]
}
]
}
}The if filter starts the Bash hooks only for gh commands, including each
command in a && chain. The wake hook's argument must match its timeout.
The signature hook checks these commands:
gh pr comment,gh pr review, andgh issue commentwith--body/-bor--body-file/-Fgh apiPOSTs to…/comments,…/reviews,…/reviews/N/events, and…/replieswithbodyor…[body]fields,-F body=@file, or--inputgh api graphqlcomment and review mutations
It reads body files relative to the session's working directory. It allows
any body it cannot read with confidence: a shell variable, a command
substitution, a pipe into -F -, a file the same command writes first, or a
command the shell lexer cannot parse. A false block stalls work; a rare miss
costs one extra wake-up.
The subscribe hook covers PRs created during a session, not PRs opened before it started.
Development
uv run gharkCheck formatting and lint with:
uv run ruff format --check ghark
uv run ruff check gharkRun the behavior and MCP stdio checks with:
uv run python -m unittest discover -s tests -vSet GHARK_POLL_SECONDS to change the polling interval. The default is 30
seconds. Set GHARK_BATCH_SECONDS to change the event coalescing window. The
default is 5 seconds. Set GHARK_DISCOVER=0 to disable startup discovery.
The server logs to stderr and to ~/.local/state/ghark/ghark.log (or
$XDG_STATE_HOME/ghark/ghark.log). Set GHARK_LOG_FILE to change the path.
Each line carries the process ID. The log records why each process stopped:
the client closed the connection, a signal, or an uncaught exception. A log
that ends without one of these means the process was killed with SIGKILL.
See DESIGN.md for architecture and behavior details.
Available Tools
4 toolspending_eventsB
Read pending events and report any events dropped at the queue limit.
Inputs: no named parameters.
subscribeB
Watch a GitHub pull request for new activity.
Inputs: pr (required).
subscriptionsA
List pull requests watched by this session.
Inputs: no named parameters.
unsubscribeC
Stop watching a pull request.
Inputs: pr (required).
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 4 tools
Each tool targets a clearly distinct operation: subscribe and unsubscribe are opposite pair, subscriptions lists watches, and pending_events reads queued events. There is no realistic confusion between listing subscriptions and reading pending events, as they operate on different resources.
Names are all snake_case and thematically coherent (subscribe/unsubscribe/subscriptions pair nicely). The only minor deviation is pending_events being a pure noun while the others read as verbs/plurals, but overall the pattern is predictable and readable.
Four tools is well-matched to the narrow, focused purpose of watching pull requests for a session. It is on the lean side but each tool pulls its weight, so nothing feels redundant.
The watch lifecycle is fully covered: add (subscribe), remove (unsubscribe), list (subscriptions), and consume events (pending_events). Minor gaps exist around identifying the session or inspecting individual events, but the core workflow is complete.
Related MCP Connectors
Human-authenticated setup for routing GitHub pull requests into the right Slack channel.
Hosted coding agent: start, follow up on and stop tasks that can open GitHub pull requests.
Connect AI assistants to GitHub - manage repos, issues, PRs, and workflows through natural language.
Track public GitHub repos: free snapshot, paid release, star & issue intel via x402 USDC.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceStreams GitHub webhook events (push, PR, issues, CI) into Claude Code session in real-time, enabling live awareness of repository activity.MIT
- FlicenseAqualityDmaintenanceEnables analysis of GitHub Pull Requests, including details, diff, file lists, and review tracking.3-
- AlicenseNot gradedqualityDmaintenanceEnables reviewing GitHub pull requests via ChatGPT, including listing PRs, viewing diffs, posting comments, approving, and requesting changes.MIT
- FlicenseAqualityDmaintenanceEnables LLM clients to list, read, review, and comment on GitHub pull requests, turning an AI assistant into a fully capable code reviewer.61-