---
title: MCP server portals
description: MCP server portals in Access.
image: https://developers.cloudflare.com/zt-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/cloudflare-one/llms.txt  
> Use this file to discover all available pages before exploring further.

[Skip to content](#%5Ftop) 

# MCP server portals

An MCP server portal centralizes multiple [Model Context Protocol (MCP) servers ↗](https://www.cloudflare.com/learning/ai/what-is-model-context-protocol-mcp/) onto a single HTTP endpoint.

![MCP clients connect through an MCP portal to access internal MCP servers and SaaS MCP servers.](https://developers.cloudflare.com/_astro/mcp-portal.B5web1ii_2x3Bsf.webp) 

This guide explains how to add MCP servers to Cloudflare Access, create an MCP portal with customized tools and policies, and connect users to the portal using an MCP client.

## Key features

MCP server portals provide the following capabilities:

* **Streamlined access to multiple MCP servers**: MCP server portals support both unauthenticated MCP servers and MCP servers secured using OAuth (for example, via [Access for SaaS](https://developers.cloudflare.com/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/) or a [third-party OAuth provider](https://developers.cloudflare.com/agents/model-context-protocol/protocol/authorization/)). Users log in to the portal URL through Cloudflare Access and are prompted to authenticate separately to each server that requires OAuth.
* **Customized tools per portal**: Admins can tailor an MCP portal to a particular use case by choosing the specific tools and prompt templates that they want to make available to users through the portal. This allows users to access a curated set of tools and prompts — the less external context exposed to the AI model, the better the AI responses tend to be.
* **Tool and prompt aliases**: Admins can [rename tools and prompts](#rename-tools-and-prompts-with-aliases) and edit their descriptions at the portal or server level without modifying the upstream MCP server. Aliases help end users find the right tool and help AI agents select the correct one.
* **Context optimization**: Portals support query parameter options that reduce context window usage by minimizing or hiding tool definitions. Refer to [Optimize context](#optimize-context) for details.
* **Non-browser client support**: MCP clients authenticate to the portal using a standard OAuth 2.0 authorization code flow via [managed OAuth](https://developers.cloudflare.com/cloudflare-one/access-controls/applications/http-apps/managed-oauth/). Non-browser clients receive a `401` response with a `WWW-Authenticate` header pointing to Access's OAuth discovery endpoints, rather than a browser redirect. You can also connect using [Access service tokens](#connect-with-a-service-token) for machine-to-machine access.
* **Code mode**: Code mode is available by default on all portals. It collapses all upstream tools into a single `code` tool. The AI agent writes JavaScript that calls typed methods for each tool, and the code runs in an isolated [Dynamic Worker](https://developers.cloudflare.com/workers/runtime-apis/bindings/worker-loader/) environment. This keeps context window usage fixed regardless of how many tools are available. Refer to [code mode](#code-mode) for connection instructions.
* **Observability**: Once the user's AI agent is connected to the portal, Cloudflare Access logs the individual requests made using the tools in the portal. You can optionally route portal traffic through [Cloudflare Gateway](#route-portal-traffic-through-gateway) for richer HTTP logging and data loss prevention (DLP) scanning.

## How it works

When a user connects an MCP client to a portal, the following flow occurs:

1. The MCP client sends a request to the portal URL (`https://<subdomain>.<domain>/mcp`).
2. Cloudflare Access authenticates the user via a browser-based OAuth 2.0 flow (or [service token](#connect-with-a-service-token) headers).
3. The portal establishes an MCP session and returns the list of available tools from all enabled upstream servers.
4. When the user calls a tool, the portal identifies the target upstream MCP server based on the [tool namespace](#tool-namespacing), attaches the appropriate credentials (user OAuth token or admin credential), and proxies the request.
5. If [Gateway routing](#route-portal-traffic-through-gateway) is turned on, the outbound request passes through Cloudflare Gateway for HTTP logging and DLP inspection before reaching the upstream server.
6. The upstream server's response is returned to the MCP client.

### Transport

The portal connects to upstream MCP servers using [Streamable HTTP ↗](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#streamable-http) or [SSE ↗](https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#server-sent-events-sse-deprecated) transport. You do not need to specify which transport your upstream server uses. The portal automatically detects the correct transport by trying multiple connection strategies in order:

| Upstream URL pattern | Connection strategies (in order)                                                                                    |
| -------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Ends in /mcp         | Streamable HTTP only                                                                                                |
| Ends in /sse         | SSE (or Streamable HTTP if Gateway routing is turned on)                                                            |
| All other URLs       | Streamable HTTP on original URL, then SSE on original URL, then Streamable HTTP on {url}/mcp, then SSE on {url}/sse |

If a connection attempt returns a `404`, `405`, or `406` error, the portal falls back to the next strategy. All other errors stop the connection attempt.

### Built-in portal tools

Every portal exposes the following built-in tools to MCP clients, in addition to the upstream server tools:

| Tool                           | Description                                                                                         |
| ------------------------------ | --------------------------------------------------------------------------------------------------- |
| portal\_list\_servers          | Lists all available upstream servers with their ID, name, and whether they are currently turned on. |
| portal\_toggle\_servers        | Opens a URL-based server selection page where you can turn servers on or off.                       |
| portal\_toggle\_single\_server | Turns a single server on or off by server ID, without leaving the MCP client.                       |

When [context optimization](#optimize-context) is turned on, additional tools are exposed depending on the mode:

| Mode                 | Additional tools                                                                    |
| -------------------- | ----------------------------------------------------------------------------------- |
| minimize\_tools      | portal\_query\_tools — Search tools by regex pattern and return full definitions.   |
| search\_and\_execute | portal\_query\_tools and portal\_execute — Search tools and execute them via proxy. |

### Session lifecycle

Each MCP client connection creates a session that persists until the user disconnects or the session expires due to inactivity. Sessions expire after 24 hours of inactivity.

Within a session, users can turn individual servers on or off without disconnecting. Server toggles are scoped to the session and do not affect other users or sessions on the same portal.

### Naming

MCP server portals were previously referred to as **Agents Gateway** in some contexts. The API paths, Terraform resources, and internal codebases may still use `agents_gateway` or `agw` prefixes. The product name is **MCP server portals** and the dashboard navigation is **AI controls**.

## Prerequisites

* An [active domain on Cloudflare](https://developers.cloudflare.com/fundamentals/manage-domains/add-site/)
* Domain uses either a [full setup](https://developers.cloudflare.com/dns/zone-setups/full-setup/) or a [partial (CNAME) setup](https://developers.cloudflare.com/dns/zone-setups/partial-setup/)
* An [identity provider](https://developers.cloudflare.com/cloudflare-one/integrations/identity-providers/) configured on Cloudflare Zero Trust

## Add an MCP server

Add individual MCP servers to Cloudflare Access to bring them under centralized management.

To add an MCP server:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Go to the **MCP servers** tab.
3. Select **Add an MCP server**.
4. Enter any name for the server.
5. (Optional) Enter a custom string for the **Server ID**.
6. In **HTTP URL**, enter the full URL of your MCP server. For example, if you want to add the [Cloudflare Documentation MCP server ↗](https://github.com/cloudflare/mcp-server-cloudflare/tree/main/apps/docs-vectorize), enter `https://docs.mcp.cloudflare.com/mcp`.
7. Add [Access policies](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/) to show or hide the server in an [MCP server portal](#create-a-portal). The MCP server link will only appear in the portal for users who match an Allow policy. Users who do not pass an Allow policy will not see this server through any portals.  
Warning  
Blocked users can still connect to the server (and bypass your Access policies) by using its direct URL. If you want to enforce authentication through Cloudflare Access, [configure Access as the server's OAuth provider](https://developers.cloudflare.com/cloudflare-one/access-controls/ai-controls/secure-mcp-servers/).
8. Select **Save and connect server**.
9. If the MCP server supports OAuth, you will be redirected to log in to your OAuth provider. You can log in to any account on the MCP server. The account used to authenticate will serve as the admin credential for that MCP server. You can [configure an MCP portal](#create-a-portal) to use this admin credential to make requests.

Cloudflare Access will validate the server connection and fetch a list of tools and prompts. Once the server is successfully connected, the [server status](#server-status) will change to **Ready**. You can now add the MCP server to an [MCP server portal](#create-a-portal).

### Server status

The MCP server status indicates the synchronization status of the MCP server to Cloudflare Access.

| Status        | Description                                                                                                                                                                                         |
| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Error         | The server could not be reached or returned an error. Refer to [error details](#error-details) for more information. To fix the issue, [reauthenticate the server](#reauthenticate-the-mcp-server). |
| Sync Required | The server's OAuth credentials can no longer be refreshed and the server needs to be reauthenticated. To fix the issue, [reauthenticate the server](#reauthenticate-the-mcp-server).                |
| Waiting       | The server's tools, prompts, and resources are being synchronized.                                                                                                                                  |
| Ready         | The server was successfully synchronized and all tools, prompts, and resources are available.                                                                                                       |

#### Error details

When an MCP server is in the **Error** state, the API returns an `error_details` object with structured information to help you diagnose the issue:

| Field              | Description                                                                                                                                      |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| message            | A human-readable description of the error.                                                                                                       |
| type               | The category of error — for example, upstream\_error (the server returned an error response) or unreachable (the server could not be contacted). |
| http\_status\_code | The HTTP status code returned by the upstream server, if applicable.                                                                             |
| mcp\_error\_code   | The MCP protocol error code, if the server returned an MCP-level error.                                                                          |

Common causes of server errors include expired OAuth credentials, unreachable server URLs, and upstream server misconfigurations. If the error type is `upstream_error`, check the HTTP and MCP error codes to identify the issue on the upstream server. If the type is `unreachable`, verify that the server URL is correct and accessible.

### Reauthenticate the MCP server

To reauthenticate an MCP server in Cloudflare Access:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Go to the **MCP servers** tab.
3. Select the server that you want to reauthenticate, then select **Edit**.
4. Select **Authenticate server**.

You will be redirected to log in to your OAuth provider. The account used to authenticate will serve as the new admin credential for this MCP server.

### Synchronize the MCP server

Cloudflare Access automatically synchronizes tools and prompts from your MCP server approximately every two hours. During synchronization, Cloudflare connects to your MCP server using the [admin credential](#reauthenticate-the-mcp-server) and fetches the current list of tools and prompts. If the admin credential's OAuth access token has expired, Cloudflare refreshes it automatically using the stored refresh token before connecting.

Note

Synchronization uses the admin credential, not individual user credentials. If the admin credential's refresh token has expired or been revoked, the [server status](#server-status) will change to **Sync Required** and you will need to [reauthenticate the server](#reauthenticate-the-mcp-server). This will not impact end users' ability to connect to the MCP server. It will impact the ability to fetch tool and prompt information or additions and removals.

To manually refresh the MCP server in Zero Trust:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Go to the **MCP servers** tab and find the server that you want to refresh.
3. Select the three dots > **Sync capabilities**.

The MCP server page will show the updated list of tools and prompts. New tools and prompts are automatically enabled in the MCP server portal.

You can also trigger a sync via the API. The sync endpoint returns the current server state after synchronization, including the updated [server status](#server-status), tool count, and [error details](#error-details) if the sync failed.

### Upstream OAuth callback URL

When a user authorizes an upstream MCP server that requires per-user OAuth, the portal performs an OAuth authorization code flow with the upstream server on the user's behalf. As part of this flow, the portal registers a callback URL (`redirect_uri`) with the upstream server. The upstream server redirects to this URL after the user authorizes access.

For newly created MCP servers, the portal uses a shared callback URL owned by Cloudflare:

```

https://oauth-callbacks.cloudflareaccess.com/cdn-cgi/access/outbound-oauth-callback


```

Upstream MCP server vendors only need to allowlist this single Cloudflare URL rather than each individual portal hostname. This resolves issues where some vendors rejected portal-specific hostnames that were not in their redirect URI allowlists.

Existing MCP servers that were created before this change continue to use the portal domain as the callback URL (for example, `https://my-portal.example.com/servers-callback`). No action is required for existing servers.

Note

If an upstream OAuth provider rejects the callback URL, verify that `https://oauth-callbacks.cloudflareaccess.com/cdn-cgi/access/outbound-oauth-callback` is allowlisted as a redirect URI at the upstream provider. OAuth providers typically exact-match the full URI including path. For existing servers that still use per-portal callbacks, allowlist the portal domain instead.

## Create a portal

To create an MCP server portal:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Select **Add MCP server portal**.
3. Enter any name for the portal.
4. Under **Custom domain**, select a domain for the portal URL. Domains must belong to an active zone in your Cloudflare account. You can optionally specify a subdomain.
5. [Add MCP servers](#add-an-mcp-server) to the portal.
6. (Optional) Under **MCP servers**, [configure the tools and prompts](#manage-tools-and-prompts) available through the portal.
7. (Optional) Configure **Require user auth** for servers that support OAuth: - `Enabled`: (default) User will be prompted to utilize their own login credentials to establish a connection with the MCP server. - `Disabled`: Users who are connected to the portal will automatically have access to the MCP server via its [admin credential](#reauthenticate-the-mcp-server).
8. Add [Access policies](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/) to define the users who can connect to the portal URL.
9. Select **Add an MCP server portal**.
10. (Optional) [Customize the login experience](#customize-login-settings) for the portal.

Users can now [connect to the portal](#connect-to-a-portal) at `https://<subdomain>.<domain>/mcp` using an MCP client.

### Customize login settings

Cloudflare Access automatically creates an Access application for each MCP server portal. You can customize the portal login experience by updating Access application settings:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **Applications**.
2. Find the portal that you want to configure, then select the three dots > **Edit**.
3. To configure identity providers for the portal:  
   1. Go to **Authentication**.  
   2. Select the [identity providers](https://developers.cloudflare.com/cloudflare-one/integrations/identity-providers/) that you want to enable for your application.  
   3. (Recommended) If you plan to only allow access via a single identity provider, turn on **Apply instant authentication**. End users will not be shown the [Cloudflare Access login page](https://developers.cloudflare.com/cloudflare-one/reusable-components/custom-pages/access-login-page/). Instead, Cloudflare will redirect users directly to your SSO login event.
4. To customize the block page:  
   1. Go to **Additional settings**.  
   2. **Custom block pages**: Choose what users will see when they are denied access to the application.  
         * **Cloudflare default**: Reload the [login page](https://developers.cloudflare.com/cloudflare-one/reusable-components/custom-pages/access-login-page/) and display a block message below the Cloudflare Access logo. The default message is `That account does not have access`, or you can enter a custom message.  
         * **Redirect URL**: Redirect to the specified website.  
         * **Custom page template**: Display a [custom block page](https://developers.cloudflare.com/cloudflare-one/reusable-components/custom-pages/access-block-page/) hosted in Cloudflare One.
5. Select **Save**.

## Manage tools and prompts

When you add an MCP server to a portal, all of its tools and prompts are available to portal users by default. You can customize which tools and prompts are exposed, rename them with aliases, and override their descriptions.

### Turn off individual tools or prompts

To hide specific tools or prompts from portal users:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Find the portal you want to configure, then select the three dots > **Edit**.
3. Under **MCP servers**, find the server whose tools you want to manage.
4. Turn off the toggle next to any tool or prompt that you want to hide from users.
5. Select **Save**.

Turned-off tools will not appear in the portal's tool list. Users will not be able to call them.

### Use an allowlist pattern

By default, all tools and prompts from an MCP server are available in the portal. You can invert this behavior so that all tools are hidden by default and only explicitly turned-on tools are exposed. This is useful when an MCP server has many tools but you only want to expose a curated subset.

To configure an allowlist via the API, set `default_disabled` to `true` on the server-to-portal mapping, then explicitly list the tools you want to expose in `updated_tools`:

API request body (portal update)

```

{

  "servers": [

    {

      "id": "example-server",

      "default_disabled": true,

      "updated_tools": [

        {

          "name": "search_documents",

          "enabled": true

        },

        {

          "name": "list_projects",

          "enabled": true

        }

      ]

    }

  ]

}


```

With `default_disabled` set to `true`, only `search_documents` and `list_projects` will be available to portal users. All other tools from this server will be hidden.

### Rename tools and prompts with aliases

Aliases let you give tools and prompts clearer names in the portal. Use aliases to:

* Replace unclear tool names with names that match your organization's terminology.
* Add or improve descriptions so AI agents select the correct tool.
* Standardize naming across multiple MCP servers in a portal.

Alias names must be 1-40 characters and can only contain letters, numbers, hyphens, and underscores. Names must start and end with an alphanumeric character. The value must match `^[a-zA-Z0-9]+([_-][a-zA-Z0-9]+)*$`. For example, `search_customer_records` or `get-user-profile`. No two tools or prompts on the same server can share the same name, whether that name is an alias or the original upstream name.

#### Alias precedence

You can set aliases at two levels. Portal-level aliases take precedence over server-level aliases.

| Level            | Field         | Scope                                                         |
| ---------------- | ------------- | ------------------------------------------------------------- |
| **Server-level** | alias         | Applies across all portals that include this server           |
| **Portal-level** | portal\_alias | Applies only within a specific portal; overrides server-level |

When multiple names exist, the portal resolves them in this order: `portal_alias` \> `server_alias` \> `alias` \> original tool name.

If no alias is set, the portal uses the original name and description from the upstream server.

#### Set aliases in the dashboard

* [ Portal-level alias ](#tab-panel-6575)
* [ Server-level alias ](#tab-panel-6576)

To set an alias that applies to a specific portal:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Find the portal you want to configure, then select the three dots > **Edit**.
3. Go to the **Servers** tab.
4. Select the **Tools authorized** or **Prompts authorized** value for the server you want to configure (for example, `10/10`).
5. Find the tool or prompt you want to modify, then select the three dots > **Edit**.
6. In the modal, update the **Name** and **Description** as needed.
7. Select **Confirm**.

To set an alias that applies across all portals using a server:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Go to the **MCP servers** tab.
3. Find the server you want to configure, then select the three dots > **Edit**.
4. Go to the **Tools** or **Prompts** tab.
5. Find the tool or prompt you want to modify, then select the three dots > **Edit**.
6. In the modal, update the **Name** and **Description** as needed.
7. Select **Confirm**.
8. Scroll to the bottom of the page and select **Save server**.

Tools and prompts that have been modified display a **Modified** label in the dashboard.

#### Set aliases with the API

Send a `PUT` request to the [update a MCP portal](https://developers.cloudflare.com/api/resources/zero%5Ftrust/subresources/access/subresources/ai%5Fcontrols/subresources/mcp/subresources/portals/methods/update/) endpoint. Include the `alias` field for each tool or prompt you want to rename.

Terminal window

```

curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/portals/%7Bid%7D" \

  --request PUT \

  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \

  --json '{

    "servers": [

        {

            "server_id": "example-server",

            "updated_tools": [

                {

                    "name": "original_tool_name",

                    "enabled": true,

                    "description": "A clearer description of what this tool does.",

                    "alias": "renamed_tool"

                }

            ],

            "updated_prompts": [

                {

                    "name": "original_prompt_name",

                    "enabled": true,

                    "description": "An updated description for this prompt.",

                    "alias": "renamed_prompt"

                }

            ]

        }

    ]

  }'


```

To set server-level aliases that apply across all portals, send a `PUT` request to the [update a MCP server](https://developers.cloudflare.com/api/resources/zero%5Ftrust/subresources/access/subresources/ai%5Fcontrols/subresources/mcp/subresources/servers/methods/update/) endpoint with the same `updated_tools` and `updated_prompts` fields.

#### Reset an alias

To reset a tool or prompt to its original upstream name, open the edit modal for the tool or prompt in the dashboard and select "Reset to server definition." When using the API, omit the `alias` field from the corresponding entry in `updated_tools` or `updated_prompts`.

#### How aliases affect end users

MCP clients receive the aliased name and description instead of the original. End users do not see the original name.

If you change an alias while a user has an active session, the user must reauthenticate to see the update. Refer to [Manage portal sessions](#manage-portal-sessions) for reauthentication options.

Warning

If the upstream server renames a tool or prompt, your alias for it will be removed on the next sync. Verify that your aliases still apply after each sync.

### Tool and prompt namespacing

All tools and prompts exposed through a portal are automatically namespaced with the server ID as a prefix. The format is `{server_id}_{original_name}`. For example, a tool named `list_issues` on a server with ID `github` appears as `github_list_issues` in the portal. This prevents name collisions when multiple MCP servers expose tools with the same name.

Prompts follow the same pattern. A prompt named `summarize` on a server with ID `github` appears as `github_summarize`.

#### How the server ID is determined

The server ID used for namespacing comes from the **Server ID** field you set when [adding an MCP server](#add-an-mcp-server). You can enter a custom server ID in step 5 of the setup process, or let Cloudflare generate one automatically.

Choose short, descriptive server IDs when you plan to expose the server through a portal. The server ID becomes part of every tool name that MCP clients and AI agents see.

#### Parsing namespaced names

The portal splits namespaced names on the **first** underscore only. Everything before the first underscore is the server ID, and everything after it is the tool or prompt name. This means tool names can contain underscores without ambiguity.

| Namespaced name               | Server ID | Tool name             |
| ----------------------------- | --------- | --------------------- |
| github\_list\_issues          | github    | list\_issues          |
| github\_create\_pull\_request | github    | create\_pull\_request |
| sentry\_get\_issue\_details   | sentry    | get\_issue\_details   |

Because the split happens on the first underscore, server IDs themselves cannot contain underscores. Use hyphens instead when you need a multi-word server ID (for example, `my-server`).

#### Namespacing with aliases

If you [rename a tool with an alias](#rename-tools-and-prompts-with-aliases), the alias replaces the original tool name in the namespaced format. The server ID prefix still applies.

For example, if you alias the tool `list_issues` to `issues` on a server with ID `github`, the namespaced name becomes `github_issues`.

#### Namespacing in code mode

When [code mode](#code-mode) is active, the portal applies an additional transformation to make namespaced tool names safe for use as JavaScript identifiers. Hyphens and dots in the namespaced name are replaced with underscores, names that start with a digit get a `_` prefix, and JavaScript reserved words get a `_` suffix. For example, a server with ID `my-server` and a tool named `get-data` would appear as `my_server_get_data` in the code mode sandbox.

This sanitization happens automatically. You do not need to call any helper functions when using code mode as an end user.

#### Helper functions in the Agents SDK

If you are building an MCP client with the [Agents SDK](https://developers.cloudflare.com/agents/model-context-protocol/apis/client-api/), the SDK provides helper functions for working with server IDs and tool names:

* **`normalizeServerId`** (exported from `agents/mcp/client`) normalizes a caller-supplied server ID into a safe string. For example, `"GitHub MCP!"` becomes `"github-mcp"`. The SDK calls this automatically when you pass an `id` option to `addMcpServer()`.
* **`sanitizeToolName`** (exported from `@cloudflare/codemode`) converts a tool name into a valid JavaScript identifier by replacing hyphens and dots with underscores. This is called automatically in code mode contexts. Refer to the [code mode SDK reference](https://developers.cloudflare.com/agents/model-context-protocol/protocol/codemode/#sanitizetoolnamename) for details.

Note

The Agents SDK uses a `tool_{server_id}_{tool_name}` format (with a `tool_` prefix) when returning tools from `getAITools()`. This differs from the portal format, which uses `{server_id}_{tool_name}` without the prefix.

### Portal-native tools

In addition to upstream MCP server tools, the portal exposes its own built-in tools that let AI agents manage server connections and discover tools during a session. These tools use the `portal_` prefix and are not associated with any upstream server.

#### Always available

The following tools are available in every portal session, regardless of the connection mode:

| Tool                           | Description                                                                                                                                                                                                                                                                 |
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| portal\_list\_servers          | Lists all upstream MCP servers with their IDs, names, and whether they are currently enabled in the session.                                                                                                                                                                |
| portal\_toggle\_servers        | Opens a server selection flow. Returns a URL that the user visits in a browser to enable or disable servers and manage OAuth credentials.                                                                                                                                   |
| portal\_toggle\_single\_server | Toggles a single server on or off without requiring a browser visit. Accepts a server\_id and an action (toggle or untoggle). If the server requires OAuth and the user has not authenticated yet, the portal falls back to the browser-based portal\_toggle\_servers flow. |

These tools power the [session management](#manage-portal-sessions) features described later in this guide. AI agents call them automatically when you ask to enable a server, disable a server, or return to the server selection page.

#### Context optimization tools

When you connect with the [optimize\_context](#optimize-context) query parameter, the portal exposes additional tools for discovering and calling upstream tools:

| Tool                 | Available in                          | Description                                                                                                                                                                                                                                  |
| -------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| portal\_query\_tools | minimize\_tools, search\_and\_execute | Searches upstream tools by name, description, or schema using a regex pattern. Returns full tool definitions so the agent can call them. Required in minimize\_tools mode because upstream tool schemas are stripped to reduce context size. |
| portal\_execute      | search\_and\_execute                  | Calls an upstream tool by name with the provided arguments. In search\_and\_execute mode, upstream tools are hidden from the tool list entirely, so agents must use portal\_query\_tools to discover them and portal\_execute to call them.  |

#### Code mode tools

When you connect with [code mode](#code-mode) enabled, the portal replaces all upstream tools with two code execution tools:

| Tool                      | Description                                                                                                                                                                                                  |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| portal\_codemode\_search  | Searches available tools by running JavaScript in a sandboxed Worker. The sandbox provides a codemode.tools() function that returns all upstream tool definitions with sanitized names.                      |
| portal\_codemode\_execute | Calls upstream tools by running JavaScript in a sandboxed Worker. The sandbox provides a codemode proxy object where each property maps to an upstream tool. Supports Promise.all() for parallel tool calls. |

Refer to the [code mode SDK reference](https://developers.cloudflare.com/agents/model-context-protocol/protocol/codemode/) for details on writing code for these tools.

## Manage portals via API

In addition to the dashboard, you can manage MCP server portals programmatically using the Cloudflare API. The following examples show common operations.

Warning

Unlike the dashboard, the API does not automatically create a DNS record for your portal hostname. After creating a portal via the API, you must create a proxied CNAME record that points your portal subdomain to `gateway.agents.cloudflare.com`. Without this record, the portal will return `522` errors.

### List portals

Terminal window

```

curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/portals" \

  --request GET \

  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"


```

### Create a portal

Terminal window

```

curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/portals" \

  --request POST \

  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \

  --json '{

    "name": "Engineering Portal",

    "hostname": "mcp.example.com",

    "allow_code_mode": true,

    "secure_web_gateway": false

  }'


```

### List MCP servers

Terminal window

```

curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/servers" \

  --request GET \

  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"


```

### Create an MCP server

Terminal window

```

curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/servers" \

  --request POST \

  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \

  --json '{

    "name": "GitHub MCP Server",

    "hostname": "https://github-mcp.example.workers.dev/mcp",

    "auth_type": "oauth"

  }'


```

The `auth_type` field accepts the following values:

| Value           | Description                                                                                                                                          |
| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| oauth           | The server requires OAuth authentication. After creating the server, you will need to authenticate via the dashboard to establish admin credentials. |
| bearer          | The server uses a static bearer token for authentication. Provide the token in auth\_credentials.                                                    |
| unauthenticated | The server does not require authentication.                                                                                                          |

### Force sync an MCP server

To manually trigger a synchronization of tools and prompts from an upstream MCP server:

Terminal window

```

curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/servers/%7Bserver_id%7D/sync" \

  --request POST \

  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"


```

### Delete a portal

Terminal window

```

curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/portals/%7Bid%7D" \

  --request DELETE \

  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"


```

## Configure via Terraform

You can manage MCP server portals using the [Cloudflare Terraform provider ↗](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs). Use the `cloudflare_zero_trust_access_mcp_server_portal` resource to create and configure portals programmatically.

Warning

Unlike the dashboard, the Terraform provider does not automatically create DNS records for your portal hostname. You must create a CNAME record that points your portal subdomain to `gateway.agents.cloudflare.com`. Without this record, the portal will return `522` errors.

The following example creates an MCP server portal with a CNAME record:

MCP server portal with DNS record

```

# Create the MCP server portal

resource "cloudflare_zero_trust_access_mcp_server_portal" "example" {

  account_id = var.cloudflare_account_id

  name       = "Engineering Portal"

  hostname   = "mcp.example.com"

}


# Required: Create the CNAME record for the portal hostname

resource "cloudflare_dns_record" "mcp_portal" {

  zone_id = var.cloudflare_zone_id

  name    = "mcp"

  content = "gateway.agents.cloudflare.com"

  type    = "CNAME"

  proxied = true

}


```

For the full list of supported resource arguments, refer to the [Terraform provider documentation ↗](https://registry.terraform.io/providers/cloudflare/cloudflare/latest/docs).

## Code mode

[Code mode](https://developers.cloudflare.com/agents/model-context-protocol/protocol/codemode/) is turned on by default on all MCP server portals. It reduces context window usage by collapsing all tools in the portal into a single `code` tool. Instead of loading a separate tool definition for each upstream MCP server tool, the connected AI agent writes JavaScript that calls typed `codemode.*` methods. The generated code runs in an isolated [Dynamic Worker](https://developers.cloudflare.com/workers/runtime-apis/bindings/worker-loader/) environment, which keeps authentication credentials and environment variables out of the model context.

To use code mode, the MCP client must request it when connecting to the portal URL. Refer to [Connect with code mode](#connect-with-code-mode) for the required query parameter.

Code mode is useful for portals that aggregate many MCP servers or servers that expose a large number of tools. Context window usage stays fixed regardless of how many tools are available through the portal.

### Connect with code mode

To use code mode, append the `?codemode=search_and_execute` query string parameter to your portal URL when [connecting](#connect-to-a-portal) from an MCP client.

For example, if your portal URL is `https://<subdomain>.<domain>/mcp`, connect to:

```

https://<subdomain>.<domain>/mcp?codemode=search_and_execute


```

For MCP clients with server configuration files, use the portal URL with the query string parameter:

MCP client configuration with code mode

```

{

  "mcpServers": {

    "example-portal": {

      "command": "npx",

      "args": [

        "-y",

        "mcp-remote@latest",

        "https://<subdomain>.<domain>/mcp?codemode=search_and_execute"

      ]

    }

  }

}


```

When code mode is active, the portal advertises a single `code` tool to connected MCP clients. The AI agent discovers available tools by inspecting the typed method signatures in the Dynamic Worker environment and composes multiple tool calls into a single code execution.

For more information on building with code mode, refer to the [code mode SDK reference](https://developers.cloudflare.com/agents/model-context-protocol/protocol/codemode/).

### Turn off code mode

To turn off code mode for a portal:

* [ Dashboard ](#tab-panel-6577)
* [ API ](#tab-panel-6578)

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Find the portal you want to configure, then select the three dots > **Edit**.
3. Under **Basic information**, turn off **Code mode**.

1. Get your existing MCP portal configuration:  
Terminal window  
```  
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/portals/%7Bid%7D" \  
  --request GET \  
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN"  
```
2. Send a `PUT` request to the [Update a MCP Portal](https://developers.cloudflare.com/api/resources/zero%5Ftrust/subresources/access/subresources/ai%5Fcontrols/subresources/mcp/subresources/portals/methods/update/) endpoint with `allow_code_mode` set to `false`. To avoid overwriting your existing configuration, the `PUT` request body should contain all fields returned by the previous `GET` request.  
Terminal window  
```  
curl "https://api.cloudflare.com/client/v4/accounts/%7Baccount_id%7D/access/ai-controls/mcp/portals/%7Bid%7D" \  
  --request PUT \  
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \  
  --json '{  
    "allow_code_mode": false  
  }'  
```

## Route portal traffic through Gateway

When Gateway routing is turned on, calls to MCP servers protected by your MCP server portal are routed through [Cloudflare Gateway](https://developers.cloudflare.com/cloudflare-one/traffic-policies/). This makes portal traffic appear in your [Gateway HTTP logs](https://developers.cloudflare.com/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) alongside the rest of your organization's HTTP traffic. You can then create [Data Loss Prevention (DLP) policies](#example-gateway-policy) to detect and block sensitive data from being sent to your upstream MCP servers.

### How Gateway routing works

When a user calls a tool through the portal, the portal proxies the request to the upstream MCP server. With Gateway routing turned on, this outbound request passes through Cloudflare Gateway before reaching the upstream server. Gateway inspects the traffic and applies any matching HTTP policies, including DLP scanning.

Because portal traffic routes through Gateway, it also respects [Gateway egress policies](https://developers.cloudflare.com/cloudflare-one/traffic-policies/egress-policies/). This means outbound requests to upstream MCP servers will originate from your dedicated egress IPs or Gateway IP ranges rather than generic Cloudflare IPs. If your upstream MCP servers restrict inbound traffic by source IP (for example, to a VPN or corporate IP range), you can use egress policies to ensure portal traffic comes from a predictable set of IPs.

Note

Gateway routing only applies to real-time tool calls made by users through the portal. Background operations such as [admin credential synchronization](#synchronize-the-mcp-server) do not route through Gateway and will not use your egress policy IPs.

### Supported transports

Gateway routing supports [Streamable HTTP ↗](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#streamable-http) connections only. If an upstream MCP server is configured with a Server-Sent Events (SSE) endpoint (a URL ending in `/sse`), the portal will automatically attempt to connect using Streamable HTTP instead. If the upstream server does not support Streamable HTTP, the connection will fail when Gateway routing is turned on.

### Enable Gateway routing

To route MCP server portal traffic through Gateway:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Find the portal you want to configure, then select the three dots > **Edit**.
3. Under **Basic information**, turn on **Route traffic through Cloudflare Gateway**.
4. Select **Save**.

Portal traffic will now appear in your [Gateway HTTP logs](https://developers.cloudflare.com/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/). To apply DLP scanning, [create a Gateway HTTP policy](#example-gateway-policy).

### Example Gateway policy

To scan traffic for sensitive data, [create a Gateway HTTP policy](https://developers.cloudflare.com/cloudflare-one/data-loss-prevention/dlp-policies/) that matches both the MCP server and a predefined or custom [DLP profile](https://developers.cloudflare.com/cloudflare-one/data-loss-prevention/dlp-profiles/).

Gateway HTTP policies for MCP portal traffic must explicitly target the upstream MCP server. Ensure that your policy matches the upstream MCP server hostname (for example, `example-mcp-server.example.workers.dev`) rather than the portal URL (`<subdomain>.<domain>`).

For example, the following policy blocks traffic that contains [credentials and secrets](https://developers.cloudflare.com/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#credentials-and-secrets) or [financial information](https://developers.cloudflare.com/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#financial-information):

| Selector    | Operator | Value                                              | Logic | Action |
| ----------- | -------- | -------------------------------------------------- | ----- | ------ |
| Host        | in       | example-mcp-server.example.workers.dev             | And   | Block  |
| DLP Profile | in       | _Credentials and Secrets_, _Financial Information_ |       |        |

### Limitations

* DLP [AI prompt profiles](https://developers.cloudflare.com/cloudflare-one/data-loss-prevention/dlp-profiles/predefined-profiles/#ai-prompt) do not apply to MCP server portal traffic. AI prompt profiles are designed for specific web client API paths and do not match the MCP protocol format. Use standard DLP profiles instead.
* SSE transport is not supported through Gateway. If your upstream MCP server only supports SSE, Gateway routing will not work for that server.
* Background synchronization of tools and prompts does not route through Gateway. Only real-time user requests are inspected.

## Connect to a portal

Users can connect to your MCP server running at `https://<subdomain>.<domain>/mcp` using [Workers AI Playground ↗](https://playground.ai.cloudflare.com/), [MCP inspector ↗](https://github.com/modelcontextprotocol/inspector), or [other MCP clients](https://developers.cloudflare.com/agents/model-context-protocol/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://<subdomain>.<domain>/mcp` for the portal URL.
3. Select **Connect**.
4. In the popup window, log in to your Cloudflare Access identity provider.
5. The popup window will list the MCP servers in the portal that require authentication. For each of these MCP servers, select **Connect** and follow the login prompts.
6. Select **Done** to complete the portal authentication process.

Workers AI Playground will show a **Connected** status and list the available tools. You can now ask the AI model to complete a task using an available tool. Requests made to an MCP server will appear in your [portal logs](#view-portal-logs).

For MCP clients with server configuration files, we recommend using the `npx` command with the `mcp-remote@latest` argument:

MCP client configuration for MCP portals

```

{

  "mcpServers": {

    "example-mcp-server": {

      "command": "npx",

      "args": [

        "-y",

        "mcp-remote@latest",

        "https://<subdomain>.<domain>.com/mcp"

      ]

    }

  }

}


```

We do not recommend using the `serverURL` parameter since it may cause issues with portal session creation and management.

### Portal homepage

When users visit the portal domain (`https://<subdomain>.<domain>/`) in a browser, the portal displays a homepage with connection details and setup instructions.

Note

Do not visit the MCP endpoint URL (`https://<subdomain>.<domain>/mcp`) directly in a browser. The `/mcp` path is intended for MCP clients only and will return an `invalid token` error if accessed in a browser.

The homepage shows:

* The portal name and your organization branding (if configured in Cloudflare Access)
* The MCP endpoint URL with a copy button
* Per-client connection instructions for Claude Desktop, Workers AI Playground, OpenCode, Windsurf, and other MCP clients with OS-specific file paths

Authenticated users see their email address and a **Sign out** button in the session bar. Users who are not authenticated can still view the homepage and connection instructions.

### Sign out of a portal

To end a portal session, select **Sign out** from the [portal homepage](#portal-homepage) (`https://<subdomain>.<domain>/`). The sign-out flow:

1. Revokes all portal-level OAuth grants for your user.
2. Deletes all upstream MCP server OAuth states associated with your session.
3. Redirects through Cloudflare Access logout.

After sign-out, the portal displays a confirmation page with a summary of the revoked sessions. To reconnect, visit the portal homepage and authenticate again.

### Connect with a service token

You can connect to an MCP portal using an [Access service token](https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/service-tokens/) for machine-to-machine access. Service tokens bypass the browser-based OAuth flow and authenticate directly using the `CF-Access-Client-Id` and `CF-Access-Client-Secret` headers.

To connect with a service token:

1. [Create a service token](https://developers.cloudflare.com/cloudflare-one/access-controls/service-credentials/service-tokens/#create-a-service-token) in your Zero Trust account.
2. Add a [Service Auth policy](https://developers.cloudflare.com/cloudflare-one/access-controls/policies/#service-auth) to your portal's Access application. The policy action must be **Service Auth**, not Allow. Service Auth policies specifically match requests that include valid `CF-Access-Client-Id` and `CF-Access-Client-Secret` headers.
3. Include the service token headers when connecting from your MCP client.

Note

Service tokens do not support per-user OAuth with upstream MCP servers. When connected via a service token, the portal uses the [admin credential](#reauthenticate-the-mcp-server) for all upstream server requests. Servers configured with **Require user auth** turned on will not be available to service token sessions.

### Device authentication

MCP server portals require a browser-based authentication flow. [Device authentication](https://developers.cloudflare.com/cloudflare-one/team-and-resources/devices/cloudflare-one-client/) (picking up identity from the Cloudflare One Client without a browser redirect) is not currently supported for MCP portals. Users must complete the Access login flow in a browser when first connecting.

## Optimize context

MCP server portals support context optimization options that reduce how many tokens tool definitions consume in the model's context window. These options are useful when a portal aggregates many MCP servers or servers that expose a large number of tools.

To use context optimization, append the `optimize_context` query parameter to your portal URL when connecting from an MCP client.

### Minimize tools

The `minimize_tools` option strips tool descriptions and input schemas from all upstream tools, leaving only their names. The portal exposes a special `query` tool that agents use to search and retrieve full tool definitions on demand. Agents can discover tools without loading all definitions upfront.

This option provides up to 5x savings in token usage, though querying tool definitions before use adds a small amount of overhead.

To connect with `minimize_tools`, use the following portal URL:

```

https://<subdomain>.<domain>/mcp?optimize_context=minimize_tools


```

For MCP clients with server configuration files:

MCP client configuration with minimize\_tools

```

{

  "mcpServers": {

    "example-portal": {

      "command": "npx",

      "args": [

        "-y",

        "mcp-remote@latest",

        "https://<subdomain>.<domain>/mcp?optimize_context=minimize_tools"

      ]

    }

  }

}


```

### Search and execute

The `search_and_execute` option hides all upstream tools and exposes only two tools to the agent: `query` and `execute`. The `query` tool searches and retrieves tool definitions. The `execute` tool runs the upstream tools. The generated code runs in an isolated [Dynamic Worker](https://developers.cloudflare.com/workers/runtime-apis/bindings/worker-loader/) environment, which keeps authentication credentials and environment variables out of the model context.

This option reduces the initial token cost of portal tools to a small constant, regardless of how many tools are available. However, the agent becomes fully reliant on `query` to discover tools before it can call them.

To connect with `search_and_execute`, use the following portal URL:

```

https://<subdomain>.<domain>/mcp?optimize_context=search_and_execute


```

For MCP clients with server configuration files:

MCP client configuration with search\_and\_execute

```

{

  "mcpServers": {

    "example-portal": {

      "command": "npx",

      "args": [

        "-y",

        "mcp-remote@latest",

        "https://<subdomain>.<domain>/mcp?optimize_context=search_and_execute"

      ]

    }

  }

}


```

For more information on the code mode pattern behind `search_and_execute`, refer to the [Code mode SDK reference](https://developers.cloudflare.com/agents/model-context-protocol/protocol/codemode/).

## Manage portal sessions

Once connected to a portal, users can manage their upstream MCP server sessions without leaving their MCP client. The portal uses [MCP elicitations ↗](https://modelcontextprotocol.io/specification/2025-03-26/server/elicitation) to provide a server selection page where you can enable or disable servers, log out of individual servers, and reauthenticate.

### Return to the server selection page

To manage your server connections during an active session, ask your AI agent to take you back to the server selection page. For example, prompt your agent with:

> Take me back to the server selection page.

The portal returns an authorization URL. Open this URL in your web browser to access the server selection page:

```

https://<subdomain>.<domain>/authorize?elicitationId=<ELICITATION_ID>


```

From this page you can:

* **Enable or disable servers** — Toggle individual upstream MCP servers on or off. Disabling a server removes its tools from the active session, which reduces context window usage.
* **Log out and reauthenticate** — Log out of a server and log back in if you need to change which data the server has access to. For example, you may need to reauthenticate with different permissions.

### Enable or disable a server inline

You can also enable or disable a specific server directly from your MCP client without visiting the server selection page. For example:

> Enable the wiki server.

> Disable my Jira server.

The portal toggles the server and updates the active tool list immediately. Disabling a server removes its tools from the session, which reduces context window usage.

### Reauthenticate a server

When an upstream MCP server token expires, the portal prompts you to reauthenticate from within your MCP client. Open the provided URL in your browser and complete the login to restore the session.

If your MCP client does not display the reauthentication prompt, you can manually clear cached credentials:

Note

This command clears credentials for all MCP servers using `mcp-remote@latest`, not just MCP portals.

Terminal window

```

rm -rf ~/.mcp-auth


```

After clearing credentials, reconnect to the portal from your MCP client.

### Authorize new servers

When an admin adds a new upstream MCP server to a portal, the portal automatically prompts connected users to authorize the new server. The portal batches admin changes and redirects you to the authorization flow once, rather than interrupting for each individual server update.

## View portal logs

Portal logs allow you to monitor user activity through an MCP server portal. You can view logs on a per-portal or per-server basis.

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Access controls** \> **AI controls**.
2. Find the portal or server that you want to view logs for, then select the three dots > **Edit**.
3. Select **Logs**.

### Log fields

| Field      | Description                                         |
| ---------- | --------------------------------------------------- |
| Time       | Date and time of the request                        |
| Status     | Whether the server successfully returned a response |
| Server     | Name of the MCP server that handled the request     |
| Capability | The tool used to process the request                |
| Duration   | Processing time for the request in milliseconds     |

### Export logs with Logpush

Availability

Only available on Enterprise plans.

You can automatically export MCP portal logs to third-party storage destinations or security information and event management (SIEM) tools using [Logpush](https://developers.cloudflare.com/logs/logpush/). This allows you to integrate with your existing security workflows and retain logs for as long as your business requires.

To set up a Logpush job for MCP portal logs, refer to [Logpush integration](https://developers.cloudflare.com/cloudflare-one/insights/logs/logpush/). For a list of available log fields, refer to [MCP portal logs](https://developers.cloudflare.com/logs/logpush/logpush-job/datasets/account/mcp%5Fportal%5Flogs/).

## Troubleshooting

### After authenticating to the portal, my user receives the error `No allowed servers available, check your Zero Trust Policies`.

1. An MCP portal and server must both have an attached Access policy. Ensure that all MCP servers assigned to the portal have their own associated policy.
2. The server's admin authentication may be expired. Check that the [server's status](#server-status) is **Ready**. If the status shows **Error** or **Sync Required**, [reauthenticate the server](#reauthenticate-the-mcp-server).

### The portal URL does not prompt for authentication when it is added to an MCP client.

1. Verify that the portal has an assigned Access policy.
2. Verify that the portal URL does not have any applied [Workers](https://developers.cloudflare.com/workers/configuration/routing/custom-domains/), [Page Rules](https://developers.cloudflare.com/rules/page-rules/manage/), [custom hostname](https://developers.cloudflare.com/cloudflare-for-platforms/cloudflare-for-saas/domain-support/) definitions, or any other configuration that may interfere with its ability to connect to the MCP client.

### The portal returns a `522` error.

A `522` error indicates that Cloudflare cannot reach the portal's origin. This typically means the DNS record for the portal hostname is missing or misconfigured.

1. Verify that a CNAME record exists for your portal subdomain pointing to `gateway.agents.cloudflare.com`.
2. Ensure the CNAME record is proxied (orange-clouded) through Cloudflare.
3. If you created the portal using the [API](#manage-portals-via-api) or the [Terraform provider](#configure-via-terraform), you must create the DNS record separately. Unlike the dashboard, the API and Terraform provider do not auto-create DNS records.

### An MCP server is stuck in `Waiting` status.

The `Waiting` status means Cloudflare is attempting to connect to the upstream MCP server and fetch its tools and prompts. If the server stays in this status:

1. Verify that the upstream MCP server URL is correct and the server is reachable.
2. Check that the upstream server supports [Streamable HTTP ↗](https://spec.modelcontextprotocol.io/specification/2025-03-26/basic/transports/#streamable-http) or SSE transport. The portal will attempt multiple connection strategies automatically.
3. If the server requires authentication, verify that the admin credentials are valid by [reauthenticating the server](#reauthenticate-the-mcp-server).
4. Select the three dots > **Sync capabilities** to manually retry the connection.

### An MCP server shows `Stale` status.

A `Stale` status means the admin credential for the server could not be refreshed during the last synchronization attempt. The server's tools may still work for users who have their own OAuth tokens (servers with **Require user auth** turned on), but the admin credential needs to be refreshed.

To resolve this, [reauthenticate the server](#reauthenticate-the-mcp-server) with valid admin credentials.

### Tool calls fail with an `unauthorized` error.

1. If the server uses per-user OAuth (**Require user auth** is turned on), the user's OAuth token may have expired. Ask the user to [reauthenticate the server](#reauthenticate-a-server) from their MCP client.
2. If the server uses admin credentials, check the [server status](#server-status). A status of **Error** or **Sync Required** indicates the admin credential needs to be refreshed.
3. If the user recently changed permissions on the upstream service (for example, revoked OAuth scopes), they will need to reauthenticate.

### OAuth authentication fails with a redirect URI error when connecting to an upstream MCP server.

Errors such as `invalid_redirect_uri`, `invalid_client_metadata`, or `Redirect URI not allowed` indicate that the upstream MCP server rejected the callback URL that the portal registered during the OAuth flow. Refer to [Upstream OAuth callback URL](#upstream-oauth-callback-url) for background on how the callback URL is determined.

1. For newly created servers, the upstream provider must allowlist `https://oauth-callbacks.cloudflareaccess.com/cdn-cgi/access/outbound-oauth-callback` as a redirect URI. OAuth providers typically exact-match the full URI including path. Contact the upstream MCP server vendor if you do not control the allowlist.
2. For servers created before this feature was introduced, the upstream provider must allowlist your portal domain (for example, `https://my-portal.example.com`). To switch to the shared callback URL, delete the server and re-add it.

### Tool calls fail when Gateway routing is turned on.

1. Verify that the upstream MCP server supports Streamable HTTP transport. SSE transport is not supported through Gateway.
2. If the upstream server URL ends in `/sse`, the portal automatically attempts to connect using Streamable HTTP on a `/mcp` path instead. If the server does not support this, the connection will fail.
3. Check [Gateway HTTP logs](https://developers.cloudflare.com/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) for DLP block events. If a DLP policy is blocking the traffic, the portal returns an error to the MCP client with the DLP rule ID.

### Users cannot connect with `mcp-remote` or similar tools.

1. Ensure you are using the latest version of `mcp-remote`. Run `npx -y mcp-remote@latest` to update.
2. Use the `command` and `args` format in your MCP client configuration, not the `serverURL` parameter. The `serverURL` parameter may cause issues with portal session creation.
3. If authentication fails repeatedly, clear cached credentials by running `rm -rf ~/.mcp-auth` and reconnecting.

### The portal homepage shows the wrong name or domain.

The portal homepage displays your Access organization name and branding. If the displayed name is incorrect:

1. In the [Cloudflare dashboard ↗](https://dash.cloudflare.com/), go to **Zero Trust** \> **Settings** \> **General** \> **Team name**.
2. Update your team name. The change will take effect the next time a user visits the portal homepage.

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/cloudflare-one/","name":"Cloudflare One"}},{"@type":"ListItem","position":3,"item":{"@id":"/cloudflare-one/access-controls/","name":"Access controls"}},{"@type":"ListItem","position":4,"item":{"@id":"/cloudflare-one/access-controls/ai-controls/","name":"AI controls"}},{"@type":"ListItem","position":5,"item":{"@id":"/cloudflare-one/access-controls/ai-controls/mcp-portals/","name":"MCP server portals"}}]}
```
