恋爱邦
107.39M · 2026-04-09
MCP(Model Context Protocol)连接 AI 工具和外部服务时,HTTP 传输层需要认证。目前绝大多数客户端(Claude Code、OpenClaw 等)的做法是静态 headers:
{
"mcpServers": {
"jina": {
"type": "http",
"url": "https://mcp.jina.ai/v1",
"headers": {
"Authorization": "Bearer jina_6b..."
}
}
}
}
简单直接,但有三个企业场景下的硬伤:
| 问题 | 说明 |
|---|---|
| Token 明文暴露 | 配置文件里躺着长期有效的密钥,泄露即全损 |
| 无法自动轮换 | 短期 Token(OAuth、SSO)过期后必须手动更新 |
| 多服务器管理成本 | 10 个 MCP 服务器 = 10 份独立的认证配置 |
Claude Code 在 v2.1.85 引入了 headersHelper 机制——用外部脚本动态生成认证头。
Claude Code 启动
→ 发现 MCP 服务器配置了 headersHelper
→ 注入环境变量:
CLAUDE_CODE_MCP_SERVER_NAME=gitlab
CLAUDE_CODE_MCP_SERVER_URL=
→ 执行指定脚本(10 秒超时)
→ 脚本输出 JSON 到 stdout
→ 将输出合并为 HTTP 请求头
→ 连接 MCP 服务器
{
"mcpServers": {
"gitlab": {
"type": "http",
"url": "https://gitlab.example.com/mcp",
"headersHelper": "/opt/bin/mcp-auth.sh"
}
}
}
输入:通过环境变量传入
| 环境变量 | 值 |
|---|---|
CLAUDE_CODE_MCP_SERVER_NAME | 配置中的服务器名(如 gitlab) |
CLAUDE_CODE_MCP_SERVER_URL | 服务器 URL |
输出:stdout 输出一个 JSON 对象(string key-value)
{"Authorization": "Bearer eyJhbGciOi..."}
约束:
#!/bin/bash
# /opt/bin/mcp-auth.sh
case "$CLAUDE_CODE_MCP_SERVER_NAME" in
gitlab)
# 从 macOS Keychain 取 token
TOKEN=$(security find-generic-password -s "mcp-gitlab" -w)
;;
jira)
# 从 HashiCorp Vault 取 token
TOKEN=$(vault kv get -field=token secret/mcp/jira)
;;
internal-api)
# 调 SSO 接口获取短期 token
TOKEN=$(curl -s
--data "grant_type=client_credentials&client_id=mcp"
-u "$USER:$(cat ~/.sso-secret)" | jq -r '.access_token')
;;
*)
echo '{}' && exit 0
;;
esac
echo "{"Authorization": "Bearer $TOKEN"}"
所有服务器指向同一个脚本:
{
"mcpServers": {
"gitlab": { "url": "...", "headersHelper": "/opt/bin/mcp-auth.sh" },
"jira": { "url": "...", "headersHelper": "/opt/bin/mcp-auth.sh" },
"internal-api": { "url": "...", "headersHelper": "/opt/bin/mcp-auth.sh" }
}
}
从 AWS Secrets Manager 取 token:
#!/bin/bash
SECRET_NAME="mcp/$CLAUDE_CODE_MCP_SERVER_NAME"
TOKEN=$(aws secretsmanager get-secret-value
--secret-id "$SECRET_NAME"
--query SecretString --output text | jq -r '.token')
echo "{"Authorization": "Bearer $TOKEN"}"
从 1Password CLI 取 token:
#!/bin/bash
TOKEN=$(op read "op://MCP/$CLAUDE_CODE_MCP_SERVER_NAME/token")
echo "{"Authorization": "Bearer $TOKEN"}"
内联写法(简单场景):
{
"headersHelper": "echo '{"Authorization": "Bearer '"$(op read op://MCP/jina/token)"'"}'"
}
需要明确:headersHelper 是 Claude Code 的私有扩展,不是 MCP 协议标准的一部分。
MCP 标准只定义了:
headers:静态 key-value 头但 headersHelper 这种"委托外部脚本生成认证头"的模式,有可能成为事实标准——因为它解决了 OAuth 覆盖不到的场景(Kerberos、mTLS token、企业 SSO、Vault 集成等)。
OpenClaw 目前 只支持静态 headers,不支持动态生成。相关 Issue(#61611)已提出需求,但尚未实现。
要实现类似 headersHelper 的能力,需要改造的核心路径:
在 mcp.servers 的配置结构中增加 headersHelper 字段:
// 当前
interface McpServerConfig {
url: string;
headers?: Record<string, string>;
transport?: 'sse' | 'streamable-http';
}
// 改造后
interface McpServerConfig {
url: string;
headers?: Record<string, string>;
headersHelper?: string; // 新增:外部脚本路径或内联命令
transport?: 'sse' | 'streamable-http';
}
在 MCP 客户端建立 HTTP 连接之前,检查是否配置了 headersHelper,如果有:
读取 headersHelper 配置
→ spawn 子进程执行脚本
→ 注入环境变量(服务器名、URL)
→ 捕获 stdout,解析 JSON
→ 与静态 headers 合并(动态优先)
→ 传递给 HTTP 客户端
核心伪代码:
async function resolveHeaders(serverName: string, config: McpServerConfig) {
const staticHeaders = config.headers ?? {};
if (!config.headersHelper) return staticHeaders;
const { stdout } = await execWithTimeout(config.headersHelper, {
env: {
...process.env,
MCP_SERVER_NAME: serverName, // OpenClaw 可以用自己的变量名
MCP_SERVER_URL: config.url,
},
timeout: 10_000,
});
const dynamicHeaders = JSON.parse(stdout);
return { ...staticHeaders, ...dynamicHeaders }; // 动态覆盖静态
}
MCP 连接断开重连时,需要重新执行 headersHelper(而不是复用上次的结果),确保短期 token 被刷新。
| 模块 | 改什么 | 工作量 |
|---|---|---|
| 配置 schema | 增加 headersHelper 字段 | 小 |
| MCP 客户端 | 连接前执行脚本、解析输出、合并 headers | 中 |
| 重连逻辑 | 重连时重新执行脚本 | 小 |
| CLI 命令 | openclaw mcp set 支持设置 headersHelper | 小 |
| 文档 | 配置说明 + 脚本示例 | 小 |
核心改动集中在 MCP 客户端的 HTTP 传输层,是一个边界清晰、风险可控的改造。