qa-toolkit-mcp

qa-toolkit-mcp

An MCP server that reads test reports and provides regression analysis tools for comparing runs, identifying regressions, fixes, and persistent failures.

Category
访问服务器

README

qa-toolkit-mcp

An MCP server that reads test reports and turns them into a regression analysis a model can work with. It does not run your tests. It reads the reports your test runs already write, and answers questions like "what regressed between Monday and Friday, and which of these failures are just the same known issues?"

I built it so an agent can do the boring part of a weekly regression review for me: line up the runs, compare them, and tell me what actually broke instead of handing me a raw diff.

What it does

You point it at a folder of run reports (the JSON your CI, your cron job, or your manual runs already produce) and it gives a model three tools, one resource, and one prompt:

  • qa_list_runs - lists the runs in the folder. You can filter by suite or by date, and it pages.
  • qa_get_run - returns one run. By default it only lists the failures, to keep the model's context small. Pass include_passed when you want everything.
  • qa_compare_runs - takes two runs and sorts what changed between them. This is the main one.
  • run://{run_id}/summary.md (resource) - a Markdown summary of a single run the host can load.
  • weekly_regression_review (prompt) - walks the model through a week: list the runs, compare each consecutive pair, and write a short report.

qa_compare_runs does not hand you a raw diff. It sorts every test into the buckets a QA person actually acts on:

  • regression - passed in A, failed in B. The one you care about most.
  • fix - failed in A, passed in B.
  • persistent failure - failed in both. It also tells you whether it is the same error as before or a new one (see fingerprints below).
  • new test / removed test - showed up or disappeared between the two runs.
  • classification change - the QA label changed, even when the pass/fail did not. "Still failing here, but we changed our mind about why."

Here is what the Markdown output looks like:

# Compare `search-25` → `search-26`

Suite: search → search
Started: 2026-05-25T09:00:00 → 2026-05-26T09:00:00

**1 regression(s) · 1 fix(es) · 2 persistent · 0 new · 0 removed · 1 reclassified**

## Regressions (passed → failed)
- `SI-POS-008` — **AssertionError**: Expected success, got 'error' - timeout

## Fixes (failed → passed)
- `SI-POS-005`

## Persistent failures
- `SI-POS-006` (same error) — SQLGrammarException: unexpected token
- `SI-POS-007` (same error) — SQLGrammarException: unexpected token

## Classification changes (QA oracle changed its mind)
- `SI-POS-007`: unclassified → bug real

Every tool can return JSON instead of Markdown, for when the agent needs to read the numbers and not the prose.

What it doesn't do

  • It does not run your tests. It only reads reports. Whatever writes the report (a pytest job, a pipeline, a person) stays separate from this server.
  • It does not compute the fingerprint that groups "the same error". The producer of the report does that (more on why below).
  • No HTTP yet. It speaks stdio only, which is what local MCP clients use.
  • No dashboard and no UI. That is a separate thing that will read the same JSON one day.
  • It is not on PyPI yet. You install it from the repo.

How it works

Two report formats, detected per file. Drop either kind into the runs folder and the server works out which one it is:

  • Native reports have a schema_version field and are checked against schemas/run-report.v1.json.
  • Classification reports are what a QA pipeline tends to write: a list of the failures, each with a human-assigned label. If there is a JUnit XML file sitting next to it with the same name, the server reads that too, so now it also knows about the tests that passed.

is_exhaustive. A native report, or a classification report with its XML sibling, knows every test that ran. A bare classification report only knows the failures. The compare keeps track of this: when one side only lists failures, a test that is missing is treated as passed. So a test that failed in A and is gone in B counts as a fix, not as a removed test.

Fingerprints. Each failure in a report carries a fingerprint: a hash of the test id, the error type, and the normalized message. Two failures with the same fingerprint are treated as the same root cause. That is how qa_compare_runs tells "still the same bug" apart from "now it fails for a different reason".

Flat parameters. The tools take plain top-level arguments (run_a, run_b, and so on), not one nested params object. There is a story behind that, in the next section.

Configuration. Copy .env.example to .env and set QA_TOOLKIT_RUNS_DIR to your reports folder. A real env var wins over .env, which wins over the default of ./runs/. .env is gitignored, so it stays one per machine.

Why it works this way

It reads reports instead of running tests because the thing that runs the tests and the thing that reads them should not be glued together. CI runs the tests on its own schedule and writes a report. This server reads that report whenever an agent asks. Anything that writes the schema can be read, and a dashboard could read the same files later without sharing a line of code with this server.

It categorizes instead of diffing because a raw diff makes you read everything again. A QA engineer does not treat all changes the same: a regression is urgent, a known persistent failure is something you already triaged. So the tool does that sorting up front.

The fingerprint lives in the producer, not here. Error messages change every run (timestamps, ids, line numbers), so comparing raw messages makes the same bug look new every time. A fingerprint stays stable. I put it in the producer because only the producer knows which parts of its own messages are the volatile bits, and that keeps this server framework-agnostic.

The parameters are flat because models are bad at the nested version. I found this with my own test harness: a local model could only call qa_compare_runs about one time in ten, because FastMCP was wrapping every argument under a required params object and the model kept sending the arguments flat. So I flattened the schema to match what models actually send. The whole investigation is written up in ADR 0001.

Running it

Install it:

python -m venv .venv
.venv\Scripts\Activate.ps1
pip install -e ".[dev]"

The server speaks stdio, so you do not start it by hand. Your MCP client launches it as a subprocess. Register it and point it at your reports.

Claude Code:

claude mcp add qa-toolkit -s user -- `
  "<repo>\.venv\Scripts\python.exe" -m qa_toolkit_mcp.server

Claude Desktop, in claude_desktop_config.json:

{
  "mcpServers": {
    "qa-toolkit": {
      "command": "<repo>\\.venv\\Scripts\\python.exe",
      "args": ["-m", "qa_toolkit_mcp.server"]
    }
  }
}

Or poke at it by hand with the MCP Inspector:

npx @modelcontextprotocol/inspector .\.venv\Scripts\python.exe -m qa_toolkit_mcp.server

Run the tests with pytest. The suite is layered the way I test things: the pure functions (compare, storage, the adapter, the formatters) on their own, then the tools through their real entry points, and then a set of metamorphic checks on qa_compare_runs - properties that have to hold whatever the input. For example, comparing a run against itself reports nothing changed, and the regressions going from A to B are exactly the fixes going from B to A.

Project structure

qa_toolkit_mcp/
  server.py                  the MCP server: the tools, the resource, the prompt, the entry point
  models.py                  Pydantic models for the report schema
  storage.py                 reads files, keeps paths safe, detects the format
  adapter_classification.py  turns a classification report (+ JUnit XML) into the canonical model
  compare.py                 the regression analysis, pure functions, no I/O
  formatters.py              models to Markdown or JSON
  config.py                  .env and env-var handling
schemas/
  run-report.v1.json         the report contract, the source of truth
docs/adr/                    decision records (0001 - why the tool parameters are flat)
evaluations/                 eval questions for the server
tests/                       the layered + metamorphic suite

License

MIT, see LICENSE.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选