# Cara Membatasi Akses MCP Server: Read-Only dan Toolset

> Cara membatasi akses MCP server dari nol: kredensial tanpa hak tulis, mode read-only, toolset, allow list, deny list di client, dibuktikan via tools/list.

URL: https://www.matthewswong.com/id/learn/ai-agents-mcp/scope-an-mcp-server-read-only-toolsets/

Agen AI dan MCP · Pelajaran 2 dari 4 · Ditinjau pada 17 Oktober 2026 · Dicek dengan MCP specification 2026-07-28 · Oleh Matthews Wong (https://www.matthewswong.com/id/about/)

> Least privilege untuk MCP server berarti agen hanya bisa menjangkau apa yang dibutuhkan tugasnya. Beri server kredensial yang tidak bisa menulis, aktifkan mode read-only-nya, muat hanya toolset yang perlu, tolak tool berisiko di client, lalu buktikan dengan mendaftar tool yang benar-benar diterima client.

## Setelah pelajaran ini, Anda bisa

- Mengurutkan lapisan yang membatasi MCP server, dan menyebut lapisan yang tetap bertahan saat semua filter gagal.
- Memperkecil daftar tool sebuah server dengan mode read-only, toolset, allow list, dan deny list di client.
- Membuktikan hasilnya dengan membaca tools/list yang benar-benar diterima client.

## Tiga pemegang izin antara agen dan cluster

Permintaan delete melewati aturan client dan filter server, lalu bertemu kredensial yang menolaknya; audit tools/list membuktikan apa yang benar-benar ditawarkan server.

1. **Kenali lapisannya.** Agen menjangkau cluster lewat tiga pemegang izin: aturan client, filter milik MCP server, dan kredensial yang dipegang server. Masing-masing bisa menolak.
2. **Default-nya terlalu luas.** Dengan toolset core dan helm, Kubernetes MCP server v0.0.67 menawarkan 22 tool dalam audit situs ini. Delapan di antaranya bisa mengubah cluster.

   ```
   helm_uninstall · pods_delete · pods_exec · pods_run · …
   ```

   *Tebak:* Jika semua filter server salah konfigurasi, lapisan mana yang tetap menolak delete? (Mode read-only milik server / Kredensial yang dipegang server)
3. **Kredensial adalah temboknya.** Ikat identitas server ke role yang tidak bisa menulis, misalnya role bawaan view di Kubernetes. API server menolak verb apa pun yang tidak dimiliki identitas itu, apa pun permintaan agen.

   ```
   kubectl auth can-i delete pods --as=…:mcp-viewer → no
   ```
4. **Mode read-only.** read_only = true membuat server hanya menawarkan tool yang beranotasi readOnlyHint. Di audit, 22 tool menjadi 14. Anotasi adalah label tulisan seseorang, jadi ini tembok kedua.

   ```
   read_only = true
   ```
5. **Lebih sedikit tool, plus deny list.** Muat hanya toolset yang dibutuhkan tugasnya, dan tolak juga tool berisiko di client. Di Claude Code, aturan deny yang menyebut sebuah tool MCP menghapusnya dari konteks model.

   ```
   toolsets = core, helm · deny: mcp__kubernetes__helm_uninstall
   ```

   *Tebak:* File konfigurasi berisi readonly = true, kurang satu underscore. Apa yang dilakukan v0.0.67? (Menolak berjalan / Tetap berjalan dan menawarkan 20 tool)
6. **Buktikan dengan tools/list.** Di v0.0.67, salah ketik itu berjalan tanpa peringatan dan menawarkan 20 tool, termasuk pods_delete. Hanya daftar yang diterima client yang jadi bukti, jadi audit tools/list di CI.

   ```
   fail the job if any tool lacks readOnlyHint: true
   ```

## Apa arti least privilege untuk sebuah MCP server?

MCP server memegang kredensial dan menyediakan tool yang memakainya. Least privilege berarti keduanya hanya sebesar tugasnya: server yang menjelaskan mengapa sebuah pod terus crash perlu membaca pod, log, dan event, bukan menghapusnya atau membaca Secret. Semua yang melebihi tugas itu adalah blast radius, yaitu kerusakan yang bisa ditimbulkan satu call yang keliru atau dibajak.

Daftar tool adalah blast radius yang bisa Anda lihat. Artikel Kubernetes MCP di situs ini mengaudit containers/kubernetes-mcp-server v0.0.67 dengan toolset core dan helm: server itu menawarkan 22 tool, dan 8 di antaranya bisa mengubah cluster, termasuk helm_uninstall, pods_delete, dan pods_exec. Agen yang hanya diminta menjelaskan crash loop ditawari semuanya.

Least privilege punya dua sisi. Pertama, apa yang bisa dilakukan kredensial, yang ditegakkan oleh sistem yang diajak bicara server. Kedua, apa yang ditawarkan server, yang ditegakkan oleh server dan client Anda. Sisi pertama adalah batasnya; sisi kedua mencegah model bahkan melihat pilihan yang tidak boleh dipakainya.

## Lapisan mana yang bisa menolak, dan mana batas yang sesungguhnya?

Taruh batas kerasnya di kredensial. Beri server identitas sendiri dengan role read-only, misalnya ServiceAccount Kubernetes yang diikat ke role bawaan view dengan token berumur pendek, dan simpan kredensial tulis di tempat yang tidak bisa dijangkau agen. API server lalu menolak verb apa pun yang tidak dimiliki identitas itu, apa pun permintaan agen dan apa pun kata filter server.

Lalu perkecil apa yang ditawarkan server. Di Kubernetes MCP server, read_only = true hanya menampilkan tool beranotasi readOnlyHint=true; toolsets hanya memuat kelompok yang Anda sebut, dengan core dan config sebagai default; enabled_tools dan disabled_tools mengizinkan atau menolak tool berdasarkan nama; dan denied_resources memblokir jenis resource seperti Secret walaupun kredensialnya lebih luas dari yang dimaksud. Semua ini berjalan di dalam server, jadi ini tembok kedua, bukan yang pertama.

Anotasi hanyalah label, bukan hukum alam. Spesifikasi MCP menyatakan client wajib menganggap anotasi tool tidak tepercaya kecuali berasal dari server tepercaya, dan dalam audit itu helm_install serta pods_run adalah tool tulis yang tidak ditandai destructive. Itulah sebabnya disable_destructive menyisakan 16 tool, dua di antaranya bisa menjalankan image atau chart apa pun, sedangkan read_only menyisakan 14.

Terakhir, client juga bisa menolak. Di Claude Code, aturan izin menyebut tool MCP dengan format mcp__server__tool, aturan deny diperiksa sebelum ask dan allow, dan tool yang ditolak dihapus dari konteks model. Deny list di client adalah asuransi murah untuk hari ketika upgrade server menambah tool yang belum pernah ditinjau siapa pun.

## Bagaimana membuktikan server hanya menampilkan apa yang Anda maksud?

Tanyakan ke server-nya. Request tools/list mengembalikan persis apa yang akan ditawarkan client kepada model, jadi daftarkan dengan konfigurasi yang Anda deploy dan gagalkan pengecekan bila ada tool tanpa readOnlyHint=true. File konfigurasi hanyalah klaim; daftar tool adalah buktinya.

Audit situs ini menangkap kegagalan yang menjadi alasan pengecekan ini ada. File berisi readonly = true, kurang satu underscore, membuat v0.0.67 berjalan tanpa protes dan menawarkan 20 tool dengan toolset default, termasuk pods_delete dan pods_exec. Dokumentasi proyek saat ini menyatakan key yang tidak dikenal kini menggagalkan pemuatan, jadi salah ketik yang sama rusak ke arah berbeda tergantung versi yang Anda kunci.

Buktikan kredensialnya dengan cara yang sama. kubectl auth can-i menjawab apakah sebuah identitas boleh menjalankan suatu verb, jadi pastikan jawabannya yes untuk melihat daftar pod dan no untuk menghapus pod, membaca Secret, dan exec, sebelum agen pernah melihat token-nya. Saat insiden butuh akses tulis, berikan sebagai konfigurasi terpisah dengan batas waktu, lalu cabut lagi.

## Konfigurasi nyata di balik pelajaran ini

`app/components/blogs/KubernetesMcpServerReadOnlyToolsets.tsx:218-221`

```bash
# Prove the boundary BEFORE the agent ever sees it. These are the checks that matter:
kubectl auth can-i list pods   --as=system:serviceaccount:mcp:mcp-viewer --all-namespaces  # yes
kubectl auth can-i delete pods --as=system:serviceaccount:mcp:mcp-viewer --all-namespaces  # no
kubectl auth can-i get secrets --as=system:serviceaccount:mcp:mcp-viewer --all-namespaces  # no
```

Dari artikel Kubernetes MCP di situs ini: pertanyaan yang diajukan ke ServiceAccount agen sebelum ia memegang token, beserta jawaban yang seharusnya. Jika ada jawaban no yang ternyata yes, kredensialnya terlalu luas, dan tidak ada setelan server yang bisa menggantikannya.

`app/components/blogs/KubernetesMcpServerReadOnlyToolsets.tsx:233-245`

```toml
# Only tools annotated readOnlyHint=true are exposed. On v0.0.67 with core + helm
# this left 14 of 22 tools (measured with tools/list, see the verification section).
read_only = true

# Load only what the job needs. The default is ["core", "config"]; helm, kiali,
# kubevirt, tekton and the observability sets are opt-in.
toolsets = ["core", "helm"]

# Belt and braces: even a credential broader than "view" cannot read these kinds.
[[denied_resources]]
group = ""
version = "v1"
kind = "Secret"
```

Konfigurasi dasar dari artikel yang sama. read_only dan toolsets memperkecil apa yang ditawarkan ke agen; denied_resources menjaga Secret tetap tak terjangkau meski kelak ada yang mengikat role lebih luas ke identitas itu. Angka di komentar adalah hasil ukur tools/list artikel itu sendiri pada v0.0.67.

## Yang sering rusak

- **Menganggap disable_destructive sama dengan read-only.** Setelan ini hanya menyembunyikan tool bertanda destructiveHint. Di v0.0.67 ia menyisakan helm_install dan pods_run, yang bisa memasang chart apa pun atau menjalankan image apa pun yang diizinkan kredensial. Pakai read_only, atau nonaktifkan keduanya berdasarkan nama.
- **Salah ketik yang malah membuka akses.** readonly alih-alih read_only dimuat diam-diam di v0.0.67 dan membuka 20 tool. Versi yang lebih baru justru menolak key yang tidak dikenal. Apa pun versinya, audit tools/list setiap kali konfigurasi berubah dan setiap kali upgrade.
- **Satu kredensial untuk membaca dan menulis.** Jika server milik agen memegang token yang sama dengan yang Anda pakai untuk deploy, hanya filter yang berdiri di antara sebuah prompt dan perintah delete. Buat identitas read-only terpisah dengan token berumur pendek, dan jauhkan akses tulis dari jangkauan agen.
- **Aturan allow yang lebih luas dari tugasnya.** Aturan allow seperti mcp__kubernetes__* menyetujui di muka semua tool yang ditawarkan server itu, termasuk tool tulis yang ditambahkan versi berikutnya. Izinkan tool baca satu per satu berdasarkan nama, dan tolak tool tulis secara eksplisit.

### Lapisan yang membatasi MCP server, dan siapa yang menegakkannya

| Lapisan | Setelan | Ditegakkan oleh |
| --- | --- | --- |
| Kredensial | Role view dan token berumur pendek | Sistem yang diajak bicara server, di sini API server Kubernetes |
| Mode read-only | read_only | MCP server, berdasarkan anotasi tool |
| Toolset dan allow list | toolsets, enabled_tools, disabled_tools | MCP server |
| Deny list resource | denied_resources | MCP server |
| Deny list di client | Aturan izin seperti mcp__server__tool | MCP client, misalnya Claude Code |

## Coba sendiri

Anda butuh Node.js 22.19 atau lebih baru untuk MCP Inspector, dan Claude Code untuk dua langkah terakhir. Server rujukan filesystem berperan sebagai server yang memegang akses: allowed directories-nya berperan sebagai kredensial, dan server menolak path di luarnya.

1. Buat folder latihan yang tidak berisi apa pun yang penting. Hanya folder ini yang boleh disentuh server.

   ```bash
   mkdir -p ~/mcp-practice && echo hello > ~/mcp-practice/note.txt
   ```
2. Daftarkan tool milik server dan kelompokkan berdasarkan anotasi readOnlyHint. Catat tool tulisnya: write_file, edit_file, move_file, dan create_directory.

   ```bash
   npx -y @modelcontextprotocol/inspector --cli npx @modelcontextprotocol/server-filesystem ~/mcp-practice --method tools/list
   ```
3. Minta sebuah file di luar folder yang diizinkan. Server seharusnya menolak dengan error, karena /etc berada di luar allowed directories-nya.

   ```bash
   npx -y @modelcontextprotocol/inspector --cli npx @modelcontextprotocol/server-filesystem ~/mcp-practice --method tools/call --tool-name read_text_file --tool-arg path=/etc/hosts
   ```
4. Daftarkan server itu di Claude Code untuk proyek ini, lalu tolak empat tool tulisnya di .claude/settings.json.

   ```bash
   claude mcp add fs -- npx -y @modelcontextprotocol/server-filesystem ~/mcp-practice
   
   {
     "permissions": {
       "deny": [
         "mcp__fs__write_file",
         "mcp__fs__edit_file",
         "mcp__fs__move_file",
         "mcp__fs__create_directory"
       ]
     }
   }
   ```
5. Jalankan Claude Code dan tanyakan tool fs apa saja yang bisa dipanggilnya. Empat tool yang ditolak seharusnya tidak muncul, karena aturan deny menghapus tool dari konteks model.

   ```bash
   claude "List every tool you can call from the fs MCP server."
   ```

## Kuis singkat

1. **Filter MCP server Anda sempurna, tetapi token yang dipegangnya bisa menghapus pod. Apa yang sebenarnya membatasi agen yang dibajak?**
   A. Setelan read_only milik server
   B. Apa yang boleh dilakukan token itu
   C. Instruksi untuk model
   Jawaban: B. Filter berjalan di dalam server dan bergantung pada konfigurasi serta label; API server menegakkan kredensial apa pun yang terjadi. Least privilege dimulai dari token yang tidak bisa menulis.

2. **Apa yang sebenarnya disembunyikan disable_destructive?**
   A. Semua tool yang bisa menulis
   B. Hanya tool beranotasi destructiveHint=true
   C. Semua tool di luar toolset core
   Jawaban: B. Setelan ini berpatokan pada anotasi destructive. helm_install dan pods_run bisa menulis tetapi tidak ditandai destructive, jadi di v0.0.67 ia menyisakan 16 tool, sedangkan read_only menyisakan 14.

3. **Bagaimana Anda tahu read_only benar-benar berlaku?**
   A. Server berjalan tanpa error
   B. File konfigurasi berisi read_only = true
   C. tools/list hanya mengembalikan tool read-only
   Jawaban: C. Di v0.0.67, key yang salah eja berjalan diam-diam dan membuka 20 tool. Hanya daftar tool yang diterima client yang menjadi bukti apa yang ditawarkan ke model.

## Pikirkan dulu

- **Mengapa memakai kredensial tulis terpisah, bukan satu token ditambah setelan read-only di server?** Setelan di dalam server hanya sebenar konfigurasi dan label tool-nya. Kredensial yang tidak bisa menulis ditegakkan oleh sistemnya sendiri, jadi salah ketik, upgrade, atau tool yang salah label tidak bisa berubah menjadi delete.
- **Upgrade server menambah tool tulis baru. Lapisan mana yang menangkapnya, dan mana yang meloloskannya?** Kredensial read-only tetap menolak apa yang dicoba tool itu, dan audit tools/list di CI menandai nama barunya. Aturan allow yang luas seperti mcp__server__* menyetujuinya diam-diam, itulah sebabnya allow per nama dan deny yang eksplisit lebih awet.
- **Mengapa mengatur denied_resources untuk Secret padahal role view sudah tidak bisa membaca Secret?** Ia tetap aman saat kredensial bergeser. Jika kelak ada yang mengikat role yang lebih luas ke identitas agen, server tetap menolak jenis resource tersebut.

## Pelajari lebih dalam

- [Kubernetes MCP Server: read_only, Toolsets, Denied Resources](https://www.matthewswong.com/id/blog/kubernetes-mcp-server-go-read-only-toolsets/)
- [Checklist Keamanan MCP Server untuk Developer](https://www.matthewswong.com/id/blog/mcp-server-security-checklist/)
- [Least Privilege AI Agent: Credential, Token, dan Secret](https://www.matthewswong.com/id/blog/ai-agent-least-privilege-credentials/)
- [MCP Write Controls: Cloudflare WriteGuard vs Approval Prompt](https://www.matthewswong.com/id/blog/cloudflare-writeguard-mcp-write-controls/)

## Sumber

- [Dokumentasi Kubernetes MCP Server: konfigurasi (read_only, toolsets, denied_resources)](https://github.com/containers/kubernetes-mcp-server/blob/main/docs/configuration.md)
- [Dokumentasi Kubernetes: otorisasi RBAC dan role view](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)
- [Spesifikasi MCP 2026-07-28: tool dan anotasi yang tidak tepercaya](https://modelcontextprotocol.io/specification/2026-07-28/server/tools)
- [Spesifikasi MCP 2026-07-28: praktik keamanan terbaik (scope minimization)](https://modelcontextprotocol.io/specification/2026-07-28/basic/security_best_practices)
- [Dokumentasi Claude Code: aturan izin untuk tool MCP](https://code.claude.com/docs/en/permissions)

---

- HTML version: https://www.matthewswong.com/id/learn/ai-agents-mcp/scope-an-mcp-server-read-only-toolsets/
- English: https://www.matthewswong.com/en/learn/ai-agents-mcp/scope-an-mcp-server-read-only-toolsets/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
