When to use it
- Before merging a pull request you did not write
- When a change is large and you want the risky parts first
- To check a PR's CI status and test coverage in one pass
What it looks like
Review PR #214 in acme/web and tell me if anything blocks the merge.
pull_request_readget_file_contentssearch_codeactions_listVerdict: ready with small fixes. 1) src/cart/total.ts lines 40-52: the new discount path skips the tax rounding used everywhere else, so totals can be off by a cent. 2) No test covers a 100% discount. CI passed on the head commit. I did not post anything; say the word and I will draft the review comment.
The SKILL.md file
name: github-pull-request-reviewReview a GitHub pull request the way a strong senior reviewer does, judging intent, risk and missing tests before style, and sorting feedback into blocking, should-fix and nit. Use when the user asks to review, check, summarise or sign off a pull request, or wants to know whether a change is safe to merge.
A review answers one question: is it safe and sensible to merge this? Style comes last. A review that lists twenty nits and misses the broken edge case has failed.
1. Understand intent before reading code
Read the title, description, linked issue and the author's comments with pull_request_read (method get). Write down, in one sentence, what the change is supposed to do. If the description is empty and the diff is non-trivial, say so; that is itself feedback.
Check the facts that change how you review:
- Draft? Review for direction, not polish.
- Base branch is not
main/the default? It may be a stacked PR; review only its own commits. - Size. Over about 400 changed lines of real code, or mixing a refactor with a behaviour change: say that it should be split, then still review the risky part. Do not silently skim.
2. Review in risk order, not file order
Fetch the file list (pull_request_read, method get_files) and sort it yourself:
- Read first, hardest: auth, permissions, payments, data deletion, migrations, concurrency, anything parsing untrusted input.
- Core logic the description is about.
- Tests.
- Glue, config, docs.
- Skip line by line: lockfiles, snapshots, generated code, vendored files. Check only that they are consistent with the source change (a dependency bump with no lockfile change is a finding).
Read the code around each hunk with get_file_contents at the head ref. A hunk alone hides the function it sits in.
3. What diffs hide: look for what is missing
The most valuable findings are not in the diff. Ask:
- A changed function signature or return shape. Find every caller with
search_code. Any caller not updated in this PR is a likely break. - A new env var, flag or config key. Is it added to the deploy config,
.env.exampleand docs? - A schema migration. Is it reversible, safe on a big table (no long lock, no backfill in the same transaction), and is the code deployable before and after the migration runs?
- A new endpoint or query. Authorisation check present? Input validated? Paginated? Index for the new filter?
- An error path. What happens on null, empty list, timeout, retry, partial failure?
- A new dependency. Maintained, licensed compatibly, worth its weight?
- A deleted check, test or
skip. Why?
4. Judge the tests, not just their existence
A test is worth something only if it fails without the fix. Check that new tests assert behaviour (outputs, state) rather than that a mock was called. For a bug fix, there should be a test that reproduces the bug. Flag tests that can never fail, such as asserting on the value they just set.
5. Read CI properly
Use pull_request_read (method get_status) and actions_list for the head commit. A green run on an older commit means nothing. Red checks: say which and whether they relate to this change or look flaky or pre-existing (compare with the base branch's latest run). Never wave a failure away as "probably flaky" without evidence.
6. Sort every comment
Label each finding so the author knows what must happen:
| Label | Meaning | Blocks merge |
|---|---|---|
| Blocking | Bug, security hole, data loss, broken contract, missing test for risky behaviour | Yes |
| Should fix | Likely future bug or real maintainability cost | Discuss |
| Question | You cannot tell if it is a problem; ask, do not assert | No |
| Nit | Taste, naming, formatting | No |
Cap nits at three and say "plus similar nits elsewhere". If a linter or formatter could catch it, do not comment on it at all.
Be specific and kind: quote the line, say what goes wrong and for which input, and offer a fix. "This breaks when items is empty because line 48 indexes items[0]" beats "handle edge cases".
Output
Verdict: <ready | ready with small fixes | blocked> — <one sentence why>
What it does: <one sentence>
Scope: <files/lines reviewed; anything skipped and why>
Blocking
1. path:lines — what breaks, for which input, suggested fix
Should fix
...
Questions
...
Nits (max 3)
...
Missing tests: <behaviour with no coverage>
CI: <state on the head commit, relevant failures>
Could not check: <honest gaps>
Posting the review
Drafting is free. Do not post, approve, request changes or merge without the user's explicit yes in this conversation, and never approve a pull request the user authored. When the user does approve posting, put all comments in one review rather than many single comments, so the author gets one notification, and use suggestion blocks only for small, certain fixes.
Done when
- The verdict is stated first and is justified by at least one concrete finding or by an explicit "I checked X, Y, Z and found nothing".
- Every blocking item has a file, a line range and a fix.
- The "could not check" line is honest.
GitHub tools it uses
pull_request_readget_file_contentssearch_codeactions_list
Tool list from github/github-mcp-server README.
FAQ
Will it approve or comment on my pull request?
Not unless you ask. It reads and reports. Posting a review, approving or requesting changes needs your explicit yes.
Can it review very large pull requests?
Yes. It goes file by file and says plainly which files it could not review in full.