SSD Nodes Learn 🎉 VPS $5.50/aydan başlayan
Rehberler Matt ConnorYazan Matt Connor · Güncellendi 2026-08-13

Stateless MCP sunucusu nedir ve neler değişti?

2026-07-28 tarihli MCP revizyonu ile oturum ve initialize el sıkışması kaldırıldı. Reverse proxy, sağlık kontrolleri ve kimlik doğrulama süreçlerindeki güncellemeleri inceleyin.

Stateless bir MCP sunucusu nedir

Stateless (durumsuz) bir MCP sunucusu, istekler arasında istemci bazlı herhangi bir durum bilgisi tutmaz. Her istek; protokol sürümünü, istemci yeteneklerini ve sunucunun yanıt vermesi için gereken kimlik bilgilerini taşır; bu sayede herhangi bir makinedeki herhangi bir süreç, her türlü isteği yanıtlayabilir. MCP (ajanların araçlara erişmek için kullandığı kablo formatı olan Model Context Protocol), 2026-07-28 revizyonunda bunu bir kural haline getirmiş ve altında yatan initialize el sıkışması ile HTTP oturumunu kaldırmıştır.

Operasyonel açıdan tüm amaç budur. İstemci bazlı hiçbir şey tutmayan bir sunucu, oturum bağımlılığı (session affinity) gerektirmeyen sıradan bir yük dengeleyicinin arkasında çalışabilir, dağıtım sırasında istemcileri kesintiye uğratmadan yeniden başlatılabilir ve tek bir süreç yerine dört özdeş süreç olarak çalıştırılabilir. Oturum odaklı bir sunucu, ek mekanizmalar olmadan bunların hiçbirini yapamaz.

Model Context Protocol, stateless bir protokoldür: Bir isteği işlemek için gereken tüm bilgiler isteğin kendi içinde bulunur. Sunucu her isteği bağımsız olarak işler; aynı bağlantı veya akış üzerindekiler de dahil olmak üzere, önceki isteklerden hiçbir durum çıkarımı yapılmamalıdır.

Stateless olması, sunucunuzun hiçbir şey depolamadığı anlamına gelmez. Veritabanınız, kuyruğunuz ve önbelleğiniz hala yerindedir. Bu, protokolün bağlantı üzerinde herhangi bir durum taşımadığı anlamına gelir; dolayısıyla sunucu, bir bağlantıyı, bir süreci veya açık bir soketi "bu istemci, konuşmanın ortasında" şeklinde bir temsilci olarak görmemelidir.

2026-07-28 revizyonu neleri kaldırdı

2026-07-28, Ağustos 2026 itibarıyla spesifikasyonun güncel revizyonudur. 2025-11-25 ile karşılaştırıldığında, oturumları desteklemek için var olan beş öğeyi kaldırır.

  • initialize isteği ve notifications/initialized bildirimi. Artık herhangi bir el sıkışma (handshake) süreci yoktur (SEP-2575).
  • Mcp-Session-Id başlığı ve HTTP DELETE ile oturum sonlandırma (SEP-2567).
  • Sunucuların bildirimleri gönderdiği bağımsız HTTP GET akışı. Bunun yerini, yanıtı uzun ömürlü bir akış olan standart bir POST isteği olan subscriptions/listen almıştır.
  • SSE (server-sent events) akış devam ettirilebilirliği. Last-Event-ID başlığı ve olay bazlı kimlikler kaldırılmıştır; bu nedenle kesilen bir akış, o an işlenmekte olan isteği kaybeder ve istemci, yeni bir istek kimliğiyle isteği yeniden oluşturmalıdır.
  • ping, logging/setLevel ve notifications/roots/list_changed. Günlük seviyesi artık _meta içindeki io.modelcontextprotocol/logLevel alanıyla istek bazlı olarak belirlenmektedir.

Bir yöntem eklenmiştir ve her sunucunun bunu uygulaması zorunludur. server/discover, sunucunun desteklediği protokol sürümlerini, yeteneklerini ve kimliğini tek bir çağrıda döndürür. Bu, geriye kalan el sıkışma işlemine en yakın yapıdır ve istemciler için çağrılması isteğe bağlıdır.

Oturum taşıma mekanizmasını canlı ortamda çalıştırmanın zorlukları

