RoselineMCP
MCP serverDev toolsMCP server for C# code analysis and automated fixing using Roslyn analyzers and code fix providers.
Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.
Connect ahel once, and every AI you use reads what you have installed.
From the project's README
As published by atypical-consulting/roselinemcp in README.md.
Roslyn code intelligence for AI coding agents, over MCP. Give Claude, Cursor, and Copilot a semantic view of your C# solution — symbols, references, call graphs, surgical edits — so they navigate by structure instead of re-reading source. Measured 85% fewer tokens (median) →
📖 Documentation, tool reference & the honest benchmark →
Table of Contents
- Why RoselineMCP
- Quick Start
- Features
- Tech Stack
- Getting Started
- MCP Client Compatibility
- Available Tools
- Tool Annotations
- Tool Compatibility Policy
- Architecture
- Project Structure
- Compile guard
- Security
- Documentation
- Roadmap
- Contributing
- License
- Acknowledgments
Why RoselineMCP
Your coding agent shouldn't read a 700-line file to change one method. Source code dominates an agent's token budget, so the cheapest win is to stop feeding it whole files.
RoselineMCP wraps the Roslyn compiler platform as an MCP server. Instead of dumping source into the model, it answers structural questions precisely — where is this symbol used, what implements this interface, who calls this method, what's the shape of this file — and it edits surgically: a member-level diff, not a whole-file rewrite.
On RoselineMCP's own source, the read-only navigation tools returned a median 85% fewer tokens per task (pooled, size-weighted: 93%) than reading the corresponding files — measured honestly, weak cases included.
search_symbolsonProgram.cs: 2,093 tokens → 120 (−94%). The agent gets the shape of the file; you skip the wall.
Quick Start
Any MCP client that speaks dnx (the .NET equivalent of npx) runs it on demand — no install step.
Requires the .NET 10 SDK.
// claude_desktop_config.json · .vscode/mcp.json · ~/.cursor/mcp.json
{
"mcpServers": {
"roseline": { "command": "dnx", "args": ["RoselineMCP", "--yes"] }
}
}
Then ask your agent to "find every caller of OrderService.Checkout" or "rename Foo to Bar
across the solution." Prefer a pinned NuGet install or Docker? See
Getting Started.
Features
- Token-efficient code navigation -- symbols, references, call graphs, type hierarchies, and file outlines via Roslyn instead of whole files. A measured 85% median token reduction per task (93% pooled, size-weighted) -- see the benchmark.
- Surgical code edits -- replace/add/delete a member or rename a symbol solution-wide, emitting a unified diff instead of a whole-file rewrite. Preview by default.
- Comprehensive analysis & auto-fix -- diagnostics across a solution (Roslyn + Roslynator) with automated fixes and reviewable patches.
- Read-only by default -- the seven navigation tools and the diagnostics/patch tools never touch disk; the three write tools require an explicit
previewOnly: false. - Compile guard (opt-in) -- a
PostToolUsehook that puts the compiler's verdict behind every file write, not just RoselineMCP's own -- see Compile guard below. - Works with your client -- Claude Desktop, VS Code (Copilot / MCP), Cursor. Install via
dnx, NuGet global tool, or Docker. - Honest, reproducible benchmark -- run it against your own solution:
dotnet run --project RoselineMCP.TokenBenchmark -c Release.
Tech Stack
| Layer | Technology |
|---|---|
| Runtime | .NET 10.0 |
| Compiler Platform | Roslyn (Microsoft.CodeAnalysis) 5.6.0 |
| Analyzers | Roslynator 4.15.0 |
| MCP SDK | ModelContextProtocol 2.2.0 |
| Diff Engine | DiffPlex 1.9.0 |
| Build System | MSBuild 18.8.2 |
| Hosting | Microsoft.Extensions.Hosting 10.0.10 |
Versions above are kept in sync with
RoselineMCP/RoselineMCP.csproj— that file is the source of truth if this table ever drifts.
Getting Started
Prerequisites
- NuGet global tool: .NET 10.0 SDK or later
- Docker: Docker Desktop or Docker Engine
- Build from source: .NET 10.0 SDK + MSBuild (included with Visual Studio or .NET SDK)
- MCP client: Claude Desktop or any MCP-compatible client
Installation
Claude Desktop, one click: download
RoselineMCP.mcpbfrom the latest release and open it — Claude Desktop shows an install dialog, no config editing. (It launches viadnxunder the hood, so the .NET 10 SDK is still required.) Prefer to edit config yourself, or using another client? Use one of the options below.
Option 1 -- dnx (no install step) (recommended)
RoselineMCP ships an MCP server registry manifest, so any MCP client that
understands the dnx launcher (the .NET equivalent of npx — resolves and runs a NuGet-packaged
tool on demand, without a separate dotnet tool install step) can start it directly. Requires the
.NET 10.0 SDK.
{
"mcpServers": {
"roseline": {
"command": "dnx",
"args": ["RoselineMCP", "--yes"]
}
}
}
Add this to your Claude Desktop or VS Code MCP configuration (see
MCP Client Compatibility below for exact file locations per client).
dnx downloads and caches the tool on first use, so there's nothing to pre-install globally.
Option 2 -- NuGet Global Tool (offline / pinned-version installs)
Requires .NET 10.0 SDK or later.
dotnet tool install -g RoselineMCP
After installation, the roseline-mcp command is available globally.
Claude Desktop configuration (NuGet global tool)
Add to your Claude Desktop configuration file (claude_desktop_config.json):
{
"mcpServers": {
"roseline": {
"command": "roseline-mcp"
}
}
}
Config file location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json- Windows:
%APPDATA%\Claude\claude_desktop_config.json
Option 3 -- Docker
No SDK required. Works on any platform with Docker installed.
docker run -i --rm phmatray/roseline-mcp:latest
Claude Desktop configuration (Docker)
{
"mcpServers": {
"roseline": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"phmatray/roseline-mcp:latest"
]
}
}
}
Note: The
-iflag is required for stdio transport. The--rmflag removes the container after the session ends.
Option 4 -- Build from Source
git clone https://github.com/Atypical-Consulting/RoselineMCP.git
cd RoselineMCP
dotnet build
dotnet test
Claude Desktop configuration (build from source)
{
"mcpServers": {
"roseline": {
"command": "dotnet",
"args": ["run", "--project", "/path/to/RoselineMCP/RoselineMCP.csproj"]
}
}
}
MCP Client Compatibility
RoselineMCP speaks plain stdio MCP, so it should work with any MCP-compatible client. The snippets below are documented, not independently verified in every case — we've confirmed the protocol-level behavior (stdio transport, tool discovery, JSON responses) works correctly, but we have not personally exercised each client's own configuration UI/file end to end. If one of these doesn't work as written for your client version, please open an issue.
Edit claude_desktop_config.json (see file locations under Installation
above) and add a roseline entry under mcpServers, using any of the four install options shown
above (dnx, global tool, Docker, or build-from-source).
Add an entry to your workspace or user mcp.json (Command Palette → "MCP: Open User
Configuration", or .vscode/mcp.json in the workspace):
{
"servers": {
"roseline": {
"command": "dnx",
"args": ["RoselineMCP", "--yes"]
}
}
}
Substitute "command": "roseline-mcp" (no args) if you installed via the NuGet global tool
instead.
Add a roseline entry to ~/.cursor/mcp.json (global) or .cursor/mcp.json (project-local),
using the same command/args shape as the VS Code snippet above.
Available Tools
Every tool's description — the text an MCP client shows the model at tool-selection time — states
its own Limitations: and shows one Example: call, within a test-enforced word ceiling; the
twelve tools with an optional project share one wording for the worktree caveat below, and
resolvedPath is how you confirm which checkout actually answered. See
docs/API.md § Tool description contract.
1. AnalyzeSolution
Analyzes an entire C# solution for diagnostics. Read-only — never modifies files on disk.
pathOrGit also accepts an http(s):// Git URL, which is shallow-cloned to a temp directory,
analyzed, and deleted afterward.
analyzeSolution({
pathOrGit: "/path/to/solution.sln",
include: "Core", // Optional: only project names containing this substring
exclude: "Test", // Optional: skip project names containing this substring
severity: "warning", // Optional: minimum severity (Error|Warning|Info|Hidden)
maxDiagnostics: 100 // Optional: Maximum diagnostics to return (default: 100)
})
Returns: solution file name, project count, a diagnosticSummary (counts by severity), a
topDiagnostics array with project/file/line/column/id/severity/message per diagnostic, and
analyzerLoad — present only when an analyzer reference contributed nothing (merged across the
analyzed projects), naming it and why.
2. ListDiagnostics
Gets detailed diagnostics for a specific project. Read-only — never modifies files on disk.
project is optional and accepts the same references as the navigation tools (name,
directory, .csproj, or .sln path); when omitted, the solution/project is auto-discovered from
the working directory.
listDiagnostics({
project: "MyProject.csproj", // Optional: name, directory, .csproj, or .sln; auto-discovered if omitted
ids: ["CS0168", "CS0219"], // Optional: Filter by diagnostic IDs
files: ["Controller.cs"], // Optional: substring match against each diagnostic's file path (case-insensitive; NOT a glob pattern)
max: 50 // Optional: Maximum results
})
Returns: project name, resolvedPath (the absolute .sln/.csproj actually loaded),
totalDiagnostics count, the filtered diagnostics list, stats (counts grouped by ID and by
severity), suggestedFixableIds — diagnostic IDs a code fix provider is actually registered for,
whether it ships with Roslyn, in the bundled Roslynator catalog, or inside one of the project's
own analyzer references — and analyzerLoad, which names every analyzer reference that
contributed nothing (and why), present only when there is something to say.
3. ApplyFixes
Applies automated code fixes for specified diagnostics. Defaults to preview mode:
previewOnly defaults to true, so calling this tool without setting it never writes to disk —
you must pass previewOnly: false explicitly to apply changes. project is optional and
accepts the same references as the navigation tools (name, directory, .csproj, or .sln path);
when omitted, the solution/project is auto-discovered from the working directory.
applyFixes({
ids: ["CS0168", "RCS1001"], // Diagnostic IDs to fix
project: "MyProject.csproj", // Optional: name, directory, .csproj, or .sln; auto-discovered if omitted
previewOnly: false // Optional (default: true). Set false to write changes to disk.
})
Returns: project name, resolvedPath (the absolute .sln/.csproj actually loaded),
fixedCount, fixersApplied (diagnostic IDs actually fixed), changedFiles
(relative to resolvedPath's directory, forward slashes — the same path base as the navigation
tools), a unified
diff patch, notes (the scope — which project was fixed and which of the solution's projects were
not analyzed, plus any linked file whose write reaches a sibling — and skipped/failed IDs and status
messages), previewOnly echoing back what the caller asked for, applied (whether anything
actually reached disk), verification — the compiler's verdict on the fixed code — and
analyzerLoad (present only when an analyzer reference contributed nothing, so "no diagnostics
found for X" can be told apart from "the analyzer that reports X never loaded"). Fixers are looked
up in the Roslyn built-ins, then the bundled Roslynator catalog, then the project's own analyzer
references; the bundled provider wins for an ID both carry.
A .sln target fixes one project — its primary project — and that scope is enforced on the
write path, not only announced by the confirmation prompt: only the anchor project's documents are
verified and written, and a caller who never sees a prompt (preview, a non-eliciting client, an
unattended host) still reads which projects were skipped in notes. Pass a .csproj to fix a
specific project — then nothing is reported as skipped, since that is what was asked for.
14. CheckCompilation
Answers "does this compile right now, and what broke" against on-disk state — the replacement
for a dotnet build round trip in an agent's edit loop. Read-only, and compiler diagnostics only:
analyzers cost several times a bare compile, which is the whole reason this tool is fast enough to
run after every edit.
It reports on whatever is on disk, whoever wrote it, so it works just as well for edits made by
other tools. The speed comes from the warm MSBuildWorkspace the server already holds: the first
call of a session pays a cold load, every call after it reuses an incremental Roslyn compilation.
checkCompilation({
project: "MyApp.sln", // Optional: name, directory, .csproj, or .sln; auto-discovered if omitted
max: 20 // Optional (default: 20). The rest are counted in `omitted`.
})
Returns: resolvedPath, compiles, errors (omitted when it compiles), omitted, scope
(the projects compiled), scopeComplete and notes.
check_compilationvslist_diagnostics:check_compilationanswers "is it still building?" — compiler errors, fast, for the edit loop.list_diagnosticsanswers "what should I clean up?" — analyzer diagnostics, statistics, and which IDs are auto-fixable. Reach for the first after an edit and the second when exploring.
4. CreatePatch
Generates a unified diff between two text versions. Read-only — operates purely on the provided strings, never touches the filesystem.
createPatch({
before: "original code",
after: "modified code",
fileName: "Example.cs", // Optional: For display in diff
ignoreWhitespace: false, // Optional: ignore whitespace-only differences
ignoreCase: false // Optional: ignore case differences
})
Returns: the unified diff patch, hasChanges, linesAdded, linesRemoved, and the
fileName/summary used in the diff header.
Code Navigation Tools (read-only)
These tools return precise structure instead of whole files, so an AI agent can orient itself
in a codebase while spending far fewer tokens than reading source directly. All are read-only and
take an optional project (name, directory, .csproj path, or .sln path) — when omitted,
RoselineMCP auto-discovers the solution/project from its working directory. When the project belongs
to a solution, the whole solution is loaded and symbol search/resolution spans every project in it
(including sibling projects the requested project doesn't reference), so references/renames span
projects. Full request/response shapes are in docs/API.md.
Working in a git worktree? Auto-discovery is anchored to the server's working directory — the one the MCP client launched RoselineMCP in — not yours. They differ whenever work happens in a git worktree (e.g.
.claude/worktrees/<name>), which sits below the discovery walk's reach, so an omittedprojectresolves the main checkout instead. Every tool that takes an optionalproject— the seven navigation tools, both edit tools,listDiagnosticsandapplyFixes— reportsresolvedPath, the absolute.sln/.csprojthat actually answered — the.slnwhen the solution was loaded and contains the project, otherwise the.csprojthat was opened directly (e.g. a project not listed in its nearest ancestor.sln). Check it, and pass an absolute path asprojectto target a specific checkout. (analyzeSolutionis the exception: itspathOrGitis required, so it auto-discovers nothing.)Failures report it too, which is where you will usually meet this: the wrong checkout answers
NotFoundError: Symbol not found: 'X'rather than a plausible-looking success. The failure envelope'serror.resolvedPathnames the checkout that was searched, and is omitted entirely when the call failed before resolving anything. See docs/API.md.Relative file paths hang off
resolvedPath. The navigation tools'file/definitionFile,applyFixes/editMember/renameSymbol'schangedFilesand patch headers, andverification.errors[]/checkCompilation'serrors[]file, are relative to the directory containingresolvedPath— sodirname(resolvedPath) + <returned path>is the real file, and a returnedpatchapplies (git apply -p1) from that directory. That is the solution root in the usual case, and the project's own directory whenever a.csprojanswered directly — including a project not listed in its nearest ancestor.sln. One exception:listDiagnostics/analyzeSolutionreportfileabsolute, so there is nothing to join. See docs/API.md.
Tool names on the wire are
snake_case. The section headings below use friendly PascalCase/camelCasefor readability, but the actual MCP tool names returned bytools/list(and expected bytools/call) are:search_symbols,get_symbol_info,find_references,find_implementations,get_call_graph,get_type_hierarchy,get_symbol_at_position,edit_member,rename_symbol(matching the existinganalyze_solution/list_diagnostics/apply_fixes/check_compilation/create_patch).
5. SearchSymbols
Find symbols by wildcard/substring name pattern, or outline a single file.
searchSymbols({
project: "MyApp.Core",
query: "*Service", // Substring, or wildcard with * and ? — omit to outline a file
file: "UserService.cs", // Optional: restrict to one file, or outline it when query omitted
kinds: ["class", "method"], // Optional: filter by kind (also accepts "type" / "member")
max: 50 // Optional (default: 50)
})
Returns: symbols (name, fullName, kind, signature, file, line — file is relative to resolvedPath's directory; the single-file outline instead returns name, kind, signature, line, containingType), totalFound, truncated (omitted when not capped).
6. GetSymbolInfo
The compact "go to definition": a symbol's declaration metadata and (optionally) its source.
getSymbolInfo({
project: "MyApp.Core",
symbol: "Acme.Users.UserService.GetUser", // Simple or fully-qualified name
includeSource: true // Optional (default: true)
})
Returns: name, fullName, kind, signature, and (each omitted when empty/absent) modifiers, baseTypes, interfaces, documentation, definitionFile/Line, and source. Accessibility is already part of signature; definitionFile is relative to resolvedPath's directory.
7. FindReferences
Every use site of a symbol across the solution, as location + one-line snippet.
findReferences({ project: "MyApp.Core", symbol: "GetUser", includeDefinition: false, max: 100 })
Returns: references (file — relative to resolvedPath's directory, line, snippet), totalReferences, truncated (omitted when not capped).
8. FindImplementations
Implementations of an interface/member, overrides of a virtual/abstract member, or derived types of a class.
findImplementations({ project: "MyApp.Core", symbol: "IRepository", max: 100 })
Returns: implementations (symbol summaries), totalFound, truncated.
9. GetCallGraph
A depth-bounded caller and/or callee graph for a method, with cycle detection.
Shortened here. Read the whole README on GitHub.
Signals
- GitHub stars
- 5
- Last commit
- Sep 2026
Advanced
- Delivery
- roseline-mcp MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
- Catalog kind
- mcp-server
- Gateway key
io-github-atypical-consulting-roseline-mcp- Source
- github.com/atypical-consulting/roselinemcp