让MCP服务走进生产:大晓智能无人农技服务平台的OAuth接入实践

互联网
2026
10/30
18:03
分享
评论

本地用STDIO跑通一个MCP服务,十分钟就够了。可一旦要把它开放给外部的AI客户端,问题就来了:谁在调用?属于哪个租户?没见过的客户端怎么接入?权限签出去还能不能收回?多实例怎么部署?这篇文章复盘大晓智能把无人农技服务平台的农业计算能力做成远程MCP服务、并接入自家认证中心的过程。

11.png

要点速览

• MCP授权:未带令牌返回401,客户端顺着受保护资源元数据找到认证服务器,再走OAuth授权码+PKCE流程。

• 客户端注册:新版MCP规范首选CIMD(client_id 即元数据文档地址),DCR(RFC 7591)作为兼容选项。

• 令牌:给第三方客户端签发引用令牌,用户撤销授权后立即失效。

• 部署:工具无状态时使用MCP无会话模式,多实例部署无需粘性路由。

背景:让人工智能调用农业计算能力

无人农技服务平台是大晓智能自研的SaaS平台,覆盖种植全流程,依托人工智能、卫星遥感、无人机、大数据等技术,赋能农场日常管理。平台内置AHA农业计算引擎,可实现施肥推荐、养分平衡调控、土壤pH调节、碳排放核算等功能。

平台把AHA拆成了几十个只做纯计算的MCP工具,希望让AI助手和大模型客户端直接调用,例如"帮我算一下这块玉米地该怎么施肥"。

这些计算依赖租户自己的地块和作物数据,所以服务不能裸奔,必须知道"是谁、哪个租户、有没有权限"。按照MCP规范的授权方案,这条链路最终长这样:

22.png

图 1 · MCP客户端接入的完整授权流程

客户端事先只知道一个MCP地址,剩下的每一步都要靠自己"发现"。下面按顺序讲每一环踩过的坑。

1 授权发现:让客户端自己找到登录入口

MCP规范要求,未带令牌的请求返回401,并在 WWW-Authenticate 头里带上 resource_metadata 地址。客户端顺着这个地址读取受保护资源元数据(/.well-known/oauth-protected-resource),从中拿到认证服务器地址,再去认证服务器读取注册、授权、令牌等端点。

社区已有现成的MCP安全组件,做了大部分工作,但上线时还是踩了两个坑。

坑一:拿不到401,只拿到403。 客户端第一次请求是一个不带令牌的POST,被安全框架默认开启的 CSRF 防护先拦下,直接返回403。客户端拿不到401,也就拿不到 resource_metadata,发现流程在第一步就断了。MCP入口是无状态的Bearer令牌接口,本来就不需要CSRF,关掉即可。

坑二:元数据里少了认证服务器。 自定义了元数据(补充资源名称和支持的scope),结果客户端说找不到认证服务器。原因是自定义的配置会整体替换组件的默认设置,必须在里面再显式设置一次 authorization_servers。以Spring Security为例,关键配置如下:

df5acf29-0c84-442b-a809-a4ef21474e94.png

经验法则: 授权发现链上的任何一环断了,客户端报出来的错误往往和真正的原因毫不相干。排查时要从第一个请求开始,逐条看响应。

2 客户端注册:从DCR到CIMD

传统做法是管理员在后台为每个应用登记client_id。可MCP客户端五花八门,每个用户的本机回调端口也不一样,人工登记不现实。MCP规范为此先后给出了两种方案。

先做了DCR

最早的方案是OAuth 2.0动态客户端注册(DCR,RFC 7591):客户端调用注册端点为自己注册,拿到client_id后再走标准的授权码流程。

允许匿名注册客户端,等于对外开了一个口子,所以给它加了几道护栏:

• 流程收窄:只允许授权码+刷新令牌,强制PKCE,并且必须经过用户确认。

• 回调与scope受限:回调只接受https、本机回环地址或白名单里的自定义协议;scope只能在允许范围内申请。

• 身份标记:动态注册的客户端有独立的标识,不挂靠任何租户,令牌的租户取自登录的用户。确认页会明确提示"第三方自助注册、未经审核",并显示回调主机。

• 防刷:注册接口同时按单个IP和全站总量限流,防止有人换IP刷接口。

• 闲置清理:定期清理长期未使用的动态客户端,闲置期不短于刷新令牌的有效期。

DCR的问题,以及CIMD

DCR跑起来之后,问题也很明显:每个客户端、每台电脑都会注册出一条新记录,认证中心得一直存着、限流、清理;而且注册来的客户端谁都可以自称"某某AI助手",身份无从验证。

所以新版MCP规范把CIMD(Client ID Metadata Document,客户端ID元数据文档)列为首选方案,DCR退为兼容选项。CIMD的思路很简单:client_id本身就是一个https地址,指向客户端自己托管的一份元数据JSON:

1fa03caa-b618-4ba7-a09f-656e9c090e44.png

认证中心在授权时去抓取这份文档,校验文档里的 client_id 与地址一致、回调地址在文档列出的范围内,然后照常走授权码流程。认证中心在自己的元数据里声明 client_id_metadata_document_supported,客户端就知道可以这样用。

55.png

图 2 · DCR 与 CIMD:客户端身份从哪里来

和DCR相比,CIMD有三个好处:

• 认证中心不用存客户端:没有注册接口,也就不需要限流和闲置清理。

• 身份锚定在域名上:确认页可以显示真实域名,用户能判断"这到底是谁"。

• 客户端自己维护:回调地址变了,只需改自己托管的那份文档。

代价是认证中心要主动去请求一个外部地址,必须防范SSRF:只允许https,禁止内网地址,限制响应大小和超时,并做好缓存。另外,本机回环回调依然可能被冒用,确认页上的提示不能省。

客户端选择注册方式的顺序是:有预登记的client_id就用它;认证中心支持CIMD就用CIMD;都不行再退回DCR。下一步就是在认证中心支持CIMD,同时保留DCR,兼容还没跟上的老客户端。

3 引用令牌:签出去的权限要收得回来

认证中心原本签发的都是自包含JWT,资源服务在本地验签,性能很好。但JWT 有个固有问题:签出去就收不回,过期前一直可用。

对后台登记过的内部应用来说,这可以接受。可自助接入的第三方客户端未经审核,用户在个人中心点了"撤销授权",就应该立即生效。于是让动态客户端改发引用令牌:令牌本身只是一串不透明字符串,MCP服务每次都调用认证中心的令牌内省端点(introspect)查询令牌状态,撤销后下一次请求就会失败。

66.png

图 3 · JWT 与引用令牌:撤销何时生效

为了让下游无感,内省响应里同样带上租户、用户和角色信息,与JWT的claim保持一致。权限方面,所有工具都要求令牌带有指定的scope,并在方法上做校验。

坑三:删了客户端,内省却报500。 授权记录单独存储,不会随客户端一起删除。残留的有效令牌在内省时,会按授权里记录的客户端ID回查客户端,查不到就是空指针。修复分两步:查不到时按接口约定返回null;后台删除客户端时同时撤销它的全部授权,并且先删客户端再撤销,避免两步之间又签出新令牌。

经验法则: 给第三方签发的权限,要优先考虑能不能撤销,其次才是性能。

4 无会话模式:多实例部署不再需要粘性

早期的Streamable HTTP传输是有会话的:客户端先发 initialize 握手,服务端返回一个 Mcp-Session-Id,后续请求都要带上它。问题在于,这个会话保存在创建它的实例内存里。

用一个轮询代理挂了三个后端实例做对照实验:在会话模式下必须开启粘性路由,把同一个会话的请求固定转发到创建它的实例,否则请求一旦落到别的实例,就会找不到会话。

77.png

图 4 · 会话模式与无会话模式的部署对比

新版MCP协议支持无会话(stateless)模式:不需要 initialize,也没有 Mcp-Session-Id,每个请求都自带完整的身份和参数。借着一次框架升级,把服务切到了无会话模式,只改了一项配置。

切换后,负载均衡器随意分发、实例随时扩缩容都没有问题。工具都是纯计算或查询,本来就不需要服务端保存会话状态,无会话模式正合适。

顺带一个工具定义上的坑:自动生成的JSON Schema曾把所有入参都标成必填,模型被迫为可选参数编造数值。现在逐个字段显式标注必填或可选,并补了Schema契约测试,防止它退化。

5 调试:为它写了一个调试器

授权链路一长,用命令行排查就很痛苦。于是写了一个MCP调试器,打包后是一个独立的可执行文件:

• 支持STDIO、Streamable HTTP、SSE三种连接方式

• 根据工具的入参Schema自动生成调用表单

• 每一条JSON-RPC请求、响应、通知都在时间线里,带耗时

• 服务返回401时自动拉起浏览器登录,支持动态注册和PKCE,令牌过期自动刷新

下面是连接AHA服务时的真实记录。右侧时间线里,前两次连接因为没有令牌而失败;第三次调试器识别出需要登录,自动拉起浏览器,用户登录后,initialize 和 tools/list 依次成功,左侧列出了全部30个工具。

88.png

图 5 · 调试器连接 AHA 服务:未登录失败 → 浏览器登录 → 列出工具

无会话模式下,服务端并不要求 initialize;调试器为了兼容老版本的服务,仍然会先发送它。

本文前面提到的几个坑,大多是在调试器的时间线里一眼看出来的。

结语:一张检查清单

57076e78-a516-4027-8cd4-f85b294c289f.png

把MCP服务开放出去,难点从来不在工具本身,而在于认证、授权和部署这些"周边"。好在这些问题都有现成的标准可循,按规范一步步补齐,就能让AI客户端安全地用上你的业务能力。

常见问题

MCP 服务怎样告诉客户端去哪里登录?

未带令牌的请求返回401,并在WWW-Authenticate头里给出resource_metadata 地址。客户端读取受保护资源元数据,从authorization_servers找到认证服务器,再读取认证服务器的元数据,拿到授权和令牌端点。

DCR 和 CIMD 有什么区别?

DCR是客户端调用注册接口,由认证中心生成并保存client_id;CIMD是客户端把元数据文档托管在自己的域名下,client_id就是这份文档的https 地址,认证中心授权时去抓取校验。新版MCP规范首选CIMD,DCR作为兼容选项。

为什么第三方 MCP 客户端要用引用令牌?

JWT签发后在过期前一直有效,无法立即撤销。引用令牌每次都要到认证中心内省,用户撤销授权或管理员删除客户端后,下一次请求就会失败。

什么情况下适合 MCP 无会话模式?

工具是纯计算或查询、不需要服务端保存会话状态时,适合无会话模式。它不需要 initialize 握手和Mcp-Session-Id,多实例部署可以直接轮询,无需粘性路由。

大晓智能无人农技服务平台的 MCP 服务提供哪些能力?

提供AHA农业计算工具,覆盖施肥推荐、养分平衡、土壤调酸和碳当量核算,AI助手可以通过MCP协议直接调用。所有调用都要经过OAuth授权,并按租户和scope校验权限。

THE END
广告、内容合作请点击这里 寻求合作
免责声明:本文系转载,版权归原作者所有;旨在传递信息,不代表砍柴网的观点和立场。

相关热点

相关推荐

1
3