场景

一个第三方平台(如校园智能体平台)要连你的 MCP 服务,但它的「注册远程服务」表单只有 URL 输入框,没有自定义请求头的地方。

而你的 MCP 服务是 Bearer 鉴权的 —— 不收 Authorization 头就回 401。

症状

在平台里填正确 URL,连接失败:

SSE error: Non-200 status code (401)

用 curl 手测同一个 URL 带 Bearer 头却完全正常。说明问题在平台没把头发出去,不是你的服务有问题。

先确认:平台到底支不支持

别急着改服务端。先验证平台是不是真的吞了头字段。

两种确认方式:

  1. 看前端表单:抓平台页面的 JS chunk,找注册请求的 body 构造。如果只有 name / description / transport / httpUrl 四个字段,就是没有。
  2. 直接打它的 API:手动往注册接口塞 headers / authHeaders 字段,再查服务详情 —— 如果字段被丢弃,说明后端也不存。

实测(2026-09,灵犀平台):塞进去的 headers 字段被直接丢掉,返回的对象里根本没有这个键。

解法:让 nginx 把 URL 参数转成请求头

思路是把密钥放在 URL 里(平台支持填任意 URL),由 nginx 取出后转成 Authorization 头转发给后端。

第 1 步:定义 map

放在 nginx 的 http 上下文(nginx.conf.d/maps.conf 之类被 include 在 http 层的文件里):

map $arg_token $mcp_bearer_auth {
    default  "Bearer $arg_token";
    ''       $http_authorization;
}

语义:

  • URL 带 ?token=xxx → 转成 Authorization: Bearer xxx
  • 不带 token → 透传客户端自己带的 Authorization 头

第二行是关键,它保证了向后兼容:老的、能正常带头的客户端不受影响。

第 2 步:在目标路由上引用

location ^~ /your/mcp/path {
    proxy_pass http://127.0.0.1:3182;
    proxy_set_header Authorization $mcp_bearer_auth;
    # ... 其它常规头
}

第 3 步:平台里填带 token 的 URL

https://your.domain/your/mcp/path?token=<密钥>

验证矩阵(四种都要测)

改完必须把这个矩阵走完,漏测任何一格都可能埋雷:

请求预期含义
?token=<正确>200新路通了
?token=<错误>401密钥确实在起作用,不是形同虚设
无 token + Authorization 头200老客户端没被破坏
无 token 无头401默认仍是拒绝,没变成公开

坑:nginx 配置生成器不补分号

如果用的是「routes.json → 生成 nginx 配置」那套(见 platform deploy.sh 首次部署不生效 (Nginx Deploy Copy-Order Bug) 里的 generate-config.cjs),它的 proxy_set_headers 字段是直接拼接的:

for (const header of p.proxy_set_headers || []) lines.push(`proxy_set_header ${header}`);

没有补分号。 所以值里必须自己带上:

"proxyOptions": {
  "proxy_set_headers": ["Authorization $mcp_bearer_auth;"]
}

漏了分号,nginx -t 会报:

unexpected "}" in .../sites/xxx.conf:NNN

好消息是规范的 deploy.sh 自带 nginx -t 失败回滚,不会把坏配置留下去。

下次如何识别:生成器报 unexpected "}" 时,先去掉行尾看是不是分号缺失。看生成器源码里是 lines.push(\... ${header}\`) 还是 lines.push(\`... ${header};\`)`。

安全性权衡

  • 密钥明文出现在平台的服务配置里 —— 平台管理员能看到。这是把权限交给平台,能接受才这么干。
  • 密钥会出现在 nginx 的 $request_uri 和访问日志里。如果日志会外发,考虑改用请求头方案或给 MCP 单独发一把只读 key。
  • 比「直接在路由上硬编码 Authorization 头」安全:那样等于任何知道 URL 的人都能用,等于取消鉴权。
  • 比「改 MCP 服务端让它接受 URL 参数」省事:不动业务代码,不重启服务。

相关概念


本文归档于个人知识库 · 领域:CS & AI · 模块:04 Project_CS_AI/Network