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
One OAuth connector for your team's remote MCP servers, with Tool Packs, guardrails and logs.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Remote streamable-HTTP MCP server running on a single Cloudflare Worker. Your assistant gets live Airbnb, Amazon, Booking.com, Google Flights, Maps and Reddit data, social search on X, Instagram and TikTok, the Meta Ad Library, and image/video generation without any keys. Connect your own accounts to let it send WhatsApp or Telegram messages, work an IMAP inbox, manage Meta Ads campaigns and publish to X and LinkedIn. OAuth 2.1 with PKCE; stored credentials are AES-256-GCM encrypted.
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
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.284 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.-