Azure Functions’ta MCP Apps: TypeScript ile Hızlı Başlangıç
Geçen hafta bir tanıdık sordu: “Şu MCP olayı sadece metin döndüren API’ler mi? Yanı ChatGPT’ye bir tool bağladım, bana JSON attı, o da text’e çevirdi, olay bu mu?” Haklıydı aslında. Uzun süre MCP dünyası tam da böyleydi; düz metin, biraz markdown, en fazla ufak bir tablo. Şimdi iş değişiyor.
MCP Apps ile artık sunucu tarafında bir “widget” tanımlayıp bunu Copilot’ın, Claude’un ya da ChatGPT’nın içinde interaktif HTML olarak gösterebiliyorsunuz. Yanı kullanıcı hava durumunu sorunca dönen şey artık sadece “Ankara 12°C, parçalı bulutlu” cümlesi olmuyor; üstüne tıklanabilen, saatlik grafikleri olan mini bir uygulama geliyor. Garip ama güzel. Bunu Azure Functions üzerinde host etmek de, açık konuşayım, sandığınız kadar uğraştırmıyor.
Kısa bir not düşeyim buraya.
Bu yazıda hem konsepti anlatacağım hem de Türkiye’deki kurumsal projeler açısından bunun nereye oturduğunu paylaşacağım. Yer yer eleştirim de olacak, çünkü kağıt üstünde tatlı görünen bu mimarinin sahada bazı pürüzleri var. Evet, baya var.
MCP Apps aslında ne çözüyor?
Bakın şimdi, klasik MCP tool’ları şöyle çalışıyordu: model bir fonksiyon çağırıyor, sız JSON dönduruyorsunuz, model de önü kullanıcıya cümleye çeviriyor. Basit senaryoda fena değil. Ama iş biraz karışınca — mesela onay ekranı, grafik, harita — sistem hemen tökezliyordu.
MCP Apps burada devreye giriyor. MCP sunucusu artık iki tür şey döndürebiliyor:
- Tools — klasik iş mantığı, veri döndüren fonksiyonlar
- Resources — HTML/JS kodu, yanı doğrudan client’ta render olacak arayüz parçaları
Eh, Widget içinde çalışan JS, aynı sunucudaki tool’ları çağırabiliyor. Hani ne farkı var diyorsunuz, değil mi? Yanı mimarı şöyle kuruluyor: sunucu hem veri kaynağı hem de UI paketleyici gibi davranıyor (evet, biraz ikili rol), model işe sadece orkestrasyonu yapıyor; hangi widget’ın çağrılacağına karar veriyor ve geri kalan iş client ile sunucu arasında akıyor.
Ne zaman gerçekten gerekli?
Kendi deneyimimden konuşuyorum, Açık konuşayım: her senaryoya MCP App gerekmiyor. Kullanıcıya “faturayı ödedim” demek için widget’a ihtiyaç yok. Peki neden? Ama şu durumlarda baya iş görüyor:
- Etkileşimli veri: Liste yerine tıklanabilir harita, zoom yapılabilen grafik
- Karmaşık form: Onlarca soruyu tek ekranda toplamak — modele soru sordurmaktan kat kat hızlı
- Canlı panolar: Yeni prompt beklemeden güncellenen dashboard
- Çok adımlı iş akışı: Onay süreçleri, wizard tarzı ekranlar, state korunması
Sahada gördüğüm en yaygın örneklerden biri iç kullanıma yönelik insan kaynakları onay ekranları öldü. Yönetici Copilot’a “geçen haftaki izinleri onayla” diyor, karşısına düz metin liste yerine gerçek bir onay tablosu geliyor ve tek tıkla iş bitiyor. Neden önemli bu? İşte fark burada çıkıyor.
Neden özellikle Azure Functions?
MCP sunucusu yazmak kolay görünüyor ama protokolle doğrudan uğraşınca insan yoruluyor. JSON-RPC handshake, capability negotiation, streaming, resource lifecycle… Bunları elle yazmaya kalkarsanız (hele production hedefliyorsanız) hızla konudan uzaklaşıyorsunuz.
Azure Functions burada güzel bir abstraction katmanı veriyor. Sız iş mantığını ve UI’ınızı yazıyorsunuz; bir düşüneyim… protokol detaylarını Functions runtime hallediyor. Bunun yanında paketten gelen birkaç şey daha var: Function key ile temel güvenlik. Üstüne OAuth desteğiyle enterprise auth; Bicep ve azd ile IaC; lokal test için Functions Core Tools; ayrıca otomatik ölçekleme, retry ve monitöring gibi zaten alışık olduğunuz servis davranışları.
Serverless mimarinin buradaki en büyük avantajı şu: MCP tool’ları genelde burst tarzda çağrılır. Kullanıcı sorar, cevap gider, sonra saatlerce sessizlik olur. Böyle bir yük profili için Functions’ın tüketim modeli baya uygun geliyor — VM ayağa kaldırıp bekletmek biraz saçma kalırdı.
Kurumsal Türkiye penceresinden bakınca
Türkiye’deki kurumsal ekiplerle konuştuğumda MCP konusunda iki farklı tavır görüyorum. Bir grup “bu ne şimdi, daha Copilot’u zor benimsettik” diyor; diğer grup işe — özellikle içeride kendi AI asistanını kurmaya çalışan büyük kurumlar — tam olarak şu problemi çözmeye uğraşıyor: model çağrılarını kendi backend’imize nasıl bağlarız? Hmm, asıl soru bu.
Ne yalan söyleyeyim, Cevap uzun süre custom REST API’ler ile prompt engineering karışımıydı. Şimdi MCP standart bir sözleşme veriyor. Azure Functions üzerinde host edildiğinde veri güvenliği tarafında CISO’ları rahatlatan şeyler de hazır geliyor: VNet integration, private endpoint, managed identity… Hepsi kutuda var gibi dürüyor. Data residency tarafında da West Europe ya da kurulacak yerel bölge seçilebiliyor.
Weather App örneği: parçaları anlamak
Microsoft’un yayınladığı örnek klasik bir hava durumu uygulaması. Kabul ediyorum, konu biraz basmakalıp; herkes hava durumu örneği yapıyor zaten. Ama parçaları göstermek için iş görüyor. Örnek şunları içeriyor:
- GetWeather tool: Open-Meteo API’sinden konum ve hava verisi çekiyor
- Weather Widget resource: Etkileşimli HTML/JS dönduruyor ve client’ta render oluyor (bence en önemlisi)
- TypeScript service layer: API çağrılarını ve veri dönüşümünü soyutlayan katman
- Lokal test akışı: MCP Inspector, VS Code veya custom client ile debug
Akış nasıl işliyor?
İlginç olan şu ki, Kabaca sıra şu şekilde ilerliyor:
- Azure Functions
getWeatherWidgetresource’ünü servis ediyor → HTML/JS içeriği dönüyor — bunu es geçmeyin - Client (Copilot, Claude vb.) widget’ı kendi arayüzünde render ediyor
- Widget içindeki JS
getWeathertool’ünü çağırıyor - Sunucu tool’u işletip veriyi widget’a geri gönderiyor
- Widget veriyi görsel olarak render ediyor
Bence buradaki güzellik şu: UI mantığı client tarafında kalıyor (yanı hızlı ve responsive), veri mantığı sunucu tarafında kalıyor (yanı güvenli ve kontrollü). Model işe sadece “hangi widget gösterilecek” kararını veriyor. Aslında ayrım baya mantıklı çıktı.
Çok konuştum, örnekle göstereyim.
Minimal bir tool tanımı nasıl görünüyor?
TypeScript tarafında bir MCP tool aşağı yukarı şöyle dürüyor:
import { app } from "@azure/functions";
app.mcpTool("getWeather", {
toolName: "getWeather",
description: "Belirtilen konum için hava durumu döner",
toolProperties: [
{ propertyName: "location", propertyType: "string",
description: "Şehir adı" }
],
handler: async (context, args) => {
const data = await weatherService.fetch(args.location);
return { content: [{ type: "text", text: JSON.stringify(data) }] };
}
});
Bir bakıma, i̇lginç olan şu ki, Resource tarafı da benzer gidiyor; HTML içeriğini string olarak dönüyorsunuz ya da dosyadan okuyup gönderiyorsunuz. Karmaşık değil. Zaten karmaşık olsaydı Functions’ın sağladığı değer biraz anlamsız kalırdı.
Lokal geliştirme ve deploy
Sahada en çok sevdiğim taraflardan biri şu öldü: gerçekten lokal çalışıyor olması. Function Core Tools ile func start deyince MCP endpoint’i localhost’ta ayağa kalkıyor; sonra MCP Inspector ile bağlanıp tool’ları elle çağırabiliyorsunuz. VS Code’un MCP desteğiyle Copilot Chat üzerinden test etmek de mümkün oluyor.
Deploy tarafı işe azd up. Tek komut yanı. Bicep şablonu Function App’i, Storage’ı, Application Insights’ı ve gerekli role assignment’ları kuruyor. Ha bu arada — Bicep dosyasına göz atın derim — çünkü default olarak public erişim açık geliyor olabilir; kurumsal ortamda private endpoint eklemek. Function key ya da OAuth’u zorlamak gerekiyor.
Küçük ekip mi, kurumsal mı?
İtiraf edeyim, Bunu ayırmadan geçmek istemiyorum çünkü mimarı kararlar ciddi biçimde değişiyor:
| Konu | Startup / Küçük ekip | Enterprise |
|---|---|---|
| Auth | Function key yeterli | Entra ID + OAuth, MI |
| Network | Public endpoint | VNet + Private Endpoint |
Plan
Sıkça Sorulan SorularMCP Apps ile klasik chatbot arasındaki fark ne?Klasik chatbot’ta olan şu: bot bir şeyler yazar, sız okursunuz, hepsi bu. MCP Apps işe aslında çok farklı çalışıyor — sunucudan HTML/JS içeren widget’lar dönüyor ve bunlar desteklenen client’larda gerçek bir arayüz olarak render ediliyor. Yanı grafik, form, harita, tablo gibi şeyler doğrudan sohbet penceresinde canlanıyor. Bence bu farkı bir kez görünce geri dönmek istemiyorsunuz. Azure Functions Consumption planı MCP için yeterli mi?Küçük ve orta yüklerde fazlasıyla yeter, üstelik free tier oldukça cömert. Ama açıkçası cold start sizi rahatsız edecekse Premium plan veya Flex Consumption’ın always-ready özelliğine bakın. Hani LLM zaten yavaş hissettiriyor, üstüne bir de ekstra saniye eklemek istemezsiniz. Bunu biraz açayım. MCP App’ımı hangi client’lar destekliyor?Şu an başlıca Visual Studio Code Copilot, Claude Desktop ve ChatGPT MCP resource render’ını destekliyor. Ama davranışlar henüz tam standartlaşmadı — mesela bir client’ta düzgün görünen widget başkasında farklı çıkabiliyor. Tecrübeme göre production’a çıkmadan önce hedef client’ları mutlaka test etmek çok önemli. Kurumsal ortamda güvenlik açısından nelere dikkat etmeliyim?Default kurulum function key ile geliyor, iç POC için bu tamam. Ama production’da mutlaka Entra — itiraz edebilirsiniz tabi — ID + OAuth, Managed Identity, VNet integration ve Private Endpoint eklemeniz gerekiyor. Bir de widget içinde döndürdüğünüz HTML’e XSS açısından dikkat edin — kullanıcı girdisini sanitize etmeden asla içine gömmeyin. Bence bu kısım en çok atlanan yer. TypeScript şart mı, Python da olur mu?İşin garibi, Python da destekleniyor, hatta bazı ekipler için çok daha alışıldık olabilir. Yanı zorunda değilsiniz. Ama şu an örneklerin ve SDK olgunluğunun büyük kısmı TypeScript tarafında. Widget içi kod zaten JS olacağı için tüm stack’i TypeScript’te tutmak geliştirme deneyimini bayağı iyileştiriyor — açıkçası bu yüzden TypeScript’i tercih ediyorum. Kaynaklar ve İleri OkumaMCP Apps on Azure Functions: Quickstart with TypeScript (Orijinal Blog) Azure Functions Resmî Dokümantasyonu Model Context Protocol Resmî Sitesi İşin garibi, Azure Developer CLI (azd) Belgeleri |
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.








Yorum gönder