Skip to main content
Glama
MADPANDA3D

Cloudflare MCP

by MADPANDA3D

The contract

MADPANDA3D Cloudflare MCP exposes exactly six protocol-visible tools:

  • Five agent-ready navigation tools explain configuration, search the catalog, filter endpoint coverage, and return exact descriptors. They are local unless check_configuration is explicitly asked to verify a token.

  • One advanced legacy dispatcher, cloudflare_api_request, executes one reviewed Cloudflare REST operation at a time through a fixed provider origin.

The checked-in endpoint inventory accounts for 3,148 operations from a pinned Cloudflare api-schemas snapshot. Reviewed policy 2026.07.19.3 makes 2,356 callable and leaves 792 catalog-only because their authentication, required provider headers, media types, request/success contracts, exact policy overrides, or credential-sensitive schemas fall outside the generic dispatcher boundary. Catalog-only does not mean hidden: agents can inspect those operations and the exclusion reason, but the dispatcher rejects execution.

The server is streamable HTTP at /mcp, exposes presence-only health metadata at /health, stores no Cloudflare credential, and does not turn a deployment into a public or unauthenticated Cloudflare endpoint.

Related MCP server: Cloudflare MCP Server

Choose an access mode

MCP_MODE is selected at startup. Changing it requires a restart; no tool can change the running mode.

Mode

Intended use

Service credential

Cloudflare credential

standalone

Independent self-hosting

Authorization: Bearer <MCP_ACCESS_TOKEN>

x-cloudflare-api-token on each provider request

portal

Optional MAD MCP Portal routing

Matching X-MADPANDA-PORTAL-GRANT and broker-derived tenant header

The broker forwards x-cloudflare-api-token per authorized request

There is no unauthenticated MCP mode. The service credential controls access to this server; the Cloudflare token separately controls provider permissions. Portal mode also requires the header named by MCP_TENANT_ID_HEADER (default x-madpanda-user-id) on every MCP request. The broker must remove any client-supplied value and inject its own authenticated tenant identity. That identity partitions process-local approval principals; it is not authorization and never replaces Portal access control or Cloudflare token permissions.

Request-scoped Cloudflare BYOK

Both modes use the same provider headers:

Header

Required

Purpose

x-cloudflare-api-token

Provider calls

Least-privilege Cloudflare API token for this request

x-cloudflare-account-id

Optional

Account-scope hint used by navigation and token verification

x-cloudflare-zone-id

Optional

Zone-scope hint used by navigation and workflows

x-mcp-approval-attestation

Approved mutation retry only

Short-lived one-use proof issued by a trusted operator or broker

Account and zone headers are hints, not authorization and not automatic path substitution. Provider paths still need the correct explicit resource IDs, and Cloudflare remains the authority for token permissions.

In Portal mode, the broker-controlled tenant header is mandatory service-auth context, not a provider header. Clients must never be allowed to select or forward that value directly.

There is deliberately no Cloudflare token environment fallback. Never place a provider token in .env, Compose, an image layer, a URL, an issue, a screenshot, or copied client configuration. Supply it through the MCP client's protected secret/header facility for the active request.

Five-minute standalone deployment

Prerequisites: Git, Python 3.12 or 3.13, uv, Docker Engine with Compose v2, and a Cloudflare API token with only the permissions required for your intended operations.

git clone https://github.com/MADPANDA3D/CF-MCP.git
cd CF-MCP
python3 scripts/init_runtime_env.py --mode standalone

The initializer creates ignored .env mode 0600, generates independent service and mutation-approval secrets, never overwrites an existing file, and never prints a secret. Review only the non-secret routing values:

MCP_MODE=standalone
MCP_ALLOWED_HOSTS=localhost,127.0.0.1
MCP_HOST_PORT=8000

Do not put the Cloudflare token in this file. Start the loopback-only profile:

