Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<RepositoryUrl>https://github.com/csa7mdm/DotNetDevMCP</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>mcp;mcp-server;model-context-protocol;dotnet;csharp;roslyn;testing;build;ai;agents</PackageTags>
<Description>MCP server for .NET: Roslyn code navigation and refactoring, build, and affected-test selection.</Description>
<Description>DotNetDevMCP is an open-source MCP server that gives AI coding agents Roslyn's compiler view of a .NET solution.</Description>

<!-- Documentation -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down
6 changes: 6 additions & 0 deletions docs/_config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
title: "DotNetDevMCP"
description: "DotNetDevMCP is an open-source MCP server that gives AI coding agents Roslyn's compiler view of a .NET solution."
url: "https://csa7mdm.github.io"
baseurl: "/DotNetDevMCP"
plugins:
- jekyll-sitemap
305 changes: 305 additions & 0 deletions docs/articles/polly-benchmark/index.html

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions docs/docs/_nav.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
**[Home](index.md)**

**Get started**
- [Installation](installation.md)
- [Tutorial](tutorial.md)

**Use**
- [Tools Reference](tools-reference.md)
- [Affected Tests](affected-tests.md)
- [Configuration](configuration.md)
- [Troubleshooting](troubleshooting.md)
- [Security](security.md)

**Project**
- [Benchmarks](benchmarks.md)
- [Architecture](architecture.md)
- [Contributing](contributing.md)