2025-11-25 ve önceki sürümlerde sunucu, başlatma sırasında bir oturum kimliği oluşturabilir ve bunu InitializeResult üzerindeki Mcp-Session-Id başlığında döndürebilirdi. İstemci, sonraki her istekte bu başlığı göndermek zorundaydı. Anlaşılan protokol sürümü ve istemcinin yetenekleri, sunucunun belleğinde bu kimlik ile anahtarlanmış şekilde tutulurdu. Bu seçimlerin her birinin operasyonel bir maliyeti vardı.

  • Yeniden başlatma işlemi oturum tablosunu silerdi. Teknik şartname, sunucunun geçersiz bir oturum kimliği taşıyan her isteğe 404 Not Found ile yanıt vermesini ve istemcinin yeni bir InitializeRequest ile süreci baştan başlatmasını zorunlu kılıyordu. Her dağıtım, bağlı tüm istemciler için bir yeniden bağlanma olayına dönüşüyordu.
  • İkinci bir kopya, ilk kopyanın oturumlarını bilmiyordu. Ölçeklendirme yapmak, yük dengeleyicide yapışkan yönlendirme (sticky routing) veya her kopyanın her istekte okuduğu paylaşımlı bir oturum deposu gerektiriyordu.
  • Oturum tablosu, boşta bekleyen istemcilerle birlikte büyüyen bir bellek alanıydı. DELETE kullanımı isteğe bağlıydı ve oturumu kapatmadan bağlantıyı kesen istemciler, sunucuda kalıntı kayıtlar bırakıyordu.
  • Liste sonuçları bağlantı başına değişebildiği için sunucunun önünde önbellekleme yapmak güvenli değildi.

Oturumların kaldırılması, bu dört sorunu aynı anda ortadan kaldırır. Herhangi bir yapılandırmaya dokunmadan önce anlaşılması gereken değişiklik budur.

Her isteğin taşıdığı veriler

MCP uç noktasına yapılan her POST isteği bağımsızdır. Protokol sürümü ve istemci yetenekleri, istek gövdesinde _meta altında taşınır; seçili alanlar ise bir aracı sunucunun JSON ayrıştırması yapmadan yönlendirme yapabilmesi için HTTP başlıklarına yansıtılır.

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 ve io.modelcontextprotocol/clientCapabilities her istekte zorunludur. clientInfo zorunlu değildir ancak istemcilerin bunu göndermesi önerilir. Zorunlu bir alanı eksik olan istek hatalı yapılandırılmış sayılır; bu durumda sunucu, isteği -32602 JSON-RPC hatası ve 400 Bad Request HTTP kodu ile reddetmelidir.

Mcp-Method başlığı her istekte zorunludur. Mcp-Name ise tools/call, resources/read ve prompts/get isteklerinde gereklidir. Başlık değeri gövde ile eşleşmelidir; gövdeyi işleyen bir sunucu, uyumsuzluk durumunda 400 Bad Request ve -32020 hata kodu ile HeaderMismatch yanıtını dönerek isteği reddetmelidir. Bu kuralın varlık nedeni, başlığa göre yönlendirme yapan bir yük dengeleyici ile gövdeyi işleyen sunucunun iki farklı doğruluk kaynağı olmasıdır. Eğer bu başlıklara göre yönlendirme veya hız sınırlaması (rate-limiting) yapıyorsanız, önce MCP-Protocol-Version değerini kontrol edin: önceki sürümlerde başlık ile gövde doğrulaması yapılmadığından, o sürümlerde başlık değeri güvenilir değildir.

Sürüm uyuşmazlığı artık başarısız bir el sıkışma değil, isteğe bağlı olağan bir hatadır. İstenen sürümü desteklemeyen bir sunucu, 400 Bad Request yanıtı ve -32022 hata kodu ile UnsupportedProtocolVersion hatasını döner ve desteklediği sürümleri data.supported içinde listeler. İstemci, bu listeden birini seçerek isteği tekrar eder.

Durum bilgisi nereye gitti: belirteçler, imleçler ve abonelikler

Durum bilgisi kaybolmadı. Görebileceğiniz ve günlükleyebileceğiniz yerlere taşındı.

Kimlik bilgileri her isteğin içine taşınır. Kimliği ilişkilendirecek bir oturum yoktur, bu nedenle erişim belirteci her HTTP çağrısında taşınır ve her seferinde doğrulanır. Ayrıntılar aşağıdaki kimlik doğrulama bölümündedir.

