Skip to main content
Glama
JigeeshaJain

gh-review-queue-mcp

gh-review-queue-mcp

[M8ven Score

Ein MCP-Server, der eine Frage beantwortet: was soll ich als Nächstes prüfen?

Es stellt genau ein Tool bereit, get_review_queue, das eine nach Priorität sortierte, deduplizierte Ansicht deiner GitHub-Pull-Request-Review-Queue liefert — Review-Anfragen, die an dich gerihtet sind, Review-Anfragen, die du an deine Teams gestellt hast, und deine eigenen Pull-Requests, die auf jemand andern warten.

Genau ein Tool ist eine bewusste Einschränkung. Ein Assistent, der sich zwischen list_prs, search_prs und get_pr_status entschedenn muss, verbringt seine erste Runde mit Auswählen; ein Assistent mit einem einzigen Tool, das eine bereits priorisierte List liefert, kann einfach antworten.


Was es wirklich tut

Wenn das Tool aufgerufen wird, passieren v/ier Dinge in dieser Reihenfolge.

1. Dich und deine Teams irmitteln

Der Server sendert eine GraphQL-Anfrage für viewer { login } sowie die Teams, denen du angehöhst (organizations.teams(role: MEMBER)). Die Team-Slugs sind entscheidend, denn GitHubs Such-API hat keinnen Qualifier wie „bei einem meiner Teams angefordert“ — du musst jedes Team einzeln nennen. Das ist der einzige Grund, warum der Token den read:org-Scope braucht.

2. Ein batched-Suche auslösen

GitHub hat keinn einziges Abfrage für „alles, was meine Aufmerksamkeit braucht“, also führt der Server mehreren Suchen aus und kombiniert sie. Alle laufen in einem einzigen GraphQL-Dokument über Aliases, sodass es eine einzige HTTP-Rundreise ist, egal wie vielen Teams du angehört:

Aliase

Suche

Wird zu Begründung

requested_of_me

is:pr is:open archiv:false review-requested:@me

requested_of_me

my_pr_awaiting_review

is:pr is:open archiv:false author:@me

my_pr_awaiting_review

team_0, team_1, …

is:pr is:open archiv:false team-review-requested:<org>/<team>

requested_of_my_teams

Suchstrings werden als GraphQL-Variabeln übergeben, niemals in das Anfrage-Dokument interpoliert — ein Team-Slug cann die Abfrage also nicht umformen.

Dieselbe Abfrage fragt außerem end rateLimit { remaining resetAt }, sodass jede A ntorte das verbleibende Budget ohne zweiten Aufruf melden kann.

Zwwei Anmerkungen zur Form der Antwort. GitHubs search(type: ISSUE) liefert Issues wie Pull Requests; weil das Auswahl-Set ein Inline-Fragment auf PullRequest ist, kommen Issues als leere Ergebnisse zurück und werden beim Lesen verworfen. statusCheckRollup wird aus comments(last:1) gelesen — der CI-Zustand des Head-Comits, nicht der gesamte Branch-Verlauf.

3. Zusammenführen, deduplizieren, filtern, rein

Der selbe Pull-Request taucht rourcinemäßig in mehrere Suchen auf — bei ein PR, bei dem du als direkter Reviewer eingetragen bist und deine Team angefordert wurde, erscheint er in zwei Eimer. Die Eimer werden anhand der GraphQL-Node-ID dedupliziert, und die Begründungen werden auf einem Eintrag accumulates. Die Antwort sagt dann „das ist aus zwei Grühenden hier“ anstatt es zweimal aufzustellen.

Danach werden deine Filter angewend, und was überbleibt, erhält eine Punktwert und wird sortiert.

4. Serialisieren

Die reelhte Liste kommt als struktierte Ausgabe zurü. Das Tool deklär ein vollständiges JSON-Ausgabescerma, sodass ein Client typsierte Feld erhält, anstatt zu parsen.


Related MCP server: pr-reviewer-mcp

Wie die Reihung funktion.

Die Reihung ist gestufft und nicht durch Gewichtsungen. Jeder Pull-Request landet in genau einer Stufe, und die Stufe ist deutlich mehr Wert als alles, was sich innerhalb irgendeine ansammelrt.

Stufe

Bedingung

Basis

3

Eine eigene PR mit fehlgeschlagener CI

300

2

Eine eigene PR mit angeforder Änderungen

200

1

Eine direkte Review-Anfrage an dich

100

0

Eine Team-Anfrage oder eine eigene PR, die nur wartet

0

Innerhalb ein. Innerhalb einer Stufe gelten zwei Klaine Signale:

  • Alter — 2 Punkte pro Tag since Öffnung der PR, gedeckelt auf 20. Alt Review-Anfragen tauchen damit auf, aber eine sechs Monate PR PR kann den für immer dominieren.

  • Kleiner Diff — einen flachen 8-Punkte-Bonus bei bis zu 100 Zeilen. Das Idee dahinten: ein kleine Review, das du jetzt abschließt, schlägt eine große, das du aufschieden sty.

Das Cap ist der ganze Punkt. Das Maximum, was innerhalb einer Stufe zusam kondern kann, ist 20 + 8 = 28 und damit klar unter dem Stufen- Sprung von 100. So gilt Stufen- Dominanz by Konstruktion: eine brand-neue direkte Anfrage übertrifft immer eine alt website, and no future weight tuning can still flip that. If you add a signal, keep total under 100, otherwise no longer up to the guarantee.

Bei Gleichstand zählt die jügste Aktivität (updatedAt), sodass eine aktive Diskussion eine festgefahrene mit demselben Punktwert übertrifft.

Jeder Eintrag enthält priority_reasons — menschenlesbare "Zeichenketten wie ["my PR, CI failing", "3 days old"] — sodass sich die Reihung nachvollzogen läst, anstatt als une-rklärter Punktwert zu kommen.


Installation

Erfordert Python 3.11+ und uv.

git clone <this repo>
cd ReviewQueueMcp
uv sync

Token

Der Server liest ein GitHub-personal-Access-Token aus der Bunkt GITHUB_TOKEN:

cp .env.example .env      # then edit it
export GITHUB_TOKEN=ghp_...

Erforderliche Scopes:

  • repo — liest Pull Requests in private Repos

  • read:org — liest deine Team-Mitgliedidungen for die Suchen nach team-review-requested

A klassisches PAT ist am einfachen. Feingranulare Tokens funktionieren, wenn sie "Pull requests: read" und Organizations-Mitglied-Lesechte erhalten. Eretes one unter https://github.com/settings/tokens.

GITHUB_GRAPHQL_URL überschreibt optional door endpoint for GitHub Enterprise Server.

Das Token wird pro Tool-Aufruf gelesen, nicht beim Start — der Server startert ohne Essen, und erst beim Aufruf is commit anwendbaren Fehler, anstatt bei MCP-Handshake zu herben, wo der Client to beเพียง einen „broken pipe“ sähe.

Wait, "wo der Client nur einen broken pipe sähe" perhaps. We use "gebrochene Pipe" okay.

## Running it

" Es sprch:

Es sprächt MCP über stdio und erwartet einen Client am anderen Ende; if run directly, it waits until.

uv run gh-review-queue-mcp

Mit MCP Inspector

npx @modelcontextprotocol/inspector uv --directory /absolute/path/to/ReviewQueueMcp run gh-review-queue-mcp

Öffne the printed URL, verbinde dich, and the tool appears under Tools via automatic Input-Schem.

With Claude Desktop

Füge in claude_desktop_config.json ein — under macOS under ~/~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "gh-review-queue": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/ReviewQueueMcp",
        "run",
        "gh-review-queue-mcp"
      ],
      "env": {
        "GITHUB_TOKEN": "ghp_..."
      }
    }
  }
}

