MCP 服务端架构与源生成器 (MCP Architecture & Source Generators)
在传统的微服务或单体架构中,将业务接口暴露给大模型(LLM)需要大量的胶水适配:编写 MCP Schema、编写 JSON-RPC 反序列化代码、手动提取文档描述,并手动进行模型校验。
BitzOrcas.Modern 引入了 BitzOrcas.Mcp.SourceGenerator,在 C# 编译阶段自动分析带有 [GenerateMcpTool] 标记的 CQRS 消息,提取 XML 文档注释与主构造参数类型,生成标准化的 IMcpToolPartition 分区注册代码。整个过程完全免除运行时反射,完美契合 Native AOT 极致性能与低内存启动需求。
1. 编译期元数据声明:[GenerateMcpTool]
开发者只需要在应用层的 Mediator Command 或 Query record 上标注 [GenerateMcpTool] 属性,并编写标准的 C# XML 文档注释:
using BitzOrcas.Application.Commands;using BitzOrcas.Domain.Results;using BitzOrcas.Mcp.Attributes;
namespace BitzOrcas.Modules.Litigation.Application.Commands.Cases;
/// <summary>/// 创建新的诉讼案件卷宗并初始化案号/// </summary>/// <param name="CaseTitle">诉讼案件全称,例如:某科技公司与某创投机构投资合同纠纷案</param>/// <param name="CaseType">案件类型标识,例如:CivilLitigation 或 CommercialArbitration</param>/// <param name="ClaimAmount">诉讼标的金额(单位:元),必须大于零</param>/// <param name="DefendantName">主要被告或被申请人法定全称</param>[GenerateMcpTool("create_litigation_case", "创建新的诉讼案件卷宗,初始化案号并绑定承办律师")]public sealed record CreateLitigationCaseCommand( string CaseTitle, string CaseType, decimal ClaimAmount, string DefendantName): ICommand<Result<string>>;属性约束与参数提取物理法则
- 主构造参数即入参:生成器仅提取主构造函数(Primary Constructor)的参数作为大模型可见的参数列表。定义在 record 内部的 get-only 计算属性、委托注入或上下文服务不会泄露给 LLM;
- XML 注释映射为字段 Description:主构造参数上的
<param name="parameterName">注释内容,被自动提取为 JSON Schema 中对应属性的description,作为指导大模型填参的提示词依据; - 强类型与必填校验:非可空类型自动标注在 JSON Schema 的
required数组中;数值类型自动附加number/integer类型约束; - 命名稳定性:工具名称首选符合 OpenAI / Anthropic 规范的小写蛇形命名(Snake Case,如
create_litigation_case),保障多模型平台提示词调度的稳定性。
2. 源码生成器输出产物 (Roslyn Emit)
编译期分析器会在内存中为每个模块生成一个强类型工具分区类与依赖注入扩展。生成的代码逻辑结构如下所示:
// <auto-generated/>#nullable enable
namespace BitzOrcas.Modules.Litigation.Generated;
/// <summary>/// 编译期自动生成的 Litigation 模块 MCP 工具分区/// </summary>public sealed class LitigationMcpToolPartition : global::BitzOrcas.Mcp.Abstractions.IMcpToolPartition{ public string ModuleName => "Litigation";
public global::System.Collections.Generic.IReadOnlyList<global::BitzOrcas.Mcp.Abstractions.McpToolDefinition> GetTools() { return new global::BitzOrcas.Mcp.Abstractions.McpToolDefinition[] { new global::BitzOrcas.Mcp.Abstractions.McpToolDefinition( Name: "create_litigation_case", Description: "创建新的诉讼案件卷宗,初始化案号并绑定承办律师", InputSchemaJson: """ { "type": "object", "properties": { "CaseTitle": { "type": "string", "description": "诉讼案件全称,例如:某科技公司与某创投机构投资合同纠纷案" }, "CaseType": { "type": "string", "description": "案件类型标识,例如:CivilLitigation 或 CommercialArbitration" }, "ClaimAmount": { "type": "number", "description": "诉讼标的金额(单位:元),必须大于零" }, "DefendantName": { "type": "string", "description": "主要被告或被申请人法定全称" } }, "required": ["CaseTitle", "CaseType", "ClaimAmount", "DefendantName"] } """, ExecuteAsync: async (jsonElement, serviceProvider, cancellationToken) => { var mediator = global::Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions .GetRequiredService<global::BitzOrcas.Application.Mediator.IMediator>(serviceProvider);
var command = global::System.Text.Json.JsonSerializer.Deserialize<global::BitzOrcas.Modules.Litigation.Application.Commands.Cases.CreateLitigationCaseCommand>( jsonElement.GetRawText(), new global::System.Text.Json.JsonSerializerOptions(global::System.Text.Json.JsonSerializerDefaults.Web));
if (command is null) { return global::BitzOrcas.Mcp.Abstractions.McpToolResult.Fail("入参 JSON 反序列化失败"); }
var result = await mediator.SendAsync(command, cancellationToken); if (result.IsSuccess) { return global::BitzOrcas.Mcp.Abstractions.McpToolResult.Success(result.Value); }
return global::BitzOrcas.Mcp.Abstractions.McpToolResult.Fail(result.Error.Message); } ) }; }}3. 宿主装配与端点暴露 (Host Registration)
在 API 宿主(src/Hosts/BitzOrcas.Api)中装配 MCP 协议层非常简明:
3.1 服务注册 (Program.cs)
using BitzOrcas.Infrastructure.Mcp;
var builder = WebApplication.CreateBuilder(args);
// 1. 注册核心 MCP 传输层与工具分区聚合器builder.Services.AddBitzOrcasMcpProtocol();
// 2. 注册各业务模块的工具分区(由源生成器自动产出)builder.Services.AddLitigationMcpTools();
var app = builder.Build();3.2 中间件与端点映射
// 映射 MCP 协议标准端点(默认挂载到 /mcp)app.MapBitzOrcasMcp(McpProtocolDependencyInjection.DefaultRoutePattern);
app.Run();此时,BitzOrcas.Api 将在 /mcp 端点上监听客户端的标准 Streamable HTTP / Server-Sent Events (SSE) 协议,客户端发送 tools/list 请求即可动态发现已注册的工具清单。