Model Context Protocol di Server Kecil — Apa yang Distandardisasi Spesifikasi, dan Apa yang Ditinggalkan Kepadamu
Ada satu istilah yang muncul di hampir semua pembicaraan tentang menghubungkan asisten AI ke perangkat lunak yang sungguhan: MCP. Model Context Protocol disebut sebagai cara standar untuk memberi model akses ke tools dan data, dan itu memang berguna. Tapi frasa "ini standar" menyembunyikan pertanyaan yang harus dijawab server kecil: cukup standar sampai dua program yang ditulis orang berbeda bisa saling mengerti, atau cukup standar sampai aku bisa berhenti memikirkan keamanan? Untuk protokol yang versinya lebih muda dari dua bulan sebelum artikel ini ditulis, jawabannya layak dibedah pelan-pelan.
Aku ingin memisahkan tiga hal di sini: apa yang benar-benar tertulis di spesifikasi, apa akibatnya bagi server yang kupunyai sendiri, dan apa yang belum bisa kupastikan. Semua hal yang bersifat versi di bawah dikunci pada revisi 2026-07-28, revisi terbaru yang tersedia hari ini.
Apa yang benar-benar distandardisasi protokol
Spesifikasi menyebut MCP sebagai protokol terbuka untuk menghubungkan aplikasi LLM ke sumber data dan tool eksternal, yang dikirim melalui pesan JSON-RPC 2.0 antara tiga peran: host, yaitu aplikasi AI yang memulai koneksi; client, yaitu konektor di dalam host; dan server, yaitu layanan yang menyediakan konteks dan kemampuan. Spec ini menyebutkan bahwa ide dasarnya terinspirasi dari Language Server Protocol, yang menyelesaikan masalah serupa — setiap editor harus mengintegrasikan setiap bahasa satu per satu — untuk bahasa pemrograman.
Halaman arsitektur menambahkan bagian yang penting secara operasional: satu host menjalankan banyak client, dan setiap client bicara dengan tepat satu server. Satu aplikasi AI karena itu bisa menjangkau server berkas, database, dan API jarak jauh tanpa satu pun dari mereka saling tahu. Salah satu dari empat prinsip desain yang disebut di sana bukan fitur, melainkan batas: server "should not be able to read the whole conversation, nor 'see into' other servers".
Yang bisa ditawarkan server memang sedikit dan konkret: resources (konteks dan data), prompts (templat pesan), dan tools (fungsi yang bisa dieksekusi model). Yang ditawarkan client ke server pada revisi ini lebih sempit — elicitation, yaitu server meminta informasi tambahan dari pengguna.
Sebuah tool adalah nama, deskripsi, dan skema
Spesifikasi tools terasa membosankan dengan cara yang justru bagus. Sebuah tool punya name yang unik, description yang bisa dibaca manusia, dan inputSchema berupa objek JSON Schema, yang default-nya JSON Schema 2020-12 bila tidak ada deklarasi $schema. Ini definisi tool dari contoh resmi di spesifikasi:
{
"name": "get_weather",
"description": "Get current weather information for a location",
"inputSchema": {
"type": "object",
"properties": {
"location": { "type": "string", "description": "City name or zip code" }
},
"required": ["location"]
}
}
Memanggilnya berarti mengirim permintaan tools/call, dan hasilnya kembali dengan content, opsional structuredContent yang cocok dengan outputSchema, serta penanda isError. Penanda itu lebih penting dari kelihatannya. Spec membelah kegagalan menjadi dua: protocol error (tool tidak dikenal, permintaan malformed) datang sebagai JSON-RPC error, sedangkan tool execution error — tanggal dengan format salah, nilai di luar rentang, panggilan API gagal — datang sebagai hasil biasa dengan isError: true. Client diminta meneruskan yang kedua ke model supaya model bisa memperbaiki dirinya sendiri, dan lebih hati-hati terhadap yang pertama, yang biasanya tidak bisa diperbaiki model.
Ada dua detail yang layak diperhatikan. Pertama, tool disebut sebagai model-controlled: model yang memutuskan memanggilnya, dan protokol tidak mewajibkan bentuk antarmuka yang dipakai pengguna untuk melihat itu. Rekomendasi spesifikasi adalah agar selalu ada manusia dalam lingkup yang bisa menolak pemanggilan tool. Kedua, definisi tool boleh membawa annotations tentang perilakunya, dan halaman tools maupun bagian keamanan di tingkat atas sama-sama memperingatkan bahwa annotations harus diperlakukan sebagai input tidak tepercaya kecuali berasal dari server yang sudah dipercaya. Deskripsi tool adalah konten dari orang asing, bukan jaminan dari kode milik sendiri.
Dua transport, dua profil risiko
Daftar tool yang sama bergerak dengan sangat berbeda tergantung bagaimana server dijangkau, dan spesifikasi memperlakukan keduanya sebagai situasi keamanan yang berbeda.
stdio adalah kasus lokal. Client menjalankan server sebagai subprocess dan berbicara JSON-RPC bari per baris melalui standard stream miliknya. Pesan tidak boleh memuat newline di dalamnya, stderr adalah tempat untuk log, dan server "MUST NOT write anything to its stdout that is not a valid MCP message" — aturan itu ada karena satu print() nyasar di sebuah skrip akan merusak stream yang sedang dibaca client. Server sebaiknya keluar saat input-nya ditutup, dan client disarankan me-restart proses kalau mati mendadak, yang sekarang jadi murah karena tidak ada state sesi yang harus diselamatkan.
Streamable HTTP adalah kasus jarak jauh. Server mengekspos satu endpoint yang menerima POST; setiap permintaan adalah POST-nya sendiri, dijawab dengan objek JSON atau dengan stream Server-Sent Events yang terikat pada permintaan itu. Bagian keamanan pada spesifikasi transport cukup terus terang, dan layak dibaca sebagai daftar periksa, bukan sebagai teori:
- Server MUST memvalidasi header
Originpada setiap koneksi masuk dan menjawab403 Forbiddenbila header itu ada tetapi tidak valid, karena tanpa ini penyerang bisa memakai DNS rebinding untuk menjangkau server MCP lokal dari sebuah halaman web. - Saat berjalan lokal, server SHOULD hanya bind ke
127.0.0.1, bukan ke0.0.0.0. - Server SHOULD menerapkan autentikasi yang benar untuk semua koneksi.
- Di belakang reverse proxy, server SHOULD mengirim
X-Accel-Buffering: nosupaya nginx berhenti menahan event stream di buffer.
Permintaan juga membawa metadata wajib. Setiap POST harus menyertakan header MCP-Protocol-Version, header Mcp-Method, dan header Mcp-Name untuk tools/call, resources/read, serta prompts/get. Bila sebuah header berbeda dengan nilai di dalam body, server harus menolak dengan 400 Bad Request dan error HeaderMismatch — aturan ini ada agar load balancer dan backend tidak mengambil keputusan dari dua sumber kebenaran yang berbeda. Kalau ditulis sebagai perintah shell, bukan sebagai client, permintaan daftar tool akan terlihat seperti ini:
curl -i -X POST https://mcp.example.lan/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2026-07-28' \
-H 'Mcp-Method: tools/list' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list",
"params": {
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {}
}
}
}'
Permintaan itu dirakit dari persyaratan resmi spesifikasi, bukan disalin dari sesi yang benar-benar berjalan — aku belum menyalakan server MCP untuk melihatnya berhasil. Blok _meta bukan hiasan: io.modelcontextprotocol/protocolVersion dan io.modelcontextprotocol/clientCapabilities wajib ada di setiap permintaan, dan permintaan yang kehilangan salah satunya dianggap malformed, harus ditolak dengan JSON-RPC -32602 dan, lewat HTTP, 400 Bad Request.
Revisi terbaru menghapus handshake
Inilah bagian yang membuat membaca spesifikasi, bukan artikel populer, jadi pilihan paling masuk akal. Pada revisi 2026-07-28, MCP menjadi stateless di level protokol. Handshake initialize / notifications/initialized yang mendefinisikan sesi versi lama sudah hilang, begitu juga sesi protokol dan header Mcp-Session-Id; setiap permintaan membawa versi dan kemampuannya sendiri. Server yang butuh state lintas panggilan diharapkan membuat handle eksplisit dan menerimanya kembali sebagai argumen tool biasa — id keranjang belanja hanyalah sebuah string, dan model yang bertanggung jawab membawanya ke panggilan berikutnya.
Negosiasi versi ikut pindah. Tidak ada handshake negosiasi: client menaruh versi yang diingerinkannya di dalam permintaan, dan server yang tidak mengimplementasikan versi itu akan menjawab dengan UnsupportedProtocolVersionError, membawa kode JSON-RPC -32022 dan daftar versi yang memang didukung. Server wajib mengimplementasikan metode server/discover yang mengumumkan versi, kemampuan, dan identitasnya; client boleh memanggilnya lebih dulu, dan halaman versioning memakainya sebagai probe untuk membedakan server modern dari server lama.
Untuk siapa pun yang menjalankan software yang harus interoperabel, changelog adalah ringkasan jujur tentang seberapa belum stabil kondisi ini masih. Streamable HTTP kehilangan endpoint GET, header sesinya, dan stream event yang bisa dilanjutkan; subscription dibangun ulang di sekitar satu permintaan subscriptions/listen; tugas eksperimental dipindahkan keluar dari protokol inti ke sebuah extension opsional; dan Roots, Sampling, serta Logging — fitur yang paling sering dipakai tutorial tahun 2024 dan 2025 — kini berstatus deprecated, bersama transport HTTP+SSE lama. Proyek ini juga mengadopsi kebijakan deprecation dengan jendela minimal dua belas bulan, yang setidaknya berarti fitur yang masih bekerja tidak seharusnya hilang mendadak.
Apa yang protokol ini enggan lakukan untukmu
Kalimat paling layak dikutip dari seluruh spesifikasi itu juga yang paling penting bagi orang yang mengoperasionalkan server. Setelah menyusun prinsip tentang persetujuan pengguna, privasi data, keamanan tool, dan kontrol sampling, spec menyatakan polos bahwa "MCP itself cannot enforce these security principles at the protocol level" lalu menyebutkan apa yang sebaiknya dikerjakan implementor: membangun alur persetujuan dan otorisasi, mendokumentasikan implikasi keamanannya, menerapkan kontrol akses, mengikuti praktik keamanan terbaik, dan mempertimbangkan privasi saat merancang fitur.
Artinya, batas keamanannya adalah server dan aplikasi host milikmu, bukan format kabelnya. Halaman tools menaruh kewajiban server dalam daftar MUST: validasi seluruh input tool, terapkan kontrol akses yang benar, batasi laju pemanggilan, dan bersihkan output. Semua hal tentang apakah manusia menyetujui pemanggilan itu adalah keputusan antarmuka yang dibiarkan terbuka oleh protokol — dan justru itu sebabnya spesifikasi merekomendasikan perilaku human-in-the-loop, bukan mewajibkannya.
Kalau server-nya tidak ada di laptopmu
Otorisasi adalah bagian tempat server kecil paling mungkin memotong sudut, jadi layak disebutkan apa yang sebenarnya diminta spesifikasi. Otorisasi bersifat opsional bagi implementasi MCP. Implementasi dengan transport HTTP seharusnya mengikuti spesifikasi otorisasi; implementasi dengan stdio seharusnya tidak mengikutinya dan sebaiknya mengambil kredensial dari environment. Pembedaan itu praktis: subprocess yang kamu jalankan sendiri dari aplikasi desktop punya hubungan kepercayaan yang berbeda dari sebuah URL yang bisa dijangkau internet.
Untuk server HTTP, spesifikasi dibangun di atas pekerjaan OAuth yang sudah ada, bukan skema baru. Server diminta mengimplementasikan OAuth 2.0 Protected Resource Metadata (RFC 9728, April 2025), dan client diminta memakai Resource Indicators (RFC 8707, Februari 2020), yaitu mekanisme yang mengikat access token ke resource yang memang ditujunya. Halaman security considerations menambahkan bahwa server "MUST NOT pass through the token it received from the MCP client" saat memanggil API hulu, dan dokumen Security Best Practices menjelaskan alasannya: server yang meneruskan token tanpa memvalidasinya untuk dirinya sendiri berubah menjadi confused deputy, orang tengah yang permintaannya terlihat datang dari pihak lain.
Ada satu risiko lain yang justru muncul karena protokol menjadi stateless. Tanpa sesi protokol, setiap state lintas panggilan adalah handle buatan server — dan dokumen keamanan memperlakukan kepemilikan handle itu sebagai kepemilikan state orang lain, kecuali kamu bertindak. Sarannya: buat handle dengan generator angka acak yang aman, beri masa berlaku, dan kaitkan handle dengan pengguna terautentikasi di sisi server, bukan dengan mempercayai string yang kembali di hasil tool.
Daftar periksa yang akan kupakai sendiri
Bagian ini adalah interpretasi pribadi, bukan teks spesifikasi, dan memang spesifik untuk setup self-hosted kecil:
- Mulai dari stdio. Subprocess lokal menghindari seluruh permukaan serangan HTTP, dan panduan resmi untuk kredensial pada stdio hanyalah environment.
- Sebelum bind ke apa pun selain localhost, tulis validasi
Origindan autentikasinya, jangan baru direncanakan. Keduanya adalah item MUST/SHOULD di spesifikasi, dan keduanya murah selagi server masih ditulis. - Baca versi yang benar-benar dikirim client, bukan versi yang diklaim client di README. Revisi yang jadi dasar artikel ini mengubah model handshake sepenuhnya, dan daftar tool yang "hilang" sering kali hanya mismatch versi.
- Perlakukan deskripsi tool dan annotations dari server yang bukan buatanmu sebagai input tidak tepercaya, dengan kategori yang sama seperti halaman web yang tidak akan kulemparkan ke dalam prompt.
- Kalau kamu membuat handle untuk state, kaitkan dengan pemanggil. Kalau kamu meneruskan permintaan ke API lain, tukar tokennya, jangan diteruskan.
Apa yang belum bisa kupastikan
Aku belum menjalankan semua ini terhadap server MCP yang hidup, jadi aku tidak bisa memastikan seberapa baik client sekarang menangani revisi stateless yang baru diterbitkan dua bulan lalu, dan aku sengaja tidak membuat klaim tentang aplikasi mana yang mendukung revisi mana — spesifikasi sendiri hanya menjamin apa yang dikerjakan implementasi yang patuh. Aku juga tidak mengaudit ekosistem extension, registry, atau daftar fitur deprecated, dan aku tidak menilai keamanan implementasi server mana pun. Di bagian mana artikel ini menjelaskan protokol, yang dijelaskan adalah dokumennya; di mana ia menjelaskan risiko, itu pembacaanku atas apa yang diminta dokumen untuk diwujudkan sebagai tanggung jawab implementor.
Ringkasnya yang sempit itu, menurutku, tetap berguna. MCP menstandardisasi percakapan antara aplikasi AI dan tool milikmu: format kabel yang spesifikasinya berasal dari 2010, daftar tool yang dijelaskan dengan JSON Schema, dan sebuah cara untuk menyatakan versi sehingga dua pihak bisa berkata "saya tidak berbicara dialekmu". Yang tidak distandardisasi adalah apakah seharusnya ada yang memanggil toolmu, dan spesifikasi menyatakannya terang-terangan. Bagi server kecil, protokol itu bagian yang mudah. Bagian yang sulit justru bagian yang sengaja tidak dikerjakan spesifikasi untukmu.
References
- Model Context Protocol project, Specification, revision 2026-07-28. Accessed 29 September 2026.
- Model Context Protocol project, Architecture. Accessed 29 September 2026.
- Model Context Protocol project, Versioning and Compatibility. Accessed 29 September 2026.
- Model Context Protocol project, Key Changes (changelog for revision 2026-07-28). Accessed 29 September 2026.
- Model Context Protocol project, stdio transport. Accessed 29 September 2026.
- Model Context Protocol project, Streamable HTTP transport. Accessed 29 September 2026.
- Model Context Protocol project, Tools. Accessed 29 September 2026.
- Model Context Protocol project, Authorization. Accessed 29 September 2026.
- Model Context Protocol project, Authorization Security Considerations. Accessed 29 September 2026.
- Model Context Protocol project, Security Best Practices. Accessed 29 September 2026.
- JSON-RPC Working Group, JSON-RPC 2.0 Specification (origin 26 March 2010, updated 4 January 2013). Accessed 29 September 2026.
- B. Campbell, P. Bradley, Y. Tschofenig, RFC 8707: Resource Indicators for OAuth 2.0, IETF, February 2020. Accessed 29 September 2026.
- M. Jones, P. Hunt, A. Parecki, RFC 9728: OAuth 2.0 Protected Resource Metadata, IETF, April 2025. Accessed 29 September 2026.
