home/docs/mcp tools

MCP tools

sidekit mcp serves ten tools over stdio. Every one answers with compact, structured JSON, omits null and default fields, and caps long lists with an explicit count of what was left out.

Every tool that takes a project defaults to the workspace's single project when the workspace has exactly one. When it does not, the tool asks you to name a .csproj rather than guessing. Paths may be absolute or workspace-relative, and answers come back workspace-relative.

Reading an answer

A few fields appear across several tools and carry more meaning than their size suggests. An agent has to be able to tell a narrow answer from a wide one, or it will trust a precision it did not get.

resolution — how confident the answer is

ValueMeaning
lineAnswered from the coverage map at line granularity — the narrowest answer.
fileAnswered from the coverage map for the whole file.
projectA superset from the static project graph. Correct, but wider than necessary.
fullA workspace-level build file changed; everything is in scope.
noneNothing to select.

stale — the map predates your edit

stale: true means the file has uncommitted edits newer than the coverage map, so the answer describes the code as it was when the map was built. Refresh it by re-running the file's tests with withCoverage. The field is omitted entirely when false.

searched — how much of the workspace was looked at

find_symbol and find_usages add searched when some projects could not be loaded — “3 of 26 projects; 2 unsearchable: NETSDK1004: run dotnet restore”. It is omitted when everything was searched, so its absence is the signal that the answer is complete. An empty result over an incomplete workspace also carries a diagnostic saying as much, because “no matches” and “not looked yet” must never read the same.

The capability envelope

get_status and run_tests answers carry capabilitiesrunner (mtp, vstest or none), coverageResolution and instrumentation — so a degraded repository reports reduced capability as data rather than as a failure you discover by crashing.

get_status

Does this project compile right now, and what broke?

Blocks briefly while an in-flight evaluation settles, then returns the build state with shaped top-N diagnostics. The daemon keeps every queried project warm, so repeat calls do not pay a cold project load.

ParameterDefaultMeaning
projectworkspace defaultPath to the .csproj to evaluate.
waitMs5000Milliseconds to wait for an in-flight evaluation to settle.
sinceGenerationnoneThe generation from your last answer. Still matching means nothing changed.
answer · unchanged since your last call
{ "generation": 7, "unchanged": true }

That is the whole response — around 33 bytes. Passing sinceGeneration on every poll is the single cheapest thing an agent can do.

answer · a build failure
{
  "generation": 8,
  "ready": true,
  "state": "BuildFailed",
  "capabilities": { "runner": "mtp", "coverageResolution": "none" },
  "build": {
    "items": [
      { "id": "CS0103", "severity": "Error", "file": "src/A.cs", "line": 3, "message": "…" }
    ],
    "totalDistinct": 1, "remainder": 0,
    "errorCount": 1, "warningCount": 0, "compiles": false
  }
}

Possible state values: BuildPassed, BuildFailed, Building, LoadError, EngineError.

run_tests

Build incrementally and run a project's tests out of process.

Answers with counts, duration and the top failure summaries plus a remainder count — never the console output. When nothing in the project's reference closure has changed, the build is skipped entirely.

ParameterDefaultMeaning
projectworkspace defaultPath to the test project's .csproj.
testswhole suiteFully-qualified test method names to run.
maxFailures5Failure summaries to include; the rest becomes a count.
withCoveragefalseAlso refresh the per-test coverage map for the tests this run executes.
retryFailed0Re-run failures up to N times (max 5); one that passes on retry is flagged flaky.
answer · a run with clustered failures
{
  "ran": true,
  "total": 100, "passed": 0, "failed": 100, "skipped": 0,
  "durationMs": 9000,
  "failures": [
    { "test": "Acme.Tests.Feature0Tests.Case_000", "message": "Shared root cause 0", "affected": 20 }
  ],
  "moreFailures": 0,
  "capabilities": { "runner": "vstest", "coverageResolution": "none" }
}

A hundred failures stay inside 800 bytes because tests failing from one cause are reported once with an affected count, biggest clusters first.

A run interrupted by your own edit is not an error. If a build-relevant edit lands mid-run, the test process is killed and the answer comes back ran: false with a diagnostic saying it was superseded. Re-run for current results. A failed build answers the same way — structured, never a transport error.

With withCoverage: true the answer adds a coverage node (refreshed, skipped, current). See Coverage map for what that costs.

get_failures

What failed in the last run — without running anything again.

Re-queries the recorded run. Summary mode gives clustered first-line messages; full messages and stack traces come only on request, so an agent never pays for stacks it will not read.

ParameterDefaultMeaning
projectworkspace defaultPath to the test project's .csproj.
topN5Failures to include; the rest becomes a count.
includeStacksfalseFull messages and stack traces instead of first-line summaries.
testsallScope to these tests (names or * wildcards) with full detail per match.

Naming tests implies full detail — it is the cheap way to inspect one failure without turning stacks on for all of them. When no run is recorded, the answer says so structurally rather than erroring.

select_tests

Which tests should I run for this change?

Diff-first: with no arguments the git working tree is the change set. Answers a high-confidence superset from the static project dependency graph — changed file, to owning project, to transitively dependent test projects.

ParameterDefaultMeaning
filesgit working treeAn explicit change set, absolute or workspace-relative.
answer
{
  "resolution": "project",
  "testProjects": [ "tests/Api.Tests/Api.Tests.csproj" ],
  "changed": 1
}
Refinement only upgrades when every changed file is mapped. If the coverage map knows all of them, resolution becomes file and the answer names exact tests. One unmapped file keeps the honest project-level superset — it never narrows on partial knowledge.

