TypeScript ile MCP Sunucusu Geliştirme: Production Rehberi
MCP'nin AI tool entegrasyonunu nasıl standartlaştırdığını, TypeScript örnekleriyle server geliştirme, güvenlik yönetimi ve production performans optimizasyonunu öğren.
Entegrasyon Problemi#
AI entegrasyonları tekrarlayan bir pattern ortaya koyuyor: her yeni AI modeli, her veri kaynağı ve tool için custom bağlantılara ihtiyaç duyuyor. Matematik acımasız; M model çarpı N tool, M×N custom implementation demek. Ekipler Slack, GitHub ve database’ler için haftalarca bespoke entegrasyon geliştiriyor ve AI provider değiştiğinde tüm süreci baştan tekrarlıyor.
Geleneksel API’ler bunun için tasarlanmamış. REST endpoint’leri tahmin edilebilir request pattern’leri bekliyor, ama AI agent’ları conversation başına yüzlerce request üretiyor, latency gereksinimleri çok farklı. GraphQL flexible query’lerde yardımcı olsa da, dynamic tool discovery veya multiple invocation’lar arası session management için built-in desteği yok.
Model Context Protocol (MCP) bunu entegrasyon katmanını standartlaştırarak çözüyor. Her AI model ve service kombinasyonu için custom bağlantı kurmak yerine, MCP her iki tarafın da bir kere implement ettiği tek bir protocol sağlıyor.
MCP Nedir#
MCP, AI-to-service communication için bir protocol specification. Anthropic tarafından Kasım 2024’te başlatıldı. İlk yılında OpenAI, Google DeepMind ve SAP, Oracle, Docker gibi enterprise platformlar tarafından adopt edildi. Protocol, AI sistemlerinin tool’ları nasıl keşfettiğini ve invoke ettiğini, resource’lara nasıl eriştiğini ve session’ları nasıl yönettiğini tanımlıyor.
İşte architecture:
Core primitive’ler:
- Tools: AI’ın invoke edebileceği executable function’lar (model-controlled)
- Resources: Application’ın yönettiği data source’lar (application-controlled)
- Prompts: Interaction’ları yapılandırmak için reusable template’ler
Transport seçenekleri:
- stdio: stdin/stdout üzerinden local subprocess communication (development)
- Streamable HTTP: Optional SSE streaming ile remote HTTP POST (production)
Protocol tüm message exchange için JSON-RPC 2.0 kullanıyor, transport ne olursa olsun consistent communication pattern’leri sağlıyor.
İlk MCP Server’ını Geliştirmek#
Filesystem operation’larını expose eden minimal viable bir server geliştirelim. Bu core concept’leri gösteriyor: server initialization, tool registration, schema validation ve security constraint’ler.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { readdir, readFile } from "fs/promises";
import { join } from "path";
// Server'ı metadata ile initialize et
const server = new McpServer({
name: "filesystem-server",
version: "1.0.0",
});
// Directory içeriğini listeleme tool'u
server.registerTool(
"list_directory",
{
title: "List Directory",
description: "Directory içeriğini listele",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "Listelenecek directory path"
}
},
required: ["path"]
}
},
async ({ path }) => {
// Güvenlik: Path'in allowed directory içinde olduğunu kontrol et
const allowedDir = process.env.HOME || "/";
const fullPath = join(allowedDir, path);
if (!fullPath.startsWith(allowedDir)) {
throw new Error("Erişim reddedildi: Path allowed directory dışında");
}
const entries = await readdir(fullPath, { withFileTypes: true });
return {
content: [
{
type: "text",
text: JSON.stringify(
entries.map(entry => ({
name: entry.name,
type: entry.isDirectory() ? "directory" : "file"
})),
null,
2
),
},
],
};
}
);
// Dosya içeriğini okuma tool'u
server.registerTool(
"read_file",
{
title: "Read File",
description: "Dosya içeriğini oku",
inputSchema: {
type: "object",
properties: {
path: {
type: "string",
description: "Okunacak file path"
}
},
required: ["path"]
}
},
async ({ path }) => {
// Path validate et - traversal yok, home directory içinde olmalı
if (path.includes("..") || !path.startsWith(process.env.HOME || "/")) {
throw new Error("Geçersiz path");
}
try {
const content = await readFile(path, "utf-8");
return {
content: [
{
type: "text",
text: content,
},
],
};
} catch (error) {
console.error(`Dosya okuma başarısız: ${error.message}`);
return {
content: [
{
type: "text",
text: `Dosya okuma hatası: ${error.message}`,
},
],
isError: true,
};
}
}
);
// Server'ı stdio transport ile başlat
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Filesystem MCP server stdio üzerinde çalışıyor");
}
main().catch(console.error);
Kritik implementation detayları:
- stderr’e loglama: stdout JSON-RPC message’lar için rezerve. stdout’a yazmak protocol’ü bozuyor.
- Schema validation: Her tool parametresinin tanımlı bir schema’sı olmalı. AI-generated input’lara güvenilemez.
- Path sandboxing: File erişim sınırlarını code içinde enforce et; runtime’ın gerçekten kontrol edebileceği yer orası.
- Error handling: Server’ı çalışır durumda tutmak için exception throw etmek yerine error content döndür.
Bu server’ı MCP Inspector ile test et:
npx @modelcontextprotocol/inspector node dist/filesystem-server.js
Resources vs Tools#
Tool’lar ve resource’lar farklı problemleri çözüyor. Bunları karıştırmak inefficient implementation’lara yol açıyor.
Tools (model-controlled):
- AI ne zaman invoke edeceğine karar veriyor
- Side effect’leri olabilir (create, update, delete)
- Action’lar ve operation’lar için tasarlanmış
- Örnekler: send_email, create_database, deploy_service
Resources (application-controlled):
- Application access ve discovery’yi yönetiyor
- Read-only, side effect yok
- Data retrieval için tasarlanmış
- Örnekler: file contents, database record’ları, API documentation
İşte bir resource implementation:
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
// URI parametreli resource template kaydet
server.registerResource(
"file-contents",
new ResourceTemplate("file://{filepath}", { list: undefined }),
{
title: "Dosya İçeriği",
description: "Belirli bir dosyanın içeriğini oku",
mimeType: "text/plain",
},
async (uri, { filepath }) => {
// filepath'i validate et
const allowedDir = process.env.HOME || "/";
if (!filepath.startsWith(allowedDir)) {
throw new Error("Erişim reddedildi");
}
const content = await readFile(filepath, "utf-8");
return {
contents: [
{
uri: uri.href,
mimeType: "text/plain",
text: content,
},
],
};
}
);
Temel fark: tool’lar AI’ın function listesinde görünüyor ve conversation context’e göre invoke edilebiliyor. Resource’lar application’ın URI ile explicit request’i gerektiriyor.
Birden Fazla MCP Server’ı Yönetmek#
Production application’ları genelde birden fazla MCP server’a bağlanıyor; biri GitHub operation’ları için, diğeri database’ler için, üçüncüsü Slack notification’ları için. Bu dikkatli tool namespacing ve connection management gerektiriyor.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
class MultiServerClient {
private clients: Map<string, Client> = new Map();
async addServer(name: string, command: string, args: string[]) {
const transport = new StdioClientTransport({
command,
args,
stderr: "inherit",
});
const client = new Client(
{
name: "multi-client",
version: "1.0.0",
},
{
capabilities: {
roots: { listChanged: true },
sampling: {},
},
}
);
await client.connect(transport);
this.clients.set(name, client);
console.error(`${name} server'a bağlanıldı`);
return client;
}
async discoverAllTools() {
const allTools = new Map();
for (const [serverName, client] of this.clients) {
const { tools } = await client.listTools();
tools.forEach(tool => {
// Conflict'leri önlemek için tool'ları server ile namespace'le
allTools.set(`${serverName}:${tool.name}`, {
...tool,
server: serverName,
});
});
}
return allTools;
}
async callTool(serverName: string, toolName: string, args: any) {
const client = this.clients.get(serverName);
if (!client) {
throw new Error(`Server ${serverName} bulunamadı`);
}
return await client.callTool({ name: toolName, arguments: args });
}
}
// Kullanım
const mcpClient = new MultiServerClient();
await mcpClient.addServer("filesystem", "node", ["./filesystem-server.js"]);
await mcpClient.addServer("database", "node", ["./database-server.js"]);
await mcpClient.addServer("github", "node", ["./github-server.js"]);
const tools = await mcpClient.discoverAllTools();
console.log("Mevcut tool'lar:", Array.from(tools.keys()));
// Çıktı: ["filesystem:list_directory", "filesystem:read_file",
// "database:query", "github:create_pr", ...]
Tool namespacing pattern’leri:
- Server prefix conflict’leri önlüyor (birden fazla server’da
get_statustool’u olabilir) - Operation’ların clear ownership’ini koruyor
- Hangi server’ın request’i handle ettiğini belirleyerek debugging’i basitleştiriyor
Production HTTP Deployment#
Stdio local development’ta iş görüyor, ama production sistemler scalability, monitoring ve multi-client desteği için HTTP transport’a ihtiyaç duyuyor.
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
import crypto from "crypto";
const app = express();
const transport = new StreamableHTTPServerTransport({
enableJsonResponse: true,
sessionIdGenerator: () => crypto.randomUUID(),
});
// CORS ve DNS rebinding prevention için allowed origin'ler
const ALLOWED_ORIGINS = [
"http://localhost:3001",
"https://app.example.com",
];
function isAllowedOrigin(origin: string | undefined): boolean {
if (!origin) return false;
return ALLOWED_ORIGINS.includes(origin);
}
// MCP message endpoint
app.post("/mcp/message", async (req, res) => {
// DNS rebinding attack'larını önlemek için origin validate et
const origin = req.headers.origin;
if (origin && !isAllowedOrigin(origin)) {
return res.status(403).json({ error: "Forbidden origin" });
}
// Session management
const sessionId = req.headers["mcp-session-id"] as string;
console.error(`Session için message işleniyor: ${sessionId}`);
await transport.handleMessage(req, res);
});
// Server-to-client streaming için SSE endpoint
app.get("/mcp/events", async (req, res) => {
const origin = req.headers.origin;
if (origin && !isAllowedOrigin(origin)) {
return res.status(403).send("Forbidden");
}
// Connection drop olduysa last event'ten resume et
const lastEventId = req.headers["last-event-id"] as string;
if (lastEventId) {
console.error(`Event'ten resume ediliyor: ${lastEventId}`);
}
await transport.handleStream(req, res);
});
// Health check endpoint
app.get("/health", (req, res) => {
res.json({ status: "healthy", timestamp: new Date().toISOString() });
});
// Metrics endpoint (Prometheus formatı)
app.get("/metrics", (req, res) => {
// Implementation monitoring setup'a bağlı
res.send("# MCP server metrics\n");
});
app.listen(3000, () => {
console.error("MCP server http://localhost:3000 üzerinde dinliyor");
});
Production consideration’lar:
- Origin validation: Malicious site’ların localhost server’lara bağlanmasını engelleyen DNS rebinding attack’larını önlüyor
- Session management: HTTP stateless; session ID’ler request’ler arası conversation context’i track ediyor
- SSE resumability:
Last-Event-IDheader client’ların connection drop sonrası specific event’lerden resume etmesini sağlıyor - Health check’ler: Load balancer health detection ve orchestration platform’ları için essential
- Metrics: Latency tracking, error rate’ler ve capacity planning için observability
Defense in Depth#
MCP güvenliği defense in depth gerektiriyor. Tool description’lar security control değil; AI model için hint’ler, prompt injection ile bypass edilebilir.
// YANLIŞ: Güvenlik için description'a güvenmek
server.registerTool(
"delete_database",
{
title: "Delete Database",
description: "Tüm database'i sil - son derece dikkatli kullan",
inputSchema: {
type: "object",
properties: {
confirm: { type: "boolean" }
},
required: ["confirm"]
}
},
async ({ confirm }) => {
// Description "sadece user explicit request ettiğinde kullan" diyor
// Prompt injection nedeniyle AI yine de invoke edebilir!
await dropDatabase();
}
);
// DOĞRU: Gerçek safeguard'lar implement et
server.registerTool(
"delete_database",
{
title: "Delete Database",
description: "Proper authorization ile database'i sil",
inputSchema: {
type: "object",
properties: {
confirm: {
type: "string",
enum: ["DELETE_EVERYTHING"],
description: "Exact string DELETE_EVERYTHING olmalı"
},
adminToken: {
type: "string",
description: "Admin authorization token"
}
},
required: ["confirm", "adminToken"]
}
},
async ({ confirm, adminToken }) => {
if (!verifyAdminToken(adminToken)) {
throw new Error("Unauthorized");
}
// Audit logging: action'ı kaydet, credential'ı asla
auditLog("delete-database", { actor: "admin" });
await dropDatabase();
}
);
Implement edilmesi gereken security layer’ları:
// 1. Capability-based access control
class SecureFileSystem {
private allowedPaths: Set<string>;
constructor(allowedPaths: string[]) {
this.allowedPaths = new Set(allowedPaths);
}
async readFile(path: string): Promise<string> {
if (!this.allowedPaths.has(path)) {
throw new Error("Erişim reddedildi");
}
return await readFile(path, "utf-8");
}
}
// 2. Compliance için audit logging
function auditLog(
action: string,
params: Record<string, unknown>,
result?: { success: boolean; error?: string }
) {
console.error(JSON.stringify({
timestamp: new Date().toISOString(),
action,
params,
outcome: result ? (result.success ? "success" : "failure") : "started",
error: result?.error,
}));
}
// 3. Client başına rate limiting
const rateLimiter = new Map<string, number>();
function checkRateLimit(clientId: string, maxPerSecond: number = 10): boolean {
const now = Date.now();
const key = `${clientId}:${Math.floor(now / 1000)}`;
const count = rateLimiter.get(key) || 0;
if (count >= maxPerSecond) {
return false;
}
rateLimiter.set(key, count + 1);
// Eski entry'leri temizle
for (const [k, v] of rateLimiter.entries()) {
const timestamp = parseInt(k.split(':')[1]);
if (timestamp < Math.floor(now / 1000) - 60) {
rateLimiter.delete(k);
}
}
return true;
}
// 4. Schema validation'ın ötesinde input sanitization
function sanitizePath(path: string): string {
// Null byte'ları kaldır, separator'ları normalize et
return path
.replace(/\0/g, '')
.replace(/\\/g, '/')
.replace(/\/+/g, '/');
}
Production’da kritik güvenlik problemleri:
- Prompt injection: User input’a gömülü malicious instruction’lar tool behavior’ını override edebilir
- Tool permission abuse: Proper authorization olmadan excessive privilege’lara sahip tool’lar
- Rug pull attack’ları: Installation sonrası behavior değiştiren tool’lar (checksum’ları verify et)
- Tool shadowing: Bir server’ın tool’larının diğerinin aynı isimdeki tool’larını override etmesi
Static analysis için Invariant Labs MCP-Scan, runtime monitoring için Akto MCP Security gibi security tool’lar kullan.
Context Window’lar için Optimizasyon#
Tool’lardan dönen her byte AI modelinin context window’unu tüketiyor. Conversation başına yüzlerce tool invocation ile inefficient response’lar hızla available token’ları tüketiyor.
// YANLIŞ: Tüm API response'u döndürmek
server.registerTool("get_user",
{
title: "Get User",
description: "User bilgisini getir",
inputSchema: {
type: "object",
properties: {
id: { type: "string", description: "User ID" }
},
required: ["id"]
}
},
async ({ id }) => {
const user = await api.getUser(id);
return {
content: [{
type: "text",
text: JSON.stringify(user)
}]
};
// 50+ field döndürüyor, sadece 3'ü relevant
});
// DOĞRU: Sadece relevant field'ları döndür
server.registerTool("get_user",
{
title: "Get User",
description: "User bilgisini getir",
inputSchema: {
type: "object",
properties: {
id: { type: "string", description: "User ID" }
},
required: ["id"]
}
},
async ({ id }) => {
const user = await api.getUser(id);
return {
content: [{
type: "text",
text: JSON.stringify({
name: user.name,
email: user.email,
status: user.status
})
}]
};
});
Token optimization stratejileri:
- Schema optimization: Kısa tool description’lar (her kelime sayılıyor)
- Progressive disclosure: Detaylı data’yı sadece gerektiğinde yükle
- Resource link’ler: Büyük content’i embed etmek yerine reference et
// Büyük data için resource link kullan
server.registerTool("analyze_repository",
{
title: "Analyze Repository",
description: "Repository'yi dosyalar için tara",
inputSchema: {
type: "object",
properties: {
repo: { type: "string", description: "Repository path" }
},
required: ["repo"]
}
},
async ({ repo }) => {
const files = await scanRepository(repo);
// Tüm dosya içeriklerini embed etmek yerine (100K+ token),
// resource link'leri döndür
return {
content: [
{
type: "text",
text: `${files.length} dosya bulundu. Detaylar için read_file tool'unu kullan.`
},
...files.map(f => ({
type: "resource",
resource: { uri: `file://${f}` }
}))
]
};
});
MCP vs Geleneksel API’ler: Ne Zaman Ne Kullanılmalı#
MCP tek bir problemi hedefliyor: runtime’da tool keşfedip invoke etmesi gereken bir AI client. API’nin diğer tüketicileri REST veya GraphQL alanında kalıyor. İşte decision framework:
MCP kullan:
- AI-first application geliştiriyorsan (agent’lar, assistant’lar)
- Runtime’da dynamic tool discovery gerekiyorsa
- Birden fazla AI provider destekliyorsan (vendor lock-in’den kaçınmak için)
- Persistent context ile session-based workflow’lar
MCP kullanma:
- Geleneksel client-server application’lar
- Real-time streaming data (bunun yerine WebSocket kullan)
- Sub-100ms latency gereksinimleri
- Agent-to-agent communication (spec’te henüz desteklenmiyor)
Hybrid pattern’ler iyi çalışıyor:
// Mevcut REST API'yi wrap eden MCP server
class RESTtoMCPAdapter {
constructor(private baseURL: string, private apiKey: string) {}
createMCPTools(server: McpServer) {
// OpenAPI spec'ten tool'lar generate et
server.registerTool("create_order",
{
title: "Create Order",
description: "Yeni order oluştur",
inputSchema: {
type: "object",
properties: {
items: {
type: "array",
items: {
type: "object",
properties: {
id: { type: "string" },
qty: { type: "number" }
}
}
}
},
required: ["items"]
}
},
async ({ items }) => {
const response = await fetch(`${this.baseURL}/orders`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${this.apiKey}` },
body: JSON.stringify({ items })
});
const data = await response.json();
// Context window için response'u optimize et
return {
content: [{
type: "text",
text: JSON.stringify({
orderId: data.id,
status: data.status,
total: data.total
})
}]
};
}
);
}
}
Bu pattern mevcut REST API’lerini web/mobile client’lar için korurken AI entegrasyonları için optimize edilmiş MCP interface’leri expose etmeni sağlıyor.
MCP Server’larının Production’da Kırıldığı Yerler#
MCP ile çalışmak, implementation’ları en çok zorlayan noktaları tekrar tekrar aynı yerlerde gösteriyor:
1. Protocol İhlalleri#
stdout, JSON-RPC mesajları için rezerve; rastgele bir console.log response stream’ini bozuyor. Diagnostic çıktısını stderr’e yönlendir:
console.error("Operation başlatılıyor...");
2. Stateful stdio vs Stateless HTTP#
In-memory state, single process olduğu için stdio ile sorunsuz çalışıyor; ama multiple instance’ın devrede olduğu HTTP’de dağılıyor:
// YANLIŞ - HTTP request'ler arasında kaybolur
let sessionState = {};
// DOĞRU - Redis veya benzerini kullan
import { createClient } from "redis";
const redis = createClient();
async function getSession(sessionId: string) {
const data = await redis.get(`session:${sessionId}`);
return data ? JSON.parse(data) : {};
}
3. Blocking Long Operation’lar#
Synchronous long-running operation’lar çalıştıkları sürece server’ı block ediyor:
// YANLIŞ - 30 saniye block oluyor
server.registerTool("process_large_file",
{
title: "Process Large File",
description: "Büyük dosyayı işle",
inputSchema: {
type: "object",
properties: {
path: { type: "string", description: "Dosya yolu" }
},
required: ["path"]
}
},
async ({ path }) => {
const result = await processFile(path); // 30 saniye sürüyor
return { content: [{ type: "text", text: result }] };
});
// DOĞRU - task pattern kullan (Kasım 2025 itibariyle experimental)
const tasks = new Map<string, { status: string; result?: string }>();
server.registerTool("start_processing",
{
title: "Start Processing",
description: "Long-running dosya işleme task'ını başlat",
inputSchema: {
type: "object",
properties: {
path: { type: "string", description: "İşlenecek dosya yolu" }
},
required: ["path"]
}
},
async ({ path }) => {
const taskId = crypto.randomUUID();
tasks.set(taskId, { status: "running" });
// Background'da işle
processFile(path).then(result => {
tasks.set(taskId, { status: "completed", result });
});
return {
content: [{
type: "text",
text: `Task ${taskId} başlatıldı. İlerlemeyi izlemek için check_task kullan.`
}]
};
});
server.registerTool("check_task",
{
title: "Check Task",
description: "Processing task'ının durumunu kontrol et",
inputSchema: {
type: "object",
properties: {
taskId: { type: "string", description: "Kontrol edilecek Task ID" }
},
required: ["taskId"]
}
},
async ({ taskId }) => {
const task = tasks.get(taskId);
if (!task) {
throw new Error("Task bulunamadı");
}
return { content: [{ type: "text", text: JSON.stringify(task) }] };
});
4. Yetersiz Error Handling#
Handle edilmeyen exception’lar tüm server’ı çökertiyor: tool handler içindeki bir throw, tüm process’i beraberinde götürüyor. Handler’ı sar ve graceful bir error response döndür:
server.registerTool("risky_operation",
{
title: "Risky Operation",
description: "Error handling ile riskli işlem",
inputSchema: {
type: "object",
properties: {
id: { type: "string", description: "İşlem ID" }
},
required: ["id"]
}
},
async ({ id }) => {
try {
const result = await unreliableAPI(id);
return { content: [{ type: "text", text: result }] };
} catch (error) {
console.error(`Tool başarısız: ${error.message}`);
return {
content: [{
type: "text",
text: `İşlem başarısız: ${error.message}`
}],
isError: true
};
}
});
Cache’leme ve Latency Metrikleri#
MCP server’ları optimize etmek geleneksel API optimization’a benzer pattern’leri takip ediyor, token efficiency için additional consideration’larla.
// Multi-tier caching
const memCache = new Map<string, any>(); // L1: In-memory (ms latency)
const redisClient = createClient(); // L2: Redis (1-5ms latency)
async function getCachedData(key: string) {
// L1'i dene
if (memCache.has(key)) {
return memCache.get(key);
}
// L2'yi dene
const redisData = await redisClient.get(key);
if (redisData) {
const parsed = JSON.parse(redisData);
memCache.set(key, parsed); // L1'e promote et
return parsed;
}
// Source'tan fetch et
const dbData = await db.query(key);
await redisClient.setEx(key, 300, JSON.stringify(dbData)); // 5 dk TTL
memCache.set(key, dbData);
return dbData;
}
Track edilecek performans metrikleri:
- Latency: p50, p95, p99 tool invocation duration
- Token efficiency: Conversation başına context window utilization
- Error rate’ler: Tool type’a göre success rate
- Yerleşim: Server’ınla model provider’ın region’ı arasındaki round-trip süresi; her tool call’un alt sınırını bu belirliyor
Caching en çok, çok sayıda tool call’un okuduğu ve nadiren yazılan reference data’da işe yarıyor: schema description’ları, user profile’ları, repository metadata’sı. Invalidation ucuz kalıyor ve L1 hit critical path’ten bir network hop siliyor.
Pratikte Entegrasyon Pattern’leri#
DevOps Otomasyonu#
Birden fazla MCP server’ı combine etmek automation workflow’ları oluşturuyor:
// Multi-server DevOps workflow
server.registerTool("deploy_feature",
{
title: "Deploy Feature",
description: "Feature branch'i environment'a deploy et",
inputSchema: {
type: "object",
properties: {
branch: { type: "string", description: "Deploy edilecek branch" },
environment: {
type: "string",
enum: ["staging", "production"],
description: "Hedef environment"
}
},
required: ["branch", "environment"]
}
},
async ({ branch, environment }) => {
// 1. GitHub: PR oluştur
const pr = await mcpClient.callTool("github", "create_pr", {
branch,
base: "main",
title: `${branch}'i ${environment}'a deploy et`
});
// 2. CI: Test'leri çalıştır
const tests = await mcpClient.callTool("ci", "run_tests", {
branch
});
if (tests.status !== "passed") {
return {
content: [{
type: "text",
text: `Test'ler başarısız: ${tests.failures}`
}],
isError: true
};
}
// 3. Terraform: Infrastructure'ı apply et
await mcpClient.callTool("terraform", "apply_plan", {
environment,
autoApprove: environment === "staging"
});
// 4. Slack: Takımı bilgilendir
await mcpClient.callTool("slack", "send_message", {
channel: "#deployments",
text: `${branch} ${environment}'a deploy edildi`
});
return {
content: [{
type: "text",
text: `${environment}'a başarıyla deploy edildi`
}]
};
}
);
Test’ler fail olduğunda tool erken dönüyor, böylece Terraform apply ve Slack mesajı bozuk bir branch üzerinde çalışmıyor.
Trade-off’lar ve Karar Framework’ü#
MCP bir yaşında; hala olgunlaşıyor. Ne zaman mantıklı olduğuna dair değerlendirme:
Avantajlar:
- Dynamic discovery documentation maintenance yükünü ortadan kaldırıyor
- Büyüyen pre-built server ecosystem’i (filesystem, GitHub, database’ler)
- Session management ve resumability protocol’e built-in
Dezavantajlar:
- Specification hala evolve oluyor (quarterly update’ler)
- Public olarak dokümante edilmiş production deployment pattern’i az
- Security concern’ler dikkatli implementasyon gerektiriyor
- Agent-to-agent communication için henüz tasarlanmamış
Maliyet consideration’ları:
- Initial implementation: MCP server başına 2-5 gün vs bespoke integration için 2-3 hafta
- Infrastructure: stdio ücretsiz, HTTP service başına ~$50-200/ay (3 replica, load balancer)
- Learning curve: Production-ready expertise için 1-2 hafta
Yatırım birden fazla entegrasyona ihtiyacın olduğunda veya birden fazla AI provider desteklemeyi planladığında karşılığını veriyor. Single-provider, single-tool senaryolar için native function calling daha basit olabilir.
Sonraki Adımlar#
MCP’yi use case’in için değerlendiriyorsan:
- Locally stdio ile başla: 2-3 tool ile basit bir server geliştir
- MCP Inspector ile test et: AI client’lara bağlanmadan önce protocol compliance’ı verify et
- Bir production pattern implement et: Proper error handling ile HTTP transport
- Security layer’ları ekle: Input validation, rate limiting, audit logging
- Performansı ölç: Latency, token kullanımı ve error rate’leri track et
- Hybrid düşün: Rebuild etmek yerine mevcut API’leri MCP adapter’larla wrap et
Official TypeScript SDK (@modelcontextprotocol/sdk) başlamak için doğru yer; ilk remote deployment’tan önce güvenlik rehberini okumaya değer.
Kaynaklar#
- Model Context Protocol - Resmi Dokümantasyon (yeni sekmede açılır) - Transport, yetenekler ve protokol gereksinimlerini kapsayan yetkili MCP spesifikasyonu
- MCP Inspector - Model Context Protocol (yeni sekmede açılır) - MCP sunucularını test etmek ve hata ayıklamak için interaktif geliştirici aracına resmi rehber
- GitHub - modelcontextprotocol/servers (yeni sekmede açılır) - Protokol özelliklerini ve SDK kullanımını gösteren referans MCP sunucu uygulamaları
- GitHub - modelcontextprotocol/typescript-sdk (yeni sekmede açılır) - MCP sunucu ve istemcileri oluşturmak için resmi TypeScript SDK’sı
- GitHub - modelcontextprotocol/python-sdk (yeni sekmede açılır) - MCP sunucu ve istemcilerini uygulamak için resmi Python SDK’sı
İlgili yazılar
Zapier MCP'nin AI agent'lar için aksiyon bazlı beyaz liste, merkezi kimlik yönetimi ve insan onay mekanizması sunması. Özel proxy çözümlerine yönetilen bir alternatif.
mcp · security · ai-agents +4
Production takımları geniş MCP erişimini neden scoped API proxy'leriyle değiştirir? Atlassian, Google Workspace ve Notion için FastAPI, CLI ve n8n örnekleri.
mcp · api-design · python +4
Model Context Protocol için kurumsal kalıplar: araç bileşimi, çoklu ajan orkestrasyonu, rol tabanlı erişim kontrolü ve production gözlemlenebilirlik.
mcp · ai-adoption-strategy · authorization +4
Kurumsal AI entegrasyonu için pratik 6 seviyeli framework: ChatGPT, RAG, MCP agent veya fine-tuning ne zaman kullanılmalı, PII ve finans uyumluluğu odaklı.
ai-adoption-strategy · rag · mcp +2
AI/LLM alanında pratik, implementation odaklı bir sözlük. Token'lardan agent'lara, RAG'dan fine-tuning'e, kod örnekleri ve dürüst değerlendirmelerle.
llm · ai-agents · rag +6