OAuth Client Credentials 扩展(io.modelcontextprotocol/oauth-client-credentials)为 MCP 增加了对 OAuth 2.0 client credentials 流程的支持。这让自动化系统无需交互式用户授权即可连接 MCP 服务器。
Specification
OAuth Client Credentials 扩展的完整技术规范。
它是什么
标准 MCP 授权流程要求用户以交互方式批准访问:浏览器打开,用户登录并授予权限。这对真人用户很合适,但在没有用户在场时就无法工作。
OAuth Client Credentials 扩展通过让客户端使用应用级凭据(client ID 和 secret,或签名 JWT assertion)而不是委托用户凭据来解决这个问题。客户端直接向授权服务器证明自己的身份,授权服务器无需浏览器重定向或用户交互即可签发 access token。
何时使用
以下场景适合使用 OAuth Client Credentials:
- 后台服务需要按计划或响应事件调用 MCP tools,且没有用户在场
- CI/CD 流水线需要在自动构建、测试或部署 workflow 中调用 MCP 服务器
- 服务器到服务器集成需要连接两个后端系统,且不涉及最终用户
- 守护进程或长时间运行的 worker 需要持续访问 MCP 资源
如果你的集成中存在应明确授权访问的真人用户,请改用标准 MCP 授权流程。
工作原理
该扩展支持两种凭据格式:
JWT Bearer Assertions(推荐)
RFC 7523 定义的 JWT Bearer Assertions 允许客户端使用私钥签名 token,并将其作为身份证明提交。授权服务器使用客户端注册的公钥验证签名。
JWT assertion 通常包含:
iss: Client ID(签发者)
sub: Client ID(被认证的主体)
aud: 授权服务器 token 端点 URL
exp: 过期时间
iat: 签发时间
Client Secrets
对于更简单的部署,该扩展也支持使用 client_id 和 client_secret 的标准 client credentials 流程。客户端将凭据直接发送到授权服务器的 token 端点,并获得 access token。
Client secrets 是长期有效凭据,可在没有用户交互的情况下授予访问权限。如果 secret 泄露,攻击者可以静默地以你的应用身份完成认证,直到该 secret 被轮换。为降低风险:
- 将 secrets 存储在 secrets manager 中,绝不要放在源代码或提交到版本控制的环境文件里。
- 按固定计划轮换 secrets,并在怀疑泄露后立即轮换。
- 将凭据 scope 限制到所需的最小权限。
- 尽可能优先使用 JWT assertions,因为它们生命周期短,且不需要传输签名密钥。
实现指南
面向 MCP 客户端
要使用 OAuth Client Credentials 扩展,客户端必须:
声明支持
在 initialize 请求 capabilities 中包含该扩展:{
"capabilities": {
"extensions": {
"io.modelcontextprotocol/oauth-client-credentials": {}
}
}
}
获取 access token
连接 MCP 服务器前,使用 client credentials grant 从授权服务器请求 token。
携带 token
在发送给 MCP 服务器的 HTTP 请求中,通过 Authorization header 传递 token:Authorization: Bearer <access_token>
处理 token 刷新
client credentials token 的生命周期通常短于用户委托 token。请实现 token 刷新逻辑,在过期前获取新 token。
面向 MCP 服务器
要接受 client credentials token,服务器必须:
验证 token
在每个请求中,使用授权服务器公钥验证 JWT 签名和 claims(通常通过 JWKS 端点)。
检查 scopes
确保 token 包含所请求操作所需的 scopes。
声明支持能力
可以在 initialize 响应中包含该扩展(为便于发现,建议这样做):{
"capabilities": {
"extensions": {
"io.modelcontextprotocol/oauth-client-credentials": {}
}
}
}
SDK 示例
官方 MCP SDK 内置支持 client credentials 认证,并会自动处理 token 获取和刷新。
安装 SDK
npm install @modelcontextprotocol/client
创建 provider 并连接
选择与你的设置匹配的凭据格式:使用 client secret
import {
Client,
ClientCredentialsProvider,
StreamableHTTPClientTransport,
} from "@modelcontextprotocol/client";
const provider = new ClientCredentialsProvider({
clientId: "my-service",
clientSecret: "s3cr3t",
});
const client = new Client(
{ name: "my-service", version: "1.0.0" },
{ capabilities: {} },
);
const transport = new StreamableHTTPClientTransport(
new URL("https://mcp.example.com/mcp"),
{ authProvider: provider },
);
await client.connect(transport);
// 使用 client
const tools = await client.listTools();
console.log(
"Available tools:",
tools.tools.map((t) => t.name),
);
await transport.close();
from mcp.client.auth.extensions.client_credentials import (
ClientCredentialsOAuthProvider,
)
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession
provider = ClientCredentialsOAuthProvider(
server_url="https://mcp.example.com/mcp",
client_id="my-service",
client_secret="s3cr3t",
scopes="read write",
)
async with streamablehttp_client(
"https://mcp.example.com/mcp",
auth_provider=provider,
) as (read_stream, write_stream, _):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
# 使用 client
tools = await session.list_tools()
print("Available tools:", [t.name for t in tools.tools])
使用 JWT 私钥
import {
Client,
PrivateKeyJwtProvider,
StreamableHTTPClientTransport,
} from "@modelcontextprotocol/client";
const provider = new PrivateKeyJwtProvider({
clientId: "my-service",
privateKey: process.env.CLIENT_PRIVATE_KEY_PEM,
algorithm: "RS256",
});
const client = new Client(
{ name: "my-service", version: "1.0.0" },
{ capabilities: {} },
);
const transport = new StreamableHTTPClientTransport(
new URL("https://mcp.example.com/mcp"),
{ authProvider: provider },
);
await client.connect(transport);
// 使用 client
const tools = await client.listTools();
console.log(
"Available tools:",
tools.tools.map((t) => t.name),
);
await transport.close();
from mcp.client.auth.extensions.client_credentials import (
PrivateKeyJWTOAuthProvider,
SignedJWTParameters,
)
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession
# 基于密钥参数创建签名 JWT assertion provider
jwt_params = SignedJWTParameters(
issuer="my-service",
subject="my-service",
signing_key=open("private_key.pem").read(),
signing_algorithm="RS256",
lifetime_seconds=300,
)
provider = PrivateKeyJWTOAuthProvider(
server_url="https://mcp.example.com/mcp",
client_id="my-service",
assertion_provider=jwt_params.create_assertion_provider(),
scopes="read write",
)
async with streamablehttp_client(
"https://mcp.example.com/mcp",
auth_provider=provider,
) as (read_stream, write_stream, _):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
# 使用 client
tools = await session.list_tools()
print("Available tools:", [t.name for t in tools.tools])
客户端支持
不同客户端对此扩展的支持情况不同。扩展需要显式启用,默认永远不会激活。
请查看客户端矩阵,了解各 MCP 客户端当前的实现状态。
相关资源
RFC 6749 — Client Credentials Grant
底层 OAuth 2.0 规范
RFC 7523 — JWT Bearer Assertions
JWT assertion 格式规范