A workspace-level build file change escalates to resolution: "full" with fullRun: true. Run whatever comes back with run_tests.

get_tests_for_file

Which tests exercise this file, or this line?

Answers from the persistent per-test coverage map, degrading honestly: line, then file, then the project-level superset when the file was never mapped.

ParameterDefaultMeaning
filerequiredThe file to ask about.
linewhole fileA specific line, 1-based.

Line queries on a file with uncommitted edits are translated back through the diff to the map's original numbering, so your line numbers keep working between refreshes. A line inside an edited hunk degrades to file resolution instead of answering wrongly.

get_blast_radius

What could a change to this file break?

The owning project, the downstream projects that transitively depend on it, the test projects guarding it, and the tests known to exercise it — in one envelope.

ParameterDefaultMeaning
filerequiredThe file to ask about.
linewhole fileNarrows the answer to the symbol declared there.
answer · with a line, narrowed to the symbol
{
  "project": "src/Core/Core.csproj",
  "impactedProjects": [ "src/Api/Api.csproj" ],
  "testProjects": [ "tests/Core.Tests/Core.Tests.csproj" ],
  "coveringTests": [ "Core.Tests.Adds" ],
  "symbol": "Core.Calc.Add(int, int)"
}

Given a line, the answer covers that symbol's declaration, its overrides and implementations, and its transitive callers — and coveringTests narrows from everything touching the file to everything reaching that symbol.

symbol is omitted when the answer is file-granular, and that case is never silent: if no symbol resolves at the line you gave, diagnostic reads “No symbol resolved at that line — answered at file granularity.”

get_flaky_tests

Is this red real?

Answers from the persistent run history: tests whose recent outcomes flip between pass and fail, most confident first, with runs, failures, flips, confidence and last outcome per test.

ParameterDefaultMeaning
lastRuns20How many recent runs to consider.
A single flip is not flake. A test needs at least three executed runs and at least two flips to be reported. One flip is a regression or a fix, and calling it flake would teach an agent to ignore a real break.

find_symbol

Where is this declared?

Searches the workspace's C# declarations by exact name or * pattern and answers with kind, file:line and a signature — the replacement for grepping for class Foo or interface IFoo. Every match is returned rather than one being guessed at.

ParameterDefaultMeaning
namerequiredExact name, or a * pattern such as Sqlite*.
kindanyRestrict to type, interface, method, property, field or event.
limit20Declarations to return; the rest becomes moreMatches.
answer
{
  "matches": [
    { "symbol": "Acme.Persistence.SqliteIndex", "kind": "type",
      "at": "src/Acme.Infrastructure/Persistence/SqliteIndex.cs:39" }
  ]
}

Measured on Sidekit's own repository, that answer costs 37 tokens. The grep -rn an agent runs to answer the same question costs 2,496, and hands back comments, strings and same-named members of unrelated types along with the declaration.

signature is included only when it says something the name does not, and then parameter types only — never a fully rendered signature with return types, parameter names and default values, which measured larger than the answer it was decorating.

find_usages

Who uses this?

Every reference across the whole solution — including from the projects that depend on the one declaring it — grouped by file with counts. Correct where grep is not: no comments, no strings, no same-named members of unrelated types.

ParameterDefaultMeaning
namenoneSymbol name, optionally qualified — SqliteIndex or Persistence.SqliteIndex.
filenoneFile declaring the symbol, when a name alone is ambiguous.
linenone1-based line of that declaration.
limit20Candidate declarations offered when the name is ambiguous.

Give it a name, or a file and line naming the declaration — one or the other is required.

answer · grouped by file
{
  "symbol": "Acme.Persistence.SqliteIndex",
  "declaration": "src/Acme.Infrastructure/Persistence/SqliteIndex.cs:39",
  "uses": 60, "files": 12,
  "byFile": [
    { "file": "tests/Acme.Infrastructure.Tests/Persistence/SqliteIndexTests.cs",
      "count": 19, "lines": [6, 19, 29, 31, 39, 55] }
  ],
  "moreFiles": 4, "moreUses": 4
}

Sixty uses across twelve files, in 264 tokens — against 2,496 for the grep that answers the same question less accurately. Below roughly ten uses the answer comes back as a flat sites list instead, because grouping is not free: both shapes are built and the smaller one is sent. See Token economy for the budgets.

An ambiguous name is answered, not guessed. RunAsync is declared nineteen times in Sidekit's own solution, so asking by that name alone returns the candidate list and a diagnostic“'RunAsync' is declared 19 times — ask again with the file and line of the one you mean.” Returning the first would hand back a complete-looking use list for a method nobody asked about, with nothing in the answer saying so.
This replaces grep, not a language server. Against grep -rn the answer is around five times smaller overall, and 8–19× on heavily-used symbols. Against the rendered output of a host's own C# language server it is 1.32×, and below ten references it is larger. Where your host already runs a working one, what is left is addressing by name, one call instead of two, and an answer that states its own coverage.

doctor

Why is something behaving oddly?

A structured pass / warn / fail report across the .NET SDK, workspace and solution discovery, NuGet restore state, which runner each test project uses, coverage-collector availability, the git working tree, index writability and file watching. Every check is probed rather than assumed, and every warning names what degrades and how to fix it.

ParameterDefaultMeaning
workspacePathserver workspaceWorkspace to diagnose.

The same report is available from the terminal as sidekit doctor, which is how you tell a Sidekit problem from an agent-host environment problem.