ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Slnmap:基于Roslyn的代码图MCP Server,让AI理解.NET代码结构

Slnmap:基于Roslyn的代码图MCP Server,让AI理解.NET代码结构 Slnmap 是一个基于 Roslyn 的代码图 MCP server面向 .NET 代码库。它解决的问题很直接当 AI 编程助手需要理解一个 C# 解决方案时只靠文本检索是不够的。类型之间的继承关系、方法的调用链、符号的精确引用位置这些都是代码文本之外的结构化信息。Slnmap 通过 Roslyn 分析 .sln 和 .csproj把代码组织成可供查询的图结构再通过 MCP 协议开放给客户端。整条链路打通之后开发者可以在支持 MCP 的 AI 工具里直接问“OrderService 被哪些地方引用”并拿到带文件名和行号的可信答案。下面的内容会围绕 Slnmap 这一类工具拆开讲先解释为什么要用代码图加 MCP再给出环境准备和最小接入案例然后深入 Roslyn 实现代码图的关键 API最后补充验证、排错和工程化建议。适用于对 .NET 工具链、Roslyn 二次开发或 MCP 服务端开发感兴趣的读者。无论你是准备直接使用 Slnmap还是想基于相同思路自己写一个匹配团队代码库的 MCP server都可以从这些内容里找到起点。1. 为什么 .NET 代码库需要“代码图 MCP”1.1 AI 助手理解代码的两种方式现在很多团队尝试用大模型协助代码开发。最朴素的做法是把整个仓库喂给模型或者用向量数据库做语义检索。这种方式对“某段代码在做什么”的问题有效但对“这个接口被哪些模块调用”“这个类的父类来自哪个程序集”“重载方法中哪一个会被现有调用点命中”这类问题经常答错。原因在于这类问题依赖编译器级别的精确语义而不是文本相似度。Roslyn 能给出语法树和语义模型语法树告诉模型“这里有一个方法调用”语义模型告诉模型“这个调用实际指向哪个方法”。把这两层信息组合成图才算是真正的代码图。代码图和普通索引的区别可以类比成地图和地点列表。地点列表告诉你某个地址存在地图告诉你它和周围道路如何相连。对于代码审查、变更影响分析和重构辅助相连关系往往比单个符号更有价值。Slnmap 的设计目标正是把这些相连关系系统化类型之间、成员之间、调用点与定义之间最终都能变成一张可查询的图。1.2 MCP 在代码分析链路中的角色MCP 全称是 Model Context Protocol是一种让大模型应用调用外部工具的标准协议。它并不关心分析逻辑在哪台机器上执行只规定消息格式、工具声明和调用方式。MCP 中有两个核心角色客户端和服务器。客户端通常是 AI 应用或编辑器插件服务器负责执行具体工具并返回结构化结果。通信可以走 stdin/stdout也可以走 HTTP 或 WebSocket。对于 .NET 代码分析场景最常见的做法是采用 stdio 模式客户端启动一个本地进程进程的标准输入输出就是 MCP 消息通道。Slnmap 在这个协议里扮演服务器角色。它把 Roslyn 的分析能力封装成若干工具例如 search_type、find_references、get_callers。客户端不需要知道 Roslyn 怎么工作只需要声明“我要调用 find_references”服务器就会返回一份符合结构的 JSON。这样做还有一个好处分析逻辑可以复用。团队里不同的编辑器、不同的 AI 工具只要都支持 MCP就能连接到同一个代码分析服务不用为每个界面重复实现一遍 Roslyn 解析。1.3 Slnmap 的完整工作链路Slnmap 这个名字可以理解为 Solution Map即把解决方案映射成可导航的代码图。它的完整链路大致是四段。第一段读取 .sln 文件找到所有项目。第二段用 Roslyn 的 MSBuildWorkspace 打开项目获得语法树、语义模型和符号信息。第三段把这些信息抽取成节点和边节点可以是命名空间、类、接口、方法边可以是继承、实现、方法调用、字段引用。第四段把代码图封装成 MCP 工具通过 stdio 或 TCP 与客户端通信。链路中的每一段都有独立的失败点后面排错部分会专门展开。这里要强调一个容易误会的点代码图并不是项目启动时必须全部构建完成。Slnmap 这类工具更适合“按需查询”先加载解决方案建立符号索引当客户端请求某个符号的引用时再调用 Roslyn 的 SymbolFinder 做精确搜索。全量构建代码图对大型解决方案会造成可感知的启动延迟后面性能部分会给出优化建议。2. 准备环境SDK、客户端和项目结构2.1 运行环境要求开始之前先确认本机环境符合要求。Slnmap 依赖 Roslyn而 Roslyn 需要 .NET SDK 和可用的 MSBuild。注意这里需要 SDK 而不是 runtime因为 MSBuildWorkspace 要加载目标项目的 SDK 风格配置。如果机器上同时安装了多个 .NET 版本还要确认 Slnmap 目标和目标解决方案使用的版本范围。组件说明建议.NET SDK提供 Roslyn 和 SDK 风格项目支持.NET 8 或更高版本实际版本以 Slnmap 的 README 为准MCP 客户端发起查询并展示结果支持 MCP 的桌面客户端或编辑器插件目标代码库待分析的 .sln 或 .csproj仓库根目录尽量用绝对路径MSBuildRoslyn 加载项目依赖Windows 下可由 SDK/VS 提供Linux/macOS 使用 dotnet msbuild2.2 检查命令配置前先运行下面命令记录当前 SDK 版本避免后续出现依赖不匹配。dotnet --version dotnet --list-sdks dotnet --info如果dotnet命令不存在说明 SDK 没有正确安装。此时直接启动 Slnmap 会出现找不到运行时或 MSBuild 的错误。如果存在多个 SDK可以用 global.json 固定版本。2.3 代码库目录约束目标代码库需要先执行依赖还原因为 Roslyn 在加载项目时要读取 obj/project.assets.json 和 .deps 等信息。没有得到还原的项目即使编译能通过MSBuildWorkspace 也可能加载失败。运行下面的命令确保在后续分析之前依赖完整。cd /path/to/demo dotnet restore还要注意Slnmap 应该作为只读分析工具使用。不要在分析过程中让 server 同时写被分析目录避免生成文件、日志文件影响代码图结果。如果要分析源码建议先在干净的 checkout 或 CI 工作区里跑。3. 最小可运行案例把 Slnmap 接到客户端3.1 获取 SlnmapSlnmap 是开源项目常见来源是 GitHub。下面代码块展示了从源码构建的通用流程。实际仓库地址、项目路径和启动参数请以项目 README 为准这里只是给一个可参考的骨架。git clone https://github.com/example/slnmap.git cd slnmap dotnet restore dotnet build -c Release dotnet run --project src/Slnmap -- --sln /path/to/demo.sln如果项目发布为 .NET Tool也可以使用dotnet tool install安装再通过命令行直接启动。两种方式都需要最终得到一个可以被 MCP 客户端调用的长驻进程而不是一次性分析完就退出。3.2 配置 MCP 客户端拿到可执行文件或 DLL 后需要在 MCP 客户端里注册服务器。以支持 MCP 的桌面客户端为例配置通常是一个 JSON 文件里面声明服务器名称、启动命令和参数。下面是一个典型的 stdio 配置模式。{ mcpServers: { slnmap: { command: /usr/local/bin/dotnet, args: [ /path/to/Slnmap.dll, --sln, /path/to/demo.sln, --transport, stdio ] } } }不同客户端对配置结构的解释略有差异但核心思想一致把command指向可执行程序args传入解决方案路径。如果直接运行二进制会比 DLL 更简单可以把command改成二进制的完整路径并删除args中的dotnet。3.3 启动连接验证配置完成后先不要立刻打开客户端。在终端手动执行配置里的完整命令确认进程能正常启动并且控制台能看到类似“Slnmap MCP server started on stdio”的日志。如果进程启动后立即退出说明路径或参数有问题先在这里解决。然后重启客户端并进入 MCP 配置页。正常情况下客户端会连接服务器并抓取工具列表。如果配置正确应该能看到类似search_type、find_references的条目。如果看不到查看客户端日志常见原因是配置文件 JSON 格式错误、路径中缺少转义、或者进程写入 stdout 的调试日志污染了 MCP 协议。4. Roslyn 如何构建代码图核心实现要点4.1 用 MSBuildWorkspace 打开解决方案理解 Slnmap 的实现要从 Roslyn 的两个核心 API 入手。第一个是MSBuildWorkspace它能把 .sln 或 .csproj 加载成 Roslyn 的Solution对象。加载过程中Roslyn 会解析项目引用、NuGet 包、配置和编译参数和编译器看到的信息保持一致。using Microsoft.Build.Locator; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.MSBuild; MSBuildLocator.RegisterDefaults(); using var workspace MSBuildWorkspace.Create(); var solutionPath args.Length 0 ? args[0] : demo.sln; var solution await workspace.OpenSolutionAsync(solutionPath); Console.WriteLine($Projects: {solution.Projects.Count()}); foreach (var project in solution.Projects) { Console.WriteLine($ {project.Name} - {project.FilePath}); }代码里最关键的是MSBuildLocator.RegisterDefaults()。Roslyn 的MSBuildWorkspace需要和本机 MSBuild 交互如果在启动时没有注册合适的 MSBuild 实例会出现“Unable to locate a copy of MSBuild”之类的错误。RegisterDefaults会让 Roslyn 自动定位当前 .NET SDK 自带的 MSBuild。4.2 从语法树到语义模型第二个核心 API 是语义模型。拿到Solution之后遍历每个Document先获取SyntaxTree再通过Compilation获取SemanticModel。语法树只能告诉我们代码长什么样语义模型才能告诉我们代码代表什么。var project solution.Projects.First(p p.Name MyApp); var compilation await project.GetCompilationAsync(); foreach (var document in project.Documents) { var tree await document.GetSyntaxTreeAsync(); var model compilation.GetSemanticModel(tree); var root await tree.GetRootAsync(); foreach (var classDecl in root.DescendantNodes().OfTypeClassDeclarationSyntax()) { var symbol model.GetDeclaredSymbol(classDecl); if (symbol is not null) { Console.WriteLine(${symbol.Name} - {document.FilePath}); } } }用GetDeclaredSymbol可以把语法节点转换成符号例如ClassDeclarationSyntax对应INamedTypeSymbol。符号携带完整元数据所在命名空间、基类、实现的接口、成员集合等。代码图里的大部分节点信息都来自这一层。4.3 构建引用关系图有了符号之后下一步是建立关系。最简单的关系是“某符号出现在某个位置”。Roslyn 的SymbolFinder提供了 FindReferencesAsync
返回列表