Skip to main content
Glama
JigeeshaJain

gh-review-queue-mcp

gh-review-queue-mcp

MCP-сервер, который отвечает на один вопрос: что мне ревьюить следующим?

Он предоставляет ровно один инструмент, get_review_queue, который возвращает ранжированное и дедуплицированное представление вашей очереди ревью пул-реквестов GitHub — ревью, запрошенные у вас, ревью, запрошенные у ваших команд, и ваши собственные пул-реквесты, ожидающие кого-то другого.

Один инструмент — это осознанное ограничение. Ассистент, которому приходится выбирать между list_prs, search_prs и get_pr_status, тратит свой первый ход на выбор; ассистент с одним инструментом, возвращающим уже приоритизированный список, может просто ответить.


Что это на самом деле делает

Когда инструмент вызывается, по порядку происходят четыре вещи.

1. Определение вас и ваших команд

Сервер отправляет GraphQL-запрос для viewer { login } плюс команды, в которых вы состоите (organizations.teams(role: MEMBER)). Слаги команд важны, потому что в поисковом API GitHub нет квалификатора «запрошено у любой из моих команд» — каждую команду нужно называть явно. Это единственная причина, по которой токену требуется область read:org.

2. Разветвление в один пакетный поиск

В GitHub нет единого запроса для «всего, что требует моего внимания», поэтому сервер выполняет несколько поисков и объединяет их. Все они отправляются в одном GraphQL-документе с использованием алиасов, так что это один HTTP-запрос, независимо от того, в скольких командах вы состоите:

Алиас

Поиск

Становится причиной

requested_of_me

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

requested_of_me

my_pr_awaiting_review

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

my_pr_awaiting_review

team_0, team_1, …

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

requested_of_my_teams

Поисковые строки передаются как GraphQL-переменные и никогда не интерполируются в документ запроса, поэтому слаг команды не может изменить запрос.

Тот же запрос также запрашивает rateLimit { remaining resetAt }, так что каждый ответ может сообщить ваш оставшийся бюджет без дополнительного вызова.

Два замечания о форме ответа. search(type: ISSUE) в GitHub возвращает и issues, и пул-реквесты; поскольку набор полей — это инлайн-фрагмент на PullRequest, issues возвращаются как пустые узлы и отбрасываются при разборе. А statusCheckRollup читается из commits(last: 1) — состояние CI головного коммита, а не всей истории ветки.

3. Объединение, дедупликация, фильтрация, ранжирование

Один и тот же пул-реквест часто приходит из нескольких поисков — PR, где вы являетесь непосредственным ревьюером и где запрошена ваша команда, появляется в двух корзинах. Они дедуплицируются по node id GraphQL, а причины накапливаются в одной записи, так что ответ говорит «это здесь по двум причинам», а не перечисляет его дважды.

Затем применяются ваши фильтры, а то, что осталось, получает оценку и сортируется.

4. Сериализация

Ранжированный список возвращается в виде структурированного вывода — инструмент объявляет полную JSON-схему вывода, так что клиент получает типизированные поля, а не текст, который нужно разбирать.


Related MCP server: github-ops-mcp

Как работает ранжирование

Ранжирование многоуровневое, а не настраиваемое весами. Каждый пул-реквест попадает ровно на один уровень, и уровень значит намного больше, чем всё, что накапливается внутри него:

Уровень

Условие

База

3

Ваш собственный PR с упавшим CI

300

2

Ваш собственный PR с запрошенными изменениями

200

1

Ревью, запрошенное у вас напрямую

100

0

Запрос от команды или ваш PR, который просто ждёт

0

Внутри уровня действуют два меньших сигнала:

  • Возраст — 2 очка в день с момента открытия PR, максимум 20. Старые запросы на ревью всплывают, но шестимесячный PR не может доминировать вечно.

  • Небольшой диф — плоский бонус в 8 очков за дифы размером 100 строк или меньше, исходя из того, что небольшое ревью, которое можно закончить сейчас, лучше большого, которое вы отложите.

Ограничение — и есть суть. Максимум, что может накопиться внутри уровня, — 20 + 8 = 28, что значительно меньше шага уровня в 100, так что доминирование уровня выполняется по построению: новый прямой запрос всегда превосходит древний командный запрос, и никакая будущая подстройка весов не может это незаметно изменить. Если вы добавляете сигнал оценки, держите внутриуровневую сумму ниже 100, иначе эта гарантия нарушится.

Связи разрешаются по последней активности (updatedAt), так что активное обсуждение опережает зависшее при том же балле.

Каждый элемент несёт priority_reasons — человекочитаемые строки вроде ["мой PR, CI упал", "3 дня"], — чтобы ранжирование можно было объяснить, а не получить как необъяснимое число.


Установка

Требуются Python 3.11+ и uv.

git clone <this repo>
cd ReviewQueueMcp
uv sync

Токен

Сервер читает персональный токен доступа GitHub из GITHUB_TOKEN:

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