docker compose --env-file .env -f docker-compose.yml config --quiet
docker compose --env-file .env -f docker-compose.yml up -d --build
docker compose --env-file .env -f docker-compose.yml ps
curl --fail http://127.0.0.1:8000/health

Expected safe health fields include:

{
  "status": "healthy",
  "service": "cloudflare-mcp",
  "tool_count": 6,
  "configuration": {
    "mode": "standalone",
    "provider_credentials_mode": "per_request_byok"
  }
}

/health reports mode, readiness, bounds, and release/catalog identity without returning a service token, Portal grant, Cloudflare token, account ID, or zone ID.

Connect an MCP client

Client formats differ, but the logical HTTP configuration is:

{
  "mcpServers": {
    "cloudflare": {
      "type": "http",
      "url": "http://127.0.0.1:8000/mcp",
      "headers": {
        "Authorization": "Bearer ${MCP_ACCESS_TOKEN}",
        "x-cloudflare-api-token": "${CLOUDFLARE_API_TOKEN}",
        "x-cloudflare-account-id": "${OPTIONAL_CLOUDFLARE_ACCOUNT_ID}",
        "x-cloudflare-zone-id": "${OPTIONAL_CLOUDFLARE_ZONE_ID}"
      }
    }
  }
}

${...} means “load this value from the client's protected secret or environment facility.” It is not universal interpolation syntax. Omit optional headers when they are not needed, and never commit resolved values.

The five navigation tools need only the selected service-auth header. Provider execution and optional live token verification also need x-cloudflare-api-token. Mutation-capable clients additionally need a trusted per-request mechanism to inject a short-lived x-mcp-approval-attestation after an operator or broker approves the preview. If the client cannot inject a one-request header, use the server as read-only.

Tools

Tool

Tier

Provider call

Behavior

check_configuration

agent_ready

Optional

Reports secret-safe readiness; verifies a token only when explicitly requested

list_capabilities

agent_ready

No

Returns compact catalog counts or the complete deterministic ToolManifest

get_endpoint_coverage

agent_ready

No

Filters the pinned 3,148-operation inventory; results include coverage, auth, required-header/body, contract, and risk metadata

get_tool_usage

agent_ready

No

Resolves one native, canonical, or documented alias to its exact descriptor and usage contract

find_tools

agent_ready

No

Searches the local catalog; advanced legacy results are opt-in

cloudflare_api_request

legacy

Yes

Previews or executes one pinned method/path JSON operation through the guarded advanced path

All six tools remain protocol-visible for compatibility. Default task discovery returns the five agent-ready tools; callers must deliberately include the advanced legacy tier to select the dispatcher.

Endpoint coverage

The generated inventory is pinned to Cloudflare's official api-schemas repository:

Identity

Value

Upstream commit

aefa753f1190c85866f65dcc7f348e18c7a1ca4a

Source snapshot SHA-256

6c141cf38b45a514fcba04d322d43916eaba179a4442c8d91afaf5e7a66c8f1f

Upstream license

BSD-3-Clause

Reviewed endpoint-policy version

2026.07.19.3

Total operations

3,148

Read / write / destructive

1,499 / 1,201 / 448

Callable reviewed operations

2,356

Catalog-only operations

792

Unsupported request / success media

41 / 103

Required provider headers / no Bearer API-token proof

2 / 87

No reviewable JSON request schema

127

No explicit 2xx response contract

49

Ambiguous non-204/205 bodyless success

15

No reviewable successful JSON schema

120

Credential-sensitive schema operations

263

Sensitive request / successful-response schemas

104 / 206

Credential-semantic signal operations

172

Reviewed overrides / forced catalog-only overrides

117 / 114

High-risk / reviewed side-effecting GET operations

469 / 23

