MCP server and API
From an agent, or the API
Mergepatrol serves an MCP server at /mcp, so a coding agent can do anything the dashboard does: queue a PR, take it out, read where it is, review it, read the queue’s history, change a repo’s settings. Every call acts as a GitHub user or bot: Mergepatrol puts the labels on, comments, and reviews with their token, so GitHub checks they may.
An MCP client that supports OAuth signs in with GitHub in the browser, as the dashboard does:
claude mcp add --transport http mergepatrol https://mergepatrol.ai/mcp
The client gets a Mergepatrol session, and the GitHub token stays on the server. Mergepatrol sends the sign-in back only to a loopback address, where native clients such as Claude Code, Codex and Cursor listen.
An agent without a browser, such as one in CI, passes a GitHub token instead: one from gh auth token, a personal access token, or a workflow’s GITHUB_TOKEN. A fine-grained token also needs write access to issues or pull requests. Mergepatrol never stores the token. It remembers its hash, in memory for 5 minutes, so calls in a row do not each ask GitHub again.
claude mcp add --transport http mergepatrol https://mergepatrol.ai/mcp \
--header "Authorization: Bearer $(gh auth token)"
A GitHub token reaches only the repos GitHub lets it read: Mergepatrol cannot tell which of its workspaces such a token belongs to. The tools marked Sign-in work across a workspace, so they need the sign-in. Each tool takes the repo as repo, owner/name, or the workspace as workspace, its installation ID, which get_session lists.
| Tool | Does | Sign-in |
|---|---|---|
get_changelog | Mergepatrol’s newest changelog entries | |
get_session | Who you are signed in as, and your workspaces | |
get_inbox | Your PRs, and the PRs that ask for your review, across workspaces | Yes |
list_queue | The queues, in the order they take PRs: a repo’s, or the workspace’s | Without repo |
list_events | A repo’s queue events, newest first | |
get_fix | The diff of a commit the fix agent pushed | |
list_history | The PRs that left the queue: merged, ejected, or removed | Yes |
get_history_stats | The queue’s numbers over a window | Yes |
list_repos | The workspace’s repos, and whether each queues | Yes |
set_repo | Turns a repo’s queue on or off, or back to following its config file | Yes |
list_open_pulls | A repo’s open PRs that are not queued | |
get_pull | A PR’s description, labels, checks, and timeline | |
get_pull_diff | A PR’s changes, or one commit’s | |
pull_status | Where a PR is, as /api/v1 below says, and its latest events | |
queue_pull | Queues a PR, or its stack. priority puts it at the front | |
dequeue_pull | Takes a PR, and its stack, out of the queue | |
close_pull | Closes a PR | |
review_pull | Approves a PR, asks for changes, or comments, then queues it if asked | |
get_agent_review | The agent’s review of a queued PR: what it found, and what it asks a human | |
answer_agent_review | Answers what the agent’s review asks, as your review on GitHub | |
ask_agent_about_review | Asks the agent a question about one of its findings | |
ask_agent_review_reviewers | Asks others to answer the agent’s review | |
mark_pull_seen | Marks a PR read in your inbox | Yes |
snooze_pull | Puts a PR out of your inbox for a while | Yes |
unsnooze_pull | Puts a snoozed PR back in your inbox | Yes |
nudge_reviewers | Mentions reviewers in a comment, asking them to review | |
get_repo_config | A repo’s queue settings | |
propose_repo_config | Opens a PR that changes a repo’s queue settings | Yes |
get_settings | Which model API keys and ChatGPT login the workspace has | Yes |
set_api_key | Sets a model provider’s API key | Yes |
delete_api_key | Removes a model provider’s API key | Yes |
start_chatgpt_login | Starts a ChatGPT sign-in: a code for the user to enter | Yes |
delete_chatgpt_login | Removes the ChatGPT login | Yes |
poll_chatgpt_login | Finishes the ChatGPT sign-in once the user entered the code | Yes |
get_review_settings | The agent’s review settings, and its track record | Yes |
set_review_settings | Turns the agent’s review on or off, and sets how sure it must be | Yes |
get_billing | The workspace’s plan and seats | Yes |
start_checkout | A Stripe page to subscribe | Yes |
open_billing_portal | A Stripe page to manage the subscription | Yes |
free_seat | Frees a seat | Yes |
Each tool calls a route of the JSON API under /api, which the dashboard uses. A script can call the API itself with the same Authorization: Bearer header:
curl -H "Authorization: Bearer $(gh auth token)" \
https://mergepatrol.ai/api/w/<installation ID>/repos/<owner>/<name>/pulls/<number>/status
Read a PR’s state from another app
An app that shows PRs, such as a PR list in an editor, reads what Mergepatrol knows of them from /api/v1, instead of working it out from labels and the config file. It authenticates as the API above does: Authorization: Bearer with the viewer’s GitHub token (OAuth, personal access token, or an App installation token) or a Mergepatrol session. GitHub decides what the token can read. A repo it cannot read is a 404, the same as a repo that does not exist. A repo it can read is a 200, also when Mergepatrol does not run there.
curl -H "Authorization: Bearer $(gh auth token)" \
https://mergepatrol.ai/api/v1/repos/acme/widgets/pulls/42
curl -H "Authorization: Bearer $(gh auth token)" \
"https://mergepatrol.ai/api/v1/repos/acme/widgets/pulls?numbers=42,43,44"
The second form reads up to 100 PRs of one repo, as { "pulls": [...] } in the order asked. Each PR reads:
{
"repo": "acme/widgets",
"number": 42,
"url": "https://mergepatrol.ai/acme/widgets/pull/42",
"mergepatrol": {
"state": "on",
"installed": true,
"has_config": true,
"override": null,
"config_error": null
},
"queue": {
"trunk": "main",
"state": "held",
"hold": {
"reason": "review_required",
"layer": null,
"message": "waiting for a required review"
},
"priority": false,
"position": 3,
"stack": null,
"batch_pr": null
},
"merged": null,
"ejected": null
}
| Field | Says |
|---|---|
url | The PR in the dashboard. It opens the PR’s lane while queued, its history once merged or ejected, the repo’s settings while Mergepatrol is off there, and the repo’s open PRs otherwise |
mergepatrol.state | on, off (no App on the repo, no config file, or turned off), or paused (the config file does not parse: config_error says why) |
mergepatrol.override | on or off when turned so in the dashboard, whatever the config file says. null follows the file |
queue | null unless queued. state is waiting, running or held. position counts from 1 in the order the queue takes PRs. stack is { number, layers } for a stack. batch_pr is the draft PR testing it, while it runs in a batch |
queue.hold.reason | draft, review_required, changes_requested, workflow_approval (a fork PR’s runs wait for approval), fork_edits_off (a fork PR behind its trunk that does not allow edits by maintainers), no_queue (it targets a branch without a queue), no_subscription, fair_use_limit, reviewing (the agent’s automatic review runs), review_asks (that review waits for a human’s answers) or review_changes (a human answering it asked for changes, and it waits for a push). layer is the stack’s PR that holds it |
merged, ejected | How it last left the queue: { at, sha } or { at, reason }. Both null while it is queued, and when it left otherwise |
Every response has an ETag: send it back as If-None-Match for a 304 when nothing changed. Each caller, by its token, has 5,000 calls an hour, a 304 included, which x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-used and x-ratelimit-reset (Unix seconds) report. Past the limit, a 429 has a retry-after, in seconds. So does a 503 when GitHub’s own rate limit is used up.
Docs
- Get started with Mergepatrol
Sign in with GitHub, install the App, merge the setup PR, add a model key, and label a pull request to queue it.
- How the merge queue works
What a queued PR needs, the agent that resolves conflicts and fixes failed checks, automatic review, branches, batches, stacks and PRs from forks.
- Configuration: .mergepatrol/mergepatrol.yml
Every setting in a repo's .mergepatrol/mergepatrol.yml: the label, batch size, the agent's model, verify commands, CI fixes, extra branches and review bars.
- Pricing
Mergepatrol is free on public repos. Private repos cost $15 a seat a month after a 14-day trial with no card. Self-hosting is free.
- Self-host Mergepatrol
Run the open-source Mergepatrol yourself with Docker Compose, configure it, bring your own GitHub App, and isolate its jobs in gVisor sandboxes.