The R2 MCP Worker runs as a Cloudflare Worker and exposes your R2 object storage to AI clients over the Model Context Protocol. Once deployed, any MCP-compatible client can connect to yourDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/XxYouDeaDPunKxX/cloudflare-r2-remote-mcp-worker/llms.txt
Use this file to discover all available pages before exploring further.
/mcp endpoint and interact with R2 through a structured set of tools — all protected by GitHub OAuth when running in production.
Deployment model
The deployed Worker exposes these routes:| Resource | Purpose |
|---|---|
| R2 bucket (production) | Object storage for deployed Workers |
| R2 bucket (preview) | Object storage for local and preview runs |
KV namespace (OAUTH_KV) | Short-lived OAuth state for callback validation |
The preview bucket is used exclusively by
npm run dev and Wrangler preview deployments. The production bucket is used by all deployed Workers. Keeping them separate prevents local experiments from touching production data.Full deployment walkthrough
Install project dependencies, then authenticate your local Wrangler CLI against your Cloudflare account:
Wrangler opens a browser window for OAuth. After completing the flow your credentials are cached locally.
Dashboard path (alternative): Navigate to Cloudflare Dashboard → R2 Object Storage → Create bucket and create both buckets from the UI. Choose the same region for both.
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "cloudflare-r2-remote-mcp-worker",
"main": "src/index.ts",
"compatibility_date": "2026-05-01",
"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
"r2_buckets": [
{
"binding": "R2_BUCKET",
"bucket_name": "example-bucket",
"preview_bucket_name": "example-bucket-preview"
}
],
"kv_namespaces": [
{
"binding": "OAUTH_KV",
"id": "00000000000000000000000000000000"
}
],
"vars": {
"AUTH_MODE": "github",
"ALLOWED_GITHUB_LOGINS": "your-github-login",
"R2_BUCKET_NAME": "example-bucket",
"R2_ROOT_PREFIX": "",
"MAX_INLINE_TEXT_BYTES": "262144",
"MAX_TRANSFER_BYTES": "1048576",
"MAX_LIST_LIMIT": "100",
"ENABLE_ACCOUNT_TOOLS": "false",
"ENABLE_PRESIGN_TOOLS": "false"
},
"observability": {
"enabled": true
},
"dev": {
"port": 8787
}
}
name — Worker name, used to determine the workers.dev subdomainr2_buckets[0].bucket_name — production bucket name from step 2r2_buckets[0].preview_bucket_name — preview bucket name from step 2vars.R2_BUCKET_NAME — must match r2_buckets[0].bucket_namevars.R2_ROOT_PREFIX — optional prefix to scope all object keys (leave empty for full bucket access)vars.AUTH_MODE — "github" for protected deployments, "none" for local dev onlyvars.ALLOWED_GITHUB_LOGINS — comma-separated list of GitHub usernames allowed to log inENABLE_ACCOUNT_TOOLS, ENABLE_PRESIGN_TOOLSwrangler.jsonc is listed in .gitignore and must never be committed. It contains real bucket names and namespace IDs. Similarly, never commit .dev.vars — use wrangler secret put for secrets in production.The Worker accesses R2 exclusively through the
R2_BUCKET binding. Set both bucket names in wrangler.jsonc:"r2_buckets": [
{
"binding": "R2_BUCKET",
"bucket_name": "your-bucket-name",
"preview_bucket_name": "your-preview-bucket-name"
}
]
This namespace is only used during the GitHub OAuth callback exchange. It holds transient state values that expire automatically.
Public deployments must use
AUTH_MODE=github. You need a GitHub OAuth App whose callback URL matches your Worker URL.name in wrangler.jsonc.name in wrangler.jsonc.See GitHub OAuth setup for detailed instructions on creating the OAuth App.
Production secrets must be stored with
wrangler secret put, not in wrangler.jsonc. Required for AUTH_MODE=github:npx wrangler secret put GITHUB_CLIENT_ID -c wrangler.jsonc
npx wrangler secret put GITHUB_CLIENT_SECRET -c wrangler.jsonc
npx wrangler secret put COOKIE_ENCRYPTION_KEY -c wrangler.jsonc
For
COOKIE_ENCRYPTION_KEY, generate a random high-entropy string of at least 32 characters. On macOS/Linux:Wrangler will prompt you to paste each secret value. The values are encrypted and stored in Cloudflare’s secrets store — they are never visible after submission.
Account tools expose read-only Cloudflare R2 admin operations (bucket listing, usage stats). Enable them in
wrangler.jsonc:"vars": {
"ENABLE_ACCOUNT_TOOLS": "true",
"CLOUDFLARE_ACCOUNT_ID": "your-account-id",
"R2_BUCKET_NAME": "your-bucket-name"
}
Use the narrowest API token scope that can read the R2 resources required by your deployment. An R2 read-only token is sufficient for account tools.
Presign tools generate time-limited pre-signed URLs for direct S3-compatible access to R2 objects. Enable them in
wrangler.jsonc:"vars": {
"ENABLE_PRESIGN_TOOLS": "true",
"R2_BUCKET_NAME": "your-bucket-name",
"R2_S3_REGION": "auto"
}
npx wrangler secret put R2_ACCESS_KEY_ID -c wrangler.jsonc
npx wrangler secret put R2_SECRET_ACCESS_KEY -c wrangler.jsonc
ENABLE_PRESIGN_TOOLS=true
R2_BUCKET_NAME=...
R2_ACCESS_KEY_ID=...
R2_SECRET_ACCESS_KEY=...
R2_S3_REGION=auto
If
R2_S3_ENDPOINT is omitted, the Worker derives the endpoint automatically from CLOUDFLARE_ACCOUNT_ID:{
"ok": true,
"bucketAccessible": true,
"authMode": "none",
"accountToolsEnabled": false,
"presignToolsEnabled": false,
"rootPrefix": "",
"maxInlineTextBytes": 262144,
"maxListLimit": 100,
"maxTransferBytes": 1048576
}
If
bucketAccessible is false, check that the preview bucket name in wrangler.jsonc matches the bucket you created in step 2.Confirm the deployment by hitting
/healthz on the live URL, then proceed to Verify Your Deployment for full end-to-end checks.Next steps
- Configuration reference — complete environment variable and binding reference
- Verify your deployment — health checks, MCP Inspector walkthrough, and SDK smoke tests
- GitHub OAuth setup — creating and configuring the GitHub OAuth App