Pfade müssen absolute sein — Claude Desktop startet Server nicht from deiner Shell, hat also keinen Wzug with inputed "Eschäft" environment. Desktop start after edit. And fragment asks: "Was soll ich heute überprüfen?"


Tool-Referenz

get_review_queue

Alle Argument sind optionen.

Argument

Typ

Standard

Bedeutung

include

requested_of_me | requested_of_my_teams |

Oops, table row correction. Need include array of. Let's compose carefully:

| include | array of requested_of_me | requested_of_my_teams | my_pr_awaiting_review | alle drei | Welche Begründungen eingeschlossen werden sollen. Ein Element bleibt erhalten, wenn irgendeine deiner Begründungen eingeschlossen ist. | | exclude_drafts | boolean | true | Drafts verwerfen. Sie sind ausgeschossen, nicht abgewertet — ein Draft ist noch nicht regul mac hen. | | max_age_days | integer | keine | Verfällt PRs, die mehr als angegeben Tage old. At boundary including. Wait original "opened more than this many days ago". translate: "Verfällt PRs, die vor mehr als x Tagen geöffnet wurden. Grenze inklusive." |

Hmm "max_age_days" code "none" default; "Verfällt Pull Requests, die vor mehr als diesen vielen Tagen geöffnet wurden. An der Grenze einschließend." Good.

