Skip to main content

Set up a coding agent with ProtoTest

One install connects a coding agent to the runs in your repository. The server is a .NET tool. It reads .prototrace archives and answers questions about them over the Model Context Protocol.

Install​

dotnet tool install --global ProtoTest.Mcp

The tool command is prototest-mcp. The package targets .NET 8, 9 and 10, so the install picks the framework your SDK has.

Register it​

An MCP client starts the server and talks to it over stdio. Add the server to the client's configuration file. The shape is the same everywhere. Only the file and the root key change.

.mcp.json (repository root)
{
"mcpServers": {
"prototest": {
"command": "prototest-mcp",
"args": ["--project", "."]
}
}
}

The server resolves . against its working directory. Keep the file at the repository root. If the client starts the server elsewhere, pass an absolute --project path.

Check it​

Three steps, none of which needs a suite of your own.

  1. Read a committed failing run with the CLI. Download l0-environment-drill.prototrace from the Learn track, then:

    dotnet tool install --global ProtoTest.Cli
    prototest summary l0-environment-drill.prototrace
    ProtoTest trace 2.0 · run 59f2aa5ff7fd43069060b49f0cfe3106 · 2026-10-02 08:32:36Z - 2026-10-02 08:32:40Z
    1 tests · 1 failed

    FAILED Northstar.ProtoTest.FailureDrills.TheAddressWasHardcodedForOneMachine (2.57 s)
    ConnectionError reaching http://127.0.0.1:5099: connection refused.
    test.execution Test execution · failed
    cause: runner-reported failure

    That is the drill file's own output, so it matches what you downloaded. Your own runs print different ids and times. prototest summary prints the same diagnosis the MCP tools return, which makes it the quickest way to check that the file an agent would read says what you expect. The CLI reference documents all seven verbs, their arguments and their exit codes.

  2. Point the server at one file with --trace, or at a folder of runs with --project. For one archive, --trace works wherever the file was written.

  3. Run your own suite once so a .prototrace exists, and ask the agent to list the runs. It should answer with a run id and a trace file from your machine.

Where it reads​

The server started withIt reads
--trace <file>exactly that archive. A folder argument is refused
--project <folder>that folder's TestResults/ when it holds a readable trace, and stops there. Otherwise it walks the folder's tree
neitherthe same, from PROTOTEST_PROJECT, else from the working directory

The walk skips obj, .git and node_modules, and inside bin it reads only TestResults folders.

"Newest" is the run's recorded start time, never a file timestamp. An archive the reader cannot open is skipped with a reason in list_runs and never guessed at.

The default trace lands in the test project's build output, bin/<configuration>/<framework>/TestResults. The walk reads those folders and nothing else inside bin, so a fixture copied to the output is never taken for a run. A results folder of your own is still the better setup: one place for every run, and the first place the server looks. The continuous integration page sets that up with one environment variable, so CI and local runs write to the same place:

var results = Environment.GetEnvironmentVariable("PROTOTEST_RESULTS")
?? Path.Combine("TestResults", "ProtoTest");

builder.ConfigureTracing(trace =>
trace.OutputPath = Path.Combine(results, "run.prototrace"));

Set PROTOTEST_RESULTS to an absolute path such as <repository>/TestResults when you run locally.

Give the agent the skill​

The repository carries two skills. prototest-evidence-loop teaches the loop from a red test to a fix. prototest-write-test teaches writing a new test that reuses what the suite already composes, and proving it. Both are copy-in, not an install.

A client that reads skills folders (Claude Code, for example) loads it from its skills directory. From a checkout of the ProtoTest repository:

mkdir -p .claude/skills
cp -r skills/prototest-evidence-loop skills/prototest-write-test .claude/skills/

A project made with dotnet new prototest already has both in .claude/skills/. Otherwise download the files from the repository and place it in the same layout. A client without a skills folder reads a file as an instruction instead: paste its body into the client's rules or instructions file.

The skills are optional. The MCP server's tool descriptions are the contract, so an agent without the bundle can still list the tools and work from them.

What the agent can see​

The tools read your traces and return their content, including expected and actual values, to the connected agent. Run the server as an identity that may see them.

