# Testomat.io MCP Server Model Context Protocol (MCP) server that enables AI assistants (Claude, Cursor, OpenCode, etc.) to interact with Testomat.io Public API v2. ## Features - **One CLI-style tool per entity** - `tests`, `suites`, `runs`, `testruns`, `plans`, `rungroups`, `steps`, `snippets`, `labels`, `requirements`, `branches`, `tags`, `milestones`, `issues`; each takes a `command` argument (`list`, `get`, `create`, `update`, `delete`, plus scoped `issues_*`/`attachments_*`/`share` commands where applicable) and flat command params - **Project Information** - fetch project configuration, metadata, features, and CI profiles - **Issue Linking** - link/unlink issues to any resource (`issues_*` commands on each entity, plus the global `issues` tool) - **Attachments** - `attachments_*` commands on tests, suites, and testruns; upload sends a local file path as multipart field `files` - **Requirements** - including file uploads from local file paths - **API Compatibility** - automatic handling of payload format differences (flat vs wrapped) - **Automatic API Sessions** - groups MCP changes in Testomat.io history using API sessions - **Run Management** - status transitions via `status_event` parameter - **TQL-Only Search** - `tests`/`runs` with `command: "list"` use `tql` as the single search/filter input - **Built-In TQL Reference** - TQL parameters include the exact field whitelist and examples; `tql_help` provides syntax details on demand - **Tool Surface Profiles** - expose only the tools a session needs via `--tools full|core|read` (default `full`); cuts the per-call schema cost for long agentic sessions ## Quick Start ### Installation ```bash npm install -g @testomatio/mcp@latest ``` Need enterprise analytics tools? Install `@testomatio/mcp-enterprise@latest` instead. Details are in the `Enterprise Analytics` section below. ### Configuration **Required credentials:** - `TESTOMATIO_PROJECT_TOKEN` - Your project API token - `TESTOMATIO_PROJECT_ID` - Your project ID **Run server:** ```bash npx testomatio-mcp --token --project ``` **Or with environment variables:** ```bash export TESTOMATIO_PROJECT_TOKEN= export TESTOMATIO_PROJECT_ID= npx testomatio-mcp ``` A bare hostname is expanded to `https://`. For full control use `--base-url` / `TESTOMATIO_BASE_URL`, which takes precedence over the host option **Optional: tool surface profile** By default the server exposes all tools. For long, token-sensitive sessions you can expose a smaller set with `--tools`: ```bash npx testomatio-mcp --token --project --tools core ``` | Profile | What's exposed | |---------|----------------| | `full` (default) | Everything | | `core` | Core entities with all commands (excludes the steps, snippets, labels, rungroups tools) | | `read` | Core entities restricted to read-only commands (`list`, `get`, `search`, `issues_list`, `attachments_list`) | Values are case-insensitive; an unknown value prevents the server from starting. Set the profile at launch with the flag or the `TESTOMATIO_TOOLS` environment variable — it can't be changed mid-session. The CLI flag takes precedence when both are set. ## Usage with AI Assistants ### Cursor IDE Add this config to `.cursor/mcp.json` in your project, or to `~/.cursor/mcp.json` for global access: ```json { "mcpServers": { "testomatio": { "type": "stdio", "command": "npx", "args": [ "-y", "@testomatio/mcp@latest", "--token", "", "--project", "" ], "env": { "TESTOMATIO_BASE_URL": "https://app.testomat.io" } } } } ``` ### Claude Desktop Add this config to: - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` - Windows: `%APPDATA%\Claude\claude_desktop_config.json` ```json { "mcpServers": { "testomatio": { "command": "npx", "args": [ "-y", "@testomatio/mcp@latest", "--token", "", "--project", "" ], "env": { "TESTOMATIO_BASE_URL": "https://app.testomat.io" } } } } ``` ### OpenCode Add this config to `opencode.json` in your project root, or to `~/.config/opencode/opencode.json` for global access: ```json { "$schema": "https://opencode.ai/config.json", "mcp": { "testomat": { "type": "local", "command": [ "npx", "-y", "@testomatio/mcp@latest", "--token", "", "--project", "" ], "enabled": true, "environment": { "TESTOMATIO_BASE_URL": "https://app.testomat.io" } } } } ``` ## HTTP Transport Besides stdio, the server runs over Streamable HTTP on a Cloudflare Worker hosted by Testomat.io. The project is part of the URL, so every tool signature stays the same: ``` https://mcp.testomat.io/mcp/ ``` Point an MCP client at that URL with a project token: ```json { "mcpServers": { "testomatio": { "url": "https://mcp.testomat.io/mcp/", "headers": { "Authorization": "Bearer " } } } } ``` Web connectors such as claude.ai have nowhere to put a static token and instead run OAuth 2.1 with PKCE and Dynamic Client Registration against the same URL. Tokens starting with `testomat_` or `tstmt_` always bypass OAuth and are passed straight through, so IDE clients and CI keep working. The endpoint is POST only; `GET` returns `405`, because the server never initiates traffic. Testing with `curl` requires both media types in `Accept`: ```bash curl -sS https://mcp.testomat.io/mcp/ \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' ``` Self-hosted installations keep using stdio. ## Quick Examples Every entity tool works like a CLI: pass `command` plus the params that command needs. Each param description in the tool schema lists the commands it applies to. **List tests:** ```json { "name": "tests", "arguments": { "command": "list", "page": 1, "per_page": 50, "tql": "priority == 'high'" } } ``` **Create test:** ```json { "name": "tests", "arguments": { "command": "create", "title": "User login test", "suite_id": "123", "priority": "high" } } ``` **Create run:** ```json { "name": "runs", "arguments": { "command": "create", "title": "Smoke tests", "kind": "automated", "env": "production" } } ``` **Finish run:** ```json { "name": "runs", "arguments": { "command": "update", "run_id": "456", "status_event": "finish" } } ``` **Upload attachment to a test:** ```json { "name": "tests", "arguments": { "command": "attachments_upload", "test_id": "123", "file_path": "/path/to/screenshot.png" } } ``` **Bulk create/update tests from markdown:** ```json { "name": "tests", "arguments": { "command": "bulk_upsert", "markdown": "\n# Login Functionality\n\n# Successful Login\n\n# Failed Login" } } ``` Tests with an `id: @T...` are updated, tests without one are created; suites are resolved by `id: @S...` or title and created when missing. Up to 100 tests per call. See [docs/tools.md](./docs/tools.md) for the full semantics. ## Documentation Complete tool reference: [docs/tools.md](./docs/tools.md) ## Project Structure ```text src/ |- config/ # Config loading, defaults |- core/ # Errors, logger |- api/ # HTTP client, Testomat.io API client |- mcp/ # MCP server, tools, registry | |- definitions/ # Tool definitions by entity | |- configs/ # Registry generation configs | `- registry/ # Tool handlers `- cli/ # CLI bootstrap ``` ## Environment Variables | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `TESTOMATIO_PROJECT_TOKEN` | Yes* | - | Project token (preferred) | | `TESTOMATIO_API_TOKEN` | Yes* | - | Alternative token | | `TESTOMATIO_PROJECT_ID` | Yes | - | Project ID | | `TESTOMATIO_HOST` | No | - | API host, e.g. `beta.testomat.io` | | `TESTOMATIO_BASE_URL` | No | `https://app.testomat.io` | API base URL, wins over `TESTOMATIO_HOST` | | `TESTOMATIO_TOOLS` | No | `full` | Tool profile: `full`, `core`, or `read` | *Either `TESTOMATIO_PROJECT_TOKEN` or `TESTOMATIO_API_TOKEN` ## Corporate TLS Certificates If the MCP server runs behind a corporate proxy or TLS inspection, Node.js may reject Testomat.io HTTPS requests even when the same URL works in a browser. This usually means the company root certificate is trusted by the operating system, but not by Node.js. Use Node.js system CA support: ```bash NODE_OPTIONS=--use-system-ca testomatio-mcp --token --project ``` For MCP clients, pass `NODE_OPTIONS` in the server environment: ```json { "mcpServers": { "testomatio": { "command": "testomatio-mcp", "args": ["--token", "", "--project", ""], "env": { "NODE_OPTIONS": "--use-system-ca" } } } } ``` If your Node.js version does not support `--use-system-ca`, export the corporate root certificate to a PEM file and use Node.js extra CA support: ```bash NODE_EXTRA_CA_CERTS=/path/to/company-root-ca.pem testomatio-mcp --token --project ``` ## Important Notes - **CLI-style Tools** - One tool per entity; all operations are the `command` argument (`tests` + `command: "list"` instead of the old `tests_list`). Unknown commands return an error listing the valid ones - **Run Status** - Use `runs` with `command: "update"` and `status_event` for transitions (finish, launch, rerun, etc.) - **Search/Filter** - No dedicated `/search` endpoints; filtering is done via the `list` command (`tql` for tests and runs, OpenAPI-aligned filters for other entities) - **Slim List Responses** - List commands request compact API responses by default and omit heavy entity fields and null values. Pass `verbose: true` to return full objects, or `fields: ["id", "title", "description"]` to return only selected fields. Both options disable the backend `slim=true` request so heavy fields remain available when requested. - **TQL** - Use `tql` as the single search/filter input for `tests`/`runs` with `command: "list"` - **TQL Syntax** - For user-facing syntax details and more examples, see the official TQL docs: https://docs.testomat.io/advanced/tql/ - **TQL Scope** - TQL parameter descriptions keep the documented field whitelist in-band; call `tql_help` for syntax details and additional examples - **Issue Linking** - Scoped commands available on each entity: `command: "issues_link"` / `"issues_unlink"` - **Attachments** - Scoped commands available on tests, suites, and testruns: `command: "attachments_list" | "attachments_upload" | "attachments_delete"`. Upload sends one local file path as multipart field `files`. - **Enterprise Package** - Analytics tools are intentionally exposed only by `@testomatio/mcp-enterprise`, not by the standard `@testomatio/mcp` package - **API Sessions** - The server automatically starts a Testomat.io session before the first `POST`, `PUT`, or `DELETE` request, sends the returned session hash as `X-Session-Hash` on later mutating requests, and stops the session when the MCP server shuts down. `GET` requests do not start or use sessions. ## Development ```bash npm install npm run start -- --token --project npm test ``` For local MCP development, point Claude Desktop to the checked-out entrypoint: ```json { "mcpServers": { "testomatio-local": { "command": "node", "args": ["/path/to/mcp/index.js", "--token", "", "--project", ""] } } } ``` ## Enterprise Analytics Enterprise analytics is available only in the separate `@testomatio/mcp-enterprise` package. Installation: ```bash npm install -g @testomatio/mcp-enterprise@latest ``` Run: ```bash testomatio-mcp-enterprise --token --project ``` Included tools: - `analytics_tests` - `GET /api/v2/{project_id}/analytics/tests/{kind}` - `analytics_stats` - `GET /api/v2/{project_id}/analytics/stats/{kind}` - `analytics_charts_list` - `GET /api/v2/{project_id}/analytics/charts` - `analytics_charts_get` - `GET /api/v2/{project_id}/analytics/charts/{id}` - `analytics_charts_results` - `GET /api/v2/{project_id}/analytics/charts/{id}/result` Analytics endpoints require the `api_analytics` subscription feature. Use `q` as the TQL filter parameter for analytics tools. Example `analytics_tests` call: ```json { "name": "analytics_tests", "arguments": { "kind": "flaky", "q": "priority == 'high'", "days": 30, "page": 1, "per_page": 20 } } ``` Example `analytics_stats` call: ```json { "name": "analytics_stats", "arguments": { "kind": "success-rate-by-date", "q": "tag IN ['@smoke']", "from": "2026-04-01", "to": "2026-04-30" } } ``` `analytics_charts_get` returns a chart definition with its TQL queries; `analytics_charts_results` returns per-query totals or the matching tests/runs of one query (by its zero-based `number`). Example `analytics_charts_results` call: ```json { "name": "analytics_charts_results", "arguments": { "chart_id": "abc123", "number": 2, "per_page": 50 } } ``` ```