Setting up the agent
The demo needs an AI client with two things wired up: a model reached through Cloudflare AI Gateway, and the four MCP servers reached through the MCP server portal. These are the two places the controls live.
- Model traffic goes to
, the AI Gateway's own custom domain, whichwire-protection.shputs behind Cloudflare Access. On a custom domain, AI Gateway accepts a valid Access JWT as the request credential, so the client sends no gateway token and no API key at all. - Tool traffic goes to the MCP portal at
, which fronts,,and.
Both are Access applications on the same identity provider, so one login covers the lot, and
every request — model and tool — is attributed to the person who made it. AI Gateway
records the authenticated user as cf.user_id, which means logs, analytics and spend
controls are per-user without the client passing an identity.
Sign in as the right person
Every demo is run as Delta Graham — delta.graham@company.com,
password Savetheinternet!1. When the agent connects to the portal you will be sent
through Cloudflare Access and then FlareID; log in as Delta, not as the admin account. The whole point
is that the agent is acting with a real, low-privilege identity.
Option A — opencode
Add a provider pointing at AI Gateway's OpenAI-compatible endpoint and an MCP entry pointing at the
portal. In ~/.config/opencode/opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"cf-ai-demo": {
"npm": "@ai-sdk/openai-compatible",
"name": "Cloudflare AI Gateway (demo)",
"options": {
"baseURL": "https://aig.<ZONE>/compat"
},
"models": {
"workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast": {
"name": "Llama 3.3 70B (Workers AI)"
}
}
}
},
"mcp": {
"ai-demo": {
"type": "local",
"command": ["npx", "-y", "mcp-remote@latest", "https://mcp.<ZONE>/mcp"],
"enabled": true
}
}
}
The machine is enrolled in the Cloudflare One client and already signed in,
and the Access application in front of is configured to accept that client
session (allow_authenticate_via_warp). So the device's existing session authorises
the request, and there is no credential in the config file, in an environment variable, or on
disk anywhere.
On a machine without the client, use cloudflared to fetch a short-lived Access
token instead — opencode can run it for you through the auth.command field of
a discovery file. Cloudflare documents that pattern under
AI Gateway → Integrations → coding agents.
cloudflare-ai-gateway is a real provider id in
models.dev, with a catalogue of the 47 third-party models AI
Gateway can proxy. Name your provider that and opencode matches it by id, merges that catalogue,
and ignores the models map you wrote: the picker fills up with Claude, GPT and Qwen
entries, and the app starts probing models you have no provider keys for. Any id that isn't in
models.dev - cf-ai-demo here - avoids it. Restart opencode after editing, since it
caches the catalogue.
Use @cf/meta/llama-3.3-70b-instruct-fp8-fast. Reasoning models on Workers AI -
@cf/google/gemma-4-26b-a4b-it, for instance - return 200 through the
compat endpoint with finish_reason: "length" and an empty content string,
because the budget goes on reasoning tokens the OpenAI-compatible shape doesn't surface. It looks
like a broken gateway and isn't.
mcp-remote handles the portal's OAuth flow and opens a browser window for the Access
login. If your opencode build supports remote MCP servers with OAuth directly, you can use
{ "type": "remote", "url": "https://mcp.<ZONE>/mcp" } instead.
Option B — Open WebUI
- Model. Settings → Connections → add an OpenAI-compatible connection. Open WebUI
runs server-side, so it has no Cloudflare One client session to borrow: point it at
https://gateway.ai.cloudflare.com/v1/<ACCOUNT_ID>/ai-demo/compatwith a Cloudflare API token that has Workers AI access, or give it an Access service token againsthttps://aig.<ZONE>/compat. Use the model idworkers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast. - Tools. Bridge the MCP portal to an OpenAPI tool server and add it under Settings → Tools:
uvx mcpo --port 8000 -- npx -y mcp-remote@latest https://mcp.<ZONE>/mcp
Recent Open WebUI builds can also add a streamable-HTTP MCP server directly; either path works, the portal does not care. - Access. Open WebUI itself sits behind its own Cloudflare Access application with SSO, so the person typing prompts is already authenticated before they reach it.
Running without the protection layer first
If the suite was deployed with DEPLOY_PROTECTION=false there is no portal and no AI
Gateway yet. Point the client at the four MCP servers individually and at the model provider directly:
{
"mcp": {
"workweek": { "type": "local", "command": ["npx", "-y", "mcp-remote@latest", "https://hr-mcp.<ZONE>/mcp"] },
"pipeline": { "type": "local", "command": ["npx", "-y", "mcp-remote@latest", "https://crm-mcp.<ZONE>/mcp"] },
"relay": { "type": "local", "command": ["npx", "-y", "mcp-remote@latest", "https://work-mcp.<ZONE>/mcp"] },
"nexus": { "type": "local", "command": ["npx", "-y", "mcp-remote@latest", "https://wiki-mcp.<ZONE>/mcp"] }
}
}
Each server runs its own OAuth 2.1 flow and delegates login to Cloudflare Access, so you will sign in as Delta once per server. Every demo script works in this mode — that is the "before" half of each one.
The portal namespaces every tool with its server id, so list_employees becomes
hr_list_employees, get_pipeline_summary becomes
crm_get_pipeline_summary, and so on with work_ and wiki_. The
demo scripts name the underlying tool; your transcript will show the prefixed one.
Check it works
Before running any script, ask the agent something harmless that proves both legs are live:
Who am I, and which tools do you have available?
You should see Delta Graham come back from the whoami tool, a list of tools from all four
servers, and — if the protection layer is deployed — a corresponding request in the AI
Gateway log and in the MCP portal log.