TypeScript ile Canlıya Alınabilir MCP Sunucusu Geliştirme ve Güvenlik Rehberi
Büyük Dil Modelleri (LLM'ler) sohbet arayüzlerinden otonom yazılım ajanlarına evrilirken temel bir mühendislik problemi ortaya çıkar: Yapay zeka modelleri kurumsal veritabanları, iç API'ler, geliştirici araçları ve dosya sistemleriyle hassas altyapıyı riske atmadan nasıl güvenle etkileşime girebilir?
Anthropic tarafından açık kaynak olarak sunulan Model Context Protocol (MCP), yapay zeka host'larını (Claude Desktop, Cursor IDE ve özel agent sistemleri) dış veri kaynaklarına ve araçlara bağlayan mimari bir standarttır.
Basit MCP öğreticileri yerel stdio bağlantılarını gösterirken, canlı (production) ortamlarda çalışan MCP sunucuları sıkı girdi doğrulaması (input validation), katman güvenliği, kimlik doğrulama middleware'i, oran sınırlama (rate limiting) ve hata izolasyonu gerektirir.
Bu rehberde, TypeScript ve @modelcontextprotocol/sdk kullanarak canlıya alınabilir, güvenlik testlerinden geçmiş bir MCP sunucusunun mimarisini ve canlıya alım adımlarını inceleyeceğiz.
1. Model Context Protocol (MCP) Mimari Temelleri
MCP, LLM yürütmesini alt sistem kaynaklarından ayırmak için istemci-sunucu (client-server) mimarisini uygular:
- Host (AI Host): LLM'in çalıştığı ortam (Örn: Claude Desktop, Cursor IDE, LangChain agent).
- Client (MCP Client): Host içinde yer alan, protokol yeteneklerini yöneten ve araç çağrılarını ileten istemci.
- Server (MCP Server): İstemciye açık Kaynaklar (Resources), Araçlar (Tools) ve Prompt'lar sunan bağımsız servis.
graph LR
Host[AI Host / Claude / Cursor] <--> Client[MCP Client]
Client <-->|Stdio / SSE / HTTP| Server[Production MCP Server]
Server <--> Guard[Girdi Doğrulama & Zod Şeması]
Guard <--> DB[(Canlı Veritabanı)]
Guard <--> ExternalAPI[Harici API'ler]
MCP Tarafından Sunulan Temel Bileşenler
- Tools (Araçlar): LLM'in eylem gerçekleştirmesini sağlayan çalıştırılabilir fonksiyonlar (Örn: SQL sorgusu çalıştırma, güvenlik günlüğü tarama, webhook tetikleme).
- Resources (Kaynaklar): LLM'in okuma yetkisine sahip olduğu veri uç noktaları (Örn: yapılandırma dosyaları, sistem metrikleri, veritabanı kayıtları).
- Prompts (Şablonlar): Karmaşık ajan etkileşimlerini standartlaştıran parametreli istem şablonları.
2. Production TypeScript Projesinin Kurulumu
Kurumsal düzeyde bir MCP sunucusu geliştirmek için TypeScript projenizi sıkı tip güvenliği ve şema doğrulama bağımlılıklarıyla başlatın:
mkdir mcp-production-server
cd mcp-production-server
npm init -y
npm install @modelcontextprotocol/sdk zod dotenv express cors helmet
npm install -D typescript @types/node @types/express tsx
tsconfig.json dosyasını sıkı tip denetimi için yapılandırın:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
3. Canlıya Alınabilir MCP Sunucusunun Kodlanması
Güvenlik tespiti ve veri analizi yetenekleri sunan güvenli bir MCP sunucusu oluşturalım.
Adım 1: Sunucu Örneğinin Başlatılması
src/server.ts dosyasını oluşturun:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ErrorCode,
McpError,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
const server = new Server(
{
name: "production-security-mcp",
version: "1.0.0",
},
{
capabilities: {
tools: {},
resources: {},
},
}
);
Adım 2: Sıkı Zod Şeması ile Girdi Doğrulama
Girdi doğrulaması, Prompt Injection ve Veri Manipülasyonu saldırılarına karşı ilk savunma hattıdır. Tüm araç parametreleri için sıkı Zod şemaları tanımlayın:
const AuditLogQuerySchema = z.object({
environment: z.enum(["production", "staging", "development"]),
limit: z.number().int().min(1).max(100).default(20),
severity: z.enum(["LOW", "MEDIUM", "HIGH", "CRITICAL"]).optional(),
searchTerm: z.string().max(200).optional(),
});
type AuditLogQuery = z.infer<typeof AuditLogQuerySchema>;
Adım 3: Araç Kaydı ve Yetenek Bildirimi
Kapasite görüşmesi sırasında LLM'e kullanılabilir araçları bildirin:
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "query_audit_logs",
description: "Kurumsal güvenlik denetim günlüklerini filtrelenmiş olarak getirir.",
inputSchema: {
type: "object",
properties: {
environment: {
type: "string",
enum: ["production", "staging", "development"],
description: "İncelenecek hedef ortam",
},
limit: {
type: "number",
description: "Döndürülecek maksimum kayıt sayısı (1-100)",
},
severity: {
type: "string",
enum: ["LOW", "MEDIUM", "HIGH", "CRITICAL"],
description: "Günlük önem derecesine göre filtreleme",
},
searchTerm: {
type: "string",
description: "Arama terimi (maks 200 karakter)",
},
},
required: ["environment"],
},
},
],
};
});
Adım 4: Güvenli Araç Çalıştırma Yöneticisi
Araç çalıştırma işleyicisini hata sınırları (error boundaries) ve veri dezenfeksiyonu ile uygulayın:
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "query_audit_logs") {
// Parametreleri Zod şemasına göre doğrula
const parseResult = AuditLogQuerySchema.safeParse(args);
if (!parseResult.success) {
throw new McpError(
ErrorCode.InvalidParams,
`Geçersiz araç parametreleri: ${parseResult.error.message}`
);
}
const { environment, limit, severity, searchTerm } = parseResult.data;
try {
// Doğrulanmış sorguyu altyapıda çalıştır
const logs = await fetchSecurityAuditLogs({
environment,
limit,
severity,
searchTerm,
});
return {
content: [
{
type: "text",
text: JSON.stringify(logs, null, 2),
},
],
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Bilinmeyen veritabanı hatası";
return {
content: [
{
type: "text",
text: `Çalıştırma başarısız: ${errorMessage}`,
},
],
isError: true,
};
}
}
throw new McpError(ErrorCode.MethodNotFound, `Bilinmeyen araç: ${name}`);
});
4. Canlı (Production) MCP Dağıtımlarında Güvenlik Sıkılaştırma
MCP sunucularını canlı ortamda çalıştırmak özel güvenlik gereksinimleri doğurur:
A. Girdi Dezenfeksiyonu ve Path Traversal Engelleme
LLM araç argümanlarından gelen ham metinleri doğrudan dosya yollarına veya kabuk (shell) komutlarına iletmeyin:
import path from "path";
export function sanitizeFilePath(baseDir: string, userPath: string): string {
const safePath = path.normalize(userPath).replace(/^(\.\.[\/\\])+/, "");
const resolvedPath = path.resolve(baseDir, safePath);
if (!resolvedPath.startsWith(baseDir)) {
throw new Error("Güvenlik Uyarısı: Path traversal girişimi tespit edildi.");
}
return resolvedPath;
}
B. Taşıma Katmanı Güvenliği: Stdio vs SSE
- Stdio Transport: Yerel çalıştırma için idealdir (Claude Desktop, Cursor). İşletim sistemi süreç izolasyonu doğal güvenlik sınırı sağlar.
- Server-Sent Events (SSE) Transport: Bulut dağıtımları için gereklidir. TLS (HTTPS), CORS kısıtlamaları ve Bearer Token / OAuth2 kimlik doğrulama başlıkları ile korunmalıdır.
C. Uzaktan SSE Taşıma Katmanı İçin Kimlik Doğrulama Middleware'i
MCP sunucusunu HTTP/SSE üzerinden sunarken taşıma katmanını Express kimlik doğrulama korumasıyla sarmalayın:
import express from "express";
import helmet from "helmet";
import cors from "cors";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
const app = express();
app.use(helmet());
app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(",") || ["https://gucluyumhe.dev"] }));
// Kimlik Doğrulama Koruması
app.use("/sse", (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith("Bearer ")) {
res.status(401).json({ error: "Yetkisiz: Geçersiz veya eksik Bearer token" });
return;
}
const token = authHeader.substring(7);
if (token !== process.env.MCP_SECRET_KEY) {
res.status(403).json({ error: "Yasaklandı: Geçersiz MCP gizli anahtarı" });
return;
}
next();
});
let sseTransport: SSEServerTransport;
app.get("/sse", async (req, res) => {
sseTransport = new SSEServerTransport("/messages", res);
await server.connect(sseTransport);
});
app.post("/messages", async (req, res) => {
await sseTransport.handlePostMessage(req, res);
});
app.listen(3001, () => {
console.log("Production MCP Sunucusu 3001 portunda SSE ile çalışıyor.");
});
5. Canlıya Alma Stratejileri (Vercel, Railway, Docker)
Docker ve Railway / AWS ECS Dağıtımı
Süreç izolasyonu sağlamak için kapalı bir Dockerfile hazırlayın:
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json tsconfig.json ./
RUN npm ci
COPY src ./src
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/dist ./dist
USER node
EXPOSE 3001
CMD ["node", "dist/server.js"]
Sonuç
Model Context Protocol (MCP), statik LLM'leri kurumsal yapay zeka ajanlarına dönüştüren devrim niteliğinde bir standarttır. Sıkı Zod doğrulaması, dosya yolu dezenfeksiyonu, kimlik doğrulamalı SSE taşıma katmanı ve izole çalıştırma ortamları uygulayarak MCP sunucularınızı canlıya güvenle alabilirsiniz.
Tüm kaynak kodları ve açık kaynak araçları incelemek için GitHub profilimi ziyaret edebilir veya gucluyumhe.dev üzerinden iletişime geçebilirsiniz.
