MCP ekosisteminde dolaşırken şöyle bir his oluşuyor: bu iş TypeScript ve Python geliştiricileri için tasarlanmış. Örnekler npx ile başlıyor, blog yazıları uv kuruyor, konferans sunumlarında ekranda Node var.
Oysa Türkiye'deki kurumsal yazılım dünyasının önemli bir kısmı .NET üzerinde koşuyor. Bankada, sigortada, lojistikte, kamuya iş yapan entegratörlerde on beş yıllık ASP.NET kod tabanları var. O kod tabanının yanına Node.js süreci koymak isteyen kimse yok.
İyi haber: C# resmî SDK'lardan biri ve yeni durumsuz protokol modeli ASP.NET Core ile MCP sunucusu barındırmayı gerçekten kolaylaştırdı. Bu yazıda sıfırdan çalışan bir sunucu kuruyoruz, sonra kurumsal ortamda karşınıza çıkacak şeyleri konuşuyoruz.
Neden .NET tarafında hayat kolaylaştı
Eski MCP modelinde sunucu, istemciye bir oturum kimliği veriyordu ve sonraki her istek o kimliği taşıyordu. Bu, uygulamayı üreten instance'a bağlıyordu. App Service'te üç instance'a scale ettiğinizde ya sticky routing kurmanız ya da paylaşımlı bir oturum deposu yazmanız gerekiyordu.
28 Temmuz 2026 için hazırlanan sürüm adayıyla protokol katmanındaki oturum kaldırılıyor. initialize handshake'i yok, Mcp-Session-Id başlığı yok. Her istek kendi kendine yeten bir POST.
.NET tarafında bunun anlamı şu: MCP sunucunuz artık sıradan bir stateless Web API. Aynı health check'ler, aynı yatay ölçekleme, aynı deployment slot mantığı, aynı Application Insights kurulumu. Özel bir şey yok. Ve bu iyi bir şey.
Kurulum
Boş bir web projesiyle başlıyoruz.
dotnet new web -n Kurumsal.Mcp
cd Kurumsal.Mcp
dotnet add package ModelContextProtocol.AspNetCore --prerelease
Prerelease bayrağı şu an gerekli, çünkü C# SDK'sı 2026-07-28 sürümünü hedefleyen beta aşamasında. Tier 1 SDK'ların final spesifikasyon penceresinde destek yayınlaması bekleniyor.
Program.cs tarafı beklediğinizden kısa:
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddMcpServer()
.WithHttpTransport()
.WithToolsFromAssembly();
var app = builder.Build();
app.MapMcp("/mcp");
app.Run();
WithToolsFromAssembly() çağrısı assembly'yi tarayıp attribute ile işaretlenmiş tool'ları topluyor. Bu, klasik ASP.NET controller keşfine benziyor ve ekipteki insanlar için tanıdık geliyor.
İlk tool
Diyelim ki bir kargo takip sisteminiz var ve modelin gönderi durumunu sorgulayabilmesini istiyorsunuz.
using System.ComponentModel;
using ModelContextProtocol.Server;
[McpServerToolType]
public class KargoAraclari
{
private readonly IKargoServisi _servis;
public KargoAraclari(IKargoServisi servis) => _servis = servis;
[McpServerTool(Name = "gonderi_durumu")]
[Description("Takip numarasına göre bir gönderinin güncel durumunu döner.")]
public async Task<GonderiDurumu> GonderiDurumuAsync(
[Description("14 haneli takip numarası")] string takipNo,
CancellationToken ct)
{
return await _servis.DurumGetirAsync(takipNo, ct);
}
}
Burada dikkat edilecek iki şey var.
Birincisi, dependency injection çalışıyor. Tool sınıfınız normal bir servis gibi container'dan besleniyor. Mevcut repository'lerinizi, HttpClient factory'nizi, DbContext'inizi olduğu gibi kullanabiliyorsunuz. MCP sunucusu kod tabanınızın üzerine ince bir katman olarak oturuyor, paralel bir dünya kurmuyorsunuz.
İkincisi, Description attribute'ları dekorasyon değil. Modelin tool'u ne zaman ve nasıl çağıracağına dair tek bilgi kaynağı bunlar. "Kargo sorgular" yazmayın. Ne alır, ne döner, hangi durumda kullanılır, hangi durumda kullanılmaz yazın. Bir tool açıklamasını yazarken kafanızda canlandırmanız gereken şey, dokümantasyon değil, işe yeni başlamış bir stajyere bırakılan not.
Şemaları ciddiye alın
Yeni sürümle birlikte tool şemaları tam JSON Schema 2020-12'ye yükseltildi. Input şemaları hâlâ kökte type: "object" olmak zorunda ama artık oneOf, anyOf, allOf kompozisyonuna, koşullara ve $ref referanslarına izin veriyor.
C# tarafında bu, kayıt tiplerinizin ve enum'larınızın çok daha sadık şekilde yansıtılabilmesi demek. Örneğin "ya müşteri numarası ya vergi numarası, ikisi birden değil" gibi bir kısıtı artık şemada ifade edebiliyorsunuz, tool gövdesinde elle doğrulamak zorunda değilsiniz.
Bir güvenlik notu: dış $ref URI'lerini otomatik çözmeyin, spesifikasyon bunu açıkça yasaklıyor. Şema derinliğini ve doğrulama süresini sınırlayın.
Kurumsal ortamda gerçekte karşılaşacaklarınız
Buraya kadarı demo. Şimdi asıl kısım.
Gateway ve yönlendirme
Yeni transport artık Mcp-Method ve Mcp-Name başlıklarını zorunlu kılıyor. Bu, Türkiye'deki kurumsal ortamlar için beklediğinizden önemli. Çoğu banka ve sigorta şirketinde trafiğin önünde bir API gateway var ve o gateway'de "gövdeyi açıp bakma" politikası genelde yasak.
Artık gerekmiyor. Rate limiting kuralınızı doğrudan Mcp-Method: tools/call üzerine kurabilirsiniz. Belirli bir tool'u kısıtlamak isterseniz Mcp-Name yeterli. Sunucu, başlıkla gövdenin uyuşmadığı istekleri reddediyor, dolayısıyla bu başlıklara güvenebilirsiniz.
Kimlik doğrulama
Bu sürümde altı SEP yetkilendirme tarafını OAuth 2.0 ve OpenID Connect'in gerçek dünyada nasıl kurulduğuna yaklaştırdı. .NET tarafında iyi haber şu ki Microsoft.AspNetCore.Authentication.JwtBearer ile kurduğunuz mevcut yapı büyük ölçüde geçerli.
Dikkat edilecek noktalar:
- İstemciler artık RFC 9207 uyarınca yetkilendirme yanıtlarındaki
issparametresini doğrulamak zorunda. Kendi Identity Server veya Duende kurulumunuz varsaissgönderdiğinden emin olun. - İstemciler Dynamic Client Registration sırasında OpenID Connect
application_typedeğerini bildiriyor. Bu, masaüstü ve CLI istemcilerinin localhost redirect URI'lerinin reddedilmesi sorununu çözüyor. - Durumsuz modelde token doğrulaması istek başına bir kez yapılıyor, oturum bağlamı yok. Bu, per-agent kapsamlı kimlik bilgilerini uygulamayı kolaylaştırıyor.
Ölçekleme
Söylenecek fazla bir şey yok, ki asıl mesele bu. App Service veya Kubernetes fark etmez, üç replika açın, önüne düz round robin koyun, bitti. Sticky session'a, paylaşımlı Redis oturum deposuna, gateway'de deep packet inspection'a ihtiyaç yok.
Uygulama seviyesinde durum tutmanız gerekiyorsa açık tanıtıcı deseni kullanın. Bir tool sepet_olustur çağrısında sepet_id üretsin, model onu urun_ekle çağrısında argüman olarak geri versin. Durumu Redis'te veya SQL'de siz yönetin, ama protokolün sizin adınıza yönetmesini beklemeyin.
Gözlemlenebilirlik
Logging özelliği deprecate edildi. Yerine yapılandırılmış gözlemlenebilirlik için OpenTelemetry öneriliyor, stdio transport için stderr.
.NET tarafında bu aslında bir kazanç. OpenTelemetry.Extensions.Hosting zaten olgun ve W3C Trace Context yayılımı artık _meta içinde belgelendi. traceparent, tracestate ve baggage anahtar isimleri sabitlendiği için, Claude Desktop'ta başlayan bir çağrı MCP sunucunuzdan geçip arkadaki SQL Server'a kadar tek bir span ağacı olarak Application Insights'ta görünüyor.
Bu, "model neden bu tool'u çağırdı ve neden yavaştı" sorusuna cevap verebilmenin tek makul yolu.
Önbellekleme
Liste sonuçları artık ttlMs ve cacheScope taşıyor. Tool listeniz nadiren değişiyorsa ttlMs değerini cömert tutun. cacheScope ise dikkat isteyen kısım: tool listesi kullanıcıya göre değişiyorsa (ki kurumsal yetkilendirmede sık olur) bunu kullanıcılar arası paylaşılabilir işaretlemeyin.
Tasks kullanıyorsanız
Uzun süren işlemler için Tasks özelliğini kullanan bir sunucunuz varsa, yeniden yazmanız gerekecek. Tasks çekirdek protokolden çıkıp uzantıya taşındı. Yeni yaşam döngüsünde sunucu tools/call çağrısına bir task handle ile cevap veriyor, istemci tasks/get, tasks/update ve tasks/cancel ile ilerletiyor. Task oluşturma kararı sunucuya ait: istemci uzantıyı desteklediğini bildiriyor, hangi çağrının task olarak koşacağına sunucu karar veriyor.
tasks/list tamamen kaldırıldı, çünkü oturum olmadan güvenli şekilde kapsamlandırılamıyor.
.NET dünyasında uzun işlemler için zaten Hangfire veya Azure Functions kullanıyorsanız, Tasks uzantısını o altyapının önüne ince bir kabuk olarak düşünün.
Kapanış
MCP'nin ilk yılında C# geliştiricisi olmak biraz ikinci sınıf vatandaşlık gibiydi. Örnekler başka dildeydi, SDK geriden geliyordu, blog yazıları sizden bahsetmiyordu.
Durumsuz protokole geçişle birlikte bu ayrım büyük ölçüde kapandı. Çünkü protokolün sizden istediği şey artık egzotik değil: kimlik doğrulaması yapan, başlıkları okuyan, JSON dönen bir HTTP endpoint'i. .NET bunu on beş yıldır iyi yapıyor.
Kurumsal bir ortamda mevcut sisteminizin üzerine MCP koymayı düşünüyorsanız, bugün başlamak için iyi bir gün.
Bir sonraki yazıda aynı sunucuyu KVKK açısından ele alacağım: kurumsal veriyi bir dil modeline açtığınızda hukuken ne olur.