Требуемые области доступа:

  • repo — чтение пул-реквестов в частных репозиториях

  • read:org — чтение вашего членства в командах для поиска по командным запросам

Классический PAT — проще всего. Тонкозернистые токены работают, если выдано «Pull requests: read» плюс чтение участников организации. Создайте его на https://github.com/settings/tokens.

GITHUB_GRAPHQL_URL опционально переопределяет конечную точку для GitHub Enterprise Server.

Токен читается при каждом вызове инструмента, а не при запуске — сервер запускается без него и возвращает понятную ошибку при вызове, а не падает во время MCP-рукопожатия, где клиент увидел бы только разорванный канал.


Запуск

uv run gh-review-queue-mcp

Он говорит по MCP через stdio и ожидает клиента на другом конце; запущенный напрямую, он просто ждёт.

С MCP Inspector

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

Откройте напечатанный URL, подключитесь, и инструмент появится в разделе Tools со своей сгенерированной входной схемой.

С Claude Desktop

Добавьте в claude_desktop_config.json — в macOS по пути ~/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_..."
      }
    }
  }
}

Пути должны быть абсолютными — Claude Desktop не запускает серверы из вашей оболочки, поэтому у него нет рабочего каталога или экспортированного окружения для наследования. После редактирования перезапустите Claude Desktop. Затем спросите его: «что мне сегодня ревьюить?»


Справочник инструмента

get_review_queue

Все аргументы необязательны.

Аргумент

Тип

По умолчанию

Значение

include

массив requested_of_me | requested_of_my_teams | my_pr_awaiting_review

все три

Какие причины включать. Элемент сохраняется, если включена любая из его причин.

exclude_drafts

логический

true

Отбрасывать черновики. Они исключаются, а не понижаются — черновик ещё нельзя ревьюить.

max_age_days

целое число

нет

Отбрасывать PR, открытые более этого количества дней назад. Включительно по границе.

repos

массив owner/name

нет

Ограничить этими репозиториями. Точное совпадение.

limit

целое число 1–100

25

Максимум возвращаемых элементов. total_matching по-прежнему сообщает полное количество.

Ответ:

{
  "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 и total_matching различают «вот 25» и «их много» — без этого ограниченный ответ неотличим от полного.

warnings несёт частичные ошибки GraphQL. GitHub может вернуть полезные данные вместе с ошибками (одна организация нечитаема, один поиск падает); вместо того чтобы выбрасывать всю очередь, они понижаются до предупреждений, а остальные результаты возвращаются.


Архитектура

Четыре модуля в src/gh_review_queue/, и границы несут нагрузку:

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.

Выигрыш — в queue.py: поскольку он принимает QueueSnapshot и datetime и больше ничего, каждое правило ранжирования тестируется на простых данных и без моков, сети и подмены часов. Именно поэтому проведено разделение и почему импорт httpx никогда не должен туда попадать.

Деградация вместо отказа

Неизвестные значения перечислений от GitHub — новый reviewDecision, новое состояние сводки CI — преобразуются в None, а не вызывают исключение. Состояние, добавленное на стороне GitHub, не должно ломать всю вашу очередь. Тот же инстинкт проходит через весь слой разбора: отсутствующие авторы становятся ghost (собственное соглашение GitHub для удалённых аккаунтов), результаты поиска, не являющиеся PR, отбрасываются, а отсутствующие временные метки — единственный действительно невосстановимый случай, который вызывает исключение.


Разработка

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)

Запускайте mypy без аргументов — он берёт цели из [tool.mypy] files в pyproject.toml, поэтому передача пути проверяет меньше, чем задумано.

Подход к тестированию

Тесты работают на tests/fixtures/queue_response.json — одном захваченном GraphQL-ответе, созданном так, чтобы содержать неудобные случаи: PR, появляющийся в двух корзинах, черновик, очень устаревший PR, PR зрителя с упавшим CI и нулевая сводка статуса.

test_rank_orders_the_fixture_the_way_a_reviewer_would_read_it проверяет точные оценки относительно фиксированных часов. Это канарейка для изменений оценки — если она падает, решите, действительно ли новый порядок лучше, прежде чем обновлять числа.


Статус

Фаза

Объём

Состояние

1

Каркас, упаковка, инструментарий

готово

2

models.py, queue.py, доменные тесты

готово

3

github.py GraphQL-клиент, настоящий server.py

готово

4

Клиентские и серверные тесты

не начато

5

Документация

этот файл

Фаза 3 проверена в конце концов — реальное MCP-рукопожатие через stdio, обнаружение инструмента и вызов инструмента, — но tests/test_server.py по-прежнему заглушка. Пути ошибок клиента (401, 403, частичные сбои GraphQL, недостижимый хост) написаны, но ещё не покрыты автоматическими тестами.

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.

ParametersJSON Schema
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

ParametersJSON 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. Dates show when Glama detected each change.

  1. 1 tool updatev0.1.0
    • First observedget_review_queue

TDQS

A4.4/5.0
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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/JigeeshaJain/ReviewQueueMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server