| repos | array of owner/name | keine | Nur diese Repositories – exact Übereinstimmung. |

| limit | integer 1–100 | 25 | Maximal zurückgegeben Elemente. total_matching meldet bienr trotzdem die vollständige Anzahle. |

Response:

{
  "viewer": "octocat",
  "generated_at": "2026-08-20T12:00:00Z",
  "returned": 5,
  "total_matching": 5,
  "rate_limit_remaining": 4712,
  "warnings": [],
  "items": [
    {
      "repository": "acme/payments-api",
      "number": 4830,
      "title": "Add idempotency keys",
      "url": "https://github.com/acme/payments-api/pull/4830",
      "author": "octocat",
      "reasons": ["my_pr_awaiting_review"],
      "priority_score": 306.0,
      "priority_reasons": ["my PR, CI failing", "3 days old"],
      "age_days": 3.0,
      "diff_size": 374,
      "changed_files": 12,
      "is_draft": false,
      "review_decision": "REVIEW_REQUIRED",
      "ci_status": "FAILURE"
    }
  ]
}

returned vs total_matching unterscheidet „hier sind 25“ von „es gibt viele„ — without because "s` left. A limitierte Antwort imüsste "unterscheidbar from a complete one."

warnings enthält GraphQL-teilweise Fehler. GitHub can mit Fehlern bra uchtbare Daten (eine Org nicht lesbar, e Suche fehlgeschlagen); statt getthe whole queue wegzuwerfen, werden die herabgestufen and the rest of the results are still returned.


Architektur

For modules under src/gh_review_queue/, and the boundaries are load-bearing:

server.py    MCP wiring. Parse arguments -> call client -> domain layer -> serialize.
   |         Deliberately thin; its docstring sets a ~120-line budget.
   v
github.py    The only module that touches the network. Builds GraphQL, handles HTTP
   |         and GraphQL errors, returns domain objects. Never ranks or filters.
   v
queue.py     Pure functions: merge -> apply_filters -> rank/score, via build_queue.
   |         Input is a snapshot and a clock. Nothing else.
   v
models.py    Frozen pydantic value objects. The only place GitHub's nested GraphQL
             shape is flattened. No network types.

Der egentliche Gewinn is queue.py: Weil it takes QueueSnapshot and a datetime and nothing else, jede Reihungs-Regel wird mit einfachen Daten getestst and ohne Mocks, ohne Netzwork and ohne Uhr zupachen. Das is the reason for split, and why an httpx import never becomm there.

Abschwächung statt Fehlschlag

