MCP Server Entegrasyonu ve Yapılandırılmış Hata Yanıtları
Model Context Protocol'ün project vs user scope ayrımı, .mcp.json + env var substitution, isError flag'i, 4 hata kategorisi (transient/validation/business/permission) ve local recovery pattern'ı.
Öğreneceklerin
- MCP server scope seçimini (project .mcp.json vs user ~/.claude.json) bilmek
- Environment variable substitution ile secret yönetimini uygulamak
- isError flag'inin ve yapılandırılmış error metadata'nın önemini açıklamak
- 4 hata kategorisini (transient/validation/business/permission) somut örneklerle ayırt etmek
- Generic 'Operation failed' mesajının neden recovery kararını engellediğini göstermek
- MCP resources kavramını (content catalogs) ve exploratory call ihtiyacını nasıl azalttığını bilmek
- Local recovery subagent içinde, sadece çözülemeyen hatalar coordinator'a çıkma prensibini uygulamak
MCP Server Entegrasyonu ve Yapılandırılmış Hata Yanıtları
Bu derste iki şeyi birleştiriyoruz: MCP’nin nerede tanımlandığı (scope kararı) ve MCP tool’larının nasıl hata döndüğü (recovery kararını belirleyen metadata). Sınav bu ikisini sıkça aynı senaryoda sorar: “agent MCP tool’u çağırdı, hata aldı, ne yapmalı?” — cevap sadece hata tipine göre değil, hatanın kategorisine göre değişir.
Neden bu konu sınavda çıkıyor
Domain 2 sınavın %18’i. Pratik senaryolardan “Customer Support” ve “Agentic AI Tools” MCP + error handling soruları içerir. Tipik sınav sorusu: bir MCP server hata döndürüyor, agent’ın doğru aksiyonu ne? Doğru cevap ya “retry with backoff” (transient) ya “input düzelt ve tekrar dene” (validation) ya “escalate” (permission) ya da “kullanıcıya net mesaj” (business). Bu dörtlü ayrım sınavın en çok puan getiren kısmı.
Temel kavramlar
MCP server scope kararı
MCP server tanımı iki yerde yaşar:
| Dosya | Scope | Kim paylaşır? | Ne zaman? |
|---|---|---|---|
.mcp.json (proje kökü) | Project | VCS (git) ile birlikte, tüm ekip aynı server’ı görür | Repo’ya özel servisler (GitHub, internal DB) |
~/.claude.json (kullanıcı home) | User | Sadece sen, başka makineye taşınmaz | Kişisel tool’lar (Spotify, kişisel Notion) |
Karar matrisi basit: Server ekibin başka bir üyesi de aynı komutla ayağa kalkabiliyorsa → .mcp.json. Sadece senin makinende çalışan bir kişisel token varsa → ~/.claude.json. Pratik örnek: şirketin GitHub organizasyonu için MCP server .mcp.json’da, kişisel Spotify queue’un için ~/.claude.json’da.
Bağlanan TÜM MCP server’ların tool’ları bağlantıda otomatik keşfedilir ve aynı anda kullanılabilir — tool sayısı bu yüzden hızla büyür, 4-5 tool güvenilirlik eşiğini zorlar. Standart entegrasyonlar (GitHub, Postgres, Slack) için custom server yazmak yerine community MCP server tercih et.
.mcp.json yapısı
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
},
"postgres-readonly": {
"command": "uvx",
"args": ["mcp-server-postgres", "--readonly"],
"env": {
"DATABASE_URL": "${READONLY_DB_URL}"
}
}
}
}
Üç kritik nokta:
-
${VAR}substitution — Token plaintext yazılmaz. Claude Code oturum başında environment’tan okur,.mcp.jsondosyasında secret yer tutucu kalır. Bu sayede.mcp.jsonVCS’e girer ama token girmez. Sınav tuzağı: “secret’ı doğrudan.mcp.json’a yaz” seçeneği her zaman yanlıştır. -
command+args—npx,uvx,node,pythongibi runtime çağrıları. Tek binary değil, “nasıl başlatılır” tanımı. -
Server isimleri unique —
github,postgres-readonlygibi. Tool çağrılarındamcp__github__list_issuesformatı bu isimden türetilir.
isError flag’i
MCP tool’ları iki şekilde dönebilir:
// Başarılı sonuç
{
"content": [{ "type": "text", "text": "..." }],
"isError": false
}
// Hata — Claude bunu tool başarısız olarak okur
{
"content": [{ "type": "text", "text": "..." }],
"isError": true
}
isError: true göndermek Claude’a “bu tool call başarısız oldu, gerekirse başka tool dene veya düzelt” sinyali verir. Ama bu tek başına yeterli değil. Sınavda en çok yanlış cevap isError: true + "Operation failed" kombinasyonu — Claude “failed” görür ama neyi düzelteceğini bilemez.
Yapılandırılmış hata metadata
📝 Rehber kapsamı dışında: rehberin 4.4 şeması errorCategory / isRetryable / message / attempted_query / partial_results alanlarını verir; buradaki ek alanlar (retryAfterSeconds, httpStatus, toolName, documentationUrl, field, expected, requiredScope, userActionable, alternativeActions) pratikte faydalı opsiyonel zenginleştirmelerdir, sınavda zorunlu değildir.
Doğru yaklaşım: hata yanıtının content alanını yapılandırılmış tutmak — errorCategory + isRetryable + message + attempted_query + partial_results alanlarını doğrudan content objesinin içine yerleştirmek (rehber 4.4 şeması). Bir MCP server’ın döndüğü zengin hata yanıtı:
{
"isError": true,
"content": {
"errorCategory": "transient",
"isRetryable": true,
"message": "GitHub API rate limit exceeded",
"attempted_query": "list_issues?repo=anthropics/claude-code",
"partial_results": null,
"retryAfterSeconds": 60,
"httpStatus": 429,
"toolName": "github.list_issues",
"documentationUrl": "https://docs.github.com/en/rest/rate-limit"
}
}
Bu yapıyı kullandığında Claude (veya agent loop) şu kararı verebilir:
errorCategory: "transient"+isRetryable: true→ backoff ile tekrar deneerrorCategory: "validation"+isRetryable: false→ input parametrelerini düzelterrorCategory: "permission"→ escalate to user/coordinatorerrorCategory: "business"→ kullanıcıya net mesaj, devam etme
4 hata kategorisi — somut örnekler
1. Transient (geçici, retry’ye değer)
{
"isError": true,
"content": {
"errorCategory": "transient",
"isRetryable": true,
"message": "Upstream DB temporarily unavailable",
"attempted_query": "db.query('SELECT * FROM orders WHERE ...')",
"partial_results": null,
"retryAfterSeconds": 5
}
}
Network timeout, 503 Service Unavailable, rate limit (429), DB connection drop. Retry + exponential backoff doğru. Hard retry 3x yanlış — jitter ve backoff şart.
2. Validation (geçersiz input)
{
"isError": true,
"content": {
"errorCategory": "validation",
"isRetryable": false,
"message": "Invalid query: limit must be 1-100, got 500",
"attempted_query": "list_items(limit=500)",
"partial_results": null,
"field": "limit",
"expected": "integer between 1 and 100"
}
}
Schema dışı parametre, enum’da olmayan değer, format hatası. Retry işe yaramaz; input düzeltilmeli. Sınav tuzağı: validation hatasını “transient” muamelesi yapıp tekrar tekrar denemek — token israfı + aynı sonuç.
3. Business (domain kuralı ihlali)
{
"isError": true,
"content": {
"errorCategory": "business",
"isRetryable": false,
"message": "Issue #1234 is locked; only collaborators can comment",
"attempted_query": "add_comment(issue=1234, body='...')",
"partial_results": null,
"userActionable": true,
"alternativeActions": [
"open a new issue referencing #1234",
"request collaborator access from issue author"
]
}
}
“PR merge conflict”, “issue locked”, “subscription expired”. Retry’le çözülmez. Kullanıcıya net mesaj + alternatif aksiyon öner. userActionable: true flag’i Claude’a “bunu çözemezsin, kullanıcıya sor” demesini sağlar.
4. Permission (yetki yok)
{
"isError": true,
"content": {
"errorCategory": "permission",
"isRetryable": false,
"message": "Token missing scopes: repo:write",
"attempted_query": "push_commit(...)",
"partial_results": null,
"requiredScope": "repo:write",
"documentationUrl": "https://github.com/settings/tokens"
}
}
Auth eksik, scope yetersiz, IP whitelist dışı. Escalate — coordinator’a veya kullanıcıya çık. Retry anlamsız; config değişmeli.
Local recovery pattern’ı
Akış şu: subagent içinde transient/validation hatalarını yerel olarak çözmeye çalış. Çözemiyorsa coordinator’a escalate et. Generic “Operation failed” gibi yapılandırılmamış content alındığında subagent kategoriyi bilemez (content’te errorCategory alanı yok), bu yüzden ya retry döngüsüne girer (yanlış) ya da körlemesine escalate eder (gürültü).
subagent.tool_call()
|
v
[mcp_server] -> response
|
+-- isError: false --> done
|
+-- isError: true
|
+-- errorCategory: "transient", isRetryable: true
| -> backoff ile retry (max 3, exponential + jitter)
|
+-- errorCategory: "validation"
| -> input'u content.expected'a göre düzelt
| -> tekrar dene
| -> yine fail --> escalate
|
+-- errorCategory: "business"
| -> kullanıcıya alternativeActions ile mesaj
| -> done (yeniden deneme yok)
|
+-- errorCategory: "permission"
-> escalate to coordinator
-> kullanıcıya documentationUrl ile birlikte
Generic mesaj alındığında subagent’ın elinde sadece iki seçenek kalır: kör retry veya kör escalate. İkisi de kötü. content yapılandırılmamış generic string olduğunda doğru recovery kararı verilemez — kategoriyi content’ten çıkarsayamazsın.
PostToolUse hook ile format normalizasyonu — Birden fazla üçüncü-parti MCP server farklı format döndürdüğünde (Unix timestamp vs ISO 8601, farklı status kodları), normalizasyonu prompt’a değil PostToolUse hook’una koy: deterministik garanti sağlar, her tool sonucunu Claude görmeden önce tek formata çevirir. (Rehber Ch 3.5 + 11.2; practical test bu kalıbı soruyor.)
MCP resources
MCP üç şey sunar: Tools (çağrılabilir fonksiyon), Resources (okunabilir veri), Prompts (hazır template). Resources, rehber 4.5’e göre şu kullanım senaryolarına hizmet eder: content catalogs (örn. tüm proje task’larının listesi, issue/task özetleri), şema/dokümantasyon görünümleri (veritabanı şeması, API referansları, internal guide’lar), hiyerarşik navigasyon — hepsi exploratory tool call ihtiyacını azaltır.
Resources, content catalog gibi davranır: agent “şu anda açık issue’lar neler?” sorusunu sormak için bir tool’u körlemesine çağırmak yerine ilgili resource’u okuyabilir. Bu iki pratik fayda sağlar:
- Exploratory call azalır — token economy
- Tutarlılık — aynı veri formatı, aynı URI şeması
Sınav tuzağı: “her şey için tool çağır, resources sadece metadata” seçeneği yanlış. Resources read-only veri erişimi içindir, çoğu zaman tool yerine geçer.
Sınav tuzakları
- “Generic ‘Operation failed’ alıyorsan 3 kez retry et” klasik yanlış cevap.
contentyapılandırılmamış string olduğu için transient/validation/permission ayrımı yapılamaz; kör retry token israfı + aynı sonuç. Doğru: server’ıcontentiçinde yapılandırılmış alanlar (errorCategory,isRetryable,message,attempted_query,partial_results) dönecek şekilde güncelle veya kendi try/except’inde kategori üret. - “Tüm MCP server’ları
~/.claude.json’a koy ki repo temiz kalsın” mantıklı gelir. Yanlış çünkü ekibin diğer üyeleri aynı server’ı farklı komutlarla kurar; project scope’un VCS avantajı kaybolur. Doğru: paylaşılan server’lar.mcp.json, kişisel olanlar~/.claude.json. - “Secret’ı plaintext
.mcp.json’a yaz, .gitignore ekle” kısmi çözümdür. Yanlış çünkü.gitignoreunutulursa token leak olur; başkaları repoyu klonlayınca token görünmez olur. Doğru:${ENV_VAR}substitution + secret manager / CI variable. - “Validation hatasını transient say ve backoff ile dene” dikkat dağıtıcıdır. Validation hatası retry’le çözülmez; input düzeltilmeli. Doğru:
errorCategory: "validation"görünce input’u yeniden incele. - “Permission hatasını kullanıcıya göstermeden sessizce retry” güvenlik riski. Doğru: escalate et, gerekirse audit log’a yaz, kullanıcıyı bilgilendir.
Hands-on
Bir MCP server’da yapılandırılmış hata dönmek (TypeScript örneği):
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server({ name: "github-tools", version: "0.1.0" });
// *(SDK kullanımı basitleştirilmiştir; güncel MCP SDK şema-tabanlı `setRequestHandler(CallToolRequestSchema, ...)` kalıbını kullanır.)*
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
try {
const result = await callGitHub(name, args);
return { content: [{ type: "text", text: JSON.stringify(result) }], isError: false };
} catch (err) {
// err: { status, category, message, retryAfter?, requiredScope?, attemptedQuery? }
return {
isError: true,
content: {
errorCategory: err.category, // transient | validation | business | permission
isRetryable: err.category === "transient",
message: err.message,
attempted_query: err.attemptedQuery,
partial_results: null,
retryAfterSeconds: err.retryAfter,
httpStatus: err.status,
documentationUrl: err.docsUrl,
},
};
}
});
const transport = new StdioServerTransport();
await server.connect(transport);
Agent tarafında ise (Agent SDK PreToolUse hook) bu content alanlarına bakıp karar veren küçük bir normalizer:
def pre_tool_use(tool_name, tool_input, context):
last_error = context.get("lastToolError")
if not last_error:
return # no error, proceed
content = last_error.get("content", {})
cat = content.get("errorCategory")
if cat == "transient" and content.get("isRetryable"):
retry_after = content.get("retryAfterSeconds", 5)
time.sleep(retry_after)
return # allow retry
if cat == "validation":
# fix input based on content.expected
tool_input = apply_validation_fix(tool_input, content)
return {"modifiedInput": tool_input}
if cat == "permission":
return {"action": "block", "escalate": True,
"message": f"Missing scope: {content.get('requiredScope')}"}
if cat == "business":
return {"action": "block", "userMessage": content.get("message")}
Bu örnek iki şeyi gösteriyor: (1) server content içinde yapılandırılmış hata alanları üretmek, (2) client/agent bu content’e göre policy enforcement yapmak. İkinci kısım Agent SDK’nın PreToolUse hook’u ile deterministik yapılır — prompt’a güvenmek yerine koda yazılır.
Özet
- MCP scope: paylaşılan server →
.mcp.json(VCS), kişisel →~/.claude.json. Secret’lar${ENV_VAR}substitution ile yönetilir, plaintext asla. - Tool yanıtı
isError: truetaşıyorsa bu tek başına yeterli değil;contentiçinde yapılandırılmış alanlar (errorCategory,isRetryable,message,attempted_query,partial_results) olmadan doğru recovery kararı verilemez. - 4 hata kategorisi: transient (retry + backoff), validation (input düzelt), business (kullanıcıya mesaj), permission (escalate).
- Local recovery subagent’ta; sadece çözemediği durumda coordinator’a escalate. Generic “Operation failed” alındığında bu ayrım yapılamaz —
content’in yapılandırılmış alanlar içermesi zorunlu. - MCP resources content catalog olarak exploratory call ihtiyacını azaltır; tool yerine resource kullanmak token economy sağlar.
- Recovery kararı prompt’a değil, koda yazılmalı — PreToolUse hook’unda
contentalanlarına bakıp deterministik aksiyon almak en sağlam yöntem.
Kaynaklar
- Anthropic Docs — Model Context Protocol: https://docs.anthropic.com/en/docs/agents-and-tools/mcp
- MCP Specification — Resources & Tools: https://modelcontextprotocol.io/specification
- Anthropic Docs — Agent SDK Hooks: https://docs.anthropic.com/en/docs/agents-and-tools/agent-sdk/hooks