“Callable” means the method/path exists in the pinned schema, the operation contract proves compatibility with this server's fixed Bearer API-token client, it requires no caller-supplied provider headers, and its request and success contracts fit the reviewed JSON boundary. A JSON request or successful response must have a reviewable schema; missing, empty, and bare type: object schemas do not prove which fields or sensitive values may cross the boundary. If OpenAPI marks a request body required, the runtime rejects an omitted body. This remains basic contract admission, not complete query/body field validation.

Every operation must declare an explicit 2xx response. Only 204 and 205 are accepted as implicitly bodyless; every other successful status must declare a response content contract. That policy leaves 49 operations with no explicit 2xx, 15 with ambiguous non-204/205 bodyless success, and 120 with unreviewable successful JSON schemas catalog-only. The corresponding request-side rule excludes 127 operations whose JSON request schema cannot be reviewed. Reason counts overlap.

Credential signals include unmarked capability and payment values such as license or pairing keys, card/payment fields, invoice documents, and signed upload, download, or debugger URLs. Exact review also forces 87 credential-capable endpoint contexts—including arbitrary outbound header maps and temporary privileged URLs—out of the generic dispatcher. Exact reviewed transport overrides also keep these contracts fail-closed:

  • GET /accounts/{account_id}/email-security/investigate requires the Location header from its 202 response to continue polling, but the generic dispatcher intentionally omits provider response headers.

  • POST, PATCH, and DELETE /accounts/{account_id}/teamnet/routes/network/{ip_network_encoded} require an encoded CIDR slash that the path normalizer rejects to prevent ambiguous path traversal.

  • GET /accounts/{account_id}/cni/interconnects/{icon}/loa is classified as a write, flagged as a side-effecting GET, and forced catalog-only because it generates a Letter of Authorization document.

Callable status does not mean a caller's token has permission or an operation is free. Every high-risk operation is blocked from generic execution.

The dispatcher does not validate arbitrary query keys or JSON body fields against Cloudflare's complete parameter and request-body schemas. It enforces a pinned method/path match, endpoint policy, supported JSON media, and structural JSON bounds. It also enforces OpenAPI's required-body presence bit, but Cloudflare remains authoritative for field-level validation. A caller—and, for mutations, the approval issuer—must review the exact query and body rather than treating callable as proof that those fields are valid.

The dispatcher rejects operations missing from the snapshot, operations without Bearer API-token compatibility proof, required provider headers it cannot construct, unreviewable request/success contracts, arbitrary provider origins, caller-supplied HTTP headers, non-JSON request bodies, unsupported success media types, redirects, and ambiguous query-in-path URLs. It is not a general-purpose HTTP proxy.

See the endpoint coverage ledger for the generated matrix and provenance for the attribution chain.

Externally approved, one-use mutations

Reviewed reads execute once after authentication, BYOK, pinned method/path, JSON-media, and endpoint-policy checks. Ordinary writes and destructive operations use an approval flow whose signing secret is separate from the agent and MCP client:

  1. Call cloudflare_api_request with the exact method, path, query, JSON body, and content type plus the request-scoped Cloudflare token.

  2. The server makes no provider call. It returns executed=false, an expiring approval_payload, operation identity, and canonical request digest.

  3. A trusted operator or Portal broker creates one exact review JSON document containing only approval_payload, method, path, query, body, and content_type. The issuer independently reviews the full target and effects, including every query/body field, then signs that document with the separately controlled MCP_APPROVAL_SIGNING_KEY. For example:

    {
      "approval_payload": "<EXACT_SERVER_RETURNED_PAYLOAD>",
      "method": "POST",
      "path": "/accounts/<ACCOUNT_ID>/<REVIEWED_PATH>",
      "query": null,
      "body": {"<REVIEWED_FIELD>": "<REVIEWED_VALUE>"},
      "content_type": "application/json"
    }
    uv run --env-file .env python scripts/sign_approval.py \
      < approval-review.json
  4. Inject the resulting short-lived value as x-mcp-approval-attestation on one repeat of the unchanged MCP request.

