{
  "markdown": "# allure-testops-mcp\n\n<!-- mcp-name: io.github.mshegolev/allure-testops-mcp -->\n\n[![PyPI](https://img.shields.io/pypi/v/allure-testops-mcp.svg?logo=pypi&logoColor=white)](https://pypi.org/project/allure-testops-mcp/)\n[![Python](https://img.shields.io/pypi/pyversions/allure-testops-mcp.svg?logo=python&logoColor=white)](https://pypi.org/project/allure-testops-mcp/)\n[![License: MIT](https://img.shields.io/pypi/l/allure-testops-mcp.svg)](LICENSE)\n[![Tests](https://github.com/mshegolev/allure-testops-mcp/actions/workflows/test.yml/badge.svg)](https://github.com/mshegolev/allure-testops-mcp/actions/workflows/test.yml)\n\nAn [MCP](https://modelcontextprotocol.io) server for [Allure TestOps](https://qameta.io/). It lets an\nLLM agent (Claude Code, Cursor, OpenCode, …) explore and manage projects, launches, test cases, test\nresults and reference data through the Allure REST API.\n\n- **Stack:** Python 3.10+, [FastMCP](https://github.com/modelcontextprotocol/python-sdk), **stdio** transport.\n- **Compatibility:** any Allure TestOps instance — SaaS `qameta.io` or self-hosted / on-prem (API at `/api/rs`).\n- **Instance-wide — all projects at once:** one connection serves every project on the instance. The server isn't pinned to a single project — discover them with `allure_list_projects`, then pass any `project_id`. No reconfiguration to switch or compare projects.\n- **Corporate-friendly:** API-token auth, optional SSL-verify toggle, deliberate proxy bypass.\n- **Safe by default:** 13 read-only tools; the 7 write tools are off unless you opt in.\n\n## Quick start\n\n```bash\nclaude mcp add allure -s user \\\n  --env ALLURE_URL=https://allure.example.com \\\n  --env ALLURE_TOKEN=your-api-token \\\n  -- uvx --from allure-testops-mcp allure-testops-mcp\n```\n\nThen ask your agent: *\"List all Allure projects\"* or *\"Show failed tests in the last launch for project 175\"*.\nGet an API token in Allure TestOps under **Profile → API tokens**. See [Configuration](#configuration) for\nother clients and [Environment variables](#environment-variables) for all options.\n\n## Tools at a glance\n\n20 tools — 13 read-only (always on) and 7 write tools (opt-in via `ALLURE_ENABLE_WRITE=true`). Every tool\ncarries MCP annotations and returns both a typed `structuredContent` payload and a markdown summary.\n\n| Tool | Kind | Purpose |\n|------|------|---------|\n| `allure_list_projects` | read | All projects (id, name, abbreviation) |\n| `allure_get_project_statistics` | read | TC count, automation rate, last-launch summary |\n| `allure_list_launches` | read | Recent launches with pass/fail stats |\n| `allure_get_test_results` | read | Test results in a launch (filter by status) |\n| `allure_search_failed_tests` | read | FAILED/BROKEN tests in the last or a given launch |\n| `allure_list_test_cases` | read | Test cases (automated/manual + owner filters) |\n| `allure_get_test_case` | read | One test case's full detail + scenario steps |\n| `allure_get_test_case_custom_fields` | read | A test case's custom-field values |\n| `allure_list_statuses` | read | A project's statuses (id, name, color) |\n| `allure_list_layers` | read | A project's test layers (id, name) |\n| `allure_list_custom_fields` | read | A project's custom-field schema |\n| `allure_list_categories` | read | Defect categories (named/coloured buckets) |\n| `allure_list_category_matchers` | read | Regex automation rules (message/trace → category) |\n| `allure_create_test_case` | write&nbsp;⚑ | Create a test case |\n| `allure_update_test_case` | write&nbsp;⚑ | Partial update of a test case |\n| `allure_delete_test_case` | write&nbsp;⚑ | Permanent delete (destructive — needs `confirm=true`) |\n| `allure_create_category` | write&nbsp;⚑ | Create a defect category |\n| `allure_delete_category` | write&nbsp;⚑ | Permanent delete (destructive — needs `confirm=true`) |\n| `allure_create_category_matcher` | write&nbsp;⚑ | Create + attach a regex automation rule |\n| `allure_delete_category_matcher` | write&nbsp;⚑ | Permanent delete (destructive — needs `confirm=true`) |\n\n⚑ Registered only when `ALLURE_ENABLE_WRITE=true`. Without the flag they are never imported, so the agent\nnever sees them — see [Security considerations](#security-considerations).\n\n### Write tools — status & layer by name or id\n\n`allure_create_test_case` / `allure_update_test_case` accept status and layer as either a **name**\n(`status` / `layer`) or a numeric **id** (`status_id` / `layer_id`). Names are auto-resolved to ids against\nthe project's status/layer lists (`GET /api/rs/status`, `GET /api/rs/testlayer`) — an unknown name returns an\nactionable error listing the valid options. Update is partial (only the fields you pass change), and\n`allure_delete_test_case` is irreversible: it carries `destructiveHint: True` (compliant clients prompt) and\nadditionally requires an explicit `confirm=true` argument.\n\n## Design highlights\n\n- **Full tool annotations** — read tools are `readOnlyHint: True` / `openWorldHint: True` so clients don't\n  prompt; `allure_delete_test_case` is `destructiveHint: True`.\n- **Structured output on every tool** — each tool declares a `TypedDict` return type, so FastMCP\n  auto-generates an `outputSchema` and every result carries both `structuredContent` and a markdown block.\n- **Actionable errors** — auth / 400 / 403 / 404 / 409 / 429 / 5xx / missing-env errors are converted to\n  specific, next-step messages (e.g. *\"Authentication failed — verify ALLURE_TOKEN has API scope\"*).\n- **Pydantic input validation** — every argument has typed constraints (ranges, lengths, literals), exposed\n  as JSON Schema; usernames are alphabet-restricted to prevent RQL injection.\n- **Pagination** — list tools return a `pagination` block with `page`, `total`, `has_more`, `next_page`.\n- **Progress reporting** — multi-call tools emit `ctx.report_progress` + `ctx.info` events.\n- **Version-agnostic update verb** — `allure_update_test_case` issues `PATCH` and falls back to `PUT` on\n  HTTP 405, so it works across Allure deployments that expose only one verb.\n- **Single source of truth for version** — `__version__` derives from installed package metadata, and a test\n  asserts `pyproject.toml` matches both `server.json` version fields, so the published version can't drift.\n\n## Installation\n\nRequires Python 3.10+. No manual install needed if you use `uvx` (recommended) — your MCP client runs it.\n\n```bash\n# run on demand via uvx (recommended)\nuvx --from allure-testops-mcp allure-testops-mcp\n\n# or install with pipx\npipx install allure-testops-mcp\n```\n\n## Configuration\n\n**Claude Code** — one command:\n\n```bash\nclaude mcp add allure -s user \\\n  --env ALLURE_URL=https://allure.example.com \\\n  --env ALLURE_TOKEN=your-api-token \\\n  --env ALLURE_SSL_VERIFY=true \\\n  -- uvx --from allure-testops-mcp allure-testops-mcp\n```\n\n**Any MCP client** — add to `~/.claude.json`, a project `.mcp.json`, Cursor's `mcp.json`, etc.:\n\n```json\n{\n  \"mcpServers\": {\n    \"allure\": {\n      \"type\": \"stdio\",\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"allure-testops-mcp\", \"allure-testops-mcp\"],\n      \"env\": {\n        \"ALLURE_URL\": \"https://allure.example.com\",\n        \"ALLURE_TOKEN\": \"${ALLURE_TOKEN}\",\n        \"ALLURE_SSL_VERIFY\": \"true\"\n      }\n    }\n  }\n}\n```\n\nSee [`.env.example`](./.env.example) for a template. Verify the connection:\n\n```bash\nclaude mcp list\n# allure: uvx --from allure-testops-mcp allure-testops-mcp - ✓ Connected\n```\n\n## Environment variables\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `ALLURE_URL` | yes | — | Allure TestOps URL (e.g. `https://allure.example.com`) |\n| `ALLURE_TOKEN` | yes | — | API token (Allure → Profile → API tokens) |\n| `ALLURE_SSL_VERIFY` | no | `true` | `true`/`false`. Set `false` for self-signed corp certs |\n| `ALLURE_ENABLE_WRITE` | no | `false` | `true` registers the 7 write tools; default is a read-only server |\n\n`ALLURE_TEST_PROJECT_ID` (plus optional `ALLURE_TEST_STATUS` / `ALLURE_TEST_LAYER`) are used only by the\nopt-in live integration tests — see [Development](#development).\n\n## Updating\n\nThe server is a stdio process your client respawns each session, so the running version is decided by the\n`uvx` invocation. `uvx` caches the resolved environment under `~/.cache/uv`, so an older version sticks until\nyou refresh:\n\n```bash\nuvx --refresh --from allure-testops-mcp allure-testops-mcp   # force latest on next run\nuv cache clean allure-testops-mcp                            # or drop the cached env\n```\n\nThen reconnect the server (`/mcp` → reconnect, or restart the session). To control the version from config,\nedit `args` — pin for stability, or always-latest for currency:\n\n```jsonc\n// Pin a version (deterministic; bump consciously)\n\"args\": [\"--from\", \"allure-testops-mcp==0.8.0\", \"allure-testops-mcp\"]\n\n// Always latest on every start (adds a PyPI lookup per launch)\n\"args\": [\"--refresh\", \"--from\", \"allure-testops-mcp\", \"allure-testops-mcp\"]\n```\n\n## Example prompts\n\nRead-only:\n\n- \"List all Allure projects\"\n- \"Show the last 10 launches for project 63\"\n- \"Failed tests in the last launch for project 175\"\n- \"What's the automation rate for project 842?\"\n- \"Compare the automation rate of project 63 and project 842\" — works across projects in one session\n- \"Show me the steps of test case 641012\"\n- \"Which custom fields does project 1664 have?\"\n\nWith `ALLURE_ENABLE_WRITE=true`, drive test-case CRUD in natural language:\n\n- \"Create a Draft manual TC named 'Login flow' in project 63\"\n- \"Add an automated smoke TC in project 63 tagged `smoke`, layer `E2E`\"\n- \"Rename test case 555 to 'Login (rewritten)' and set its status to Active\"\n- \"Delete test case 555\" — the agent passes `confirm=true`, and a compliant client prompts you first\n\n## Security considerations\n\n- **API token is read from `ALLURE_TOKEN` only** — never passed on the command line, never written to logs.\n- **Secrets are never echoed back** in tool responses (no header dumps, no auth reflection).\n- **Self-signed SSL is opt-in** via `ALLURE_SSL_VERIFY=false` (default `true`). Disabling verification on a\n  public network is a risk; use only for trusted corporate instances.\n- **Proxy discovery is disabled** (`session.trust_env = False`) — the server ignores `HTTP_PROXY` /\n  `HTTPS_PROXY` so it can't be silently routed through an unintended proxy.\n- **Writes are opt-in and least-privilege** — without `ALLURE_ENABLE_WRITE=true` the server registers only the\n  13 read-only tools and cannot create, modify, or delete anything, even with a write-scoped token. When\n  enabled, the destructive tools (`allure_delete_test_case` / `allure_delete_category` /\n  `allure_delete_category_matcher`) carry `destructiveHint: True` and require `confirm=true`.\n- **Input validation via Pydantic** — every argument is typed and bounded; usernames are alphabet-restricted\n  to prevent RQL injection through the search endpoint.\n- **A token is never more privileged than its account** — Allure `Api-Token` auth inherits the issuing user's\n  role, so a read-only (guest) account yields a read-only server regardless of the `ALLURE_ENABLE_WRITE` flag.\n\n## Rate limits\n\nAllure TestOps enforces per-instance rate limits (typically ~60 requests/minute per token). On HTTP 429 the\nserver returns an actionable error suggesting you wait 30–60s, reduce `size`, or paginate with smaller pages.\nTwo tools make multiple API calls internally — `allure_get_project_statistics` (3) and\n`allure_search_failed_tests` (2–3) — and report per-step progress via MCP `Context`.\n\n## Development\n\n```bash\ngit clone https://github.com/mshegolev/allure-testops-mcp.git\ncd allure-testops-mcp\npip install -e '.[dev]'\npytest          # unit suite (all HTTP mocked)\nruff check src tests && ruff format --check src tests\n```\n\nRun the server directly (stdio transport — waits on stdin for MCP messages):\n\n```bash\nALLURE_URL=... ALLURE_TOKEN=... allure-testops-mcp\n```\n\n### Live-instance integration tests\n\nAn opt-in suite runs a real create → update → delete lifecycle against a live Allure project. It is\ndeselected by default and skips itself unless credentials are present, so a normal `pytest` stays green:\n\n```bash\nexport ALLURE_URL=https://allure.example.com\nexport ALLURE_TOKEN=...                 # token from an account with write access\nexport ALLURE_ENABLE_WRITE=true\nexport ALLURE_TEST_PROJECT_ID=63        # a throwaway project you can write to\npytest -m integration tests/integration -v\n```\n\n## Contributing\n\nIssues and PRs welcome. Keep the unit suite green (`pytest`) and the linter clean (`ruff check`,\n`ruff format`); CI runs both on Python 3.10 / 3.11 / 3.12. See [`CHANGELOG.md`](./CHANGELOG.md) for the\nrelease history.\n\n## License\n\nMIT © Mikhail Shchegolev\n",
  "bytes": 12628,
  "sha": "37a106497362d0c4918bc8d5ce9447dce05c2fd464dd63399737be9b3aa67198",
  "repo_slug": "mshegolev/allure-testops-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_mshegolev_allure_testops_mcp_8816919c/readme"
}