test-intelligence-mcp
test-intelligence-mcp
Сервер MCP (Model Context Protocol), который предоставляет AI-агентам кодирования — Claude Code, Claude Desktop или любому другому MCP-клиенту — возможность анализировать состояние тестов Python-репозитория: покрытие, нестабильные тесты и прогнозирование рисков для pull request на основе машинного обучения.
Статус: в активной разработке. Этот README расширяется с каждой вехой; см. Статус сборки ниже, чтобы узнать, что уже реализовано, а что ещё в планах.
Что он делает
Укажите ему Python-репозиторий, и в ходе беседы на естественном языке с MCP-совместимым агентом вы сможете:
Запустить набор тестов репозитория с покрытием и получить реальные показатели по каждому файлу (
analyze_coverage)Запустить набор тестов несколько раз и обнаружить действительно нестабильные тесты, в отличие от зависящих от порядка или окружения сбоев (
detect_flaky_tests)Сохранять результаты тестовых запусков в Postgres для накопления истории с течением времени (
record_test_run)Запрашивать эту историю (
get_test_history)Обучить классификатор на основе градиентного бустинга на накопленной истории запусков, чтобы прогнозировать, какие файлы в pull request, вероятно, сломают тесты (
train_risk_model)Сравнить ветку с базовым рефом и получить ранжированную оценку риска для каждого изменённого файла (
predict_pr_risk)
Всё основано на реальном выполнении тестов в подпроцессе и реальном парсинге coverage.json — здесь нет скрейпинга вывода терминала или поддельных чисел.
Related MCP server: Chisel
Зачем MCP
MCP — это протокол (с открытым исходным кодом от Anthropic, ныне широко принятый), который позволяет AI-агенту обнаруживать и вызывать инструменты, предоставляемые отдельным серверным процессом, через стандартный транспорт JSON-RPC (stdio локально или HTTP/SSE удалённо). Вместо того чтобы создавать собственный API и обучать системный промпт агента работе с ним, вы предоставляете типизированные Python-функции как «инструменты»; клиент автоматически обнаруживает их имена, схемы аргументов и строки документации и вызывает их в процессе беседы. Этот проект использует FastMCP, эргономичный Python SDK, построенный поверх официальной спецификации MCP — @mcp.tool() на обычной типизированной функции достаточно, чтобы предоставить её.
Технологический стек
Компонент | Выбор |
MCP-сервер | |
Выполнение тестов | pytest, pytest-cov, coverage.py (парсит |
База данных | PostgreSQL, async SQLAlchemy 2.0 ( |
Миграции | Alembic (версионные, без |
Машинное обучение | scikit-learn |
Git-операции | GitPython / subprocess |
CI | GitHub Actions |
Локальный Postgres | Docker + docker-compose |
Управление пакетами |
Структура репозитория
test-intelligence-mcp/
src/test_intelligence/
server.py # FastMCP server + tool registration
config.py # typed settings, loaded from .env
safety.py # repo-path allowlist gate (see Safety below)
paths.py # cross-platform file-path normalization
runners/ # pytest/coverage execution, JUnit + coverage.json parsing
flaky/ # multi-run comparison logic, order/seed control
ml/ # features.py, synthetic.py, training.py, prediction.py, model_store.py
db/ # SQLAlchemy models, session, query helpers
git/ # commit/branch metadata (repo_info.py), diff stats (diff.py)
tests/ # tests for THIS project's own code
fixtures/ # tiny throwaway repos the runner tests execute for real
scripts/
ci_report.py # flaky-check + coverage summary, invoked by CI (see below)
alembic/ # migration scripts
.github/workflows/
ci.yml # runs on every PR — see Continuous Integration below
docker-compose.yml # local Postgres
pyproject.toml
.env.exampleНастройка
1. Предварительные требования
Python 3.11+
uv — быстрая, современная замена pip + venv + virtualenv, используется здесь для управления зависимостями и выполнения команд. На Windows:
winget install -e --id astral-sh.uv.Docker Desktop — используется для локального запуска Postgres через docker-compose, поэтому вам не нужно устанавливать Postgres на свою машину. На Windows:
winget install -e --id Docker.DockerDesktop.
2. Установка зависимостей
uv syncuv sync читает pyproject.toml, разрешает заблокированный набор зависимостей (записывая/используя uv.lock) и создаёт .venv/ — аналог pip install -r requirements.txt внутри свежего virtualenv от uv, но быстрее и воспроизводимо на разных машинах.
3. Запуск Postgres
docker-compose up -dЭто запускает контейнер Postgres 16, определённый в docker-compose.yml, доступный на localhost:5433 с учётными данными, встроенными в этот файл. (Порт 5433, а не стандартный для Postgres 5432, чтобы избежать конфликта, если у вас уже установлен Postgres нативно — см. docker-compose.yml для подробностей.) -d запускает его в фоновом режиме (detached). Проверьте, что он здоров:
docker-compose psВы должны увидеть test-intelligence-postgres со статусом healthy.
4. Настройка окружения
cp .env.example .envЗначения по умолчанию в .env.example уже соответствуют учётным данным из docker-compose.yml, поэтому для локальной разработки обычно не нужно ничего менять, кроме TI_ALLOWED_REPO_ROOTS (см. Безопасность ниже).
5. Применение миграций базы данных
uv run alembic upgrade headAlembic последовательно воспроизводит каждый скрипт миграции из alembic/versions/, приводя схему базы данных к последней версии. В отличие от Base.metadata.create_all() из SQLAlchemy (который может только создать таблицы, соответствующие текущему коду модели, без памяти о прошлых состояниях), Alembic отслеживает историю схемы как упорядоченную цепочку скриптов — поэтому изменения можно просматривать в git, откатывать (alembic downgrade) и применять одинаково в dev, CI и prod.
6. Регистрация в MCP-клиенте
Claude Code
claude mcp add test-intelligence -- uv run --directory "C:\path\to\test-intelligence-mcp" test-intelligence-mcpИспользование --directory (вместо того чтобы полагаться на текущую директорию, из которой вы запустили claude mcp add) делает регистрацию работоспособной независимо от того, откуда процесс Claude Code позже запустит сервер — это важно, потому что сервер читает .env относительно своей рабочей директории при запуске.
Это регистрирует сервер как MCP-сервер с транспортом stdio, привязанный к вашей локальной конфигурации Claude Code. Проверьте подключение с помощью claude mcp list, затем запустите новую сессию Claude Code (уже запущенная сессия не подхватит сервер, зарегистрированный после её старта) и попросите его перечислить доступные инструменты.
Claude Desktop
Добавьте в claude_desktop_config.json (Windows: %APPDATA%\Claude\claude_desktop_config.json):
{
"mcpServers": {
"test-intelligence": {
"command": "uv",
"args": ["--directory", "C:\\path\\to\\test-intelligence-mcp", "run", "test-intelligence-mcp"]
}
}
}Перезапустите Claude Desktop; шесть инструментов должны появиться под иконкой 🔨.
Безопасность
Поскольку эти инструменты выполняют реальный набор тестов целевого репозитория — то есть произвольный Python-код — как подпроцесс, безусловно применяются два ограничения:
Белый список путей: каждый аргумент
repo_pathпреобразуется в абсолютный путь и проверяется на соответствиеTI_ALLOWED_REPO_ROOTS(разделённый запятыми список разрешённых базовых директорий) в.env. Пути вне белого списка отклоняются до запуска любого подпроцесса.Тайм-ауты подпроцессов: каждый вызов
subprocess(запуск pytest, git-команда) имеет жёсткий тайм-аут (TI_SUBPROCESS_TIMEOUT_SECONDSв.env, по умолчанию 300 с), чтобы зависший или бесконечно зацикленный набор тестов не мог заблокировать сервер на неопределённое время.
Примеры использования
analyze_coverage
Попросите MCP-совместимый агента примерно так: «Запусти analyze_coverage для C:\path\to\some-repo». Инструмент запускает набор тестов этого репозитория с покрытием (используя его собственный .venv/venv, если он есть, иначе возвращаясь к интерпретатору этого сервера) и возвращает:
{
"status": "ok",
"tests_passed": true,
"overall_coverage_percent": 87.5,
"total_statements": 120,
"total_covered_lines": 105,
"total_uncovered_lines": 15,
"files": [
{
"file": "pkg/calculator.py",
"coverage_percent": 80.0,
"num_statements": 10,
"covered_lines": 8,
"uncovered_line_count": 2,
"uncovered_lines": [12, 13]
}
]
}files отсортирован от худшего покрытия к лучшему, поэтому агент может сразу указать на файлы, наиболее нуждающиеся в тестах. Требует, чтобы в целевом репозитории были установлены pytest и pytest-cov в том окружении Python, которое будет использовано.
record_test_run + get_test_history
«Запиши тестовый запуск для C:\path\to\some-repo, затем покажи историю для pkg/calculator.py» — первый вызов однократно выполняет набор тестов через вывод --junitxml pytest (так что в целевом репозитории достаточно только pytest, плагин не нужен), сохраняет набор строк Repository/TestRun/TestResult в Postgres и помечает запуск текущим SHA коммита и веткой целевого репозитория (через GitPython), если это настоящий git-репозиторий:
{
"status": "ok",
"run_id": 3,
"repo_id": 1,
"commit_sha": "a1b2c3d...",
"branch": "main",
"duration_seconds": 0.52,
"total_tests": 3,
"passed_count": 1,
"failed_count": 1,
"skipped_count": 1
}get_test_history только читает строки, уже записанные record_test_run — он никогда не запускает тесты сам и запрашивает данные по всем репозиториям, которые когда-либо записывал этот сервер (аргумента repo_path нет), с возможностью фильтрации по одному file_path:
{
"status": "ok",
"count": 2,
"history": [
{
"repo_name": "C:\\path\\to\\some-repo",
"run_id": 3,
"commit_sha": "a1b2c3d...",
"branch": "main",
"started_at": "2026-08-16T00:20:11+00:00",
"node_id": "tests/test_calculator.py::test_divide",
"file_path": "tests/test_calculator.py",
"outcome": "passed",
"duration_seconds": 0.001,
"error_message": null
}
]
}detect_flaky_tests
«Запусти detect_flaky_tests для C:\path\to\some-repo с 5 запусками» — выполняет набор тестов runs раз с явно отключёнными известными плагинами, перемешивающими порядок тестов (pytest-randomly, pytest-random-order), так что порядок тестов идентичен в каждом запуске. Это изолирует настоящую недетерминированность (время выполнения, общее состояние, неинициализированная случайность в тестируемом коде) как единственное возможное объяснение того, что тест даёт разные результаты в разных запусках — плагин, перемешивающий порядок, иначе сделал бы сбои, зависящие от порядка, неотличимыми от настоящей нестабильности. Прогресс передаётся в реальном времени через MCP-уведомления о прогрессе (видимые клиентам, которые их поддерживают), так как 5+ последовательных запусков большого набора тестов могут занять некоторое время:
{
"status": "ok",
"repo_id": 2,
"runs_requested": 5,
"runs_completed": 5,
"total_tests_observed": 2,
"flaky_test_count": 1,
"flaky_tests": [
{
"node_id": "tests/test_flaky.py::test_alternates",
"runs_observed": 5,
"inconsistency_count": 2,
"flakiness_rate": 0.4,
"outcomes": ["passed", "failed", "passed", "failed", "passed"],
"majority_outcome": "passed"
}
],
"run_failures": []
}Обнаруженные нестабильные тесты также сохраняются в таблицу flaky_reports.
train_risk_model
«Обучи модель риска» — обучает GradientBoostingClassifier прогнозировать «упадёт ли тест после изменения этого файла?» на основе 10 признаков на файл (объём изменений, историческое количество сбоев, текущее покрытие, количество тестов, затрагивающих файл, дней с момента последнего изменения, количество разных авторов, размер файла, цикломатическая сложность — см. Стратегия холодного старта для ML-модели для информации о том, откуда берутся обучающие данные), оценивает на отложенной выборке и честно сообщает:
{
"status": "ok",
"model_path": "models/risk_model.joblib",
"real_sample_count": 0,
"synthetic_sample_count": 500,
"total_sample_count": 500,
"test_set_size": 125,
"metrics": {
"accuracy": 0.6,
"precision": 0.5962,
"recall": 0.5167,
"f1": 0.5536
},
"caveat": "Only 0 real training example(s) recorded so far (via record_test_run) — this training run is dominated by synthetic, artificially-generated bootstrap data. These metrics describe how well the model fits that synthetic relationship, NOT real predictive power on an actual repository. Keep calling record_test_run on real repos, then retrain, before trusting these numbers for anything beyond confirming the training pipeline itself works."
}Поле caveat исчезает только когда real_sample_count превышает реальный порог (30, см. ml/training.py) — этот инструмент никогда не представляет метрики, основанные на синтетических данных, как если бы они были проверены на реальных.
(Остальные инструменты будут добавлены по мере достижения вех — см. Статус сборки.)
Стратегия холодного старта для ML-модели
train_risk_model нужны размеченные примеры — «имея эти признаки об изменении файла, провалился ли тест, связанный с этим файлом, после этого?» На только что настроенном сервере не записано ни одного запуска, поэтому нет истории для обучения. Перед написанием любого ML-кода были рассмотрены три варианта:
Воспроизвести историю git/CI реального репозитория с открытым исходным кодом. Клонировать реальный проект, пройти по его коммитам, проверить каждый, установить его зависимости, какими они были в тот момент истории, запустить его набор тестов, извлечь реальные признаки и реальные метки. Самые реалистичные данные — но дорого и хрупко в реализации: установка зависимостей ломается на протяжении многих лет истории (устаревшие пакеты, изменение версий Python), полная проверка истории медленна, и это привязывает жёсткую внешнюю зависимость (конкретный репозиторий в конкретный момент времени) к CI этого проекта, который должен будет воспроизводить её при каждом запуске.
Воспроизвести собственные коммиты этого проекта. Та же идея, меньший масштаб — не решает основную проблему стоимости, и собственная история этого проекта слишком коротка и узка, чтобы представлять широту паттернов изменения файлов, на которых должна обобщаться модель общего назначения.
Генерация синтетических данных (выбрано). Извлечь векторы признаков из правдоподобных распределений и вывести метки на основе намеренно разработанного, предметно-ориентированного порождающего правила — больше изменений + больше прошлых сбоев + меньшее покрытие + большая сложность → более высокая вероятность сбоя, плюс шум — а не подбрасывание монетки. Быстро, полностью воспроизводимо, не требует внешнего репозитория и достаточно для честной проверки всего конвейера (извлечение признаков → обучение → оценка) уже сегодня.
Честный компромисс: модель, обученная исключительно на синтетических данных, выучила форму правдоподобной зависимости риска, а не реальную. Её метрики на отложенных синтетических данных выглядят разумно (~0.6 точность, ROC-AUC ~0.67 — см. tests/ml/test_synthetic.py), что доказывает только работоспособность конвейера, а не то, что он что-либо прогнозирует для реального репозитория. train_risk_model смешивает реальные примеры, как только они появляются (через record_test_run → строки file_changes — см. ниже) и всегда сообщает соотношение реальных/синтетических данных, а также явное предупреждение, когда реальных данных слишком мало, чтобы им доверять, вместо того чтобы когда-либо представлять синтетические числа как проверенные.
Откуда берутся реальные примеры: record_test_run вычисляет реальный git diff (HEAD~1..HEAD) после каждого прогона и записывает одну строку file_changes на каждый изменённый файл, помеченную tests_failed_after = «провалился ли хоть один тест в этом прогоне» — применяется ко всем изменённым в нём файлам, а не атрибутируется пофайлово. Это осознанный выбор: привязка сбоя к конкретному файлу, который его вызвал, потребовала бы трассировки на основе покрытия (какой тест выполнял какие строки исходного кода), чего этот проект не делает. Более грубый сигнал — это честно корреляционный («этот файл был частью коммита, который что-то сломал»), а не причинно-следственный — полное обоснование, включая то, почему эвристика сопоставления строк путей к файлам выглядела бы более точной, будучи на самом деле более узкой и вводящей в заблуждение, см. в комментарии в runners/record_run.py.
Извлечение исторических признаков (используемых для реальных обучающих примеров) читает историю git «по состоянию на» временную метку и коммит каждого записанного прогона — git log --before, git show <sha>:<path> — никогда не текущее состояние файла, поэтому модель не может случайно обучиться на информации, которой ещё не существовало на момент предсказания. coverage_percent — единственный признак, который невозможно восстановить исторически без повторного запуска всего набора тестов на том же коммите (слишком дорого для каждого обучающего примера), поэтому для реальных исторических строк он сохраняется как явный маркер «неизвестно» и вычисляется заново только для живых предсказаний (predict_pr_risk, ниже).
predict_pr_risk
«Предсказать риск PR для C:\path\to\some-repo относительно main» — вычисляет diff base_ref..HEAD (реальный git diff --numstat), извлекает живые признаки для каждого изменённого файла (текущее состояние рабочей директории плюс свежий прогон analyze_coverage для реального текущего покрытия — не маркер «неизвестно», который получают исторические обучающие строки), оценивает каждый с помощью обученной модели и ранжирует по убыванию риска:
{
"status": "ok",
"repo_id": 3,
"base_ref": "0bcbeba860df0457c55ad3c1d3826ed5fd941506",
"commit_sha": "8306f4598275f92907de89e1161f982772f3aac7",
"model_trained_at": "2026-08-17T19:03:42.707577+00:00",
"model_real_sample_count": 0,
"predictions": [
{ "file": "tests/test_calculator.py", "predicted_risk_probability": 0.0743, "lines_added": 9, "lines_deleted": 1 },
{ "file": "pkg/calculator.py", "predicted_risk_probability": 0.0457, "lines_added": 7, "lines_deleted": 0 }
]
}model_real_sample_count передаётся из метаданных обучения модели — чтобы вызывающий код мог с первого взгляда увидеть, получены ли эти предсказания от модели, в которой доминируют синтетические данные (см. Стратегия холодного старта), без отдельного запроса. Требует, чтобы train_risk_model был запущен хотя бы один раз (в противном случае ошибка no_trained_model) — этот инструмент никогда не обучает модель неявно как побочный эффект. Каждое предсказание сохраняется в risk_predictions с actual_outcome, оставленным NULL, чтобы предсказания для реального репозитория можно было в конечном итоге проверить на соответствие тому, что произошло на самом деле — такая оценка ещё не реализована, но данные собираются с первого дня, чтобы её можно было добавить без изменения схемы.
Непрерывная интеграция (GitHub Actions)
GitHub Actions — это CI, встроенная непосредственно в GitHub: workflow — один YAML-файл, .github/workflows/ci.yml — описывает jobs, которые запускаются автоматически в ответ на события репозитория (здесь: открытие/обновление pull request или push в main). Каждый job выполняется на свежей, одноразовой виртуальной машине («runner») — между запусками ничего не сохраняется, кроме того, что явно кэшировано или загружено — и представляет собой просто последовательность steps, каждый из которых является либо shell-командой, либо переиспользуемым action (упакованный шаг, опубликованный кем-то другим, на который ссылаются как actions/checkout@v4).
Workflow этого проекта — это самотестирование проекта «из конца в конец»:
Проверить репозиторий и установить
uv+ зависимости — те же инструменты, которые разработчик устанавливает локально.Запустить Postgres как сервисный контейнер — второй контейнер, который GitHub Actions запускает вместе с job, доступный по адресу
localhost:5433из каждого шага, точно так же, какdocker-compose up -dлокально, но управляемый GitHub вместо Docker Desktop. Job не запускает свои шаги, пока не пройдёт его healthcheck — не требуется ручного цикла ожидания Postgres.Применить миграции Alembic, затем запустить собственный тестовый набор проекта с
--cov-fail-under=$COVERAGE_THRESHOLD— встроенный шлюз pytest-cov; сборка завершается ошибкой, если покрытие падает ниже этого порога (в настоящее время 80%, с запасом под реальными ~93%).Запустить
detect_flaky_testsдля собственных тестов проекта — через scripts/ci_report.py, который вызывает инструмент через реальный слой MCP (fastmcp.Client, взаимодействующий с actual объектом сервера), а не через сокращённый путь. Только информационно — он никогда не приводит к сбою сборки, только покрытие.Загрузить оба отчёта как артефакты workflow (
actions/upload-artifact) — доступны для скачивания со страницы запуска workflow в течение 90 дней по умолчанию.Написать сводку job (
$GITHUB_STEP_SUMMARY, отображается как Markdown непосредственно на странице запуска) и опубликовать её как комментарий к PR (actions/github-script, используя встроенныйGITHUB_TOKENзапуска — не требуется дополнительных секретов). Сводка шага — это запасной вариант, который всегда работает, в том числе для PR из форков, которые получают токен только для чтения, не позволяющий публиковать комментарии (ограничение безопасности GitHub, а не ошибка в этом workflow) — шаг комментария обёрнут вcontinue-on-error: true, чтобы это ограничение корректно обрабатывалось, а не приводило к сбою всего job.
Чтобы реально увидеть это в действии, проект должен находиться в реальном репозитории GitHub с отправленными в него коммитами — ничего в этом локальном процессе сборки ещё не создало его. Как только это появится: откройте PR, и вкладка Actions (и сам PR, после того как комментарий появится) покажет его работу в реальном времени.
Статус сборки
Этот проект строится этап за этапом, каждый из которых проверяется на работоспособность перед переходом к следующему.
Этап 1 — скелет проекта, docker-compose Postgres,
.env.example, этот READMEЭтап 2 — уровень базы данных (модели SQLAlchemy + Alembic)
Этап 3 — скелет сервера FastMCP (6 зарегистрированных инструментов, тела-заглушки)
Этап 4 —
analyze_coverageЭтап 5 —
record_test_run+get_test_historyЭтап 6 —
detect_flaky_testsЭтап 7 — Стратегия холодного старта ML, извлечение признаков,
train_risk_modelЭтап 8 —
predict_pr_riskЭтап 9 — GitHub Actions CI (собрано + проверено локально; живой запуск PR ожидает реального репозитория GitHub)
Этап 10 — финальная полировка
Запуск собственных тестов этого проекта
uv sync --extra dev # installs pytest-asyncio + ruff on top of the base deps
uv run pytest -v
uv run pytest --cov --cov-report=term-missing # with coverage
uv run ruff check . # lintТесты уровня БД используют реальную, одноразовую базу данных Postgres (test_intelligence_test, создаваемую и уничтожаемую автоматически) и запускают против неё фактические миграции Alembic, а не имитируют базу данных или используют create_all() — тот же подход, который CI использует через свой сервисный контейнер Postgres (Этап 9). Подробности см. в tests/conftest.py.
Модель данных
Шесть таблиц, управляемых миграциями Alembic:
repositories — отслеживаемый репозиторий (имя + локальный путь или удалённый URL)
test_runs — одна строка на вызов
pytest(репозиторий, SHA коммита, ветка, временная метка, длительность, счётчики пройдено/провалено/пропущено)test_results — одна строка на ID узла теста в рамках прогона (результат, длительность, сообщение об ошибке)
file_changes — статистика diff по файлам для прогона (добавлено/удалено строк, провалились ли тесты после изменения)
flaky_reports — сводка нестабильности по узлу теста (количество наблюдённых прогонов, количество несоответствий, временная метка обнаружения)
risk_predictions — оценки риска ML по файлам для коммита, плюс фактический результат после его получения (для офлайн-оценки модели)
Лицензия
MIT
Available Tools
6 toolsanalyze_coverageA
Run the test suite with coverage and report real, per-file coverage numbers.
Executes pytest with pytest-cov against the target repository, then parses the machine-readable coverage.json report it produces (never the human-readable terminal summary) to compute overall coverage percentage, per-file coverage, and uncovered line counts.
| Name | Required | Description | Default |
|---|---|---|---|
| repo_path | Yes | Absolute path to the Python repository to analyse. Must resolve to a location under one of this server's allowed repo roots. | |
| test_path | No | Optional path, relative to repo_path, restricting which tests to run (e.g. "tests/unit"). Defaults to the whole test suite. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It discloses that the tool executes pytest with pytest-cov, parses coverage.json (never the human-readable summary), and computes overall coverage, per-file coverage, and uncovered line counts. This is comprehensive for a coverage analysis tool, though it could mention any destructive effects or file modifications.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise at two sentences: the first line states the core purpose, and the second line details the execution and parsing behavior. Every sentence adds value without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema exists (so return values are documented separately), the description adequately covers the tool's behavior, parameters, and execution context. It could mention prerequisites like pytest-cov installation, but for a tool with good schema coverage and output schema, this is mostly sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% coverage, so the baseline is 3. The description adds value by explaining that test_path restricts which tests to run and defaults to the whole suite, and repo_path must be under allowed roots. However, it does not elaborate on the parameters further beyond what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states that this tool runs the test suite with coverage and reports real, per-file coverage numbers. It specifies the resource (test suite) and verb (analyze coverage), and the detail about parsing machine-readable coverage.json distinguishes it from any generic coverage tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use this tool (for running pytest with pytest-cov and getting machine-readable per-file coverage), but it does not explicitly say when not to use it or mention alternatives like detect_flaky_tests or record_test_run. It implies usage for coverage analysis among siblings focused on flaky detection, risk prediction, and history.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
detect_flaky_testsA
Run the test suite multiple times and flag tests with inconsistent outcomes.
Runs the full suite runs times, comparing each test's outcome (by its stable
pytest node ID) across runs. A test that passes in some runs and fails in
others is reported as flaky, along with its flakiness rate. Test order and
random seeds are controlled so genuine flakiness isn't confused with
order-dependent or environment-dependent failures.
| Name | Required | Description | Default |
|---|---|---|---|
| runs | No | How many times to run the suite. Higher values give more confidence but take proportionally longer. Defaults to 5. | |
| repo_path | Yes | Absolute path to the Python repository to analyse. Must resolve to a location under one of this server's allowed repo roots. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations were provided, so the description carries the full burden. It discloses that the tool runs the full suite multiple times (potentially expensive), uses stable pytest node IDs, controls test order and seeds, and reports flakiness rates. However, it does not mention if the tool modifies any files or has any side effects beyond reporting.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and structured effectively. The first sentence immediately states the core purpose, followed by a short paragraph explaining the mechanism and guarantees. Every sentence adds essential information without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that the tool has only 2 parameters with 100% schema coverage, an output schema exists, and no annotations are provided, the description sufficiently covers what the tool does, how it works, and what guarantees it provides (controlled order/seeds). There are no obvious gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value by explaining that runs controls the number of executions and that higher values give more confidence but take proportionally longer. It also clarifies that repo_path must be a Python repository and resolves under allowed roots. The first sentence of the description contextualizes both parameters together.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose using a specific verb-resource pair: 'Run the test suite multiple times and flag tests with inconsistent outcomes.' It immediately distinguishes itself from siblings by focusing on flakiness detection rather than coverage analysis, risk prediction, or history retrieval.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implicitly suggests when to use this tool (to identify flaky tests) vs alternatives (e.g., analyze_coverage for coverage, get_test_history for historical results). It explains that test order and seeds are controlled to avoid false positives, but does not explicitly state when NOT to use it or provide direct comparisons to siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_test_historyA
Query previously recorded test run history from the database.
Reads rows written by record_test_run — this tool never triggers a new test run itself, it only reports on what's already been recorded.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of history entries to return, most recent first. Defaults to 50. | |
| file_path | No | Optional file path to filter history down to test runs/results touching that specific file. Omit to get history across the whole repo. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description correctly identifies the tool as read-only ('never triggers a new test run', 'reports on what's already been recorded'). This adequately covers the main behavioral trait. However, it could add more details like handling of empty results or performance implications for large histories.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the main purpose, and no wasted words. The description efficiently conveys the tool's role and its safety characteristic.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple read-only query tool with an output schema, the description covers purpose, read-only nature, and relationship to record_test_run. It doesn't mention ordering (most recent first) but that is in the parameter description. Minor gap in not stating that history is returned sorted, but overall complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% with both parameters fully described. The description adds no additional parameter semantics beyond the schema, so baseline 3 is appropriate. The context about reading history is helpful but does not directly enhance parameter understanding.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description clearly states it queries previously recorded test run history, with the verb 'query' and resource 'test run history'. It distinguishes from siblings by noting it reads rows written by record_test_run and never triggers a new test run, differentiating it from record_test_run and other tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly states this tool is for reading existing history and never triggers a new test run, guiding the agent to use it when querying past results. It indirectly points to record_test_run as the alternative for writing, providing clear usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
predict_pr_riskA
Diff the current branch against a base ref and rank changed files by predicted risk.
Computes the file-level diff between HEAD and base_ref, extracts the same features the risk model was trained on for each changed file (churn, historical failure count, current coverage, etc.), and runs them through the trained GradientBoostingClassifier to produce a ranked risk score per file.
| Name | Required | Description | Default |
|---|---|---|---|
| base_ref | No | The git ref to diff the current branch against, e.g. "main" or a commit SHA. Defaults to "main". | main |
| repo_path | Yes | Absolute path to the Python repository to analyse. Must resolve to a location under one of this server's allowed repo roots. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the behavioral burden. It explains the internal steps (diffing, feature extraction, model inference) and names the model type (GradientBoostingClassifier), which aids transparency. However, it does not disclose side effects (e.g., does it write anything) or failure modes for missing refs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences: the first is a concise summary, the second provides helpful internal detail. Each sentence adds value, though the second could be slightly tighter.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's moderate complexity (2 params, 100% schema coverage, and an output schema exists), the description is largely sufficient. It explains how the tool works internally, which compensates for the lack of annotations, leaving no major gaps for typical use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already documents both parameters. The description calls out 'base_ref' implicitly by stating it diffs HEAD against base_ref, but does not add new semantics beyond the schema's default and description for base_ref.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool diffs the current branch against a base ref and ranks changed files by predicted risk. It uses specific verbs ('diff', 'rank') and resources ('changed files', 'predicted risk'), distinguishing it from siblings like 'analyze_coverage' or 'train_risk_model'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is used for assessing risk of changed files in a branch, which differs from siblings like 'detect_flaky_tests' or 'analyze_coverage'. It does not explicitly mention when not to use it or provide alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
record_test_runA
Execute the test suite once and persist the results to the database.
This is the explicit, distinct step that builds up the training data other tools (get_test_history, train_risk_model) rely on — it never runs implicitly as a side effect of another tool, so it's always clear when history is being written versus just read or analysed.
| Name | Required | Description | Default |
|---|---|---|---|
| repo_path | Yes | Absolute path to the Python repository to run. Must resolve to a location under one of this server's allowed repo roots. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, description carries full burden. It discloses that the tool executes a test suite (potentially side-effecting) and persists results, and it emphasizes the explicit, distinct nature preventing implicit side effects. It does not mention failure behavior or data overwrite semantics, but it provides security context (repo_path must resolve under allowed roots).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, first is direct action, second adds valuable context on when to use. No redundant filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool executes arbitrary Python tests and persists data, description covers the core role and relation to siblings. Output schema exists, so return values are documented elsewhere. Missing some edge-case behavior but adequate for an AI agent to select and invoke.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers the only parameter fully with a description including absolute path and allowed root constraint. The tool description does not add further meaning, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description starts with a specific action: 'Execute the test suite once and persist the results to the database.' It clearly distinguishes from siblings by explaining that this is the explicit write step while get_test_history and train_risk_model consume the data, and it never runs implicitly, so it's unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explains that this tool is the explicit step for writing history versus reading or analyzing, referencing sibling tools get_test_history and train_risk_model. It could be more explicit about when NOT to use it (e.g., for read-only queries, use get_test_history), but the contrast between 'written versus just read or analysed' provides clear context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
train_risk_modelA
Retrain the PR-risk classifier on everything currently in the database.
Trains a GradientBoostingClassifier using file_changes/test_results history as labelled examples (did a test tied to this file fail within N runs after the file changed?), then reports accuracy, precision, recall, and F1 on a held-out split. When the training set is small, the returned metrics come with an explicit caveat that they aren't yet statistically meaningful — this tool never reports metrics as trustworthy without that context.
Returns: Training set size and accuracy/precision/recall/F1, with an honest caveat attached when the training set is too small for the metrics to mean much.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully explains the tool's behavior: it trains a GradientBoostingClassifier, uses training history, reports metrics, and includes a caveat for small datasets. The disclosure about statistical meaningfulness is valuable. However, it doesn't mention how long training might take, whether it requires a running database, or if it's a destructive operation (overwrites the current model?), which would justify a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three well-structured paragraphs: first a one-liner summary, then technical detail, then returns section. Every sentence adds value without redundancy. It is concise (every sentence earns its place) and front-loaded with the core purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema is present, so return values are documented elsewhere. The description covers the algorithm, training data source, metric reporting with caveats, and honesty about statistical significance. This is complete for a training-tool description given the schema availability and zero parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero parameters, and schema description coverage is 100% (schema is empty). The description doesn't need to explain parameters, but it adds value by detailing the training process and metrics returned. Baseline is 4 due to no parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('Retrain the PR-risk classifier on everything currently in the database') and specifies the algorithm (GradientBoostingClassifier) and output metrics. It distinguishes the tool from siblings like 'predict_pr_risk' (which presumably uses the model, not trains it) and 'analyze_coverage' (which analyzes coverage, not risk).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies the tool is for retraining the model using historical data, with no explicit when-to-use or when-not-to-use guidance. However, given that all sibling tools have distinct purposes (e.g., 'predict_pr_risk' for predictions, 'analyze_coverage' for coverage), the context is clear enough. No alternatives or exclusions are stated, so score 4.
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.
6 tool updates
v0.1.0- First observed
analyze_coverage - First observed
detect_flaky_tests - First observed
get_test_history - First observed
predict_pr_risk - First observed
record_test_run - First observed
train_risk_model
TDQS
Scored across 6 tools
Each tool targets a distinct function: coverage analysis, flaky detection, risk prediction, test execution recording, history querying, and model training. There is no functional overlap, and the descriptions clearly differentiate their purposes.
Tool names consistently use a verb_noun pattern (e.g., analyze_coverage, detect_flaky_tests, train_risk_model). The pattern is clear and predictable, though 'record_test_run' and 'get_test_history' slightly shift the pattern (record vs. get, test_run vs. test_history), but it's still minor and readable.
With 6 tools, the set is well-scoped for a CI/testing intelligence server. Each tool covers a necessary stage (record, query, analyze, detect flakiness, predict risk, train model), and none feel redundant or extraneous.
The tool set covers the core lifecycle: recording test runs (record_test_run), querying history (get_test_history), analyzing coverage (analyze_coverage), detecting flakiness (detect_flaky_tests), predicting risk (predict_pr_risk), and training the risk model (train_risk_model). A minor gap is the lack of a tool to delete or manage stored history, but the essential pipeline is complete.
Maintenance
Related MCP Connectors
An MCP server that gives your AI access to the source code and docs of all public github repos
MCP server for building and testing AI agents with multi-model experimentation and insights.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA production-ready MCP server that provides comprehensive dbt project quality assessment for any GitHub repository, enabling AI agents to analyze dbt models, check metadata coverage, and map data lineage.8MIT
- AlicenseNot gradedqualityCmaintenanceMCP server for test impact analysis and code intelligence. Maps tests to code and git history to determine impacted tests, risk scores, and ownership for AI coding agents.4MIT
- AlicenseNot gradedqualityBmaintenanceThis MCP server enables automated maintenance and code analysis for Python/pytest repositories in isolated Docker environments. It supports read-only investigations, fix-and-verify tasks, and provides full audit trails with SQLite event history and artifact exports.MIT
- AlicenseNot gradedqualityCmaintenanceAnalyzes codebases to produce structured quality health reports with risk-scored modules based on git churn, test coverage, and test-to-source mapping, acting as an MCP server for AI coding agents and a standalone CLI.MIT