MCP 协议集成(Aegis.AI.Mcp)
当你希望同一份 Controller 方法既对外提供 REST API、又能让 LLM 通过 MCP 协议(Model Context Protocol)直接调用为 Tool 时,接入 Aegis.AI.Mcp。本文按 Aegis 3.x / .NET 8 口径说明组件的接入与使用方式,读完后你应该能完成组件引入、Tool 暴露(方法级或类级)、配置调整、ApiResponse 解包、XML 注释接入和连通性验证。
组件概览
| 字段 | 说明 |
|---|---|
| 组件名称 | MCP 协议集成 |
| 真实类库 | Aegis.AI.Mcp |
| 组件定位 | 把 MVC Controller 上的 [McpTool] 方法/类同时暴露为 REST API 和 MCP Tool,复用 MVC 权威发现避免幽灵工具 |
| 引入方式 | Component.deps.json |
| 组件声明 | Mcp |
| 核心能力 | Controller 方法/类双协议透出、Streamable HTTP 传输、Stateless 默认模式、Tool 自动扫描与去重、ApiResponse 自动解包、XML 注释 → description、HTTP 谓词推断 annotation、命名策略可配置 |
| 是否可扩展 | 是 |
| 目标框架 | net8.0 |
| 注册入口 | src/AI/Aegis.AI.Mcp/ServiceCollectionExtensions.cs |
这个组件的边界比较明确:它负责把已有的 MVC Controller 方法以 MCP Tool 形式对外暴露,并提供 /mcp HTTP 端点。它不负责重写 Controller、不接管路由、不替代 LLM 客户端代码。如果你需要把一段独立的业务逻辑(非 Controller 方法)暴露为 MCP Tool,更适合直接用官方 SDK 的 McpServerTool.Create(...) 手动注册,那属于另一路接入方案。
什么时候要用它
适合场景
- 已有 MVC Controller 提供业务接口,希望 LLM 也能调用相同业务,避免写两份代码
- Agent / RAG 系统需要调用 Aegis 业务方的能力(订单查询、库存更新、外部 API 透传等)
- 团队希望保持「业务方写一遍接口,REST 客户端和 LLM 都能用」的代码组织
- 已有大量 Controller 方法希望批量暴露,不想逐个加
[McpTool](用类级标记 +[McpToolIgnore]) - 业务侧统一返回
ApiResponse,希望 LLM 拿到干净数据而不是包装层(开UnwrapApiResponse) - 需要 Stateless、纯查询或幂等写入的 Tool 暴露
不适合场景
- 业务逻辑不在 Controller 里(在 BackgroundService、消息消费者、CLI 里)—— 适合用官方 SDK 的
McpServerTool.Create手动注册 - 需要长会话、sampling、elicitation 等 server→client 回调能力 —— 把
Stateless改为false,或评估是否走其他协议 - 一次性脚本 / 工具 —— MCP 协议本身就是给 Agent 长期订阅用的,单次调用场景用普通 HTTP 更直接
最小可运行路径
这一节给你一条最短路径,用来确认组件已经真正生效。
第一步:安装与配置
确保你已经:
- 安装
Aegis.AI.Mcp包(会传递依赖Aegis.Transfer,用于ApiResponse解包能力) - 在
Component.deps.json的Services和Middlewares中都加入Mcp - (可选)在
appsettings.json中加入Mcp配置节点
Component.deps.json:
{
"Components": {
"Services": [
"Mcp"
],
"Middlewares": [
"Mcp"
]
}
}
Services 阶段负责扫描 Controller、注册 MCP Server;Middlewares 阶段负责调用 MapMcp(...) 挂载 HTTP 端点。两者缺一不可。
中间件顺序建议把 Mcp 放在 Cors、Authentication 之后,便于 MCP Client 跨域和鉴权链路正常工作:
{
"Components": {
"Middlewares": [
"Cors",
"Authentication",
"Mcp"
]
}
}
第二步:在 Controller 上标记 [McpTool]
[McpTool] 既可标在方法上(精确控制),也可标在类上(批量暴露)。
姿势 A:方法级标记
using Aegis.AI.Mcp;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Demo.Controllers;
[ApiController]
[Route("api/demo")]
public class DemoController : ControllerBase
{
[AllowAnonymous]
[HttpGet("greet")]
[McpTool(Name = "demo_greet", Description = "传入姓名生成问候语")]
public GreetResponse Greet(string name)
{
return new GreetResponse
{
Name = name,
Greeting = $"Hello, {name}! (from MCP + REST)"
};
}
}
姿势 B:类级标记(批量暴露)
using Aegis.AI.Mcp;
using Aegis.Core.Infrastructure.Controller;
using Aegis.Transfer.Responses;
using Microsoft.AspNetCore.Mvc;
namespace Demo.Controllers;
[McpTool(Description = "多级缓存策略示例")] // 整个类批量暴露
public class CacheController : ApiControllerBase
{
/// <summary>Redis 基础读取</summary>
[HttpGet("EasyGet")]
public Task<ApiResponse> EasyGet() { /* ... */ }
/// <summary>Redis 容错降级读取</summary>
[HttpGet("RedisDegradation")]
public Task<ApiResponse> RedisDegradation() { /* ... */ }
[McpToolIgnore] // 单方法排除
[HttpGet("internal-debug")]
public Task<ApiResponse> Debug() { /* ... */ }
}
[McpToolIgnore] // 整个类排除(覆盖类级 [McpTool])
public class LegacyController : ControllerBase { /* ... */ }
类级标记下,扫描器自动按 HTTP 谓词、返回类型、参数类型做六道过滤(详见「类级扫描的六道过滤」),不会误把 helper、文件流、void 方法暴露给 LLM。Tool description 会从 XML <summary> 自动取,无需逐个声明。
方法同时被两种协议消费:
- REST:浏览器或 curl 访问
GET /api/demo/greet?name=xxx - MCP:LLM Agent 通过 MCP Client 连接
http://host/mcp,看到名为demo_greet的 Tool;类级批量标记时所有合格方法都会出现
第三步:启动应用并验证
启动后留意控制台的组件装配输出,正常情况应该看到类似:
MCP Server 注册成功(Stateless=True, 端点=/mcp, Server=Aegis.Mcp, 命名=SnakeCase, 注解推断=True)
共注册 N 个 MCP Tool(其中 M 个声明返回 ApiResponse,可走解包过滤)
已启用 ApiResponse 自动解包,覆盖 M 个 Tool ← 仅当 UnwrapApiResponse=true 时出现
MCP 端点注册成功: /mcp
随后用任意 MCP Client(如 Claude Desktop、Cursor、官方 inspector)连接 http://localhost:port/mcp,应能看到刚标记的 Tool。
配置项说明
组件读取 Mcp 配置节点,对应 McpOptions:
| 节点 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Mcp:RoutePattern | string | /mcp | MCP HTTP 端点的路由模式,可改为 /api/mcp 等以匹配自身路由规范 |
Mcp:Stateless | bool | true | 是否启用 Stateless 模式;关闭时使用 stateful session,支持 sampling / elicitation 等 server→client 回调 |
Mcp:ServerName | string | Aegis.Mcp | MCP Server 名称,出现在 initialize 响应的 serverInfo 中 |
Mcp:ServerVersion | string | 入口程序集版本号 | MCP Server 版本;为 null 时从入口程序集 AssemblyName.Version 自动填充 |
Mcp:UnwrapApiResponse | bool | false | 是否自动解包 ApiResponse 包装并标记业务错误。默认关以保持向后兼容,业务方按需开启 |
Mcp:InferAnnotations | bool | true | 是否按 HTTP 谓词推断 MCP annotation(readOnlyHint / destructiveHint / idempotentHint) |
Mcp:NamingStrategy | enum | SnakeCase | Tool 命名策略:SnakeCase / KebabCase / DotNotation |
Mcp:XmlCommentsPath | string | {CurrentDirectory}/{CurrentProject}.xml | XML 文档注释路径,与 Swagger 约定一致;文件不存在不报错 |
Mcp:ExtensionXmlComments | string[] | [] | 额外的 XML 注释路径列表(如 DTO 项目),支持通配符 |
完整配置示例:
{
"Mcp": {
"RoutePattern": "/mcp",
"Stateless": true,
"ServerName": "Aegis.Mcp",
"UnwrapApiResponse": true,
"InferAnnotations": true,
"NamingStrategy": "SnakeCase",
"XmlCommentsPath": "{CurrentDirectory}/{CurrentProject}.xml",
"ExtensionXmlComments": []
}
}
不写任何配置也能跑起来,全部走默认值(UnwrapApiResponse=false、InferAnnotations=true、NamingStrategy=SnakeCase)。
进阶能力
能力一:ApiResponse 自动解包
开启开关:Mcp:UnwrapApiResponse=true
业务侧大量使用 ApiResponse 包装(如 SuccessResult(...) / FailedResult(...)),但 LLM 看到包装层会混淆 —— 它会以为 code / message 是业务字段。开启解包后:
| 场景 | LLM 看到什么 |
|---|---|
业务成功(code = 200/201) | 裸 result 字段提升到顶层,LLM 直接拿到业务数据 |
| 业务失败(其他 code) | MCP 协议层 IsError=true + 业务消息(如 业务失败(code=500):xxx),LLM 直接向用户报错 |
裸 ApiResponse(无 result 字段) | message 文本作为响应内容 |
| POCO / 基础类型 | 原样透传,不受 Filter 影响 |
只对声明返回 ApiResponse / EntityResponse<T> / EntitiesResponse<T> 的方法生效(按声明类型精确匹配,不会误伤含 code/message 字段的第三方 DTO)。
示例:
[HttpGet("wrapped")]
[McpTool(Name = "demo_wrapped", Description = "演示 ApiResponse 透传风格")]
public ApiResponse GreetWrapped(string name)
{
return SuccessResult(new GreetResponse { Name = name, Greeting = $"Hello, {name}!" });
}
- 关闭
UnwrapApiResponse:LLM 拿到{ code: 200, messageType: 0, message: "OK", result: { name, greeting } } - 开启
UnwrapApiResponse:LLM 拿到{ name, greeting },失败时收到IsError=true
Filter 实现保证 JSON 解析异常时返回 null(原样透传),不会让成功的 Tool 调用变失败。
能力二:[FromServices] 真正可用
组件在扫描期从 IServiceCollection 的 descriptor 列表构造一个最小 IServiceProviderIsService 实现(ScanTimeServiceProbe),让 SDK 正确识别服务类型并自动剔除 [FromServices] 参数。
[McpTool(Name = "demo_echo_service", Description = "演示 [FromServices] 参数")]
public string EchoWithService([FromServices] ILogger<DemoController> logger, string message)
{
logger.LogInformation("Echo: {Message}", message);
return $"echo: {message}";
}
LLM 调用时只看到 message 参数,logger 由 DI 在运行时从 RequestContext.Services 解析。开放泛型(如 ILogger<T>)也按 GetGenericTypeDefinition 匹配。
[FromHeader] 和 [FromRoute] 参数在 MCP 协议里无意义(前者不是 LLM 该填的,后者是路径占位符),组件会在扫描阶段直接拒绝整个方法(不暴露为 Tool)并在 ComponentRegisterResult 中报告警告。这种处理方式比"暴露后调用失败"更稳,避免 LLM 看到工具却在调用时报错。
能力三:Tool 命名策略可配置
默认 snake_case,与 REST URL 风格一致。可通过 NamingStrategy 切换:
| 策略 | 分隔符 | 示例(OrderController.Create) |
|---|---|---|
SnakeCase(默认) | _ | order_create |
KebabCase | - | order-create |
DotNotation | . | order.Create |
{
"Mcp": { "NamingStrategy": "KebabCase" }
}
[McpTool(Name = "...")] 显式声明时优先采用,不走派生。方法级 Name 优先级高于类级 Name。
能力四:类级 [McpTool] + [McpToolIgnore]
类级 [McpTool] 是「批量暴露该类下所有 Action 方法」的开关本身,不需要额外的全局开关。
[McpTool(Description = "订单服务")] // 整个类批量暴露
public class OrderController : ControllerBase { /* ... */ }
[McpToolIgnore] // 整个类排除
public class LegacyController : ControllerBase { /* ... */ }
[McpToolIgnore] 在两个层级生效:
- 标在类上:整个类不会被扫描(即使类上同时有
[McpTool]) - 标在方法上:在类级批量扫描时跳过该方法
类级扫描的六道过滤
类级 [McpTool] 批量扫描时,扫描器按以下顺序过滤方法,避免误把不适合 LLM 调用的方法暴露出去:
| # | 过滤规则 | 排除的方法示例 |
|---|---|---|
| 1 | [McpToolIgnore] 显式排除 | 业务方主动排除的个别方法 |
| 2 | [NonAction] 标记 | helper 方法显式声明非 Action |
| 3 | 无 HTTP 谓词 | 没有 [HttpGet]/[HttpPost] 等的 helper |
| 4 | 返回 IActionResult / FileResult / minimal API IResult | 文件流、二进制响应(MCP 是 JSON-RPC,无法承载) |
| 5 | 返回 void / 裸 Task / 裸 ValueTask | 空响应(LLM 无从处理);Task<T> 不受影响 |
| 6 | 参数含 IFormFile / IFormFileCollection / IFormFile[] | 二进制文件上传(MCP 客户端通常不支持) |
方法级显式 [McpTool] 只走前两道([McpToolIgnore] / [NonAction]),因为方法级标记代表业务方知情同意,允许暴露特殊签名(如返回 IActionResult 的设计选择)。
能力五:XML 注释自动加载为 Tool description
零额外配置:开启 csproj 的 GenerateDocumentationFile 即可,路径与 Swagger 完全一致。不仅 <summary> 走 Tool description,<param> 也走参数 description、<returns> 作 Tool description 弱兜底。
复用 Swagger 的 {CurrentDirectory}/{CurrentProject}.xml 路径约定:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
description 按下面的优先级合并(取第一个非空值):
| 优先级 | 来源 | 示例 |
|---|---|---|
| 1 | 方法级 [McpTool(Description = "...")] | [McpTool(Description = "按 ID 查询订单")] |
| 2 | 方法级 [System.ComponentModel.Description("...")] | [Description("按 ID 查询订单")] |
| 3 | XML 注释 <summary> | /// <summary>按 ID 查询订单</summary> |
| 4 | 类级 [McpTool(Description = "...")] | [McpTool(Description = "订单相关工具")] |
| 5 | XML 注释 <returns>(前缀 Returns: ...,弱兜底) | /// <returns>订单详情 DTO</returns> → Returns: 订单详情 DTO |
| 6 | 自动兜底 | Order - GetById |
业务方只写 XML <summary> 即可让 Tool description 自动生成,无需在 [McpTool] 上重复声明。类级批量标记时这是首选方式(参考 demo CacheController)。
参数级 description
除了 Tool 自身的 description,每个参数也走多源合并写入 inputSchema 的 description 字段:
| 优先级 | 来源 | 示例 |
|---|---|---|
| 1 | 参数级 [System.ComponentModel.Description("...")] | [Description("订单号,大于 0")] long id |
| 2 | XML 注释 <param name="..."> | /// <param name="id">订单号,大于 0</param> |
| 3 | 留空 | 让 LLM 靠参数名推断语义 |
注入路径走 SDK 原生的 McpServerToolCreateOptions.SchemaCreateOptions.ParameterDescriptionProvider,业务方只要在 csproj 启用 GenerateDocumentationFile、在方法上写 <param> 注释,参数描述就会自动出现在 Tool schema 里。建议优先写 <param> 而不是 [Description] 特性 —— 前者同时服务于 Swagger / XML 文档,一份注释多处复用。
<summary> / <param> / <returns> 三种节点共用同一份 XML 文档(同一个 GenerateDocumentationFile 产物),不需要额外配置路径或开关注释源。
如需把跨项目的 DTO 注释也接入,配置扩展路径(支持通配符):
{
"Mcp": {
"ExtensionXmlComments": [ "{CurrentDirectory}/*.Dto.xml" ]
}
}
文件不存在或解析失败时优雅降级为警告,不影响 Tool 注册。
自定义扩展
命名 Tool
[McpTool] 支持两个属性:
| 属性 | 是否必填 | 说明 |
|---|---|---|
Name | 否 | Tool 全局唯一名称。LLM 通过此名称调用。留空时按 Mcp:NamingStrategy 派生 {controller}{sep}{method}(剥离 Controller 后缀) |
Description | 否 | 给 LLM 看的工具说明,建议写清楚「做什么」和「什么时候该调用」。类级标记时作为该类下所有方法的兜底描述 |
显式命名示例:
[McpTool(Name = "order_get_by_id", Description = "按订单 ID 查询订单详情。传入订单号返回订单实体。")]
public OrderDto GetOrder(long id) { ... }
选择返回风格
| 风格 | 写法 | UnwrapApiResponse=false 时 LLM 看到什么 | UnwrapApiResponse=true 时 LLM 看到什么 |
|---|---|---|---|
| POCO | 返回业务 DTO | { name, greeting } | { name, greeting }(不受影响) |
ApiResponse 包装 | 返回 SuccessResult(...) | { code, messageType, message, result } | result 字段提升到顶层 |
| 基础类型 | 返回 int / string 等 | 直接的基础类型值 | 直接的基础类型值(不受影响) |
三种风格的代码示例见 demo/Aegis.Webapi.BaseDemo/Controllers/McpDemo/McpDemoController.cs。
Controller 注入依赖
Controller 方法支持 [FromServices],Controller 实例的构造函数支持注入 DI 服务。ScanTimeServiceProbe 让 SDK 在生成 schema 时正确识别服务参数并自动剔除。Controller 实例的激活策略与 MVC DefaultControllerActivator 一致:
- 业务方调用过
AddControllersAsServices():Controller 直接从 DI 容器解析 - 否则:走
ActivatorUtilities.CreateInstance,按构造函数参数从 DI 解析
高级用法
与鉴权集成
MCP 端点跑在 ASP.NET Core 管线上,可以正常使用 Authentication / Authorization 中间件。如果 Tool 需要登录态:
[HttpGet("me")]
[McpTool(Name = "user_get_me", Description = "获取当前登录用户信息")]
[Authorize]
public UserDto GetMe()
{
// 从 HttpContext 取用户
...
}
注意 MCP Client 是否在请求头里带上鉴权凭据,这取决于 Client 的实现。
切换到 Stateful Session
如果业务需要 sampling(让 Server 调用 Client 的 LLM)、elicitation(向 Client 用户问询)等回调能力,把 Stateless 关掉:
{
"Mcp": {
"Stateless": false
}
}
Stateful 模式下 Server 会维护会话状态,适合多轮交互的 Agent 场景。
修改路由前缀
如果业务方的 API 全部带 /api/v1 前缀,希望 MCP 端点也归入:
{
"Mcp": {
"RoutePattern": "/api/v1/mcp"
}
}
HTTP 谓词推断 annotation
InferAnnotations=true(默认)时,扫描器按 REST 语义推断 MCP annotation,帮 LLM 在多步规划时优先选只读/幂等工具:
| HTTP 谓词 | ReadOnly | Destructive | Idempotent | 说明 |
|---|---|---|---|---|
HttpGet | true | false | true | 只读、幂等,最强保证 |
HttpPut | false | false | true | 可写、幂等,可安全 retry |
HttpDelete | false | true | false | 破坏性、非幂等,需谨慎 retry |
HttpPost / HttpPatch / 无谓词 | false | false | false | 默认值 |
SDK 默认对未声明的方法设 Destructive=true,组件改写为 false(更贴近 REST 语义),让 LLM 不会因为保守而拒绝调用 POST 接口。
接入后怎么确认生效
接入完成后,按顺序核对:
-
Component.deps.json的Services和Middlewares都包含Mcp - 启动日志中出现
MCP Server 注册成功和MCP 端点注册成功: /mcp - 启动日志中
共注册 N 个 MCP Tool,N 等于预期方法数(方法级标记 + 类级批量扫描的合格方法) - 如果开启
UnwrapApiResponse,启动日志中出现已启用 ApiResponse 自动解包,覆盖 M 个 Tool,M 等于声明返回ApiResponse的方法数 - 如果开启 XML 注释,启动日志中出现
加载了 K 个 XML 文档文件 - 用 MCP Client 列出 Tool 时,Tool 的 description 和每个参数的 description 都符合预期(
<summary>/<param>注释已注入) -
curl http://localhost:port/mcp能拿到 MCP 协议响应(不是 404) - 用 MCP Client 连接后,能列出预期的 Tool 列表,每个 Tool 的 description 与预期一致
- 调用 Tool 能拿到与对应 REST 接口一致的返回;开启
UnwrapApiResponse后ApiResponse已解包 - 含
[FromServices]参数的方法在 schema 里没有该参数
如果 N 不对,多半是扫描阶段没找到对应方法或被过滤规则排除,检查:
- 方法是否
public实例方法(不支持static) - 是否在
Controller类的公开方法上 - 是否被
IsSpecialName(如属性 getter)排除 - 类级扫描时:是否缺少 HTTP 谓词、是否返回
IActionResult/void、是否含IFormFile参数 - 是否出现重名被跳过(看
ComponentRegisterResult的 Fail 消息)
常见问题
为什么我加了 [McpTool],但 Tool 没出现?
优先检查下面几项:
- 方法是否
public实例方法(不支持static) - 所在类是否是 MVC 认可的 Controller(被
AddControllers()扫描到) - 类级扫描时是否被六道过滤规则排除(缺 HTTP 谓词、返回
IActionResult、参数含IFormFile等) - 是否出现重名 Tool,后来者被静默跳过(扫描器会调
AddFail但框架可能吞掉消息) - 是否依赖未注册(如
ApplicationPartManager还没初始化,扫描器会直接返回空列表)
重名 Tool 会怎样?
扫描器按发现顺序注册,遇到重名时跳过后来者并调用 ComponentRegisterResult.AddFail 记录原因。由于 Aegis 框架可能不展示 Fail 消息,建议所有 [McpTool] 都显式指定 Name,或切换 NamingStrategy 避免碰撞(例如 UserController.Delete 和 UserGroupController.Delete 在 SnakeCase 下都会推导出 user_delete,但 KebabCase 下变成 user-delete vs user-group-delete)。
开启 UnwrapApiResponse 后,POCO 方法会被影响吗?
不会。解包按方法的声明返回类型精确匹配 —— 只有声明返回 ApiResponse / EntityResponse<T> / EntitiesResponse<T> 的方法才会被 Filter 处理,POCO 和基础类型原样透传。Filter 内部用 HashSet<string> 做 Tool 名精确查找,零误伤风险。
为什么 ApiResponse 没被自动解包?
检查清单:
Mcp:UnwrapApiResponse是否为true(默认false)- 方法是否声明返回
ApiResponse或派生类型(POCO 不会被解包) - 启动日志是否出现
已启用 ApiResponse 自动解包(如果UnwrapApiResponse=true但没出现,说明没有声明返回ApiResponse的方法)
默认关闭是为了保持行为克制,业务方按需显式开启。
为什么 [FromServices] 参数注入失败?
确认依赖是否在 DI 容器里注册过。ScanTimeServiceProbe 通过查 IServiceCollection 的 descriptor 列表回答「这个类型注册过吗」,开放泛型(如 ILogger<T>)按 GetGenericTypeDefinition 匹配。如果同一个 Controller 通过 HTTP 调用时能注入成功,那通过 MCP 调用时也能注入成功 —— 真正的服务解析仍由 SDK 走 RequestContext.Services(scoped 容器)完成。
XML 注释为什么没生效?
检查清单:
- csproj 是否启用
<GenerateDocumentationFile>true</GenerateDocumentationFile> - 编译产物里是否真的有
{项目名}.xml文件(在bin/Debug/net8.0/下) Mcp:XmlCommentsPath路径是否正确(默认{CurrentDirectory}/{CurrentProject}.xml会替换占位符)- 启动日志是否出现
加载了 N 个 XML 文档文件(如果没出现,说明文件路径不对) - 启动日志的
加载了 N 个 XML 文档文件:A 条 summary / B 条 param / C 条 returns反映了三类节点的解析量 - 方法的
<summary>是否非空;参数的<param name="...">名字是否与方法参数一致
<summary> / <param> 注释里多换行/缩进会被自动压缩为单行空格分隔,不会污染 description。
类级 [McpTool] 暴露的方法比预期少?
类级批量扫描时会自动应用六道过滤(见「类级扫描的六道过滤」),检查被漏掉的方法:
- 是否有
[HttpGet]/[HttpPost]等谓词 - 是否返回
IActionResult/FileResult/ minimal APIIResult(被排除) - 是否返回
void或裸Task(被排除,Task<T>不受影响) - 是否含
IFormFile参数(被排除)
方法级显式 [McpTool] 只走前两道过滤([McpToolIgnore] / [NonAction]),允许暴露特殊签名。
配套阅读
- 想了解 Aegis 组件装配流程,参考 自定义组件注入
- 想了解 Controller 基座和统一响应类型(
ApiResponse/EntityResponse<T>/EntitiesResponse<T>),参考 基础设施 - 想了解 Swagger 的 XML 注释约定,参考 Swagger 文档扩展
- MCP 协议规范参考 Model Context Protocol 官方文档
- 官方 SDK 参考
ModelContextProtocol.AspNetCore