Unbekannte Enum-Werte from GitHub — a new reviewDecision, a new CI-Rollup-Zustand — are on None abgebilidet, answer st auf. A state added on GitHub side should never break the whole queue. The same Grundatz passes through the parsing layer: fehlende Autogor become ghost (GitHub's own convention for errscheut? verifzte accounts? Wait English "deleted" is "gelöschte". Need "gelöschte Konten". "Nicht-PR-Suchergebnisse werden verworfen, and fehlende Zeitstempe sind the one truly unrecoverable case, the "einen Fehler auslösen.

## Development

uv run pytest                       # all tests
uv run pytest tests/test_queue.py   # one file
uv run pytest -k "rank or score"    # by name
uv run ruff check .                 # lint
uv run ruff format .                # format
uv run mypy                         # typecheck (strict)

Führe mypy without aarguments — it "takes its a target from [tool.mypy] files in pyproject.toml, thes s: "One path is enough not beabsicht."

Testing approach

Tests laufen run by tests/fixtures/queue_response.json, a caught GraphQL answer, that she awkward cases built: PR, the PR in two blocks, a Draft, a very stale PR, a PRs of the viewer with failing CI and a null status rollup.

test_rank_orders_the_ixture_the_way_a_reviewer_would_read_it asserts exact scores against a fixed clock. It's the canary for "Scoring changes" — if it fails, decide whether the new order is genuinely better before to update the numbers.

Status

Phase

Umfang

Zustand

1 Ein Construct, package, tooling

fertig

2 models.py, queue.py, domain-Tests

The

3 github.py GraphQL Client, real server.py

4 Client und Server-Tests

not started

5 Document

this is the is the

Phase 3 is end-to-end verified — a real MCP over stdio handshake, tool discovery and a tool call — but tests/test_server.py is still placeholder. The client's exception paths (401, 403, partial GraphQL failures, unreachable host) are noted but not yet covered by tests automatic.

License

This project is licensed under Apache License 2.0. See LICENSE file for details.

Available Tools

1 tool
get_review_queueA

Return the viewer's GitHub pull request review queue, ranked by what needs attention first: their own pull requests with failing CI, then their own with changes requested, then reviews requested of them directly, then reviews requested of their teams. Within a tier, older and smaller pull requests rank higher. Every item carries priority_reasons explaining its position, and total_matching reports how many matched before the limit.

NameRequiredDescriptionDefault
limitNoMaximum items to return.
reposNoRestrict to these repositories, as 'owner/name'.
includeNoWhich reasons to include. Defaults to all three.
max_age_daysNoDrop pull requests opened more than this many days ago.
exclude_draftsNoDrop draft pull requests. Defaults to true.

Output Schema

NameRequiredDescription
itemsYes
viewerYes
returnedYes
warningsNo
generated_atYes
total_matchingYes
rate_limit_remainingNo

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the burden of disclosure. It reveals the ranking tiers, tie-breaking rules, and the fact that results include priority_reasons and total_matching. It does not discuss auth, errors, or side effects, but the operation is clearly read-oriented and described in useful detail.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose and ranking intent, then economically conveys the tier order and output signals in two structurally clear runs. Every clause earns its place and no filler exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete enough for reliable invocation. It covers behavior, output information, ordering, and scoping semantics, the output schema and full parameter documentation handle the remaining return-value details, and there are no required parameters or sibling tools to complicate selection.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description does not elaborate on the individual parameters such as limit, repos, include, max_age_days, or exclude_drafts, but it does not need to because those parameters are already well-documented in the input schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: "Return the viewer's GitHub pull request review queue," and goes further by specifying the exact ranking logic. It is immediately clear what this tool does and how it differs from a generic list-pull-requests tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There are no siblings to contrast against, so the explicit when/when-not language is less necessary. The description makes the intended use clear: retrieve a prioritized review queue with tiered attention ordering, which is sufficient context for an agent to select it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev0.1.0
    • First observedget_review_queue

TDQS

A4.4/5.0

Scored across 1 tool

Disambiguation5/5

The set contains only one tool, so there is no possibility of overlap or selecting the wrong tool. Its purpose is clearly and specifically described.

Naming Consistency5/5

The single tool name follows the conventional verb_noun pattern with a clear action and resource. There are no other tool names to create inconsistency.

Tool Count4/5

One tool is small, but the server is narrow by design: it exists specifically to fetch a GitHub review queue. The tool is substantial rather than trivial, so the count is slightly lean but still appropriate for the server's scope.

Completeness5/5

The tool covers the full review queue surface described: own PRs, requested changes, direct review requests, and team review requests, along with ranking reasons and match counts. There are no obvious read-model gaps within this narrow domain.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers