SSD Nodes Learn 🎉 VPS mulai $5.50/bln
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-13

Apa yang Berubah pada Server MCP Stateless?

Revisi MCP 2026-07-28 menghapus session dan handshake initialize. Pahami dampaknya pada reverse proxy, health check, timeout, autentikasi, dan load balancer.

Apa yang dimaksud dengan server MCP stateless

Server MCP stateless tidak menyimpan state per klien di antara request. Setiap request membawa versi protokol, kapabilitas klien, dan kredensial yang diperlukan server untuk memprosesnya. Karena itu, proses apa pun pada mesin mana pun dapat memproses request apa pun. MCP (Model Context Protocol, format wire yang digunakan agent untuk mengakses tool) menetapkan hal ini sebagai aturan pada revisi 2026-07-28. Revisi tersebut menghapus handshake initialize dan HTTP session yang sebelumnya digunakan.

Itulah tujuan operasionalnya. Server yang tidak menyimpan apa pun per klien dapat ditempatkan di belakang load balancer biasa tanpa session affinity, di-restart selama deployment tanpa memutus klien, dan dijalankan sebagai empat proses identik, bukan satu proses. Server yang berorientasi session tidak dapat melakukan hal-hal tersebut tanpa komponen tambahan.

Model Context Protocol adalah protokol stateless: semua informasi yang diperlukan untuk memproses request terdapat di dalam request itu sendiri. Server memproses setiap request secara independen. Tidak ada state yang boleh disimpulkan dari request sebelumnya, termasuk request pada connection atau stream yang sama.

Stateless bukan berarti server Anda tidak menyimpan apa pun. Database, queue, dan cache Anda tetap ada. Artinya, protocol tidak membawa state pada connection. Karena itu, server tidak boleh memperlakukan connection, process, atau socket yang terbuka sebagai pengganti untuk "klien ini yang masih berada di tengah percakapan".

Hal-hal yang dihapus dalam revisi 2026-07-28

2026-07-28 adalah revisi spesifikasi saat ini per Agustus 2026. Dibandingkan dengan 2025-11-25, revisi ini menghapus lima hal yang sebelumnya diperlukan untuk mendukung sesi.

  • Request initialize dan notifikasi notifications/initialized. Tidak ada handshake sama sekali (SEP-2575).
  • Header Mcp-Session-Id dan penghentian sesi dengan HTTP DELETE (SEP-2567).
  • Stream HTTP GET mandiri yang digunakan server untuk mengirim notifikasi. Stream ini digantikan oleh subscriptions/listen, yaitu POST biasa yang responsnya berupa stream berumur panjang.
  • Kemampuan melanjutkan stream SSE (server-sent events). Header Last-Event-ID dan ID untuk setiap event dihapus. Jika stream terputus, request yang sedang berjalan akan hilang. Klien harus mengirimkannya kembali sebagai request baru dengan request ID baru.
  • ping, logging/setLevel, dan notifications/roots/list_changed. Level log kini menjadi field per request, yaitu io.modelcontextprotocol/logLevel dalam _meta.

Satu method ditambahkan, dan setiap server wajib mengimplementasikannya. server/discover mengembalikan versi protokol, kapabilitas, dan identitas yang didukung server dalam satu pemanggilan. Method ini adalah hal yang paling mendekati handshake yang masih tersedia. Namun, klien tidak wajib memanggilnya.

Mengapa transport sesi sulit dijalankan di lingkungan produksi

Pada 2025-11-25 dan versi sebelumnya, server dapat membuat ID sesi saat inisialisasi dan mengembalikannya dalam header Mcp-Session-Id pada InitializeResult. Setelah itu, klien harus mengirimkan header tersebut pada setiap permintaan berikutnya. Versi protokol yang dinegosiasikan dan kemampuan klien disimpan di memori server berdasarkan ID tersebut. Setiap pilihan ini menimbulkan biaya operasional.

  • Restart menghapus tabel sesi. Spesifikasi mengharuskan server menjawab setiap permintaan yang membawa ID sesi yang sudah tidak berlaku dengan 404 Not Found, dan mengharuskan klien memulai ulang dengan InitializeRequest baru. Setiap deployment menjadi peristiwa koneksi ulang bagi semua klien yang terhubung.
  • Replika kedua tidak mengetahui sesi pada replika pertama. Penskalaan horizontal memerlukan routing sticky pada load balancer, atau penyimpanan sesi bersama yang dibaca setiap replika pada setiap permintaan.
  • Tabel sesi menggunakan memori yang bertambah seiring jumlah klien yang tidak aktif. DELETE bersifat opsional, dan klien yang menutup koneksi tanpa mengirimkannya meninggalkan entri di tabel.
  • Hasil list dapat berbeda untuk setiap koneksi, sehingga caching di depan server tidak aman.

Menghapus sesi mengatasi keempat masalah tersebut sekaligus. Perubahan inilah yang perlu dipahami sebelum Anda mengubah konfigurasi apa pun.

Hal yang kini dibawa setiap request

Setiap POST ke endpoint MCP berdiri sendiri. Versi protokol dan kapabilitas client dikirim dalam body request di bawah _meta. Beberapa field terpilih disalin ke HTTP header agar perantara dapat melakukan routing berdasarkan field tersebut tanpa mengurai JSON.

POST /mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: get_weather
Authorization: Bearer <access token>

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {"location": "Seattle, WA"},
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": {"name": "ExampleClient", "version": "1.0.0"},
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}

io.modelcontextprotocol/protocolVersion dan io.modelcontextprotocol/clientCapabilities wajib ada pada setiap request. clientInfo tidak wajib, tetapi client sebaiknya mengirimkannya. Request yang tidak memiliki field wajib dianggap malformed. Karena itu, server harus menolaknya dengan error JSON-RPC -32602 dan HTTP 400 Bad Request.

Header Mcp-Method wajib ada pada setiap request. Mcp-Name wajib ada pada tools/call, resources/read, dan prompts/get. Nilai header harus sama dengan body. Server yang memproses body harus menolak ketidaksesuaian dengan 400 Bad Request dan kode error -32020, HeaderMismatch. Aturan ini diperlukan karena load balancer yang melakukan routing berdasarkan header dan server yang mengeksekusi body menggunakan dua sumber kebenaran yang berbeda. Jika Anda menggunakan header tersebut untuk routing atau rate limiting, periksa MCP-Protocol-Version terlebih dahulu. Revisi sebelumnya tidak pernah memvalidasi header terhadap body. Pada versi tersebut, nilai header tidak dapat dipercaya.

Perbedaan versi kini menjadi error biasa pada setiap request, bukan lagi kegagalan handshake. Server yang tidak mengimplementasikan versi yang diminta menjawab 400 Bad Request dengan error -32022, UnsupportedProtocolVersion, dan mencantumkan versi yang didukungnya dalam data.supported. Client memilih salah satu versi dari daftar tersebut lalu mengirim request ulang.

Ke mana state berpindah: token, kursor, dan subscription

State tidak hilang. State berpindah ke tempat yang dapat Anda lihat dan catat di log.

Kredensial berpindah ke setiap request. Tidak ada session yang dapat dikaitkan dengan identitas, sehingga access token disertakan pada setiap HTTP call dan divalidasi setiap kali. Detailnya dijelaskan di bagian autentikasi di bawah.

Kursor harus membawa posisinya sendiri. Pagination pada tools/list, resources/list, prompts/list, dan resources/templates/list menggunakan string kursor buram, dan client tidak boleh menguraikan atau mengubahnya. Pada server dengan satu proses, offset biasanya disimpan di memory berdasarkan session. Tanpa session, kursor harus cukup untuk memungkinkan replica mana pun melanjutkan listing. Karena itu, enkode posisi ke dalam kursor lalu tanda tangani, atau simpan posisi tersebut di storage yang digunakan bersama oleh semua replica. Kursor yang tidak valid harus mengembalikan -32602. Tanda tangani kursor karena kursor buram tetap merupakan input yang diberikan client, lalu di-decode dan dipercaya oleh kode Anda.

Subscription terkait dengan request, bukan connection. Client yang menginginkan notifikasi perubahan mengirim subscriptions/listen dengan filter yang menyebutkan tipe yang diinginkan: toolsListChanged, promptsListChanged, resourcesListChanged, dan resourceSubscriptions. Server merespons dengan notifications/subscriptions/acknowledged dan mempertahankan response stream tersebut tetap terbuka. Jika stream terputus, server tidak menyimpan apa pun, dan client mengirim ulang subscriptions/listen untuk mendapatkannya kembali.

State aplikasi antar-call menjadi handle eksplisit. Jika server benar-benar harus mengingat sesuatu di antara call, jawaban spesifikasi adalah identifier yang dibuat server dan dikirim kembali sebagai argumen tool biasa. Identifier tersebut muncul dalam tool schema, dapat dicatat di log, dan tidak pernah dianggap berasal dari connection. Server dengan data per pengguna yang sebenarnya, seperti server email MCP yang di-host sendiri, menggunakan pola ini sebagai pengganti session: identifier mailbox atau draft menjadi argumen tool, sehingga replica mana pun dapat menangani call berikutnya. Banyak tool sama sekali tidak memerlukan handle: tool pencarian yang menggunakan instance SearXNG milik Anda sendiri menerima query lalu mengembalikan hasil, tanpa data yang perlu digunakan untuk melanjutkan call berikutnya dan tanpa alasan untuk memedulikan replica yang memberikan respons.

Deployment: reverse proxy, timeouts, health checks

Endpoint MCP adalah satu path yang menerima POST. Sebagian besar trafik berupa request singkat dan response JSON, yang dapat ditangani oleh proxy apa pun. Pengecualiannya adalah response streaming, saat default proxy justru menimbulkan masalah. Bagian inilah yang berubah ketika Anda beralih dari demo di laptop ke server MCP yang berjalan pada VPS.

location /mcp {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_read_timeout 1h;
    proxy_send_timeout 1h;
}

proxy_buffering off penting karena nginx secara default melakukan buffering terhadap response yang diteruskan melalui proxy. Akibatnya, event SSE ditahan sampai buffer penuh atau response selesai. Spesifikasi juga meminta server mengirim X-Accel-Buffering: no pada response SSE, dan nginx mematuhi header tersebut. Jadi, server yang benar dapat memberi tahu proxy tentang konfigurasi yang tepat secara otomatis. Tetap tetapkan direktif tersebut karena itu adalah bagian yang dapat Anda kendalikan.

proxy_read_timeout memiliki default 60 detik. Stream subscriptions/listen yang tidak mengirim data selama lebih dari durasi tersebut akan ditutup oleh nginx, bukan oleh server Anda. Akibatnya, log Anda menunjukkan proses yang sehat, sedangkan client menunjukkan stream yang terputus. Naikkan nilainya hanya pada lokasi MCP, bukan pada seluruh server. Server juga dianjurkan mengirim baris komentar SSE (baris yang diawali titik dua) sebagai keep-alive selama periode tanpa aktivitas. Dengan demikian, perantara tidak akan mengakhiri stream karena timeout.

Caddy memerlukan konfigurasi yang lebih sedikit. Secara default, Caddy melakukan buffering parsial demi efisiensi transmisi dan langsung melakukan flush ketika response membawa Content-Type: text/event-stream. Karena itu, streaming dapat berfungsi tanpa direktif tambahan.

mcp.example.com {
	reverse_proxy 127.0.0.1:8080 {
		health_uri /healthz
		health_interval 10s
	}
}

Perhatikan target pemeriksaan kesehatan tersebut. Jangan arahkan pemeriksaan aktif ke endpoint MCP dengan GET. Server yang hanya mengimplementasikan revisi ini akan menjawab 405 Method Not Allowed untuk GET dan DELETE, sedangkan metode health check default Caddy adalah GET. Proxy kemudian dapat menandai backend yang sepenuhnya sehat sebagai down. Sediakan path biasa seperti /healthz untuk proxy, lalu periksa protokol secara terpisah menggunakan POST.

curl -sS https://mcp.example.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: server/discover' \
  -d '{"jsonrpc":"2.0","id":"health-1","method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'

200 yang membawa daftar supportedVersions berarti proses aktif dan berkomunikasi menggunakan protokol tersebut. 404 dengan error JSON-RPC -32601 berarti proses aktif, tetapi tidak menyediakan server/discover, yang wajib diimplementasikan oleh setiap server 2026-07-28. 400 dengan -32022 berarti pemeriksa meminta versi yang tidak didukung oleh build ini. Inilah kondisi yang ingin Anda deteksi setelah upgrade dependensi. nginx open source tidak memiliki active health check. Karena itu, gunakan max_fails dan fail_timeout pasif pada upstream, lalu jalankan pemeriksaan protokol dari sistem monitoring Anda.

Rolling restart kini hanya menyebabkan request yang sedang berjalan terputus. Lakukan drain, biarkan POST yang terbuka selesai, jalankan proses baru, lalu minta client mengirim ulang request yang gagal. Satu-satunya hal lain yang tetap terputus adalah stream subscriptions/listen yang masih terbuka karena stream tersebut merupakan koneksi aktif ke satu proses tertentu. Statelessness menghapus session affinity. Namun, statelessness tidak menghapus connection affinity untuk stream yang sedang terbuka, dan tidak ada aturan routing yang dapat memperbaikinya. Client dapat membedakan kedua kondisi tersebut: stream yang berakhir dengan hasil subscriptions/listen kosong telah ditutup secara graceful, sedangkan stream yang berakhir tanpa hasil tersebut terputus dan dapat dianggap client sebagai alasan untuk melakukan reconnect.

Caching kini dapat digunakan untuk pertama kalinya. Hasil dari metode list kini membawa ttlMs dan cacheScope, sedangkan cacheScope: "public" memberi tahu perantara bersama bahwa response tersebut boleh di-cache. Hal ini aman hanya karena hasil list tidak lagi berbeda berdasarkan koneksi, sebagai konsekuensi langsung dari penghapusan session.

Mengapa autentikasi berubah ketika tidak ada sesi

Dengan sesi, Anda mungkin tergoda untuk melakukan autentikasi satu kali di initialize lalu menganggap ID sesi sebagai bukti untuk semua permintaan berikutnya. Jika digunakan dengan cara itu, ID sesi menjadi kredensial bearer tanpa audience, masa kedaluwarsa, atau mekanisme pencabutan, yang dibuat oleh server Anda sendiri. Menghapus sesi berarti menghilangkan jalan pintas tersebut, sehingga penggantinya lebih ketat.

MCP server yang dilindungi bertindak sebagai resource server OAuth 2.1. Setiap permintaan HTTP dari klien harus membawa Authorization: Bearer <access token>, dan server memvalidasi token pada setiap permintaan. Validasi mencakup audience: server harus memastikan bahwa token diterbitkan khusus untuk server tersebut, sesuai RFC 8707 (Resource Indicators for OAuth 2.0), serta tidak boleh menerima atau meneruskan token yang ditujukan untuk hal lain. Klien meminta audience yang benar dengan mengirimkan parameter resource menggunakan URI kanonis server.

Discovery berjalan berdasarkan challenge. Jika permintaan tiba tanpa token yang dapat digunakan, server merespons dengan 401 Unauthorized.

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
                         scope="files:read"

Klien membaca resource_metadata, mengambil dokumen tersebut (RFC 9728, OAuth 2.0 Protected Resource Metadata, yang wajib diimplementasikan oleh MCP server), menemukan authorization server, lalu menjalankan alurnya. Token yang valid tetapi memiliki terlalu sedikit izin akan mendapatkan 403 Forbidden dengan error="insufficient_scope" dan scope yang diperlukan untuk operasi tersebut.

Hal ini menimbulkan dua konsekuensi dalam pengoperasiannya. Validasi token kini dilakukan pada setiap permintaan, bukan satu kali per sesi. Karena itu, satu perjalanan pulang-pergi jaringan ke endpoint introspeksi pada setiap pemanggilan akan terlihat dalam latensi Anda. Sebaiknya gunakan token yang dapat diverifikasi secara lokal berdasarkan signature, audience, dan masa kedaluwarsa, atau cache hasil validasi dalam jangka waktu singkat dengan token sebagai kuncinya. Selain itu, karena tidak ada sesi yang menyimpan identitas, otorisasi harus dihitung dari token pada setiap pemanggilan. Model ini lebih transparan daripada model sesi, dan sejalan dengan praktik yang lebih luas untuk menyimpan kredensial di luar proses agent, yang dibahas dalam menyimpan rahasia di luar agent AI.

Hal yang benar dan yang tidak dalam revisi ini

Semua penjelasan di atas menguraikan revisi 2026-07-28. Penjelasan tersebut tidak berlaku untuk MCP selamanya dan tidak menjelaskan server yang Anda deploy tahun lalu.

Client dan server pada 2025-11-25 dan versi sebelumnya masih menggunakan model handshake. Spesifikasi menyebut revisi tersebut sebagai legacy, sedangkan revisi yang menggunakan metadata per-request disebut modern. Server yang hanya mendukung revisi ini, saat berkomunikasi dengan client lama, harus mengembalikan 405 Method Not Allowed untuk GET atau DELETE pada endpoint MCP, mengabaikan header Mcp-Session-Id tanpa membuat atau mengembalikannya, serta mengabaikan Last-Event-ID karena stream tidak dapat dilanjutkan. Server yang mendukung kedua era dapat melayani keduanya pada satu endpoint: request yang membawa _meta dilayani secara stateless, sedangkan request initialize memilih semantik session lama.

Karena itu, periksa string revisi sebelum mengandalkan semua penjelasan ini. Jika SDK Anda masih mengirim initialize, session masih digunakan dalam deployment Anda dan masalah yang berkaitan dengan session di atas masih harus Anda kelola. Hal yang sama berlaku pada sisi client: proses agent pada mesin Anda sendiri, seperti konfigurasi dalam menjalankan coding agent pada VPS, hanya bersifat stateless dalam pengertian ini jika library yang digunakannya mendukung revisi modern. Baca versi yang dinegosiasikan oleh runtime Anda, lalu baca revisi spesifikasi yang sesuai. Perlakukan halaman ini sebagai penjelasan tentang satu revisi tertentu, bukan tentang protokol secara umum.

FAQ

Apakah server MCP stateless berarti saya tidak dapat menyimpan apa pun?

Tidak. Stateless menjelaskan protokol, bukan aplikasi Anda. Database, queue, dan cache tetap berfungsi seperti sebelumnya. Perubahannya adalah state yang berlangsung selama beberapa panggilan harus direferensikan dengan identifier eksplisit yang dikirimkan client pada setiap request, misalnya handle yang dibuat server dalam argumen tool. Yang tidak boleh dilakukan adalah menyimpulkan konteks dari koneksi. Spesifikasi menyatakan bahwa server tidak boleh bergantung pada request sebelumnya melalui koneksi yang sama untuk menetapkan kapabilitas, versi protokol, atau identitas client, karena setiap request menyertakan informasi tersebut dalam _meta.

Apakah saya masih memerlukan sticky session pada load balancer?

Tidak untuk request biasa. Dalam revisi 2026-07-28, setiap POST membawa versi protokol, kapabilitas, dan kredensialnya sendiri. Karena itu, replica mana pun dapat menjawab request apa pun dan round-robin dapat digunakan. Satu-satunya hal yang tetap berumur panjang adalah stream respons subscriptions/listen, yaitu satu koneksi terbuka ke satu proses. Stream tersebut berakhir ketika proses itu berakhir, lalu client mengirim ulang subscriptions/listen untuk membangunnya kembali. Ini adalah masa hidup koneksi, bukan afinitas session, dan tidak ada aturan routing yang dapat mencegahnya.

Apa yang terjadi pada Mcp-Session-Id dan stream HTTP GET?

Keduanya dihapus pada revisi 2026-07-28 berdasarkan SEP-2567 dan SEP-2575. Server yang hanya mengimplementasikan revisi ini harus memberikan respons 405 Method Not Allowed terhadap GET dan DELETE pada endpoint MCP, serta mengabaikan header Mcp-Session-Id, bukan mengirimkannya kembali. Notifikasi perubahan yang diprakarsai server kini dikirim melalui stream respons dari request subscriptions/listen, bukan melalui stream GET yang berdiri sendiri. Server yang harus tetap melayani client lama mengimplementasikan perilaku revisi sebelumnya bersamaan dengan revisi ini.

Bagaimana cara melakukan health check pada server MCP tanpa handshake?

Gunakan dua tingkat pemeriksaan. Arahkan active check pada proxy ke path HTTP biasa yang disediakan aplikasi Anda, karena GET ke endpoint MCP secara benar akan mengembalikan 405 dan dapat menandai backend yang sehat sebagai down. Kemudian periksa protokolnya dengan mengirim POST server/discover. Setiap server 2026-07-28 wajib mengimplementasikannya. Pastikan responsnya adalah HTTP 200 dan mencantumkan versi protokol yang digunakan client Anda. Respons 404 dengan error JSON-RPC -32601 berarti proses sedang berjalan, tetapi tidak menyediakan method tersebut. Respons 400 dengan -32022 berarti versi yang diminta tidak didukung oleh build tersebut.