基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
  • 知识卡片碎片时间过考点
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
    • AI 热点每日 AI 资讯与动向
AI 工作台
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-11-11 12:12

用 ASP.NET Core 落地远程 MCP 服务

dev.to · AI#MCP#身份认证#后端开发
Editor brief · 编辑速览

作者分享在 ASP.NET Core 10 中使用官方 C# SDK 构建远程 MCP 服务的经验,涉及 OAuth、客户端注册和 TLS 问题。摘录重点解释了多实例负载均衡下有状态会话的风险,以及选择无状态 HTTP 传输的原因。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

把 MCP 服务跑在自己的电脑上,和让普通用户在 Claude、ChatGPT 里点击「Connect」就能使用,是两件复杂度差很多的事。PersonaXpand 团队把连接器接进现有的 ASP.NET Core 10 应用,真正耗时间的是授权、客户端注册,以及一次只在生产环境出现的 TLS 握手失败。

这篇最有用的地方,是把远程 MCP 当成一个需要长期运维的服务来讲:多实例怎么部署,开放注册会带来什么风险,为什么本地连接成功仍然不够。它提供了一组具体实现,也留下了几处不能直接照搬的前提。

远程 MCP 为什么需要一整套 OAuth

PersonaXpand 是 Nextoria 在迪拜开发的社交媒体工具,连接器允许 Claude 和 ChatGPT 操作用户自己的账号,包括发帖、排期和回复评论。他们使用官方 C# SDK ModelContextProtocol.AspNetCore,服务运行在负载均衡后的多个实例上。

下面的配置解决请求落到不同实例时,会话无法接续的问题:

services.AddMcpServer(o =>
    {
        o.ServerInfo = new Implementation { Name = "personaxpand", Title = "PersonaXpand", Version = "1.0.0" };
    })
    .WithHttpTransport(o => o.Stateless = true)
    .WithTools<PersonaXpandTools>();

关键是 Stateless = true。Streamable HTTP 可以为客户端保留会话,但作者建议,除非需要服务端向客户端流式推送,否则使用无状态模式。这样每个请求自带 bearer token,任何实例都能处理。

接下来才是更重的部分:让助手发现授权服务,并取得客户端身份。

/.well-known/oauth-protected-resource 告诉助手该找哪个授权服务器;/.well-known/oauth-authorization-server 则列出授权、令牌、注册和撤销端点。

客户端 ID 有两条获取路径。动态客户端注册(DCR,RFC 7591)允许客户端 POST 名称和回调 URI,换取 ID;客户端 ID 元数据文档(CIMD)直接把 HTTPS URL 当作 ID,服务端访问该地址,读取包含回调 URI 的 JSON。作者主张两者都支持,因为不同助手的接入方式不同;至于 CIMD 会成为趋势,这是作者的判断。

他们采用的 OAuth 2.1 配置也很具体:

  • 每个授权码都使用 PKCE,只接受 S256,授权码有效期为 60 秒。
  • access token 有效期为 1 小时,refresh token 为 30 天;每次刷新都会轮换 refresh token,旧令牌随之退役。
  • 旧 refresh token 在轮换后 60 秒内重用,按网络重试容忍;超过窗口再出现,就撤销整个连接。
  • 授权码、access token、refresh token 都按哈希存储和查找;用户修改密码后,撤销全部助手连接。

这些数字是他们的配置选择。原文没有提供对照实验,不能据此认为每个团队都应采用相同期限。

为什么有了 PKCE,回调地址仍然要管

作者最初踩中的安全坑,来自开放 DCR。

攻击者可以注册自己的回调站点,再把真实的授权链接发给受害者。用户看到的是服务自己的登录页和授权按钮,但点击同意后,授权码会被送到攻击者那里。

PKCE 防不住由攻击者自己发起、并持有 verifier 的这条授权流程。

他们因此加入回调主机白名单:

public static readonly string[] DefaultRedirectHosts =
    { "claude.ai", "claude.com", "chatgpt.com", "openai.com", "vscode.dev" };

public bool IsRedirectHostAllowed(string? redirectUri)
{
    if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out var uri)) return false;
    if (IsLoopback(uri)) return true;            // 发到这里的授权码只会抵达用户自己的机器
    if (!_config.GetValue("Mcp:RedirectAllowlist", true)) return true;   // 应急关闭开关
    var extra = _config.GetSection("Mcp:ExtraRedirectHosts").Get<string[]>() ?? [];
    return HostIs(uri.Host, DefaultRedirectHosts.Concat(extra).ToArray());
}

