Skip to main content
Glama
shreyasKaturi2004

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 — здесь нет скрейпинга вывода терминала или поддельных чисел.

Зачем MCP

MCP — это протокол (с открытым исходным кодом от Anthropic, ныне широко принятый), который позволяет AI-агенту обнаруживать и вызывать инструменты, предоставляемые отдельным серверным процессом, через стандартный транспорт JSON-RPC (stdio локально или HTTP/SSE удалённо). Вместо того чтобы создавать собственный API и обучать системный промпт агента работе с ним, вы предоставляете типизированные Python-функции как «инструменты»; клиент автоматически обнаруживает их имена, схемы аргументов и строки документации и вызывает их в процессе беседы. Этот проект использует FastMCP, эргономичный Python SDK, построенный поверх официальной спецификации MCP — @mcp.tool() на обычной типизированной функции достаточно, чтобы предоставить её.

Технологический стек

Компонент

Выбор

MCP-сервер

FastMCP

Выполнение тестов

pytest, pytest-cov, coverage.py (парсит coverage.json)

База данных

PostgreSQL, async SQLAlchemy 2.0 (AsyncSession), драйвер asyncpg

Миграции

Alembic (версионные, без create_all())

Машинное обучение

scikit-learn GradientBoostingClassifier

Git-операции

GitPython / subprocess

CI

GitHub Actions

Локальный Postgres

Docker + docker-compose

Управление пакетами

uv

Структура репозитория

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 sync

uv 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 head

Alembic последовательно воспроизводит каждый скрипт миграции из 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-кода были рассмотрены три варианта:

  1. Воспроизвести историю git/CI реального репозитория с открытым исходным кодом. Клонировать реальный проект, пройти по его коммитам, проверить каждый, установить его зависимости, какими они были в тот момент истории, запустить его набор тестов, извлечь реальные признаки и реальные метки. Самые реалистичные данные — но дорого и хрупко в реализации: установка зависимостей ломается на протяжении многих лет истории (устаревшие пакеты, изменение версий Python), полная проверка истории медленна, и это привязывает жёсткую внешнюю зависимость (конкретный репозиторий в конкретный момент времени) к CI этого проекта, который должен будет воспроизводить её при каждом запуске.

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

  3. Генерация синтетических данных (выбрано). Извлечь векторы признаков из правдоподобных распределений и вывести метки на основе намеренно разработанного, предметно-ориентированного порождающего правила — больше изменений + больше прошлых сбоев + меньшее покрытие + большая сложность → более высокая вероятность сбоя, плюс шум — а не подбрасывание монетки. Быстро, полностью воспроизводимо, не требует внешнего репозитория и достаточно для честной проверки всего конвейера (извлечение признаков → обучение → оценка) уже сегодня.

Честный компромисс: модель, обученная исключительно на синтетических данных, выучила форму правдоподобной зависимости риска, а не реальную. Её метрики на отложенных синтетических данных выглядят разумно (~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 этого проекта — это самотестирование проекта «из конца в конец»:

  1. Проверить репозиторий и установить uv + зависимости — те же инструменты, которые разработчик устанавливает локально.

  2. Запустить Postgres как сервисный контейнер — второй контейнер, который GitHub Actions запускает вместе с job, доступный по адресу localhost:5433 из каждого шага, точно так же, как docker-compose up -d локально, но управляемый GitHub вместо Docker Desktop. Job не запускает свои шаги, пока не пройдёт его healthcheck — не требуется ручного цикла ожидания Postgres.

  3. Применить миграции Alembic, затем запустить собственный тестовый набор проекта с --cov-fail-under=$COVERAGE_THRESHOLD — встроенный шлюз pytest-cov; сборка завершается ошибкой, если покрытие падает ниже этого порога (в настоящее время 80%, с запасом под реальными ~93%).

  4. Запустить detect_flaky_tests для собственных тестов проекта — через scripts/ci_report.py, который вызывает инструмент через реальный слой MCP (fastmcp.Client, взаимодействующий с actual объектом сервера), а не через сокращённый путь. Только информационно — он никогда не приводит к сбою сборки, только покрытие.

  5. Загрузить оба отчёта как артефакты workflow (actions/upload-artifact) — доступны для скачивания со страницы запуска workflow в течение 90 дней по умолчанию.

  6. Написать сводку 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 зарегистрированных инструментов, тела-заглушки)

  • Этап 4analyze_coverage

  • Этап 5record_test_run + get_test_history

  • Этап 6detect_flaky_tests

  • Этап 7 — Стратегия холодного старта ML, извлечение признаков, train_risk_model

  • Этап 8predict_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

-
license - not tested
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • An MCP server that gives your AI access to the source code and docs of all public github repos

  • Hosted MCP server for structured code review passes on human- and AI-written code. Free tier.

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

View all MCP Connectors

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/shreyasKaturi2004/test-intelligence-mcp'

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