Bir MCP tool çağrısı dakikalarca sürdüğünde standart senkron JSON-RPC modeli seni zor durumda bırakır: bağlantı timeout'a takılır, istemci çökerse iş yarım kalır, ilerleme hakkında hiçbir sinyal gelmez. MCP Tasks nedir ve nasıl kullanılır sorusunun cevabı tam burada başlıyor — resmi io.modelcontextprotocol/tasks extension'ı, sunucunun anlık bir sonuç yerine kalıcı bir taskId döndürmesini sağlayarak bu üç sorunu aynı anda çözer. Bu rehberde yaşam döngüsünden input_required onay akışına, polling'den bildirim tabanlı güncellemelere kadar protokolün gerçek mekaniğini kod örnekleriyle inceleyeceğiz.
💡 Pro Tip: Sunucu pollIntervalMs döndürdüyse onu görmezden gelme — spesifikasyon istemcilerin bu aralığa uymasını "SHOULD" seviyesinde bekler; agresif polling sunucuyu gereksiz yere yorar.İçindekiler
- Neden Blocking Çalışmıyor
- Transport timeout'ları
- Crash resilience
- İlerleme görünürlüğü
- Task Yaşam Döngüsü ve Durumlar
- Capability Negotiation: Client ve Server Tarafı
- İstemci tarafı
- Sunucu tarafı
- CreateTaskResult, ttlMs ve pollIntervalMs
- ttlMs — görevin ömrü
- pollIntervalMs — önerilen sorgu aralığı
- input_required ile İnsan Onayı Akışı
- notifications/tasks ile Polling'den Kurtulma
- Cancellation: Kooperatif İptal
- Client Desteği: Bugün Kim Destekliyor?
- Uzun Build/Deploy Aracını Tasks'a Taşımak
- SSS
- MCP Tasks nedir ve ne zaman kullanılmalı?
- Uzun süren bir MCP tool çağrısında timeout'tan nasıl kaçınılır?
- input_required durumu nasıl ele alınır?
- Client çöktüğünde MCP task'ı kaldığı yerden devam eder mi?
- Tasks kullanmak için hangi client'ların desteği gerekir?
- Güncelleme (Eylül 2026)
- Sonuç
- Kaynaklar
Neden Blocking Çalışmıyor
MCP araç çağrıları varsayılan olarak senkron bir JSON-RPC request/response döngüsüdür: istemci ister, sunucu bağlantıyı açık tutar, sonucu üretir üretmez döner. CI pipeline'ları, toplu veri işleme veya dış bir job sistemini saran araçlar için bu model üç ayrı noktada kırılır.
Transport timeout'ları
Resmi dokümantasyon bunu net biçimde tanımlıyor: birçok istemci ve transport ara katmanı, birkaç saniyeyi aşan bağlantıları pratik olmaktan çıkaran timeout'lar dayatır. Bağlantıyı dakikalarca açık tutmayı transport katmanına güvenerek çözmeye çalışmak kırılgan bir tasarımdır — ara proxy'ler, load balancer'lar veya istemci tarafındaki HTTP client'lar senin kontrolünde değildir.
Crash resilience
Bir görev sürerken istemci bağlantıyı kaybedebilir, sekme kapanabilir, süreç yeniden başlatılabilir. Tasks extension'ında taskId kalıcı (durable) bir handle'dır — istemci ister aynı oturumda ister tamamen yeni bir bağlantıyla, aynı ID'yi kullanarak polling'e kaldığı yerden devam edebilir. İş sunucu tarafında bağımsız sürer, istemcinin varlığına bağımlı değildir.
İlerleme görünürlüğü
Senkron modelde istemci "hâlâ çalışıyor mu, takılı mı kaldı?" sorusuna hiçbir cevap alamaz. Tasks, görevin durum meta verisini (working, input_required, completed, failed, cancelled) ve opsiyonel durum mesajlarını taşıyarak bu görünürlüğü protokol seviyesinde sağlar. Şemadaki statusMessage alanı opsiyoneldir ve her durum için insan-okur bir açıklama taşıyabilir: working için ilerleme tarifi, input_required için işin neye takıldığı, failed için tanı bilgisi. Toplu işlerde ilerlemeyi raporlayan da bu alandır — istemci arayüzünde "iş sürüyor" veya "onayını bekliyorum" gibi net bir durum gösterebilirsin.
Task Yaşam Döngüsü ve Durumlar
Bir task, sunucu tarafından oluşturulduğu andan itibaren beş durumdan birinde bulunur. Bunlardan üçü terminal'dir: bir kez ulaşıldığında görevin durumu bir daha değişmez.
Durum | Anlamı | Terminal mi? |
|---|---|---|
working | Sunucu görevi hâlâ işliyor | Hayır |
input_required | Görev, istemciden ek girdi bekliyor (ör. elicitation onayı) | Hayır |
completed | Görev başarıyla tamamlandı, sonuç hazır | Evet |
failed | Görev hata ile sonlandı | Evet |
cancelled | Görev istemci talebiyle iptal edildi | Evet |
Terminal durumlardan birine ulaşan bir task'ın durumu bir daha değişmez — TTL dolmadığı sürece istemci tasks/get ile tekrar sorgulasa bile aynı terminal durumu ve aynı sonucu görür. Bu, polling mantığını basitleştirir: istemci terminal bir durum gördüğü anda polling döngüsünü güvenle sonlandırabilir.
Capability Negotiation: Client ve Server Tarafı
Tasks, MCP'nin standart extension negotiation mekanizmasını kullanır — protokole özel bir "task modu" flag'i icat edilmemiştir. Extension'ın kimliği io.modelcontextprotocol/tasks string'idir ve iki taraf da bunu ayrı ayrı bildirmek zorundadır.
İstemci tarafı
İstemci, göndereceği her istekte per-request capability'ler arasına io.modelcontextprotocol/tasks extension'ını dahil eder. Bu, "ben task-aware bir istemciyim, sonuç yerine bir taskId dönerse anlarım" sinyalidir.
Sunucu tarafı
Sunucu da kendi server/discover yanıtında aynı extension kimliğini listeler. Şema tarafında bu capability'nin gövdesi kasıtlı olarak boştur:
typescript
1type TasksExtensionCapability = Record<string, never>;2// Boş bir obje desteği belirtir; extension'a özel3// herhangi bir ayar şu an tanımlı değildir.Task desteği her iki taraftan da açık opt-in gerektirir — yalnızca istemci ya da yalnızca sunucu extension'ı bildirmesi yetmez, ikisi de aynı capability kimliğini duyurmalıdır.
- Extension Identifier:
io.modelcontextprotocol/tasks— hem istemci hem sunucu tarafında aynı string. - Per-request capability: İstemci, task desteğini genel bir handshake yerine istek bazında bildirir.
- server/discover: Sunucunun extension desteğini duyurduğu standart keşif yanıtı.
CreateTaskResult, ttlMs ve pollIntervalMs
Sunucu bir isteği asenkron işlemeye karar verdiğinde, standart sonuç şekli yerine resultType: "task" alanı taşıyan bir CreateTaskResult döner. Şema tanımı bunu Result & Task birleşimi olarak ifade eder — yani hem genel MCP Result alanlarını hem de task'a özgü alanları (taskId, status, statusMessage, createdAt, lastUpdatedAt, ttlMs, pollIntervalMs) aynı anda taşır. createdAt ve lastUpdatedAt, şemada ISO 8601 zaman damgası olarak zorunlu alanlardır.
json
1{2 "jsonrpc": "2.0",3 "id": 42,4 "result": {5 "resultType": "task",6 "taskId": "task_8f2a1c",7 "status": "working",8 "createdAt": "2026-08-12T09:14:02Z",9 "lastUpdatedAt": "2026-08-12T09:14:02Z",10 "ttlMs": 1800000,11 "pollIntervalMs": 500012 }13}ttlMs — görevin ömrü
ttlMs alanı, task'ın oluşturulma anından itibaren milisaniye cinsinden yaşam süresidir. Değer null ise sınırsız demektir; bir sayı verilmişse sunucu bu süre dolduktan sonra task'ı çöpe atabilir (discard). Yukarıdaki örnekte 1800000 ms, yani 30 dakikalık bir TTL tanımlanmış — istemci bu süreden sonra aynı taskId ile sorgu yaparsa sonuç alamayabilir.
pollIntervalMs — önerilen sorgu aralığı
pollIntervalMs, sunucunun istemciye önerdiği polling aralığıdır. Spesifikasyon dili "SHOULD" seviyesindedir: istemcilerin sunucuyu gereksiz yere yormamak için bu değere uyması beklenir, ancak zorunlu (MUST) değildir. Alan opsiyoneldir — sunucu döndürmezse istemci kendi makul aralığını seçer.
- taskId: Görevi tekil biçimde tanımlayan,
tasks/getvetasks/cancelçağrılarında kullanılan kalıcı kimlik. - Durable creation: Task, yanıt istemciye gönderilmeden önce sunucu tarafında kalıcı olarak oluşturulmuş olmalıdır — yanıt geldiğinde task zaten "var"dır.
input_required ile İnsan Onayı Akışı
Bazı görevler ortasında insan onayına ihtiyaç duyar — bir elicitation, bir "bu işlemi onaylıyor musun?" sorusu, ya da eksik bir parametre. Bu durumda task input_required durumuna geçer ve bekleyen istekleri inputRequests map'inde taşır. Anahtarlar (key) keyfi tanımlayıcılardır; amaçları isteği ilgili yanıtla eşleştirmektir. Değerler ise kısaltılmış bir özet değil, tam JSON-RPC istek nesneleridir: şema InputRequest tipini CreateMessageRequest | ListRootsRequest | ElicitRequest birleşimi olarak tanımlar, yani method ve params alanlarıyla gelirler.
json
1{2 "jsonrpc": "2.0",3 "id": 43,4 "result": {5 "resultType": "complete",6 "taskId": "task_8f2a1c",7 "status": "input_required",8 "createdAt": "2026-08-12T09:14:02Z",9 "lastUpdatedAt": "2026-08-12T09:16:41Z",10 "ttlMs": 1800000,11 "inputRequests": {12 "confirm_deploy": {13 "method": "elicitation/create",14 "params": {15 "mode": "form",16 "message": "Prod deploy'u onaylıyor musun?",17 "requestedSchema": {18 "type": "object",19 "properties": {20 "approve": { "type": "boolean" }21 },22 "required": ["approve"]23 }24 }25 }26 }27 }28}tasks/get yanıtının resultType alanı şema gereği "complete" olmak zorundadır — "task" yalnızca görevin ilk oluşturulduğu CreateTaskResult yanıtına aittir.
İstemci bu isteğe ikinci bir bağlantı açmadan veya sunucudan beklenmedik bir mesaj almadan, doğrudan tasks/update çağrısıyla cevap verir:
json
1{2 "jsonrpc": "2.0",3 "id": 44,4 "method": "tasks/update",5 "params": {6 "taskId": "task_8f2a1c",7 "inputResponses": {8 "confirm_deploy": {9 "action": "accept",10 "content": { "approve": true }11 }12 }13 }14}Yanıt tarafı da simetriktir: InputResponse tipi CreateMessageResult | ListRootsResult | ElicitResult birleşimidir, bu yüzden bir elicitation yanıtı action ve content alanlarıyla döner. Şema kuralı açık: inputResponses içindeki her anahtar, o an bekleyen (outstanding) bir inputRequests anahtarına karşılık gelmek zorundadır. Bu mekanizma, polling döngüsünü koparmadan insan-döngüde (human-in-the-loop) akışlar kurmanı sağlar — deploy onayı, riskli bir migration'ı çalıştırma izni veya belirsiz bir parametreyi netleştirme gibi senaryolar için doğal bir çözümdür.
- inputRequests: Görev yürütülürken karşılanması gereken server-to-client isteklerinin haritası.
- tasks/update: İstemcinin bekleyen input isteklerine cevap verdiği method.
notifications/tasks ile Polling'den Kurtulma
Polling, Tasks extension'ının varsayılan davranışıdır — istemci pollIntervalMs kadar bekler, tasks/get ile sorgular, tekrar bekler. Ancak sunucu isterse durum değişikliklerini aktif olarak da itebilir. Bildirim parametreleri şemada NotificationParams & DetailedTask olarak tanımlıdır: her bildirim eksiksiz bir DetailedTask taşır, bu yüzden completed bir bildirimde result alanı da gelir ve fazladan bir tasks/get turuna gerek kalmaz.
json
1{2 "jsonrpc": "2.0",3 "method": "notifications/tasks",4 "params": {5 "taskId": "task_8f2a1c",6 "status": "completed",7 "createdAt": "2026-08-12T09:14:02Z",8 "lastUpdatedAt": "2026-08-12T09:21:55Z",9 "ttlMs": 1800000,10 "result": {11 "content": [{ "type": "text", "text": "Deploy tamamlandı." }]12 }13 }14}İstemcinin bu bildirimleri alabilmesi için subscriptions/listen mekanizması üzerinden abone olması gerekir; TaskSubscriptionNotifications şemasındaki taskIds alanı, hangi görev kimlikleri için bildirim istendiğini belirtir. Sunucu tarafında da bunun karşılığı vardır: TaskSubscriptionAcknowledgedNotifications.taskIds, sunucunun hangi görev kimlikleri için durum bildirimi göndermeyi kabul ettiğini bildirir. Polling varsayılan davranıştır; sunucu bildirimleri destekliyorsa istemci polling yerine onlara güvenebilir. Yani bildirimler polling'in yerini almaz, onu opsiyonel hale getirir: sunucu destekliyorsa istemci tasks/get round-trip'lerinden kurtulur, desteklemiyorsa hiçbir şey kırılmaz.
Pratikte bu, kısa TTL'li ve sık kontrol edilmesi gereken görevlerde (ör. saniyeler süren ama yine de asenkron işlenen bir işlem) ağ trafiğini belirgin şekilde azaltır — her pollIntervalMs turunda yeni bir tasks/get isteği göndermek yerine istemci tek bir abonelikle beklemeye geçer.
Cancellation: Kooperatif İptal
İstemci, tasks/cancel çağrısıyla bir görevi her an iptal etmeye çalışabilir. Ancak bunun sunucu tarafında ne anlama geldiğini net biçimde bilmelisin: iptal kooperatiftir. Sunucu iptal niyetini kabul eder ama işi gerçekten durdurmakla yükümlü değildir. Resmi uygulama rehberi şöyle diyor: sunucu iptal isteklerini boş bir sonuçla onaylar ve mümkün olduğunda yerine getirir, ama iptal kooperatif olduğu için görev yine de cancelled dışında bir terminal duruma ulaşabilir — altta yatan iş (ör. bir bulut deployment job'ı) zaten geri döndürülemez bir noktaya gelmişse, task pekâlâ completed ya da failed olarak kapanabilir. Bu, dış bir job sistemini saran araçlar yazarken özellikle önemlidir: tasks/cancel çağrısının "işi anında durdurduğu" varsayımıyla tasarım yapma.
Client Desteği: Bugün Kim Destekliyor?
Tasks, ana MCP spesifikasyonunda resmi bir extension olarak listelenir — ama "spec'te resmi olmak" ile "her istemcide çalışıyor olmak" farklı şeylerdir. Resmi client desteği tablosunda şu an üç extension listeleniyor; Tasks bunların arasında değil:
Extension | Client desteği tablosunda listeli mi? |
|---|---|
MCP Apps | Evet |
OAuth Client Credentials | Evet |
Enterprise-Managed Authorization | Evet |
Tasks | Hayır |
Tasks bu tabloda henüz bir satıra sahip değil. Bu, extension'ın çalışmadığı anlamına gelmez — spec seviyesinde tanımı ve şeması nettir — ama hangi istemcilerin io.modelcontextprotocol/tasks capability'sini gerçekten implemente ettiğini varsaymak yerine, kullandığın istemcinin kendi dokümantasyonunda veya server/discover yanıtında doğrulaman gerekir. Bir sunucu yazıyorsan, negotiation başarısız olma ihtimaline karşı task-aware olmayan istemcilerle de test etmeyi ihmal etme.
Uzun Build/Deploy Aracını Tasks'a Taşımak
Tasks'ın en doğal kullanım alanı, zaten kendi job ID'sini kullanan dış bir sistemi saran araçlardır: bulut deployment'ları, kuyruğa alınan işler, uzun süren async API'ler. Mantık şu: job'u oluşturduğunda bir task döndür, iş tamamlandığında task'ı çöz (resolve).
typescript
1// Sunucu tarafı — dış job sistemini task'a sar2type DeployParams = { service: string; ref: string };3type Job = {4 id: string;5 createdAt: string;6 finished: boolean;7 succeeded: boolean;8};9 10declare const cloudProvider: {11 createDeployment(params: DeployParams): Promise<Job>;12 getDeployment(id: string): Promise<Job>;13};14 15const DEPLOY_TTL_MS = 3_600_000;16 17async function handleDeployTool(params: DeployParams) {18 const job = await cloudProvider.createDeployment(params);19 return {20 resultType: "task" as const,21 taskId: job.id,22 status: "working" as const,23 createdAt: job.createdAt,24 lastUpdatedAt: job.createdAt,25 ttlMs: DEPLOY_TTL_MS,26 pollIntervalMs: 8000,27 };28}29 30async function handleTasksGet(taskId: string) {31 const job = await cloudProvider.getDeployment(taskId);32 const base = {33 resultType: "complete" as const,34 taskId,35 createdAt: job.createdAt,36 lastUpdatedAt: new Date().toISOString(),37 ttlMs: DEPLOY_TTL_MS,38 };39 40 // Terminal olmayan durum: yalnızca status; result/error yok.41 if (!job.finished) {42 return { ...base, status: "working" as const };43 }44 45 // CompletedTask: result ZORUNLU.46 if (job.succeeded) {47 return {48 ...base,49 status: "completed" as const,50 result: {51 content: [{ type: "text", text: "Deploy " + taskId + " tamamlandı." }],52 },53 };54 }55 56 // FailedTask: error ZORUNLU.57 return {58 ...base,59 status: "failed" as const,60 error: { code: -32000, message: "Deploy " + taskId + " başarısız oldu." },61 };62}Bu desen, kendi altyapında zaten var olan bir job ID'sini MCP task ID'si olarak yeniden kullanmanı sağlar — ayrı bir durum makinesi icat etmene gerek kalmaz. İstemci tarafında da fark aynı ölçüde büyüktür: uzun bir deploy artık tek bir bloklu istek değil, ilerlemesi izlenebilen, crash'e dayanıklı, gerektiğinde iptal edilebilen bir görevdir.
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
Okuyucu Ödülü
Kendi MCP sunucunda Tasks extension'ını devreye almadan önce hızlıca gözden geçirebileceğin bir kontrol listesi hazırladım — hem capability negotiation hem de görev yaşam döngüsü tarafını kapsıyor.
SSS
MCP Tasks nedir ve ne zaman kullanılmalı?
MCP Tasks (io.modelcontextprotocol/tasks), bir sunucunun anlık bir sonuç yerine kalıcı bir taskId döndürmesini sağlayan resmi MCP extension'ıdır. CI pipeline'ları, toplu veri işleme, insan onayı gerektiren adımlar, dakikalar veya saatler süren dış job sistemleri (job ID döndüren bulut API'leri) ve bağlantısı kararsız olabilecek istemciler için kullanılması önerilir.
Uzun süren bir MCP tool çağrısında timeout'tan nasıl kaçınılır?
Sunucu isteği bloklamak yerine resultType: "task" alanı taşıyan bir CreateTaskResult döner; bu yanıt taskId, başlangıç durumu, TTL ve önerilen polling aralığını içerir. İstemci bağlantıyı kapatıp daha sonra tasks/get ile periyodik sorgu yapar — bağlantı ara katmanlarının dayattığı kısa timeout'lar bu sayede devre dışı kalır, çünkü artık tek bir uzun bağlantıya değil, kısa ve tekrarlanan sorgulara güveniyorsun.
input_required durumu nasıl ele alınır?
Görev insan onayı gibi ara girdi gerektirdiğinde durum input_required'a geçer; tasks/get yanıtı bekleyen istekleri inputRequests map'inde taşır. İstemci bunlara tasks/update çağrısıyla, inputResponses alanında ilgili anahtarları doldurarak cevap verir. Şema, inputResponses içindeki her anahtarın o an bekleyen bir inputRequests anahtarına karşılık gelmesini zorunlu kılar — böylece zaten yanıtlanmış veya hiç var olmamış bir isteğe yanıt gönderilmesi şema seviyesinde engellenir.
Client çöktüğünde MCP task'ı kaldığı yerden devam eder mi?
Evet — taskId kalıcı bir handle'dır; iş sunucu tarafında sürerken istemci bağlantıyı kaybetse veya yeniden başlasa bile aynı ID ile tasks/get çağrısı yapıp polling'e devam edebilir. Bu, resmi dokümantasyonda "crash resilience" olarak tanımlanan tasarım amacıdır. İptal (tasks/cancel) ise ayrı bir konudur ve kooperatiftir — sunucu isteği kabul eder ama işi anında durdurmak zorunda değildir.
Tasks kullanmak için hangi client'ların desteği gerekir?
Hem istemci hem sunucu tarafı io.modelcontextprotocol/tasks capability'sini ayrı ayrı bildirmelidir. Ana spesifikasyon Tasks'ı resmi extension olarak listelese de, resmi client desteği tablosunda şu an Tasks'a ait bir satır yok — kullandığın istemcinin desteğini varsaymak yerine kendi ortamında doğrulaman gerekir.
Güncelleme (Eylül 2026)
Bu rehberin yayımlanmasından sonra resmi destek matrisi değişti: 8 Eylül 2026'da MCP Client Support Matrix sayfasının "Extension overview" tablosuna dördüncü bir satır olarak Skills over MCP (io.modelcontextprotocol/skills) eklendi; değişikliği getiren commit'in başlığı "docs: add Skills extension overview and support tracking" ve sayfanın güncel dateModified değeri 13 Eylül 2026'dır. Tasks'ın bu tabloda hâlâ kendi satırı yok — yani yukarıdaki "client desteğini varsayma, doğrula" tavsiyesi bugün de aynen geçerli.
Sonuç
Tasks, MCP'nin senkron request/response varsayımını kırmadan uzun süren işleri protokole dahil etmenin resmi yoludur: durable bir taskId, net bir durum makinesi, TTL ve polling aralığı için açık sözleşmeler, insan onayını akışa dahil eden input_required mekanizması ve polling'i opsiyonel hale getiren bildirimler. Extension'ı devreye almadan önce capability negotiation'ı her iki tarafta da doğru kurduğundan ve client desteğini varsaymadan test ettiğinden emin ol.
MCP'nin bu dönemki değişikliklerini daha geniş bağlamda görmek istersen MCP'nin stateless geçiş rehberine ve aynı spesifikasyon döneminde kaldırılan yeteneklere odaklanan roots/sampling/logging geçiş planına göz atabilirsin. Protokolün genel entegrasyon tarafı için MCP ile AI entegrasyonu rehberi ve Claude Code'un MCP kullanımı için Claude Code MCP rehberi faydalı olacaktır. Uzun süren işleri bir agent döngüsü seviyesinde ele almakla ilgileniyorsan — Tasks'ın çözdüğü protokol seviyesindeki sorundan farklı olarak, agent'ın kendi karar döngüsü seviyesinde — agentic AI tool-use planner loop rehberine bakabilirsin.
Kaynaklar
- MCP Tasks — Overview — extension'ın resmi tanımı, yaşam döngüsü, capability negotiation ve kullanım senaryoları.
- MCP Specification 2026-07-28 — Tasks'ı resmi extension olarak listeleyen ana protokol spesifikasyonu.
- ext-tasks reposu (GitHub) — Tasks extension'ının şema ve referans tanımlarını barındıran resmi repo.
- ext-tasks README — extension'ın kimliği, şema sürümleri ve kapsamı.
- MCP Client Support Matrix — hangi extension'ların hangi istemcilerde desteklendiğini gösteren resmi tablo.

