Skip to main content
BlogSecurity

Enterprise MCP Reference Architecture dengan Cloudflare Portals

Stack enterprise MCP berlapis di Cloudflare: Access OAuth, satu portal per audiens, Code Mode, Gateway DLP, dan batasan yang tercantum di dokumentasi.

Matthews Wong

Software Engineer

Oktober 202612 menit baca
Enterprise MCP Reference Architecture dengan Cloudflare Portals

Ringkasan Utama

Enterprise MCP reference architecture di Cloudflare punya lima lapisan: remote server dengan Access sebagai OAuth provider, portal untuk discovery dan policy tool per grup, Code Mode untuk mengecilkan definisi tool, Gateway DLP pada traffic portal, dan WAF untuk server publik. Write default-deny ada di lapisan server.

Angka yang membuat saya membaca post enterprise MCP Cloudflare dua kali bukan soal keamanan. Angkanya 52 tool yang memakan sekitar 9.400 token konteks sebelum karyawan mengetik satu kata. Empat MCP server internal, satu portal, dan model sudah membawa buku panduan kecil yang tidak ia perlukan.

Ini adalah pembacaan dekat atas post tersebut (terbit 14 April 2026, diubah 15 Juli 2026) dibandingkan dengan dokumentasi Cloudflare terbaru untuk MCP server portals, tutorial Gateway untuk mendeteksi traffic MCP, dan halaman AI Security for Apps. Saya belum men-deploy stack ini sendiri. Semua angka berasal dari Cloudflare, semua batasan dikutip dari dokumentasi, dan jika dua sumber berbeda saya sebutkan.

Kenapa Cloudflare melarang MCP server lokal lebih dulu

Arsitektur ini dimulai dari sebuah penolakan. Cloudflare menilai MCP server lokal sebagai liabilitas keamanan sejak awal dengan tiga alasan: sumber dan versi software yang tidak diperiksa (masalah supply chain), tool injection atau tool poisoning, dan tidak ada administrasi IT atau keamanan sama sekali karena setiap karyawan memutuskan sendiri. Solusinya adalah menjadikan remote server satu-satunya jalur yang mudah.

Tim pusat menjalankan platform MCP bersama di dalam monorepo. Karyawan yang ingin mengekspos resource internal meminta persetujuan tim AI governance, menyalin template, menulis definisi tool lalu deploy. Template membawa write control default-deny, audit logging, pipeline CI/CD yang dibuat otomatis, dan secrets management. Menurut post-nya ini hanya butuh beberapa menit scaffolding, dan governance yang tertanam di platform itulah yang membuat adopsi menyebar cepat. Saya membacanya sebagai prinsip desain yang sebenarnya: jalur yang aman harus menjadi jalur termurah, kalau tidak orang akan menghindarinya.

Lima lapisan dan tanggung jawab masing-masing

Post ini lebih mudah dipakai kalau lapisannya dipisah, karena tiap lapisan menjawab pertanyaan berbeda dan tiap lapisan punya celah yang ditutup lapisan berikutnya. Tabel ini ringkasan saya sendiri dari post dan dokumentasi, bukan diagram Cloudflare.

LapisanProduk CloudflarePertanyaan yang dijawabYang tidak bisa dilakukan sendirian
ServerWorkers dengan template monorepoApa yang bisa dilakukan tool ini, dan apakah write boleh?Tidak ada di luar tool-nya sendiri; direct URL bisa melewati policy portal
IdentitasAccess sebagai OAuth providerSiapa ini, di device apa, dari mana?Tidak menentukan tool server mana yang dilihat seseorang
Discovery dan policyMCP server portalServer dan tool apa yang didapat grup ini?Tidak memeriksa payload kecuali Gateway routing aktif
DataGateway HTTP policy dengan DLPApakah data sensitif keluar atau kembali?Hanya memindai 1.024 byte pertama dari body POST
Server publikWAF dengan AI Security for AppsApakah traffic masuk berupa prompt injection atau kebocoran PII?Detection field adalah add-on berbayar; hanya request JSON

Baca kolom terakhir sebagai alasan perlunya kelimanya. Access saja tidak melihat intent di level tool, portal saja tidak melihat body request, dan Gateway saja tidak tahu siapa yang boleh memanggil tool write. Post juga menempatkan AI Gateway antara MCP client dan model untuk pergantian provider dan batas token per karyawan, tetapi tanpa konfigurasi, jadi saya anggap itu catatan desain, bukan sesuatu untuk disalin.

