mcp-fetch-worker

mcp-fetch-worker

Enables AI bots and agents to call self-hosted APIs behind Cloudflare Access through a single HTTP fetch tool, using human-approved OAuth tokens instead of long-lived credentials.

Category
Visit Server

README

mcp-fetch-worker

A generic MCP connector for bots and agents that need to call your self-hosted APIs without handing over long-lived credentials.

Put Cloudflare Access in front of this Worker with Managed OAuth, and bots like Grok Bot or OpenClaw can connect via Dynamic Client Registration (DCR). You approve access when the bot first connects; revoke it any time from Access. The bot gets a scoped token tied to the connector, not a generic API key it can reuse elsewhere.

Production MCP URL: https://<your-domain>/mcp

Why this exists

Managed bots are good at tool calling, but most homelab and self-hosted apps don't ship an MCP server. You could:

  • Use a service token: persistent access via custom headers. Fine for scripts; risky for bots you don't fully trust.
  • Let the bot use Chrome: it opens your web UI, you sign in through SSO, repeat. Works, but burns tokens and doesn't scale.
  • Build a bespoke MCP per app: correct, but a lot of work for a Home Assistant API or a random internal dashboard.

This Worker is the middle ground: one thin MCP connector that exposes a single http_fetch tool. Point it at APIs on the same origin (already behind Access), and the bot can GET/POST them using the OAuth token you approved. No dedicated MCP server required on the other end.

See First World Problem: How do I grant my bots access to my stuff without it wrecking everything? for the full write-up.

How it works

Bot (Grok Bot, OpenClaw, Cursor, …)
  → OAuth via Cloudflare Access Managed OAuth (you approve)
  → MCP at /mcp (this Worker)
  → http_fetch to allowed origins on your domain
  → Access JWT forwarded on outbound requests
  1. Access handles authentication: Managed OAuth + DCR. The Worker is not an OAuth server; it validates the Cf-Access-Jwt-Assertion Access forwards.
  2. This Worker serves Streamable HTTP MCP with one tool: http_fetch.
  3. Your APIs sit on the same origin behind Access. Create Linked App Token policies so the forwarded JWT is accepted downstream.

Outbound fetches are restricted to the Worker's own origin by default (ALLOWED_ORIGINS overrides this).

What it does

  • Serves MCP at /mcp (and /sse as a legacy alias) using the Agents SDK createMcpHandler
  • Exposes one tool, http_fetch, for GET/POST requests against allowed origins
  • Validates Cf-Access-Jwt-Assertion against your team's JWKS when CF_ACCESS_TEAM_DOMAIN and CF_ACCESS_AUD are set
  • Forwards the caller's Access JWT on outbound fetches (Cf-Access-Jwt-Assertion, Cf-Access-Token, Authorization: Bearer)

Deploy

npm install
npm run cf-typegen   # regenerate Env types from wrangler.jsonc
npx wrangler deploy

Configuration lives in wrangler.jsonc. Workers Logs and Traces are enabled by default via the observability block.

Custom domain route

Add a Worker route in wrangler.jsonc (or via the dashboard):

"routes": [
  {
    "pattern": "example.com/mcp*",
    "zone_name": "example.com"
  }
]

Then redeploy. The MCP endpoint is https://<your-domain>/mcp.

Environment variables

Set in wrangler.jsonc vars, as secrets (wrangler secret put), or in .dev.vars for local dev (see .dev.vars.example):

Variable Required Description
CF_ACCESS_TEAM_DOMAIN Production https://<team>.cloudflareaccess.com
CF_ACCESS_AUD Production AUD tag from your Access application
ALLOWED_ORIGINS No Comma-separated fetch allowlist (default: Worker origin)
MAX_RESPONSE_BYTES No Truncate bodies above this size (default: 65536)
DEFAULT_HEADERS No JSON object of default outbound headers (secret; see below)

When CF_ACCESS_* vars are unset, the Worker still serves MCP (useful for wrangler dev) but does not validate Access JWTs. Set both vars in production.

Default headers for API keys

Store extra outbound headers as a Worker secret so http_fetch can call APIs that need a key without putting secrets in chat or baking one API into the Worker:

wrangler secret put DEFAULT_HEADERS
# paste: {"X-Api-Key":"your-key-here"}

For local dev, set a non-empty value in wrangler.jsonc vars, pass --var DEFAULT_HEADERS='{"X-Api-Key":"..."}' to wrangler dev, or use wrangler secret put on the deployed Worker. When secrets.required is configured, .dev.vars only loads those secret names, so put DEFAULT_HEADERS in vars or use --var locally.

DEFAULT_HEADERS must be a JSON object with string values, e.g. {"X-Api-Key":"..."}. On every http_fetch call, those headers are merged into the outbound request. Headers you pass in the tool call override defaults on name conflicts. Access JWT forwarding (Cf-Access-Jwt-Assertion, Cf-Access-Token, Authorization) always wins and cannot be overridden by defaults.

Unset or empty DEFAULT_HEADERS adds no extra headers.

Cloudflare Access setup

  1. Add a self-hosted Access application for your MCP URL (e.g. https://<your-domain>/mcp or a path covering /mcp*)
  2. Enable Managed OAuth on the application (Advanced settings); this enables DCR for MCP clients
  3. Copy the application's AUD tag into CF_ACCESS_AUD
  4. Set CF_ACCESS_TEAM_DOMAIN to your team domain (e.g. https://<team>.cloudflareaccess.com)
  5. Configure Access policies for who may connect: time-bound, email-based, or whatever fits your approval model

For downstream Access-protected APIs on the same origin, create Linked App Token policies on those applications so the forwarded JWT is accepted.

MCP client configuration

Paste this URL into Cursor, Grok Bot, OpenClaw, or any Streamable HTTP MCP client:

https://<your-domain>/mcp

Use Streamable HTTP transport (not legacy SSE). The client will OAuth through Access Managed OAuth on first connect; that's your human-in-the-loop approval step.

Local development

npm run dev

MCP is available at http://localhost:8787/mcp without Access validation.

http_fetch tool

{
  "url": "/api/example",
  "method": "GET"
}
  • url: absolute URL or same-origin path
  • method: GET (default) or POST
  • headers: optional extra headers (override DEFAULT_HEADERS on conflict)
  • body: optional POST body

DEFAULT_HEADERS (secret) are applied first; tool headers override them. Access JWT headers are always applied last.

Returns JSON with status, headers, body, and truncated flag.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured