Cloudflare MCP portal service tokens skip per-user OAuth servers

A Cloudflare service-token session cannot finish a per-user OAuth grant, so an OAuth upstream like Sume is left out. What to use for unattended agents.

5 min readSume
All posts

A Cloudflare MCP portal session that authenticates with a service token cannot reach any upstream that needs a per-user OAuth grant. Cloudflare says so directly: servers that still require per-user OAuth are excluded from service token sessions because a service token cannot complete a per-user OAuth grant (read 2026-10-03). Sume's hosted MCP, when connected through OAuth, is exactly that kind of server, so a bot behind a service token will not see it unless the portal reaches Sume with a Sume API key instead.

The Cloudflare facts are from the service token changelog, and the portal feature reached general availability in the 2026-09-24 announcement. Sume's side is in MCP OAuth and API keys.

How a service-token session connects

Per Cloudflare, a bot presents CF-Access-Client-Id and CF-Access-Client-Secret headers. You need a Service Auth policy that matches the token on the portal's Access application and the same policy on each linked server's Access application, and per-user authentication turned off with on_behalf: false so the portal uses administrative credentials rather than an individual's grant.

Service-token session rules, read 2026-10-03
ItemValue
HeadersCF-Access-Client-Id, CF-Access-Client-Secret
PolicyService Auth policy on the portal app and on every linked server app
Settingon_behalf: false to disable per-user authentication
ExclusionServers that require per-user OAuth

What Sume offers a machine caller

Sume's docs name an API key as the path for automation that does not speak OAuth: send Authorization: Bearer <SUME_API_KEY> or x-api-key. An API-key session sees the full hosted tool set, and spend is wallet and admission based. Every write or paid call still needs an idempotency_key; dry_run and max_spend_usd are optional.

Whether a portal can attach that header to an upstream is a question for Cloudflare's portal documentation, which this post does not cover. The portable options are on the Sume side: connect the autonomous client straight to https://mcp.sume.com/mcp with the key, or skip MCP for the unattended job and call the API.

  • Unattended, task varies per call: Agent Completions with a required generation_spend_cap_usd; service-account keys are refused there with 403 insufficient_scope, so use a user key.
  • Unattended, task fixed: a saved schedule from the Scheduled actions page, which has a $1.00 default cap when unset.
  • Interactive and human-owned: keep the OAuth connector, and let each person consent.

A safe default for a bot

Give the bot a key that you can revoke on its own, keep the key in the secret store rather than the portal config you share around, and make the first call read-only. This sketch checks the server before a bot is allowed to spend.

import os, httpx

URL = "https://mcp.sume.com/mcp"
KEY = os.environ.get("SUME_API_KEY", "")
if not KEY:
    raise SystemExit("SUME_API_KEY is empty; refusing to call")

headers = {
    "x-api-key": KEY,
    "content-type": "application/json",
    "accept": "application/json, text/event-stream",
}
body = {
    "jsonrpc": "2.0", "id": 1, "method": "initialize",
    "params": {
        "protocolVersion": "2025-11-25",
        "capabilities": {},
        "clientInfo": {"name": "bot-check", "version": "0.1"},
    },
}
r = httpx.post(URL, headers=headers, json=body, timeout=20)
print(r.status_code)

A 200 here proves the credential and the route; it does not prove a paid call will pass a portal policy. Run tools_list next and read the safety metadata before granting anything. The related service token polling post covers the REST side of the same Access setup.

Choosing between the three unattended paths

The three options differ in what the caller decides at run time. An MCP session with an API key lets the agent pick tools turn by turn, which is flexible and also means the agent decides when to spend. Agent Completions moves that decision to a request you author: the task is in the request, and generation_spend_cap_usd is the ceiling for that single run. A saved schedule moves it further, to a stored instruction that fires on cron or an API trigger.

If the work is the same every time, the schedule is the least moving parts, and Sume notes schedules are created in the dashboard and are not exposed over MCP or the CLI. If the task varies per call, Agent Completions returns a 202 receipt and you poll the status URL, or register a webhook instead of a loop. If the agent genuinely needs to explore tools, use the API-key MCP session and keep max_spend_usd on every paid call.

None of these needs a Cloudflare service token to reach Sume, which is the useful takeaway: the service token solves access to your internal servers, and Sume is reachable with its own credential.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume