Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cloudflare/cloudflare-os/llms.txt

Use this file to discover all available pages before exploring further.

Cloudflare OS supports the Model Context Protocol (MCP) natively, letting agents and Gadgets connect to any MCP-compatible server as a typed capability. Each of the server’s tools becomes a named method on the session, generated from the server’s own inputSchema, so agents get full type information and can call tools as if they were first-class APIs.
MCP gatekeepers are the best way to connect proprietary internal tools. If your organization runs any internal service with an MCP endpoint — a custom knowledge base, an internal CRM, an operations runbook tool — you can wire it into Cloudflare OS without any Gadgets-specific work on the server side.

Two MCP Gatekeepers

Cloudflare OS ships two complementary MCP gatekeepers:
gatekeeper-mcpgatekeeper-mcp-portal
Endpoint sourceUser pastes a URLAdmin sets MCP_PORTAL_URL in deployment config
Who can use itAny user, any endpointEveryone in the organization, via one portal
Trust tierbyo (user-supplied)byo by default; vetted if MCP_PORTAL_TRUST_ANNOTATIONS=true
Auto-approve writesNeverOnly when vetted + annotation permits
Grant scopeWhole server, or named toolsOne upstream server behind the portal, or named tools
No connect formUser supplies the endpointNo endpoint form — admin pre-configures it
Both gatekeepers are built on the shared @gadgets/mcp-shared library, which owns the MCP client, OAuth lifecycle, tool classification logic, scope grammar, and approval-queue wiring.

Trust Model

Auto-approval of write actions (without queuing for human review) requires a vetted endpoint. Only gatekeeper-mcp-portal can produce a vetted endpoint, and only when MCP_PORTAL_TRUST_ANNOTATIONS=true is set. User-supplied endpoints in gatekeeper-mcp are always byo and can never auto-apply writes.
The trust tier governs how far a server’s own claims about its tools are believed:
The default for all gatekeeper-mcp endpoints and for gatekeeper-mcp-portal without MCP_PORTAL_TRUST_ANNOTATIONS.
  • A tool annotated readOnlyHint: true runs as an observation and returns immediately
  • All other tools are queued for human approval before the call reaches the server
  • No claim the server makes can auto-apply a write
Honoring readOnlyHint on byo is a deliberate tradeoff: refusing it would mean an approval prompt for every search and list, making the connector impractical. The limit is that writes are never auto-applied on the server’s word alone.
Annotations are optional in MCP and most servers publish none. Every hint is compared with === true or === false, so an unannotated tool is treated as an action, always queued for approval, and can never auto-apply on either tier.

Session API

Each tool on the connected MCP server becomes a typed method on the session, generated from the server’s inputSchema. For example, for a Linear MCP server:
// Read-only tool (readOnlyHint: true) — resolves immediately as an observation.
let search = await env.MCP_LINEAR.searchIssues({ query: "state:open" });
if (search.status !== "ok") throw new Error(search.message);
let { text } = search;

// Any other tool — queued for approval; result collected afterwards.
let queued = await env.MCP_LINEAR.createIssue({ title: "Fix the thing" });
if (queued.status === "pending") {
  let outcome = await env.MCP_LINEAR.getActionResult(queued.actionId);
}

// Call by exact wire name (for tools whose names can't be method names).
await env.MCP_LINEAR.callTool("search_issues", { query: "state:open" });
The binding name is derived from the endpoint host — https://mcp.linear.app/mcp suggests MCP_LINEAR. The agent receives the full generated .d.ts as static documentation, where each method carries the tool’s own description as JSDoc and states whether calling it needs approval.

Grant Scope Grammar

Both gatekeepers share the same resource-URL scope grammar, implemented in @gadgets/mcp-shared/scope:
GrantResource URL formatWhat’s accessible
Whole server<endpoint>Every tool the server offers, including ones added later
Named tools<endpoint>#tool=a&tool=bOnly the listed tools; all others are refused
Portal server<endpoint>#server=githubEvery tool of one upstream server behind the portal
Portal server + tools<endpoint>#server=github&tool=a&tool=bOnly the listed tools of one upstream server
#server= fragments are refused by gatekeeper-mcp — scoping to a single server behind a portal is the portal connector’s grammar only.

Setting Up gatekeeper-mcp

gatekeeper-mcp requires no admin pre-configuration. Users supply endpoints themselves.
1

Add the service binding

Add GATEKEEPER_MCP to your router’s wrangler.jsonc:
wrangler.jsonc
{
  "services": [
    { "binding": "GATEKEEPER_MCP", "service": "gatekeeper-mcp" }
  ]
}
2

Configure optional variables

In the gatekeeper Worker’s environment or .dev.vars:
.dev.vars
# Required for OAuth redirect flows:
MCP_CLIENT_NAME=My Company OS

# Local dev only — allows http:// and private-IP endpoints:
MCP_ALLOW_INSECURE=true
No per-server credentials are needed. Users supply endpoints via the connect form and the gatekeeper handles OAuth discovery automatically.
3

User connects a server

  1. The user opens a Gadget’s Connections tab and clicks + New Connection → MCP.
  2. They paste the MCP endpoint URL (e.g., https://mcp.linear.app/mcp).
  3. The gatekeeper validates the endpoint (HTTPS required; private/loopback IPs blocked unless MCP_ALLOW_INSECURE).
  4. If the server requires OAuth, the standard discovery chain runs: protected resource metadata → authorization server metadata → dynamic client registration → PKCE authorization code flow.
  5. After authorization, the user chooses the grant breadth: All tools or Choose tools.
  6. The Gadget now has a typed session for the connected server.

Setting Up gatekeeper-mcp-portal

gatekeeper-mcp-portal is designed for organizations that run a centralized MCP server portal — such as a Cloudflare MCP server portal — and want every user to connect through it without typing endpoint URLs.
1

Add the service binding

Add GATEKEEPER_MCP_PORTAL to your router’s wrangler.jsonc:
wrangler.jsonc
{
  "services": [
    { "binding": "GATEKEEPER_MCP_PORTAL", "service": "gatekeeper-mcp-portal" }
  ]
}
2

Configure the portal endpoint

Set the required variables in the portal gatekeeper’s deployment configuration or local .dev.vars:
.dev.vars
# Required: the portal's MCP endpoint URL.
MCP_PORTAL_URL=https://mcp-portal.example.com/mcp

# Optional: display name shown in the connector list and approval prompts.
MCP_PORTAL_NAME=My Company MCP Portal

# Optional: auth mode — "oauth" (default), "none", or "token".
MCP_PORTAL_AUTH=oauth

# Optional: bearer token, only for MCP_PORTAL_AUTH=token.
# MCP_PORTAL_TOKEN=your-token-here

# Optional: trust upstream tool annotations for auto-approval of writes.
# MCP_PORTAL_TRUST_ANNOTATIONS=true
If MCP_PORTAL_URL is unset or invalid, the connector hides itself from the Connections UI rather than offering a connector that fails on first use.
3

User connects to the portal

Once configured, users do not need to type any endpoint URL. Pressing Connect on the portal connector goes straight to the portal’s sign-in flow. After authorizing, the user sees a list of upstream servers the portal exposes and chooses which one (and which tools) to grant to the Gadget.

OAuth Support

Both MCP gatekeepers run the full MCP OAuth 2.0 discovery chain automatically when a server responds with 401. The chain follows:
401 + WWW-Authenticate
  → protected resource metadata   (RFC 9728)
  → authorization server metadata (RFC 8414)
  → dynamic client registration   (RFC 7591)
  → authorization code + PKCE     (RFC 7636)
    + resource indicator          (RFC 8707)
The SDK falls back to conventional paths (/authorize, /token, /register) for servers that do not publish discovery metadata. Tokens are stored in the account’s Durable Object and refreshed proactively before expiry. A mid-session 401 marks the account as needing reconnection rather than triggering a refresh — the token was believed valid, so a rejection means revocation, not expiry.
All SDK OAuth operations use sdkFetch internally, which re-runs endpoint and SSRF checks on every request and redirect hop — including those the OAuth library itself follows.

Sharing Limitations

MCP Gadgets are owner-only. A Gadget bound to any MCP endpoint cannot be shared with collaborators — addObserver refuses unconditionally. Being able to authenticate to a server is not evidence of being allowed to see what the owner read from it, and the Gadget runs on the owner’s credentials throughout. To collaborate on MCP-based work, publish the Gadget as a Blueprint and let each person connect their own MCP server.

Current Limitations

  • No simulation. MCP has no way to predict a tool’s effect, so queued calls are not reflected in later reads. The agent’s turn suspends until you decide on the pending action.
  • No revert. MCP has no inverse for a tool call, so rejected actions cannot be undone.
  • No scoping below tool names. The narrowest grant available is a list of allowed tool names — “this repo only” cannot be expressed.
  • Only tools/*. Prompts, resources, sampling, and elicitation are not implemented.

Build docs developers (and LLMs) love