Skip to content

OAuth full-access grant cannot access zone SSL/custom-hostname endpoints through mcp.cloudflare.com #170

Description

@houmark

Bug Report

MCP Client

OpenAI Codex Desktop / Codex Cloudflare plugin

Authentication Type

OAuth

MCP Server Configuration

{
  "mcpServers": {
    "cloudflare-api": {
      "type": "http",
      "url": "https://mcp.cloudflare.com/mcp"
    }
  }
}

OAuth Scopes

Maximum access was selected in every authorization screen shown during the OAuth flow.

The first Cloudflare MCP permission picker was set to full access (78 / 78). The selected scope list included ssl_certs:write / "SSL certificates Write" along with Workers, DNS, Pages, D1, and the other available read/write permissions.

The subsequent Cloudflare OAuth authorization screen also used maximum available access, with access granted for all visible Cloudflare accounts/organizations. That screen showed a smaller final consent set (48 total permissions) even though the previous MCP permission picker had 78 / 78 selected.

After completing authorization, the Cloudflare user settings page for connected applications showed the Cloudflare MCP Server authorization as 77 / 77 permissions granted. That 78 -> 77 difference may be unrelated, but it is worth noting because the user attempted to grant maximum access throughout the flow.

Despite that, the live OAuth token can read zones and Workers resources, but cannot read zone SSL/custom-hostname endpoints.

Failing Endpoints

These fail through the execute tool using cloudflare.request():

GET /zones/{zone_id}/custom_hostnames
GET /zones/{zone_id}/custom_hostnames/quota
GET /zones/{zone_id}/custom_hostnames/fallback_origin
GET /zones/{zone_id}/ssl/universal/settings
GET /zones/{zone_id}/ssl/certificate_packs

Bug Description

With maximum OAuth access selected for https://mcp.cloudflare.com/mcp in both authorization stages, and with access granted across all visible Cloudflare accounts/organizations, the Cloudflare API MCP server can successfully authenticate, list accounts, read the target account, read the target zone, and read Workers/D1/Pages resources. However, the same OAuth-authenticated session cannot access zone SSL and Cloudflare-for-SaaS custom-hostname endpoints.

This looks like an OAuth scope mapping or authorization-boundary issue rather than an account-selection issue:

  • GET /user succeeds.
  • GET /accounts shows the expected accounts.
  • GET /accounts/{account_id} succeeds for the target account.
  • GET /zones/{zone_id} succeeds for the target zone.
  • GET /accounts/{account_id}/workers/scripts succeeds.
  • GET /accounts/{account_id}/workers/domains succeeds.
  • GET /accounts/{account_id}/d1/database succeeds.
  • GET /accounts/{account_id}/pages/projects succeeds.
  • Passing the account ID explicitly to the MCP execute call does not change the failure.
  • The failure reproduces on two separate accounts/zones where the user has admin-level access.

The repository appears to intend to support this permission via src/auth/scopes.ts:

'ssl_certs:write': 'Manage SSL certificates'

But the OAuth token issued after full-access consent does not appear able to use zone SSL/certificate APIs or Cloudflare-for-SaaS custom hostname APIs.

Steps to Reproduce

  1. Configure an MCP client with https://mcp.cloudflare.com/mcp.
  2. Authenticate using OAuth.
  3. Select full access in the Cloudflare MCP permission picker (78 / 78).
  4. Complete the following Cloudflare OAuth authorization screen with maximum available permissions and access to all visible Cloudflare accounts/organizations.
  5. After authorization, verify the connected application appears in Cloudflare user settings. In this reproduction it showed 77 / 77 permissions granted, not 78 / 78.
  6. Confirm ordinary account/zone/product access works:
await cloudflare.request({ method: "GET", path: "/user" })
await cloudflare.request({ method: "GET", path: "/accounts/{account_id}" })
await cloudflare.request({ method: "GET", path: "/zones/{zone_id}" })
await cloudflare.request({ method: "GET", path: "/accounts/{account_id}/workers/scripts" })
await cloudflare.request({ method: "GET", path: "/accounts/{account_id}/workers/domains" })
  1. Try zone SSL/custom-hostname endpoints:
await cloudflare.request({
  method: "GET",
  path: "/zones/{zone_id}/custom_hostnames",
  query: { page: 1, per_page: 1 }
})

await cloudflare.request({
  method: "GET",
  path: "/zones/{zone_id}/custom_hostnames/fallback_origin"
})

await cloudflare.request({
  method: "GET",
  path: "/zones/{zone_id}/ssl/universal/settings"
})

await cloudflare.request({
  method: "GET",
  path: "/zones/{zone_id}/ssl/certificate_packs",
  query: { page: 1, per_page: 1 }
})

Expected Behavior

An OAuth session that selected maximum available access throughout the flow, includes ssl_certs:write / "SSL certificates Write", and was granted access to all visible Cloudflare accounts/organizations should be able to read and manage zone SSL/certificate endpoints. It should also be able to at least read Cloudflare-for-SaaS custom hostname/fallback-origin state for zones the user can administer.

If these endpoints are intentionally outside OAuth support, the MCP server should document that boundary and surface a clearer error recommending API-token mode with the required zone-level API token permissions.

Actual Behavior

The same MCP OAuth session succeeds on account/zone/Workers/D1/Pages reads but fails on zone SSL/custom-hostname endpoints:

GET /zones/{zone_id}/custom_hostnames
Cloudflare API error: 10000: Authentication error

GET /zones/{zone_id}/custom_hostnames/quota
Cloudflare API error: 10000: Authentication error

GET /zones/{zone_id}/custom_hostnames/fallback_origin
Cloudflare API error: 10000: Authentication error

GET /zones/{zone_id}/ssl/universal/settings
Cloudflare API error: 9109: Unauthorized to access requested resource

GET /zones/{zone_id}/ssl/certificate_packs
Cloudflare API error: 9109: Unauthorized to access requested resource

Additional Context

This does not appear to be the MCP client selecting the wrong account. The account selector was tested explicitly and the same failures remained.

It also does not appear to be general Workers/DNS access failure. Workers scripts, Workers custom domains, D1, Pages, accounts, and zones all worked through the same OAuth connection.

The practical workaround is likely API-token mode with explicit zone-level permissions, including:

  • Zone -> Zone -> Read
  • Zone -> SSL and Certificates -> Edit
  • Zone -> DNS -> Edit, if DNS automation is needed
  • Account -> Workers Scripts / Workers Routes permissions, if Workers automation is needed

However, for OAuth/full-access mode, either the ssl_certs:write scope is not mapping to the required zone-level SSL permission, custom-hostname endpoints require an additional OAuth scope that is not available in the permission picker, or these endpoints currently do not accept this OAuth token type.

Could you confirm whether https://mcp.cloudflare.com/mcp OAuth full-access mode is expected to support:

  • /zones/{zone_id}/ssl/*
  • /zones/{zone_id}/custom_hostnames*

If yes, this looks like a scope mapping/authorization bug. If no, the README and/or tool error handling should make the limitation explicit and recommend API-token mode for Cloudflare-for-SaaS custom-hostname work.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions