RoselineMCP

MCP serverDev tools

MCP 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_symbols on Program.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 PostToolUse hook 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

LayerTechnology
Runtime.NET 10.0
Compiler PlatformRoslyn (Microsoft.CodeAnalysis) 5.6.0
AnalyzersRoslynator 4.15.0
MCP SDKModelContextProtocol 2.2.0
Diff EngineDiffPlex 1.9.0
Build SystemMSBuild 18.8.2
HostingMicrosoft.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.mcpb from the latest release and open it — Claude Desktop shows an install dialog, no config editing. (It launches via dnx under 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 -i flag is required for stdio transport. The --rm flag 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_compilation vs list_diagnostics: check_compilation answers "is it still building?" — compiler errors, fast, for the edit loop. list_diagnostics answers "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 omitted project resolves the main checkout instead. Every tool that takes an optional project — the seven navigation tools, both edit tools, listDiagnostics and applyFixes — reports resolvedPath, the absolute .sln/.csproj that actually answered — the .sln when the solution was loaded and contains the project, otherwise the .csproj that was opened directly (e.g. a project not listed in its nearest ancestor .sln). Check it, and pass an absolute path as project to target a specific checkout. (analyzeSolution is the exception: its pathOrGit is 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's error.resolvedPath names 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's changedFiles and patch headers, and verification.errors[]/checkCompilation's errors[] file, are relative to the directory containing resolvedPath — so dirname(resolvedPath) + <returned path> is the real file, and a returned patch applies (git apply -p1) from that directory. That is the solution root in the usual case, and the project's own directory whenever a .csproj answered directly — including a project not listed in its nearest ancestor .sln. One exception: listDiagnostics/analyzeSolution report file absolute, 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/camelCase for readability, but the actual MCP tool names returned by tools/list (and expected by tools/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 existing analyze_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