关于 C# Class Analyzer MCP
CSharpAnalyserMcp 是供 AI 助手和开发者浏览 C# 解决方案的只读 MCP 服务。它使用 Roslyn 提供针对声明、文档、继承关系和方法源码的查询,避免将整个文件读入上下文。
主要功能
- 01按文件夹或命名空间列出类型
- 02获取类型文档和成员签名
- 03查看方法签名、文档和方法体
- 04追踪继承关系与方法重写
- 05使用正则表达式搜索源码
- 06刷新缓存的解决方案快照
适用场景
- 01在大型 C# 解决方案中查找类型
- 02无需加载整个文件即可检查方法
- 03追踪实现和继承关系
- 04搜索符合模式的 C# 源码
MCP 相关
服务通过 stdio 通信,并将只读 Roslyn 查询作为 MCP 工具提供。它在 MCP 握手前将指定解决方案载入缓存快照。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
English | 简体中文
CSharpAnalyserMcp
一个只读的 Model Context Protocol(MCP)服务,可让 AI 助手通过 Roslyn 浏览任意 C# 解决方案。
该服务使用 MSBuildWorkspace 将解决方案加载到缓存的 Roslyn 快照中,然后回答关于解决方案的结构化查询:每种类型的声明位置、XML 文档,以及方法的签名、文档、源代码位置和主体。因此,Agent 可以提取所需的确切源代码片段,而不必将整个文件读入上下文窗口。
- 传输方式:stdio(
StdioServerTransport),协议流量仅通过 stdout 输出;所有日志均写入 stderr。 - 解决方案路径通过命令行传入(
--workspace <path to .sln>);解决方案在 MCP 握手之前加载,因此路径无效时会立即失败,在 stderr 输出消息并以非零退出代码退出。 - 每个工具都返回 MCP 文本内容,从不返回
structuredContent:列表/层级/搜索工具每个条目返回一个文本块(从客户端角度看是字符串列表);详情工具则输出path / desc / namespace / signature / body块,各块之间以包含---的行分隔。 - 分发方式:以 .NET 工具 CSharpAnalyserMcp(命令
csharp-analyser-mcp)发布到 NuGet,并以io.github.undy-mosq/CSharpAnalyserMcp列入官方 MCP Registry。
安装
NuGet .NET 工具(推荐)
dotnet tool install --global CSharpAnalyserMcp
csharp-analyser-mcp --workspace D:\MyProject\MySolution.sln
# upgrade / remove
dotnet tool update --global CSharpAnalyserMcp
dotnet tool uninstall --global CSharpAnalyserMcp
请全局安装:--local 安装只会写入清单条目,因此 csharp-analyser-mcp 不会出现在 PATH 中,MCP 客户端也无法启动它。
使用 dnx 按需运行(无需安装,.NET SDK 10+)
# "--" separates dnx options from the arguments forwarded to the server
dnx CSharpAnalyserMcp --yes -- --workspace D:\MyProject\MySolution.sln
从源代码构建
git clone https://github.com/undy-mosq/CSharpAnalyserMcp
dotnet build CSharpAnalyserMcp\CSharpAnalyserMcp.csproj
dotnet run --project CSharpAnalyserMcp -- --workspace D:\MyProject\MySolution.sln
要求
| 用途 | 要求 |
|---|---|
运行已发布的工具(dotnet tool install --global) | PATH 中需有 .NET SDK 8.0 或更高版本(该包面向 net8.0) |
使用 dnx 按需运行 | .NET SDK 10.0 或更高版本 — dnx 随 SDK 一同提供 |
| 从源代码构建/运行 | .NET SDK 8.0 或更高版本(项目面向 net8.0) |
| 加载要分析的解决方案 | 能够打开该解决方案的 MSBuild 工具链 — Visual Studio 2022 或 VS Build Tools(Microsoft.Build.Locator 会注册最新安装的实例),或用于 SDK 风格项目的 .NET SDK 本身 |
客户端配置
该服务使用单个参数启动:--workspace,指向解决方案文件。
已安装的 .NET 工具
全局工具启动器位于 %USERPROFILE%\.dotnet\tools。MCP 客户端会自行启动进程,且只能看到继承的 PATH,因此请使用绝对路径指向启动器(展开 %USERPROFILE%,例如 C:\Users\you\.dotnet\tools\csharp-analyser-mcp.exe):
Claude Desktop(claude_desktop_config.json):
{
"mcpServers": {
"csharp-analyser": {
"command": "C:\\Users\\you\\.dotnet\\tools\\csharp-analyser-mcp.exe",
"args": ["--workspace", "D:\\MyProject\\MySolution.sln"]
}
}
}
VS Code(.vscode/mcp.json):
{
"servers": {
"csharp-analyser": {
"type": "stdio",
"command": "C:\\Users\\you\\.dotnet\\tools\\csharp-analyser-mcp.exe",
"args": ["--workspace", "D:\\MyProject\\MySolution.sln"]
}
}
}
仅当该目录已在客户端的 PATH 中时,裸命令名("command": "csharp-analyser-mcp")才有效;在安装工具之前启动的客户端会保留旧环境,因此更改 PATH 后请重启客户端。
使用 dnx 按需运行(无需安装)
{
"servers": {
"csharp-analyser": {
"type": "stdio",
"command": "dnx",
"args": ["CSharpAnalyserMcp", "--yes", "--", "--workspace", "D:\\MyProject\\MySolution.sln"]
}
}
}
在包 ID 后追加 @<version> 可固定版本。NuGet 包页面的 MCP Server 选项卡提供了相同的 JSON,可供复制。dnx 是一个 .cmd 启动器;如果客户端无法启动它,请改用等效的 dotnet 形式:"command": "dotnet",并设置 "args": ["tool", "exec", "CSharpAnalyserMcp", "--yes", "--", "--workspace", "D:\\MyProject\\MySolution.sln"]。
已发布的可执行文件
VS Code(.vscode/mcp.json):
{
"servers": {
"csharp-analyser": {
"type": "stdio",
"command": "C:\\Tools\\csharp-analyser\\CSharpAnalyserMcp.exe",
"args": ["--workspace", "D:\\MyProject\\MySolution.sln"]
}
}
}
从源代码运行项目
{
"servers": {
"csharp-analyser": {
"type": "stdio",
"command": "dotnet",
"args": [
"run",
"--project",
"D:\\MyProject\\CSharpAnalyserMcp\\CSharpAnalyserMcp\\CSharpAnalyserMcp.csproj",
"--",
"--workspace",
"D:\\MyProject\\MySolution.sln"
]
}
}
}
在 VS Code 中按 F5 可调试服务本身;.vscode/launch.json 已传入 --workspace 参数 — 请将其指向你自己的解决方案。
工具
| 工具 | MCP 注解 | 用途 |
|---|---|---|
list_classes_in_folder | 只读 | 列出文件夹及其子文件夹中的类型声明(每种类型对应一段文本) |
list_classes_in_namespace | 只读 | 列出命名空间中的类型声明(可选包含子命名空间;每种类型对应一段文本) |
get_class_documentation | 只读 | 返回根据类型声明行定位的类型的 XML 文档和成员签名 |
get_method | 只读 | 根据方法声明行及其所属类型的声明行定位方法,并返回其签名、XML 文档、位置和方法体 |
find_inheritance | 只读 | 列出类型的派生类型树(第一项为类型本身) |
find_method_overrides | 只读 | 列出方法的重写项/实现项(第一项为方法本身) |
search_regex | 只读 | 使用正则表达式逐行搜索目录/文件(默认仅搜索 *.cs);每个匹配项对应一段文本 |
reload_solution | 幂等 | 源文件更改后重新构建 Roslyn 快照 |
set_resolve_base_path | 幂等 | 更改用于解析相对路径的目录 |
list_classes_in_folder
{ "folderPath": "src/Services", "kinds": ["class", "interface"] }
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
folderPath | string,必填 | — | 绝对路径,或相对于解析基目录的路径(默认为解决方案目录)。 |
kinds | string[] | ["class"] | 要包含的类型种类(参见类型种类与 kinds)。 |
会跳过 bin/obj 下的文件。返回字符串列表,每种类型对应一段文本,格式为 {relativePath} L{start}-{end} {namespace}: {signature};对于全局命名空间,会省略命名空间部分;对于 partial 类型,会报告其首个声明的位置,并在末尾附加 ...(partial xN)。
list_classes_in_namespace
{ "namespaceName": "MyApp.Services", "includeSubNamespaces": true, "kinds": ["all"] }
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
namespaceName | string | 全局命名空间 | 命名空间名称,例如 MyApp.Services。空值、global、global namespace 或 <global namespace> 均表示全局命名空间。 |
includeSubNamespaces | bool | true | 同时包含在子命名空间中声明的类型。 |
kinds | string[] | ["class"] | 要包含的类型种类。 |
get_class_documentation
{ "typeDeclaration": "public static class ItemConfig", "maxXmlChars": 2000 }
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
typeDeclaration | string,必填 | — | 类型的声明行(列表工具输出的 signature 部分),例如 public static class ItemConfig。去除空白字符后,必须与规范签名或声明的第一行源码完全匹配。 |
namespaceName | string | 任意命名空间 | 类型所属的命名空间;空值或 global 表示全局命名空间。 |
maxXmlChars | int | 2000 | 每种类型原始 XML 文档的最大字符数(desc 字段);0 = 无限制。 |
匹配成功时,结果为一个文本块:path/desc/namespace(全局命名空间显示为 global)/signature/body(成员签名,每行一个);多个匹配项之间用仅包含 --- 的行分隔。未匹配时,会输出 (no match),随后是 candidates : 和候选声明行。
get_method
{ "methodDeclaration": "public static void SetEffect(RoleAttributes owner, int stack)", "typeDeclaration": "public class RoleAttributes", "namespaceName": "DuelModel" }
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
methodDeclaration | string,必填 | — | 方法的声明行,例如 public static string TextName(this ItemInfo.ItemType itemType)。方法名称及其参数类型会从此文本中解析。 |
typeDeclaration | string,必填 | — | 声明该方法的类/接口/结构体的声明行,例如 public static class ItemConfig。 |
namespaceName | string | 任意命名空间 | 声明类型所在的命名空间;为空或为 global 表示全局命名空间。 |
maxBodyChars | int | 8000 | 方法体的最大字符数;0 = 不限。 |
匹配规则:
- 两条声明行都必须在去除空白后,与规范签名或声明的首个源代码行完全匹配(不进行子字符串匹配)。
- 只搜索该类型自身声明的成员(普通方法;不包括继承成员、构造函数或属性)。也会匹配显式接口实现(
IFoo.Bar)。 - 从声明行解析出的参数类型会以忽略大小写和空白的方式进行比较,且不包含命名空间前缀;C# 别名会映射为其元数据名称(
int→Int32、string→String,……)。比较会逐步放宽:完全匹配 → 忽略泛型参数 → 允许参数列表前缀匹配。 - 未匹配时,会打印
(no match),随后是candidates :和候选声明行(先列出该类型中同名的方法,否则列出该类型的所有方法)。
每个重载对应一个文本块,包含:path / desc(原始 XML 文档,最多 2000 个字符)/ namespace / class / signature / body(移除最外层花括号和公共缩进后的源代码);重载之间以单独一行 --- 分隔。
find_inheritance
{ "typeDeclaration": "public abstract class BuffEntity", "namespaceName": "Duel" }
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
typeDeclaration | string,必填 | — | 类/接口/结构体/记录的声明行。 |
namespaceName | string | 任意命名空间 | 类型所在的命名空间;为空或为 global 表示全局命名空间。 |
第一项是所查询的类型本身(无缩进、无前缀);每个派生类型随后作为独立文本内容列出,缩进一个制表符,并按层级在前面加上 -。类根类型会列出派生自它的类/记录;接口根类型会列出子接口,以及直接实现它的类/结构体/记录。泛型基类型通过 OriginalDefinition 匹配(Base<T> 和 Base<int> 视为同一基类型)。每个类型只出现一次,取其最浅层级;未匹配时,会打印 (no match),随后是 candidates : 和候选声明行。
find_method_overrides
{ "methodDeclaration": "public abstract void Apply(BuffEntity buff)", "typeDeclaration": "public abstract class BuffEntity", "namespaceName": "Duel" }
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
methodDeclaration | string,必填 | — | 方法的声明行。 |
typeDeclaration | string,必填 | — | 声明该方法的类/接口/结构体的声明行。 |
namespaceName | string | 任意命名空间 | 声明类型所在的命名空间;为空或为 global 表示全局命名空间。 |
第一项是方法本身;实现/重写随后作为独立文本内容列出,缩进一个制表符,并按层级在前面加上 -。接口成员根方法会列出所有实现类型中的实现方法(包括显式 IFoo.Bar 实现);类方法根方法会列出派生类中的 override。只报告向下方向,不会向上追溯基类声明。未匹配时,会打印 (no match),随后是 candidates : 和候选声明行。
search_regex
{ "path": "src/Services", "pattern": "public\\s+\\w+\\s+Get", "fileFilters": ["*.cs"] }
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
path | string,必需 | — | 目录或文件:绝对路径,或相对于解析基目录的路径。 |
pattern | string,必需 | — | .NET 正则表达式,逐行匹配(默认区分大小写;使用 (?i) 可忽略大小写)。 |
fileFilters | string[] | ["*.cs"] | 文件名 glob(* / ?,不区分大小写;多个模式之间为 OR 关系);当 path 是文件时忽略。 |
- 递归搜索目录;跳过
bin/obj/.git和符号链接目录。文件路径则直接搜索。 - 每个匹配项对应一条文本内容:
{relativePath} L{line}: {line text}(行文本会去除首尾空白)。 - 最多返回 1000 个匹配项;结果被截断时,会追加一条
... [truncated: showing 1000 of N matches]。 - 直接读取磁盘上的文件,因此不受 Roslyn 快照 /
reload_solution影响。 - 路径不存在或正则表达式无效时,会在文本中返回原因并设置
isError;没有匹配项时返回(no match)。
reload_solution
无参数。丢弃缓存的快照并重新打开解决方案,因此后续查询会读取磁盘上的当前文件。输出 solution / resolveBaseDirectory / projects / reloaded : true ({ms} ms)。
set_resolve_base_path
{ "basePath": "src" }
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
basePath | string | 解决方案目录 | 绝对路径,或相对于当前基目录的路径。留空 / 省略时恢复默认值(解决方案文件所在目录)。 |
类型种类和 kinds
kind 使用小写,且各值互斥:class、static class、record、struct、record struct、interface、enum、delegate。只有类可以是 static,因此 class 和 static class 是不同的类型;结构体、枚举、接口和委托没有静态形式。
kinds 筛选器不区分大小写,并接受多个值(省略 kinds 时,行为等同于 ["class"]):
kinds 值 | 匹配项 |
|---|---|
(省略) / class | 所有类,包括静态类和记录 |
static / static class | 仅静态类 |
record | 记录和记录结构体 |
struct | 结构体和记录结构体 |
record struct | 仅记录结构体 |
enum / interface / delegate | 对应种类 |
all | 所有类型 |
同一张表、声明行协议(严格匹配 typeDeclaration / methodDeclaration 并通过 candidates 进行恢复)以及文本输出格式都会在 initialize 握手期间作为服务端级别的指令发送给客户端(参见 Tools/ServerInstructions.cs),因此模型无需猜测可接受的值或输出格式。
输出格式
每个工具都会返回 MCP 文本内容,不会返回 structuredContent。列表 / 层级 / 搜索工具会为每个条目返回一条文本内容,因此客户端收到的是字符串列表,而非以换行符连接的单个文本块。路径显示为相对于解析基目录的路径(默认为解决方案所在目录),并使用 / 作为分隔符。
列表(list_classes_in_folder / list_classes_in_namespace)
每种类型对应一条文本内容(工具返回字符串列表):
{relativePath} L{start}-{end} {namespace}: {signature}
全局命名空间会省略命名空间段(仅保留 :);partial 类型会显示其首个声明的位置,后接 ...(partial xN);未找到匹配项时返回单条 (no match)。
script/config/ItemConfig.cs L14-163: public static class ItemConfig
src/Services/UserService.cs L12-88 MyApp.Services: public class UserService : BaseService
类型详情(get_class_documentation)
每个同名类型对应一个文本块,各块之间以包含 --- 的行分隔:
path : {relativePath} L{start}-{end}
desc :
{raw XML documentation, truncated to maxXmlChars; the whole section is omitted when there is none}
namespace : {namespace; the global namespace prints as global}
signature : {modifiers + kind + name + type/primary-constructor parameters + constraints + base list}
body :
{member signatures, one per line: enum members / fields / properties / methods / events / nested types; omitted when there are none}
方法详情(get_method)
每个重载对应一个文本块,各块之间以包含 --- 的行分隔:
path : {relativePath} L{start}-{end}
desc :
{raw XML documentation, 2000-char cap}
namespace : {namespace}
class : {type name}
signature : {modifiers + return type + name + parameters (including this / ref / out / in / params)}
body :
{method source: block bodies lose their outermost braces and common indentation, expression bodies lose "=>"}
未找到匹配项时,会打印 (no match),随后是 candidates : 和候选声明行(简要行格式,可直接复制到声明参数中):
(no match)
candidates :
script/config/ItemConfig.cs L16-39: public static string TextName(this ItemInfo.ItemType itemType)
script/config/ItemConfig.cs L40-120: public static void RegisterItem(ItemModel model)
层级(find_inheritance / find_method_overrides)
第一项是查询的类型 / 方法本身(不缩进,也没有 -);派生类型 / 实现作为各自的文本内容列出,每个层级缩进一个制表符,并以 - 为前缀:
script/config/BuffEntity.cs L12-40 Duel: public abstract class BuffEntity
-script/config/DamageBuff.cs L8-25 Duel: public class DamageBuff : BuffEntity
-script/config/BurnBuff.cs L6-18 Duel: public class BurnBuff : DamageBuff
-script/config/HealBuff.cs L9-20 Duel: public class HealBuff : BuffEntity
没有匹配项时,会返回单个 (no match) 项;如果有候选项,后面会列出 candidates : 和候选声明行。
正则搜索(search_regex)
每个匹配项都是一个文本内容:
src/Services/UserService.cs L42: public User GetById(int id)
没有匹配项时,会返回单个 (no match) 项;匹配项超过 1000 个时,会在末尾追加一条截断提示。
工作区(reload_solution / set_resolve_base_path)
solution : {absolute path of the solution file}
resolveBaseDirectory : {resolve base directory}
projects : {project count}
reloaded : true ({ms} ms)
输出限制
较长的内容会被截断,以缩小工具结果;追加的标记格式为 ... [truncated: showing {n} of {m} chars]。所有阈值均在 Services/TextTruncator.cs 中设置。
| 内容 | 默认限制 | 如何提高限制 |
|---|---|---|
desc(XML 文档) | 2000 个字符 | 在 get_class_documentation 中使用 maxXmlChars(get_method 固定为 2000) |
方法 body | 8000 个字符 | 在 get_method 中使用 maxBodyChars |
将 0 传给 maxXmlChars / maxBodyChars,即可在本次调用中禁用截断。
典型工作流
- 使用指向解决方案的
--workspace启动一次服务(MCP 客户端会自动完成此操作)。 - 查找类型:
list_classes_in_namespace { "namespaceName": "MyApp.Services" }或list_classes_in_folder { "folderPath": "src/Services", "kinds": ["all"] };若要查找特定字符串,请使用search_regex { "path": "src", "pattern": "..." }。 - 通过从列表中复制类型的声明行,读取其契约:
get_class_documentation { "typeDeclaration": "public class UserService : BaseService" }。 - 读取确切的实现:
get_method { "methodDeclaration": "public User GetById(int id)", "typeDeclaration": "public class UserService : BaseService" }。 - 遍历层级:
find_inheritance { "typeDeclaration": "public abstract class UserServiceBase" }或find_method_overrides { "methodDeclaration": "public abstract User GetById(int id)", "typeDeclaration": "public abstract class UserServiceBase" }。 - 源文件发生更改后,在下一次查询前调用
reload_solution,否则查询仍会基于过期快照返回结果。
故障排除
- 服务立即退出,并在标准错误中显示
Solution file not found(或其他 MSBuild 错误)→--workspace路径错误;必须指向解决方案文件,而不是文件夹。 - 客户端的 MCP 日志中显示
The command "dnx" was not found→dnx随 .NET SDK 10 及更高版本提供。请安装 .NET SDK 10,或改用已安装的工具或已发布的可执行文件。 dotnet tool install报告该软件包不是 .NET 工具 → 你指定的版本在启用PackAsTool之前就已打包;请改为安装当前版本(dotnet tool list --global可显示已安装的工具)。- 客户端日志中显示
spawn csharp-analyser-mcp ENOENT或MCP error -32000: Connection closed→ 客户端无法解析被告知要运行的命令。请使用%USERPROFILE%\.dotnet\tools\csharp-analyser-mcp.exe的绝对路径(--local安装完全不会在此处提供 shim),然后重启客户端。 - 发布后立即出现
Cannot find package CSharpAnalyserMcp with version x.y.z→ NuGet 索引可能晚于发布更新。请稍后重试,或从已下载的.nupkg安装:dotnet tool install -g --add-source <folder> CSharpAnalyserMcp --version x.y.z。
行为说明与限制
- 快照语义。 解决方案只会从磁盘读取一次;只有调用
reload_solution后,文件编辑才会生效。未保存的编辑器缓冲区始终不可见。 - 正则表达式搜索读取磁盘。
search_regex直接读取文件,绕过 Roslyn 快照;默认搜索*.cs,递归时跳过bin/obj/.git,最多返回 1000 个匹配项。 - 仅查看源代码。 会跳过
bin/obj下的语法树,因此不会报告没有源代码声明的类型。 - 仅限已声明成员。
get_method不会遍历基类型、接口或partial类型的其他部分。 - 名称必须完全匹配。 类型和方法名称必须完全匹配;命名空间按完整显示字符串进行比较。使用
namespaceName(或带限定名的className)来区分。 - 开销。 每次查询都会请求每个项目生成其编译,因此响应时间会随着解决方案规模增大而变长。
- 诊断信息。 工作区加载问题会以
[WorkspaceFailed] {Kind}: {Message}的格式写入 stderr;stdout 中只会输出 JSON-RPC 内容。 - 每个进程仅支持一个解决方案。 工作区路径在启动时固定;要分析其他解决方案,请启动另一个实例。
许可证
MIT — 参见 LICENSE。
构建、发布和版本发布流程:docs/DEVELOPMENT.zh-CN.md(中文)。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。