---
title: Authenticate MCP server to self-hosted apps · Cloudflare Zero Trust docs
description: Cloudflare Access can delegate access from any self-hosted
  application to an Access for SaaS MCP server via OAuth. The OAuth access token
  authorizes the MCP server to make requests to your self-hosted applications on
  behalf of the user, using the user's specific permissions and scopes.
lastUpdated: 2025-10-22T21:11:06.000Z
chatbotDeprioritize: false
tags: MCP
source_url:
  html: https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/mcp-servers/linked-apps/
  md: https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/mcp-servers/linked-apps/index.md
---

Cloudflare Access can delegate access from any [self-hosted application](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-public-app/) to an [Access for SaaS MCP server](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/mcp-servers/saas-mcp/) via [OAuth](https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization). The OAuth access token authorizes the MCP server to make requests to your self-hosted applications on behalf of the user, using the user's specific permissions and scopes.

For example, your organization may wish to deploy an MCP server that helps employees interact with internal applications. You can configure [Access policies](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/#selectors) to ensure that only authorized users can access those applications, either directly or by using an MCP client.

```mermaid
flowchart LR
accTitle: Link MCP servers and self-hosted applications in Access
    subgraph SaaS["Access for SaaS <br> OIDC app"]
        mcp["MCP server <br> for internal apps"]
    end

    subgraph "Access self-hosted app"
        app1[Admin dashboard]
    end

    subgraph "Access self-hosted app"
        app2[Company wiki]
    end

		User --> client["MCP client"]
    client --> mcp
    mcp -- Access token --> app1
		mcp -- Access token --> app2
		idp[Identity provider] <--> SaaS
```

This guide covers how to use the Cloudflare API to link a self-hosted application to a remote MCP server. The core of this feature is the `linked_app_token` rule type, which allows an Access policy on one application to accept OAuth access tokens generated for another.

## Prerequisites

* A [self-hosted Access application](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/self-hosted-public-app/)

## 1. Secure the MCP server with Access for SaaS

The first step is to add the MCP server to Cloudflare Access as an OIDC-based SaaS application. For step-by-step instructions on how to add an MCP server, refer to [Secure MCP servers with Access for SaaS](https://developers.cloudflare.com/cloudflare-one/applications/configure-apps/mcp-servers/saas-mcp/).

## 2. Get the SaaS application ID

Get the `id` of the MCP server SaaS application:

Required API token permissions

At least one of the following [token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/) is required:

* `Access: Apps and Policies Revoke`
* `Access: Apps and Policies Write`
* `Access: Apps and Policies Read`

```bash
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps" \
  --request GET \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
```

```json
{
  "id": "3537a672-e4d8-4d89-aab9-26cb622918a1",
  "uid": "3537a672-e4d8-4d89-aab9-26cb622918a1",
  "type": "saas",
  "name": "mcp-server-cf-access",
  ...
}
```

## 3. Create an Access policy

1. Create the following Access policy, replacing the `app_uid` value with the `id` of your SaaS application:

   Required API token permissions

   At least one of the following [token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/) is required:

   * `Access: Apps and Policies Write`

   ```bash
   curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/policies" \
     --request POST \
     --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
     --json '{
       "name": "Allow MCP server",
       "decision": "non_identity",
       "include": [
           {
               "linked_app_token": {
                   "app_uid": "3537a672-e4d8-4d89-aab9-26cb622918a1"
               }
           }
       ]
     }'
   ```

   Note

   The `linked_app_token` rule type only works with [`non_identity` decisions](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/#service-auth), similar to service token rules.

2. Copy the Access policy `id` returned in the response:

   ```json
   {
       "created_at": "2025-08-06T20:06:23Z",
       "decision": "non_identity",
       "exclude": [],
       "id": "a38ab4d4-336d-4f49-9e97-eff8550c13fa",
       "include": [
         {
           "linked_app_token": {
             "app_uid": "6cdc3892-f9f1-4813-a5ce-38c2753e1208"
           }
         }
       ],
       "name": "Allow MCP server",
       ...
   }
   ```

This policy will allow requests if they present a valid OAuth access token that was issued for the specified SaaS application.

## 4. Update the self-hosted application

You can add the `linked_app_token` policy to any `self_hosted` application in your Zero Trust account. Other app types (such as `saas`) are [not currently supported](#known-limitations).

1. Get your existing self-hosted application configuration:

   Required API token permissions

   At least one of the following [token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/) is required:

   * `Access: Apps and Policies Write`
   * `Access: Apps and Policies Read`

   ```bash
   curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps/$APP_ID" \
     --request GET \
     --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"
   ```

2. Add the Access policy to the self-hosted application. To avoid overwriting your existing configuration, the `PUT` request body should contain all fields returned by the previous `GET` request.

   Required API token permissions

   At least one of the following [token permissions](https://developers.cloudflare.com/fundamentals/api/reference/permissions/) is required:

   * `Access: Apps and Policies Write`

   ```bash
   curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/apps/$APP_ID" \
     --request PUT \
     --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
     --json '{
       "policies": [
           "a38ab4d4-336d-4f49-9e97-eff8550c13fa"
       ]
     }'
   ```

## 5. Configure the MCP server

With the policy in place, every API request to the self-hosted application must now include a valid `access_token` from Cloudflare Access. You will need to configure the MCP server to forward the `access_token` in an HTTP request header:

```txt
Authorization: Bearer ACCESS_TOKEN
```

The end-to-end authorization flow is as follows:

1. The MCP server authenticates against the Access for SaaS app via OAuth.
2. Upon success, the MCP server receives an `access_token`.
3. The MCP server makes an API request to the self-hosted application with the token in the request headers.
4. Cloudflare Access intercepts the request to the self-hosted app, inspects the token, and validates it against the `linked_app_token` rule in the policy.
5. If the token is valid and was issued for the linked SaaS app, the request is allowed. Otherwise, it is blocked.

## Known limitations

The MCP OAuth feature only works with self-hosted applications that rely on the [Cloudflare Access JWT](https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/validating-json/) to authenticate and identify the user. If the application implements its own layer of authentication after Cloudflare Access, then this feature is at best a partial solution. Requests that are successfully authenticated by Access may still be blocked by the application itself, resulting in an HTTP `401` or `403` error.
