cf-control-mcp
Provides tools for managing a Cloudflare account, including zones, DNS records, Workers, and KV namespaces.
Provides tools for listing and inspecting Cloudflare Worker scripts and their metadata.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@cf-control-mcplist my Cloudflare zones and DNS records"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
cf-control-mcp
Private infrastructure control plane for MCP clients
Cloudflare Workers · OAuth + PKCE · Cloudflare · Hugging Face · ProxyHarvest · Internet Intelligence · Sandboxed Execution
A private remote MCP server that turns a Cloudflare Worker into an authenticated control surface for infrastructure, provider APIs, web access, and controlled code execution.
Production MCP · Source · OAuth Wrapper · Plugin · Verification
Developed by DreamWorker
THE FUTURE IS CREATED TODAY
✦ What this project is
cf-control-mcp is a private, owner-approved remote MCP server deployed on Cloudflare Workers. It exposes a single MCP endpoint that can inspect and manage Cloudflare resources, work with Hugging Face repositories, inspect ProxyHarvest gateway reachability, fetch/search the public web, run short-lived code in Piston, and dispatch real ephemeral Ubuntu jobs through GitHub Actions.
The server is intentionally stateless at the MCP transport layer: one authenticated HTTP request produces one JSON-RPC response, without requiring Durable Objects or a long-lived SSE session.
MCP Client
│
│ OAuth + PKCE / legacy bearer
▼
Cloudflare Worker ───────────────► Cloudflare API
│ Hugging Face Hub API
│ ProxyHarvest Gateway
│ Public Web
│ Piston Sandbox
└─────────────────────────────► GitHub Actions Ubuntu RunnerRelated MCP server: Remote MCP Server on Cloudflare
✦ Capability map
Surface | What it provides | Representative tools |
☁️ Cloudflare | Zones, DNS, cache, Workers, routes, KV, token verification, direct API passthrough |
|
🤗 Hugging Face | Identity, model search, repo metadata, file commits/deletes, generic Hub API access |
|
🌐 Internet Intelligence | Unified/multi-provider search, news & image search, hardened fetch, browser rendering, markdown/extract/links, bounded crawl, evidence-based deep research |
|
⚙️ Execution | Fast Piston snippets and full ephemeral GitHub Actions Ubuntu runners |
|
🛰️ ProxyHarvest | Gateway health, source reachability, TCP/TLS transport reachability |
|
🔐 Auth | OAuth discovery, DCR, PKCE S256, owner approval, access/refresh tokens, legacy bearer |
|
Boundary: ProxyHarvest Cloudflare checks are reachability signals only. They are never tunnel/protocol/WireGuard verification. Real
VERIFIEDstatus remains exclusive to the Local Real Test Bridge usingsing-box + curl.
✦ Architecture
flowchart LR
C["MCP Client<br/>ChatGPT · Claude · Codex · Desktop"]
O["OAuth 2.1-style flow<br/>PKCE S256 + Owner Approval"]
L["Legacy Bearer Path"]
W["Cloudflare Worker<br/>cf-control-mcp"]
CF["Cloudflare API v4"]
HF["Hugging Face Hub"]
PH["ProxyHarvest Gateway"]
WEB["Public Web"]
P["Piston Sandbox"]
GH["GitHub Actions<br/>Ephemeral Ubuntu"]
C --> O --> W
C --> L --> W
W --> CF
W --> HF
W --> PH
W --> WEB
W --> P
W --> GHDesign principles
Private by default —
/mcprequires an accepted OAuth access token or the legacy owner bearer token.Provider secrets stay server-side — Cloudflare, Hugging Face, and GitHub credentials remain Worker secrets.
Read first, write deliberately — focused destructive tools expose destructive annotations and, where implemented, require explicit confirmation flags.
No fake verification — reachability, deployment, and execution claims are returned from the actual provider/tool path.
Stateless transport — simple Streamable HTTP JSON-RPC without per-client server sessions.
✦ Authentication & OAuth
There are two credential layers:
Layer | Credential | Purpose |
Client → MCP Worker | OAuth access token or | Protects access to the MCP control surface |
MCP Worker → Providers |
| Authenticates server-side provider calls |
The OAuth wrapper supports:
protected-resource discovery
authorization-server discovery
Dynamic Client Registration
Authorization Code flow
PKCE with
S256explicit owner approval page
access tokens
refresh tokens through
offline_accessOAuth resource/audience binding
legacy owner-token compatibility
OAuth endpoints
Endpoint | Purpose |
| Protected-resource metadata |
| Authorization-server metadata |
| Dynamic client registration |
| PKCE authorization + owner approval |
| Authorization-code / refresh-token exchange |
| Streamable HTTP MCP endpoint |
Current authorization behavior
After the owner explicitly approves an OAuth client, the current implementation exposes the same owner-approved tool catalog as the legacy bearer path. This includes tools capable of writes and destructive operations.
The advertised OAuth scopes are currently:
mcp:read
offline_accessoffline_access enables refresh tokens. The current implementation does not use separate fine-grained OAuth scopes to hide individual write-capable tools after owner approval.
Generic passthrough tools such as
cf_api_requestandhf_api_requestshould be treated as privileged interfaces because their effective power follows the underlying provider token permissions.
Emergency revocation
Rotating MCP_AUTH_TOKEN invalidates the stateless client-side authorization artifacts derived from it, including registered client IDs, authorization codes, access tokens, refresh tokens, and the legacy bearer credential.
✦ Quick start
1. Install
npm install
wrangler login2. Configure required Worker secrets
openssl rand -hex 32
wrangler secret put MCP_AUTH_TOKEN
wrangler secret put CLOUDFLARE_API_TOKEN
wrangler secret put CLOUDFLARE_ACCOUNT_ID3. Optional provider integrations
# Hugging Face control tools
wrangler secret put HUGGINGFACE_TOKEN
# GitHub Actions real-runner execution
wrangler secret put GITHUB_PAT
# Optional; defaults to nimazasinich/cf-control-mcp
wrangler secret put GITHUB_REPO
# Internet Intelligence search providers (all optional)
wrangler secret put BRAVE_SEARCH_API_KEY
wrangler secret put TAVILY_API_KEY
wrangler secret put EXA_API_KEY4. Deploy
npm run deployProduction endpoint:
https://cf-control-mcp.amin-chinisaz-edu.workers.dev/mcp✦ Connecting an MCP client
The repository includes a direct MCP configuration at:
plugins/cf-control/.mcp.jsonIt points to the production endpoint:
{
"mcpServers": {
"cf_control": {
"type": "http",
"url": "https://cf-control-mcp.amin-chinisaz-edu.workers.dev/mcp"
}
}
}OAuth-capable clients can discover the authorization server from the 401 challenge and the .well-known metadata, dynamically register, complete PKCE, and continue with access/refresh tokens after owner approval.
Trusted legacy/desktop clients may use the owner bearer path directly:
curl -X POST \
https://cf-control-mcp.amin-chinisaz-edu.workers.dev/mcp \
-H "Authorization: Bearer <MCP_AUTH_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'✦ Cloudflare control surface
Focused operations
Tool | Purpose | Type |
| Verify the configured Cloudflare token | Read |
| List/filter zones | Read |
| Inspect DNS records | Read |
| Create DNS record | Write |
| Delete DNS record | Destructive |
| Purge selected URLs or entire zone cache | Destructive |
| List deployed Worker scripts | Read |
| Inspect Worker/service metadata | Read |
| Resolve the workers.dev subdomain | Read |
| Inspect Worker routes for a zone | Read |
| Deploy a single-module ES Worker | Destructive / Write |
| Delete Worker script | Destructive |
| List KV namespaces | Read |
| Read KV value | Read |
| Write KV value | Write |
| Generic Cloudflare API v4 passthrough | Privileged |
cf_deploy_worker_module sends source directly to Cloudflare, does not persist that source inside the MCP Worker, validates script/module names, caps source size, and requires confirm_destructive=true.
cf_api_request exists for Cloudflare APIs that do not yet have focused tools, including areas such as zone settings, SSL/TLS, WAF/firewall, Access, R2, D1, Pages, Stream, Images, Load Balancing, and other account/zone endpoints allowed by the configured Cloudflare token.
✦ Hugging Face control surface
Enable with:
wrangler secret put HUGGINGFACE_TOKENTool | Purpose |
| Verify token and return account metadata |
| Search public or author-filtered models |
| Read model/dataset/space metadata |
| List repo tree at a revision |
| Create model/dataset/space repo |
| Delete a repo |
| Create/update one non-LFS file through the Commit API |
| Delete a repo file through the Commit API |
| Generic Hugging Face Hub API passthrough |
If HUGGINGFACE_TOKEN is not configured, hf_* tools return an explicit configuration error instead of silently falling back.
✦ Internet Intelligence (v1.6.0)
Version 1.6.0 turns the Worker into a private internet-capable MCP server. Twelve tools cover search, reading, rendering, link discovery, bounded crawling, and evidence-based research — all behind the same owner-approved auth, with the same server-side secret model.
Search providers
Search is provider-agnostic. Results from every provider are normalized to a single shape ({title, url, snippet, provider, published_at?, score?}) before they reach the client.
Provider | Secret | Strengths | Configured when |
Brave |
| General web, news, images | secret present |
Tavily |
| Recent/news/research queries | secret present |
Exa |
| Semantic/neural retrieval | secret present |
DuckDuckGo | (none) | Keyless best-effort fallback | always |
All three paid keys are optional. The server starts and runs with zero, one, two, or all three present. web_search with provider="auto" picks the best configured provider and falls back to the next only on genuine failure; with no paid keys it still answers via the keyless DuckDuckGo fallback. A provider-specific request (e.g. provider="brave") returns a CONFIGURATION_ERROR when that provider's secret is absent — it never silently degrades.
# All optional — enable the providers you want
wrangler secret put BRAVE_SEARCH_API_KEY
wrangler secret put TAVILY_API_KEY
wrangler secret put EXA_API_KEYTools
Tool | Purpose | Type |
| Unified search; | Read |
| Query several providers concurrently, merge + dedupe, per-provider errors isolated | Read |
| Recency-oriented search (Tavily → Brave → Exa → DDG) | Read |
| Image metadata search (Brave); no image bytes downloaded | Read |
| Hardened public fetch: GET/HEAD/POST/PUT/PATCH/DELETE, bounded body, redirect re-validation | Read/Write* |
| Cloudflare Browser Rendering of JS-heavy public pages | Read |
| Clean Markdown of a page (Browser Rendering, with fetch fallback) | Read |
| CSS-selector content extraction (scrape endpoint, with fetch fallback) | Read |
| Absolute-resolved, deduped outbound/internal links | Read |
| One-page inspection: final URL, status, title, markdown, text, optional links | Read |
| Strictly bounded multi-page crawl (default 10 / max 50 pages, max depth 5); robots.txt-aware by default | Read |
| Query expansion → multi-provider search → fetch → evidence aggregation | Read |
*web_fetch is deliberately not annotated readOnlyHint because it can issue POST/PUT/PATCH/DELETE. Every other web tool is readOnlyHint: true. All web tools are openWorldHint: true.
SSRF hardening
Every URL-taking tool validates its target before and after each redirect hop, so a public-looking URL cannot 30x-redirect into a private target. The guard rejects:
localhost, loopback (127.0.0.0/8), and the unspecified address (0.0.0.0)RFC1918 private ranges (
10/8,172.16/12,192.168/16), carrier-grade NAT (100.64/10)link-local (
169.254/16) and cloud metadata endpoints (e.g.169.254.169.254,metadata.google.internal)obfuscated IPv4 (decimal / octal / hex / short) forms of the above
IPv6 loopback (
::1), unique-local (fc00::/7), link-local (fe80::/10), and IPv4-mapped equivalents*.internal,*.local, and other internal hostname patterns
The robots.txt fetch used by the crawler goes through this same hardened path — it is never a bypass.
HTML processing layer
web_markdown, web_extract, web_links, web_snapshot, web_crawl, and web_deep_research share a single, dependency-free HTML layer (src/internet/html.ts) built on the Workers-native HTMLRewriter. There is no second, ad-hoc parser. It streams the markup rather than building a DOM, so it stays light and predictable:
htmlToText— page title + readable text, with<script>/<style>/<noscript>removedhtmlToMarkdown— headings, paragraphs, list items, blockquotes, and preformatted blocks, with a nesting-aware block stack so inner blocks don't clobber outer onesextractLinks—href/srcresolved to absolute URLs, normalized and deduped, with an optional same-origin filterextractBySelectors— per-selector text extraction that returns exactly what the DOM contained (no model-generated fields)
Output size limits are measured in actual UTF-8 bytes (via TextEncoder), not JavaScript character count, and truncation never splits a multi-byte character.
Robots layer
web_crawl and web_deep_research are polite by default. Before fetching a page they consult the origin's /robots.txt through the robots layer (src/internet/robots.ts):
a conservative subset parser —
User-agent,Allow,Disallow,Crawl-delay— with longest-prefix rule matching and simple*wildcard support (this is not a full RFC 9309 implementation)one fetch per origin, cached in memory (10-minute TTL); it does not re-fetch
/robots.txtfor every pagecrawler identity is
cf-control-mcp/1.6— it does not impersonate Chrome, Googlebot, or Bingbot, and robots matching uses that same identitypages disallowed by robots are reported with
skippedReason: "ROBOTS_DENIED"andstatus: null— a policy decision, not a network failurea site-declared
Crawl-delayis honored but clamped toMAX_ROBOTS_CRAWL_DELAY_SECONDS(10 s); an origin asking for more is dropped withROBOTS_CRAWL_DELAY_EXCEEDEDso execution stays boundedfailure policy: a
2xxrobots.txt is parsed and applied; a404/non-2xxis treated as "allow all"; a timeout or error never crashes the crawl. The result reports whether a policy was actually fetched (robotsFetched) — it never pretends to have evaluated one it couldn't retrieve
Set respect_robots: false on web_crawl/web_deep_research to disable this gating. web_fetch and web_render are single-target tools and do not consult robots.txt.
Limits & error model
Results are capped (search ≤ 50), fetch bodies bounded (default 200 KB, max 1 MB), renders time-boxed (default 30 s, max 60 s), crawls bounded (≤ 10 default / ≤ 50 hard pages), and research bounded by source count, total bytes, and duration. Failures map to a structured code — CONFIGURATION_ERROR, PROVIDER_ERROR, RATE_LIMITED, TIMEOUT, BLOCKED_TARGET, INVALID_ARGUMENT, NETWORK_ERROR, RENDER_ERROR — and provider API keys are never included in error text.
web_deep_research deliberately returns structured evidence (ranked sources plus extracted supporting passages), not an invented narrative or conclusion; synthesis is left to the calling model.
web_render, web_markdown, and web_extract use Cloudflare Browser Rendering via the existing CLOUDFLARE_API_TOKEN — no new secret. If that token lacks the Browser Rendering permission, those tools return a clear RENDER_ERROR and (where possible) fall back to a plain fetch.
✦ Execution tools
Fast sandbox: Piston
run_code sends short snippets to the public Piston service and returns execution output. Use list_code_runtimes to discover currently available runtime versions.
This path is:
ephemeral
stateless
external to the Cloudflare account
not suitable for secrets or private data
Real runner: GitHub Actions
gh_run_code dispatches .github/workflows/mcp-exec.yml and returns a run_key.
gh_get_run_result polls that key until the Actions run is visible/completed, then returns status and job logs.
The runner is a real ephemeral Ubuntu VM with internet access and can run optional setup commands such as pip install, npm install, or apt operations within the workflow’s limits.
Required Worker secret:
wrangler secret put GITHUB_PATThe token should be scoped to the target repository with the permissions required by the workflow dispatch/read path.
✦ ProxyHarvest boundary
The three ProxyHarvest tools are intentionally read-only:
Tool | Meaning |
| Gateway / edge / optional AI-provider health |
| Public source reachability |
| TCP/TLS transport reachability |
They explicitly return classifications that distinguish gateway/source/transport reachability from real proxy verification.
Reachable ≠ Verified.
✦ Verification
Local static checks
npm ci
npx tsc --noEmit
python3 -m py_compile scripts/oauth_smoke.py scripts/apex_cf_bridge.pyLive OAuth/MCP smoke test
The deployment workflow executes scripts/oauth_smoke.py against the live Worker. The current smoke test validates:
protected-resource discovery
authorization-server discovery
Dynamic Client Registration
PKCE
S256owner approval UI
authorization-code exchange
access + refresh tokens
OAuth
tools/listpresence of the owner-approved write-capable catalog
a harmless
GET /zonesthroughcf_api_requestusing OAuthrefresh-token grant
legacy bearer compatibility
optional Hugging Face live check
unauthenticated
401+WWW-Authenticateresource metadata
The smoke test does not print the owner secret or issued OAuth tokens.
✦ Repository layout
.
├── src/
│ ├── index.ts # MCP server + tool handlers
│ ├── oauth-worker.ts # OAuth discovery, PKCE, tokens, MCP wrapper
│ └── internet/ # v1.6 Internet Intelligence layer
│ ├── types.ts # normalized result + structured error model
│ ├── util.ts # limits, URL normalize/dedupe, bounded fetch
│ ├── ssrf.ts # SSRF guard + redirect-revalidating safeFetch
│ ├── fetch.ts # hardened fetch / fetchText + bounded stream reader
│ ├── html.ts # shared HTMLRewriter text/markdown/links/extract
│ ├── robots.ts # robots.txt policy layer (crawl politeness)
│ ├── browser.ts # Cloudflare Browser Rendering client
│ ├── crawl.ts # bounded, robots-aware crawl + snapshot
│ ├── research.ts # evidence-based deep research pipeline
│ ├── search-router.ts # provider selection, fallback, multi-search
│ ├── providers/ # brave / tavily / exa / ddg adapters
│ └── tools.ts # the 12 web_* MCP tool definitions
├── scripts/
│ └── oauth_smoke.py # live end-to-end OAuth/MCP verification
├── plugins/cf-control/
│ ├── .mcp.json # remote MCP connection descriptor
│ └── skills/ # OpenAI/Codex skill package
├── .github/workflows/
│ ├── ci.yml # static verification
│ ├── deploy.yml # deploy + live smoke verification
│ └── mcp-exec.yml # ephemeral real-runner execution backend
├── assets/
│ └── dreamworker-logo.webp # DreamWorker developer brand
├── wrangler.jsonc
├── package.json
└── README.md✦ Operational notes
Keep all provider credentials in Worker/GitHub secret stores, never in source control.
Scope
CLOUDFLARE_API_TOKENto the minimum provider permissions needed for the enabled tools.Treat generic API passthroughs as privileged interfaces.
Treat
gh_run_codeas intentional arbitrary code execution on disposable Actions runners.Treat Piston as a public third-party sandbox; never send secrets through it.
Verify state after writes using focused read tools when practical.
Workers observability remains enabled through
wrangler.jsonc.
✦ Roadmap direction
The current 1.6.0 architecture behaves as a multi-provider infrastructure and internet-intelligence MCP gateway. Natural next upgrades include:
fine-grained OAuth scopes per capability family
schema-first argument validation
provider-aware pagination helpers
rate limiting / abuse controls
structured audit events
safer generic passthrough policies
richer MCP resources/prompts alongside tools
explicit capability discovery and health reporting
DNS-pinned SSRF protection once the Workers runtime exposes resolved IPs
Built for operators who want real control, not simulated success.
DreamWorker
THE FUTURE IS CREATED TODAY
cf-control-mcp · private remote infrastructure tooling through MCP
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
OAuth 2.1 short-link tools for AI agents with scoped tokens, approvals, audit logs, and revocation.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceA Cloudflare-deployable server that implements Model Context Protocol (MCP) capabilities, allowing AI assistants like Claude to access custom tools via OAuth authentication flows.-
- FlicenseNot gradedqualityCmaintenanceEnables deploying a Model Context Protocol (MCP) server on Cloudflare Workers with built-in OAuth authentication. It allows local clients like Claude Desktop to securely connect to and use remote tools through an HTTP/SSE transport.-
- AlicenseNot gradedqualityCmaintenanceEnables deploying and connecting to MCP servers on Cloudflare Workers with OAuth login, allowing remote access to tools via MCP clients like Claude Desktop.221 npmMIT
- FlicenseNot gradedqualityCmaintenanceEnables deploying a remote MCP server on Cloudflare Workers with OAuth, allowing MCP clients like Claude Desktop to call tools over the internet.-