Content
# dotnet-mcp-starter
**A minimal Roslyn MCP server for .NET — compiler-grade code intelligence as tools for Claude Code and other AI agents.**
An agent working on C# from text alone is pattern-matching, however fluently. This server
gives it the compiler's answers instead: symbol search, reference finding, and live
diagnostics over the [Model Context Protocol](https://modelcontextprotocol.io), so
"what implements `IPaymentProvider`?" returns verified truth with `file:line` evidence —
not grep guesses.
This is the deliberately-minimal starter version of the server described in
[A Roslyn MCP server is the cheat code for AI on .NET](https://codemajesty.tech/blog/roslyn-mcp-server-the-1m-line-of-context-trick).
No SDK dependency — the whole MCP protocol surface is one readable file.
## Tools
| Tool | Answers | Notes |
|------|---------|-------|
| `find_symbol` | Where is anything named X? | Case-insensitive contains match (agents half-remember names), capped at 20/project |
| `find_references` | Who uses this symbol? | Compiler-verified via `SymbolFinder` — includes what text search misses |
| `get_compilation_errors` | What is broken right now? | Per-project diagnostics without paying for a full `dotnet build` |
Every result carries `file:line` — the server's job is not to replace reading code, it is
to make every read *the right read*.
## Quickstart
Requires the .NET 10 SDK.
Build the server once, then point Claude Code at the compiled DLL:
```bash
git clone https://github.com/Code-Majesty-Tech/dotnet-mcp-starter.git
cd dotnet-mcp-starter
dotnet build -c Release
```
Wire it into your solution's `.mcp.json` (Claude Code). The server walks **up from its
working directory** to find your `.sln`/`.slnx`, so it discovers the right solution
automatically:
```json
{
"mcpServers": {
"roslyn": {
"command": "dotnet",
"args": ["/absolute/path/to/dotnet-mcp-starter/bin/Release/net10.0/RoslynMcpStarter.dll"],
"timeout": 30000
}
}
}
```
**Invoke the built DLL, not `dotnet run --project`.** Claude Code launches MCP servers with
the working directory set to *your* repo. If your repo pins an SDK via `global.json` (e.g. a
.NET 8 project mid-migration), `dotnet run` resolves that pinned SDK from the cwd and fails to
build this net10.0 server — the server never starts and Claude Code loops forever on
`-32000: Connection closed`. Invoking the compiled DLL (`dotnet path/to/x.dll`) uses runtime
resolution, **ignores `global.json`**, and starts near-instantly with no per-connection
MSBuild pass.
The 30-second timeout is not decoration: first contact pays MSBuild *workspace* loading plus
the first compilation of *your* solution. A shorter timeout kills the server mid-warmup —
after which the agent silently falls back to grep for the whole session and you wonder why
quality regressed.
## Design rules baked in
- **Cap every result set** — the agent's context window is the scarce resource this
server exists to protect. Uncapped reference queries cost more context than the grep
they replace.
- **`MSBuildLocator.RegisterDefaults()` before any Roslyn type loads** — the classic
footgun. Runtime MSBuild assemblies come from the located SDK, never from NuGet
(see the `ExcludeAssets="runtime"` note in the csproj).
- **Minimally-qualified signatures** — `CancelAsync(CancelCommand)` reads at a glance;
fully-qualified monsters burn tokens to say less.
- **Compilation caching** — first call per project pays seconds; cached calls return in
milliseconds. A server that rebuilds per call trains the agent to stop calling it.
- **Stateless tools, stateful workspace** — restartable mid-session, because MSBuild
workspaces have moods.
## Known limitations (starter honesty)
- **The workspace loads once, at startup.** Structural changes (new projects, csproj
edits) need a server restart; in-flight file edits are not re-read into the loaded
compilations. The production pattern re-reads per call — left out here for clarity.
- Three tools, not nine. The natural next ones — `get_implementations`,
`get_type_hierarchy`, `get_dependency_graph`, `find_unused_code`, `analyze_usings`,
`get_project_dependencies` — follow the exact shape of the three shipped; the
[companion post](https://codemajesty.tech/blog/roslyn-mcp-server-the-1m-line-of-context-trick)
describes what each should return and why.
- Single solution per server instance, by design.
## Where this fits
Semantic ground truth is one layer of a larger guardrail stack for running agents on
production .NET — investigation gates, build gates, oscillation detection, and the rest:
- [The twelve layers of Claude Code guardrails for .NET](https://codemajesty.tech/blog/twelve-layers-of-claude-code-guardrails-for-dotnet)
- [The CLAUDE.md + hooks starter repo](https://github.com/Code-Majesty-Tech/dotnet-claude-template)
- [How we run Claude Code on a 5-developer .NET team](https://codemajesty.tech/blog/claude-code-for-dotnet-teams-the-honest-setup)
## License
[MIT](LICENSE) — take it, extend it, tell us what broke.