scripts/sign_approval.py recomputes the operation and canonical request digest from that exact review document before signing. Never pipe or sign an approval_payload by itself, and never let an agent generate an unreviewed document for automatic signing.

The attestation is bound to the authenticated service principal (including the broker-derived tenant in Portal mode), request-scoped Cloudflare credential fingerprint, operation, exact request, expiry, and random challenge. It is atomically consumed before the provider attempt, so direct forgery, argument changes, credential swapping, principal swapping, and replay fail closed. A timeout remains outcome-ambiguous; inspect provider state with a safe read before requesting a new approval.

The approval ledger is bounded and process-local. A mutation preview and its approved retry must reach the same live process within five minutes. Use a single mutation-admitting replica or explicit same-replica routing; otherwise operate read-only. Never expose MCP_APPROVAL_SIGNING_KEY to an agent, MCP client, prompt, ticket, log, or provider request.

All 469 high-risk operations—including detected credential-sensitive, account-administration, billing/commerce, and reviewed side-effecting GET classes—are permanently blocked from the generic dispatcher. There is no startup toggle. All 263 operations with detected sensitive request or successful response schemas and all 114 force-catalog reviewed overrides are catalog_only; response redaction remains a defense-in-depth control, not an admission mechanism.

Provider calls receive one outbound attempt. This server does not automatically retry reads, writes, destructive requests, or billable calls.

Configuration

Start from .env.example. Principal settings are:

Variable

Required

Purpose and reviewed default

MCP_MODE

Yes

Startup mode: standalone or portal

MCP_ACCESS_TOKEN

Standalone

Bearer token of at least 32 characters

MCP_PORTAL_GRANT_TOKEN

Portal

Unique Portal-to-service grant of at least 32 characters

MCP_TENANT_ID_HEADER

Portal

Broker-controlled tenant-partition header name; default x-madpanda-user-id

MCP_APPROVAL_SIGNING_KEY

Mutations

Separate operator/broker HMAC key; must differ from service and provider credentials; blank makes the deployment read-only

MCP_HOST_PORT

Standalone Compose

Loopback host port; default 8000

MCP_ALLOWED_HOSTS

Yes

Explicit Host allowlist; wildcards fail startup validation

MCP_ALLOWED_ORIGINS

Optional

Explicit browser origins; empty rejects requests carrying Origin

MCP_REQUEST_BODY_MAX_BYTES

Optional

MCP request limit; default 128 KiB, hard maximum 8 MiB

MCP_RESPONSE_BODY_MAX_BYTES

Optional

Complete MCP response limit; default 1 MiB, hard maximum 2 MiB

MCP_PROVIDER_RESPONSE_MAX_BYTES

Optional

Streamed provider response limit; default 64 KiB, hard maximum 240 KiB

MCP_BUILD_SHA

Source builds

Build argument baked into image identity; release Compose does not accept a runtime override

MCP_SOURCE_FINGERPRINT

Source builds

Source-archive fingerprint baked into image identity; release Compose does not accept a runtime override

MCP_IMAGE_REFERENCE

Source builds

development by default; release Compose derives the immutable reference from the digest

MCP_RELEASE_DIGEST

Immutable release Compose

Only release-identity input: the exact 64-character GHCR digest, in addition to normal service credentials

The provider origin is fixed to https://api.cloudflare.com/client/v4; there is no supported environment override. Provider credentials and account/zone hints are request headers, not environment variables.

Optional Portal deployment

Portal mode uses the same server and provider contract:

MCP_MODE=portal
MCP_PORTAL_GRANT_TOKEN=<UNIQUE_PORTAL_TO_SERVICE_GRANT>
MCP_TENANT_ID_HEADER=x-madpanda-user-id
MCP_APPROVAL_SIGNING_KEY=<SEPARATELY_CONTROLLED_BROKER_SIGNING_KEY>
MCP_PORTAL_NETWORK=<PRIVATE_DOCKER_NETWORK_NAME>
MCP_ALLOWED_HOSTS=cloudflare-mcp,<TRUSTED_PROXY_HOST>