要看清的是关闭开关所在的一行:当 Mcp:RedirectAllowlist 为 false 时,后续主机限制直接跳过。它方便紧急恢复接入,也确实放宽了这层防线。

桌面客户端常用 http://127.0.0.1:<random port> 回调,因此作者放行 loopback,并依据 RFC 8252 在匹配 loopback 回调时忽略端口。新增助手域名通过 Mcp:ExtraRedirectHosts 配置,无须重新部署。拒绝时,错误信息也要写清被拒绝的主机和联系渠道,不能只剩 invalid_redirect_uri。

但授权页面的责任还没结束。PersonaXpand 把能力分成「read」和「act」:前者查看帖子、分析和收件箱,后者发布、排期和回复;涉及通过用户账号发送消息的操作,仍要求在聊天中先确认。

作者还要求工具描述明确说明:来自账号外部的内容只能作为数据,不能作为指令。评论区里的一段文字可能诱导助手执行操作,用户允许读取评论,并不等于允许评论替自己下命令。

本地接入成功,为什么生产环境仍然失败

CIMD 增加了一条容易漏测的网络链路:MCP 服务端需要主动向客户端域名发起 HTTPS 请求,下载元数据。

作者遇到某个 MCP 目录登录失败,界面只提示「This app is not registered」。本地正常,生产异常,最终日志才暴露 TLS 握手失败。

他们的生产环境是 Azure App Service on Windows,出站 TLS 使用 SChannel。对方只接受 TLS 1.3 和 X25519 密钥交换,拒绝 TLS 1.2 及 P-256/P-384 曲线;双方没有共同可用的握手组合。作者把细节反馈给对方,对方第二天增加 TLS 1.2 与 P-256 支持,连接才恢复。

这是原文最明确的一次故障排查经历。它支持的结论也很直接:元数据请求失败时,要记录异常类型;CIMD 必须在与生产相同的操作系统和宿主环境里测试。一个笼统的「无法读取客户端元数据」,足以把传输层问题伪装成注册问题。

他们还补了几处运行细节。AddListToolsFilter 和 AddCallToolFilter 可以按客户端过滤工具,他们为遵守 Claude 目录规则隐藏了图像生成。破坏性调用则按账号加分布式锁:助手可能同时发起 20 次删除,如果删除阈值依赖近期计数,并发请求就可能全都读到零。

时钟同样会影响授权。他们发现过一台开发机比 UTC 偏差 4 小时,表现却是难懂的 invalid grant。最后,作者建议发布到官方 MCP Registry,并保留用于证明域名所有权的 DNS TXT 记录。

我的判断

这篇说得最对的,是把接入问题拆到了请求、授权、回调、工具执行和出站网络。只验证一次工具调用,确实覆盖不了这些边界。

不过,它是项目复盘,不是完整的安全实现规范。HostIs 和 IsLoopback 没有展开,无法仅凭片段确认主机边界及 URI 校验是否严密;按主机筛选也不能替代完整回调 URI 的校验。60 秒重试宽限期里如何返回令牌、如何处理并发刷新,原文也没说清。

白名单适合他们支持已知助手的场景,但会增加新客户端接入成本。相较之下,预注册客户端更容易控制信任范围,却需要维护注册关系。哪种合适,要看服务是否真的需要向未知客户端开放。

无状态部署、令牌期限、按账号串行执行,都是有理由的工程选择;作者没有给出性能或安全对照数据,应当把它们当作可参考的经验。

能拿来做什么

  • 如果你正把本地 MCP 搬到远程,先列出发现文档、客户端注册、授权、刷新和撤销的完整路径,再检查多实例之间是否依赖内存会话。
  • 如果你负责授权页面,把「读取」和「执行」能做什么写清楚,并让发送消息等操作保留明确确认步骤。
  • 如果你的 Node 或 .NET 服务需要下载 CIMD,在生产同类环境中验证请求,并保留能区分 TLS 故障与元数据错误的日志。
  • 如果工具支持批量删除,检查并发调用能否绕过计数和确认规则;多实例部署时,把账号级互斥落实到分布式锁。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
法院文件暗藏指令,揭示智能体注入风险
下一篇
Claude Code 记忆扩展该如何选

资讯速览

dev.to · AI · 2026-11-11 12:12

AI 评分 8.0 / 10

相关标签

#MCP#身份认证#后端开发

继续阅读

  • Qwen Code开放融合Qoder的Agent内核
  • 法院文件暗藏指令,揭示智能体注入风险
  • Claude Code 记忆扩展该如何选
  • RuView 用 WiFi 实现无摄像头空间感知
  • Fable 5.2 主打长任务编程与降价