Skip to main content

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 the id → 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.5
  • the 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].qty is rejected: got 500. A 5xx is a bug.
  • ✗ missing required field items[0].qty is 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.

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]), json paths ($.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: save from JSON paths, headers (header:Location) or status; 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: true runs every step (generated negative suites use it). Cleanup always runs.

Tools

Tool What it does
list_environments Environments and whether each allows writes (and why), auth profiles, databases, fixtures, flows.
draft_flow User story → scenarios, the spec operations most likely involved, and a flow skeleton.
generate_negative_tests Negative and boundary cases for one operation, with real base values from fixtures.
write_flow Validates and saves a flow under tests/api/.
run_flows Runs flows (all, by path or by tag) with full evidence and reports.
call_api One request with the same guardrails, to explore an API while writing tests.
query_db One read-only query against a test database, with masking.
export_pytest 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 POST/PUT/PATCH/DELETE are refused on environments named like production (prod, production, live) unless they set both allow_writes: true and production_writes_acknowledged: true. Other environments must be local, named like a test environment, or set allow_writes: true.
Writing to a database db steps may only read (SELECT/WITH/SHOW/EXPLAIN; no write verbs anywhere, no FOR UPDATE), on a read-only connection inside a rolled-back transaction. Writes happen only in fixtures and cleanup, on databases with allow_writes: true.
SQL injection Values go in :named parameters; ${...} inside SQL text is refused, as is more than one statement. Table and column names in fixtures are validated.
Leaking secrets Credentials come from ${ENV:NAME}. Authorization headers show as •••, and any secret value is masked in requests, responses, errors and reports, as are database columns listed under mask.
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 staging

It 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-qa

The 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-mcp

Claude 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 run

Scope

This server is for functional REST testing with database checks. Related K11 tools:

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.

Metadata

Release files for k11-api-functional-qa-mcp 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for k11-api-functional-qa-mcp 0.1.0
File Size Uploaded Provenance Actions
k11_api_functional_qa_mcp-0.1.0.tar.gz 32.6 kB With provenance Details for k11_api_functional_qa_mcp-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for k11-api-functional-qa-mcp 0.1.0
File Interpreter ABI Platform Actions
k11_api_functional_qa_mcp-0.1.0-py3-none-any.whl Python 3 none any Details for k11_api_functional_qa_mcp-0.1.0-py3-none-any.whl

Total release size: 71.8 kB

Release files / k11_api_functional_qa_mcp-0.1.0.tar.gz

Download URL k11_api_functional_qa_mcp-0.1.0.tar.gz
Size 32.6 kB
Tags Source
SHA-256 checksum
How to use checksums
374fe4989b44a565c810ab2edc2a7bd98d617f33ee0ce2c16cce173e857a9677
BLAKE2b-256 checksum
How to use checksums
4314de7e16ede5f6a7c16ba3968ec340e8038b132e207006c8f602d8a7c0abbf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.

Transparency log

Release files / k11_api_functional_qa_mcp-0.1.0-py3-none-any.whl

Download URL k11_api_functional_qa_mcp-0.1.0-py3-none-any.whl
Size 39.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
02c5477837820ebca746dc014924f8822fe6f624513d0c10c52c85b5109fc2e9
BLAKE2b-256 checksum
How to use checksums
19943efd0c340bc53d5de328eb74ec9188191a5c1645704722ee971c004963c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.

Transparency log

Security for k11-api-functional-qa-mcp 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for k11_api_functional_qa_mcp-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page