Lapisan satu: Access sebagai OAuth provider untuk server Anda sendiri

Server publik seperti MCP dokumentasi Cloudflare dan MCP Radar terbuka untuk siapa saja. Yang privat tidak. Template monorepo menghubungkan Cloudflare Access sebagai OAuth provider, sehingga Access menjalankan login flow, menerbitkan token, dan menerapkan SSO, MFA serta pemeriksaan kontekstual seperti alamat IP, lokasi, dan device certificate. Penulis server cukup menulis tool dan tidak menyentuh sistem identitas.

Mendaftarkan server ke portal adalah langkah terpisah. Di dashboard lokasinya Zero Trust, Access controls, MCP Portals, tab MCP servers. Lewat API, field auth_type menerima oauth, bearer, atau unauthenticated. Untuk server yang tidak mendukung Dynamic Client Registration, Anda memberikan client yang sudah terdaftar dan mengirim client secret sebagai field tersendiri; Cloudflare menyimpannya terenkripsi dan tidak pernah mengembalikannya.

# Register an upstream server that cannot do Dynamic Client Registration.
# The client secret is a SIBLING field, never inside auth_credentials.
# Cloudflare encrypts it and never returns it from read, create or update calls.
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/ai-controls/mcp/servers" \
  --request POST \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --json '{
    "id": "github",
    "name": "GitHub MCP Server",
    "hostname": "https://github-mcp.example.com/mcp",
    "auth_type": "oauth",
    "is_shared_oauth_callback_enabled": true,
    "auth_credentials": "{\"auth_mode\":\"manual\",\"config\":{\"authorization_endpoint\":\"https://github.com/login/oauth/authorize\",\"token_endpoint\":\"https://github.com/login/oauth/access_token\"},\"registration_info\":{\"client_id\":\"<client-id>\",\"token_endpoint_auth_method\":\"client_secret_basic\",\"scope\":\"repo read:user\"}}",
    "client_secret": "<client-secret>"
  }'

# The server id becomes the tool prefix: list_issues is exposed as github_list_issues.
# Server ids cannot contain underscores (the portal splits on the first one). Use hyphens.

Peringatan dari Cloudflare sendiri: user yang diblokir policy Access di portal tetap bisa mencapai server lewat direct URL. Menyembunyikan server di portal bukan penegakan. Dokumentasi menyarankan mengonfigurasi Access sebagai OAuth provider server jika Anda ingin autentikasi ditegakkan di server itu sendiri.

Lapisan dua: satu portal per audiens, tool di-allow bukan disembunyikan

Portal menempatkan banyak MCP server di balik satu endpoint HTTP pada path /mcp di domain Anda sendiri. User login sekali lewat Access lalu mengautentikasi terpisah ke tiap upstream yang butuh OAuth. Contoh dalam post adalah pernyataan model yang paling jelas: portal finance yang dibatasi untuk grup finance dan hanya mengekspos tool read-only dari server code repository, serta portal engineering yang dibatasi untuk engineer dengan laptop korporat dan mengekspos tool read dan write dari server yang sama. Satu server, dua portal, dua daftar tool.

Detail yang membuatnya aman adalah allowlist. Secara default semua tool dan prompt dari server yang ditambahkan terlihat. Mengatur default_disabled menjadi true pada mapping server ke portal membalik itu: hanya tool yang Anda daftarkan di updated_tools yang diekspos. Di sinilah write default-deny menjadi properti portal sekaligus server, dan di sini pula jebakan pertama. Update portal yang menyertakan servers mengganti seluruh mapping, jadi menghilangkan sebuah server berarti menghapusnya dan menghilangkan sebuah field berarti mengembalikannya ke default.

# Finance portal: PUT replaces the WHOLE servers array, so list every server
# you want to keep and repeat its existing mapping fields. Omit one and it is removed.
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/access/ai-controls/mcp/portals/$PORTAL_ID" \
  --request PUT \
  --header "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
  --json '{
    "servers": [
      {
        "server_id": "github",
        "default_disabled": true,
        "updated_tools": [
          { "name": "search_documents", "enabled": true },
          { "name": "list_projects",    "enabled": true }
        ]
      }
    ]
  }'
# Every other tool and every prompt of this server is now hidden from this portal.
# Terraform creates the portal but NOT its DNS record. Without the proxied CNAME
# the portal hostname answers 522.
resource "cloudflare_zero_trust_access_mcp_server_portal" "engineering" {
  account_id = var.cloudflare_account_id
  name       = "Engineering Portal"
  hostname   = "mcp.example.com"
}

resource "cloudflare_dns_record" "mcp_portal" {
  zone_id = var.cloudflare_zone_id
  name    = "mcp"
  content = "gateway.agents.cloudflare.com"
  type    = "CNAME"
  proxied = true
}

Tool diberi namespace berupa server ID, underscore, lalu nama tool, jadi github plus list_issues menjadi github_list_issues. Portal memecah pada underscore pertama, artinya server ID tidak boleh berisi underscore; pakai hyphen. Jika Anda membuat portal lewat API atau Terraform, dokumentasi menegaskan tidak ada DNS record yang dibuat otomatis: tambahkan CNAME dengan proxy ke gateway.agents.cloudflare.com atau portal menjawab 522.

Lapisan tiga: Code Mode dan arti sebenarnya dari 94 persen

Portal internal Cloudflare menaungi empat MCP server. Sebagai tool biasa itu 52 definisi dengan sekitar 9.400 token. Dengan Code Mode, model melihat dua tool portal, portal_codemode_search dan portal_codemode_execute, dengan sekitar 600 token. Post menyebutnya pengurangan 94 persen; 600 dibagi 9.400 adalah 6,4 persen, jadi penghematannya lebih dekat ke 93,6 dan pembulatannya wajar. Kalimat yang lebih penting adalah biayanya tetap saat server ditambah. Post Cloudflare terpisah tentang API server mereka melaporkan sekitar 1.000 token untuk dua tool dibanding perkiraan 1,17 juta tanpa Code Mode.

Code Mode adalah policy pada setiap portal, bukan kebiasaan client. Defaultnya opt-in. Portal juga bisa dipaksa aktif, yang penting jika Anda ingin penghematan token menjadi jaminan platform, bukan sesuatu yang harus diingat setiap engineer.

Policy (nilai API)Perilaku defaultOverride dari client
Off (off)Code Mode tidak tersediaQuery parameter diabaikan
Opt-in (opt_in)Code Mode matiTambahkan codemode=search_and_execute untuk menyalakan
On by default (default_on)Code Mode nyalaTambahkan codemode=off untuk mematikan
Enforced (enforced)Code Mode nyalaQuery parameter diabaikan
// Opt-in portal: the client asks for Code Mode in the URL.
// The portal then advertises portal_codemode_search and portal_codemode_execute
// instead of every upstream tool definition.
{
  "mcpServers": {
    "example-portal": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote@latest",
        "https://mcp.example.com/mcp?codemode=search_and_execute"
      ]
    }
  }
}

// Gentler alternative that keeps tools visible by name only:
//   https://mcp.example.com/mcp?optimize_context=minimize_tools
// Use command and args as above. The docs advise against the serverURL parameter.

Ada opsi kedua yang lebih ringan. Parameter optimize_context menawarkan minimize_tools, yang membuang deskripsi dan schema sehingga hanya nama yang tersisa, dengan klaim dokumentasi hingga 5x penghematan, dan search_and_execute, yang menyembunyikan tool upstream sepenuhnya. Dokumentasi memperingatkan agent lalu sepenuhnya bergantung pada tool query untuk menemukan apa pun. Eksekusi berjalan di Dynamic Worker yang terisolasi, sehingga credential dan environment variable tetap di luar konteks model.

Pilih enforced hanya setelah memeriksa client Anda. Dokumentasi menyebut portal tidak mendukung upstream server yang menjalankan Code Mode sendiri, dan menyarankan opt-in atau on by default jika sebagian client membawa implementasinya sendiri, supaya tidak terjadi eksekusi kode bersarang.

Lapisan empat: Gateway DLP pada traffic portal, dan apa yang bisa dibacanya

Access mencatat tiap tool call lewat portal, tetapi tidak bisa membaca payload. Untuk DLP Anda menyalakan Route traffic through Cloudflare Gateway, untuk seluruh portal atau satu server. Dokumentasi menyebut traffic portal didekripsi otomatis, jadi Anda tidak perlu pengaturan TLS decryption tingkat akun, tetapi policy HTTP Do Not Inspect yang cocok dengan MCP server Anda tetap mengecualikannya dan DLP lalu tidak melihat apa-apa.

# Gateway HTTP policy for portal traffic (Cloudflare's documented example).
# Target the UPSTREAM hostname, not the portal hostname.
Selector      Operator  Value                                          Logic  Action
Host          in        example-mcp-server.example.workers.dev        And    Block
DLP Profile   in        Credentials and Secrets, Financial Information

# DLP regex for MCP JSON-RPC from the detection tutorial (Rust regex syntax).
# Scans only the first 1,024 bytes of a POST body.
"method"\s{0,5}:\s{0,5}"tools/call"

Policy harus menarget hostname upstream server, bukan hostname portal. Contoh Cloudflare memblokir ketika Host adalah hostname Workers dan DLP profile-nya Credentials and Secrets atau Financial Information. Blokir bekerja dua arah: request yang membawa data cocok ditolak, dan response yang membawa data cocok diganti dengan error. Itulah mekanisme di balik contoh dalam post tentang aturan DLP yang menjauhkan PII dari server tertentu.

Dua batasan penting. Gateway routing hanya mendukung Streamable HTTP, jadi upstream yang hanya SSE gagal saat routing aktif. Dan DLP AI prompt profile tidak berlaku untuk traffic portal karena menarget path API web client tertentu; pakai profile standar. Untuk traffic yang tidak lewat portal, tutorial Gateway mendeteksi MCP lewat hostname yang memuat mcp, path /mcp dan /sse, serta regex DLP atas method JSON-RPC seperti tools/call. Tutorial juga menyatakan DLP hanya memindai 1.024 byte pertama body POST, dan post sendiri mencatat bahwa response dan notification mungkin tidak membawa field method, jadi anggap ini heuristik.

Di mana desain ini lebih lemah daripada yang digambarkan diagram

Dokumentasi portal mencantumkan batasan yang tidak ditekankan blog post. Inilah yang akan saya masukkan ke risk register sebelum rollout.

  • Independent MFA, purpose justification, dan temporary authentication tidak ditegakkan ketika server diotorisasi lewat portal. Policy yang menuntut step-up MFA pada sebuah server tidak akan muncul setelah login portal. Selector seperti email, grup, negara, dan device posture tetap ditegakkan.
  • Token OAuth admin bisa kedaluwarsa diam-diam. Server lalu menampilkan Error atau Sync Required, hilang dari portal bagi end user, dan dokumentasi menyebut admin tidak diberi notifikasi. Seseorang harus memantau status server.
  • Alias tool dihapus pada sync berikutnya jika upstream server mengganti nama tool, dan background sync berjalan sekitar tiap dua jam untuk server yang memakai Dynamic Client Registration.
  • Hanya remote HTTP server yang bekerja. Server stdio-only harus di-host sendiri di balik endpoint HTTP dengan bearer token atau custom header, sebagian server menolak client berbasis proxy dengan 403, dan satu portal menampung hingga 80 server.
  • Deteksi untuk server publik tidak gratis: AI Security for Apps memindai endpoint berlabel cf-llm, hanya menangani application/json, dan detection field serta ruleset Log Mode adalah add-on berbayar di Enterprise; endpoint discovery tersedia di semua plan.

Semua ini tidak merusak arsitekturnya. Artinya portal adalah lapisan akses dan routing, bukan pengganti otorisasi di dalam tiap server. Write control default-deny tetap harus ada di template server, karena itu satu-satunya lapisan yang tahu apa yang sebenarnya dilakukan sebuah tool.

Yang dikerjakan hari Senin

Jika Anda sudah menjalankan MCP server, Anda tidak perlu membangun ulang apa pun untuk mendapat sebagian besar manfaat ini. Urutan di bawah menaruh visibilitas termurah di depan.

  1. Ukur apa yang Anda punya: pindai log Gateway HTTP untuk traffic MCP dengan query di bawah, dan daftar setiap tujuan yang di-allow, bukan diblokir.
  2. Taruh server yang ingin dipertahankan di balik satu portal per audiens, dengan default_disabled true dan daftar eksplisit tool read untuk grup yang hanya membaca.
  3. Jadikan Access sebagai OAuth provider di setiap server yang Anda kontrol, sehingga direct URL tidak lagi menjadi jalan pintas.
  4. Nyalakan Code Mode sebagai opt-in, bandingkan token definisi tool sebelum dan sesudah di server Anda sendiri, baru pertimbangkan enforced.
  5. Arahkan portal lewat Gateway, tambahkan policy Host plus DLP ke tiap hostname upstream, dan pastikan tidak ada rule Do Not Inspect yang mencakup host tersebut.

