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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@
All notable changes to DotNetDevMCP are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow [SemVer](https://semver.org/).

## [0.3.2] - 2026-09-24

Security release, prompted by two external reviews; each claim was checked against the code first. Upgrade from 0.3.0/0.3.1.

### Security
- **Argument injection.** Tool values were concatenated into `dotnet`/`git` command lines, so a crafted value could add options:
an MSBuild property value like `1.0 -p:CustomBeforeMicrosoftCommonTargets=evil.targets` imported a targets file (code
execution during the build), a `gitBase` like `--output=...` made git write a file, and `framework`, branch and remote values
could add flags. Every child process now receives its arguments as a list (one value, one argument); framework, runtime,
configuration, MSBuild property names and git refs are validated; property values escape MSBuild's `;` and `,` list
separators; under Microsoft.Testing.Platform the `filter` may only carry `--filter*` / `--treenode-filter` options.
- **Path check.** Roslyn edit tools checked "inside the solution" with a plain string prefix, so `..` segments and look-alike
sibling folders (`App-other` for `App`) passed. Paths are now resolved before comparing.

### Added
- `--clean-env`: child processes get a minimal allow-listed environment (PATH, temp and profile folders, proxies, `DOTNET_*`,
`NUGET_*`, `MSBUILD*`...) instead of inheriting the server's, so tokens and cloud credentials in environment variables aren't
passed on. Not a sandbox. Startup logs how many variables were dropped; `--log-level Debug` lists their names.
- `dotnet_test_affected` project-level fallback: when the selection runs out of budget or is too large, it runs only the test
projects that reference the changed projects (`ranScope: "projects"`, `testProjectsRun`), not the whole solution. Helper
libraries that reference a test framework but declare no tests are skipped.
- Security model in SECURITY.md, the README and the [wiki](https://github.com/csa7mdm/DotNetDevMCP/wiki/Security).

### Fixed
- The VSTest name filter had no length limit (Windows caps command lines at 32K); it now widens to classes, then to no
filter, like the Microsoft.Testing.Platform path.
- `WorkflowEngine` no longer wraps already-async steps in `Task.Run`.

## [0.3.1] - 2026-09-24

Documentation and community release; no behavior changes.
Expand Down
4 changes: 2 additions & 2 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
<NoWarn>$(NoWarn);CS1591</NoWarn> <!-- Missing XML comment for publicly visible type or member -->

<!-- Version Information -->
<Version>0.3.1</Version>
<Version>0.3.2</Version>
<IsPackable>false</IsPackable>
<AssemblyVersion>0.2.0.0</AssemblyVersion>
<FileVersion>0.3.1.0</FileVersion>
<FileVersion>0.3.2.0</FileVersion>

<!-- Package Metadata -->
<Authors>Ahmed Mustafa</Authors>
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ claude mcp add dotnetdevmcp -- dnx DotNetDevMCP --yes

`dnx` downloads the package from NuGet.org on first run. Prefer a permanent install? `dotnet tool install -g DotNetDevMCP`, then use `dotnetdevmcp` as the command.

Pass `--load-solution <path>` to have Roslyn load your solution at startup, or let the agent call `SharpTool_LoadSolution` when it needs to. `--http --port 3001` serves Streamable HTTP instead of stdio. `dotnetdevmcp --help` lists everything.
Pass `--load-solution <path>` to have Roslyn load your solution at startup, or let the agent call `SharpTool_LoadSolution` when it needs to. `--http --port 3001` serves Streamable HTTP instead of stdio (localhost only, no authentication: see [Security](#security)). `--clean-env` starts `dotnet` and `git` with a minimal environment so tokens and cloud credentials in environment variables aren't passed on. `dotnetdevmcp --help` lists everything.

Git and Monitoring tools (see the table below) are off by default - a shell an agent already has covers them, and every registered tool costs context tokens in every session. Pass `--enable git,monitoring` (comma-separated and/or repeated, e.g. `--enable git --enable monitoring`) to turn either or both on.

Expand Down Expand Up @@ -136,10 +136,19 @@ On this repository, editing `ConcurrentExecutor.cs` selects 22 of 44 tests (the
| `dotnet_test_run` | 44 | 8.3 s |
| `dotnet_test_affected` (change to `ConcurrentExecutor.cs`) | 22 | 6.6 s |

The suite here is small, so the saving is small. On a real library the picture is clearer: [benchmarks/polly](https://github.com/csa7mdm/DotNetDevMCP/blob/main/benchmarks/polly/README.md) replays 40 Polly commits and injects faults into its code. A one-file change ran its 5 affected tests in 4.6 s against 33 s for the net10.0 suite, and the selections included 111 of the 112 tests the injected faults broke (the miss builds its object through reflection). Changes that reach hundreds of tests gain nothing: of the last 40 commits, 16 ran a filtered selection and 24 ran the full suite. The first selection of a session on busy code is slower (Roslyn binds the files it touches, then caches them). `dryRun: true` shows what it picked and why (`via`).
The suite here is small, so the saving is small. On a real library the picture is clearer: [benchmarks/polly](https://github.com/csa7mdm/DotNetDevMCP/blob/main/benchmarks/polly/README.md) replays 40 Polly commits and injects faults into its code. A one-file change ran its 5 affected tests in 5.1 s against 48.1 s for the net10.0 suite (same session), and the selections included 111 of the 112 tests the injected faults broke (the miss builds its object through reflection). Changes that reach hundreds of tests gain nothing: of the last 40 commits, 16 ran a filtered selection and 24 ran the full suite. The first selection of a session on busy code is slower (Roslyn binds the files it touches, then caches them). `dryRun: true` shows what it picked and why (`via`).

![Benchmark on Polly: 5 affected tests in 5.1 s against 48.1 s for the full suite; 6.5 KB of references against 202 KB of grep output](https://raw.githubusercontent.com/csa7mdm/DotNetDevMCP/main/docs/images/benchmark-polly.svg)

## Security

DotNetDevMCP runs as you, for an agent you trust with your code. `dotnet build` and `dotnet test` execute whatever the solution
contains, so a malicious test or `.csproj` runs with your privileges, exactly as it would in your terminal; the server adds no
sandbox. What it does guarantee: tool arguments can't smuggle extra options into `dotnet` or `git`, Roslyn edits stay inside the
solution directory, and `--clean-env` keeps secrets in environment variables away from child processes. For code you don't
trust, run the agent and the server in a container with no credentials. Don't expose `--http` beyond localhost. Details:
[SECURITY.md](https://github.com/csa7mdm/DotNetDevMCP/blob/main/SECURITY.md#security-model).

## Help, feedback and contributing

- **Questions, ideas, "is this a bug?"**: [Discussions](https://github.com/csa7mdm/DotNetDevMCP/discussions).
Expand Down Expand Up @@ -183,7 +192,7 @@ Built on the official [MCP C# SDK](https://github.com/modelcontextprotocol/cshar

## Status

0.3.1. The Roslyn tools are mature (they come from SharpTools). Testing, build, git and orchestration are newer and have been exercised on this repository and a few others; expect rough edges on unusual project layouts. Issues and PRs welcome, see [CONTRIBUTING](https://github.com/csa7mdm/DotNetDevMCP/blob/main/CONTRIBUTING.md).
0.3.2. The Roslyn tools are mature (they come from SharpTools). Testing, build, git and orchestration are newer and have been exercised on this repository and a few others; expect rough edges on unusual project layouts. Issues and PRs welcome, see [CONTRIBUTING](https://github.com/csa7mdm/DotNetDevMCP/blob/main/CONTRIBUTING.md).

Known gaps: `dotnet_test_affected` follows C# references only (no reflection, no DI-by-convention, no string-keyed lookups), so a change reached only through those paths will not select the test; use `dryRun` to check what it picks. Tests that hang instead of failing are only caught by a run that finishes. Test attribute detection covers xUnit, NUnit and MSTest by attribute name. Past the command-line length limit the filter widens from methods to classes, then to the whole project (more tests, never fewer).

Expand Down
75 changes: 33 additions & 42 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Currently, only the latest version of DotNetDevMCP is supported with security up

| Version | Supported |
| ------- | ------------------ |
| 0.3.x | :white_check_mark: |
| 0.3.2+ | :white_check_mark: |
| 0.3.0-0.3.1 | :x: (argument injection, weak path check: upgrade) |
| < 0.3 | :x: |

## Reporting a Vulnerability
Expand Down Expand Up @@ -44,57 +45,47 @@ We practice coordinated disclosure:
- Once a fix is ready, we will coordinate a release timeline
- We will publicly credit you for the discovery (unless you prefer to remain anonymous)

## Security Best Practices
## Security model

When using DotNetDevMCP:
DotNetDevMCP is a **local developer tool**. It runs as you, on your machine, for an AI agent you chose to trust with your
code. Read this before using it on code you don't trust or exposing it beyond your own machine.

1. **Keep Dependencies Updated**
- Regularly update to the latest version
- Monitor security advisories for .NET and dependencies
### What it can do on your machine

2. **Validate Input**
- Always validate and sanitize user input
- Be cautious with paths and file operations
- **Run code.** `dotnet build` and `dotnet test` execute whatever the solution contains: MSBuild targets (`<Exec>`), build
tasks, source generators, test code. If the agent (or text in the repository steering the agent, i.e. prompt injection)
writes a malicious test or `.csproj`, running the build runs it. This is true of `dotnet build` in any terminal; the
server adds no sandbox.
- **Read and write files.** Roslyn edit tools write only inside the loaded solution's directory (paths are normalized, so
`..` and look-alike sibling folders are rejected). Build, test and git tools accept any path you or the agent give them.
- **See your environment.** Child processes inherit the server's environment variables, including tokens and cloud
credentials, unless you start the server with `--clean-env`.

3. **Least Privilege**
- Run DotNetDevMCP with minimal required permissions
- Avoid running as administrator/root unless necessary
For a local agent that already has a shell (Claude Code, Cursor, Copilot agent mode), none of this is new power: the agent
could run the same commands itself. What the server guarantees is that its own tools don't widen that: arguments are passed
to `dotnet` and `git` as separate arguments and validated (framework, configuration, runtime, git refs), so a crafted value
can't add options such as `-p:CustomBeforeMicrosoftCommonTargets=...` or `--output=...`.

4. **Secure Configuration**
- Use secure defaults
- Review configuration for security implications
- Keep sensitive data (API keys, tokens) out of source control
### Options that reduce exposure

5. **Network Security**
- Use HTTPS for all network communications
- Validate SSL/TLS certificates
- Use secure authentication mechanisms
| Option | What it does | What it does not do |
|---|---|---|
| Default (git and monitoring tools off) | Fewer tools for the agent to misuse | - |
| `--clean-env` | Child processes get a minimal environment: tokens, API keys and cloud credentials in environment variables are not passed on | Not a sandbox: files such as `~/.aws/credentials` and the network are still reachable |
| Edits stay in the solution directory | Roslyn edit tools refuse paths outside it | Doesn't restrict what a build does |

## Known Security Considerations
### Untrusted code

### File System Access
DotNetDevMCP requires file system access to:
- Read source code files
- Execute build and test commands
- Write temporary files
For repositories you don't trust (a pull request from a stranger, a downloaded sample), run the agent and DotNetDevMCP inside a
container or VM with only that repository mounted, no credentials, and restricted network. The server cannot provide that
isolation itself.

**Mitigation**: Run in sandboxed environments when processing untrusted code.
### `--http` mode

### Code Execution
DotNetDevMCP executes:
- `dotnet build` commands
- `dotnet test` commands
- MSBuild scripts

**Mitigation**: Validate all inputs and use isolated build environments.

### Dependencies
DotNetDevMCP depends on:
- .NET Runtime
- Roslyn compiler
- Third-party NuGet packages

**Mitigation**: Regularly update dependencies and monitor for vulnerabilities.
HTTP mode listens on `localhost` only and has **no authentication, TLS or origin checks**. Anyone who can reach the port can
build, test and edit with your privileges. Don't forward the port, put it behind a proxy, or run it on a shared machine.
A multi-user or hosted deployment would need authentication, a sandbox per session and audit logging; DotNetDevMCP doesn't
provide those today.

## Security Updates

Expand Down
Loading
Loading