The broker sends:

X-MADPANDA-PORTAL-GRANT: <SERVICE_GRANT>
x-madpanda-user-id: <BROKER_DERIVED_TENANT_ID>
x-cloudflare-api-token: <OWNER_CLOUDFLARE_API_TOKEN>
x-cloudflare-account-id: <OPTIONAL_OWNER_ACCOUNT_ID>
x-cloudflare-zone-id: <OPTIONAL_OWNER_ZONE_ID>
x-mcp-approval-attestation: <ONE_USE_BROKER_ATTESTATION_FOR_APPROVED_MUTATION>

The broker must strip or overwrite any client-supplied x-madpanda-user-id value before forwarding. The tenant value partitions approval principals and same-tenant preview/retry routing; it does not authorize the request or grant Cloudflare access.

Use deployment-specific placeholders for the internal service name, network, locked MCP URL, health URL, proxy, and secret-store reference. This repository contains no production topology. See the Portal compatibility guide.

Production posture

  1. Keep /mcp behind TLS or loopback/private networking.

  2. Preserve the selected service-auth context and reviewed BYOK headers at the trusted proxy; in Portal mode, strip/overwrite the tenant header from authenticated broker identity and never log its value.

  3. Block direct container access from untrusted networks.

  4. Keep .env mode 0600, with no Cloudflare provider credential inside.

  5. Apply connection, request-size, idle-timeout, and rate limits at the edge.

  6. Preserve Host allowlisting and default browser-Origin rejection.

  7. Verify health, unauthorized rejection, six-tool discovery, local navigation, missing-BYOK rejection, catalog-only rejection, one-use approval binding/replay denial, and permanent high-risk denial before a provider smoke.

  8. Use a least-privilege test token and an explicitly approved read as the first provider call. Do not certify first with a mutation or paid action.

The application stores no database or provider credential. Read traffic can use multiple identical replicas, but the bounded one-use mutation approval ledger is process-local. Keep mutation admission on one replica or use explicit same-replica routing from preview through approved retry.

Immutable containers

The source is prepared as a v1.0.0 release candidate; documentation alone does not prove that a hosted GitHub Release or public GHCR digest exists. The exact annotated tag and tagged workflow may start while the clean canonical source remains private. The workflow first builds only a run-scoped candidate image with BuildKit SBOM/provenance and scans its exact digest. The package owner then makes only that GHCR package public so the same candidate can pass anonymous pull, Compose, and runtime smokes. Only after the image gate succeeds is the source repository made public for exact-SHA Verify/CodeQL admission. GitHub-signed package and image attestations are created after that public admission gate, before stable tags are promoted by digest without rebuilding. The GitHub Release is created last with deterministic double-built wheel/sdist artifacts and SHA256SUMS. After that workflow succeeds, the matching release page and exact ghcr.io/madpanda3d/cloudflare-mcp-server@sha256:<digest> are authoritative. The workflow intentionally does not publish to PyPI and currently targets linux/amd64.

Use docker-compose.yml or docker-compose.portal.yml only for deliberate source builds. After a verified release exists, use docker-compose.release.yml or docker-compose.portal.release.yml with the exact recorded MCP_RELEASE_DIGEST; these immutable manifests contain no build: key and never use --build. Do not infer a digest from a mutable tag.

