NestJS ile production'da stateless MCP server: auth, session'lar ve state nereye gittiRunning a stateless MCP server in production with NestJS: auth, sessions, and where the state went
28 Temmuz 2026'da MCP, session'ları ve initialize handshake'ini kaldırdı. Bu yazı, revizyonun deployment katmanında neyi değiştirdiğini, neyi sessizce size geri devrettiğini ve düz bir round-robin load balancer arkasında NestJS ile ne ölçtüğümüzü anlatıyor.
On July 28, 2026, MCP dropped sessions and the initialize handshake. This post covers what the revision changes at the deployment layer, what it quietly hands back to you, and what we measured behind a plain round-robin load balancer with NestJS.
Naylalabs · MühendislikEngineering28 Temmuz 2026'da Model Context Protocol, operasyonel olarak en pahalı iki fikrini çöpe attı: protokol seviyesindeki session ve initialize handshake'i. O tarihten önce bir remote MCP server işlettiyseniz, altyapınızın bir kısmı — sticky-session konfigürasyonu, paylaşımlı session store'u, trafiği yönlendirebilmek için JSON-RPC gövdesini açıp okumak zorunda kalan gateway — artık legacy.
On July 28, 2026, the Model Context Protocol dropped its two most operationally expensive ideas: the protocol-level session and the initialize handshake. If you ran a remote MCP server before that date, some of your infrastructure — the sticky-session config, the shared session store, the gateway that had to inspect JSON-RPC bodies to route traffic — is now legacy.
Bu, breaking change'in makbul türü. Ama göründüğü kadar bedava da değil. Session store'unuzun tuttuğu state yok olmadı; yer değiştirdi ve artık sizin uygulamanızın problemi. MCP'ye yabancıysanız önce MCP nedir yazısına bakın; burada protokolü değil, onu production'da işletmeyi konuşacağız.
This is the good kind of breaking change. It is also less free than it looks. The state your session store used to hold did not disappear; it moved, and it is now your application's problem. If MCP is new to you, start with what MCP is; here we talk not about the protocol but about operating it in production.
012026-07-28 revizyonu gerçekte neyi değiştirdiWhat the 2026-07-28 revision actually changed
Kısa versiyon, resmi changelog'dan:
The short version, from the official changelog:
- Session'lar gitti.
Mcp-Session-Idheader'ı kaldırıldı;tools/listartık bağlantıya göre değişmiyor. Çağrılar arası state, sıradan tool argümanı olarak dolaşan handle'lara taşındı. - Sessions are gone. The
Mcp-Session-Idheader is removed;tools/listno longer varies per connection. Cross-call state moved into handles passed as ordinary tool arguments. - Handshake gitti. Her istek, protokol versiyonunu ve client yeteneklerini
_metaiçinde taşıyor; yeniserver/discoverRPC'si (sunucular için MUST) yetenekleri baştan ilan ediyor. - The handshake is gone. Every request carries its protocol version and client capabilities in
_meta; the newserver/discoverRPC (a MUST for servers) advertises capabilities up front. - SSE resumability gitti. Kopan bir response stream'i uçuştaki isteği kaybediyor; client isteği yeni bir request ID ile tekrar göndermek zorunda.
- SSE resumability is gone. A broken response stream loses the in-flight request; the client MUST re-issue it as a new request with a new request ID.
- Routing header'ları zorunlu. POST'lar
Mcp-MethodveMcp-Nametaşıyor — L7 proxy gövdeyi parse etmeden yönlendirebiliyor. - Routing headers are required. POSTs carry
Mcp-MethodandMcp-Name— an L7 proxy can route without parsing bodies. - List cevapları cache'lenebilir.
tools/listve akrabalarıttlMs+cacheScopedönmek zorunda (CacheableResult). - List responses are cacheable.
tools/listand friends must returnttlMs+cacheScope(CacheableResult). - Auth sıkılaştı.
issdoğrulaması (RFC 9207) zorunlu, credential'lar issuer'a bağlı, Dynamic Client Registration deprecated — yerini Client ID Metadata Documents alıyor. - Auth got stricter.
issvalidation (RFC 9207) is mandatory, credentials are bound to their issuer, and Dynamic Client Registration is deprecated in favor of Client ID Metadata Documents. - Roots, Sampling ve Logging deprecated — on iki aylık kaldırma penceresiyle.
- Roots, Sampling and Logging are deprecated — with a twelve-month removal window.
Migration planlamadan önce bir nüans: hedeflediğiniz spec revizyonunun yayın durumunu kontrol edin. MCP blog'u 2026-07-28 için release-candidate yazısını da final-spec yazısını da birkaç gün arayla yayınladı ve SDK desteği önce beta olarak çıktı. Sunucunuzun server/discover içinde beyan ettiği revizyon string'ini sabitleyin ve bunu dokümantasyonunuzda açıkça söyleyin.
One nuance before you plan a migration: verify the release status of the spec revision you are targeting. The MCP blog published both a release-candidate post and a final-spec post for 2026-07-28 within days of each other, and SDK support shipped as betas first. Pin the exact revision string your server claims in server/discover, and say it out loud in your docs.
02"Stateless", "state yok" demek değil — "state artık senin" demek"Stateless" does not mean "no state" — it means the state is now yours
MCP session'ı örtük bir sözleşmeydi: sunucu, bu client'a bu bağlantıda ne söylediğini hatırlayabilirdi. O sözleşmenin bedeli sticky session'lar, session-affinity bug'ları ve her istekte danışılan paylaşımlı bir store'du.
An MCP session used to be an implicit contract: the server could remember what it told this client on this connection. That contract paid for sticky sessions, session-affinity bugs, and a shared store consulted on every request.
Yerine gelen şey bilinçli olarak sıkıcı: bir tool'un sürekliliğe ihtiyacı varsa — pagination cursor'ı, bir sepet, uzun süren bir iş — sunucu bir handle (opak bir string) üretip döner. Client bir sonraki çağrıda bu handle'ı sıradan bir tool argümanı olarak geri gönderir. State hâlâ var; handle'larınızı sırtlayan storage'da yaşıyor ve protokol bunu artık sizden saklamıyor.
The replacement is deliberately boring: if a tool needs continuity — a pagination cursor, a shopping cart, a long-running job — the server mints a handle (an opaque string) and returns it. The client passes it back as a normal tool argument on the next call. State still exists; it lives in whatever storage backs your handles, and the protocol no longer hides that from you.
| Öncesi (protokol session'ları)Before (protocol sessions) | Sonrası (server-minted handle'lar)After (server-minted handles) | |
|---|---|---|
| Storage maliyetiStorage cost | Session store'a her istekte gidilirSession store consulted on every request | Sadece state isteyen tool'lardaOnly on tools that need state |
| YönlendirmeRouting | Affinity şart ya da global latency bedelli paylaşımlı storeAffinity required, or shared store with global latency cost | Her replica her isteği karşılayabilirAny replica can serve any request |
| ÖmürLifetime | Bağlantı semantiği, bulanıkConnection semantics, fuzzy | Sizin TTL politikanız, explicitYour TTL policy, explicit |
| GörünürlükVisibility | Tool şemasında görünmezInvisible in the tool schema | Şemada görünür — sözleşme, state'ini gösterirVisible in the schema — the contract shows its state |
Son satır göründüğünden önemli: handle'lar artık tool şemalarınızda duruyor, yani state sadece bir ops meselesi değil, bir tasarım meselesi. Bir handle'ın ne vaat ettiği, ne kadar yaşadığı ve bir agent onu replay ettiğinde ne olacağı, serinin tool contract tasarımı yazısının konusu. Burada sadece nereye koyduğumuzu söyleyelim: sert TTL'li, {tenant}:{handle} ile anahtarlanmış bir Redis hash'i — çünkü pod restart'ından sağ çıkan handle, state'i process dışına taşımanın zaten bütün amacı.
That last row matters more than it looks: handles now live in your tool schemas, which makes state a design concern, not just an ops concern. What a handle promises, how long it lives, and what happens when an agent replays it belongs to this series' tool-contract article. Here we will only say where we put them: a Redis hash with a hard TTL, keyed by {tenant}:{handle} — a handle that survives a pod restart is the whole point of moving state out of the process.
// "State nereye gitti" sorusunun operasyonel yarısı.// The operational half of the "where did the state go" question. // Handle'lar client için opak; yine de versiyonlayın (h1_ prefix'i).// Handles are opaque to the client; version them anyway (the h1_ prefix). @Injectable() export class HandleStore { constructor(@Inject(REDIS) private readonly redis: Redis) {} async mint(tenantId: string, payload: object, ttlSeconds: number): Promise<string> { const handle = `h1_${randomUUID()}`; await this.redis.set(`${tenantId}:${handle}`, JSON.stringify(payload), 'EX', ttlSeconds); return handle; } async resolve<T>(tenantId: string, handle: string): Promise<T | null> { const raw = await this.redis.get(`${tenantId}:${handle}`); return raw ? (JSON.parse(raw) as T) : null; } }
Tenant prefix'ine dikkat. Session olmayınca, bir handle'ı çağırana bağlayan tek şey sizin authorization katmanınız — tenant'lar arası sızan bir handle, ucuna agent bağlanmış bir IDOR açığıdır. Bir handle'ın hangi agent kimliğine bağlı olduğu ve kotaların ona nasıl iliştiği ise serinin kimlik yazısının konusu.
Mind the tenant prefix. Without sessions, the only thing binding a handle to a caller is your authorization layer — a handle leaked across tenants is an IDOR bug with an agent attached. Which agent identity a handle is bound to, and how quotas attach to it, is this series' identity article.
03Nihayet sahip olabileceğiniz deployment topolojisiThe deployment topology you can finally have
Temmuz öncesinde, yatay ölçeklenen bir MCP server şunlardan birine mecburdu: load balancer'da sticky session, hot path üzerinde paylaşımlı session store, ya da metoda göre yönlendirme yapabilmek için JSON-RPC gövdesini açan bir gateway. Üçü de gereksinim listesinden düştü. 2026-07-28 topolojisi, on yıllık REST servislerinden zaten bildiğiniz topoloji:
Pre-July, a horizontally scaled MCP server needed one of: sticky sessions at the load balancer, a shared session store on the hot path, or a gateway doing deep packet inspection to route by method. All three dropped off the requirements list. The 2026-07-28 topology is the one you already know from a decade of REST services:
Mcp-Method header'ına bakarak yönlendirir; Redis hot path'in dışında, yalnızca handle'lı tool'lar dokunur.
Figure 2. The ingress routes on the Mcp-Method header; Redis sits off the hot path, touched only by handle-backed tools.
Topolojiyi bu kadar temizleyen iki spec detayı var. Birincisi, routing artık header üstünden: her Streamable HTTP POST Mcp-Method (tool çağrılarında ek olarak Mcp-Name) taşıyor. Ingress'iniz tools/list'i cache'li katmana, tools/call'u worker katmanına, gövdeye hiç dokunmadan gönderebilir:
Two spec details make the topology this clean. First, routing happens on headers now: every Streamable HTTP POST carries Mcp-Method (plus Mcp-Name for tool calls). Your ingress can send tools/list to a cached tier and tools/call to the worker tier without touching the body:
# list trafiği cache'lenebilir katmana# route list traffic to a cacheable layer - match: headers: Mcp-Method: { exact: "tools/list" } route: { destination: mcp-list-tier } - route: { destination: mcp-worker-tier }
İkincisi, liveness yine düz HTTP: protokol seviyesindeki ping kaldırıldı. Probe'lar için sıradan bir /healthz kullanın ve MCP endpoint'ini health-check yolunun dışında tutun; NestJS'te bu, @nestjs/terminus'un her zamanki işi.
Second, liveness is plain HTTP again: the protocol-level ping is removed. Use an ordinary /healthz for probes and keep the MCP endpoint out of the health-check path; in NestJS that is @nestjs/terminus doing what it always did.
Sunucunun kendisindeki en büyük sadeleşme, silebildikleriniz: initialize state'ini takip eden connection-lifecycle servisi ve Mcp-Session-Id okuyan her guard. Uzun ömürlü tek route kalıyor: eski GET endpoint'inin yerini alan tek POST stream'i subscriptions/listen. Ona cömert idle timeout verin; proxy'niz zorluk çıkarıyorsa küçük, ayrı bir tier'a yönlendirin.
The biggest simplification in the server itself is what you get to delete: the connection-lifecycle service tracking initialize state and every guard reading Mcp-Session-Id. One long-lived route remains: subscriptions/listen, the single POST stream that replaced the old GET endpoint. Give it generous idle timeouts, and route it to a small dedicated tier if your proxy makes that awkward.
NestJS'te bunu sıfırdan yazmak zorunda da değilsiniz: MCP-Nest, 2026-07-28 desteğini dual-era modda getiriyor — aynı /mcp endpoint'i, aynı @Tool metodlarından, hem eski protokolü hem stateless revizyonu aynı anda cevaplıyor (protocol: 'dual'). Bu, aşağıdaki migration checklist'inin "pencerede iki davranışı birden çalıştırın" maddesinin hazır implementasyonu; demo repo da bu kurulumu kullanıyor.
You do not have to write this from scratch in NestJS either: MCP-Nest ships 2026-07-28 support in dual-era mode — the same /mcp endpoint answers both the old protocol and the stateless revision from the same @Tool methods (protocol: 'dual'). It is a ready-made implementation of the migration checklist's "run both behaviors during the window" item; the demo repo uses this setup.
04Production'da auth: two-hop problemi bir yere gitmediAuth in production: the two-hop problem did not go away
Statelessness transport'u sadeleştirdi. Asıl on-call çağrısı üreten kısmı, yani authorization'ı hiç sadeleştirmedi. Production'daki bir MCP server iki OAuth ilişkisinin ortasında oturur ve bu ikisi tek ilişkiye indirgenmeyi reddeder:
Statelessness simplified transport. It did nothing to simplify the part that actually generates on-call pages: authorization. A production MCP server sits in the middle of two OAuth relationships, and they refuse to collapse into one:
iss'i doğrular (RFC 9207)authorize — the client validates iss on the response (RFC 9207)tools/call + Bearer — self-contained istektools/call + Bearer — self-contained requestresultType: completecall with the exchanged token → result → resultType: complete2026-07-28 sıkılaştırması hop 1'de geçerli: credential'lar onları üreten issuer'a bağlı — bir authorization server'dan alınmış credential başka birine karşı kullanılamaz, issuer değişirse yeniden kayıt zorunlu. Entegratörleri ısıracak deprecation ise şu: Dynamic Client Registration kapıya kadar geldi; yerini, client kimliğinin kendi kontrol ettiği bir URL olduğu Client ID Metadata Documents alıyor. Onboarding dokümanlarınız agent geliştiricilerine "DCR yapın" diyorsa, o yolu şimdiden taşımaya başlayın — pencere on iki ay.
The 2026-07-28 hardening applies on hop 1: credentials are bound to the issuer that minted them — a credential from one authorization server must never be replayed against another, and an issuer change forces re-registration. The deprecation that will bite integrators: Dynamic Client Registration is on its way out, replaced by Client ID Metadata Documents, where the client's identity is a URL it controls. If your onboarding docs tell agent developers to "do DCR", start migrating that path now — the window is twelve months.
05Retry'lar artık sizin probleminiz — ve doğru adres orasıRetries are now your problem — and that is the correct place for them
On-call odasının duvarına poster yapılacak değişiklik bu. 2025-11-25'te kopan bir SSE stream'i Last-Event-ID ile devam ettirilebilirdi. 2026-07-28'de resumability kaldırıldı: kopan response stream'i uçuştaki isteği kaybeder ve client bunu yeni bir request ID ile yeni bir istek olarak tekrar göndermek zorundadır.
This is the change we would put on a poster in the on-call room. Under 2025-11-25, a dropped SSE stream could be resumed with Last-Event-ID. Under 2026-07-28, resumability is removed: a broken response stream loses the in-flight request, and the client MUST re-issue it as a new request with a new request ID.
Bunu bir operatör olarak okuyun: bir tools/call sırasındaki her ağ hıçkırığı artık ikinci bir çalıştırma denemesi üretiyor — ilkine protokol seviyesinde hiçbir bağı olmadan. Rolling deploy sırasında bu bir edge case değil; ana senaryo. Üç sonuç:
Read that as an operator: every network blip during a tools/call now produces a second execution attempt with no protocol-level linkage to the first. During a rolling deploy that is not an edge case — it is the main case. Three consequences:
- Duplicate side effect, varsayılan arıza modu. Tool ticket açıyorsa ve stream, ticket yaratıldıktan sonra ama sonuç ulaşmadan koparsa, retry ikinci ticket'ı açar. Protokol sizi kurtarmayacak; tool contract'ınız kurtarmak zorunda — idempotency key deseni serinin contract yazısının ana argümanı.
- Duplicate side effects are the default failure mode. If a tool creates a ticket and the stream breaks after creation but before the result lands, the retry creates a second one. The protocol will not save you; your tool contract has to — the idempotency-key pattern is the core argument of this series' contract article.
- Rolling deploy'larınızı izleyin.
terminationGracePeriodSecondsdeğerini en yavaş tool'unuzun p99'unun üstüne kurun, SIGTERM'de stream'leri drain edin — ve her rollout'ta yine de bir retry patlaması bekleyin. - Watch your rolling deploys. Set
terminationGracePeriodSecondsabove your slowest tool's p99, drain streams on SIGTERM — and expect a retry burst at every rollout anyway. - Loglara korelasyon stratejisi lazım. Her retry'da yeni request ID demek, denemeleri birbirine bağlamak için protokole yaslanamamak demek. Trace context taşıyın — 2026-07-28,
_metaiçindetraceparent'ı resmileştirdi; OpenTelemetry tarafı serinin ayrı bir yazısı. - Request logs need a correlation strategy. A new request ID per retry means the protocol will not tie attempts together for you. Carry trace context — 2026-07-28 formalizes
traceparentin_meta; the OpenTelemetry side is its own article in this series.
Bu bir tahmin değil; ölçtük. Aşağıda 07'de tablonun tamamı var: sekiz crash'lik bir koşuda naif tool niyetlerin %1,37'sinde duplicate fatura üretti; aynı koşullarda idempotency key bunu %0,05'e indirdi.
This is not a prediction; we measured it. The full table is in section 07: across a run with eight crashes, the naive tool produced duplicate invoices on 1.37% of intents; under identical conditions an idempotency key cut that to 0.05%.
06Caching: spec nihayet CDN'in size yardım etmesine izin veriyorCaching: the spec finally lets a CDN help you
List cevapları artık bağlantıya göre değişmediği için, ilk kez dürüst birer HTTP kaynağı. CacheableResult bunu explicit yapıyor: her list cevabı ttlMs ve public/private değerli bir cacheScope taşıyor. Dikkat: varsayılan, muhafazakâr olan — ttlMs: 0, cacheScope: private. Yani caching'e bilinçli opt-in gerekiyor; demo repo'da bunu cacheHints ile yapıyoruz.
Because list responses no longer vary per connection, they are honest HTTP resources for the first time. CacheableResult makes it explicit: every list response carries ttlMs and a cacheScope of public/private. Note the default is the conservative one — ttlMs: 0, cacheScope: private — so caching is a deliberate opt-in; the demo repo does it via cacheHints.
tools/list:public, cömert TTL. Tool kataloğunuz deploy anında değişir, istek anında değil. TTL'i saatler mertebesine kurun, deploy'da bust edin.tools/list:public, generous TTL. Your tool catalog changes at deploy time, not request time. Set the TTL to hours, bust on deploy.- Tenant'a göre şekillenen her şey:
private. Entitlement'lar tool listesini çağırana göre filtreliyorsa,publicdeğeri performans şapkası takmış bir bilgi sızıntısıdır. - Anything tenant-shaped:
private. If entitlements filter the tool list per caller,publicis an information leak wearing a performance hat. - Tool'ları deterministik sırada dönün. Spec bunu artık açıkça öneriyor ve gerekçesi çok 2026: stabil sıralama, karşı taraftaki agent'ın LLM prompt cache'inin de tutması demek. Tek satır: sort.
- Return tools in a deterministic order. The spec now recommends it explicitly, and the reason is very 2026: stable ordering means the agent's LLM prompt cache hits too. One line: sort.
Paylaşımlı cache'in origin trafiğine etkisini demo repo'nun 4. parçasıyla ölçeceğiz; sonuç geldiğinde bu yazının changelog'u söyleyecek.
We will measure the shared cache's effect on origin traffic with part 4 of the demo repo; the changelog of this post will say when the numbers land.
07Ne ölçtükWhat we measured
Tüm sayılar demo repo'dan. Ortam konusunda dürüst olalım: lokal bir kind cluster'ı (tek node, Docker Desktop, Apple Silicon), pod başına 1 CPU limiti, k6 aynı makinede. Bu bir datacenter benchmark'ı değil — mekanizma kanıtı. Senaryolar ve analiz script'i repo'da; kendi ortamınızda tekrarlayabilirsiniz.
All numbers come from the demo repo. Let's be honest about the environment: a local kind cluster (single node, Docker Desktop, Apple Silicon), a 1-CPU limit per pod, k6 on the same machine. This is not a datacenter benchmark — it is mechanism evidence. The scenarios and the analysis script are in the repo; reproduce them on your own setup.
Sabit yük, 1'e karşı 3 replica (echo_upper, round-robin):
Steady load, 1 vs 3 replicas (echo_upper, round-robin):
| YükLoad | 1 replica · p95 / p99 | 3 replica · p95 / p99 |
|---|---|---|
| 100 rps · 60 sn | 6.7 ms / 21.1 ms | 7.4 ms / 30.8 ms |
| 600 rps · 45 sn | 14.2 ms / 51.6 ms | 16.2 ms / 52.4 ms |
Sizi şaşırtacak dürüst bulgu: bu iş yükünde yatay ölçeklemenin latency getirisi yok — ucuz bir tool'da tek Node process'i 600 rps'i tek başına karşılıyor. "Affinity yok" kazancını latency tablosunda değil, bir alttaki tabloda arayın: crash'ler sırasında servis ayakta kalıyor.
The honest finding that may surprise you: horizontal scaling buys no latency here — on a cheap tool, a single Node process handles 600 rps on its own. Look for the "no affinity" dividend not in the latency table but in the one below: the service stays up through crashes.
Pod-kill duplicate deneyi — 30 rps fatura niyeti, 90 sn, commit-sonrası 500 ms cevap penceresi; koşu başına 8 kez rastgele pod'lar crash ettirildi (POST /chaos/crash). Client, spec'in zorunlu kıldığı gibi kopan isteği yeni request-id ile tekrar gönderiyor:
Pod-kill duplicate experiment — 30 rps of invoice intents for 90 s, a 500 ms post-commit response window; 8 random pod crashes per run (POST /chaos/crash). The client re-issues broken requests with a new request ID, exactly as the spec mandates:
| KoşuRun | NiyetIntents | Retry | Duplicate faturaDuplicate invoices | OranRate |
|---|---|---|---|---|
create_invoice_naive | 2 700 | 132 | 37 | %1.37 |
create_invoice + idempotency_key | 2 108 | 1 121 | 1 | %0.05 |
Üç okuma: (1) Sekiz crash'e rağmen kaybolan niyet naifte 0/2700, key'lide 1/2108 — üç replica servis sürekliliğini taşıdı; ölçeklemenin gerçek getirisi bu. (2) Naif tool'da her 73 niyetten biri çift fatura kesti — %1,37 "nadir" gibi okunuyorsa, gününüzdeki tool çağrısı sayısıyla çarpın. (3) Key'li koşudaki tek duplicate, dürüstlüğün en öğretici satırı: crash, iş commit'i ile idempotency kaydının arasına denk geldi. Read-then-write idempotency atomik değildir; gerçek exactly-once istiyorsanız iş commit'i ile idem kaydını aynı transaction'a koyun — bu da serinin tool contract yazısının konusu.
Three readings: (1) Despite eight crashes, lost intents were 0/2700 naive and 1/2108 keyed — three replicas carried service continuity; that is the real scaling dividend. (2) The naive tool double-billed one in every 73 intents — if 1.37% reads as "rare", multiply it by your daily tool-call volume. (3) The single duplicate in the keyed run is honesty's most instructive row: the crash landed between the business commit and the idempotency record. Read-then-write idempotency is not atomic; if you want true exactly-once, put the commit and the idem record in one transaction — which is this series' tool-contract article.
08Migration checklist'iMigration checklist
- Session middleware'ini ve
Mcp-Session-Idokuyan her yeri silin; list endpoint'lerini bağlantıdan bağımsız yapın. - Delete session middleware and every read of
Mcp-Session-Id; make list endpoints connection-invariant. initializeişlemeyi kaldırın; her istekte_metaprotokol versiyonunu doğrulayın; uyuşmazlıkta-32022dönün.- Remove
initializehandling; validate the_metaprotocol version per request; return-32022on mismatch. server/discover'ı implemente edin (MUST) ve beyan ettiğiniz revizyon string'ini sabitleyin.- Implement
server/discover(a MUST) and pin the revision string you claim. - Çağrılar arası state'i TTL'li, tenant-scoped handle'lara taşıyın; her handle okumasını authorization açısından denetleyin.
- Move cross-call state into TTL'd, tenant-scoped handles; audit every handle read for authorization.
- GET endpoint'i ve
resources/subscribeyerinesubscriptions/listen; ona kendi timeout bütçesini verin. - Replace the GET endpoint and
resources/subscribewithsubscriptions/listen; give it its own timeout budget. - Health check'lerden protokol
ping'ini çıkarın;/healthzkullanın.Mcp-Method/Mcp-Namegönderin ve routing'i onlarla yapın. - Drop protocol
pingfrom health checks; use/healthz. SendMcp-Method/Mcp-Nameand route on them. - List sonuçlarına
ttlMs/cacheScopeekleyin; tool'ları deterministik sıralayın. - Add
ttlMs/cacheScopeto list results; sort tools deterministically. issdoğrulamasını ve issuer'a bağlı credential saklamayı zorunlu kılın; DCR → Client ID Metadata Documents geçişini planlayın.- Enforce
issvalidation and issuer-keyed credential storage; plan the DCR → Client ID Metadata Documents migration. - Bir rolling deploy'u yük altında test edin ve duplicate çalıştırmaları — bir agent sizin yerinize saymadan önce — kendiniz sayın.
- Load-test a rolling deploy and count duplicate executions yourself — before an agent does it for you.
09Sık sorulanlarFAQ
2026-07-28 mevcut client'ları kırar mı?initialize bekleyen eski client'lar, katı bir 2026-07-28 sunucusuna karşı başarısız olur. server/discover uyumluluk sondası olarak da çalışır; dış tüketiciniz varsa deprecation penceresinde iki davranışı birden çalıştırın (MCP-Nest'in dual-era modu tam olarak bu) ve hangi revizyonları kabul ettiğinizi yazın.
Does 2026-07-28 break existing clients?
Older clients expecting initialize fail against a strict 2026-07-28 server. server/discover doubles as a compatibility probe; if you have external consumers, run both behaviors during the deprecation window (MCP-Nest's dual-era mode is exactly this) and document which revisions you accept.
Protokol stateless ise Redis'e hâlâ ihtiyacım var mı?
Sadece gerçekten çağrılar arası state isteyen tool'lar için. Fark şu: maliyet artık her isteği session store'dan geçiren bir vergi değil, tool başına görünür bir kalem.
Do I still need Redis if the protocol is stateless?
Only for tools that genuinely need cross-call state. The difference: the cost is now a visible per-tool line item instead of a tax on every request through a session store.
MCP server'ımı sıradan bir CDN arkasına koyabilir miyim artık?public list endpoint'leri için evet — ttlMs/cacheScope bunun için var. Tool çağrıları cache'lenemez POST'lar olarak kalıyor.
Can I put my MCP server behind a regular CDN now?
For public list endpoints, yes — that is what ttlMs/cacheScope are for. Tool calls remain uncacheable POSTs.
SSE resumability'nin kaldırılması bir gerileme değil mi?
Kayıplı ağlar için tartışılır. Ama nadiren doğru implemente edilen bir sunucu özelliğini, explicit bir client retry sözleşmesiyle takas ediyor — ve idempotency'yi tool tasarımına, yani ait olduğu yere itiyor.
Isn't removing SSE resumability a regression?
For lossy networks, arguably. But it trades a rarely-implemented-correctly server feature for an explicit client retry contract — and pushes idempotency into tool design, where it belongs.
10Changelog
- 1 Ağustos 2026 — İlk yayın; MCP 2026-07-28 ve demo repo
v0.1.0ile doğrulandı. - August 1, 2026 — Initial publication; verified against MCP 2026-07-28 and demo repo
v0.1.0.