OAuth Client Credentials

no
Summary: Machine-to-machine authentication for MCP using the OAuth 2.0 client credentials flow

Original Documentation

Documentation Index#

Fetch the complete documentation index at: https://modelcontextprotocol.io/llms.txt Use this file to discover all available pages before exploring further.

Machine-to-machine authentication for MCP using the OAuth 2.0 client credentials flow

The OAuth Client Credentials extension (io.modelcontextprotocol/oauth-client-credentials) adds support for the OAuth 2.0 client credentials flow to MCP. This enables automated systems to connect to MCP servers without interactive user authorization.

Full technical specification for the OAuth Client Credentials extension.

What it is#

The standard MCP authorization flow requires a user to interactively approve access — a browser opens, the user logs in, and grants permission. That works well for humans, but breaks down when there’s no user present.

The OAuth Client Credentials extension solves this by letting a client authenticate using application-level credentials (a client ID and secret, or a signed JWT assertion) rather than delegated user credentials. The client proves its identity directly to the authorization server, which issues an access token without requiring a browser redirect or user interaction.

When to use it#

Use OAuth Client Credentials when:

  • Background services need to call MCP tools on a schedule or in response to events, without a user present
  • CI/CD pipelines invoke MCP servers as part of automated build, test, or deployment workflows
  • Server-to-server integrations connect two backend systems where there’s no end user involved
  • Daemon processes or long-running workers need persistent access to MCP resources

If your integration has a human user who should explicitly authorize access, use the standard MCP authorization flow instead.

How it works#

The extension supports two credential formats:

Defined in RFC 7523, JWT Bearer Assertions let the client sign a token with its private key and present it as proof of identity. The authorization server validates the signature using the client’s registered public key.

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)

The JWT assertion typically includes:

  • iss: Client ID (the issuer)
  • sub: Client ID (subject being authenticated)
  • aud: Authorization server token endpoint URL
  • exp: Expiration time
  • iat: Issued-at time

Client Secrets#

For simpler deployments, the extension also supports the standard client credentials flow using a client_id and client_secret. The client sends its credentials directly to the authorization server’s token endpoint and receives an access token in return.

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)

Client secrets are long-lived credentials that grant access without user interaction. If a secret is leaked, an attacker can silently authenticate as your application until the secret is rotated. To reduce risk:

  • Store secrets in a secrets manager, never in source code or environment files checked into version control.
  • Rotate secrets on a regular schedule and immediately after any suspected compromise.
  • Scope credentials to the minimum permissions required.
  • Prefer JWT assertions when possible — they are short-lived and do not require transmitting the signing key.

Implementation guide#

For MCP clients#

To use the OAuth Client Credentials extension, your client must:

Include the extension in the initialize request capabilities:

    {
      "capabilities": {
        "extensions": {
          "io.modelcontextprotocol/oauth-client-credentials": {}
        }
      }
    }
    ```
  <span class="step-end"></span>

  <span class="step-marker" data-step-title="Obtain an access token"></span>
Request a token from the authorization server using the client credentials grant before connecting to the MCP server.
  <span class="step-end"></span>

  <span class="step-marker" data-step-title="Include the token"></span>
Pass the token in the `Authorization` header of HTTP requests to the MCP server:
Authorization: Bearer <access_token>
```

Client credentials tokens typically have shorter lifetimes than user-delegated tokens. Implement token refresh logic to obtain a new token before expiry.

For MCP servers#

To accept client credentials tokens, your server must:

On each request, verify the JWT signature and claims against your authorization server’s public keys (usually via a JWKS endpoint).

Ensure the token includes the required scopes for the requested operation.