[NuGet](https://www.nuget.org/packages/DotNetDevMCP) · [Issues](https://github.com/csa7mdm/DotNetDevMCP/issues) · [Discussions](https://github.com/csa7mdm/DotNetDevMCP/discussions)
95 changes: 95 additions & 0 deletions docs/docs/affected-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: "Affected tests"
description: "dotnet_test_affected answers \"which tests can my change break?\" with the compiler, then runs only those tests."
---
# Affected tests

`dotnet_test_affected` answers "which tests can my change break?" with the compiler, then runs only those.

![How affected tests are chosen](../images/affected-tests.svg)

## How it decides

```mermaid
sequenceDiagram
participant Agent
participant Tool as dotnet_test_affected
participant Roslyn
participant CLI as dotnet test
Agent->>Tool: changed files (or git)
Tool->>Roslyn: symbols declared in those files
loop up to maxDepth hops, within maxSelectionSeconds
Tool->>Roslyn: FindReferences(symbol)
Roslyn-->>Tool: the members that use it
end
Tool->>Tool: which of them are test methods?
alt finished, and at most maxSelectedFraction of all tests
Tool->>CLI: one run per test project, filtered to those tests
else out of budget, or too many tests
Tool->>Tool: which test projects reference the changed projects?
Tool->>CLI: only those test projects (or the whole solution if that's all of them)
end
CLI-->>Tool: TRX results
Tool-->>Agent: results, the reason for each selected test (via), and what was run
```
<!-- mermaid: rendered on GitHub; see the SVG diagrams for the Pages version -->


1. **Changed files**: from `changedFiles`, or from git (uncommitted changes, or `gitBase: "main"` for a branch).
2. **Symbols**: everything the files declare that can run: methods, properties, constructors. A type or field hops through
its constructors (field initializers run there) and, for fields, through everything that reads them. That's how xUnit
`[MemberData]` theories are found.
3. **Reference walk**: Roslyn finds every member that uses those symbols, then every member using *those*, up to
`maxDepth` hops (default 8).
4. **Test methods**: members with `[Fact]`, `[Theory]`, `[Test]`, `[TestCase]`, `[TestCaseSource]`, `[TestMethod]` or
`[DataTestMethod]`.

## When it doesn't filter by test name

| Situation | Why | What runs |
|---|---|---|
| The walk didn't finish within `maxSelectionSeconds` (10) | The change touches code nearly everything depends on; tracing it costs more than running tests | The test projects that reference the changed projects |
| The selection is more than `maxSelectedFraction` (20%) of all test methods | A filtered run that large is no faster than running those projects whole (measured on Polly) | Same |
| A changed file isn't compiled C# (`.csproj`, `.razor`, `appsettings.json`, resources, a deleted `.cs` file) | The reference walk can't see it, but it can still break tests | The test projects that reference the project folder holding it; the file is listed in `untracedFiles` |
| A build-wide file changed (`.props`, `.targets`, `global.json`, `nuget.config`, `.editorconfig`) | It can affect every project | The whole solution |
| Every test project is reachable | Nothing to narrow | The whole solution, in one run |

The response says what ran and why: `ranScope` is `selection`, `projects` or `solution`, `testProjectsRun` lists the projects,
and `note` explains the reason. A partial selection is never presented as complete. The project step follows project
references only: a test project that uses the changed code through a NuGet package reference isn't found. On a solution
where every test project uses the changed library (Polly and `Polly.Core`), this still runs everything; it narrows when
modules are independent.

## Parameters

| Parameter | Default | Meaning |
|---|---|---|
| `changedFiles` | git working tree | Files to start from |
| `gitBase` | - | Diff against this ref instead, e.g. `main` |
| `dryRun` | false | Only list the tests and why |
| `maxDepth` | 8 | Reference hops. `3` narrows more changes but misses tests reached through long call chains |
| `maxSelectionSeconds` | 10 | Time budget for the walk |
| `maxSelectedFraction` | 0.2 | Above this share of all tests, run everything |
| `framework` | all | Run one target framework, e.g. `net10.0` |
| `noBuild` | false | Skip building the affected test projects |
| `timeoutSeconds` | 600 | Kill the run if a test hangs; the response names the modules that never finished |

## What it can't see

- **Reflection** (`Activator.CreateInstance`, `GetMethod`, `BindingFlags`), **string-keyed lookups**, and **DI by
convention** (assembly scanning). A test that reaches your code only that way won't be selected. On Polly this was the
one miss out of 112 broken tests.
- **Documentation and files outside every project** (`*.md`, images, CI workflows) are ignored: they can't break a test.
- Calls through an **interface or base class** *are* followed: changing `OrderService.Submit` selects a test that only calls
`IOrderService.Submit`.
- **Hanging tests** are killed after `timeoutSeconds` and reported by module, not by test name (VSTest runs do name the test).
- **The first selection of a session** on busy code can hit the time budget and run everything; Roslyn caches what it
learned, so the same selection is fast the next time.

## Test frameworks

VSTest (`dotnet test` classic) and Microsoft.Testing.Platform (`"test": { "runner": "Microsoft.Testing.Platform" }` in
global.json) are both supported, with xUnit v3 detected automatically and MSTest/NUnit handled through `--filter`.
DotNetDevMCP runs `dotnet` from your project's directory, so your `global.json` (SDK version, test runner) applies.

Numbers: [Benchmarks](benchmarks.md).
70 changes: 70 additions & 0 deletions docs/docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: "Architecture"
description: "A map of DotNetDevMCP's projects and how a tool call flows through them, for contributors."
---
# Architecture

A map for contributors. The code is in [csa7mdm/DotNetDevMCP](https://github.com/csa7mdm/DotNetDevMCP).

## Projects

```mermaid
flowchart TB
Server["DotNetDevMCP.Server<br/>CLI options, tool registration, stdio / HTTP"]
CI["CodeIntelligence<br/>Roslyn workspace, SharpTool_* (fork of SharpTools)"]
Testing["Testing<br/>dotnet test runner, TRX parsing, affected-test selection"]
Build["Build<br/>dotnet build, restore, clean"]
Analysis["Analysis<br/>dependencies, quality"]
Orch["Orchestration<br/>ConcurrentExecutor, WorkflowEngine, ResourceManager"]
SC["SourceControl<br/>git tools (opt-in)"]
Mon["Monitoring<br/>process metrics (opt-in)"]
Core["Core<br/>shared interfaces and models"]

Server --> CI & Testing & Build & Analysis & Orch
Server -. "--enable git" .-> SC
Server -. "--enable monitoring" .-> Mon
Testing --> CI
CI & Testing & Orch --> Core
```
<!-- mermaid: rendered on GitHub; see the SVG diagrams for the Pages version -->


| Project | Start reading at |
|---|---|
| Server | `Program.cs`: options and `AddServices`, the one place every tool is registered |
| CodeIntelligence | `Services/SolutionManager.cs` (loading), `Mcp/Tools/AnalysisTools.cs` (find references), `Services/CodeModificationService.cs` (edits) |
| Testing | `TestRunner.cs` (VSTest and Microsoft.Testing.Platform), `AffectedTestFinder.cs` (the reference walk), `Mcp/Tools/TestingTools.cs` |
| Orchestration | `WorkflowEngine.cs`, `ConcurrentExecutor.cs`, `Mcp/Tools/OrchestrationTools.cs` |

## A tool call, end to end

```mermaid
sequenceDiagram
participant Client as MCP client
participant Server as DotNetDevMCP (stdio)
participant Tool as tool method
participant Ext as Roslyn / dotnet / git
Client->>Server: tools/call {name, arguments} (JSON-RPC on stdin)
Server->>Tool: bind arguments, inject services
Tool->>Ext: in-process Roslyn call, or a child process
Ext-->>Tool: result
Tool-->>Server: object (serialized to JSON)
Server-->>Client: result (stdout)
```
<!-- mermaid: rendered on GitHub; see the SVG diagrams for the Pages version -->


Two consequences of stdio that every contributor should know:

- **stdout is the protocol.** Logs go to stderr. Child processes get their own redirected stdout and a closed stdin;
otherwise they read from the MCP connection and hang.
- **Tool parameters are arrays.** The MCP C# SDK tries to resolve `IEnumerable<T>` parameters from dependency injection;
use `string[]`.

## Design decisions

Architecture decision records are in [docs/architecture/adr](https://github.com/csa7mdm/DotNetDevMCP/tree/main/docs/architecture/adr),
starting with why the Roslyn tools are a fork of [SharpTools](https://github.com/kooshi/SharpToolsMCP) rather than a
package reference.

How affected tests are chosen: [Affected Tests](affected-tests.md). Next: [Contributing](contributing.md).
35 changes: 35 additions & 0 deletions docs/docs/benchmarks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
title: "Benchmarks"
description: "Measured on Polly: affected-test selection speed, fault-injection recall and reference answer size compared with grep."
---
# Benchmarks

Measured on [Polly](https://github.com/App-vNext/Polly) (801 C# files, test projects multi-targeted to net8/9/10, 12,262
test executions, xUnit v3 on Microsoft.Testing.Platform) with the packaged server, driven over stdio the way an agent calls
it. Full method, scripts and raw data: [benchmarks/polly](https://github.com/csa7mdm/DotNetDevMCP/blob/main/benchmarks/polly/README.md).

![Benchmark on Polly](../images/benchmark-polly.svg)

## Headline numbers

| Question | Result |
|---|---|
| How fast is a small change's test run? | 5 affected tests in 5.1 s, against 48.1 s for the full net10.0 suite (same session) |
| Is the selection safe? | Faults injected into 10 real methods: selections included 111 of the 112 tests the faults broke. The miss uses reflection |
| How often does selection help? | Of Polly's last 40 commits: 16 ran a filtered selection, 24 ran the full suite (broad changes, or out of the 10 s budget) |
| "Where is X used?" versus grep | `CancellationToken`: 137 real references in 6.5 KB, against 1,354 grep lines in 202 KB |

## What the numbers don't say

- Changes that reach hundreds of tests gain nothing. The tool runs the full suite for them on purpose.
- Absolute times varied a lot between sessions on the test laptop (the full net10.0 suite took 33 s in one, 48 s in
another). Compare within a session.
- One repository, one machine. Numbers from your solution are very welcome in
[Discussions](https://github.com/csa7mdm/DotNetDevMCP/discussions).

## What benchmarking changed

Running on Polly found six problems in 0.2.x: runs failed on every Microsoft.Testing.Platform repository, the repository's
`global.json` was ignored, selection never finished on multi-targeted solutions, `[MemberData]` tests were missed,
reference counts were inflated once per target framework, and big selections were slower than running everything. All
fixed in 0.3.0. One improvement (a background warm-up) measured no effect and was removed.
45 changes: 45 additions & 0 deletions docs/docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
title: "Configuration"
description: "DotNetDevMCP server options: loading a solution at startup, enabling git and monitoring tools, HTTP mode, logging and --clean-env."
---
# Configuration

Server options go after `--` in a `dnx` command, or straight after `dotnetdevmcp` for a global install:

```bash
dotnet dnx DotNetDevMCP --yes -- --load-solution MyApp.sln --enable git
```

| Option | Default | What it does |
|---|---|---|
| `--load-solution <path>` | - | Load a `.sln` or `.slnx` at startup, so the agent doesn't need `SharpTool_LoadSolution` first |
| `--build-configuration <Debug\|Release>` | - | Configuration used when loading the solution |
| `--enable <groups>` | none | Turn on optional tool groups: `git` (10 tools), `monitoring` (6). Comma-separated or repeated: `--enable git,monitoring` |
| `--clean-env` | off | Start `dotnet` and `git` with a minimal environment (PATH, temp and profile folders, `DOTNET_*`, `NUGET_*`, `MSBUILD*` and the like) instead of inheriting yours, so tokens, API keys and cloud credentials in environment variables aren't passed on. Not a sandbox: see [Security](security.md) |
| `--git-commit-edits` | off | Edit tools create a `sharptools/<timestamp>` branch and commit each change; enables `SharpTool_Undo` |
| `--http` | off | Serve Streamable HTTP instead of stdio |
| `--port <n>` | 3001 | Port for `--http` |
| `--log-level <level>` | Information | `Verbose`, `Debug`, `Information`, `Warning`, `Error`, `Fatal` |
| `--log-directory <dir>` | - | Also write rolling log files there. Logs always go to stderr, never stdout (stdout is the MCP channel) |
| `--version` | | Print the version |
| `--disable-git` | | Deprecated, has no effect |

## Why git and monitoring are off by default

Every tool the server registers is described to the agent in every session, which costs context tokens. An agent already
has a shell for `git status` and `git commit`, and the monitoring tools describe the server process, not your code. So
they're opt-in. Enable them if your client has no shell, or if you want the agent to stick to MCP tools.

## Per-call options

Test and build behavior is set per call, by the agent, through tool parameters: for example `framework`, `maxDepth` and
`timeoutSeconds` on the testing tools, `verbose` on `dotnet_build`. See [Tools Reference](tools-reference.md) and
[Affected Tests](affected-tests.md). You can ask for them in plain language: *"run the affected tests, net10.0 only"*.

## HTTP mode

```bash
dotnetdevmcp --http --port 3001 --load-solution MyApp.sln
```

The MCP endpoint is `http://localhost:3001/`. It listens on localhost only and has no authentication, TLS or origin checks: don't expose it. See [Security](security.md).
51 changes: 51 additions & 0 deletions docs/docs/contributing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: "Contributing"
description: "Contributions of every size are welcome, and not only code: bug reports, docs fixes and runs on your own solution help too."
---
# Contributing

Contributions of every size are welcome, and not only code.

```mermaid
flowchart LR
Try[Run it on your solution] --> Report{Something off?}
Report -- yes --> Issue[Bug report or Discussion]
Report -- no --> Share[Share your numbers in Discussions]
Issue --> Fix[Fix it yourself?]
Fix --> PR[Pull request]
Idea[Idea for a tool] --> Feature[Feature request]
Docs[Something unclear here] --> Edit[Edit this wiki]
```
<!-- mermaid: rendered on GitHub; see the SVG diagrams for the Pages version -->


## Ways to help

1. **Try it on your own solution** and report what happened: large or multi-targeted solutions, and test suites on
Microsoft.Testing.Platform, NUnit, MSTest or TUnit are the most valuable. [Bug report](https://github.com/csa7mdm/DotNetDevMCP/issues/new?template=bug_report.yml),
or post your numbers in [Discussions](https://github.com/csa7mdm/DotNetDevMCP/discussions).
2. **Pick a [good first issue](https://github.com/csa7mdm/DotNetDevMCP/labels/good%20first%20issue).** Comment that you're
on it; the maintainer answers questions and reviews quickly.
3. **Edit this wiki.** New MCP client setup, a clearer tutorial step, a troubleshooting entry.
4. **Propose a tool.** [Feature request](https://github.com/csa7mdm/DotNetDevMCP/issues/new?template=feature_request.yml):
describe what your agent was trying to do and where it got stuck.

## Building and testing

```bash
git clone https://github.com/csa7mdm/DotNetDevMCP.git
cd DotNetDevMCP
dotnet build -c Release
dotnet test -c Release
```

To try your build from an MCP client, point the client at `src/DotNetDevMCP.Server/bin/Release/net10.0/dotnetdevmcp`
(`.exe` on Windows). The full guide, code style and pull request checklist are in
[CONTRIBUTING.md](https://github.com/csa7mdm/DotNetDevMCP/blob/main/CONTRIBUTING.md); the map of the code is on
[Architecture](architecture.md).

Changing how tests are selected or run? Re-run [benchmarks/polly](https://github.com/csa7mdm/DotNetDevMCP/blob/main/benchmarks/polly/README.md)
and put the before/after numbers in your pull request.

Everyone taking part follows the [Code of Conduct](https://github.com/csa7mdm/DotNetDevMCP/blob/main/CODE_OF_CONDUCT.md).
If the project saves you time, you can [sponsor its development](https://github.com/sponsors/csa7mdm).
42 changes: 42 additions & 0 deletions docs/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: "DotNetDevMCP"
description: "DotNetDevMCP is an open-source MCP server that gives AI coding agents Roslyn's compiler view of a .NET solution."
---
# DotNetDevMCP

DotNetDevMCP is an [MCP](https://modelcontextprotocol.io) server that gives AI coding agents the compiler's view of your .NET
solution. Instead of `grep` and guessing, your agent can find real references, rename a symbol across the solution with a
compile check, build, and run only the tests your change can break.

![How DotNetDevMCP works](../images/how-it-works.svg)

## Start here

| I want to... | Page |
|---|---|
| Install it in Claude Code, VS Code, Visual Studio, Cursor or Claude Desktop | [Installation](installation.md) |
| See what a first session looks like, step by step | [Tutorial](tutorial.md) |
| Know every tool and its parameters | [Tools Reference](tools-reference.md) |
| Understand how affected tests are chosen, and when it runs everything | [Affected Tests](affected-tests.md) |
| Change server options (`--load-solution`, `--enable`, HTTP mode...) | [Configuration](configuration.md) |
| Fix something that isn't working | [Troubleshooting](troubleshooting.md) |
| See real numbers on a real library | [Benchmarks](benchmarks.md) |
| Know what it can do on my machine, and how to limit it | [Security](security.md) |
| Understand the code before contributing | [Architecture](architecture.md) |
| Help out | [Contributing](contributing.md) |

## In one minute

```bash
claude mcp add dotnetdevmcp -- dotnet dnx DotNetDevMCP --yes
```

Then ask your agent: *"Load MySolution.sln and tell me where `OrderService.Submit` is used."*

Requires the [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0). Package: [nuget.org/packages/DotNetDevMCP](https://www.nuget.org/packages/DotNetDevMCP).
Also listed in the [MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.csa7mdm/dotnetdevmcp`.

## Something missing or wrong?

This wiki is open to edits: fix it directly. For bugs, [open an issue](https://github.com/csa7mdm/DotNetDevMCP/issues/new/choose);
for questions, use [Discussions](https://github.com/csa7mdm/DotNetDevMCP/discussions).
Loading
Loading