k11 API Functional QA
OfficialIntegrates with GitHub Actions by writing test reports, setting GitHub Actions outputs, and exiting non-zero when flows fail.
Supports MariaDB test databases via the same optional MySQL/MariaDB extra for read-only checks and fixture/cleanup writes.
Supports MySQL test databases via the optional MySQL extra for read-only checks and fixture/cleanup writes.
Supports PostgreSQL test databases via the optional PostgreSQL extra for read-only checks and fixture/cleanup writes.
Exports flows as plain pytest + httpx code so tests can be run outside the MCP server.
Supports SQLite databases for read-only database checks in test flows, with writes limited to fixtures and cleanup on allowlisted test databases.
Parses Swagger 2.0 specifications to generate API tests and validate response schemas.
k11 API Functional QA — MCP server & Claude Code plugin
Functional API tests that prove the API did the right thing, down to the database row.
Contract checks tell you an API still has the right shape. They don't tell you whether placing an order charges the right amount, whether cancelling really soft-deletes the row, or whether a malformed request crashes the server. This MCP server lets Claude (or any MCP client) write and run those tests with you:
User story → multi-step test flow. Chain calls (
POST /orders→ save theid→GET /orders/{id}→DELETE) with readable YAML and value checks.Database checks in the same flow. After the API call, check the row really changed:
SELECT status, deleted_at FROM orders WHERE id = :orderId. Read-only by default, with masked columns.Negative and boundary tests from the spec, including nested fields and array items: missing required fields, wrong types, values outside enums and min/max/length limits, no credentials, unknown IDs. Every case must get a 4xx, never a 5xx.
Evidence for every step: the request, response, query, rows and each check, in Markdown (PR comment), JSON and JUnit XML for CI dashboards.
Guardrails: data-changing calls are refused on production-like environments, database writes only in fixtures and cleanup on allowlisted test databases, secrets only from environment variables and masked everywhere, and requests never leave the environment's host.
No lock-in: export any flow as plain pytest + httpx code.
Built on the parsers of k11 API Contract QA (OpenAPI 3.x and Swagger 2.0 for test generation and response-schema checks).
See it find real bugs
The demo shop API (demo/shop/) has two planted bugs. Two hand-written flows and one generated negative suite found them, plus one I didn't plant.
1. The order total is wrong only in the API response. order_total.yaml orders two bags at 42.50:
❌ Order total is quantity times price
place an order for two bags: failed. Response: HTTP 201. ✗
$.total 85.0: expected 85.0, got 42.5the database stores 85.00: passed
order history shows 85.00: passed
The database check shows exactly where the bug is: the stored total is right, and only the POST /orders response is wrong.
2. Malformed input crashes the server. generate_negative_tests("POST /orders") produced 18 cases from the spec. 16 behaved correctly, and two returned HTTP 500:
✗ wrong type for
items[0].qtyis rejected: got 500. A 5xx is a bug.✗ missing required field
items[0].qtyis rejected: got 500. A 5xx is a bug.
The second crash wasn't planted. It's the same code path, and the generator found it because it tests nested array items, not just top-level fields.
3. The soft delete works, proven in the database. cancel_order.yaml passes all 7 steps, including checks that the row is kept with status = cancelled and a deleted_at timestamp.
Related MCP server: API Testing Platform
A flow
name: Customer cancels an order and it is soft-deleted
fixtures: [customer] # seeded in the test database, removed afterwards
steps:
- name: place an order
call: POST /orders
body: { customerId: "${fixture.customer.id}", channel: web, items: [{ sku: HAT-2, qty: 1 }] }
expect:
status: 201
json: { "$.status": pending, "$.total": 15.0, "$.items": { length: 1 } }
save: { orderId: "$.id" }
- name: the row is kept and marked deleted
db: SELECT status, deleted_at FROM orders WHERE id = :orderId
params: { orderId: "${orderId}" }
expect: { rows: 1, row: { status: cancelled, deleted_at: { ne: null } } }
cleanup:
- db: DELETE FROM orders WHERE id = :orderId
params: { orderId: "${orderId}" }Checks (
expect):status(201,"2xx",[400, 422]),jsonpaths ($.items[0].qty,$.items[*].sku,$.items.length),headers,body_contains,max_ms, and the response schema from the spec (on by default). Values compare as literals or with operators:eq ne gt gte lt lte approx contains matches length type in exists empty.Variables:
savefrom JSON paths, headers (header:Location) orstatus; use them as${orderId}. Generators:${uuid},${random_email},${random_int},${now},${today}. Secrets:${ENV:NAME}.Failures: a step that only fails value checks lets the flow continue, to gather more evidence. A wrong status, a 5xx, a blocked write or a missing saved value stops it.
continue_on_failure: trueruns every step (generated negative suites use it). Cleanup always runs.
Tools
Tool | What it does |
| Environments and whether each allows writes (and why), auth profiles, databases, fixtures, flows. |
| User story → scenarios, the spec operations most likely involved, and a flow skeleton. |
| Negative and boundary cases for one operation, with real base values from fixtures. |
| Validates and saves a flow under |
| Runs flows (all, by path or by tag) with full evidence and reports. |
| One request with the same guardrails, to explore an API while writing tests. |
| One read-only query against a test database, with masking. |
| A flow as plain pytest + httpx code. |
Configure: .qa-mcp/api-test.yaml
spec: openapi.yaml
default_environment: local
environments:
local: { base_url: http://localhost:8080 }
staging: { base_url: https://staging.example.com, allow_writes: true }
prod: { base_url: https://api.example.com } # read-only: writes refused
auth:
default: { type: bearer, token: "${ENV:API_TOKEN}" }
# basic: {username, password} · api_key: {header, value} · oauth2: {token_url, client_id, client_secret, scope}
databases:
main: { url: "${ENV:TEST_DB_URL}", allow_writes: true, mask: [email, card_number] }
fixtures:
customer:
db: main
insert: { table: customers, values: { id: "${uuid}", email: "${random_email}", name: Test Customer } }
delete: { table: customers, where: { id: "${id}" } }Databases: SQLite (built in), PostgreSQL (pip install "k11-api-functional-qa-mcp[postgres]"), MySQL/MariaDB ([mysql]) and SQL Server ([mssql]).
Guardrails
Risk | What happens |
Changing data in production |
|
Writing to a database |
|
SQL injection | Values go in |
Leaking secrets | Credentials come from |
Sending credentials elsewhere | Requests must stay on the environment's host; a flow can't send your token to another URL. |
Runaway queries | At most 1,000 rows are read per query, with timeouts on every connection and request. |
In CI
k11-api-functional-qa-mcp run --env stagingIt writes api-test-report.md (PR comment), api-test-report.json (evidence) and api-test-junit.xml, sets GitHub Actions outputs and exits non-zero when a flow fails (--fail-on any|failed|never). See docs/api-tests.yml.
Install
Requires Python 3.10+.
/plugin marketplace add K11-Software-Solutions/k11techlab-api-functional-qa-mcp
/plugin install api-functional-qa@k11-api-functional-qaThe plugin adds /api-story-test and /api-negative-tests. To add only the MCP server:
claude mcp add api-functional-qa -- uvx k11-api-functional-qa-mcpClaude Desktop and other clients: run uvx k11-api-functional-qa-mcp with QA_MCP_ROOT set to your project.
Try the demo
python demo/shop/server.py --port 8080
SHOP_URL=http://127.0.0.1:8080 SHOP_TOKEN=test-token QA_MCP_ROOT=demo/shop k11-api-functional-qa-mcp runScope
This server is for functional REST testing with database checks. Related K11 tools:
Contract and breaking-change checks (REST, GraphQL, gRPC, AsyncAPI, SOAP): k11 API Contract QA
UI tests: Playwright QA and Selenium QA
LLM red-teaming: LLM Assure
Not covered: load testing (use k6 or Gatling) and deep fuzzing (use Schemathesis).
Development
python -m venv .venv && .venv/Scripts/pip install -e ".[dev]" # bin/pip on macOS/Linux
pytest # values, guardrails, flows against the demo shop, negative tests, export, CLI, MCP (25)Roadmap
GraphQL, gRPC and SOAP calls as flow steps
Java export (REST Assured)
MongoDB checks
Schema-driven test data (realistic values per field format)
Built by K11 Software Solutions. Part of the K11 Tech Lab agentic QA series. MIT licensed.
Available Tools
8 toolscall_apiCall APIA
Make one request (e.g. call='GET /orders/{id}', path_params={'id': '1001'}) to explore an API while writing tests. Same guardrails as flows: writes only where allowed, secrets masked, schema checked when a spec is set.
Inputs: call (required), auth, body, query, +3 more.
draft_flowDraft flow from a user storyB
Turn a user story or acceptance criteria into scenarios, the spec operations most likely involved, and a flow skeleton to complete (chain IDs with save/${...}, add value and db checks).
Inputs: story (required), spec.
export_pytestExport flow to pytestA
Turn a flow into a plain pytest + httpx test (database checks included), so the suite can live without this tool. Returns the code; with write=true saves it under tests/ (never overwriting).
Inputs: path (required), write.
generate_negative_testsGenerate negative testsA
Negative and boundary tests for one operation (e.g. 'POST /orders'), derived from the spec, including nested fields and array items: missing required fields, wrong types, values outside enums and min/max/length/item limits, missing parameters, no credentials, unknown IDs. Every case must get a 4xx, never a 5xx; boundary cases that must be accepted clean up after themselves. base_values supplies real values for the valid parts (e.g. {"customerId": "${fixture.customer.id}"}) with fixtures=["customer"]. Save the result with write_flow.
Inputs: endpoint (required), spec, fixtures, base_values.
list_environmentsList environmentsB
Environments (base URL, whether writes are allowed and why), auth profiles, databases and fixtures from .qa-mcp/api-test.yaml, plus the flows in tests/api/.
Inputs: no named parameters.
query_dbQuery test databaseA
Run one read-only query (SELECT/WITH/SHOW/EXPLAIN) against a configured test database, with :named params. Masked columns are shown as •••. Use it to find test data and write db checks.
Inputs: sql (required), params, database.
run_flowsRun flowsB
Run flows (default: everything in tests/api/) against an environment. Returns per-flow results with the failing checks and the request/response or query evidence; writes Markdown, JSON and JUnit reports.
Inputs: tags, paths, environment, write_report.
write_flowWrite flowA
Save a YAML flow under tests/api/ after validating it. Won't overwrite unless asked.
Inputs: path (required), content (required), overwrite.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 8 tools
Most tools occupy clearly distinct steps of the QA workflow: discovery (list_environments), exploration (call_api), authoring (draft_flow, generate_negative_tests, write_flow), execution (run_flows), data (query_db) and export (export_pytest). The only soft overlap is between draft_flow (produces a skeleton) and write_flow (persists it), which an agent could momentarily conflate since both concern flow files. Descriptions largely resolve this by explaining the pipeline order.
Every tool follows the same snake_case verb_noun pattern (list_environments, draft_flow, write_flow, run_flows, call_api, query_db, export_pytest), with generate_negative_tests as the only longer variant and still the same convention. Verb choice maps predictably to action type (list/draft/generate/write/run/call/query/export). No camelCase or stylistic mixing.
Eight tools is well within the ideal 3-15 range and each one maps to a discrete step in the API-QA lifecycle, so nothing feels padded. No redundant aliases or duplicate execution paths exist.
The surface covers the full authoring-to-execution loop plus DB access and a pytest escape hatch, which is strong lifecycle coverage. The gap is around existing flows: there is no tool to read, edit, or delete a saved flow (write_flow refuses to overwrite), so revising an existing test requires external file access. Minor but real dead end for iterating on a failing flow.
Maintenance
Related MCP Connectors
MEOK MCP Test MCP — golden-file + schema-drift + tool-failure tests for any MCP server. Drop-in
3rd Generation Testing (3TG) — generate deterministic test suites from Markdown spec tables via MCP.
End-to-end API testing — generate and run tests from OpenAPI, curl, Postman, or real user traffic.
JSON Schema validator for API payloads via MCP: bounded checks and structured pass/fail errors.
Related MCP Servers
- AlicenseAqualityDmaintenanceMCP server for API test case generation from Swagger/OpenAPI specs. Parses Swagger 2.0 and OpenAPI 3.x, generates test cases across 8 categories (positive, negative, boundary, auth, security, idempotency, pagination, business logic), and exports to Postman, TestRail, Allure, k6, pytest, Gherkin, and CSV. Supports internal corporate APIs with auth headers. Auto-saves export files to your working di1010 npm2MIT
- FlicenseNot gradedqualityDmaintenanceAn LLM-driven API testing platform exposed over MCP, enabling declarative test definition and DAG-based execution.-
- FlicenseNot gradedqualityCmaintenanceEnables MCP-compatible clients to discover API endpoints from OpenAPI/Swagger specs, send HTTP requests, validate responses, and analyze status codes for automated API testing.1-
- AlicenseNot gradedqualityCmaintenanceEnables running YAML-defined test suites against MCP servers and agent workflows, with live or simulated execution, assertions, and regression reporting.293 npmMIT