Scan-nya adalah query dari tutorial Cloudflare yang diringkas. Butuh API token dengan izin Zero Trust read, dan dataset mencakup hingga 30 hari.

// Step 1: which MCP-looking destinations are allowed, and for whom?
// Dataset: gatewayHttpRequestsAdaptiveGroups, up to 30 days of history.
const query = [
  "query MCPTrafficScan($accountTag: string, $since: string, $until: string) {",
  "  viewer { accounts(filter: { accountTag: $accountTag }) {",
  "    gatewayHttpRequestsAdaptiveGroups(",
  "      filter: { datetime_geq: $since, datetime_leq: $until,",
  "        OR: [ { httpHost_like: \"%mcp%\" },",
  "              { httpRequestURI_like: \"%/mcp%\" },",
  "              { httpRequestURI_like: \"%/sse%\" } ] }",
  "      limit: 10000",
  "    ) { dimensions { httpHost action users } count }",
  "  } }",
  "}",
].join("\n");

const res = await fetch("https://api.cloudflare.com/client/v4/graphql", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.CLOUDFLARE_API_TOKEN,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    query,
    variables: { accountTag: process.env.ACCOUNT_ID, since: "2026-09-17T00:00:00Z", until: "2026-10-17T00:00:00Z" },
  }),
});
const groups = (await res.json()).data?.viewer?.accounts?.[0]?.gatewayHttpRequestsAdaptiveGroups ?? [];

// action "allow" means an active MCP connection nobody has blocked or governed.
const open = groups.filter((g) => g.dimensions.action === "allow");
console.table(open.map((g) => ({ host: g.dimensions.httpHost, count: g.count, users: g.dimensions.users })));

Aturan yang saya bawa dari sini: tanam governance di platform yang membuat server murah dibangun, lalu tambah lapisan yang masing-masing menutup apa yang tidak terlihat lapisan di bawahnya. Angka 94 persen token memang judulnya, tetapi ide yang awet adalah write default-deny di server, satu portal per audiens, dan DLP yang diarahkan ke host upstream. Verifikasi batasannya di plan Anda sendiri sebelum menjanjikan apa pun.

FAQ

Pertanyaan Umum

Jawaban singkat untuk pertanyaan yang paling sering diajukan pembaca tentang topik ini.

Apa itu MCP server portal?

MCP server portal menempatkan beberapa MCP server di balik satu endpoint HTTP pada path /mcp di domain Anda sendiri. User login sekali lewat Cloudflare Access, dan admin memilih server serta tool apa yang diekspos tiap portal. Portal juga memusatkan logging dan bisa mengarahkan traffic lewat Gateway untuk DLP.

Seberapa besar Code Mode mengurangi token tool MCP?

Di portal internal Cloudflare sendiri, empat MCP server mengekspos 52 tool dengan sekitar 9.400 token. Dengan Code Mode, model melihat dua tool portal dengan sekitar 600 token, yang dilaporkan Cloudflare sebagai pengurangan 94 persen. Itu angka Cloudflare untuk server mereka sendiri, jadi ukur server Anda sebelum mengandalkannya.

Bisakah tool MCP dibuat read-only untuk satu tim dan bisa write untuk tim lain?

Bisa, dengan satu portal per audiens. Contoh Cloudflare memberi grup finance tool read-only dari server repository dan memberi engineer dengan laptop korporat tool read dan write dari server yang sama. Mengatur default_disabled ke true membuat portal hanya mengekspos tool yang Anda daftarkan secara eksplisit.

Apakah portal mencegah user mengakses MCP server secara langsung?

Tidak. Dokumentasi Cloudflare memperingatkan bahwa user yang diblokir di portal tetap bisa terhubung lewat direct URL server. Untuk menegakkan autentikasi di server, konfigurasikan Cloudflare Access sebagai OAuth provider server tersebut.

Bisakah Cloudflare Gateway memblokir data sensitif yang dikirim ke MCP server?

Bisa, saat Route traffic through Cloudflare Gateway aktif untuk portal atau server. Anda membuat HTTP policy yang cocok dengan hostname upstream server dan sebuah DLP profile, dan blokir berlaku untuk request maupun response. DLP hanya memindai 1.024 byte pertama body POST, jadi anggap ini guardrail, bukan jaminan.