Strider 0.10.1
dotnet tool install --global Strider --version 0.10.1
dotnet new tool-manifest
dotnet tool install --local Strider --version 0.10.1
#tool dotnet:?package=Strider&version=0.10.1
nuke :add-package Strider --version 0.10.1
Strider
A read-only MCP server that lets AI coding agents navigate .NET code the way the compiler sees it.
Strider loads your solution into Roslyn and answers questions like "who calls this method?", "what implements this interface?" or "what breaks if I change this signature?" with short, structured JSON. The agent doesn't have to grep for text and read whole files to work it out.
- Semantic, not textual. It resolves overloads, generics, extension methods, partial types and calls through interfaces (DI), and it ignores comments and strings that merely contain the name.
- Cheap on tokens. Answers are lists of symbols and
path:linelocations, and every list is capped. A response says whether it was cut, so the agent knows when to ask for more. - Answers it can act on. Every symbol comes back with the exact name every other tool accepts, and every error says what to call instead.
- Sees past your code. Symbols from NuGet packages and the framework can be found and decompiled to C#.
- Stays current. Edited files are picked up within seconds, new files and project edits trigger
a reload, rebuilt DLLs are swapped in, and
refresh_filesapplies an agent's changes at once. - Never writes. All 28 tools are read-only.
Works with any MCP client: Claude Code, VS Code, Cursor, Claude Desktop and others.
What it looks like
The agent asks where SymbolText.Ref is called, capped at three results:
// find_call_sites { "symbol": "SymbolText.Ref", "maxResults": 3 }
{
"Target": {
"Symbol": "Strider.Symbols.SymbolText.Ref(ISymbol, SolutionPaths, SourceClassification, CancellationToken)",
"Kind": "method",
"At": "src/Symbols/SymbolText.cs:132"
},
"CallSites": [
{ "Symbol": "Strider.Symbols.StackFrameResolver.ResolveAsync(Solution, int, StackFrame, CancellationToken)",
"Kind": "method", "At": "src/Symbols/StackFrameResolver.cs:48" },
{ "Symbol": "Strider.Symbols.SymbolLookup.ResolveAsync(Solution, string, KindFilter, string, int?, CancellationToken, int)",
"Kind": "method", "At": "src/Symbols/SymbolLookup.cs:46" },
{ "Symbol": "Strider.Tools.Hierarchy.FindImplementationsTool.ExecuteAsync(…)",
"Kind": "method", "At": "src/Tools/Hierarchy/FindImplementationsTool.cs:40" }
],
"TotalFound": 17, "Truncated": true, "Limit": 3
}
Each Symbol can be passed straight back as the symbol argument of another tool.
Quick start
You need the .NET 10 SDK. Strider uses it to load your solution, so it has to be a full SDK, not just the runtime.
dotnet tool install -g Strider
Claude Code
claude mcp add --scope user strider -- strider
Other clients. Add a stdio server whose command is strider. For example, in VS Code
(.vscode/mcp.json):
{
"servers": {
"strider": { "type": "stdio", "command": "strider" }
}
}
Clients that use the mcpServers format (Cursor, Claude Desktop) take
{ "mcpServers": { "strider": { "command": "strider" } } }.
No per-project setup is needed: the agent loads the solution it works on with load_solution.
To have one loaded at start, pass --solution (see Choosing the solution).
Tools
Find and navigate
| Tool | Answers |
|---|---|
find_symbol |
Where is this type or member declared, in source or in a referenced DLL? |
find_references |
Where is it used, and how: call, creation, write, inheritance, parameter type, type argument or read? |
find_call_sites |
Every call site of a method or constructor, including calls through the interface it implements; with argument, what each one passes to that parameter |
find_implementations |
For a type: which types implement or derive from it. For a method, property, event or indexer: its implementations and overrides |
get_type_hierarchy |
Base types, interfaces and derived types of a type |
get_callee_graph |
What does this method call, transitively? With target: the shortest path to another method |
find_attribute_usages |
Where is this attribute applied, to what, and with which argument values? |
Read without opening files
| Tool | Answers |
|---|---|
get_file_outline |
Types and member signatures of a file, with line numbers and no bodies |
get_symbol_source |
The code of one symbol, with every part of a partial type or member; types as a skeleton; DLL symbols decompiled |
get_symbol_at |
What a name on a given line refers to: overload, inferred type, nullability, docs. Like an IDE hover |
get_symbol_detail |
Signature, parameters with defaults, accessibility and doc summary |
get_public_members |
The public members one type declares; with includeInherited, also those of its base types, each once |
Analyze
| Tool | Answers |
|---|---|
analyze_change_impact |
What breaks if this symbol changes: references per project, implementations, callers, risk |
get_diagnostics |
Compiler errors and warnings; with analyzers: true also CA, IDE, Sonar… as dotnet build reports them |
find_unused_symbols |
Unused non-public types, methods and properties |
detect_type_cycles |
Groups of types that depend on each other, each with its shortest cycle |
resolve_stack_trace |
Map an exception stack trace to source lines, undoing async and lambda rewrites |
get_switch_coverage |
Every switch over an enum or a type hierarchy: which cases each one misses, and whether a default arm would take a new one |
find_package_usages |
Which APIs of a NuGet package the code uses, how often and where, per project — before an upgrade or a replacement |
Project structure and ASP.NET Core
| Tool | Answers |
|---|---|
get_project_graph |
Projects, target frameworks and the projects each one compiles against |
get_generated_sources |
The files source generators add to the compilation, and their code — they are not on disk |
get_endpoint_map |
Endpoints with method, route, auth, handler and API versions (Minimal API, attribute and conventional controllers) |
get_di_registrations |
DI registrations per app, duplicates, and singletons that capture scoped services (also through transients) |
get_project_graph leaves out package references. For those and their resolved versions use the
.NET CLI: dotnet package list --format json (SDK 10) or dotnet list package --format json
(earlier SDKs).
Workspace
| Tool | Does |
|---|---|
get_solution_status |
Shows what is loaded and its state, plus MSBuild's load diagnostics |
load_solution |
Loads a .sln, .slnx, .slnf or .csproj, replacing the current one |
refresh_files |
Applies file changes at once; new files and project edits reload in the background |
reload_solution |
Reloads from disk, e.g. after a NuGet restore |
unload_solution |
Unloads the solution and frees its memory |
How every tool is called
symbolnames a type or member: bare (CreateAsync), with its type (OrderService.CreateAsync) or fully qualified; a parameter list picks an overload,<T>or`1a generic type.fileis any whole part of a path (OrderService.cs,Services/OrderService.cs,src/Orders) or a glob (**/*Tests.cs);line/columncount from 1;projectis a project name or.csprojpath.- Results give symbols as
Symbol+Kind+At("path:line", or"dll:Assembly"for a library symbol). Lists carryTotalFound;TruncatedandLimitappear only whenmaxResultscut them. - Errors carry
Error(a code such asSymbolNotFound,AmbiguousMatch,NotLoaded),Messageand usuallyHint;AmbiguousMatchlistsCandidatesto pick from.
Choosing the solution
Strider loads one solution at a time, and only one that was named. There are two ways to name it:
--solution <path>at start: a.sln,.slnx,.slnf(only the projects it lists) or.csproj(that project and its references). A directory is scanned for a solution instead, up to 3 levels deep, so--solution .loads the one in the directory the client starts the server in.load_solutionfrom the agent, at any time. It takes the same paths and replaces what is loaded. This is useful in a git worktree, where the loaded solution may belong to another checkout.
Without --solution nothing is loaded, and tools report NotLoaded until load_solution.
When a scanned directory holds several solutions, the shallowest wins, then the first alphabetically.
Other ways to run it
<details> <summary><b>Local tool, per repository</b></summary>
dotnet new tool-manifest # if the repository has none yet
dotnet tool install Strider
.mcp.json in the repository root (Claude Code starts the server there, so --solution . loads
the repository's solution):
{
"mcpServers": {
"strider": { "command": "dotnet", "args": ["tool", "run", "strider", "--solution", "."] }
}
}
</details>
<details> <summary><b>Container</b></summary>
No SDK is needed on the host. The image is built with the SDK's container publishing, so there is no Dockerfile:
dotnet publish src/Strider.csproj -c Release --os linux --arch x64 /t:PublishContainer
That builds strider:latest. Mount the solution at /workspace and keep stdin open (-i),
because MCP runs over stdio:
claude mcp add strider -- docker run -i --rm -v "$PWD:/workspace" strider:latest --solution /workspace
$PWD is expanded when you run the command, so register it from the repository root and once per
repository (the default local scope). The base image is mcr.microsoft.com/dotnet/sdk:10.0: on
a runtime-only image the server starts but cannot load any solution.
To push to a registry, add -p:ContainerRegistry=ghcr.io -p:ContainerRepository=<owner>/strider.
</details>
<details> <summary><b>From source</b></summary>
dotnet run --project src/Strider.csproj -- --solution path/to/App.sln
</details>
Troubleshooting
"No .NET SDKs were found" on macOS or Linux. MCP clients often start servers without your
shell profile, so DOTNET_ROOT is missing. A Homebrew dotnet wrapper script can cause the same
error. Strider then looks for an SDK in the sdk/ folder next to the .NET runtime it runs on. If
none is there, set DOTNET_ROOT in the server's registration:
claude mcp add-json --scope user strider \
'{"type":"stdio","command":"strider","env":{"DOTNET_ROOT":"/usr/local/share/dotnet"}}'
Use /usr/local/share/dotnet for the official installer and
/opt/homebrew/Cellar/dotnet/<version>/libexec for Homebrew.
Results come from the wrong checkout. Call get_solution_status to see what is loaded, and
load_solution to switch.
A tool returns Loading. A large solution takes a while to open. Call again in a moment.
The first queries after a load are slower. The solution is usable as soon as MSBuild has opened it; the compilations are built in the background, and a query that comes first builds the ones it needs.
Results look stale after dotnet restore. A package restore without a project file edit is
invisible to the server. Call reload_solution.
Development
dotnet build Strider.slnx -c Release
dotnet src/bin/Release/net10.0/Strider.dll --solution path/to/App.sln
| Folder | Contents |
|---|---|
src/Workspaces |
Solution discovery, MSBuildWorkspace lifecycle, change tracking, compilation cache, source classification, file patterns |
src/Symbols |
Name grammar and lookup, symbol text and kinds, type relations, call graph, stack frames, file outlines |
src/Syntax |
Line numbers, names and member signatures read from syntax, shared by the tools |
src/Metadata |
Referenced DLLs: member index and decompilation |
src/NuGet |
project.assets.json reading and package API usage scan |
src/Hosting |
Service registration, server instructions and the filters around every tool call |
src/Analyzers |
Runs the projects' own analyzers for get_diagnostics |
src/Endpoints |
ASP.NET Core endpoint discovery for get_endpoint_map |
src/DependencyInjection |
DI registration scan and container analysis for get_di_registrations |
src/ChangeImpact |
References, dependent declarations, transitive callers and risk for analyze_change_impact |
src/Tools |
One class per MCP tool, in folders by purpose (Lifecycle, Navigation, Usages, Hierarchy, Inspections, Composition, Projects), sharing ToolRun, argument checks and parameter texts |
src/Responses |
Response records, SymbolRef, capped lists, ToolError with error codes and hints, the JSON writer |
Logs go to stderr, because stdout carries the JSON-RPC stream.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.