本文详细讲解:从 Minimal API 到 Model Context Protocol:构建 CSDN RSS 订阅器的演进之路。
随着人工智能技术的快速发展,Model Context Protocol (MCP) 正在成为连接应用与 AI 助手 的 新一代标准协议 。本文将以构建一个 CSDN RSS 订阅器 为例,探讨从传统 Minimal API 到 MCP 架构的演进过程。
1. 简洁高效的起点
使用 WebApplication.CreateSlimBuilder 和 Minimal API 构建基础应用服务:
-
Minimal API是ASP.NET Core 6引入的一种更简洁的方式来构建HTTP API,它极大地减少了样板代码的数量。 -
WebApplication.CreateSlimBuilder是ASP.NET Core 8引入的构建器方法,是一个用于创建轻量级 Web 应用程序构建器,使用最少的WebApplicationBuilder默认值初始化类的新实例。
这种方法允许开发者从最小的基础开始,只添加实际需要的服务和中间件,从而获得更好的性能表现。
csharp
var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddHttpClient();
var app = builder.Build();
app.MapGet("/api/rss", async (HttpClient http) => {
var rss = await http.GetStringAsync("https://rss.csdn.net/chaitsimplelove/rss/map");
return Results.Ok(rss);
});
await app.RunAsync();
这种写法对于小型项目非常友好,代码集中在一个文件中,易于理解和快速开发。然而,当项目规模增长时,单一文件会变得难以维护。
2. 分层架构改进
为了提高可维护性,我们将 Minimal API 重构为分层架构:
csharp
// Program.cs
using Ai4c.ACP.Server.RssBot.Services;
using QuickStart.ServiceDefaults;
var builder = WebApplication.CreateSlimBuilder(args);
// Add services to the container.
builder.AddServiceDefaults();
// 注册 McpServer 服务
builder.Services.AddMcpServer()
.WithToolsFromAssembly()
.WithHttpTransport();
builder.Services.AddHttpClient();
builder.Services.AddScoped<IRssService, RssService>();
var app = builder.Build();
app.MapDefaultEndpoints(); // 将 endpoints 移到单独文件
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.MapMcp();
await app.RunAsync();
- 定义
RSS字段映射类
csharp
// Models/CsdnRssFeed.cs
namespace Ai4c.ACP.Server.RssBot.Models;
// 说明:遵循 csdn rss version="2.0"
public sealed class CsdnRssFeed
{
public string Title { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Link { get; set; } = string.Empty;
public string Language { get; set; } = string.Empty;
public string Generator { get; set; } = string.Empty;
public string Copyright { get; set; } = string.Empty;
public List<CsdnRssItem> Items { get; set; } = [];
public sealed class CsdnRssItem
{
public string Title { get; set; } = string.Empty;
public string Link { get; set; } = string.Empty;
public string Guid { get; set; } = string.Empty;
public string Author { get; set; } = string.Empty;
public DateTime PubDate { get; set; }
public string Description { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
}
}
IRssService关注点分离
csharp
public interface IRssService
{
Task<CsdnRssFeed> GetCsdnRssFeedAsync(string rssUrl);
}
IRssService实现
csharp
// Services/RssService.cs
using System.Xml.Linq;
namespace Ai4c.ACP.Server.RssBot.Services;
public partial class RssService(HttpClient httpClient) : IRssService
{
private string GetElementValue(XElement parent, string elementName)
{
var element = parent.Element(elementName);
if (element == null) return string.Empty;
// 处理 CDATA
var cdata = element.FirstNode as XCData;
return cdata?.Value ?? element.Value ?? string.Empty;
}
}
---
using Ai4c.ACP.Server.RssBot.Models;
using System.Xml.Linq;
namespace Ai4c.ACP.Server.RssBot.Services;
public partial class RssService
{
public async Task<CsdnRssFeed> GetCsdnRssFeedAsync(string rssUrl)
{
var response = await httpClient.GetAsync(rssUrl);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var doc = XDocument.Parse(content);
var channel = doc.Element("rss")?.Element("channel");
if (channel == null)
throw new InvalidOperationException("Invalid RSS format");
var feed = new CsdnRssFeed
{
Title = GetElementValue(channel, "title"),
Description = GetElementValue(channel, "description"),
Link = GetElementValue(channel, "link"),
Language = GetElementValue(channel, "language"),
Generator = GetElementValue(channel, "generator"),
Copyright = GetElementValue(channel, "copyright")
};
var items = channel.Elements("item");
foreach (var item in items)
{
feed.Items.Add(new CsdnRssFeed.CsdnRssItem
{
Title = GetElementValue(item, "title"),
Link = GetElementValue(item, "link"),
Guid = GetElementValue(item, "guid"),
Author = GetElementValue(item, "author"),
PubDate = DateTime.TryParse(GetElementValue(item, "pubDate"), out var pubDate) ? pubDate : DateTime.MinValue,
Description = GetElementValue(item, "description"),
Category = GetElementValue(item, "category")
});
}
return feed;
}
}
Endpoints目录添加RssEndpoints.cs
csharp
// Endpoints/RssEndpoints.cs
using Ai4c.ACP.Server.RssBot.Services;
using Microsoft.AspNetCore.Mvc;
namespace Ai4c.ACP.Server.RssBot.Endponts;
public static class RssEndpoints
{
// 默认 rss map 地址
private const string DefaultRssUrl = "https://rss.csdn.net/chaitsimplelove/rss/map";
public static void MapRssEndpoints(this WebApplication app)
{
// 获取 RSS 订阅内容
app.MapGet("/api/rss", async ([FromServices] IRssService rssService, [FromQuery] string? url = null) =>
{
try
{
var rssUrl = string.IsNullOrWhiteSpace(url) ? DefaultRssUrl : url;
var feed = await rssService.GetCsdnRssFeedAsync(rssUrl);
var apiResp = new ApiResponse(200, true, "ok", feed);
return Results.Ok(apiResp);
}
catch (Exception ex)
{
string error = $"Failed to fetch RSS feed: {ex.Message}";
//var apiResp = new ApiResponse(500, false, error);
return Results.BadRequest(error);
}
});
// 获取最新博客文章
app.MapGet("/api/rss/latest", async ([FromServices] IRssService rssService, [FromQuery] string ? url = null, [FromQuery] int count = 5) =>
{
try
{
var rssUrl = string.IsNullOrWhiteSpace(url) ? DefaultRssUrl : url;
var feed = await rssService.GetCsdnRssFeedAsync(rssUrl);
var latestPosts = feed.Items
.OrderByDescending(i => i.PubDate)
.Take(count)
.ToList();
var apiResp = new ApiResponse(200, true, "ok", latestPosts);
return Results.Ok(apiResp);
}
catch (Exception ex)
{
string error = $"Failed to fetch latest posts: {ex.Message}";
//var apiResp = new ApiResponse(500, false, error);
return Results.BadRequest(error);
}
});
}
record ApiResponse(int Code, bool Success, string Msg, object? Data = null);
}
这种结构将关注点分离,使得代码更易维护和测试。
- 测试验证浏览器访问接口地址:
bash
http://localhost:5050/api/rss/latest
显示信息如下:

经测试,基于 Minimal API 接口的 RSS 订阅成功!
3. 向 Model Context Protocol 演进
Model Context Protocol (MCP) 是一种新兴的标准协议,旨在为 AI 助手和应用程序之间提供标准化的通信方式 ,号称 AI时代的 USB-C 接口。它允许 AI 助手直接与应用程序交互,获取信息、执行操作,甚至参与决策过程。
接下来,我们需要在项目中安装 nuget 包:
csharp
<ItemGroup>
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.5.0-preview.1" />
</ItemGroup>
说明:此处演示使用
MCP的SSE/Streamable HTTP模式,所以安装ModelContextProtocol.AspNetCorenuget 包,如果是本地工具STDIO模式,可以使用ModelContextProtocolnuget 包即可。
3.1 MCP 基础架构
MCP 的核心是一个典型的 C/S 架构,其中主机应用程序可以连接到多个服务器:

MCP 由三个核心组件构成:Host、Client 和 Server。让我们通过一个实际场景来理解这些组件如何协同工作:
假设你正在使用 Claude Desktop (Host) 询问:"我桌面上有哪些文档?"
- MCP 主机 (MCP Hosts):Claude Desktop 作为 Host,负责接收你的提问并与 Claude 模型交互。
- MCP 客户端 (MCP Clients):当 Claude 模型决定需要访问你的文件系统时,Host 中内置的 MCP Client 会被激活。这个 Client 负责与适当的 MCP Server 建立连接。
- MCP 服务器 (MCP Servers):在这个例子中,文件系统 MCP Server 会被调用。它负责执行实际的文件扫描操作,访问你的桌面目录,并返回找到的文档列表。
整个流程是这样的:你的问题 → Claude Desktop(Host) → Claude 模型 → 需要文件信息 → MCP Client 连接 → 文件系统 MCP Server → 执行操作 → 返回结果 → Claude 生成回答 → 显示在 Claude Desktop 上。
除了核心组成,还包括以下部分:
- 本地数据源 (Local Data Sources):您的计算机的文件、数据库和 MCP 服务器可以安全访问的服务
- 远程服务 (Remote Services):可通过互联网访问的外部系统(例如,通过 API),MCP 服务器可以连接到这些系统
这种架构设计使得 Claude 可以在不同场景下灵活调用各种工具和数据源,而开发者只需专注于开发对应的 MCP Server,无需关心 Host 和 Client 的实现细节。
了解 MCP 更多信息,请查看:
3.2 RSS 订阅器的 MCP 实现
让我们将 CSDN RSS 订阅器 改造为符合 MCP 标准的服务:
csharp
// Tools/RssSubscriber.cs
using Ai4c.ACP.Server.RssBot.Models;
using Ai4c.ACP.Server.RssBot.Services;
using Microsoft.AspNetCore.Mvc;
using ModelContextProtocol.Server;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Ai4c.ACP.Server.RssBot.Tools;
// RSS订阅器工具
[McpServerToolType]
public sealed class RssSubscriber(ILogger<RssSubscriber> logger)
{
private const string DefaultRssUrl = "https://rss.csdn.net/chaitsimplelove/rss/map";
[McpServerTool(Name = "csdn_rss_subscriber"), Description("call this function to csdn rss subscriber.")]
public async Task<CsdnRssFeed?> CsdnRssSubscriberToolAsync([FromServices] IRssService rssService,
[Description("csdn rss url")] string url = DefaultRssUrl)
{
try
{
var rssUrl = string.IsNullOrWhiteSpace(url) ? DefaultRssUrl : url;
logger.LogDebug($"订阅CSDN RSS:{rssUrl}");
var feed = await rssService.GetCsdnRssFeedAsync(rssUrl);
return feed;
}
catch (Exception ex)
{
string error = $"Failed to fetch RSS feed: {ex.Message}";
logger.LogError(ex, error);
return null;
}
}
[McpServerTool(Name = "csdn_rss_latest"), Description("call this function to csdn rss latest.")]
public async Task<List<CsdnRssFeed.CsdnRssItem>?> CsdnRssLatestToolAsync([FromServices] IRssService rssService,
[Description("csdn rss url")] string url = DefaultRssUrl,
[Description("csdn rss count")] int count = 5)
{
try
{
var rssUrl = string.IsNullOrWhiteSpace(url) ? DefaultRssUrl : url;
logger.LogDebug($"订阅CSDN RSS:{rssUrl}");
var feed = await rssService.GetCsdnRssFeedAsync(rssUrl);
var latestPosts = feed.Items
.OrderByDescending(i => i.PubDate)
.Take(count)
.ToList();
return latestPosts;
}
catch (Exception ex)
{
string error = $"Failed to fetch latest posts: {ex.Message}";
logger.LogError(ex, error);
return null;
}
}
}
3.3 MCP 服务注册
在 Program.cs 中注册 MCP 服务端点:
csharp
using Ai4c.ACP.Server.RssBot.Services;
using QuickStart.ServiceDefaults;
var builder = WebApplication.CreateSlimBuilder(args);
// Add services to the container.
builder.AddServiceDefaults();
// 注册 McpServer 服务
builder.Services.AddMcpServer()
.WithToolsFromAssembly()
.WithHttpTransport();
// 注册 RSS 相关服务
builder.Services.AddHttpClient();
builder.Services.AddScoped<IRssService, RssService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
app.MapDefaultEndpoints();
app.UseHttpsRedirection();
app.MapMcp(); //使用 MCP
await app.RunAsync();
4. AI 助手集成示例
- 启动
MCP Server服务:

- 浏览器输入服务监听地址:

通过 MCP,AI 助手可以直接与我们的 RSS 订阅器交互:
这里我们可以使用本地工具 MCP Inspector 测试。
- 连接 mcp server 服务:

- 执行 tool 订阅交互

AI 助手可以获得结构化的响应:
json
{
"success": true,
"result": {
"content": [
{
"type": "text",
"text": "[{\"title\":\"基于 .NET Garnet 1.0.91 实现高性能分布式锁(使用 Lua 脚本)\",\"link\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155992850\",\"guid\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155992850\",\"author\":\"ChaITSimpleLove\",\"pubDate\":\"2025-12-17T11:08:18+08:00\",\"description\":\"本文介绍了在 .NET10 环境下使用 Garnet 数据库实现分布式锁的方法。通过GarnetClient与Lua脚本配合,实现了具备自动续期功能的分布式锁 GarnetDistributedLock,并提供了完整的测试用例验证其功能正确性与并发安全性,适用于需要跨进程或跨机器同步访问资源的场景。\",\"category\":\"\"},{\"title\":\"轻量级身份认证和授权管理插件 MiniAuth\",\"link\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155934365\",\"guid\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155934365\",\"author\":\"ChaITSimpleLove\",\"pubDate\":\"2025-12-15T10:39:08+08:00\",\"description\":\"MiniAuth 是一款轻量级 ASP.NET Core Identity 管理插件,支持 JWT、Cookie 认证,兼容多种数据库与应用类型。一行代码集成,提供用户、角色、权限管理界面,非侵入式设计,跨平台部署,助力快速构建安全认证系统。\",\"category\":\"\"},{\"title\":\"基于 .net 开发的细粒度权限管理库 Casbin.NET ,支持 ACL, RBAC, ABAC 访问\",\"link\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155918863\",\"guid\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155918863\",\"author\":\"ChaITSimpleLove\",\"pubDate\":\"2025-12-15T07:30:00+08:00\",\"description\":\"Casbin 是一个强大且高效的开源访问控制库,Casbin.NET 是 Casbin 在 .NET 平台上的实现版本,提供了完整的访问控制功能。它支持多种授权模型(如 ACL、RBAC、ABAC 等),基于 PERM 元模型(Policy, Effect, Request, Matchers)进行策略 enforcement。该库处于 production-ready 状态,可用于实际生产环境中的权限管理和访问控制需求。\",\"category\":\"\"},{\"title\":\"从美食城的思考,如何构建.net高并发系统?\",\"link\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155679749\",\"guid\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155679749\",\"author\":\"ChaITSimpleLove\",\"pubDate\":\"2025-12-08T00:18:30+08:00\",\"description\":\"基于拆分层(服务/数据拆分)解决扩展瓶颈;缓冲层(缓存+MQ)化解流量洪峰;防御层(网关+应用防护)三大核心理念构建高并发安全防线。遵循\\\"网关防外敌,应用防内乱\\\"原则,实现系统稳定高效运行,彻底排除内忧外患,让高并发不再是难题。\",\"category\":\"\"},{\"title\":\"如何定位 linux 环境 .net 进程资源占用高的程序代码\",\"link\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155677505\",\"guid\":\"https://blog.csdn.net/ChaITSimpleLove/article/details/155677505\",\"author\":\"ChaITSimpleLove\",\"pubDate\":\"2025-12-07T21:27:34+08:00\",\"description\":\"本文给出Linux下.NET高CPU完整现场排查链:top -H锁定线程→dotnet-dump抓dump→dotnet-dump analyze导出符号化堆栈,秒级定位到C#文件名与行号;附一键脚本与回传分析方案,三分钟指出热点代码。\",\"category\":\"\"}]"
}
]
}
}
到此处,说明 MCP Server 服务就可以使用了。只需把该 MCP Server 挂载开放公网访问,就可以被各种 AI Agent(智能体)使用。
5. MCP 架构的优势
- 5.1 标准化接口
MCP 提供了一套标准化的接口规范,使不同厂商的 AI 助手都能无缝集成应用功能。
- 5.2 上下文感知
通过 MCP,AI 助手可以理解应用的完整上下文,包括可用资源、功能能力和交互历史。
- 5.3 动态发现
AI 助手可以在运行时动态发现应用的功能,无需预先编程。
- 5.4 安全可控
MCP 支持细粒度的权限控制,确保 AI 助手只能访问授权的功能。
6. 架构演进的价值对比
| 特性 | Minimal API | MCP(AI时代的USB-C) |
|---|---|---|
| 开发速度 | 快 | 中等 |
| 可维护性 | 低(小项目)/差(大项目) | 高 |
| AI 集成能力 | 无 | 强 |
| 标准化程度 | 低 | 高 |
| 可发现性 | 差 | 好 |
| 上下文感知 | 无 | 强 |
7. 总结
从 Minimal API 到 Model Context Protocol 的演进代表了 AI 时代应用架构的重要转变:
- 从被动服务到主动协作:应用不再只是被动响应请求,而是主动与 AI 助手协作
- 从隐式接口到显式契约:通过 MCP 明确定义应用的能力和资源
- 从人类为中心到 AI 友好:设计考虑 AI 助手的使用场景
- 从静态功能到动态发现:支持运行时功能发现和调用
对于像 CSDN RSS 订阅器 这样的应用,MCP 架构不仅能满足传统 API 需求,还能为 AI 助手提供强大的集成能力。通过 MCP,我们可以让 AI 助手直接获取最新文章、搜索相关内容,甚至根据用户偏好推荐文章,真正实现智能化服务。
这种演进过程体现了软件架构适应 AI 时代的必然趋势。随着 MCP 标准的不断完善和普及,未来的应用开发将更多地考虑与 AI 助手的协作,创造出更加智能和便捷的用户体验。