关于 RoselineMCP
RoselineMCP 是基于 Roslyn 的 MCP 服务,为 AI 编码 Agent 提供 C# 解决方案的结构化视图和定向编辑能力。它还能检查分析器诊断,并以预览优先的方式应用修复。
主要功能
- 01导航符号、引用和调用图
- 02查看类型层次与文件大纲
- 03编辑成员并重命名符号
- 04查找诊断并应用代码修复
- 05写入前预览更改
适用场景
- 01查找 C# 符号的调用方或实现
- 02跨解决方案重命名符号
- 03审查并修复分析器诊断
- 04无需通读整个文件即可检查代码结构
MCP 相关
通过 stdio 向 MCP 客户端提供基于 Roslyn 的 C# 导航、分析和编辑工具。写入操作需明确关闭预览模式。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍

RoselineMCP
通过 MCP 为 AI 编码 Agent 提供 Roslyn 代码智能。让 Claude、Cursor 和 Copilot 能够从语义层面查看你的 C# 解决方案——符号、引用、调用图、精准编辑——从而按结构导航,而不是反复阅读源代码。实测 token 数中位数减少 85% →
目录
为什么选择 RoselineMCP
你的编码 Agent 不该为了修改一个方法而读取一个 700 行的文件。源代码占用了 Agent 的大部分令牌预算,因此最省成本的改进就是不再向它提供整个文件。
RoselineMCP 将 Roslyn 编译器平台封装为 MCP 服务。它不会把源代码倾倒给模型,而是准确回答结构性问题——此符号在哪里使用、哪些类型实现了此接口、谁调用了此方法、此文件的结构如何——并以精准方式编辑:只生成成员级差异,而不是重写整个文件。
在 RoselineMCP 自身的源代码上,只读导航工具每项任务返回的令牌数中位数减少了 85%(按大小加权汇总后为 93%),相比之下,读取对应文件需要更多令牌—— 诚实测量,也包括表现较弱的情况。
search_symbols在Program.cs上的结果:2,093 个令牌 → 120 个(−94%)。Agent 能了解文件结构;你则无需通读整份文件。
快速开始
任何支持 dnx(.NET 对应 npx)的 MCP 客户端都能按需运行它,无需安装。
需要 .NET 10 SDK。
// claude_desktop_config.json · .vscode/mcp.json · ~/.cursor/mcp.json
{
"mcpServers": {
"roseline": { "command": "dnx", "args": ["RoselineMCP", "--yes"] }
}
}
然后让你的 Agent “找出 OrderService.Checkout 的所有调用方”,或 “在整个解决方案中将 Foo 重命名为 Bar”。想使用固定版本的 NuGet 安装包或 Docker?请参阅
入门指南。
功能
- 令牌高效的代码导航 -- 通过 Roslyn 获取符号、引用、调用关系图、类型层次结构和文件大纲,而非读取整个文件。每项任务的令牌数实测中位数减少 85%(按大小加权汇总后为 93%)-- 查看基准测试。
- 精准代码编辑 -- 替换/添加/删除成员,或在整个解决方案中重命名符号,并输出统一差异,而非重写整个文件。默认仅预览。
- 全面分析与自动修复 -- 分析整个解决方案的诊断问题(Roslyn + Roslynator),并提供自动修复和便于审查的补丁。
- 默认只读 -- 七种导航工具以及诊断/补丁工具绝不会写入磁盘;三种写入工具需要显式设置
previewOnly: false。 - 编译保护(可选) --
PostToolUse钩子会在每次文件写入后检查编译器的判定,不只针对 RoselineMCP 自身的写入——详见下方的编译保护。 - 兼容你的客户端 -- 支持 Claude Desktop、VS Code(Copilot / MCP)和 Cursor。可通过
dnx、NuGet 全局工具或 Docker 安装。 - 诚实、可复现的基准测试 -- 可在自己的解决方案上运行:
dotnet run --project RoselineMCP.TokenBenchmark -c Release。
技术栈
| 层级 | 技术 |
|---|---|
| 运行时 | .NET 10.0 |
| 编译器平台 | Roslyn (Microsoft.CodeAnalysis) 5.6.0 |
| 分析器 | Roslynator 4.15.0 |
| MCP SDK | ModelContextProtocol 2.2.0 |
| 差异引擎 | DiffPlex 1.9.0 |
| 构建系统 | MSBuild 18.8.2 |
| 托管 | Microsoft.Extensions.Hosting 10.0.10 |
上述版本与
RoselineMCP/RoselineMCP.csproj保持同步——如果此表中的信息有出入,请以该文件为准。
入门指南
前置条件
- NuGet 全局工具:.NET 10.0 SDK 或更高版本
- Docker:Docker Desktop 或 Docker Engine
- 从源代码构建:.NET 10.0 SDK + MSBuild(随 Visual Studio 或 .NET SDK 提供)
- MCP 客户端:Claude Desktop 或任何兼容 MCP 的客户端
安装
Claude Desktop,一键安装: 从最新版本下载
RoselineMCP.mcpb并打开—— Claude Desktop 会显示安装对话框,无需编辑配置。(它在底层通过dnx启动, 因此仍需安装 .NET 10 SDK。)想自己编辑配置,或使用其他客户端? 请使用以下选项之一。
选项 1 -- dnx(无需安装步骤) (推荐)
RoselineMCP 提供了 MCP 服务注册表清单,因此任何支持 dnx 启动器的 MCP 客户端
(.NET 对应于 npx 的工具——可按需解析并运行 NuGet 打包的工具,无需单独执行 dotnet tool install)
都能直接启动它。需要 .NET 10.0 SDK。
{
"mcpServers": {
"roseline": {
"command": "dnx",
"args": ["RoselineMCP", "--yes"]
}
}
}
将以下内容添加到 Claude Desktop 或 VS Code 的 MCP 配置中(各客户端的确切文件位置,请参阅下文的
MCP 客户端兼容性)。
dnx 会在首次使用时下载并缓存该工具,因此无需预先全局安装。
选项 2 -- NuGet 全局工具 (离线/固定版本安装)
需要 .NET 10.0 SDK 或更高版本。
dotnet tool install -g RoselineMCP
安装后,roseline-mcp 命令即可全局使用。
Claude Desktop 配置(NuGet 全局工具)
将以下内容添加到 Claude Desktop 配置文件(claude_desktop_config.json)中:
{
"mcpServers": {
"roseline": {
"command": "roseline-mcp"
}
}
}
配置文件位置:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json- Windows:
%APPDATA%\Claude\claude_desktop_config.json
选项 3 -- Docker
无需 SDK。适用于任何安装了 Docker 的平台。
docker run -i --rm phmatray/roseline-mcp:latest
Claude Desktop 配置(Docker)
{
"mcpServers": {
"roseline": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"phmatray/roseline-mcp:latest"
]
}
}
}
注意: stdio 传输需要
-i标志。--rm标志会在会话结束后删除容器。
选项 4 -- 从源代码构建
git clone https://github.com/Atypical-Consulting/RoselineMCP.git
cd RoselineMCP
dotnet build
dotnet test
Claude Desktop 配置(从源代码构建)
{
"mcpServers": {
"roseline": {
"command": "dotnet",
"args": ["run", "--project", "/path/to/RoselineMCP/RoselineMCP.csproj"]
}
}
}
MCP 客户端兼容性
RoselineMCP 使用标准的 stdio MCP,因此应该适用于任何兼容 MCP 的客户端。以下片段是有文档记录,但并未在所有情况下逐一独立验证的——我们已确认协议层面的行为(stdio 传输、工具发现、JSON 响应)正常工作,但没有亲自端到端测试每个客户端自身的配置界面或文件。如果以下配置对你的客户端版本不起作用,请提交 issue。
Claude Desktop
编辑 claude_desktop_config.json(文件位置见上文安装部分),并在 mcpServers 下添加一个 roseline 条目,使用上文所示的四种安装选项之一(dnx、全局工具、Docker 或从源代码构建)。
VS Code(GitHub Copilot / MCP 扩展)
在工作区或用户的 mcp.json 中添加一个条目(命令面板 → “MCP: Open User
Configuration”,或工作区中的 .vscode/mcp.json):
{
"servers": {
"roseline": {
"command": "dnx",
"args": ["RoselineMCP", "--yes"]
}
}
}
如果通过 NuGet 全局工具安装,请将 "command": "roseline-mcp"(不带 args)替换进去。
Cursor
在 ~/.cursor/mcp.json(全局)或 .cursor/mcp.json(项目本地)中添加一个 roseline 条目,
使用与上文 VS Code 代码片段相同的 command/args 格式。
可用工具
每个工具的描述——即 MCP 客户端在工具选择时向模型展示的文本——都会说明自身的 Limitations:,并提供一个 Example: 调用,且字数不超过测试强制执行的上限;十三个带有可选 project 参数的工具共用下文所述的一种工作树注意事项表述,而 resolvedPath 可用于确认实际响应的是哪个检出目录。请参阅
docs/API.md § 工具描述约定。
1. AnalyzeSolution
分析整个 C# 解决方案中的诊断。只读——绝不修改磁盘上的文件。
pathOrGit 也接受 http(s):// Git URL:系统会将其浅克隆到临时目录中,
分析后再删除。
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)
})
**返回:**解决方案文件名、项目数、diagnosticSummary(按严重性统计的数量)、包含每条诊断的项目/文件/行/列/ID/严重性/消息的
topDiagnostics 数组,以及 analyzerLoad——仅当某个分析器引用加载失败或加载后未包含任何 C#
分析器时才会出现(合并统计所有已分析项目):失败项会列出名称和原因,无问题项则只计数(referencesWithoutAnalyzers)。
2. ListDiagnostics
获取特定项目的详细诊断信息。只读——绝不修改磁盘上的文件。
project 可选,接受与导航工具相同的引用形式(名称、
目录、.csproj、.sln 或 .slnx 路径);省略时,会根据工作目录自动发现解决方案/项目。
listDiagnostics({
project: "MyProject.csproj", // Optional: name, directory, .csproj, .sln, or .slnx; 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
})
返回:项目名称、resolvedPath(实际加载的 .sln/.csproj 的绝对路径)、
totalDiagnostics 诊断总数、筛选后的 diagnostics 列表、stats(按 ID 和
严重性分组的计数)、suggestedFixableIds——已实际注册代码修复提供程序的诊断 ID,无论该提供程序来自 Roslyn、随附的 Roslynator 目录,还是项目自己的某个分析器引用——以及 analyzerLoad,其中会列出每个加载失败的分析器引用(及原因),包括其代码修复器加载失败的引用;此外还会显示不含 C# 分析器的引用数量。仅在有相关信息时才会出现。
3. ApplyFixes
对指定诊断应用自动代码修复。默认处于预览模式:
previewOnly 默认为 true,因此调用此工具而不设置该参数绝不会写入磁盘——必须显式传入 previewOnly: false 才会应用更改。project 可选,接受与导航工具相同的引用形式(名称、目录、.csproj、.sln 或 .slnx 路径);
省略时,会根据工作目录自动发现解决方案/项目。执行写入
(previewOnly: false)时,请使用绝对路径——参见下方的工作树说明。
applyFixes({
ids: ["CS0168", "RCS1001"], // Diagnostic IDs to fix
project: "/repo/src/MyProject/MyProject.csproj", // Optional; absolute for a write (see the worktree note)
previewOnly: false // Optional (default: true). Set false to write changes to disk.
})
**返回:**项目名称、resolvedPath(实际加载的 .sln/.csproj 的绝对路径)、
fixedCount、fixersApplied(实际修复的诊断 ID)、changedFiles
(相对于 resolvedPath 所在目录的路径,使用正斜杠——与导航工具采用相同的路径基准)、统一
差异 patch、notes(范围——修复了哪个项目、解决方案中哪些项目未分析,以及写入任何链接文件是否会影响同级项目——还有跳过/失败的 ID 和状态消息)、回显调用方请求值的 previewOnly、applied(是否有任何更改实际写入磁盘)、verification(编译器对修复后代码的判定)以及 analyzerLoad(仅当某个引用加载失败或加载后未包含分析器时才会出现,以便区分“未发现 X 的诊断”和“报告 X 的分析器从未加载”,以及“没有修复器”和“修复器无法加载”)。修复器的查找顺序为 Roslyn 内置项、随附的 Roslynator 目录,然后是项目自己的分析器引用;如果两者都提供同一 ID 的修复器,则优先使用随附的提供程序。
以 .sln 为目标时,只会修复一个项目——其主项目;这一范围会在写入路径上强制执行,而不只是通过确认提示告知:只会验证并写入锚定项目的文档;即使调用方看不到提示(预览模式、不支持交互的客户端、无人值守的主机),也仍可通过 notes 查看哪些项目被跳过。传入 .csproj 可修复指定项目——此时不会报告任何跳过的项目,因为这正是请求的范围。
14. CheckCompilation
针对磁盘上的当前状态回答 “现在能否编译,以及哪里出了问题”——替代 Agent 编辑循环中的 dotnet build 往返操作。此工具只读,且只检查编译器诊断:分析器的开销是单纯编译的数倍,因此本工具的速度足以在每次编辑后运行。
无论代码由谁编写,它都会检查磁盘上的内容,因此同样适用于其他工具所做的编辑。它的速度来自服务已持有的热 MSBuildWorkspace:会话的首次调用需要冷加载,此后的每次调用都会复用增量 Roslyn 编译。
checkCompilation({
project: "MyApp.sln", // Optional: name, directory, .csproj, .sln, or .slnx; auto-discovered if omitted
max: 20 // Optional (default: 20). The rest are counted in `omitted`.
})
返回值: resolvedPath、compiles、errors(编译成功时省略)、omitted、scope(已编译的项目)、scopeComplete 和 notes。
check_compilation与list_diagnostics:check_compilation回答 “它还能通过编译吗?”——快速检查编译器错误,适用于编辑循环。list_diagnostics回答 “我应该清理哪些问题?”——提供分析器诊断、统计信息,以及哪些 ID 可以自动修复。编辑后使用前者,探索代码时使用后者。
15. SuggestFixExamples
对于无法通过代码修复程序修复的诊断,此工具会返回 关键示例:同一解决方案中,语法和符号结构相同但不会触发该诊断的位置,以及规则说明和帮助链接。此工具只读,并且有意不纳入编译验证流程:它不会提出修改方案,由 Agent 编写编辑内容,而 edit_member/apply_fixes 仍会验证修改。
suggestFixExamples({
id: "CS0121", // Required: the diagnostic ID
project: "MyApp.sln", // Optional: auto-discovered if omitted
file: "Foo.cs", // Optional: anchor on one occurrence (with line)
line: 42,
maxExamples: 3, // Optional (default: 3)
maxCandidates: 2000 // Optional (default: 2000)
})
返回值: resolvedPath、rule、hasFixer、alert(未触发该 ID 时不存在)、examples、candidatesScanned、truncated 和 notes。
4. CreatePatch
生成两个文本版本之间的统一差异。此工具只读——仅处理提供的字符串,不会触碰文件系统。
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
})
返回值: 统一差异 patch、hasChanges、linesAdded、linesRemoved,以及差异标头中使用的 fileName/summary。
代码导航工具(只读)
这些工具返回精确的结构,而非完整文件,让 AI Agent 能够熟悉代码库,同时比直接阅读源代码消耗少得多的 Token。所有工具均为只读,并接受一个可选的 project(名称、目录、.csproj 路径、.sln 或 .slnx 路径)——未提供时,RoselineMCP 会从其工作目录自动发现解决方案/项目。如果项目属于某个解决方案,则会加载整个解决方案,并在其中的每个项目(包括所请求项目未引用的同级项目)中搜索/解析符号,因此引用/重命名会跨项目进行。完整的请求/响应格式见 docs/API.md。
在 git worktree 中工作? 自动发现以服务端的工作目录为基准——也就是 MCP 客户端启动 RoselineMCP 时所在的目录,该目录在进程生命周期内固定不变,而不是你所在的目录。重要的只是两者不同,无论是你切换到了另一个检出目录,还是服务端启动时就在另一个检出目录中:worktree(例如
.claude/worktrees/<name>)位于发现遍历范围之外,因此省略project时会解析到主检出目录。所有接受可选project的工具——七个导航工具、两个编辑工具、listDiagnostics和applyFixes——都会返回resolvedPath,即实际处理请求的.sln/.csproj绝对路径:如果加载了包含该项目的解决方案,则返回.sln;否则返回直接打开的.csproj(例如,未列在最近的上级.sln中的项目)。请检查该值,并传入绝对路径作为project,以指定特定检出目录。(analyzeSolution是例外:它必须提供pathOrGit,因此不会自动发现任何内容。)具体表现取决于你是在读取还是写入。 如果要读取的内容在另一个检出目录中不存在,会返回
NotFoundError: Symbol not found: 'X';如果存在,则会成功读取,但resolvedPath可能与你预期的不符。无论哪种情况,失败响应中的error.resolvedPath都会标明搜索的是哪个检出目录(如果调用在解析出任何内容前就失败,则完全不包含该字段),因此可以区分“那里没有”和“检出目录不对”。写入则不能任其发生:同一仓库的两个检出目录大多包含相同的代码,因此在错误的检出目录中写入会正常解析、应用并返回成功,但修改的是你原本不打算修改的目录——只有在已经更改文件的那次调用返回时,你才会发现这一点。因此,当解析出的检出目录属于包含关联 worktree 的仓库时,若调用
applyFixes、editMember或renameSymbol时设置了previewOnly: false,且project不是绝对路径——省略、空白、"."、不带路径的项目名称、相对路径"App.sln"——则会直接拒绝(ValidationError,不会写入,也不会提示确认),不受RoselineMCP:ConfirmDestructiveWrites影响。只有传入绝对路径的project才能解除拒绝,因为只有绝对路径能指定检出目录:其他任何写法都会相对于服务端的工作目录解析,而你无法确定的恰恰就是这个目录。请参阅 docs/API.md。相对文件路径以
resolvedPath为基准。 导航工具返回的file/definitionFile、applyFixes/editMember/renameSymbol返回的changedFiles和补丁标头,以及verification.errors[]/checkCompilation的errors[]中的file,都是相对于resolvedPath所在目录的——因此,dirname(resolvedPath) + <returned path>就是实际文件路径,且返回的patch可从该目录应用(git apply -p1)。通常该目录就是解决方案根目录;如果直接打开的是.csproj,则是项目自身的目录——包括未列在最近的上级.sln中的项目。一个例外:listDiagnostics/analyzeSolution返回的file是绝对路径,无需拼接。请参阅 docs/API.md。
MCP 调用中使用的工具名称为
snake_case。 为便于阅读,以下各节标题使用易读的 PascalCase/camelCase,但tools/list返回(且tools/call需要)的实际 MCP 工具名称为:search_symbols、get_symbol_info、find_references、find_implementations、get_call_graph、get_type_hierarchy、get_symbol_at_position、edit_member、rename_symbol(与现有的analyze_solution/list_diagnostics/apply_fixes/check_compilation/create_patch保持一致)。
5. SearchSymbols
按通配符/子串名称模式查找符号,或列出单个文件的大纲。
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)
})
返回: symbols(name、fullName、kind、signature、file、line — file 相对于 resolvedPath 所在目录;单文件大纲则返回 name、kind、signature、line、containingType)、totalFound、truncated(未达到上限时省略)。
6. GetSymbolInfo
精简版“转到定义”:符号的声明元数据,以及(可选)其源代码。
getSymbolInfo({
project: "MyApp.Core",
symbol: "Acme.Users.UserService.GetUser", // Simple or fully-qualified name
includeSource: true // Optional (default: true)
})
返回: name、fullName、kind、signature,以及(为空或不存在时各自省略)modifiers、baseTypes、interfaces、documentation、definitionFile/Line 和 source。可访问性已包含在 signature 中;definitionFile 相对于 resolvedPath 所在目录。
7. FindReferences
符号在整个解决方案中的每个使用位置,以位置和单行代码片段的形式返回。
findReferences({ project: "MyApp.Core", symbol: "GetUser", includeDefinition: false, max: 100 })
返回: references(file — 相对于 resolvedPath 所在目录、line、snippet)、totalReferences、truncated(未达到上限时省略)。
8. FindImplementations
接口/成员的实现、虚拟/抽象成员的重写,或类的派生类型。
findImplementations({ project: "MyApp.Core", symbol: "IRepository", max: 100 })
返回: implementations(符号摘要)、totalFound、truncated。
9. GetCallGraph
针对方法、带深度限制并进行循环检测的调用方和/或被调用方图。
getCallGraph({
project: "MyApp.Core",
method: "Handle",
direction: "callers", // "callers" (default) | "callees" | "both"
depth: 1, // 1-3 (default: 1)
max: 50 // Optional: nodes expanded per direction
})
返回: callers/callees 节点树(fullName,使用简单参数类型名称;file — 相对于 resolvedPath 所在目录;line、truncated、children)。调用 GetSymbolInfo 可获取节点的完整签名。
10. GetTypeHierarchy
类型的基类链、实现的接口和/或派生类型。
getTypeHierarchy({
project: "MyApp.Core",
type: "SqlRepository",
direction: "both", // "base" | "derived" | "both" (default)
max: 100 // Optional: maximum derived types returned (default: 100)
})
返回: baseTypes、interfaces、derivedTypes(以符号摘要形式返回)。
13. GetSymbolAtPosition
位于 file:line(:column) 位置的符号 — 无需读取文件,即可将诊断信息、堆栈跟踪或 grep 命中项转换为符号名称。
getSymbolAtPosition({
project: "MyApp.Core",
file: "UserService.cs", // File name or path suffix (same matching as SearchSymbols)
line: 42, // 1-based
column: 17 // Optional (1-based) — omit to resolve the most relevant symbol on the line
})
返回: name、fullName、kind、signature、isDeclaration(该位置是否位于符号自身的声明上),以及(为空或不存在时各自省略)containingType、documentation、definitionFile/Line(相对于 resolvedPath 所在目录)。仅指定行的查询优先返回该行上的声明,而非被引用的符号。
代码编辑工具(默认预览)
对成员进行精确编辑,生成成员级变更,而非重写整个文件。与 ApplyFixes 一样,两者都默认使用预览模式(previewOnly: true)— 除非明确传入 previewOnly: false,否则不会写入磁盘。
11. EditMember
替换、添加或删除单个类型成员;返回统一差异。
editMember({
project: "/repo/src/MyApp.Core/MyApp.Core.csproj",
symbol: "Acme.UserService.GetUser", // The member (replace/delete), or the container type (add)
operation: "replace", // "replace" | "add" | "delete"
newSource: "public User GetUser(int id) => _repo.Find(id);", // Required for replace/add
previewOnly: false // Optional (default: true). Set false to write to disk.
})
返回: operation、target、changedFiles、patch、previewOnly、applied、notes。
12. RenameSymbol
重命名符号并更新整个解决方案中的所有引用(Roslyn 重命名);返回统一差异。
renameSymbol({ project: "/repo/src/MyApp.Core/MyApp.Core.csproj", symbol: "GetUser", newName: "GetUserById", previewOnly: false })
返回: symbol、newName、changedFiles、patch、previewOnly、applied、verification、notes。
每次写入都会经过编译验证。 在
edit_member、rename_symbol或apply_fixes修改磁盘前,候选变更会先在内存中编译。如果变更引入编译器错误 — 包括你从未指定的下游项目中的错误 — 则会拒绝写入:调用仍会成功,applied返回false,不会写入任何内容,并会返回差异和错误。传入allowIntroducedErrors: true可强制写入。已存在问题的代码仓库仍可编辑:门槛检查的是变更引入了什么,而不是整个代码树能否编译。有关检查范围规则和保证边界,请参阅编译验证。
工具注解
RoselineMCP 的 SDK(ModelContextProtocol 2.2.0)支持标准 MCP 工具
注解提示(readOnlyHint、destructiveHint、
idempotentHint),并且每个工具都会通过 [McpServerTool(ReadOnly = ..., Destructive = ..., Idempotent = ...)] 声明这些提示:
| 工具 | readOnlyHint | destructiveHint | idempotentHint | 备注 |
|---|---|---|---|---|
AnalyzeSolution | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
ListDiagnostics | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
ApplyFixes | ❌ false | ⚠️ true | ❌ false | destructiveHint 是静态的最坏情况注解:它为 true,因为传入 previewOnly: false 时,该工具可以写入文件,但默认调用(未设置 previewOnly,即 true)不会写入任何内容。SDK 的注解模型无法表达“仅在特定参数值下具有破坏性”——请参阅源码中 ApplyFixesTool.ApplyFixes 的文档注释。 |
CheckCompilation | ✅ true | ❌ false | ✅ true | 从不写入磁盘。编译已加载的解决方案并报告编译器的判定结果。 |
SuggestFixExamples | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
CreatePatch | ✅ true | ❌ false | ✅ true | 仅处理提供的两个字符串;从不接触文件系统。 |
SearchSymbols | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
GetSymbolInfo | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
FindReferences | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
FindImplementations | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
GetCallGraph | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
GetTypeHierarchy | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
GetSymbolAtPosition | ✅ true | ❌ false | ✅ true | 从不写入磁盘。 |
EditMember | ❌ false | ⚠️ true | ❌ false | destructiveHint 的最坏情况说明与 ApplyFixes 相同:仅在传入 previewOnly: false 时写入文件;默认调用不会写入任何内容。 |
RenameSymbol | ❌ false | ⚠️ true | ❌ false | destructiveHint 的最坏情况说明与 ApplyFixes 相同:仅在传入 previewOnly: false 时写入文件;默认调用不会写入任何内容。 |
这些提示是 MCP 客户端可读取的静态工具元数据(例如,在 Agent 调用破坏性工具前向用户发出警告)——它们描述的是工具的最坏情况行为,而非任何特定调用的结果。关于这些注解所依托的工具名称和参数的稳定性保证,请参阅下文的工具兼容性政策。
工具兼容性政策
- 在同一主版本内,工具名称和必需参数保持稳定。 基于
1.x版本的AnalyzeSolution(pathOrGit, ...)编写的 MCP 客户端集成,在所有1.x版本中都能继续使用。 - 次要版本中可以添加可选参数(例如,
CreatePatch新增了可选且有默认值的ignoreWhitespace/ignoreCase参数,不会影响现有调用方)。 - 重命名或移除参数、更改参数的必需/可选状态,或更改工具名称,均属于破坏性变更。 破坏性变更会在
CHANGELOG.md的专门“破坏性变更”标题下说明,且只会随主版本号升级发布。 - 响应的结构(JSON 字段名称/类型)记录在
docs/API.md中,并遵循相同政策:新增字段不属于破坏性变更,重命名或移除字段则属于破坏性变更。
支持的分析器
诊断工具(analyze_solution、list_diagnostics、apply_fixes)会报告编译器诊断和分析器诊断,并通过 Roslyn 的 CompilationWithAnalyzers 执行(check_compilation 特意不在此列表中——它仅检查编译器,因此速度足以用于编辑循环):
- Roslyn Analyzers —— 内置的 C# 编译器诊断
- Roslynator —— 500 多个 C# 分析器和修复,随 RoselineMCP 一同打包(作为与
RoselineMCP.dll同级的analyzers/文件夹提供),并默认执行,因此 RCS* 诊断会直接显示并可修复 - 自定义分析器 —— 分析器所针对的解决方案引用的任何基于 Roslyn 的分析器,都会从项目的分析器引用中加载,并与打包的分析器一同运行(没有内置的 StyleCop.Analyzers 引用——如果需要 SA* 诊断,请将其添加到所分析的解决方案中)。其代码修复器也会加载:当 Roslyn、打包的目录或项目自身的分析器引用中包含匹配的修复器时,分析器报告的规则就可以自动修复(同一 ID 两处都有时,以打包版本为准)。
无法加载的内容都会明确列出。 如果 Roslyn 无法加载某个分析器引用,例如该引用是针对比 RoselineMCP 更新的 Microsoft.CodeAnalysis 构建的,或文件已损坏,Roslyn 会让它贡献 零个 分析器,而不是直接失败。三个诊断响应都包含一个 analyzerLoad 区块(analyzersRan、referencesConsulted、referencesContributing、referencesWithoutAnalyzers——已成功加载但未声明 C# 分析器的引用,只计数而不列出名称——analyzersLoaded,以及每个加载失败引用对应的 notes[] 条目:其名称、失败原因——load-failure,包含 Roslyn 的 errorCode 和消息;unresolved(程序集不在磁盘上,因此在 Roslyn 因此报错之前已将其从已加载的解决方案中移除);exception;或在 list_diagnostics 和 apply_fixes 中出现的 fixer-load-failure(无法加载其代码修复器))。如果所有引用都做出了贡献且没有失败,则省略此区块;因此只要该区块出现,就一定包含信息;参见 docs/API.md。
将 RoselineMCP:RunAnalyzers 设为 false,即可仅获取编译器诊断(大型解决方案中速度更快;参见配置和 SECURITY.md 中关于分析器执行的说明)。此时仍会显示该区块,其中 analyzersRan: false,以便区分“已关闭”和“一切正常”。
示例
分析解决方案
# Using with an MCP client
mcp call analyzeSolution '{
"pathOrGit": "/Users/dev/MyProject/MyProject.sln",
"severity": "warning",
"maxDiagnostics": 50
}'
响应:
{
"solution": "MyProject.sln",
"projects": 5,
"diagnosticSummary": {
"error": 2,
"warning": 15,
"info": 28
},
"topDiagnostics": [
{
"id": "CS0168",
"severity": "warning",
"message": "The variable 'ex' is declared but never used",
"file": "Program.cs",
"line": 42
}
]
}
应用修复
mcp call applyFixes '{
"project": "MyProject.Core.csproj",
"ids": ["CS0168", "RCS1001"],
"previewOnly": true
}'
响应中包含一个统一差异补丁,展示将要应用的所有更改。
配置
RoselineMCP 会从服务二进制文件的安装目录(AppContext.BaseDirectory)读取 appsettings.json 和 appsettings.{Environment}.json,而不是从进程工作目录读取。从目标仓库内部启动服务时,不会读取该仓库自己的 appsettings.json;而随 dotnet tool 安装打包的设置始终可以找到。配置只在启动时读取;不会监视文件变化并重新加载。
环境变量
以 ROSELINE_ 为前缀的环境变量会覆盖 JSON 文件,并使用 __ 作为节分隔符。因此,RoselineMCP 节下的设置需要添加双重前缀:
# RoselineMCP:EnableDiagnosticLogging
ROSELINE_RoselineMCP__EnableDiagnosticLogging=true
# RoselineMCP:DefaultTimeout (ms)
ROSELINE_RoselineMCP__DefaultTimeout=300000
# RoselineMCP:RunAnalyzers (compiler-only diagnostics when false)
ROSELINE_RoselineMCP__RunAnalyzers=false
# RoselineMCP:ConfirmDestructiveWrites (no elicitation before writes; unattended hosts)
ROSELINE_RoselineMCP__ConfirmDestructiveWrites=false
# RoselineMCP:ConfirmDestructiveWritesTimeout (ms to wait for that confirmation; 0 = forever)
ROSELINE_RoselineMCP__ConfirmDestructiveWritesTimeout=300000
# Logging:LogLevel:RoselineMCP
ROSELINE_Logging__LogLevel__RoselineMCP=Debug
DOTNET_ENVIRONMENT:设置环境(Development、Production)
appsettings.json
配置日志记录和其他设置:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"RoselineMCP": "Debug"
}
},
"RoselineMCP": {
"DefaultTimeout": 120000,
"EnableDiagnosticLogging": false,
"WorkspaceCache": true,
"RunAnalyzers": true,
"ConfirmDestructiveWrites": true,
"ConfirmDestructiveWritesTimeout": 300000
}
}
RoselineMCP:DefaultTimeout:应用于每次工具调用的墙钟时间分析预算(毫秒),此外还受调用方自身的取消机制限制。0表示禁用。对于这三个写入工具,它不涵盖整个调用:触发写入确认后,只有在用户回答后计时才会开始,因此思考时间计入ConfirmDestructiveWritesTimeout。请根据分析所需时间设置,而不是调用方观察到的整个时长。RoselineMCP:EnableDiagnosticLogging:选择启用的本地工具调用跟踪——参见调试日志。默认禁用;在appsettings.Development.json中启用。RoselineMCP:WorkspaceCache:在导航/编辑工具调用之间复用已加载的 MSBuild 工作区(默认启用)——参见性能。设为false可在每次调用时加载新的工作区。这是隔离/调试开关,不是节省内存的开关——经测量,禁用后常驻内存增加约 26%,第二次调用延迟增加约 45 倍;参见内存管理。RoselineMCP:RunAnalyzers:在诊断工具中运行 Roslyn 分析器(捆绑的 Roslynator 和目标项目自身的分析器引用)(默认启用)——参见支持的分析器。设为false可仅运行编译器诊断。RoselineMCP:ConfirmDestructiveWrites:当调用方传入previewOnly: false时,在写入工具实际写入前请求客户端确认(通过 MCP elicitation)(默认启用)。对于无人值守的主机——CI、无头 Agent,或任何无法响应 elicitation 的客户端——设为false。此时,显式传入previewOnly: false是写入前唯一的保护措施。RoselineMCP:ConfirmDestructiveWritesTimeout:等待该确认的时长(毫秒);超时后将沉默视为“否”,并返回预览(默认300000,即 5 分钟)。它有意不使用DefaultTimeout——后者是分析预算,而用户查看实际差异时可能合理地超过该时限。客户端接受提示词却始终不作答,过去会导致工具调用永久阻塞;将此值设为0或更小可恢复不设上限的等待。
架构
RoselineMCP 使用 stdio 传输——这是有意为之的设计决策。服务器作为本地进程运行,由 MCP 客户端(Claude Desktop、AI Agent)启动,通过 stdin/stdout 通信,并在客户端断开连接时退出(写入确认待处理期间除外,此时服务器会继续运行,直至 ConfirmDestructiveWritesTimeout 到期;若该值为 0,则会一直运行——参见 docs/ARCHITECTURE.md)。这使其非常适合通过 NuGet 全局工具(dotnet tool install -g RoselineMCP)或 Docker 镜像分发——无需绑定端口、无需 HTTP 服务器,也无需管理基础设施。
┌─────────────────────┐
│ MCP Client │
│ (Claude Desktop, │
│ AI Assistants) │
└────────┬────────────┘
│ MCP Protocol (stdio)
▼
┌─────────────────────┐
│ RoselineMCP │
│ MCP Server │
│ │
│ ┌───────────────┐ │
│ │ Tool Layer │ │
│ │ (Analyze, │ │
│ │ Fix, Patch) │ │
│ └───────┬───────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Service Layer │ │
│ │ (Workspace, │ │
│ │ Diagnostics) │ │
│ └───────┬───────┘ │
│ ▼ │
│ ┌───────────────┐ │
│ │ Roslyn + │ │
│ │ Roslynator │ │
│ │ Analyzers │ │
│ └───────────────┘ │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ C# Source Code │
│ (.sln / .csproj) │
└─────────────────────┘
项目结构
RoselineMCP/
├── RoselineMCP/
│ ├── Interfaces/ # Service interfaces
│ ├── Services/ # Core business logic
│ ├── Tools/ # MCP tool implementations
│ ├── Models/ # Data transfer objects
│ └── Program.cs # Application entry point
├── RoselineMCP.Tests/ # Unit tests
├── .github/workflows/ # CI/CD pipelines
├── Dockerfile # Container build
└── RoselineMCP.sln # Solution file
性能
- 工作区缓存——导航、编辑和诊断/修复工具(
ListDiagnostics、ApplyFixes,以及所有由IProjectLoader支持的工具)会在多次调用间复用已加载的MSBuildWorkspace,因此除首次调用外,每次调用都能省去数百毫秒的重新加载时间。缓存条目带有指纹(.sln、每个.csproj、每个源文件及其目录的最后写入时间 + 大小),并会在每次调用时重新检查,因此磁盘上的任何更改——包括 RoselineMCP 自身所做的编辑——都会触发重新加载。对于捕获时间戳时自身最后写入时间仍较新的文件/目录,还会一并记录内容签名(哈希值或目录列表),因此即使在同一文件系统时间戳粒度区间内发生长度相同的写入,也会通过比较签名而非仅凭基本文件状态匹配来捕获;这种方式开销很低,文件保持较新时无需每次调用都强制重新加载。可通过RoselineMCP:WorkspaceCache = false禁用——但请注意,这是用于隔离/调试的开关,并非降低内存占用的方法:已释放的工作区所占内存不会归还给操作系统,因此禁用缓存后,常驻内存占用会更差约 26%,速度也会慢约 45 倍。测量结果及评估后为何拒绝空闲释放方案,见内存管理 - 工作区隔离(AnalyzeSolution)——
AnalyzeSolution仍会为每项操作创建全新的MSBuildWorkspace(见架构) - 顺序分析项目——解决方案中的项目会逐个分析,而非并发分析,以保持 MSBuild 工作区状态一致
- 结果上限——
maxDiagnostics/max限制每次调用返回的诊断数量,与发现的诊断数量无关
编译防护
EditMember/RenameSymbol/ApplyFixes 背后的编译检查仅会针对通过这些工具进行的写入触发。编译防护会将相同的检查应用于 C# 解决方案中的每次文件写入——无论 Agent 使用的是 Edit、Write、MultiEdit 还是其他工具——它会作为 PostToolUse hook 运行,并通过本地套接字向已运行的服务请求检查结果。
它只会报告,无法阻止。 PostToolUse 会在工具完成写入之后触发,而且 hook 协议不包含阻止决策。防护的作用是在同一轮中向 Agent 显示新增的编译器错误,让它接下来修复这些错误,而不是在此基础上继续操作。它无法阻止写入——有关为何拒绝可阻止写入的替代方案,请参阅此 issue。
启用它(默认关闭),然后安装 hook:
# 1. the server opens the local guard endpoint
export ROSELINE_RoselineMCP__Guard=true
# 2. print the settings.json block and paste it into .claude/settings.json
roseline-mcp guard --print-hook
每次写入时的处理方式:
| 情况 | 结果 |
|---|---|
| 编辑引入了编译器错误 | 在 Agent 当前轮次中报告错误(退出码 2,通过 stderr 输出) |
| 编辑未引入任何错误 | 静默——退出码 0,无输出,不消耗 token |
| 分支原本就有错误 | 静默——已有错误并非 Agent 所致 |
不是 .cs 文件,或没有所属项目 | 静默 |
| 服务未运行或响应过慢 | 静默——无法提供信息的防护不得造成中断 |
仅检查编译器诊断,并且范围限定为已更改项目及其依赖项目,因此足够快速,可用于内循环。它会添加一个本地 IPC 端点:在共享机器上启用前,请先阅读 SECURITY.md。
安全
- 默认只读——
AnalyzeSolution、ListDiagnostics、CreatePatch以及全部六个代码导航工具(SearchSymbols、GetSymbolInfo、FindReferences、FindImplementations、GetCallGraph、GetTypeHierarchy)绝不会写入磁盘。三个可写入的工具——ApplyFixes、EditMember和RenameSymbol——默认都将previewOnly: true;调用方必须显式传入previewOnly: false才能写入。在这个选择加入机制之后还有第二道尽力而为的保护:写入工具会通过 MCP elicitation 请求客户端确认,然后才写入;如果用户拒绝,调用会降级为预览。提示未得到回应时也会如此——往返等待时间受RoselineMCP:ConfirmDestructiveWritesTimeout限制(默认 5 分钟),超时后调用会返回预览,而不会写入或一直挂起。如果客户端不支持 elicitation,则会跳过确认;操作员也可以通过RoselineMCP:ConfirmDestructiveWrites=false完全禁用确认——此后只剩previewOnly: false这一道保护。请参阅 SECURITY.md。 - 真实、只读的 Git 克隆——
pathOrGit接受http(s)://Git URL,并会将其浅克隆(git clone --depth 1)到临时目录中;操作完成后会删除该目录。其他任何 URL 方案都不会被视为 Git 远程服务。 - MSBuild 并非沙箱——通过
MSBuildWorkspace加载.sln/.csproj会进行设计时 MSBuild 评估,并可能执行嵌入在项目中的构建逻辑(<Exec>任务、自定义UsingTask程序集、导入的.targets/.props)。分析完全不受信任的仓库或 URL,会给主机带来真实的代码执行风险。在将 RoselineMCP 指向不受信任的输入之前,请参阅SECURITY.md,了解完整说明和操作员建议。 - 执行分析器就是执行代码——诊断工具也会在进程内运行目标项目引用的 Roslyn 分析器(请参阅支持的分析器);来自不受信任仓库的分析器可执行任意代码。
RoselineMCP:RunAnalyzers = false会禁用诊断分析器阶段(包括捆绑的 Roslynator)。请参阅SECURITY.md。 - 代码修复提供程序来自相同的引用——
list_diagnostics和apply_fixes还会加载项目自身分析器引用中携带的CodeFixProvider类型。这不会加载分析器阶段尚未加载的程序集(每个类型都通过其引用自身的程序集加载器获取),但这是有意为之的选择,而非意外行为;RunAnalyzers = false不会控制此功能。详情已记录在SECURITY.md。 - 无论该开关如何设置,源生成器都会运行——生成器通过相同的
AnalyzerReferences提供,但它们会在构建任何编译时运行,而不是作为诊断阶段的一部分,因此每条语义分析路径都会执行它们——包括所有导航工具。若不破坏语义分析,就无法阻止它们运行。RunAnalyzers = false可以缩小不受信任仓库的代码执行范围,但无法消除风险。请改用隔离方式——参阅SECURITY.md。 - 没有专用的路径遍历沙箱——路径仅通过普通存在性检查进行解析,不会根据允许的根目录进行规范化;请将
pathOrGit/project/branch视为受信任的操作员输入。
故障排除
常见问题
- 找不到 MSBuild:确保已安装 .NET SDK,且其位于 PATH 中
- 无法加载解决方案:检查是否缺少 NuGet 包,并运行
dotnet restore - 未找到分析器(RCS/SA)诊断**:确认
RoselineMCP:RunAnalyzers未设置为false;对于非捆绑规则集(例如 StyleCop),请确认目标项目中已安装该分析器 - 权限被拒绝:确保对解决方案文件具有读取权限
调试日志
启用详细日志:
ROSELINE_Logging__LogLevel__RoselineMCP=Debug dotnet run --project RoselineMCP/RoselineMCP.csproj
跟踪单个工具调用
每次工具调用都会获得一个调用级别的关联 ID(GUID)。生成它的成本很低,因此始终会创建,但只有在需要时才会显示:它会包含在每个 JSON 错误响应(correlationId)中,并通过 ILogger.BeginScope 附加到该调用的日志行中。这样,用户报告故障时只需提供一个 ID,你就能找到完整的服务端日志条目——无需按时间戳搜索。
如需深入选择性跟踪每次工具调用(开始/停止、持续时间、成功/失败),并将其记录为 System.Diagnostics.Activity 跨度,请将 RoselineMCP:EnableDiagnosticLogging 设为 true(它在 appsettings.Development.json 中已经设为 true):
ROSELINE_RoselineMCP__EnableDiagnosticLogging=true dotnet run --project RoselineMCP/RoselineMCP.csproj
此功能使用内置的 ActivitySource/Activity API,而非 OpenTelemetry SDK,因此不会增加额外依赖。跨度仅通过现有的 ILogger 管道记录,该管道已路由到 stderr——绝不会写入 stdout(MCP JSON-RPC 通道),也不会通过网络发送任何内容;关闭该标志时(默认状态),不会注册监听器,跨度的开销几乎为零。
路线图
- 更多分析器规则集(SonarAnalyzer、FxCop)
- 提供带置信度评分的自动修复建议
- 集成 CI/CD,实现自动化分析流水线
- 在单个会话中支持多个解决方案
- 增量分析(仅分析已更改的文件)
- 通过 MCP 配置自定义分析器规则
想要参与贡献?任选一项路线图任务并提交 PR!
文档
- 文档网站 -- 概览、工具参考,以及 token 节省基准测试
(由
website/构建,部署到 GitHub Pages) - docs/API.md -- 每个 MCP 工具的完整请求/响应参考、服务接口、模型及错误响应约定
- docs/AGENT-BENCHMARK.md -- 端到端 A/B 测试:AI Agent 使用 RoselineMCP 实际上能否减少 token 消耗?(坦诚地说——大型代码库中效果显著,小型代码库中则持平)
- docs/ARCHITECTURE.md -- 分层架构、数据流和设计模式
- PROMPTS.md -- 各工具的提示词示例和端到端工作流
- CHANGELOG.md -- 发布历史和重大变更
- SECURITY.md -- 漏洞报告和 MSBuild 代码执行注意事项
- CONTRIBUTING.md -- 开发环境设置和 PR 流程
Atypical MCP 服务
这是 Atypical Consulting 的一套 Model Context Protocol 服务的一部分:
- RoselineMCP — 面向 AI Agent 的 Roslyn 代码智能
- ASTral — 结构化代码检索(tree-sitter)
- AdrMcp — 通过 MCP 提供架构决策记录
- MarkdownInk — 终端 Markdown 渲染
贡献
欢迎贡献!请先阅读 CONTRIBUTING.md。
- Fork 此仓库
- 创建功能分支(
git checkout -b feature/amazing-feature) - 使用 conventional commits 规范提交(
git commit -m 'feat: add amazing feature') - 将分支推送到远程仓库(
git push origin feature/amazing-feature) - 发起 Pull Request
许可证
MIT © 2026 Atypical Consulting SRL
致谢
- 基于 Roslyn 构建 —— .NET 编译器平台
- 由 Roslynator 提供支持 —— C# 分析器和重构工具
- 使用 DiffPlex —— 差异生成库
- 实现 Model Context Protocol —— AI 助手集成协议
由 Atypical Consulting 倾心打造 —— 主张鲜明、达到生产级标准的开源项目。
由 Atypical Consulting 打造。我们还开发了 NuGetKeep, 一款具备供应链隔离功能的可自托管 NuGet 服务。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。