> ## Documentation Index
> Fetch the complete documentation index at: https://mcp.developerdoc.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# OAuth Client Credentials

> 使用 OAuth 2.0 client credentials 流程为 MCP 提供机器到机器认证

OAuth Client Credentials 扩展（`io.modelcontextprotocol/oauth-client-credentials`）为 MCP 增加了对 [OAuth 2.0 client credentials 流程](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4)的支持。这让自动化系统无需交互式用户授权即可连接 MCP 服务器。

<Card title="规范" icon="file-lines" href="https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/oauth-client-credentials.mdx">
  OAuth Client Credentials 扩展的完整技术规范。
</Card>

## 它是什么

标准 MCP 授权流程要求用户以交互方式批准访问：浏览器打开，用户登录并授予权限。这对真人用户很合适，但在没有用户在场时就无法工作。

OAuth Client Credentials 扩展通过让客户端使用应用级凭据（客户端 ID 和 secret，或签名 JWT assertion）而不是委托用户凭据来解决这个问题。客户端直接向授权服务器证明自己的身份，授权服务器无需浏览器重定向或用户交互即可签发访问 token。

## 何时使用

以下场景适合使用 OAuth Client Credentials：

* **后台服务**需要按计划或响应事件调用 MCP 工具，且没有用户在场
* **CI/CD 流水线**需要在自动构建、测试或部署工作流中调用 MCP 服务器
* **服务器到服务器集成**需要连接两个后端系统，且不涉及最终用户
* **守护进程**或长时间运行的工作进程需要持续访问 MCP 资源

如果你的集成中存在应明确授权访问的真人用户，请改用标准 MCP 授权流程。

## 工作原理

该扩展支持两种凭据格式：

### JWT Bearer Assertions（推荐）

[RFC 7523](https://datatracker.ietf.org/doc/html/rfc7523) 定义的 JWT Bearer Assertions 允许客户端使用私钥签名 token，并将其作为身份证明提交。授权服务器使用客户端注册的公钥验证签名。

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant AS as Authorization Server
    participant MCP as MCP Server

    Client->>AS: POST /token<br/>grant_type=urn:ietf:params:<br/>oauth:grant-type:jwt-bearer<br/>assertion=<signed JWT>
    AS-->>Client: access_token
    Client->>MCP: MCP request (Bearer token)
```

JWT assertion 通常包含：

* `iss`: 客户端 ID（签发者）
* `sub`: 客户端 ID（被认证的主体）
* `aud`: 授权服务器 token 端点 URL
* `exp`: 过期时间
* `iat`: 签发时间

### 客户端密钥

对于更简单的部署，该扩展也支持使用 `client_id` 和 `client_secret` 的标准 client credentials 流程。客户端将凭据直接发送到授权服务器的 token 端点，并获得访问 token。

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant AS as Authorization Server
    participant MCP as MCP Server

    Client->>AS: POST /token<br/>grant_type=client_credentials<br/>client_id + client_secret
    AS-->>Client: access_token
    Client->>MCP: MCP request (Bearer token)
```

<Warning>
  客户端密钥是**长期有效凭据**，可在没有用户交互的情况下授予访问权限。如果 secret 泄露，攻击者可以静默地以你的应用身份完成认证，直到该 secret 被轮换。为降低风险：

  * 将密钥存储在密钥管理器中，绝不要放在源代码或提交到版本控制的环境文件里。
  * 按固定计划轮换密钥，并在怀疑泄露后立即轮换。
  * 将凭据 scope 限制到所需的最小权限。
  * 尽可能优先使用 JWT assertions，因为它们生命周期短，且不需要传输签名密钥。
</Warning>

## 实现指南

### 面向 MCP 客户端

要使用 OAuth Client Credentials 扩展，客户端必须：

<Steps>
  <Step title="声明支持">
    在 `initialize` 请求能力中包含该扩展：

    ```json theme={null}
    {
      "capabilities": {
        "extensions": {
          "io.modelcontextprotocol/oauth-client-credentials": {}
        }
      }
    }
    ```
  </Step>

  <Step title="获取访问 token">
    连接 MCP 服务器前，使用 client credentials grant 从授权服务器请求 token。
  </Step>

  <Step title="携带 token">
    在发送给 MCP 服务器的 HTTP 请求中，通过 `Authorization` 头传递 token：

    ```
    Authorization: Bearer <access_token>
    ```
  </Step>

  <Step title="处理 token 刷新">
    client credentials token 的生命周期通常短于用户委托 token。请实现 token 刷新逻辑，在过期前获取新 token。
  </Step>
</Steps>

### 面向 MCP 服务器

要接受 client credentials token，服务器必须：

<Steps>
  <Step title="验证 token">
    在每个请求中，使用授权服务器公钥验证 JWT 签名和 claims（通常通过 JWKS 端点）。
  </Step>

  <Step title="检查 scopes">
    确保 token 包含所请求操作所需的 scopes。
  </Step>

  <Step title="声明支持能力">
    可以在 `initialize` 响应中包含该扩展（为便于发现，建议这样做）：

    ```json theme={null}
    {
      "capabilities": {
        "extensions": {
          "io.modelcontextprotocol/oauth-client-credentials": {}
        }
      }
    }
    ```
  </Step>
</Steps>

## SDK 示例

官方 MCP SDK 内置支持 client credentials 认证，并会自动处理 token 获取和刷新。

<Steps>
  <Step title="安装 SDK">
    <Tabs>
      <Tab title="TypeScript">
        ```bash theme={null}
        npm install @modelcontextprotocol/client
        ```
      </Tab>

      <Tab title="Python">
        ```bash theme={null}
        pip install mcp
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="创建提供者并连接">
    选择与你的设置匹配的凭据格式：

    #### 使用 client secret

    <Tabs>
      <Tab title="TypeScript">
        ```typescript theme={null}
        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();
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        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])
        ```
      </Tab>
    </Tabs>

    #### 使用 JWT 私钥

    <Tabs>
      <Tab title="TypeScript">
        ```typescript theme={null}
        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();
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={null}
        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])
        ```
      </Tab>
    </Tabs>
  </Step>
</Steps>

## 客户端支持

<Note>
  不同客户端对此扩展的支持情况不同。扩展需要显式启用，默认永远不会激活。
</Note>

请查看[客户端矩阵](/extensions/client-matrix)，了解各 MCP 客户端当前的实现状态。

## 相关资源

<CardGroup cols={2}>
  <Card title="ext-auth 仓库" icon="github" href="https://github.com/modelcontextprotocol/ext-auth">
    源代码和参考实现
  </Card>

  <Card title="完整规范" icon="file-lines" href="https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/oauth-client-credentials.mdx">
    包含规范性要求的技术规范
  </Card>

  <Card title="RFC 6749 — Client Credentials Grant" icon="link" href="https://datatracker.ietf.org/doc/html/rfc6749#section-4.4">
    底层 OAuth 2.0 规范
  </Card>

  <Card title="RFC 7523 — JWT Bearer Assertions" icon="link" href="https://datatracker.ietf.org/doc/html/rfc7523">
    JWT assertion 格式规范
  </Card>
</CardGroup>
