-
Notifications
You must be signed in to change notification settings - Fork 9.9k
[ZT] Access MCP servers #24156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
[ZT] Access MCP servers #24156
Changes from 15 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
83a2144
define mcp servers
ranbel ebd26b5
mcp client definition
ranbel 9b96252
edit client definition
ranbel 355d425
add intro text and diagram
ranbel 87c6ee3
split content into two pages
ranbel 2faef9d
SaaS app UI instructions
ranbel 97ac489
generic mcp server config
ranbel d910047
incorporate Workers example
ranbel 0a8e66b
fix link typo
ranbel cf3611b
api example
ranbel 6e2d74b
add API calls
ranbel 1781460
access_token header
ranbel 0d4734f
known limitation
ranbel df6621e
non_identity decision
ranbel 4547cb9
replace atlassian example
ranbel 28e22f8
Update src/content/docs/cloudflare-one/applications/configure-apps/mc…
ranbel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
12 changes: 12 additions & 0 deletions
12
src/content/docs/cloudflare-one/applications/configure-apps/mcp-servers/index.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| --- | ||
| pcx_content_type: navigation | ||
| title: MCP servers | ||
| sidebar: | ||
| order: 3 | ||
| group: | ||
| hideIndex: true | ||
| --- | ||
|
|
||
| import { DirectoryListing } from "~/components"; | ||
|
|
||
| <DirectoryListing /> |
152 changes: 152 additions & 0 deletions
152
...ent/docs/cloudflare-one/applications/configure-apps/mcp-servers/linked-apps.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| --- | ||
| pcx_content_type: how-to | ||
| title: Authenticate MCP server to self-hosted apps | ||
| sidebar: | ||
| order: 2 | ||
| label: Enable MCP OAuth to self-hosted apps | ||
| --- | ||
|
|
||
| import { Render, GlossaryTooltip, APIRequest } from "~/components" | ||
|
|
||
| Cloudflare Access can delegate access from any [self-hosted application](/cloudflare-one/applications/configure-apps/self-hosted-public-app/) to an [Access for SaaS MCP server](/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](/cloudflare-one/policies/access/#selectors) to ensure that only authorized users can access those applications, either directly or by using an <GlossaryTooltip term="MCP client">MCP client</GlossaryTooltip>. | ||
|
|
||
| ```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](/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](/cloudflare-one/applications/configure-apps/mcp-servers/saas-mcp/). | ||
|
|
||
| ## 2. Get the SaaS application ID | ||
|
|
||
| Get the `id` of the MCP server SaaS application: | ||
|
|
||
| <APIRequest | ||
| path="/accounts/{account_id}/access/apps" | ||
| method="GET" | ||
| /> | ||
|
|
||
| ```json title="Response" | ||
| { | ||
| "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: | ||
|
|
||
| <APIRequest | ||
| path="/accounts/{account_id}/access/policies" | ||
| method="POST" | ||
| 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](/cloudflare-one/policies/access/#service-auth), similar to service token rules. | ||
| ::: | ||
|
|
||
| 2. Copy the Access policy `id` returned in the response: | ||
|
|
||
| ```json title="Response" {5} | ||
| { | ||
| "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: | ||
|
|
||
| <APIRequest | ||
| path="/accounts/{account_id}/access/apps/{app_id}" | ||
| method="GET" | ||
| /> | ||
|
|
||
| 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. | ||
|
|
||
| <APIRequest | ||
| path="/accounts/{account_id}/access/apps/{app_id}" | ||
| method="PUT" | ||
| 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](/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 a 401 or 403 error. | ||
196 changes: 196 additions & 0 deletions
196
...ontent/docs/cloudflare-one/applications/configure-apps/mcp-servers/saas-mcp.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| --- | ||
| pcx_content_type: how-to | ||
| title: Secure MCP servers with Access for SaaS | ||
| sidebar: | ||
| order: 1 | ||
| label: Secure MCP servers with Access for SaaS | ||
| --- | ||
|
|
||
| import { Render, GlossaryTooltip, Tabs, TabItem, APIRequest } from "~/components" | ||
|
|
||
| You can secure <GlossaryTooltip term="MCP server">Model Context Protocol (MCP) servers</GlossaryTooltip> by using Cloudflare Access as an OAuth Single Sign-On (SSO) provider. | ||
|
|
||
| This guide walks through how to deploy a remote MCP server on [Cloudflare Workers](/workers/) that requires Cloudflare Access for authentication. When users connect to the MCP server using an <GlossaryTooltip term="MCP client">MCP client</GlossaryTooltip>, they will be prompted to log in to your [identity provider](/cloudflare-one/identity/idp-integration/) and are only granted access if they pass your [Access policies](/cloudflare-one/policies/access/#selectors). | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Add an [identity provider](/cloudflare-one/identity/idp-integration/) to Cloudflare Zero Trust | ||
| - Install [npm](https://docs.npmjs.com/getting-started) | ||
| - Install [Node.js](https://nodejs.org/en/) | ||
|
|
||
| ## 1. Deploy an example MCP server | ||
|
|
||
| To deploy our [example MCP server](https://github.com/cloudflare/ai/tree/main/demos/remote-mcp-cf-access) on Workers: | ||
|
|
||
| 1. Open a terminal and clone our example project: | ||
|
|
||
| ```sh | ||
| npm create cloudflare@latest -- mcp-server-cf-access --template=cloudflare/ai/demos/remote-mcp-cf-access | ||
| ``` | ||
|
|
||
| When asked if you want to deploy to Cloudflare, select **No**. | ||
|
|
||
| 2. Go to the project directory: | ||
|
|
||
| ```sh | ||
| cd mcp-server-cf-access | ||
| ``` | ||
|
|
||
| 3. Create a [Workers KV namespace](/kv/concepts/kv-namespaces/) to store the key. The binding name should be `OAUTH_KV` if you want to run the example as written. | ||
|
|
||
| ```sh | ||
| npx wrangler kv namespace create "OAUTH_KV" | ||
| ``` | ||
|
|
||
| The command will output the binding name and KV namespace ID: | ||
|
|
||
| ```sh output | ||
| { | ||
| "kv_namespaces": [ | ||
| { | ||
| "binding": "OAUTH_KV", | ||
| "id": "<YOUR_KV_NAMESPACE_ID>" | ||
| } | ||
| ] | ||
| } | ||
| ``` | ||
|
|
||
| 4. Open `wrangler.jsonc` in an editor and insert your `OAUTH_KV` namespace ID: | ||
|
|
||
| ```jsonc | ||
| "kv_namespaces": [ | ||
| { | ||
| "binding": "OAUTH_KV", | ||
| "id": "<YOUR_KV_NAMESPACE_ID>" | ||
| } | ||
| ], | ||
| ``` | ||
|
|
||
|
|
||
| 5. You can now deploy the Worker to Cloudflare's global network: | ||
|
|
||
| ```sh | ||
| npx wrangler deploy | ||
| ``` | ||
|
|
||
| The Worker will be deployed to your `*.workers.dev` subdomain at `mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev`. | ||
|
|
||
| ## 2. Create an Access for SaaS app | ||
|
|
||
| <Tabs syncKey="dashPlusAPI"> | ||
| <TabItem label="Dashboard"> | ||
|
|
||
| 1. In [Zero Trust](https://one.dash.cloudflare.com), go to **Access** > **Applications**. | ||
| 2. Select **SaaS**. | ||
| 3. In **Application**, enter a custom name (for example, `MCP server`) and select the textbox that appears below. | ||
| 4. Select **OIDC** as the authentication protocol. | ||
| 5. Select **Add application**. | ||
| 6. In **Redirect URLs**, enter the authorization callback URL for your MCP server. The callback URL for our [example MCP server](#1-deploy-an-example-mcp-server) is | ||
| ```txt | ||
| https://mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev/callback | ||
| ``` | ||
| 7. Copy the following values to input into our example MCP server. Other MCP servers may require different sets of input values. | ||
| - **Client secret** | ||
| - **Client ID** | ||
| - **Token endpoint** | ||
| - **Authorization endpoint** | ||
| - **Key endpoint** | ||
|
|
||
| 8. (Optional) Under **Advanced settings**, turn on [**Refresh tokens**](/cloudflare-one/applications/configure-apps/saas-apps/generic-oidc-saas/#advanced-settings) if you want to reduce the number of times a user needs to log in to the identity provider. | ||
| 9. Configure [Access policies](/cloudflare-one/policies/access/) to define the users who can access the MCP server. | ||
| 10. Save the application. | ||
|
|
||
| </TabItem> | ||
| <TabItem label="API"> | ||
|
|
||
| 1. Make a `POST` request to the [Access applications](/api/resources/zero_trust/subresources/access/subresources/applications/methods/create/) endpoint: | ||
|
|
||
| <APIRequest | ||
| path="/accounts/{account_id}/access/apps" | ||
| method="POST" | ||
| json={{ | ||
| name: "MCP server", | ||
| type: "saas", | ||
| saas_app: { | ||
| auth_type: "oidc", | ||
| redirect_uris: [ | ||
| "https://mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev/callback" | ||
| ], | ||
| grant_type: [ | ||
| "authorization_code", | ||
| "refresh_tokens" | ||
| ], | ||
| refresh_token_options: { | ||
| lifetime: "90d" | ||
| } | ||
| }, | ||
| policies: [ | ||
| "f174e90a-fafe-4643-bbbc-4a0ed4fc8415" | ||
| ], | ||
| allowed_idps: [] | ||
| }} | ||
| /> | ||
|
|
||
| 2. Copy the `client_id` and `client_secret` returned in the response. | ||
| 3. To determine the OAuth endpoint URLs for the SaaS application, refer to the [generic OIDC documentation](/cloudflare-one/applications/configure-apps/saas-apps/generic-oidc-saas/#2-add-your-application-to-access). | ||
|
|
||
| </TabItem> | ||
| </Tabs> | ||
|
|
||
| ## 3. Configure your MCP server | ||
|
|
||
| Your MCP server needs to perform an OAuth 2.0 authorization flow to get an `access_token` from the SaaS app created in [Step 1](#1-create-an-access-for-saas-app). When setting up the OAuth client on your MCP server, you will need to paste in the OAuth endpoints and credentials from the SaaS app. | ||
|
|
||
| To add OAuth endpoints and credentials to our [example MCP server](#1-deploy-an-example-mcp-server): | ||
|
|
||
| 1. Create the following [Workers secrets](/workers/configuration/secrets/): | ||
|
|
||
| ```sh | ||
| wrangler secret put ACCESS_CLIENT_ID | ||
| wrangler secret put ACCESS_CLIENT_SECRET | ||
| wrangler secret put ACCESS_TOKEN_URL | ||
| wrangler secret put ACCESS_AUTHORIZATION_URL | ||
| wrangler secret put ACCESS_JWKS_URL | ||
| ``` | ||
|
|
||
| 2. When prompted to enter a secret value, paste the corresponding values from your SaaS app: | ||
|
|
||
| | Workers secret | SaaS app field | | ||
| | ------------- | -------------- | | ||
| | `ACCESS_CLIENT_ID`| Client ID | | ||
| | `ACCESS_CLIENT_SECRET` | Client secret | | ||
| | `ACCESS_TOKEN_URL` | Token endpoint | | ||
| | `ACCESS_AUTHORIZATION_URL` | Authorization endpoint | | ||
| | `ACCESS_JWKS_URL` | Key endpoint | | ||
|
|
||
| 3. Configure a cookie encryption key: | ||
|
|
||
| a. Generate a random string: | ||
|
|
||
| ```sh | ||
| openssl rand -hex 32 | ||
| ``` | ||
|
|
||
| b. Store the string in a Workers secret: | ||
|
|
||
| ```sh | ||
| wrangler secret put COOKIE_ENCRYPTION_KEY | ||
| ``` | ||
|
|
||
| ## 4. Test the connection | ||
|
|
||
| You should now be able to connect to your MCP server running at `https://mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev/sse` using [Workers AI Playground](https://playground.ai.cloudflare.com/), [MCP inspector](https://github.com/modelcontextprotocol/inspector), or [other MCP clients](/agents/guides/remote-mcp-server/#connect-your-mcp-server-to-claude-and-other-mcp-clients) that support remote MCP servers. | ||
|
|
||
| To test in Workers AI Playground: | ||
|
|
||
| 1. Go to [Workers AI Playground](https://playground.ai.cloudflare.com/). | ||
|
|
||
| 2. Under **MCP Servers**, enter `https://mcp-server-cf-access.<YOUR_SUBDOMAIN>.workers.dev/sse` for the MCP server URL. | ||
|
|
||
| 3. Select **Connect**. | ||
|
|
||
| 4. A popup window will appear requesting access to the MCP server. Select **Approve**. | ||
|
|
||
| 5. Follow the prompts to log in to your identity provider. | ||
|
|
||
| Workers AI Playground will show a **Connected** status. The MCP server should successfully obtain an `access_token` from Cloudflare Access. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.