Content
# Semantic .NET MCP
**Roslyn-based AST Code Intelligence, served over MCP.**
Your AI agent reads C# the way a junior dev reads a novel: left to right, top to bottom, hoping context arrives eventually. Semantic .NET MCP fixes that. It loads your .NET solution into Roslyn's semantic compiler, builds a full symbol index, and exposes the entire codebase as a queryable graph through the [Model Context Protocol](https://modelcontextprotocol.io/). Seven tools. Four resources. One prompt that politely tells your agent to stop using `read_file`.
The result: your agent gets type resolution, call graphs, inheritance chains, and cross-project references. Not grep. Not string matching. Actual compiler-grade understanding of your code.
Built on .NET 10 / C# 14. Output defaults to [TOON](docs/toon-format.md) (Token Optimized Object Notation), which strips ~60% of the tokens JSON would waste on braces, quotes, and commas. Because when you're paying per token, syntax noise is a tax.
## The Roslyn Advantage (It Is Not Fair, and We Do Not Apologize)
Let's be precise about something most agentic projects gloss over.
Most language tooling produces an AST: abstract syntax tree. A structural parse. Nodes and tokens. Parenthesized expressions and identifier lists. Useful, sure, the way a phonebook is useful when you need to understand a city. An AST tells you *what was written*. It does not tell you what it *means*.
Roslyn does not stop at syntax. Roslyn produces a full **semantic model**: a `Compilation` object that contains every resolved type binding, every overload resolution, every generic type argument inference, every implicit conversion, every symbol's accessibility across every project boundary. When you ask Roslyn "what does `var x = GetOrder()` resolve to?", it does not guess. It tells you `x` is `Task<Order?>` because `GetOrder()` returns `Task<Order?>` because the interface contract three projects away says so. It walks the entire dependency graph. It resolves `<inheritdoc/>` chains. It knows which `Dispose()` you mean when six types implement `IDisposable`.
This is not an AST. This is a **compiler as a service**. And no other mainstream language ecosystem has anything equivalent that is this accessible, this fast, and this correct.
Python has `ast.parse()`. It gives you a syntax tree. No type information. No cross-module resolution. No overload disambiguation. You want types? Run mypy separately, parse its output, pray the versions match.
TypeScript has the TS compiler API. Credit where due: it is the closest competitor. But it reimplements module resolution from scratch per project, chokes on monorepo boundaries, and the `ts.Program` object is mutable and not safe for concurrent access. Good luck serving concurrent queries without corrupting state.
Java has Eclipse JDT and javac trees. Both require a running JVM, neither exposes a clean `Compilation` snapshot, and incremental recompilation is a research project, not a shipping feature.
Rust has `rust-analyzer`. Excellent engineering, genuinely impressive incremental computation. But `rust-analyzer` is an IDE server, not a library. You don't embed it; you talk to it via LSP. And LSP was designed for editors, not agents. The protocol has no concept of "give me all callers of this function across the workspace in a single structured response."
Roslyn was designed from day one as an embeddable compiler platform. Immutable snapshots for concurrency. Lazy semantic model computation per-file. Incremental reparsing. Full cross-project symbol resolution. All of it available as NuGet packages you can `dotnet add` and call from your own process.
**Semantic .NET MCP exposes this entire semantic model to coding agents over MCP.** Not a syntax tree with some heuristic type guessing bolted on. The actual compiler's understanding of the code. Every resolved type. Every binding. Every cross-project reference. Every generic instantiation.
This is why C# and .NET are the superior choice for agentic development. Not because of syntax preferences or runtime benchmarks. Because the compiler itself is a queryable, embeddable, concurrent-safe semantic database. No other ecosystem hands you this for free. The advantage is structural and it compounds with codebase size: the larger and more complex your solution, the wider the gap between "agent reading text files" and "agent querying a semantic model."
## The Problem, Stated Plainly
AI coding agents consume source code as flat text. They see characters. Roslyn sees symbols, scopes, bindings, and relationships. The gap between those two views is where bugs, hallucinations, and wasted context windows live.
Consider what happens when an agent needs to understand `OrderService.CreateOrder`:
| Approach | Tokens | What The Agent Learns |
|----------|--------|-----------------------|
| `read_file` on `OrderService.cs` | ~800 | Raw text. No callers. No interface contract. Hope the agent infers the rest. |
| `read_file` on 4 related files | ~3,000+ | More text. Still no resolved types, no call graph, no inheritance chain. |
| `ast_details symbol=OrderService.CreateOrder` | ~140 | Full signature, resolved types, doc comments, location, semantic context. Done. |
That is not a rounding error. That is a 20x reduction in token cost with strictly more information. Architecture is trade-off. This one costs you nothing.
## On "Vibe Coding" (A Brief, Necessary Unpleasantness)
There is a fashionable movement where people type a prompt, accept whatever code the LLM emits, and ship it without reading it. They call this "vibe coding." They post about it with pride. They have podcasts.
Let me be direct: vibe coding is not engineering. It is not even programming. It is outsourcing your judgment to a statistical model and then faking confidence about the result. It is the software equivalent of signing a contract in a language you do not read because the vibes felt right.
The people who vibe code ship `O(n³)` loops they cannot explain. They introduce security vulnerabilities they cannot identify. They create technical debt they cannot service because they do not understand the asset. When their vibe-coded application breaks at 3 AM, they open a new chat session and vibe-code a fix on top of the breakage. Turtles all the way down.
Semantic .NET MCP exists for the opposite reason. It exists because agents should understand code *better* than text-scanning allows, not worse. The entire architecture is built around giving agents the same semantic understanding that a senior engineer builds over years of working with a codebase: type relationships, call graphs, inheritance chains, accessibility boundaries, resolved generics. When an agent has that understanding, it writes better code. It catches its own mistakes. It knows what will break downstream before making a change.
Vibe coders want the agent to go fast. We want the agent to go *right*. Speed without understanding is just generating bugs at scale.
If you are building with Semantic .NET MCP, you are building for agents that reason about code using compiler-grade semantic context. You are building for engineering teams that review, test, and understand what ships. You are building for people who think "it works on my machine" is the beginning of a conversation, not the end of one.
If you are looking for tools to help you accept LLM output faster without reading it, you are in the wrong repository. We wish you luck. You will need it.
## Requirements
- [.NET 10 SDK](https://dotnet.microsoft.com/download) or later
- A .NET solution (`.sln` or `.slnx`) in the working directory (auto-discovered) or specified explicitly
- A machine with enough RAM for Roslyn to do its thing (16GB recommended for solutions over 500 files)
## Getting Started
### Installation
**Option 1: Install as Global Tool (Recommended)**
```bash
# Clone, build, pack and install
git clone https://github.com/dotnet-semantic-mcp/dotnet-semantic-mcp.git
cd dotnet-semantic-mcp
dotnet build -c Release
dotnet pack src/DotnetSemanticMcp -c Release -o ./nupkg
dotnet tool install --global --add-source ./nupkg DotnetSemanticMcp
# Verify installation
dotnet-semantic-mcp --version
```
**Option 2: Run via dotnet run**
```bash
dotnet build
dotnet test # 265 tests. All passing.
```
### CLI Usage
```bash
# From any directory containing a .NET solution
cd /path/to/your/solution
# Global tool (after installation)
dotnet-semantic-mcp status
dotnet-semantic-mcp search "CreateOrder"
dotnet-semantic-mcp map --map-type solution
dotnet-semantic-mcp details --target OrderService.CreateOrder
# Or via dotnet run
dotnet run --project /path/to/dotnet-semantic-mcp/src/DotnetSemanticMcp -- search "CreateOrder"
```
See the [full CLI reference](docs/usage/cli.md) for all commands.
### MCP Server Mode
Configure in your AI client ([full setup guide](docs/usage/mcp-setup.md)):
**Using global tool:**
```json
{
"mcpServers": {
"ast": {
"command": "dotnet-semantic-mcp",
"cwd": "/path/to/your/repo"
}
}
}
```
**Using dotnet run:**
```json
{
"mcpServers": {
"ast": {
"command": "dotnet",
"args": ["run", "--no-build", "--project", "/path/to/dotnet-semantic-mcp/src/DotnetSemanticMcp"],
"cwd": "/path/to/your/repo"
}
}
}
```
> `cwd` sets the working directory — the server recursively discovers `.sln`/`.slnx` files from there. VS Code extensions can use `"cwd": "${workspaceFolder}"`.
> Solution files are auto-discovered **recursively** from the working directory (skipping `bin`, `obj`, `node_modules`, `.git`, etc.). All `.sln`/`.slnx` files found are loaded — no `--solution` flag needed. To override: pass `--solution path.sln` or set `DotnetSemanticMcp:SolutionPath`.
> 📖 See the [Getting Started Guide](docs/getting-started.md) for a complete walkthrough.
## MCP Surface Area
### Tools (v1.0 — Core Analysis)
| Tool | What It Does |
|------|-------------|
| [`ast_search`](docs/specs/tools-and-resources.md#31-ast_search--find-symbols-by-namepattern) | Symbol lookup by name, pattern, or glob. Index-backed, sub-100ms. |
| [`ast_map`](docs/specs/tools-and-resources.md#32-ast_map--structural-visualization) | Structural maps: solution, project, namespace, type, method, file. |
| [`ast_details`](docs/specs/tools-and-resources.md#33-ast_details--deep-inspection) | Full AST for a symbol: members, doc comments, resolved types, flow analysis. |
| [`ast_chunk`](docs/specs/tools-and-resources.md#34-ast_chunk--source-retrieval-by-location) | Source retrieval by file + line range. The escape hatch for exact syntax. |
| [`ast_references`](docs/specs/tools-and-resources.md#35-ast_references--reference--call-graph-queries) | Call graph: `callers_of`, `callees_of`, `references_to`. Cross-project. |
| [`ast_hierarchy`](docs/specs/tools-and-resources.md#36-ast_hierarchy--type-hierarchy-queries) | Type hierarchy: `implementations_of`, `base_types_of`, `overrides_of`, `extensions_for`. |
| [`ast_diagnostics`](docs/specs/tools-and-resources.md#37-ast_diagnostics--code-health) | Roslyn compiler diagnostics. Warnings, errors, code analysis. |
### Tools (v2.0 — Extended Analysis)
| Tool | What It Does |
|------|-------------|
| [`ast_dependencies`](docs/specs/tools-and-resources.md#38-ast_dependencies--project-dependency-analysis) | Project dependency graphs, NuGet references, circular detection, build order. |
| [`ast_attributes`](docs/specs/tools-and-resources.md#39-ast_attributes--attributedecorator-queries) | Query attributes/decorators: find usages, get attributes on symbol, group by category. |
| [`ast_diff`](docs/specs/tools-and-resources.md#310-ast_diff--semantic-code-comparison) | Semantic code comparison with breaking change detection. Powered by DiffPlex. |
| [`ast_diff_unified`](docs/specs/tools-and-resources.md#311-ast_diff_unified--generate-unified-diff) | Generate unified diff output (git-like format). |
### Resources (v1.0)
| URI | Description |
|-----|-------------|
| `ast:///solution/map` | Solution topology: projects, dependencies, frameworks |
| `ast:///project/{name}/map` | Project-level type map |
| `ast:///file/{path}` | File-level declaration summary |
| `ast:///workspace/status` | Loading progress, readiness, per-project status |
### Prompts (v1.0)
| Prompt | Purpose |
|--------|---------|
| `use_ast_for_code_reading` | System prompt that trains the agent to reach for AST tools instead of `read_file` |
> 📖 Full tool contracts, parameters, and examples: [Tools & Resources Spec](docs/specs/tools-and-resources.md)
> 📖 Recommended workflows: [Workflow Patterns](docs/usage/workflow-patterns.md)
## Architecture
Two modes of operation, same engine underneath:
| Mode | Transport | Lifecycle |
|------|-----------|-----------|
| **MCP Server** | stdio | Persistent. File watcher. Concurrent queries via snapshot isolation. |
| **CLI** | stdout | One-shot. Load, compile, query, print, exit. |
```
Program.cs ─── mode detection
├── MCP Mode ──→ Tools/ + Resources/ + Prompts/
└── CLI Mode ──→ Cli/CliRunner.cs
↓
Query/QueryEngine.cs
↓
Workspace/WorkspaceManager.cs ──→ Roslyn MSBuildWorkspace
↓
Projection/ ──→ TOON (default) or JSON
```
Roslyn `Compilation` objects are immutable. Queries run concurrently on the same snapshot. File changes build a new `Compilation` and swap atomically. No locks. No torn reads.
> 📖 Full architecture deep-dive: [Architecture](docs/architecture.md)
> 📖 Concurrency model, snapshot strategy, DI: [Runtime Behavior Spec](docs/specs/runtime-behavior.md)
## Roadmap
**v1.0** ✅ **Released**: Seven tools, four resources, one prompt, 265 tests. Read-only analysis. CLI mode. Global dotnet tool packaging. The foundation.
**v2.0** ✅ **Released**: Four new tools, all read-only:
- `ast_dependencies` — Project dependency graphs, circular detection, NuGet version analysis
- `ast_diff` — Semantic code comparison with DiffPlex integration (understands breaking vs. cosmetic changes)
- `ast_diff_unified` — Generate unified diff output in git-like format
- `ast_attributes` — Query decorators/annotations for DI, APIs, serialization understanding
**v3.0 — Extended Analysis** 📋 **Planned**: Still read-only, external source access:
- `ast_complexity` — Cyclomatic/cognitive complexity metrics, hotspot analysis
- `ast_sourcelink` — Resolve source code for NuGet packages via SourceLink
- `ast_decompile` — IL decompilation for closed-source dependencies (ILSpy)
**v4.0 — Code Modification** 🔮: The read-only constraint lifts, carefully:
- Git integration FIRST (auto-commit, undo, branches) as safety net
- `ast_rename` — Solution-wide rename (Roslyn's proven API)
- `ast_add_member` — Add methods/properties to classes
- Later: `ast_overwrite_member`, `ast_delete_member` (requires mature Git integration)
> 📖 Detailed specifications for v2/v3 tools: [v2-v3 Tools Spec](docs/specs/v2-v3-tools.md)
## Documentation
### Guides
| Document | What It Covers |
|----------|---------------|
| [Getting Started](docs/getting-started.md) | Prerequisites, installation, quick start with MCP and CLI |
| [MCP Setup](docs/usage/mcp-setup.md) | Client configuration for Claude Desktop, VS Code, and other MCP clients |
| [CLI Reference](docs/usage/cli.md) | All 8 subcommands, options, piping, scripting |
| [Workflow Patterns](docs/usage/workflow-patterns.md) | Progressive disclosure, investigation flows, real-world scenarios |
| [Configuration](docs/configuration.md) | All settings, environment variables, per-mode behavior |
| [TOON Format](docs/toon-format.md) | Grammar rules, conversion pipeline, JSON comparison, examples |
| [Architecture](docs/architecture.md) | Layers, components, data flow, concurrency, DI, project structure |
| [Troubleshooting](docs/troubleshooting.md) | Common issues, error codes, diagnostic steps, solutions |
### Specifications
| Document | What It Covers |
|----------|---------------|
| [Conceptual Spec](docs/specs/conceptual.md) | Problem, vision, architecture overview, scope, design decisions log |
| [Tools & Resources](docs/specs/tools-and-resources.md) | Full tool contracts: inputs, outputs, validation, pagination, annotations |
| [v2-v3 Tools Spec](docs/specs/v2-v3-tools.md) | Detailed specs for upcoming zero-risk tools: dependencies, diff, attributes, complexity, sourcelink, decompile |
| [Data Models](docs/specs/data-models.md) | Error model, source location, output schemas, JSON→TOON mapping |
| [Runtime Behavior](docs/specs/runtime-behavior.md) | Concurrency, startup lifecycle, incremental updates, memory, security |
| [Implementation](docs/specs/implementation.md) | TOON grammar, config reference, CLI spec, class diagrams, DI registration |
| [Testing](docs/specs/testing.md) | Test strategy, fixture design, sample solution, coverage targets |
## License
See [LICENSE](LICENSE).
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available