Optionally (but recommended for discoverability), include the extension in the initialize response:

    {
      "capabilities": {
        "extensions": {
          "io.modelcontextprotocol/oauth-client-credentials": {}
        }
      }
    }
    ```
  <span class="step-end"></span>
<span class="steps-end"></span>

## SDK examples

The official MCP SDKs provide built-in support for client credentials authentication. Both handle token acquisition and refresh automatically.

<span class="steps-start"></span>
  <span class="step-marker" data-step-title="Install the SDK"></span>
<span class="tab-group-start"></span>
  <span class="tab-start" data-tab-title="TypeScript"></span>
    ```bash
        npm install @modelcontextprotocol/client
        ```
  <span class="tab-end"></span>

  <span class="tab-start" data-tab-title="Python"></span>
    ```bash
        pip install mcp
        ```
  <span class="tab-end"></span>
<span class="tab-group-end"></span>
  <span class="step-end"></span>

  <span class="step-marker" data-step-title="Create a provider and connect"></span>
Choose the credential format that matches your setup:

#### Using a client secret

<span class="tab-group-start"></span>
  <span class="tab-start" data-tab-title="TypeScript"></span>
    ```typescript
        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);

        // Use the client
        const tools = await client.listTools();
        console.log(
          "Available tools:",
          tools.tools.map((t) => t.name),
        );

        await transport.close();
        ```
  <span class="tab-end"></span>

  <span class="tab-start" data-tab-title="Python"></span>
    ```python
        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()

                # Use the client
                tools = await session.list_tools()
                print("Available tools:", [t.name for t in tools.tools])
        ```
  <span class="tab-end"></span>
<span class="tab-group-end"></span>

#### Using a JWT private key

<span class="tab-group-start"></span>
  <span class="tab-start" data-tab-title="TypeScript"></span>
    ```typescript
        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);

        // Use the client
        const tools = await client.listTools();
        console.log(
          "Available tools:",
          tools.tools.map((t) => t.name),
        );

        await transport.close();
        ```
  <span class="tab-end"></span>

  <span class="tab-start" data-tab-title="Python"></span>
    ```python
        from mcp.client.auth.extensions.client_credentials import (
            PrivateKeyJWTOAuthProvider,
            SignedJWTParameters,
        )
        from mcp.client.streamable_http import streamablehttp_client
        from mcp import ClientSession

        # Create a signed JWT assertion provider from key parameters
        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()

                # Use the client
                tools = await session.list_tools()
                print("Available tools:", [t.name for t in tools.tools])
        ```
  <span class="tab-end"></span>
<span class="tab-group-end"></span>
  <span class="step-end"></span>
<span class="steps-end"></span>

## Client support

<span class="callout-start" data-callout-type="note"></span>
  Support for this extension varies by client. Extensions are opt-in and never active by default.
<span class="callout-end"></span>

Check the [client matrix](/extensions/client-matrix) for current implementation status across MCP clients.

## Related resources

<span class="card-group-start" data-cols="2"></span>
  <span class="card-start" data-card-title="ext-auth repository" data-card-icon="github" data-card-href="https://github.com/modelcontextprotocol/ext-auth"></span>
Source code and reference implementations
  <span class="card-end"></span>

  <span class="card-start" data-card-title="Full specification" data-card-icon="file-lines" data-card-href="https://github.com/modelcontextprotocol/ext-auth/blob/main/specification/draft/oauth-client-credentials.mdx"></span>
Technical specification with normative requirements
  <span class="card-end"></span>

  <span class="card-start" data-card-title="RFC 6749 — Client Credentials Grant" data-card-icon="link" data-card-href="https://datatracker.ietf.org/doc/html/rfc6749#section-4.4"></span>
The underlying OAuth 2.0 specification
  <span class="card-end"></span>

  <span class="card-start" data-card-title="RFC 7523 — JWT Bearer Assertions" data-card-icon="link" data-card-href="https://datatracker.ietf.org/doc/html/rfc7523"></span>
JWT assertion format specification
  <span class="card-end"></span>
<span class="card-group-end"></span>
Link last verified June 7, 2026. View original ↗
Source: MCP Docs
Link last verified: 2026-02-26