QA How-To
Connecting Claude to your test suite with MCP (2026)
Learn connecting Claude to your test suite with MCP through safe tools, scoped permissions, runnable FastMCP code, CI patterns, and practical QA workflows.
22 min read | 2,799 words
TL;DR
Build a local stdio MCP server that exposes allowlisted test discovery, execution, and report-reading tools. Register it at project scope in Claude Code, keep permissions narrow, and require normal CI to verify any AI-proposed change.
Key Takeaways
- Expose narrow test capabilities as MCP tools instead of giving an AI agent an unrestricted shell.
- Keep discovery, execution, and artifact reading as separate tools with explicit schemas and limits.
- Use project-scoped Claude Code configuration so the team reviews the same MCP server definition.
- Return structured summaries and bounded logs, then preserve full reports as local artifacts.
- Treat repository content and test output as untrusted input that cannot override tool policy.
- Start with read-only discovery, add allowlisted test execution, and gate mutating workflows separately.
Connecting Claude to your test suite with MCP means placing a controlled tool layer between Claude and your test runner. Claude can then list approved tests, run a bounded selection, and inspect sanitized results without receiving an unrestricted terminal. The practical goal is faster investigation with the same review, isolation, and CI controls you already expect from test automation.
This guide builds that connection with the official Python MCP SDK and a pytest example. The design also applies to Playwright, Cypress, Jest, Maven, Gradle, and custom runners because the important boundary is the tool contract, not the command behind it.
TL;DR
| Layer | Responsibility | Safe default |
|---|---|---|
| Claude Code | Chooses and calls QA tools | Ask before consequential calls |
| MCP server | Validates arguments and applies policy | Local stdio process |
| Test adapter | Maps a tool call to the runner | Allowlisted commands only |
| Artifact store | Holds full logs, traces, and reports | Repository-local ignored folder |
| CI | Produces the authoritative result | Clean, repeatable environment |
A reliable first version exposes three tools: list_tests, run_tests, and read_test_artifact. Do not begin with a generic run_command tool. The generic tool removes the security and predictability benefits of the MCP boundary.
1. What Connecting Claude to Your Test Suite with MCP Means
Model Context Protocol, or MCP, standardizes how an AI client discovers and invokes tools, reads resources, and uses prompts supplied by a server. In this design, Claude is the MCP client and your small repository utility is the MCP server. The server advertises typed capabilities such as run_tests(target, max_failures) rather than exposing every operating system command.
A tool call follows a clear path: Claude selects a tool -> the MCP server validates arguments -> a test adapter starts the runner -> the server returns a bounded result. That separation gives an SDET a place to enforce timeouts, target allowlists, environment rules, redaction, and output limits. It also produces a stable interface even if the underlying runner changes.
MCP does not make a probabilistic model authoritative. Claude can choose an unsuitable test target, misread an ambiguous failure, or propose a weak assertion. Your suite, code review, and CI remain the quality gates. Think of the connection as an investigation interface that helps a capable collaborator gather evidence.
The best early use cases are test discovery, focused reruns, failure classification, trace summarization, and locating related test code. Broad release control, production data mutation, and secret-bearing administration require separate services and stronger authorization.
2. Choose the Test Boundary Before You Write Code
Start by writing a capability inventory. For every proposed action, identify its inputs, side effects, maximum duration, sensitive data exposure, and recovery behavior. A read-only tool that lists node IDs is lower risk than a tool that updates snapshots. A browser test that creates accounts is not truly read-only even if the repository remains unchanged.
| Capability | Example input | Side effect | Recommended phase |
|---|---|---|---|
| Discover tests | file or marker | None | Phase 1 |
| Read report summary | artifact name | None | Phase 1 |
| Run selected tests | node ID | Test data may change | Phase 2 |
| Update snapshots | exact suite | Repository files change | Separate approval |
| Seed shared environment | dataset name | Shared data changes | Dedicated service |
| Run arbitrary command | free text | Unbounded | Do not expose |
Define target syntax narrowly. For pytest, accept a relative path or node ID under tests/, reject path traversal, and pass it as a single subprocess argument. For Playwright, accept a checked-in project name and a test file under an approved directory. Never build a shell string by joining model-provided text.
Also decide what success means. A test process exit code is authoritative for the run, while Claude's narrative is commentary. Store the command arguments, exit code, duration, truncation status, and artifact path in the response. These fields make later diagnosis auditable. If you are still improving your suite design, the test automation framework architecture guide is a useful companion.
3. Build a Safe FastMCP Test Server
The official Python MCP SDK provides FastMCP, typed tool decorators, and stdio transport. The following server runs pytest without a shell, restricts targets to tests/, applies a timeout, and caps returned output. Create tools/test_mcp.py in a Python project that already uses pytest.
from __future__ import annotations
import os
import subprocess
import time
from pathlib import Path
from typing import Any
from mcp.server.fastmcp import FastMCP
PROJECT = Path(os.environ.get('CLAUDE_PROJECT_DIR', '.')).resolve()
TESTS = (PROJECT / 'tests').resolve()
ARTIFACTS = (PROJECT / '.test-artifacts').resolve()
ARTIFACTS.mkdir(exist_ok=True)
mcp = FastMCP('qa-test-suite')
def safe_target(target: str) -> str:
candidate = (PROJECT / target.split('::', 1)[0]).resolve()
if candidate != TESTS and TESTS not in candidate.parents:
raise ValueError('target must be under tests/')
if not candidate.exists():
raise ValueError('target does not exist')
return target
@mcp.tool()
def list_tests(target: str = 'tests') -> dict[str, Any]:
'''Collect pytest node IDs without executing tests.'''
checked = safe_target(target)
result = subprocess.run(
['python', '-m', 'pytest', '--collect-only', '-q', checked],
cwd=PROJECT,
capture_output=True,
text=True,
timeout=60,
check=False,
)
node_ids = [line for line in result.stdout.splitlines() if '::' in line]
return {'exit_code': result.returncode, 'tests': node_ids[:500]}
@mcp.tool()
def run_tests(target: str, max_failures: int = 1) -> dict[str, Any]:
'''Run one approved pytest target and save the complete combined log.'''
checked = safe_target(target)
failures = min(max(max_failures, 1), 10)
started = time.monotonic()
result = subprocess.run(
['python', '-m', 'pytest', '-q', f'--maxfail={failures}', checked],
cwd=PROJECT,
capture_output=True,
text=True,
timeout=300,
check=False,
)
combined = result.stdout + '\n' + result.stderr
artifact = ARTIFACTS / f'pytest-{int(time.time())}.log'
artifact.write_text(combined, encoding='utf-8')
return {
'exit_code': result.returncode,
'duration_seconds': round(time.monotonic() - started, 2),
'summary': combined[-12000:],
'truncated': len(combined) > 12000,
'artifact': str(artifact.relative_to(PROJECT)),
}
if __name__ == '__main__':
mcp.run(transport='stdio')
Install the server dependency with python -m pip install 'mcp[cli]'. The script is runnable as written when the project has pytest and a tests/ directory. In a production version, catch subprocess.TimeoutExpired and return a structured timeout error. Keep the first example small enough to review.
4. Connect the Server to Claude Code
Claude Code can register a local stdio server from the command line. Project scope writes a shared .mcp.json at the repository root, so teammates review the same command and arguments. Run this from the project directory:
claude mcp add --transport stdio --scope project qa-tests -- python tools/test_mcp.py
claude mcp list
claude mcp get qa-tests
Within Claude Code, open /mcp to inspect connection status and available tools. Claude Code prompts before trusting a project-scoped MCP configuration, which is important because the configuration can start a local executable. Commit .mcp.json, but never place secrets directly in it. For a team using a virtual environment manager, make the command reproducible, for example uv run python tools/test_mcp.py, and commit the corresponding lockfile.
A representative project file is:
{
"mcpServers": {
"qa-tests": {
"type": "stdio",
"command": "python",
"args": ["tools/test_mcp.py"],
"env": {}
}
}
}
Claude Code sets CLAUDE_PROJECT_DIR for local stdio servers, which the Python example uses to resolve paths. This is safer than assuming the process working directory. Test the server from a clean checkout and on every supported operating system. If the server starts but no tools appear, check interpreter resolution, dependency installation, stderr startup errors, and /mcp status before debugging prompts.
5. Design Tool Schemas for Reliable AI Assisted Test Execution
A strong MCP tool resembles a small internal API. Its name states one action, its docstring explains constraints, its arguments use narrow types, and its result has stable fields. Claude performs better when tools are distinct enough that selection is obvious. Operators perform better when every call is easy to audit.
Prefer run_tests(target, max_failures) over execute(options). Prefer an enum such as browser: chromium | firefox | webkit over a free-form runner argument. If a tool can return several outcomes, define fields for status, exit_code, timed_out, summary, and artifact. Do not encode success only in prose.
Separate large data from the initial response. A Playwright trace, screenshot, or full JUnit XML file can be stored under an ignored artifact directory. Return a summary and a handle, then provide a read tool that accepts only known artifact paths and a byte range. This prevents a routine failure from flooding the context window. It also lets Claude request evidence progressively.
Tool errors should be actionable and non-secret. Say that a target is outside the allowed directory, not that an internal validation stack crashed. Include retry guidance only when a retry is safe. Stable error categories such as INVALID_TARGET, TIMEOUT, RUNNER_ERROR, and ARTIFACT_NOT_FOUND make prompts and dashboards easier to maintain.
6. Use a Disciplined Claude Testing Workflow
A good prompt tells Claude the goal, evidence rules, and stopping condition. For example: List tests related to checkout tax, run the smallest relevant target once, classify any failure as product, test, data, environment, or unknown, and cite the returned artifact. Do not edit files. This creates a bounded diagnostic session.
For a code change, split investigation from modification. First ask Claude to reproduce and explain the failure. Review the evidence. Then request the smallest patch and require the affected test plus a broader regression target. This mirrors a senior SDET's workflow and reduces confident edits based on a flaky or environmental failure.
Useful interaction patterns include:
- Discover node IDs before running a guessed path.
- Execute the narrowest deterministic target.
- Inspect structured failure fields before reading full logs.
- Compare a rerun only when the suite's flake policy permits it.
- Ask Claude to state what evidence would falsify its diagnosis.
- Run normal CI after any repository change.
MCP is especially effective when paired with traceable browser tests. For locator-specific failures, see the Playwright getByRole locator guide. For recurring timeout diagnosis, use the Playwright timeout troubleshooting checklist.
7. Handle Test Reports, Traces, and Large Logs
Raw logs are noisy interfaces. Convert runner output into a small normalized result before Claude sees it. For JUnit XML, extract suite name, test name, duration, failure type, and the first meaningful stack frame. For Playwright, return failed project, test title, retry index, screenshot path, trace path, and error snippet. Preserve the original file for human inspection.
Redaction must occur before output crosses the MCP boundary. Replace authorization headers, session cookies, passwords, API keys, customer identifiers, and signed URLs. Use explicit patterns plus structured redaction at the source. A generic regular expression is not enough for every token format. Test redaction with seeded canary secrets and fail closed if sensitive fields cannot be sanitized.
Artifact readers need the same path controls as test targets. Resolve paths, ensure they stay beneath the artifact root, limit file types, bound bytes, and reject symbolic links that escape the directory. Return UTF-8 text only when decoding succeeds. A separate tool can expose image metadata or a known screenshot if visual analysis is part of the approved workflow.
Retention matters too. Local artifacts should be ignored by Git, assigned a short lifetime, and removed by a cleanup job. CI artifacts should follow the organization's access policy. Claude's context is not an evidence archive, so link diagnoses back to immutable CI runs when the result influences a release decision.
8. Diagnose Connection and Execution Failures
Troubleshoot from the transport inward. If Claude cannot see the server, run claude mcp get qa-tests, inspect /mcp, and execute python tools/test_mcp.py only to confirm that it starts without import errors. A stdio server normally waits for protocol input, so silence is not itself a failure. Do not print debug messages to stdout because stdout carries MCP protocol messages. Send diagnostics to stderr or a file.
If tools appear but calls fail, validate the exact arguments and reproduce the underlying subprocess command manually. Compare environment variables, interpreter path, current repository, installed browser binaries, and test data credentials. The MCP-spawned process may not inherit an interactive shell profile. Pass only required environment values, and prefer a committed environment bootstrap command.
If results are truncated, read the saved artifact rather than increasing every response limit. If timeouts recur, determine whether the test runner, application, dependency, or MCP client timed out first. Record each timeout separately. A single generic timeout message hides the ownership boundary.
Finally, distinguish server defects from test defects. Unit test safe_target, command construction, timeout mapping, redaction, and artifact bounds without invoking Claude. Run a protocol inspector during server development, then keep ordinary adapter tests in CI. MCP should reduce diagnostic ambiguity, not become another opaque layer.
9. Secure MCP Tools for QA Teams
Repository text, issue descriptions, page content, test data, and logs can contain instructions aimed at an AI model. Treat them as untrusted data. The server must enforce policy regardless of what Claude reads. A message inside a fixture that says to upload a secret cannot expand the server's allowlist. This is the central defense against prompt injection in tool-using workflows.
Use least privilege at three levels. The process identity should have only necessary file and network permissions. Each tool should expose only one bounded capability. Each environment credential should be scoped to a disposable test environment. Avoid production credentials entirely. If a test needs cloud access, prefer short-lived identity issued outside the model context.
Keep mutating actions in a different server or behind a distinct approval policy. Snapshot updates, database resets, ticket creation, and deployment triggers deserve visible separation from read and test actions. Log tool name, validated inputs, caller context when available, start time, outcome, and artifact reference. Redact before logging.
Review third-party MCP servers as executable dependencies. Pin versions, inspect source and transitive packages, verify update behavior, and document ownership. A popular connector is still code with access to your workstation. For enterprise use, combine repository controls with client-side allowlists and managed configuration.
10. Scale Connecting Claude to Your Test Suite with MCP
Once the local workflow is stable, standardize the tool contract across repositories. A shared adapter can normalize pytest, Playwright, and Jest into the same result envelope while repository plugins supply runner-specific commands. Version the response schema and add compatibility tests so a server update does not silently break prompts or automation.
Keep local Claude sessions and CI responsibilities distinct. Local tools optimize feedback and investigation. CI starts from a trusted revision, provisions controlled dependencies, captures authoritative artifacts, and enforces merge policy. Claude can analyze CI results through a read-only service, but it should not convert a local pass into a release decision.
Measure operational usefulness rather than counting tool calls. Track time to first relevant failure, percentage of diagnoses backed by an artifact, invalid target rate, timeout rate, repeated calls, and human correction categories. Review sampled sessions for over-broad runs and misleading conclusions. These signals show where schemas, descriptions, or suite organization need work.
As capability grows, add explicit test-impact analysis, quarantine lookup, owner mapping, and historical failure search. Each addition should have a bounded data source and a measurable decision it improves. The mature system is not a terminal controlled by natural language. It is a small, governed QA platform whose tools happen to be accessible to Claude.
Interview Questions and Answers
Q: Why use MCP instead of letting Claude run shell commands directly?
MCP lets the team publish narrow, typed capabilities and enforce validation, timeouts, redaction, and audit logging in code. A shell accepts a huge command space and makes policy depend on the model following instructions. The MCP server remains responsible for safety even when Claude receives malicious or confusing text.
Q: What transport would you choose for a repository-local test server?
Use stdio for a local process that needs repository access. It avoids opening a network listener and fits Claude Code's project workflow. For a shared remote service, use authenticated streamable HTTP and apply service-level authorization.
Q: How do you prevent path traversal in a test target?
Resolve the requested path against a known project root, then verify that the resolved path equals or is a descendant of the approved test directory. Pass the validated value as one subprocess argument without a shell. Also consider symlink behavior and reject escaped artifact paths.
Q: How should an MCP test tool report a failure?
Return structured fields including exit code, duration, timeout state, a bounded summary, and an artifact reference. Keep the runner's exit code authoritative. The natural-language diagnosis can be added later and should cite the stored evidence.
Q: Where does human approval belong?
Approval belongs before consequential or mutating calls, and normal code review remains required for patches. Tool policy should not rely only on approval because users can approve a misleading request. Least-privilege implementation and visible approval work together.
Q: How would you test the MCP server itself?
Unit test argument validation, command arrays, error mapping, redaction, output truncation, and artifact containment. Add integration tests with a tiny fixture suite and verify protocol discovery with an inspector. Test from a clean environment to catch hidden interpreter and dependency assumptions.
Q: Can a local MCP result replace CI?
No. Local execution accelerates feedback but may use modified files, cached dependencies, or different infrastructure. CI remains the repeatable quality gate tied to the reviewed revision.
Common Mistakes
- Exposing
run_command(command: str)and assuming a prompt is a security boundary. - Passing model-generated text through
shell=Trueor string concatenation. - Returning megabytes of logs instead of a structured summary and artifact handle.
- Allowing repository paths without canonical resolution and containment checks.
- Mixing read-only diagnostics with snapshot updates or environment resets.
- Printing server diagnostics to stdout and corrupting the stdio protocol.
- Treating a rerun pass as proof that the first failure was harmless.
- Committing secrets in
.mcp.jsonor placing production credentials in the server environment. - Asking Claude for a fix before collecting reproducible failure evidence.
- Skipping CI because the MCP-triggered local run passed.
The corrective pattern is consistent: narrow the capability, validate in deterministic code, preserve evidence, and keep the established delivery gate.
Conclusion
Connecting Claude to your test suite with MCP works best when the server is a deliberately small QA API. Begin with discovery and bounded execution, return structured results, isolate artifacts, and make policy independent of model instructions.
Build the three-tool version against a fixture suite, review its threat model, and run it from a clean checkout. Once those controls are stable, add runner-specific intelligence one measurable capability at a time.
Interview Questions and Answers
Why would an SDET put MCP between Claude and a test runner?
MCP creates a typed, reviewable capability boundary. The server can validate targets, enforce timeouts, redact secrets, and return structured evidence. This is safer and more predictable than an unrestricted shell.
Which MCP transport fits a repository-local test integration?
Stdio is the natural choice because Claude Code starts the local process and communicates without a listening network port. A shared service would usually use authenticated streamable HTTP. This keeps repository access local and simplifies process lifecycle management.
How do you secure a run_tests MCP tool?
Allow only paths beneath the test root, use enum-like options, pass a subprocess argument array without a shell, cap duration and output, and run with least privilege. Treat logs and repository content as untrusted data. The server must enforce these controls regardless of the instructions Claude receives.
What should a test execution result contain?
It should contain an authoritative exit code, duration, timeout status, bounded summary, truncation flag, and artifact reference. Stable error categories also make failures easier to automate and audit. A full redacted log can remain in a bounded artifact for later inspection.
How would you validate an MCP test integration?
Unit test policy and adapters, integration test against a small fixture suite, and inspect protocol discovery. Then run it from a clean checkout across supported environments and verify redaction with seeded canary secrets. I would also verify that untrusted log content cannot bypass the server policy.
How do MCP tools change the role of CI?
They do not replace CI. MCP tools shorten the local evidence loop, while CI remains the controlled quality gate for the reviewed revision. A mature workflow links local diagnoses to CI artifacts.
What is the biggest anti-pattern in AI assisted test execution?
The biggest anti-pattern is exposing a generic command executor and relying on prompts for safety. Policy must be implemented in deterministic server code with narrow capabilities. The safe alternative is a set of typed tools backed by deterministic allowlists.
Frequently Asked Questions
What is the safest way to connect Claude to a test suite?
Use a local stdio MCP server that exposes narrow, allowlisted tools for discovery, execution, and artifact reading. Validate all arguments in server code and keep CI as the authoritative gate.
Can Claude Code run pytest through MCP?
Yes. A FastMCP tool can invoke `python -m pytest` with a validated target, timeout, and captured output. Pass arguments as a list and do not enable a shell.
Where should Claude Code MCP configuration be stored?
Use project scope when the server definition should be reviewed and shared through `.mcp.json`. Use local or user scope only when the configuration is intentionally private to one developer.
Should an MCP test server have production credentials?
No by default. Use disposable test environments and least-privilege, short-lived credentials when external access is necessary. Keep secrets out of configuration files and tool responses.
How do I handle large Playwright traces through MCP?
Store the full trace as an artifact and return metadata plus a bounded error summary. Provide a separate, path-restricted artifact tool for progressive inspection.
Does MCP prevent prompt injection?
MCP provides a place to enforce policy, but it does not automatically prevent prompt injection. The server must treat all model-visible content as untrusted and refuse actions outside its coded allowlist.
Can MCP-triggered tests replace the CI pipeline?
No. They improve local feedback and diagnosis, while CI provides the clean, repeatable result tied to the reviewed commit. Both serve different purposes.