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-запрос, независимо от того, в скольких командах вы состоите:
Алиас | Поиск | Становится причиной |
|
|
|
|
|
|
|
|
|
Поисковые строки передаются как 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
Все аргументы необязательны.
Аргумент | Тип | По умолчанию | Значение |
| массив | все три | Какие причины включать. Элемент сохраняется, если включена любая из его причин. |
| логический |
| Отбрасывать черновики. Они исключаются, а не понижаются — черновик ещё нельзя ревьюить. |
| целое число | нет | Отбрасывать PR, открытые более этого количества дней назад. Включительно по границе. |
| массив | нет | Ограничить этими репозиториями. Точное совпадение. |
| целое число 1–100 |
| Максимум возвращаемых элементов. |
Ответ:
{
"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 |
| готово |
3 |
| готово |
4 | Клиентские и серверные тесты | не начато |
5 | Документация | этот файл |
Фаза 3 проверена в конце концов — реальное MCP-рукопожатие через stdio, обнаружение инструмента и вызов инструмента, — но tests/test_server.py по-прежнему заглушка. Пути ошибок клиента (401, 403, частичные сбои GraphQL, недостижимый хост) написаны, но ещё не покрыты автоматическими тестами.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseAqualityDmaintenanceA minimal MCP server that exposes a focused set of GitHub PR review tools to AI agents, enabling PR listing, detail retrieval, comment viewing, and thread management.5
- AlicenseAqualityBmaintenanceAn MCP server that provides operational tooling over the GitHub API — issue triage, PR review monitoring, repo health audits, and team access reviews.111MIT
- FlicenseAqualityBmaintenanceAn MCP server exposing AI-powered GitHub PR review as tools.5
- AlicenseAqualityCmaintenanceA production-ready MCP server for triaging and reviewing GitHub pull requests via the GitHub REST API, providing typed tools to list, inspect, comment, add labels, and submit reviews.821MIT
Related MCP Connectors
A Model Context Protocol (MCP) application for automated GitHub PR analysis and issue management.…
An MCP server that gives your AI access to the source code and docs of all public github repos
A MCP server built for developers enabling Git based project management with project and personal…
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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