Skip to main content
Glama

cloudflare-mcp

Servidor MCP remoto (FastMCP + transporte streamable-http) para administrar Cloudflare. Fase 1: módulo domains (zonas y registros DNS). Fase 2: módulo workers (deploy de Workers y sitios estáticos, rutas/dominios, secrets, KV).

Estructura

cfmcp/
  server.py              # instancia FastMCP + auth (Google OAuth) + registro de tools + ASGI app
  cf_client.py            # AsyncCloudflare client (CLOUDFLARE_API_TOKEN) + resolución de account_id
  domains/
    zones.py              # list_zones, get_zone, create_zone
    dns_records.py        # list_dns_records, get_dns_record, create_dns_record, update_dns_record, delete_dns_record
  workers/
    scripts.py            # list_workers, get_worker_code, deploy_worker, delete_worker, versiones + rollback
    routing.py            # subdominio workers.dev, rutas por zona, dominios custom directos
    secrets.py            # list/set/delete de secrets por worker
    kv.py                 # namespaces KV + get/put/delete de claves
    assets.py             # deploy_static_site (Workers Static Assets, upload directo)
tests/
requirements.txt
Dockerfile

Related MCP server: @archawat/mcp-cloudflare

Autenticación

Google OAuth vía GoogleProvider (fastmcp.server.auth.providers.google), un OAuthProxy completo contra Google — el servidor delega el login a accounts.google.com en vez de emitir credenciales propias. Necesario porque la UI de conectores de Claude Desktop / claude.ai solo acepta OAuth (no tiene campo para pegar un bearer token estático — confirmado, ver issue #112).

GoogleProvider no tiene allowlist propia — cualquier cuenta de Google podría autenticar. Un AuthMiddleware (_only_allowed_user() en cfmcp/server.py) restringe el acceso a un único email (ALLOWED_GOOGLE_EMAIL), verificando el claim email (y email_verified) del token verificado por Google.

Requiere crear manualmente un OAuth Client en Google Cloud Console (no hay API para esto) — ver Despliegue en Dokploy.

Nota histórica: un proveedor de password auto-emitido (PasswordOAuthProvider) y GitHubProvider se probaron antes; se optó por Google porque la fricción de configurar un OAuth Client externo (igual en ambos) se acabó considerando aceptable a cambio de no tener que recordar/recuperar una contraseña compartida.

Variables de entorno

  • CLOUDFLARE_API_TOKEN — token de Cloudflare. Fase 1 requería solo Zone→Zone→Read y Zone→DNS→Edit, pero create_zone necesita Zone→Zone→**Edit** y, en Zone Resources, Include → All zones from an account (un token acotado a zonas específicas no puede crear zonas nuevas: la zona todavía no existe para poder seleccionarla); fase 2 (Workers) requiere además Account→Workers Scripts→Edit, Account→Workers KV Storage→Edit y Zone→Workers Routes→Edit (cubre scripts, versiones, deployments, Cron Triggers, subdominio, dominios custom); R2 y D1 requieren sus propios grupos aparte: Account→Workers R2 Storage→Edit y Account→D1→Edit. Un token con menos permisos sigue funcionando para lo que sí cubre — los tools sin permiso fallan con un error claro (401/403) en vez de romper el arranque.

  • CLOUDFLARE_ACCOUNT_ID — opcional. Las APIs de Workers son account-scoped; si no se define, se autodescubre cuando el token ve exactamente una cuenta (requiere que el token pueda listar cuentas).

  • MCP_BASE_URL — URL pública del servidor (ej. https://cfmcp.clicestrategico.com), usada para construir las URLs de OAuth.

  • GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET — credenciales del OAuth Client creado en Google Cloud Console.

  • ALLOWED_GOOGLE_EMAIL — único email de Google autorizado a autenticar.

  • FASTMCP_HTTP_ALLOWED_HOSTS / FASTMCP_HTTP_ALLOWED_ORIGINS — obligatorio en producción, formato lista JSON (ej. ["cfmcp.clicestrategico.com"]), no un string plano (pydantic-settings lo parsea como JSON y crashea si no lo es). FastMCP bloquea con 421 cualquier Host que no sea loopback por defecto (protección DNS-rebinding).

Desarrollo local

python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
CLOUDFLARE_API_TOKEN=xxx MCP_BASE_URL=http://127.0.0.1:8000 MCP_OAUTH_PASSWORD=xxx uvicorn cfmcp.server:app --host 0.0.0.0 --port 8000

Docker

docker build -t cloudflare-mcp .
docker run -p 8000:8000 --env-file .env cloudflare-mcp

Despliegue en Dokploy

Va en el proyecto "Remote MCP" existente (junto a Namecheap MCP y Google Drive MCP), mismo patrón: repo GitHub + buildType: dockerfile + dominio en *.clicestrategico.com.

  1. Crear el OAuth Client en Google Cloud Console (paso manual, sin API): APIs & Services → OAuth consent screen (mínimo requerido), luego Credentials → Create Credentials → OAuth client ID tipo Web application, con redirect URI https://cfmcp.clicestrategico.com/auth/callback (default de GoogleProvider). Copiar el Client ID y Client secret generados.

  2. Crear la app en Dokploy (application-create) en el environment de "Remote MCP".

  3. Conectar el repo vía application-saveGitProvider (no saveGithubProvider — esa requiere una GitHub App integration configurada en Dokploy, que no existe aquí; saveGitProvider con customGitUrl funciona directo contra el repo público) + saveBuildType(dockerfile).

  4. Env vars (CLOUDFLARE_API_TOKEN, MCP_BASE_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, ALLOWED_GOOGLE_EMAIL, FASTMCP_HTTP_ALLOWED_HOSTS, FASTMCP_HTTP_ALLOWED_ORIGINS) vía application-saveEnvironment. Los 400 que este endpoint daba eran un bug del bridge OpenAPI de fastmcp en dokploy-mcp (descartaba campos requeridos-pero-nullables enviados como null) — corregido en dokploy-mcp el 2026-07-21; pasar "" en buildArgs/buildSecrets siempre funciona.

  5. application-deploy.

  6. domain-create con el dominio real (certificateType: letsencrypt).

Tools disponibles

domains — list_zones, get_zone, create_zone, list_dns_records, get_dns_record, create_dns_record, update_dns_record, delete_dns_record

create_zone da de alta un dominio y devuelve los nameservers a poner en el registrador. Crea la zona vacía: Cloudflare no copia los registros existentes por API (eso solo lo hace el escaneo del dashboard), así que hay que recrearlos con create_dns_record antes de cambiar los NS o el dominio se cae al momento del switch. No hay delete_zone a propósito — borrar una zona es destructivo y difícil de deshacer.

workers —

  • Scripts: list_workers, get_worker_code, deploy_worker (worker de un solo módulo ES desde código fuente), delete_worker, list_worker_versions, rollback_worker, list_worker_deployments/get_worker_deployment (qué versión(es) están corriendo realmente y con qué split de tráfico — distinto de list_worker_versions, que solo lista versiones guardadas sin estado de despliegue)

  • Rollout gradual: create_worker_version (deja una versión lista sin mandarle tráfico) + set_worker_traffic_split (reparte tráfico entre 2+ versiones, ej. canary 10%/90%) — combinado con rollback_worker para el caso simple de 100% a una versión

  • Cron Triggers: get_worker_schedules, set_worker_schedules (reemplaza el set completo de crons del worker)

  • Routing: get_workers_subdomain, set_worker_subdomain (workers.dev), list/create/delete_worker_route (rutas por zona), list/attach/detach_worker_domain (dominios custom directos — Cloudflare crea DNS y certificado solo)

  • Secrets: list_worker_secrets, set_worker_secret, delete_worker_secret

  • KV: list_kv_namespaces, create/delete_kv_namespace, list_kv_keys, kv_get, kv_put, kv_delete

  • R2: list_r2_buckets, create_r2_bucket, get_r2_bucket, delete_r2_bucket (gestión de buckets; subida/lectura de objetos no incluida — mismo alcance que el MCP oficial de Cloudflare para este producto)

  • D1: list_d1_databases, create_d1_database, get_d1_database, delete_d1_database, query_d1_database (corre SQL real — DDL, SELECT, INSERT/UPDATE/DELETE)

  • Static sites: deploy_static_site — sube una web estática ya construida vía Workers Static Assets (manifest con hash sha256(base64(contenido)+extensión)[:32], sesión de upload con JWT propio, deploy con completion JWT). Límite deliberado de 300 archivos / 10 MiB: el contenido viaja como argumentos MCP, no como filesystem. Acepta worker_code opcional para poner un Worker delante de los assets (con binding ASSETS).

Dónde termina el MCP y empieza wrangler: este servidor es remoto — no ve los archivos locales de un proyecto. Workers de un archivo y sitios estáticos pequeños/medianos caben perfecto por MCP; apps con build (Vite/Astro/Next) y node_modules se despliegan con wrangler deploy local, y este MCP complementa gestionando rutas, dominios, secrets y KV después.

Tests

uv run --with fastmcp --with 'cloudflare>=5.4.0,<6' --with pytest python -m pytest tests/

Cubren los helpers puros del flujo de assets (algoritmo de hash exacto de Cloudflare, manifest, límites) y tests a nivel de wire con MockTransport para cada tool de escritura: que el multipart de deploy_worker nombra el módulo por filename, el flujo completo de deploy_static_site (sesión → upload con JWT de sesión y partes por hash → deploy con completion JWT), que create_worker_version pega contra /versions y no /scripts (no debe ir live), que set_worker_traffic_split manda la lista completa de versiones sin recortarla, y las formas exactas de set_worker_schedules, create_r2_bucket/list_r2_buckets y query_d1_database.

Comparación con el MCP oficial de Cloudflare (cloudflare/mcp-server-cloudflare)

Investigado 2026-07-22 leyendo su código fuente (no solo el README) antes de expandir esta fase 2. Son complementarios, no redundantes: el oficial está deliberadamente sesgado a lectura/observabilidad; este server cubre el lado de escritura/deploy que el oficial evita.

Producto

Oficial

Este MCP

Workers scripts

Solo lectura (workers_list, workers_get_worker, workers_get_worker_code)

Deploy/delete/versiones/rollback/canary/cron completos

DNS/Zone

Solo lectura (zone_details, zones_list)

CRUD completo de registros

Routing/dominios/secrets

No existe

Completo

KV

Solo namespaces (CRUD), sin leer/escribir claves

Namespaces + lectura/escritura de claves

R2

Buckets (CRUD), sin objetos

Mismo alcance (buckets, sin objetos)

D1

CRUD + query SQL

Mismo alcance

Logs/Observability

query_worker_observability — consulta real y funcional

Deliberadamente no duplicado, usar el oficial (observability.mcp.cloudflare.com)

Workers Builds

Solo monitorea builds ya conectados por dashboard

No implementado — ver nota abajo

Sobre Workers Builds: conectar un repo Git nuevo por API no es posible — confirmado contra un issue abierto de Cloudflare (cloudflare/workers-sdk#12058 pidiendo justamente ese endpoint) y la documentación oficial, que solo describe el flujo por dashboard (Settings → Builds → Connect). Lo único automatizable sin ese endpoint es disparar un rebuild de un proyecto ya conectado, vía un Deploy Hook — pero el Deploy Hook mismo también se crea solo por dashboard (Settings → Builds → Deploy Hooks), como paso manual único (mismo patrón que el OAuth Client de Google). No implementado aún porque depende de ese paso manual del usuario primero.

Roadmap

  • Registrar (dominios comprados vía Cloudflare Registrar): listar/consultar, auto_renew/locked/privacy

  • Persistencia de clientes/tokens OAuth (SQLite) para sobrevivir restarts

  • Hyperdrive bindings (pendiente confirmar si se usa)

  • Cache Purge / reglas WAF

  • Workers logs (tail) — es WebSocket, y el MCP oficial ya cubre esta necesidad vía query_worker_observability; no duplicar sin razón concreta

  • trigger_worker_build — solo si el usuario crea un Deploy Hook manualmente primero (ver nota de Workers Builds arriba)

Related MCP Connectors

Related MCP Servers