İmleçler kendi konumlarını taşımak zorundadır. tools/list, resources/list, prompts/list ve resources/templates/list üzerindeki sayfalama işlemleri opak bir imleç dizisi kullanır; istemciler bunu ayrıştırmamalı veya değiştirmemelidir. Tek süreçli bir sunucuda, ofset değerini oturum anahtarıyla bellekte tutmak yaygındı. Oturum olmadığında, imleç herhangi bir replikanın listelemeye devam etmesi için yeterli olmalıdır; bu nedenle konumu imlecin içine kodlayıp imzalayın veya tüm replikaların paylaştığı bir depolama alanında tutun. Geçersiz bir imleç -32602 hatası döndürmelidir. İmleci imzalayın çünkü opak bir imleç, kodunuzun çözdüğü ve güvendiği, istemci tarafından sağlanan bir girdidir.

Abonelikler bir bağlantıya değil, bir isteğe aittir. Değişiklik bildirimleri isteyen bir istemci, istediği türleri belirten bir filtre ile subscriptions/listen gönderir: toolsListChanged, promptsListChanged, resourcesListChanged ve resourceSubscriptions. Sunucu notifications/subscriptions/acknowledged ile yanıt verir ve bu yanıt akışını açık tutar. Akış kesilirse sunucu hiçbir şeyi saklamaz ve istemci geri almak için subscriptions/listen isteğini tekrar gönderir.

Çağrılar arası uygulama durumu açık bir tanıtıcıya dönüşür. Bir sunucunun çağrılar arasında gerçekten bir şeyi hatırlaması gerektiğinde, şartnamenin cevabı, normal bir araç argümanı olarak geri iletilen, sunucu tarafından oluşturulmuş bir tanımlayıcıdır. Bu tanımlayıcı araç şemasında görünür, günlük kaydı alınabilir ve bağlantı üzerinden asla örtük olarak varsayılmaz. Kendi kendine barındırılan bir MCP e-posta sunucusu gibi arkasında gerçek kullanıcı verisi bulunan bir sunucu, oturum yerine bu modeli kullanır: posta kutusu veya taslak tanımlayıcısı bir araç argümanıdır, bu sayede herhangi bir replika bir sonraki çağrıyı devralabilir. Birçok araç hiçbir tanıtıcıya ihtiyaç duymaz: kendi SearXNG örneğiniz tarafından desteklenen bir arama aracı, bir sorgu alır ve sonuçları geri verir; bir sonraki çağrının devam etmesi gereken bir durum yoktur ve hangi replikanın yanıt verdiğinin bir önemi yoktur.

Deployment: reverse proxy, timeouts, health checks

The MCP endpoint is one path that accepts POST. Most traffic is a short request and a JSON response, which any proxy handles. The exception is the streaming response, where proxy defaults work against you. This is the part that changes when you move from a laptop demo to an MCP server running on a 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 matters because nginx buffers proxied responses by default, which holds SSE events until a buffer fills or the response ends. The specification also asks servers to send X-Accel-Buffering: no on SSE responses, and nginx honours that header, so a correct server tells your proxy the right thing on its own. Set the directive too, because that is the half you control.

proxy_read_timeout defaults to 60 seconds. A subscriptions/listen stream that sits quiet for longer than that is closed by nginx, not by your server, so your logs show a healthy process and your client shows a dropped stream. Raise it on the MCP location only, not on the whole server. Servers are also encouraged to send an SSE comment line (a line beginning with a colon) as a keep-alive during quiet periods, which stops intermediaries from timing the stream out at all.

Caddy needs less. It buffers partially by default for wire efficiency and flushes immediately when the response carries Content-Type: text/event-stream, so streaming works without extra directives.

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

Note what that health check points at. Do not aim an active check at the MCP endpoint with GET, because a server implementing only this revision answers 405 Method Not Allowed to GET and DELETE, and Caddy's default health method is GET. The proxy would then mark a perfectly healthy backend as down. Serve a plain path such as /healthz for the proxy, and check the protocol separately with a 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":{}}}}'

A 200 carrying a supportedVersions list means the process is up and speaking the protocol. A 404 with JSON-RPC error -32601 means the process is up but does not serve server/discover, which every 2026-07-28 server must implement. A 400 with -32022 means your checker asked for a version this build does not support, which is exactly what you want to catch after a dependency upgrade. Open source nginx has no active health checks, so use passive max_fails and fail_timeout on the upstream and run the protocol check from your monitoring instead.

A rolling restart now costs you the requests in flight and nothing else. Drain, let open POSTs finish, start the new process, and clients re-issue whatever failed. The one thing you still drop is any open subscriptions/listen stream, because that stream is a live connection to one specific process. Statelessness removed session affinity. It did not remove connection affinity for a stream that is open right now, and no routing rule fixes that. A client can tell the difference: a stream that ends with the empty subscriptions/listen result closed gracefully, and a stream that ends without one dropped, which the client may treat as a reason to reconnect.

Caching becomes possible for the first time. Results from the list methods now carry ttlMs and cacheScope, and cacheScope: "public" tells shared intermediaries they may cache the response. That is only safe because list results no longer vary per connection, which is a direct consequence of removing sessions.

Oturum olmadığında kimlik doğrulamanın neden değiştiği

Oturum varken, initialize üzerinden bir kez kimlik doğrulaması yapıp ardından oturum kimliğini (session ID) sonraki her işlem için bir kanıt olarak kullanmak cazipti. Bu şekilde kullanılan bir oturum kimliği; hedef kitlesi, son kullanma tarihi ve iptal mekanizması olmayan, sunucunuz tarafından oluşturulmuş bir "taşıyıcı kimlik bilgisi" (bearer credential) işlevi görür. Oturumların kaldırılması bu kestirme yolu ortadan kaldırır ve yerine daha katı bir yöntem getirir.

Korunan bir MCP sunucusu, bir OAuth 2.1 kaynak sunucusu gibi davranır. İstemciden gelen her HTTP isteği Authorization: Bearer <access token> taşımalıdır ve sunucu, her istekte bu belirteci (token) doğrular. Doğrulama işlemi hedef kitleyi de kapsar: Sunucu, RFC 8707 (OAuth 2.0 için Kaynak Göstergeleri) uyarınca belirtecin özellikle kendisi için düzenlendiğini doğrulamalı; başka bir amaçla oluşturulmuş belirteçleri kabul etmemeli veya iletmemelidir. İstemciler, sunucunun kanonik URI'sini içeren resource parametresini göndererek doğru hedef kitleyi talep ederler.

Keşif süreci bir sınama (challenge) üzerinden yürütülür. Kullanılabilir bir belirteç içermeyen bir istek geldiğinde, sunucu 401 Unauthorized ile yanıt verir.

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

İstemci resource_metadata değerini okur, ilgili belgeyi (MCP sunucularının uygulaması gereken RFC 9728, OAuth 2.0 Korumalı Kaynak Meta Verileri) getirir, yetkilendirme sunucusunu bulur ve akışı başlatır. Yetersiz izinlere sahip geçerli bir belirteç, error="insufficient_scope" ve o işlem için gerekli kapsamlar (scopes) ile birlikte 403 Forbidden yanıtını alır.

Bunu nasıl çalıştırdığınızla ilgili iki sonuç ortaya çıkar. Belirteç doğrulaması artık oturum başına bir kez değil, her istekte gerçekleşir; bu nedenle her çağrıda bir iç gözlem (introspection) uç noktasına yapılacak ağ gidiş-dönüşü gecikme sürenize yansıyacaktır. Yerel olarak bir imza, hedef kitle ve son kullanma tarihi üzerinden doğrulayabileceğiniz belirteçleri tercih edin veya doğrulama sonucunu belirteç anahtarıyla kısa bir süre için önbelleğe alın. Ayrıca, kimlik tutan bir oturum bulunmadığından, yetkilendirme her çağrıda belirteç üzerinden hesaplanmalıdır. Bu, oturum modelinden daha şeffaf bir yöntemdir ve kimlik bilgilerini AI aracı sürecinin dışında tutma yönündeki daha geniş uygulama pratiğiyle uyumludur; bu konu gizli bilgileri AI aracının dışında tutma bölümünde ele alınmıştır.

Bu revizyon hakkında doğru olanlar ve olmayanlar

Yukarıdaki her şey 2026-07-28 revizyonunu tanımlamaktadır. Bu metin MCP'nin tamamını veya geçen yıl dağıttığınız sunucuyu tanımlamaz.

2025-11-25 ve önceki sürümlerdeki istemciler ve sunucular hala el sıkışma (handshake) modelini kullanır. Teknik şartname bu revizyonları eski (legacy), istek bazlı meta veri içeren revizyonları ise modern olarak adlandırır. Yalnızca bu revizyonu destekleyen bir sunucu, eski bir istemciyle karşılaştığında MCP uç noktasında 405 Method Not Allowed ile GET veya DELETE arasında bir yanıt vermeli, herhangi bir Mcp-Session-Id başlığını oluşturmadan veya yansıtmadan görmezden gelmeli ve akışlar devam ettirilemediği için Last-Event-ID değerini yok saymalıdır. İki dönemi de destekleyen bir sunucu, her ikisine de tek bir uç noktadan hizmet verebilir: modern _meta taşıyan bir istek durumsuz (stateless) olarak işlenir, initialize isteği ise eski oturum semantiğini seçer.

Bu nedenle, buradaki bilgilere güvenmeden önce revizyon dizisini kontrol edin. SDK'nız hala initialize gönderiyorsa, dağıtımınız için oturumlar hala geçerlidir ve yukarıda bahsedilen oturum kaynaklı sorunlar sizin sorumluluğunuzdadır. Aynı durum istemci tarafı için de geçerlidir: bir VPS üzerinde kodlama ajanı çalıştırma kurulumunda olduğu gibi, kendi makinenizdeki bir aracı süreci, ancak kullandığı kütüphane modern bir revizyonu destekliyorsa bu anlamda durumsuzdur. Çalışma zamanınızın (runtime) müzakere ettiği sürümü okuyun, ardından şartnamenin ilgili revizyonunu inceleyin ve bu sayfayı protokolün genelini değil, yalnızca isimlendirilmiş bir revizyonu tanımlayan bir kaynak olarak değerlendirin.

FAQ

Durumsuz (stateless) bir MCP sunucusu hiçbir şeyi depolayamayacağım anlamına mı gelir?

Hayır. Durumsuz ifadesi uygulamanızı değil, protokolü tanımlar. Veritabanları, kuyruklar ve önbellekler tam olarak eskisi gibi çalışır. Değişen şey, birden fazla çağrıya yayılan durumun, istemcinin her istekte ilettiği açık bir tanımlayıcı (örneğin bir araç argümanında sunucu tarafından oluşturulan bir tanıtıcı) ile referans gösterilmesi gerekliliğidir. Bağlantı üzerinden bağlam çıkarımı yapamazsınız: şartname, bir sunucunun yetenekleri, protokol sürümünü veya istemci kimliğini belirlemek için aynı bağlantı üzerindeki önceki isteklere güvenmemesi gerektiğini belirtir; çünkü her istek bunları _meta içinde sağlar.

Yük dengeleyicimde hala yapışkan oturumlara (sticky sessions) ihtiyacım var mı?

Normal istekler için hayır. 2026-07-28 revizyonu kapsamında her POST kendi protokol sürümünü, yeteneklerini ve kimlik bilgilerini taşır; bu nedenle herhangi bir kopya herhangi bir isteği yanıtlayabilir ve round-robin yöntemi uygundur. Uzun ömürlü kalan tek şey, tek bir işleme açılan tek bir bağlantı olan subscriptions/listen yanıt akışıdır. Bu akış, ilgili işlem sona erdiğinde biter ve istemci onu yeniden kurmak için subscriptions/listen gönderir. Bu, oturum yakınlığından ziyade bağlantı ömrüdür ve hiçbir yönlendirme kuralı bunu engelleyemez.

Mcp-Session-Id ve HTTP GET akışına ne oldu?

Her ikisi de SEP-2567 ve SEP-2575 kapsamında 2026-07-28 revizyonunda kaldırıldı. Yalnızca bu revizyonu uygulayan bir sunucu, MCP uç noktasında 405 Method Not Allowed ile GET ve DELETE isteklerine yanıt vermeli ve bir Mcp-Session-Id başlığını geri yansıtmak yerine görmezden gelmelidir. Sunucu tarafından başlatılan değişiklik bildirimleri artık bağımsız bir GET akışı yerine, bir subscriptions/listen isteğinin yanıt akışı üzerinden iletilir. Eski istemcilere hizmet vermeye devam etmesi gereken sunucular, önceki revizyonun davranışını bununla birlikte uygulamalıdır.

El sıkışması olmayan bir MCP sunucusunun sağlık kontrolünü nasıl yaparım?

İki seviyeli bir yöntem kullanın. Proxy'nin aktif kontrolünü uygulamanızın sunduğu düz bir HTTP yoluna yönlendirin; çünkü MCP uç noktasına yapılan bir GET isteği doğru bir şekilde 405 döndürür ve sağlıklı bir arka ucu (backend) devre dışı olarak işaretler. Ardından, her 2026-07-28 sunucusunun uygulaması gereken server/discover isteğini POST ederek protokolün kendisini kontrol edin ve yanıtın HTTP 200 olduğunu ve istemcilerinizin kullandığı bir protokol sürümünü listelediğini doğrulayın. JSON-RPC hatası -32601 içeren bir 404, sürecin çalıştığını ancak bu yöntemi sunmadığını; -32022 içeren bir 400 ise talep ettiğiniz sürümün o derleme tarafından desteklenmediğini gösterir.