Every tool is read-only and returns a compact JSON document. list_runs over a folder holding one failing run:

{
"root": "C:/dev/your-repo",
"runs": [
{
"runId": "761778e6dc82498a9f9965fa1e6b5a24",
"traceFile": "TestResults/run.prototrace",
"startedAtUtc": "2026-09-29T06:19:01Z",
"completedAtUtc": "2026-09-29T06:19:05Z",
"outcomes": { "failed": 1 },
"failingTests": [ { "testId": "00001", "name": "TheAddressWasHardcodedForOneMachine" } ],
"failingTestsTruncated": false
}
],
"truncated": false,
"skipped": null
}

The run id, file paths and timestamps above stand in for any run. A real call returns your machine's. Paths under root are relative to it, with forward slashes, so the agent spends its context on the evidence; root itself stays absolute.

ToolInputReturnsCap
list_runsoptional folder, limit (default 10)the newest runs first: run id, trace file, start and completion, outcome counts, failing test ids, and the archives it had to skip50 runs
get_failureoptional runId, testIdthe failure entry: outcome, error, source location, the selected failing operation, the other operations that failed, the shape mismatches and the test's artifacts10 failed operations, 25 mismatches
get_diagnosisoptional runId, testId, detail (summary or context)the run's diagnosis, or one failing test's context packagethe diagnosis caps
get_coverageoptional runId, target, category, includeUncovered, offset, limitcoverage totals and uncovered units from the report the run embedded, with one suggestion per endpoint: extend the test that already calls it, or write a new test shaped like the named one200 uncovered units, 20 suggestions
compare_runsoptional baselineRunId, currentRunId, baselineTraceeach test that broke, was fixed, still fails, is new or was removed, with the first operation where the two runs part. Defaults to the newest run against the run before it50 tests
check_fixoptional tests, baselineRunId, currentRunIds, baselineTracethe fix receipt: proven or not, each claimed test with its reasons and where it changed, the tests that broke, and the failing report findings. Defaults to the newest run against the run before it20 tests, 20 broken tests
review_testsoptional runId, testseach test with findings: no check, an unchecked call or an untraced gap, each with its next step and source location; clean tests are counted30 tests, 10 findings per test
get_suite_mapoptional runIdwhat a new test reuses: the capabilities and infrastructure the host composed, the clients, data provisioners, attributes, page objects and devices the tests used, one passed example test per kind of work with its file, and the open coverage gaps50 entries per list, 50 gaps

Diagnosis explains what get_failure and get_diagnosis return and what the agent can do with it.

get_suite_map lists what the run recorded, not the whole test project: a provisioner or page object no test used in that run is not in it. A keyed element lists its shape, OrdersPage.Order[key].Status, not the key one run found it by.

The job prompts​

The server offers three prompts. A client that shows prompts (a slash command or a prompt picker) starts a whole job from one, and each job ends with the tool that judges it, so the agent does not decide on its own that it is done:

PromptArgumentsThe jobDone when
fix_failureoptional testread the failure, compare with the last green run, fix code or test, reruncheck_fix says proven
cover_changechange, optional targetread the suite map and the coverage suggestions, write or extend the test, make it fail once on purposethe units read covered and review_tests reads the test clean
improve_testsoptional testapply each review finding's next step in the suite's stylereview_tests reads clean and compare_runs shows nothing broken

Every prompt starts with get_suite_map, so the agent writes in the suite's own style: its clients, data provisioners, attributes and page objects, not new setup.

Limits​

  • Read-only: no trace is written, no suite is rerun, the stdio host binds no port, and nothing on disk is modified.
  • Nothing leaves the machine by default: no telemetry, no uploads, no accounts. The stdio host reads local archives, stdout carries the protocol only, and logs go to stderr.
  • Hard caps bound every payload. list_runs returns at most 50 runs, get_coverage at most 200 uncovered units, get_suite_map at most 50 entries per list, and get_diagnosis applies the diagnosis caps.
  • The server reads evidence that already exists. A run with no .prototrace is not visible to it, so write the trace first.
  • One external dependency: the official ModelContextProtocol SDK (Apache-2.0).
  • The demo endpoint is a local sample. See Coding agents.