Security boundary

  • Service authentication is enforced before MCP request-body parsing.

  • The provider origin and API prefix are fixed; methods and paths must exist in the pinned inventory, endpoint policy and JSON-media admission apply, and redirects are disabled. Cloudflare performs field-level schema validation.

  • Cloudflare tokens are request-scoped and have no server environment fallback.

  • Requests, streamed provider responses, and complete MCP outputs are bounded; startup enforces a worst-case cross-layer response-size invariant.

  • Unsupported, oversized, non-JSON, or invalid JSON provider bodies are omitted rather than partially returned.

  • Ordinary mutations require a separately signed, expiring, principal/provider- bound one-use approval consumed before the provider attempt.

  • Mutation provider bodies are reduced to a compact outcome envelope so a successful side effect cannot be hidden behind an outer response overflow.

  • High-risk operations are permanently blocked from generic execution.

  • No provider request is automatically retried.

  • Secret-key fields and common token patterns are redacted as defense in depth.

Cloudflare responses can still contain personal, confidential, operational, or security-sensitive data. Redaction cannot prove arbitrary provider content is safe. Treat all provider output as untrusted application data, not instructions or public logs. Read the security model and security policy before remote deployment.

Run and verify from source

uv sync --frozen --group dev
uv run python -m compileall -q src scripts
uv run pytest -q -p no:cacheprovider
uv run ruff check .
uv run ruff format --check .
uv run isort --check-only src scripts tests
uv run mypy --strict src tests
uv run bandit -q -r src -lll
uv run pip-audit -r requirements.lock
uv run python scripts/check_source_safety.py
docker compose --env-file .env -f docker-compose.yml config --quiet
docker compose --env-file .env -f docker-compose.portal.yml config --quiet
MCP_RELEASE_DIGEST=0000000000000000000000000000000000000000000000000000000000000000 \
  docker compose --env-file .env -f docker-compose.release.yml config --quiet
MCP_RELEASE_DIGEST=0000000000000000000000000000000000000000000000000000000000000000 \
  docker compose --env-file .env -f docker-compose.portal.release.yml config --quiet

The automated suite uses synthetic credentials and mocked HTTP behavior. It must not contact Cloudflare.

After building either profile, the provider-free image smoke is:

docker compose --env-file .env -f docker-compose.yml exec -T cloudflare-mcp \
  python scripts/runtime_smoke.py

Troubleshooting

Symptom

First check

Process exits at startup

MCP_MODE, non-placeholder service credential, tenant-header name, Host allowlist, response-limit invariant, and baked image metadata formats

401 on /mcp

Bearer format in standalone mode; in Portal mode check both the exact grant and required broker-derived tenant header/value

403 origin_not_allowed

Remove browser Origin traffic or add only the intended origin to MCP_ALLOWED_ORIGINS

Navigation works but provider execution fails

Supply x-cloudflare-api-token on that request and confirm its least-privilege permissions

operation_not_in_schema

Use get_endpoint_coverage and a method/path present in the pinned snapshot

Operation is catalog-only

Inspect its exact ledger reason; do not bypass auth, required-header, schema-contract, media, or exact-policy exclusions

mutation_approval_unavailable

Configure a separate issuer key, or intentionally keep the deployment read-only

approval_required

Have a trusted operator/broker sign the returned payload and inject the one-use approval header

Approval rejected

Check expiry, same-process and same-tenant routing, unchanged arguments, service principal, BYOK credential, exact review JSON, and replay status

high_risk_operation_blocked

Use a separately curated implementation if one is ever security-reviewed; the generic dispatcher cannot enable it

Provider response omitted

Narrow the operation/output or page size; do not disable limits casually

Container unhealthy

Check sanitized logs, resource limits, /health, and the exact six-tool expectation

Never post tokens, .env, provider IDs, private URLs, or unredacted provider output in a support report.

Documentation

License and trademarks

The MADPANDA3D-authored code is available under the MIT License. The derived endpoint inventory retains Cloudflare api-schemas BSD-3-Clause attribution in NOTICE.

Cloudflare and related product names are trademarks of Cloudflare, Inc. or their respective owners. This independent project is not an official Cloudflare product, distribution, partnership, sponsorship, or endorsement.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

  • MCP server for interacting with the Supabase platform

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MADPANDA3D/CF-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server