docs: publish translated docs in twelve languages and the tool that maintains them (#3280)

This commit is contained in:
Max 2026-08-14 17:07:34 +01:00 • committed by GitHub
commit 52ad0a8876
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 89737 additions and 54 deletions

View file

@ -0,0 +1,131 @@
---
translation:
sections: [72f9c964769076dd, 9a2c14e10935b515, 235299eb78ab12d7, 8aee1e78c8237fb8, 9bd86acd4112138f, 55343cb7f250dc7b]
tool: 1
---
# Tamamlamalar {#completions}
Sunucunuzun üzerine bir arayüz kuran bir istemci, kullanıcı yazdıkça argüman değerlerini otomatik tamamlamak ister: dil adları, depo adları, dosya yolları.
**Tamamlamalar**, sunucunuzun bu önerileri sağlama yoludur.
## Tamamlamaya değer bir şey {#something-worth-completing}
Tamamlamalar tam olarak iki şeye uygulanır: bir **prompt**'un argümanlarına ve bir **kaynak şablonunun** parametrelerine. O halde her birinden birer tane içeren bir sunucuyla başlayın:
```python title="server.py" hl_lines="6 12"
--8<-- "docs_src/completions/tutorial001.py"
```
Burada henüz tamamlamalarla ilgili hiçbir şey yok.
* `review_code` bir `language` alır. Kullanıcı hangi yazımları kabul ettiğinizi tahmin etmek zorunda kalmamalı.
* `github_repo` bir `owner` ve bir `repo` alır. İkisi için de serbest metin kutuları kötü bir form olur.
## Tamamlama işleyicisi {#the-completion-handler}
`@mcp.completion()` ile dekore edilmiş **tek** bir fonksiyon ekleyin:
```python title="server.py" hl_lines="21-29"
--8<-- "docs_src/completions/tutorial002.py"
```
* Sunucu başına tek bir işleyici vardır. Her tamamlama isteği buraya düşer; neyin tamamlandığına göre siz dallanırsınız.
* `async def` olmak zorundadır: SDK onu await eder.
* Üç argüman alır:
* `ref`: *hangi* prompt veya kaynak şablonu olduğu; bir `PromptReference` ya da `ResourceTemplateReference` olarak gelir. İkisini `isinstance` ile ayırt edersiniz.
* `argument`: `argument.name` tamamlanmakta olan argüman, `argument.value` ise kullanıcının şu ana kadar yazdığıdır.
* `context`: hâlihazırda çözümlenmiş argümanlar. Şimdilik görmezden gelin.
* Bir `Completion(values=[...])` döndürürsünüz; sunacak bir şeyiniz yoksa `None`.
!!! tip
`argument.value`, kullanıcının yazdığı ön ektir. SDK sizin yerinize filtreleme **yapmaz**:
`values` içine ne koyarsanız arayüz onu gösterir. `startswith`'i yazmak size düşer.
### Deneyin {#try-it}
**[Test etme](../get-started/testing.md)** sayfasındaki bellek içi `Client` ile çalıştırın.
`client.complete()`'i `ref=PromptReference(name="review_code")` ve
`argument={"name": "language", "value": "py"}` ile çağırın:
```python
result.completion.values # ['python']
```
* `ref`, işleyicinizin aldığı referans türünün aynısıdır.
* `argument`, tam olarak iki anahtarı (`name` ve `value`) olan düz bir dict'tir.
Boş bir `value` gönderin, listenin tamamı geri döner. `lang.startswith("")` her dil için doğrudur:
```python
result.completion.values # ['go', 'javascript', 'python', 'rust', 'typescript']
```
`code` hakkında sorun (işleyicinizin tanımadığı bir argüman); `None` döndürür, SDK da bunu boş bir listeye çevirir:
```python
result.completion.values # []
```
`None` *"öneri yok"* demektir, asla bir hata değildir. Arayüz düz bir metin kutusuna geri döner.
## Hiç bildirmediğiniz bir yetenek {#a-capability-you-never-declared}
İşleyiciyi kaydetmek bildirimin ta kendisidir. Bir istemci bağlayın ve bakın:
```python
client.server_capabilities.completions # CompletionsCapability()
```
`completions`'ı hiçbir yerde listelemediniz. SDK işleyiciyi gördü ve yeteneği sizin yerinize bildirdi. *İsteğe bağlı* her yetenek böyle çalışır: işleyici bildirimin kendisidir. (Üç temel yapı isteğe bağlı değildir: `MCPServer` işleyici olsun olmasın bunları her zaman bildirir.)
!!! check
İlk `server.py` dosyasına (işleyicisi olmayana) dönün ve yine de sorun. Çağrı bir JSON-RPC
hatasıyla başarısız olur:
```text
Method not found
```
Ve `client.server_capabilities.completions` `None` olur. Yeteneğin anlamı budur: düzgün
davranan bir istemci bunu kontrol eder ve yanıtlayamayacağınız isteği hiç göndermez.
## Bağımlı argümanlar {#dependent-arguments}
`github://repos/{owner}/{repo}` kaynağının iki parametresi var ve `repo` için işe yarar değerler önce hangi `owner`'ın seçildiğine bağlı.
`context` tam da bunun için var. Kullanıcının **hâlihazırda çözümlediği** argümanları taşır:
```python title="server.py" hl_lines="8-11 34-38"
--8<-- "docs_src/completions/tutorial003.py"
```
* Yeni dal, şablonun `repo` parametresi için devreye girer.
* `context.arguments`, şu ana kadar seçilen değerlerin (burada `owner`) bir `dict[str, str] | None`'ıdır.
* Henüz `owner` yoksa mantıklı öneri de yoktur; bu yüzden işleyici `None` döndürür.
İstemci bu çözümlenmiş değerleri `context_arguments=` ile gönderir. Bu kez `ref` bir
`ResourceTemplateReference(uri="github://repos/{owner}/{repo}")` olur. Boş bir `value` ile
`repo`'yu isteyin ve `context_arguments={"owner": "modelcontextprotocol"}` geçirin:
```python
result.completion.values # ['python-sdk', 'typescript-sdk', 'inspector']
```
`context_arguments=`'ı kaldırın, aynı çağrı `[]` döndürür. İşleyici, sahibi bilmeden hangi depoları önereceğini bilemez.
!!! info
`Completion` ayrıca `total=` ve `has_more=` de alır. `values` daha uzun bir listenin bir dilimi
olduğunda bunları ayarlayın; böylece arayüz *"ve 200 tane daha"* gösterebilir. Çoğu işleyicinin
bunlara hiç ihtiyacı olmaz.
## Özet {#recap}
* Tamamlamalar, **prompt argümanları** ve **kaynak şablonu parametreleri** için önerilerdir. Başka bir şey değil.
* `@mcp.completion()` tek işleyiciyi kaydeder. İmzası `async def (ref, argument, context) -> Completion | None`'dır.
* `isinstance(ref, ...)` ve `argument.name` üzerinden dallanın. `argument.value`'ya göre filtrelemeyi kendiniz yapın.
* `None` boş bir listeye dönüşür. Asla bir hata değildir.
* `context.arguments` hâlihazırda çözümlenmiş değerleri tutar; istemci bunları `context_arguments=` olarak sağlar.
* `completions` yeteneği, işleyiciyi kaydettiğiniz anda ortaya çıkar. O olmadan istek `Method not found` olur.
Öneriler, kullanıcı bir prompt'u veya şablonu hâlâ *doldururken* işe yarar; bir araç çağrısının *ortasında* kullanıcıya soru sormak için **[Elicitation](../handlers/elicitation.md)** gerekir. Bir aracın metin dışında döndürebileceği her şey ise **[Görseller, ses ve simgeler](media.md)** sayfasında.

View file

@ -0,0 +1,139 @@
---
translation:
sections: [e33d441f12d50535, 7099694c603e0f5f, c1df4cf9673433e6, c9cd294541422e6e, 6cec073617bfd037, efa92b8f99e908c8, 6a22a29e27fb4601]
tool: 1
---
# Hataları ele alma {#handling-errors}
Bir araç iki şekilde başarısız olabilir ve SDK bu ikisini çok farklı ele alır.
Sıradan bir istisna fırlatırsanız bunu **model** görür. `MCPError` fırlatırsanız bunu **protokol** görür.
Bu sayfa, hangisini seçeceğinizle ilgili.
## Modelin düzeltebileceği bir hata {#an-error-the-model-can-fix}
Bir şeyi arayıp bulan bir araç düşünün; arama sonuçsuz kalsın:
```python title="server.py" hl_lines="11-12"
--8<-- "docs_src/handling_errors/tutorial001.py"
```
Bu iki satırda MCP'ye özgü hiçbir şey yok. `get_author`, herhangi bir Python fonksiyonunun yapacağı gibi düz bir `ValueError` fırlatır.
Katalogda olmayan bir başlıkla çağırın ve sonuca bakın:
```python
result.is_error # True
result.content # [TextContent(text="Error executing tool get_author: No book titled 'Nothing' in the catalog.")]
result.structured_content # None
```
* İstek **başarılı oldu**. Ortada bir sonuç var; çağıran tarafta hiçbir şey fırlatılmadı.
* `is_error` değeri `True`; istisnanızın mesajı (başına araç adı eklenmiş olarak) `content`'te, tam da modelin okuduğu yerde.
* `structured_content` değeri `None`. Başarısız bir çağrının yapılandırılacak bir dönüş değeri yoktur.
Bu bir **araç hatasıdır** ve aracınızın fırlattığı *her* istisna için varsayılan davranış budur. Neredeyse her zaman istediğiniz şey de budur.
Aracınızı çağıran modeldir. Argümanları o seçti. Bu yüzden araç hatası, konuşmada bir tur demektir: model *"No book titled 'Nothing' in the catalog."* mesajını okur, başlığı yanlış tahmin ettiğini anlar ve daha iyi bir başlıkla tekrar çağırır. Tek bir `raise` yazdınız ve kendi kendini düzelten bir ajan elde ettiniz.
!!! tip
Bir araçtan hata mesajını asla `return` ile döndürmeyin. Döndürülen bir dizenin `is_error=False`
değeri vardır; bu yüzden modele (ve her istemci arayüzüne) araç çalışmış ve yanıt o dizeymiş gibi görünür.
`raise` kullanın. Sinyali veren bayraktır.
## Modelin düzeltemeyeceği bir hata {#an-error-the-model-cannot-fix}
Şimdi `ValueError` yerine `MCPError` koyun.
```python title="server.py" hl_lines="1 3 14"
--8<-- "docs_src/handling_errors/tutorial002.py"
```
`MCPError`, SDK'nın **protokol hatasıdır**. Araç sarmalayıcısının *yakalamadığı* tek istisna budur: yayılır ve `tools/call` isteğinin tamamı bir sonuç yerine JSON-RPC hatasıyla başarısız olur.
```json
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog."
}
```
* **Sonuç yoktur**. `content` yok, `is_error` yok: modelin okuyacağı hiçbir şey yok.
* Hatayı bunun yerine **host** uygulama alır; tıpkı araç hiç var olmasaydı alacağı gibi.
* `code`, `message` ve `data` bozulmadan ulaşır. `INVALID_PARAMS` sabiti `-32602` değerini taşır; `mcp.types` onu ve diğer JSON-RPC hata kodlarını (`INVALID_REQUEST`, `INTERNAL_ERROR`, ...) sabit olarak dışa aktarır, böylece hiçbir zaman sihirli bir sayı yazmazsınız.
!!! check
Aynı arama, aynı sonuçsuzluk; ama bu kez çağrı istemci tarafında döndürmek yerine *fırlatır*:
```text
mcp.shared.exceptions.MCPError: No book titled 'Nothing' in the catalog.
```
İlk sürüm modele tepki verebileceği bir cümle vermişti. Bu sürüm ona hiçbir şey vermez.
`get_author` için bu kesinlikle daha kötüdür; bir sonraki bölümün konusu da budur.
## Hangisini fırlatmalı {#which-one-to-raise}
İki yol, iki farklı soruyu yanıtlar.
* *Yürütme* başarısızlığı için **herhangi bir istisna fırlatın**: aracınızın yapmaya çalıştığı şey işe yaramadı. Çağrıyı model seçti, bu yüzden sonucunu da model görmeli ve toparlanma şansı bulmalı. Yanlış yazılmış bir başlık, zaman aşımına uğrayan bir dış API, var olmayan bir satır: hepsi araç hatası.
* *İsteğin kendisi* reddedilmesi gerektiğinde **`MCPError` fırlatın**: istemcide aracınızın bağımlı olduğu bir yetenek eksik, sunucu kimseye hizmet verecek durumda değil, çağıran taraf zorunlu bir adımı atlamış. Modelin hiçbir yeniden denemesi bunları düzeltmez; bu yüzden mesajı ona vermenin bir kazancı yok.
Kararı tek bir soru verir: **daha akıllı bir model bundan kaçınabilir miydi?** Evet -> sıradan istisna. Hayır -> `MCPError`.
Bu ölçüte göre `get_author`'ın ikinci sürümü yanlış seçim yaptı: daha iyi bir başlık sorunu çözer, yani model mesajı görmeyi hak ediyordu. O sürüm size mekanizmayı göstermek için orada, onu önermek için değil.
!!! info
`MCPError`, `from mcp import MCPError` ile içe aktarılır ve `code`, `message` ile isteğe bağlı
bir `data` yükü alır. Bunlara ne koyarsanız istemci onu alır: SDK, fırlatılan bir
`MCPError`'ı temizlemek yerine olduğu gibi iletir.
## Var olmayan bir kaynak {#a-resource-that-doesnt-exist}
Kaynaklar da aynı çizgiyi çeker ve yaygın durum için adlandırılmış bir istisna sunar.
```python title="server.py" hl_lines="2 13"
--8<-- "docs_src/handling_errors/tutorial003.py"
```
`books://{title}` bir **şablondur**. *Her* başlıkla eşleşir; bu yüzden "URI düzgün biçimli" ile "kitap var" iki farklı sorudur ve ikincisini yalnızca fonksiyonunuz yanıtlayabilir.
Yanıtlayamadığında `ResourceNotFoundError` fırlatın. SDK bunu, spesifikasyonun eksik bir kaynağa atadığı protokol hatasına dönüştürür: `data`'da istenen URI ile birlikte `-32602`; böylece istemci *hangi* okumanın başarısız olduğunu bilir.
```json
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog.",
"data": {"uri": "books://Nothing"}
}
```
Burada `is_error=True` taşıyan yarım bir sonuç olmadığına dikkat edin. Bir kaynak okuması ya içerik döndürür ya da başarısız olur: kaynakların yalnızca protokol yolu vardır. Şablonlar ve kaynaklarla ilgili diğer her şey **[Kaynaklar](resources.md)** sayfasında.
## Hiç fırlatmadığınız hatalar {#errors-you-never-raise}
Hatalı bir argüman fonksiyonunuza asla ulaşmaz.
`get_author`'a dize olmayan bir `title` gönderin; SDK sizi çağırmadan **önce** onu girdi şemasına göre reddeder. Bu da modelin okuyup düzeltebileceği türden, aynı `is_error=True` araç hatasıdır. **[Araçlar](tools.md)** sayfası aynı reddi bir `Field(le=50)` kısıtıyla gösterir.
Bu, yazmadığınız koca bir `raise` ifadesi sınıfı demektir: kendi tür ipuçlarınızı yeniden doğrulamayın.
!!! info
Bu sayfadaki her şey bir **istemcinin** gördüğüdür; testleri yazarken kullanacağınız bellek içi
`Client` da tam olarak aynı şeyi görür. `raise_exceptions=True` bile bir araç hatasını tekrar
traceback'e çevirmez: o bayrak devreye girebilecek noktaya geldiğinde istisnanız çoktan
`is_error=True` sonucuna dönüşmüştür. Doğrulamayı sonuç üzerinde yapın. **[Test etme](../get-started/testing.md)** sayfası bu kalıbı anlatır.
## Özet {#recap}
* Bir araçta **herhangi bir istisna** fırlatın -> çağrı, mesajınız `content`'te olacak şekilde `is_error=True` döndürür. Model bunu okur ve yeniden deneyebilir. Varsayılan budur.
* **`MCPError`** fırlatın -> çağrının kendisi bir JSON-RPC hatasıyla başarısız olur. Model hiçbir şey görmez; bununla host ilgilenir. `code`, `message` ve `data` bozulmadan ulaşır.
* Belirleyici soru: *daha akıllı bir model bundan kaçınabilir miydi?* Evet -> istisna. Hayır -> `MCPError`.
* Bir kaynak işleyicisinden `ResourceNotFoundError` -> protokolün `-32602` kodu, URI `data`'da.
* Hatalı argümanlar, fonksiyonunuz çalışmadan önce şemaya göre reddedilir; bunlar için `raise` yazmazsınız.
* `from mcp import MCPError`; hata kodu sabitleri `mcp.types`'tan gelir.
Hatalar halloldu. Bir sunucunun *sunduğu* her şey bu kadar. Her işleyicinin çalışırken neleri okuyabildiği ve istemciye geri neler yapabildiği bir sonraki bölümde: **[İşleyicinin içinde](../handlers/index.md)**.
En sık karşılaşacağınız SDK hatalarının tam metni, her birinin ne anlama geldiği ve her biri için tek hamlelik çözüm **[Sorun giderme](../troubleshooting.md)** sayfasında.

View file

@ -0,0 +1,35 @@
---
translation:
sections: [09defc170a0da89d]
tool: 1
---
# Sunucular {#servers}
Bir `MCPServer`, bağlı bir istemciye üç temel yapı taşı sunar. Aralarındaki fark, onları kullanmaya kimin karar verdiğidir:
* **[Araç](tools.md)**, *modelin* seçip çağırdığı bir eylemdir. Çoğu kişinin
önce görmek istediği sayfa budur;
**[Yapılandırılmış çıktı](structured-output.md)** ise onun başvuru
eşlikçisidir: bir aracın döndürdüğü şeyin biçimiyle ilgili her şey orada.
* **[Kaynak](resources.md)**, *uygulamanın* okumayı seçtiği salt okunur
veridir. **[URI şablonları](uri-templates.md)** onun başvuru
eşlikçisidir: adresleme sözdiziminin tamamı ve yol güvenliği kuralları.
* **[Prompt](prompts.md)**, bir *kişinin* menüden ya da eğik çizgi
komutuyla adıyla çağırdığı bir mesaj şablonudur.
Bu üç yapı taşının etrafında, bir sunucunun bildirdiği diğer her şey yer alır:
* **[Tamamlamalar](completions.md)**, prompt ve kaynak şablonu argümanları
için sunucu tarafında otomatik tamamlamadır.
* **[Görseller, ses ve simgeler](media.md)**, bir aracın metin dışında
döndürebileceği her şeyi ve bir istemcinin sunucunuzun yanında gösterdiği
simgeleri kapsar.
* **[Hataları ele alma](handling-errors.md)**, modelin toparlayabileceği bir
hata ile asla görmemesi gereken bir hata arasındaki farkı açıklar.
Buradaki her sayfa kendi başına okunabilir; doğrudan ihtiyacınız olana geçin. Henüz
bir sunucu oluşturmadıysanız bunun yerine **[İlk adımlar](../get-started/first-steps.md)** sayfasıyla başlayın.
Kaydettiğiniz fonksiyonların *içinde* olup bitenler (`Context`, bağımlılık enjeksiyonu,
çağrının ortasında kullanıcıdan ek girdi isteme) bir sonraki bölümün konusu:
**[İşleyicinin içinde](../handlers/index.md)**.

View file

@ -0,0 +1,122 @@
---
translation:
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 043f526230dd243d, 6ee3e9bcfd24047a]
tool: 1
---
# Medya {#media}
Bir aracın döndürebileceği tek şey metin değildir.
SDK, ikili sonuçlar için iki yardımcı (**`Image`** ve **`Audio`**) ile sunucunuza, araçlarınıza, kaynaklarınıza ve prompt'larınıza istemcinin arayüzünde bir yüz kazandıran **`Icon`** türünü sunar.
## Görsel döndürme {#returning-an-image}
Dönüş türünü `Image` olarak belirtin, bir dosyaya yönlendirin ve döndürün:
```python title="server.py" hl_lines="8 12 14"
--8<-- "docs_src/media/tutorial001.py"
```
* `Image`, `path` (okunacak bir dosya) veya `data` (ham baytlar) argümanlarından tam olarak birini alır.
* İstemcinin gördüğü MIME türü dosya uzantısından tahmin edilir: `logo.png`, `image/png` olarak bildirilir.
* Burada logolara özgü hiçbir şey yok. `server.py` dosyasının yanındaki herhangi bir PNG iş görür: kodunuzun çizdiği bir grafik, bir diyagram, bir fotoğraf.
`Image` bir protokol türü değil, SDK'nın sağladığı bir kolaylıktır. İletilen veride dönüş değeriniz bir **`ImageContent`** bloğuna dönüşür (dosyanın base64 ile kodlanmış baytları ve MIME türü):
```python
result.content # [ImageContent(type="image", data="iVBORw0KGgoAAAANSUhEUg...", mime_type="image/png")]
result.structured_content # None
```
Dikkat edilecek iki nokta:
* `data` base64'tür. Baytlara hiç dokunmadınız; dosyayı SDK okudu ve kodlamayı yaptı.
* `structured_content` değeri `None`. Bir `Image`, uygulamanın ayrıştıracağı veri değil, modelin bakacağı içeriktir: çıktı şeması yoktur. (Dönüş tür ipucunun şemanın *ta kendisi* olduğu **[Yapılandırılmış çıktı](structured-output.md)** sayfasıyla karşılaştırın.)
!!! info
`ImageContent` ve `AudioContent`, `mcp.types` modülünde, düz bir `str` sonucunun dönüştüğü
`TextContent`'in hemen yanında yer alır (**[Araçlar](tools.md)**). Bir araç sonucu, içerik bloklarından oluşan bir listedir; `Image` ve `Audio`
iki ikili türü üretmenin en kısa yoludur.
### Deneyin {#try-it}
`server.py` dosyasının yanına herhangi bir PNG koyun, adını `logo.png` yapın ve çalıştırın:
```console
uv run mcp dev server.py
```
**Tools** sekmesini açın ve `logo` aracını çağırın. Sonuç bir dize değil: bir `image` içerik bloğu ve Inspector resminizi görüntülüyor. Diskteki dosya ile ekrandaki pikseller arasındaki her şeyi SDK yaptı.
## Ses döndürme {#returning-audio}
`Audio` da aynı biçimdedir. `logo.png` dosyasını yerinde bırakın ve yanına herhangi bir WAV dosyasını `chime.wav` adıyla koyun:
```python title="server.py" hl_lines="18-21"
--8<-- "docs_src/media/tutorial002.py"
```
Sonuç bir **`AudioContent`** bloğudur:
```python
result.content # [AudioContent(type="audio", data="UklGR...", mime_type="audio/wav")]
result.structured_content # None
```
Aynı düzen: diskteki bir dosya girer, base64 ve bir MIME türü çıkar, çıktı şeması yok.
## Baytlar veya dosya {#bytes-or-a-file}
Her iki yardımcı da `path=` yerine `data=` (ham baytlar) kabul eder. Bu, hiçbir zaman kendi dosyasından gelmemiş baytlar içindir: bir veritabanı sütunu, bir HTTP yanıtı, Pillow'un az önce çizdiği bir şey:
```python title="server.py" hl_lines="14 15"
--8<-- "docs_src/media/tutorial003.py"
```
`path=` ile bildirilecek bir şey yoktur: dosya, sonuç oluşturulurken okunur ve MIME türü uzantıdan tahmin edilir:
* `Image`: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`.
* `Audio`: `.wav`, `.mp3`, `.ogg`, `.flac`, `.aac`, `.m4a`.
Tanımadığı bir uzantı `application/octet-stream`'e geri düşer.
!!! check
`data=` ile bir dosya adı yoktur, dolayısıyla tahmin yapılacak bir şey de yoktur. `format=`
argümanını unutursanız SDK bir varsayılana geri düşer: görseller için `image/png`, ses için `audio/wav`.
MP3 baytlarından bu şekilde bir `Audio` oluşturursanız istemciye `mime_type="audio/wav"`
söylenir ve o da sadakatle çözmeyi başaramaz. `data=` geçirdiğinizde `format=` da geçirin.
## Simgeler {#icons}
`Icon` içerik değil, meta veridir. Görseli taşımaz; bir URI ile ona işaret eder ve istemci onu getirip sunucunuzun adının, bir aracın, bir kaynağın veya bir prompt'un yanında gösterebilir.
```python title="server.py" hl_lines="4-5 7 10 16"
--8<-- "docs_src/media/tutorial004.py"
```
* `src`, istemcinin çözümleyebileceği bir URI'dir: `https:` veya simgeyi ek bir getirme olmadan gömmek isterseniz bir `data:` URI'si.
* `mime_type` ve `sizes` (`"48x48"` ya da ölçeklenebilir bir biçim için `"any"`), birkaç tane sunduğunuzda istemcinin doğru olanı seçmesini sağlar.
* `theme="light"` veya `theme="dark"`, bir simgeyi tek bir renk şeması için işaretler.
Aynı `icons=[...]` anahtar sözcüğünü `MCPServer(...)`, `@mcp.tool()`, `@mcp.resource()` ve `@mcp.prompt()` kabul eder.
### İstemcinin bunları gördüğü yer {#where-a-client-sees-them}
Simgeler, süsledikleri şeyle birlikte yolculuk eder. Sunucununkiler istemci bağlandığında `client.server_info` üzerinde gelir (2026 neslinden bağlantılarda isteğe bağlıdır, bu yüzden önce türünü daraltın):
```python
assert client.server_info is not None # python-sdk servers identify themselves by default
client.server_info.icons # [Icon(src="https://example.com/brand-kit.png", mime_type="image/png", sizes=["48x48"])]
```
Bir aracın simgeleri `tools/list`'ten gelen `Tool` nesnesinde, bir kaynağınkiler `resources/list`'ten gelen `Resource`'ta, bir prompt'unkiler `prompts/list`'ten gelen `Prompt`'ta bulunur. Alanın adı her zaman `icons`'tur.
## Özet {#recap}
* Bir araçtan `Image` veya `Audio` döndürün; istemci bir `ImageContent` / `AudioContent` bloğu alır: base64 ile kodlanmış baytlarınız ve bir MIME türü.
* Bunu bir `path=` ile oluşturup MIME türünü uzantının belirlemesine bırakın ya da bellekteki `data=` ile açık bir `format=` kullanın.
* Medya sonuçları `structured_content` ve çıktı şeması taşımaz.
* `Icon` bir işaretçidir: bir `src` URI'si ile isteğe bağlı `mime_type`, `sizes` ve `theme`.
* `icons=[...]` sunucuda, araçlarda, kaynaklarda ve prompt'larda çalışır; istemciler bunları eşleşen nesnelerde bulur.
Bir aracın bir sonuca *koyabileceği* her şey bu kadar. Bir araç *başarısız olduğunda* ne olacağı (ve bundan kimin haberi olması gerektiği) **[Hataları ele alma](handling-errors.md)** sayfasında.

View file

@ -0,0 +1,155 @@
---
translation:
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, ffe823cb0fedd488, f33651add1b59094]
tool: 1
---
# Prompt'lar {#prompts}
**Prompt**, kullanıcının seçtiği bir mesaj şablonudur.
Araçlar model içindir. Prompt ise tam tersi: kullanıcı istemcisindeki bir menüden (bir slash komutu, bir düğme) birini seçer, argümanlarını doldurur ve ortaya çıkan mesajlar sanki kendisi yazmış gibi konuşmaya eklenir.
Metni döndüren bir fonksiyonun üzerine `@mcp.prompt()` koyarak bir prompt tanımlarsınız.
## İlk prompt'unuz {#your-first-prompt}
```python title="server.py" hl_lines="6-9"
--8<-- "docs_src/prompts/tutorial001.py"
```
SDK, bir araçtan okuduğu aynı üç şeyi okur:
* **Ad**, fonksiyonun adıdır: `review_code`.
* İstemcinin gösterdiği **açıklama** docstring'dir: `Review a piece of code.`
* **Argümanlar** parametrelerden gelir. `code` için varsayılan değer yok, bu yüzden zorunludur.
Bir istemci `prompts/list` çağrısından şunu alır:
```json
{
"name": "review_code",
"description": "Review a piece of code.",
"arguments": [
{"name": "code", "required": true}
]
}
```
Burada JSON Schema yok. Prompt argümanları **adlandırılmış dize değerlerinden** oluşan düz bir listedir: bir modelin kurduğu bir veri yükü değil, bir insanın doldurduğu bir form.
### Şablonu işleme {#rendering-it}
İstemci, argümanları geçirerek şablonu `prompts/get` ile işler. Fonksiyonunuz çalışır ve döndürdüğünüz `str` **tek bir kullanıcı mesajına** dönüşür:
```json
{
"description": "Review a piece of code.",
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Please review this code:\n\ndef add(a, b): return a + b"
}
}
],
"resultType": "complete"
}
```
Bir prompt'un tüm yaşamı bu: adıyla listelenir, istendiğinde işlenir, sohbete bırakılır.
!!! check
`required`, fonksiyonunuz çalışmadan önce uygulanır. `review_code`'u `code` olmadan işleyin;
isteğin kendisi bir JSON-RPC hatasıyla (kod `-32603`) başarısız olur:
```text
mcp.shared.exceptions.MCPError: Internal server error
```
Bir modele geri verilecek araç tarzı bir hata sonucu yoktur, çünkü döngüde bir model yoktur:
çağrı bir istisna fırlatır. Nedeni (`Missing required arguments: {'code'}`) sunucunuzun log'una düşer.
### Deneyin {#try-it}
Sunucuyu MCP Inspector ile çalıştırın:
```console
uv run mcp dev server.py
```
**Prompts** sekmesini açın ve `review_code`'u seçin. Inspector, tek bir zorunlu `code` alanı olan bir form çizer. Doldurun, işleyin; geriye tam olarak yukarıdaki kullanıcı mesajı döner.
## Birden fazla mesaj {#more-than-one-message}
Bir kod incelemesi tek bir mesajdır. Bir hata ayıklama oturumu ise bir konuşmadır ve bir prompt bu konuşmanın tamamının temelini atabilir.
`str` yerine bir mesaj listesi döndürün:
```python title="server.py" hl_lines="2 13-20"
--8<-- "docs_src/prompts/tutorial002.py"
```
* `UserMessage` ve `AssistantMessage`, `mcp.server.mcpserver.prompts.base` modülünden gelir. Onlara bir `str` verin, sizin için `TextContent` içine sararlar. Rol, sınıfın adıdır.
* `Message` ortak temel sınıflarıdır. Dönüş tür açıklaması olarak onu kullanın.
`debug_error` işlendiğinde artık sırasıyla üç mesaj üretilir:
```json
{
"description": "Start a debugging conversation.",
"messages": [
{"role": "user", "content": {"type": "text", "text": "I'm seeing this error:"}},
{"role": "user", "content": {"type": "text", "text": "TypeError: 'int' object is not iterable"}},
{
"role": "assistant",
"content": {"type": "text", "text": "I'll help debug that. What have you tried so far?"}
}
],
"resultType": "complete"
}
```
Sonuncusuna dikkat edin. Bir `assistant` turunu önceden doldurmak, yönlendirmeyi kullanıcıya yazdırmadan modelin *bir sonraki* yanıtını yönlendirmenin yoludur.
## Başlıklar ve argüman açıklamaları {#titles-and-argument-descriptions}
`review_code` bir etiket değil, bir fonksiyon adıdır. İstemciye düğmeye koyacak daha iyi bir şey verin ve formun kendini açıklaması için her argümanı tanımlayın:
```python title="server.py" hl_lines="10-13"
--8<-- "docs_src/prompts/tutorial003.py"
```
* `title="Code review"` insan tarafından okunabilir addır; tıpkı bir aracın `title`'ı gibi.
* `Annotated[str, Field(description=...)]`, **[Araçlar](tools.md)** sayfasının bir aracın parametrelerini açıklamak için kullandığı kalıbın aynısıdır. Burada açıklama bir şemaya değil, argümanın üzerine düşer.
* `language` için bir varsayılan değer var, bu yüzden artık zorunlu değildir.
`prompts/list` girdisi artık bir istemcinin iyi bir form çizmek için ihtiyaç duyduğu her şeyi taşır:
```json
{
"name": "review_code",
"title": "Code review",
"description": "Review a piece of code.",
"arguments": [
{"name": "code", "description": "The code to review.", "required": true},
{"name": "language", "description": "The language the code is written in.", "required": false}
]
}
```
!!! info
**[Araçlar](tools.md)** sayfasını okuduysanız bu sayfadaki her şeyi zaten biliyorsunuz. Aynı dekoratör,
açıklama olarak aynı docstring, aynı `Annotated`/`Field`. Değişen tek şey onu kimin
tetiklediği (kullanıcı) ve sonucun nereye gittiğidir (konuşmaya).
## Özet {#recap}
* Bir fonksiyonun üzerindeki `@mcp.prompt()` onu bir prompt yapar. Ad fonksiyondan, açıklama docstring'den gelir.
* Prompt'lar **kullanıcı denetimindedir**: istemci bunları listeler, kullanıcı birini seçer ve argümanları doldurur.
* Argümanlar adlandırılmış dizelerden oluşan düz bir listedir (şema yok). Varsayılanı olan bir parametre isteğe bağlıdır.
* Bir `str` döndürün, tek bir kullanıcı mesajına dönüşür. Çok turlu bir konuşmanın temelini atmak için `UserMessage` / `AssistantMessage` listesi döndürün.
* `title=` ve `Field(description=...)`, bir istemcinin arayüzüne koyduğu şeylerdir.
* Eksik bir zorunlu argüman isteğin tamamını başarısız kılar. Prompt'a özgü bir hata sonucu yoktur.
Bir prompt'un (veya bir kaynak şablonunun) argümanları için sunucu tarafı otomatik tamamlama **[Tamamlamalar](completions.md)** sayfasındadır.

View file

@ -0,0 +1,146 @@
---
translation:
sections: [09df998c2a799f78, 0cf131146d16d4f9, 4e6b91e3f8025346, 8fe4eef576db17ed, 0d0d1ed43e3d0a53]
tool: 1
---
# Kaynaklar {#resources}
**Kaynak**, uygulamanın okuması için sunduğunuz veridir.
Ayrım bu. Araç, **modelin** çağırmaya karar verdiği şeydir. Kaynak ise **uygulamanın** yüklemeye (bir yapılandırma dosyası, bir kayıt, bir belge) ve bağlam olarak modelin önüne koymaya karar verdiği şeydir.
Bir kaynağı, sıradan bir Python fonksiyonunun üzerine `@mcp.resource(uri)` koyarak bildirirsiniz.
## İlk kaynağınız {#your-first-resource}
```python title="server.py" hl_lines="6-8"
--8<-- "docs_src/resources/tutorial001.py"
```
Şekli bir araçla aynı, bir fazlası var: **URI**. Kaynaklara adla değil, adresle erişilir. İstemci `config://app` ister, asla `get_config` istemez.
SDK geri kalanını yine fonksiyondan okur:
* **Ad**, fonksiyonun adıdır: `get_config`.
* İstemcinin gördüğü **açıklama**, docstring'dir.
* **İçerik**, ne döndürürseniz odur.
`resources/list` sırasında istemci şunu alır:
```json
{
"name": "get_config",
"uri": "config://app",
"description": "The active shop configuration.",
"mimeType": "text/plain"
}
```
`config://app` kaynağını okuduğunda ise fonksiyonunuz çalışır ve dönüş değeri metin olarak geri gelir:
```python
result.contents # [TextResourceContents(uri="config://app", mime_type="text/plain", text="theme=dark\nlanguage=en")]
```
!!! tip
Listeleme ucuzdur. Fonksiyonunuz `resources/list` sırasında **çağrılmaz**; yalnızca
`resources/read` sırasında ve yalnızca istenen URI için çağrılır. Bin kaynak sunun,
bedelini yalnızca birinin açtıkları için ödersiniz.
### Deneyin {#try-it}
Sunucuyu MCP Inspector ile çalıştırın:
```console
uv run mcp dev server.py
```
Yazdırdığı URL'yi açın ve **Resources** sekmesine gidin. `config://app`, açıklamasıyla birlikte listede. Tıklayın, Inspector onu okur: iki satırlık yapılandırmanız karşınızda.
## Kaynak şablonları {#resource-templates}
Kayıt başına bir URI ölçeklenmez. URI'ye bir **yer tutucu**, fonksiyona da onunla eşleşen bir parametre koyun:
```python title="server.py" hl_lines="12-13"
--8<-- "docs_src/resources/tutorial002.py"
```
URI'de `{user_id}`, fonksiyonda `user_id: str`. Sözleşmenin tamamı bu.
Bu artık bir **kaynak şablonu** ve yeri değişir: `resources/list` yanıtından çıkar, onun yerine `resources/templates/list` yanıtında görünür; bir adres olarak değil, bir desen olarak:
```json
{
"name": "get_user_profile",
"uriTemplate": "users://{user_id}/profile",
"description": "A customer's profile.",
"mimeType": "text/plain"
}
```
İstemci yer tutucuyu doldurur ve somut bir URI okur: `users://42/profile`, `users://ada/profile`. Hepsine tek bir fonksiyon yanıt verir; eşleşen değer `user_id` olarak geçirilir:
```python
result.contents # [TextResourceContents(uri="users://42/profile", text="User 42: 12 orders since 2021.")]
```
Sonuçtaki `uri` alanına dikkat edin. Bu, şablon değil, istemcinin istediği **somut** URI'dir.
!!! check
Yer tutucular ile parametreler uyuşmak zorunda. URI hâlâ `{user_id}` derken fonksiyon
parametresinin adını `user` olarak değiştirirseniz dekoratör, herhangi bir istemci yanına
bile yaklaşmadan, **içe aktarma sırasında** reddeder:
```text
ValueError: Mismatch between URI parameters {'user_id'} and function parameters {'user'}
```
Bir uyuşmazlık ancak bir hata olabilir; bu yüzden SDK, sunucuyu böyle bir hatayla başlatmayı imkânsız kılar.
Yer tutucu sözdizimi [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) standardıdır: çok parçalı değerler için `{+path}`, isteğe bağlı sorgu parametreleri için `{?q,lang}` ve dahası. SDK ayrıca çıkarılan değerlere varsayılan olarak yol güvenliği denetimleri uygular. Tam başvuru için **[URI şablonları ve yol güvenliği](uri-templates.md)** sayfasına bakın.
`get_user_profile`, `Context` ile işaretlenmiş bir parametre de alabilir. SDK onu hiçbir zaman URI parametresi saymadan enjekte eder; size neler sağladığını **[Context nesnesi](../handlers/context.md)** sayfası anlatır.
## Döndürdükleriniz {#what-you-return}
`str` ile sınırlı değilsiniz. Her kaynağa bir `mime_type` verin ve neyi uygun görüyorsanız onu döndürün:
```python title="server.py" hl_lines="8-9 14-15 20-21"
--8<-- "docs_src/resources/tutorial003.py"
```
* `readme` bir `str` döndürür, bu yüzden olduğu gibi gönderilir. Yaygın durum budur.
* `catalog_stats` bir `dict` döndürür, bu yüzden SDK onu sizin için **JSON metnine** serileştirir:
```json
{
"books": 1204,
"authors": 391
}
```
* `placeholder_cover` `bytes` döndürür, bu yüzden istemci `TextResourceContents` yerine bir `BlobResourceContents` alır; baytlarınız `blob` alanında base64 ile kodlanmış olarak yer alır.
Aynı kural JSON'a serileştirilebilen başka her şey için de geçerlidir: bir liste, bir Pydantic modeli, bir dataclass. `str` değilse ve `bytes` değilse JSON olur.
`mime_type`'ı siz bildirirsiniz; varsayılan olarak `text/plain`. SDK, bunu tahmin etmek için döndürdüğünüz şeyi asla incelemez; bu yüzden etiketlemediğiniz bir `dict` kaynağı yine düz metin olarak duyurulur.
!!! tip
Bunları fonksiyondan türetmek istemediğinizde `@mcp.resource()`, `name=`, `title=` ve
`description=` parametrelerini de kabul eder. Yazacak bir fonksiyon hiç olmadığında ise
`mcp.server.mcpserver.resources` içinde, `mcp.add_resource(...)` ile kaydedeceğiniz hazır
`Resource` sınıfları var (`TextResource`, `BinaryResource`, `FileResource`, `HttpResource`,
`DirectoryResource`).
İstemci bir kaynağa **abone** de olabilir ve kaynak değiştiğinde bildirim alabilir; bu, hikâyenin istemci tarafı ve **[İstemci](../client/index.md)** sayfasında anlatılır.
## Özet {#recap}
* Bir fonksiyonun üzerindeki `@mcp.resource(uri)` onu kaynak yapar. URI adrestir, dönüş değeri içeriktir, docstring açıklamadır.
* URI'deki bir `{placeholder}` onu **şablon** yapar: `resources/templates/list` altında listelenir ve eşleşen her URI'ye tek bir fonksiyon hizmet verir.
* Yer tutucu adları fonksiyonun parametre adlarıyla aynı olmalıdır. Yanlış yaparsanız bunu üretimde değil, içe aktarma sırasında öğrenirsiniz.
* Fonksiyonunuz kaynak listelendiğinde değil, **okunduğunda** çalışır.
* `str` metin olur, `bytes` base64 blob olur, geri kalan her şey JSON metni olur. Etiketi `mime_type=` ile koyarsınız.
* Araçlar modelin eyleme geçmesi içindir. Kaynaklar uygulamanın okuması içindir.
Üçüncü temel yapı taşı, yani bir kişinin menüden seçtiği, **[Prompt'lar](prompts.md)**.

View file

@ -0,0 +1,250 @@
---
translation:
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, e866c192e11d1c14, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c6f7d2a148aa49f4, c851964bb3301907, d715db6f8dccc9cc, ef86634aa70498a7]
tool: 1
---
# Yapılandırılmış çıktı {#structured-output}
Düz bir `str` döndüren bir araç, sonucu iki kez üretir: `content` içinde metin olarak ve `structured_content` içinde `{"result": "..."}` olarak.
Bu sayfa o ikinci kanalla ilgili: nereden geldiği, alabileceği her biçim ve SDK'nın onu nasıl tutarlı tuttuğu.
Kısaca: **dönüş türü açıklaması (annotation) çıktı şemasıdır**. Onu zaten yazdınız.
## Çıktı şeması {#the-output-schema}
```python title="server.py" hl_lines="9"
--8<-- "docs_src/structured_output/tutorial001.py"
```
Önemli olan satır imza: `-> int`.
Bu sayede SDK'nın `tools/list` sırasında gönderdiği araç, parametrelerinizden oluşturduğu girdi şemasının yanında (onu **[Araçlar](tools.md)** sayfası anlatır) bir de `output_schema` taşır:
```json
{
"properties": {
"result": {"title": "Result", "type": "integer"}
},
"required": ["result"],
"title": "get_temperatureOutput",
"type": "object"
}
```
Tek başına bir `int` JSON nesnesi değildir, bu yüzden SDK onu `{"result": ...}` içine **sarar**. Aracı çağırdığınızda iki kanal da dolar:
```python
result.content # [TextContent(text="17")]
result.structured_content # {"result": 17}
```
Her skaler aynı sarmalayıcıyı alır: `str`, `int`, `float`, `bool`, `bytes`, `None`.
## İki kanal {#two-channels}
Neden aynı değer iki kez gönderiliyor?
* `content` **model** içindir. Bir dil modeli metin okur; sonucun gördüğü tek kısmı budur.
* `structured_content`, modelin içinde çalıştığı **uygulama** içindir: "17" geçen bir cümle değil, `17` isteyen kod.
* `output_schema` ikisi arasındaki sözleşmedir ve araç daha hiç çağrılmadan yayımlanır.
Siz tek bir Python değeri döndürürsünüz. Üçünü de SDK doldurur.
## Bir model döndürme {#return-a-model}
Biçimi bir Pydantic `BaseModel` olarak bildirin ve bir örneğini döndürün:
```python title="server.py" hl_lines="8-11 15"
--8<-- "docs_src/structured_output/tutorial002.py"
```
Artık şema `WeatherData`'nın **kendisi**. Sarmalayıcı yok, `result` anahtarı yok:
```json
{
"properties": {
"temperature": {"description": "Degrees Celsius.", "title": "Temperature", "type": "number"},
"humidity": {"description": "Relative humidity, 0 to 1.", "title": "Humidity", "type": "number"},
"conditions": {"title": "Conditions", "type": "string"}
},
"required": ["temperature", "humidity", "conditions"],
"title": "WeatherData",
"type": "object"
}
```
`structured_content` alan alan o nesnedir:
```python
result.structured_content # {"temperature": 16.2, "humidity": 0.83, "conditions": "Overcast"}
```
Model de dışarıda kalmaz. SDK aynı nesneyi `content` için JSON metnine serileştirir:
```json
{
"temperature": 16.2,
"humidity": 0.83,
"conditions": "Overcast"
}
```
`temperature` ve `humidity` üzerindeki `Field(description=...)` bilgisinin şemaya düştüğüne dikkat edin. **Girdilerinizi** tanımlayan aynı `Field`, çıktılarınızı da tanımlar.
!!! info
FastAPI'nin `response_model`'ını kullandıysanız bunu zaten biliyorsunuz: bildirilen yanıt olarak
bir Pydantic modeli, sizin yerinize serileştirilir ve belgelenir. Tek fark, burada bildirimin
tamamının dönüş açıklaması olmasıdır.
## Bir `TypedDict` {#a-typeddict}
Her biçim bir sınıfı hak etmez. Bir `TypedDict` aynı şemayı üretir:
```python title="server.py" hl_lines="8"
--8<-- "docs_src/structured_output/tutorial003.py"
```
`TypedDict` çalışma zamanında düz bir `dict`'tir; siz de onu oluşturup döndürürsünüz. Şema, doğrulama ve `structured_content`, `BaseModel` sürümüyle birebir aynıdır (`TypedDict`'te yeri olmayan açıklamalar hariç).
## Bir dataclass {#a-dataclass}
Dataclass'lar da çalışır; öznitelikleri tür ipucu taşıyan herhangi bir sıradan sınıf da öyle. SDK arka planda açıklamalardan bir Pydantic modeli oluşturur.
```python title="server.py" hl_lines="8-9"
--8<-- "docs_src/structured_output/tutorial004.py"
```
Üç yazım, tek şema. Kod tabanınızda hangisi varsa onu kullanın.
## Listeler {#lists}
Bir `list[...]` de JSON nesnesi değildir, bu yüzden `{"result": ...}` sarmalayıcısını alır; öğe türünüz içinde bir `$defs` başvurusu olarak yer alır:
```python title="server.py" hl_lines="15"
--8<-- "docs_src/structured_output/tutorial005.py"
```
```json
{
"$defs": {
"WeatherData": {
"properties": {
"temperature": {"title": "Temperature", "type": "number"},
"humidity": {"title": "Humidity", "type": "number"},
"conditions": {"title": "Conditions", "type": "string"}
},
"required": ["temperature", "humidity", "conditions"],
"title": "WeatherData",
"type": "object"
}
},
"properties": {
"result": {"items": {"$ref": "#/$defs/WeatherData"}, "title": "Result", "type": "array"}
},
"required": ["result"],
"title": "get_forecastOutput",
"type": "object"
}
```
İki günlük bir tahmin istediğinizde `structured_content`, `{"result": [{...}, {...}]}` olur. `content` ise öğe başına bir tane olmak üzere **iki** `TextContent` bloğuna dönüşür: liste, model için tek bir dizge olarak dökülmek yerine düzleştirilir.
`tuple[...]`, union'lar ve `Optional[...]` aynı şekilde sarılır.
## Sözlükler {#dictionaries}
`dict[str, ...]` zaten bir JSON nesnesi *olan* tek generic türdür, bu yüzden sarılmaz:
```python title="server.py" hl_lines="9"
--8<-- "docs_src/structured_output/tutorial006.py"
```
```json
{
"additionalProperties": {"type": "number"},
"title": "get_temperaturesDictOutput",
"type": "object"
}
```
```python
result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
```
Anahtarlar `str` olmalıdır. Bir `dict[int, float]` JSON nesnesi olamaz, bu yüzden `{"result": ...}` sarmalayıcısına geri düşer.
## Doğrulama {#validation}
`output_schema` belgeleme değildir. Fonksiyonunuz ne döndürürse döndürsün, sunucudan çıkmadan önce **ona göre doğrulanır**.
Değeri elle oluşturduğunuz sürece bunu fark etmezsiniz: Pydantic, `WeatherData`'nızın bir `WeatherData` olduğundan zaten emin olmuştur. Bunu, verinin sizin denetlemediğiniz bir yerden geldiği gün fark edersiniz:
```python title="server.py" hl_lines="9 21"
--8<-- "docs_src/structured_output/tutorial007.py"
```
Açıklama `WeatherData` vaat ediyor. Üst servisin yanıtı `humidity` göndermeyi bırakmış.
!!! check
`get_weather`'ı çağırdığınızda istemciye sessizce yarı boş bir nesne vermez. Çağrı başarısız
olur ve hatanın ilk satırları alanın adını verir:
```text
Error executing tool get_weather: 1 validation error for WeatherData
humidity
Field required [type=missing, input_value={'temperature': 16.2, 'conditions': 'Overcast'}, input_type=dict]
```
Bu metin, `is_error=True` ile araç sonucu olarak geri döner; böylece model, var olmayan bir hava
durumunu kendinden emin biçimde okumak yerine çağrının başarısız olduğunu bilir.
Bu arada, `-> WeatherData` bir araçtan düz bir `dict` döndürmek sorun değil. `json.loads`'un ürettiği tam olarak buydu. Doğrulama Python türüne değil, değere uygulanır.
## Devre dışı bırakma {#opting-out}
Bazen dönüş açıklaması protokol için değil, tür denetleyiciniz içindir. `structured_output=False` geçirin; araç yalnızca metin üretir:
```python title="server.py" hl_lines="6"
--8<-- "docs_src/structured_output/tutorial008.py"
```
`output_schema` yok, sarmalama yok, doğrulama yok. `structured_content` `None`'dır ve `content` döndürdüğünüz dizgedir.
Tersi olan `structured_output=True`, otomatik algılamayı bir zorunluluğa çevirir: dönüş türü şema üretemeyen bir araç, metne geri düşmek yerine içe aktarma anında istisna fırlatır.
## Tür ipucu olmayan bir sınıf {#a-class-without-type-hints}
İstemeden yapılandırılmamış sonuca varmanın bir yolu vardır: **gövdesinde hiç açıklama olmayan** bir sınıf döndürmek.
```python title="server.py" hl_lines="6-9"
--8<-- "docs_src/structured_output/tutorial009.py"
```
`Station`, `name` ve `online` değerlerini `__init__` içinde atar, ama *sınıf* hiçbir şey bildirmez. SDK sınıf açıklamalarını okur, hiçbir şey bulamaz ve vazgeçer.
!!! warning
**Sessizce** vazgeçer. `output_schema` `None`'dır, `structured_content` `None`'dır ve modelin
okuduğu metin nesnenin `repr`'idir:
```text
"<server.Station object at 0x7f539d75b230>"
```
Hata yok, uyarı yok, işe yaramaz bir araç. Açıklamaları sınıf gövdesine taşıyın ya da
`structured_output=True` geçirin; bu, modül içe aktarıldığı anda durumu kesin bir hataya çevirir:
`Function get_station: return type <class 'server.Station'> is not serializable for structured output`.
!!! tip
Tam denetim mi gerekiyor (`CallToolResult`'ı kendiniz oluşturmak ya da uygulamanın görüp
modelin göremediği bir `_meta` eklemek)? Bunun yeri **[Düşük seviyeli Server](../advanced/low-level-server.md)**.
## Özet {#recap}
* **Dönüş türü açıklaması** çıktı şemasıdır. `tools/list` içinde `output_schema` olarak yayımlanır.
* Skalerler, listeler, tuple'lar ve union'lar `{"result": ...}` içine sarılır. Modeller, `TypedDict`'ler, dataclass'lar, açıklamalı sınıflar ve `dict[str, ...]` zaten nesnedir ve oldukları gibi kalırlar.
* Her sonuç hem `content` (metin, model için) **hem de** `structured_content` (veri, uygulama için) taşır.
* Döndürdüğünüz şey şemaya göre doğrulanır. Uyuşmazlık bozuk bir sonuç değil, bir araç hatasıdır.
* `structured_output=False` bir aracı devre dışı bırakır. Tür ipucu olmayan bir sınıf sessizce devre dışı kalır; buna dikkat edin.
Artık bir aracın geri söyleyebileceği her şeye hâkimsiniz. Sırada ikinci ilkel yapı var: **[Kaynaklar](resources.md)**.

View file

@ -0,0 +1,177 @@
---
translation:
sections: [e4cc390d56573409, 8566e2b68594e9ad, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
tool: 1
---
# Araçlar {#tools}
**Araç**, modelin çağırabildiği bir fonksiyondur.
Sıradan bir Python fonksiyonunun üstüne `@mcp.tool()` koyarak bir araç tanımlarsınız. API'nin tamamı bu.
## İlk aracınız {#your-first-tool}
```python title="server.py" hl_lines="6-8"
--8<-- "docs_src/tools/tutorial001.py"
```
Yazdığınıza bir bakın. Şema yok, JSON yok, protokol yok; yalnızca bir fonksiyon. SDK ondan üç şey okur:
* Aracın **adı** fonksiyonun adıdır: `search_books`.
* Modelin gördüğü **açıklama** docstring'dir: `Search the catalog by title or author.`
* Modelin geçirmesine izin verilen **argümanlar** tür ipuçlarından gelir: `query: str` ve `limit: int`.
### Girdi şeması {#the-input-schema}
SDK bu tür ipuçlarından bir JSON Schema üretir ve `tools/list` sırasında istemciye gönderir:
```json
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"title": "Limit", "type": "integer"}
},
"required": ["query", "limit"],
"title": "search_booksArguments"
}
```
Hiçbirinin varsayılan değeri olmadığı için iki argüman da `required` içinde. Bunu birazdan düzelteceksiniz. (`title` anahtarları Pydantic'in ürettiği kalıntılardır; sözleşmeyi oluşturan şey özellikler, türleri ve `required`'dır.)
!!! tip
Tür ipuçları burada dokümantasyon değildir. **Sözleşmenin ta kendisidir**. Bir istemci `"limit": "ten"`
gönderirse SDK bunu, fonksiyonunuz daha çalışmadan reddeder.
### Modele dönen sonuç {#what-the-model-gets-back}
Aracı `{"query": "dune", "limit": 5}` ile çağırın; sonuç iki parçadan oluşur:
```python
result.content # [TextContent(text="Found 3 books matching 'dune' (showing up to 5).")]
result.structured_content # {'result': "Found 3 books matching 'dune' (showing up to 5)."}
```
`content`, **modelin** okuduğu metindir. `structured_content` ise **istemci uygulama** için tür bilgisi taşıyan veridir. Dönüş türünü `-> str` olarak bildirdiğiniz için oradadır.
`structured_content`'i şimdilik dert etmeyin. Araçlarınızdan gerçek Python nesneleri döndürün, gerisi doğru şekilde halledilir; **[Yapılandırılmış çıktı](structured-output.md)** sayfası tamamen bununla ilgili.
### Deneyin {#try-it}
Sunucuyu MCP Inspector ile çalıştırın:
```console
uv run mcp dev server.py
```
Yazdırdığı URL'yi açın, **Tools** sekmesine gidin ve `search_books`'u çağırın.
Inspector, zorunlu bir `query` metin alanı ve zorunlu bir `limit` sayı alanı içeren bir form gösterir. Bu formu tür ipuçlarınızdan oluşturdu. Diğer tüm MCP istemcileri de aynısını yapar.
## İsteğe bağlı argümanlar {#optional-arguments}
Bir parametreye varsayılan değer verin, zorunlu olmaktan çıkar. Hepsi bu. Bildiğiniz Python.
```python title="server.py" hl_lines="7"
--8<-- "docs_src/tools/tutorial002.py"
```
Şema da buna uyar:
```json
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"default": 10, "title": "Limit", "type": "integer"}
},
"required": ["query"],
"title": "search_booksArguments"
}
```
`limit`, `required` listesinden çıktı ve `"default": 10` kazandı. Onu göndermeyen bir istemci, tıpkı Python'da olacağı gibi `10` alır.
## `Field` ile daha zengin şemalar {#richer-schemas-with-field}
Tür ipuçları sizi epey ileri götürür, ancak bazen bir argümanı *açıklamak* ya da kısıtlamak istersiniz.
Türü `Annotated` içine sarın ve bir Pydantic `Field` ekleyin:
```python title="server.py" hl_lines="12-14"
--8<-- "docs_src/tools/tutorial003.py"
```
Üç yeni şey var, hepsi parametrelerin üzerinde:
* `Field(description=...)`: modelin docstring'le birlikte okuduğu, argümana özel bir açıklama.
* `Field(ge=1, le=50)`: sayısal sınırlar. Şemaya `"minimum": 1, "maximum": 50` olarak yansırlar.
* `Literal["fiction", "non-fiction", "poetry"]`: bir enum. Model yalnızca bunlardan birini seçebilir.
!!! check
Kısıtlamalar süs değildir. Aracı `limit=999` ile çağırın; SDK, **fonksiyonunuz çalışmadan önce**
bir araç hatasıyla yanıt verir:
```text
Input should be less than or equal to 50
```
Bu hata araç sonucu olarak modele geri döner; model onu okur ve geçerli bir değerle yeniden dener.
`le=50` ifadesini bir kez yazdınız ve kendi kendini düzelten ajanları bedavaya elde ettiniz.
!!! info
FastAPI veya Pydantic kullandıysanız bunların hepsini zaten biliyorsunuz. Aynı `Field`,
aynı `Annotated`, aynı doğrulama. Burada MCP'ye özgü öğrenilecek hiçbir şey yok.
## Parametre olarak model {#a-model-as-a-parameter}
Bir araç birkaç taneden fazla argüman aldığında bunları bir Pydantic modelinde toplayın:
```python title="server.py" hl_lines="8-11 15"
--8<-- "docs_src/tools/tutorial004.py"
```
`Book` şeması aracın girdi şemasının içine (bir `$defs` referansı olarak) yerleştirilir, model onu bir JSON nesnesi olarak doldurur ve fonksiyonunuz zaten doğrulanmış, `.title`, `.author` ve `.year` öznitelikleri olan **gerçek bir `Book` örneği** alır.
Dilediğiniz gibi karıştırabilirsiniz: model parametrelerinin yanında sıradan parametreler, iç içe modeller, model listeleri. Baştan sona Pydantic.
## `async def` {#async-def}
Bir araç G/Ç yapıyorsa (bir API çağırıyor, dosya okuyor, veritabanı sorguluyorsa) onu `async def` olarak bildirin ve içinde `await` kullanın. SDK onu await eder.
Sıradan bir `def` araç da çalışır: SDK onu bir iş parçacığında çalıştırır, böylece sunucuyu asla engellemez.
Yapılandırılacak başka bir şey yok.
## Adlar, başlıklar ve annotation'lar {#names-titles-and-annotations}
SDK'nın çıkarsadığı her şeyi dekoratörde geçersiz kılabilirsiniz:
```python title="server.py" hl_lines="7-10"
--8<-- "docs_src/tools/tutorial005.py"
```
* `title`, arayüzler için insanların okuyabileceği bir addır. İstemciler `search_books` yerine *"Search the catalog"* gösterir.
* `annotations`, istemci için davranışsal **ipuçlarıdır**:
* `read_only_hint=True`: bu araç hiçbir şeyi değiştirmez.
* `open_world_hint=False`: açık web üzerinde değil, kapalı bir şeyler kümesi (bu katalog) üzerinde çalışır.
* Diğer ikisi, `destructive_hint` ve `idempotent_hint`, *yazan* bir aracı tanımlar: bir şeyi
silebilir mi, ve onu iki kez çağırmak bir kez çağırmakla aynı şey mi? Spesifikasyon her ikisini de
yalnızca salt okunur olmayan araçlar için tanımlar; bu yüzden `search_books` üzerinde hiçbir şey ifade etmezler.
Kurallara uyan bir istemci bunları *"bunu çalıştırmadan önce kullanıcıya sormam gerekir mi?"* gibi kararlar vermek için kullanır. Bunlar ipucudur, güvenlik değil. Bir istemcinin bunlara uyacağına asla güvenmeyin.
!!! tip
Adı ve açıklamayı fonksiyon adından ve docstring'den türetmek istemiyorsanız `@mcp.tool()`
`name=` ve `description=` de kabul eder. Çoğu zaman türetmek istersiniz.
## Özet {#recap}
* Bir fonksiyonun üstündeki `@mcp.tool()` onu araç yapar. Ad fonksiyondan, açıklama docstring'den gelir.
* Tür ipuçları girdi şemasının **ta kendisidir**. Varsayılan değerler argümanları isteğe bağlı yapar.
* `Annotated[..., Field(...)]` açıklama ve kısıtlama ekler; `Literal` enum ekler.
* Yapılandırılmış bir "gövde" almanın yolu Pydantic model parametresidir.
* Hatalı argümanlar sizin yerinize reddedilir; hem de modelin okuyup toparlanabileceği bir hatayla.
* G/Ç için `async def`, geri kalan her şey için sıradan `def`.
`return` ettiğiniz değerin başına neler geldiği **[Yapılandırılmış çıktı](structured-output.md)** sayfasında.

View file

@ -0,0 +1,281 @@
---
translation:
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 101ef9d14bf4ec46, 4b6c4a845438abc7, f98b46bafbee4acd]
tool: 1
---
# URI şablonları ve yol güvenliği {#uri-templates-and-path-safety}
Bu sayfa, [`@mcp.resource`](resources.md) dekoratörünün kabul ettiği URI
şablonu sözdiziminin ve SDK'nın çıkarılan değerlere uyguladığı yol
güvenliği politikasının başvuru kaynağıdır. Kaynakların ne olduğuna ve
ne zaman kullanılacağına dair bir giriş için **[Kaynaklar](resources.md)**
sayfasıyla başlayın; bu sayfa, kaynak bildirmeye zaten alışkın olduğunuzu
ve operatör setinin tamamını, güvenlik ayarlarını ya da düşük seviyeli
bağlantıları aradığınızı varsayar.
Şablon sözdizimi [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570)
standardıdır. SDK, gelen `resources/read` URI'lerini eşleştirmek için
seçilmiş bir alt kümeyi destekler; buna ek olarak, sunmayı amaçladığınız
dizinin dışına çözümlenecek değerleri reddeden bir güvenlik katmanı vardır.
Protokol düzeyindeki ayrıntılar (mesaj biçimleri, yaşam döngüsü, sayfalama)
için [MCP kaynaklar belirtimine](https://modelcontextprotocol.io/specification/latest/server/resources)
bakın.
## Operatör setinin tamamı {#the-full-operator-set}
Düz yer tutucu `{user_id}`, **[Kaynaklar](resources.md)** sayfasının tanıttığı
biçimdir. Dört operatör biçimi daha var; yan yana görebilmeniz için hepsi tek
bir sunucuda:
```python title="server.py" hl_lines="16-17 22-23 28-29 34-35 40-41"
--8<-- "docs_src/uri_templates/tutorial001.py"
```
Vurgulanan her dekoratör, URI'yi parçalamanın farklı bir yoludur.
Aşağıdaki bölümler bunları yukarıdan aşağıya ele alır.
### Basit genişletme: `{name}` {#simple-expansion-name}
`books://{isbn}` düz, gündelik biçimdir. Yer tutucu `isbn` parametresine
eşlenir; yani `books://978-0441172719` okuyan bir istemci
`get_book("978-0441172719")` çağrısına yol açar.
Düz bir `{name}` ilk `/` karakterinde durur. `books://978/extra` eşleşmez
çünkü `978`'den sonraki eğik çizgi yakalamayı bitirir ve `/extra` artar.
### Tür dönüşümü {#type-conversion}
Çıkarılan değerler dize olarak gelir, ancak daha belirli bir tür
bildirebilirsiniz; SDK dönüştürür. `orders://{order_id}`, parametresi
`order_id: int` olan bir fonksiyona düşer; dolayısıyla `orders://12345`
okumak `get_order("12345")` değil `get_order(12345)` çağrısını yapar.
İşleyici, tür dönüştürme yapmadan üzerinde aritmetik işlem yapar
(`order_id + 1`).
### Çok segmentli yollar: `{+name}` {#multi-segment-paths-name}
Eğik çizgi içeren bir değeri yakalamak için `{+name}` kullanın.
`manuals://{+path}` ile:
* `manuals://returns.md`, `path = "returns.md"` verir
* `manuals://printing/setup.md`, `path = "printing/setup.md"` verir
Değer hiyerarşik olduğunda `{+name}` biçimine başvurun: dosya sistemi
yolları, iç içe nesne anahtarları, vekillik ettiğiniz URL yolları.
### Sorgu parametreleri: `{?a,b,c}` {#query-parameters-abc}
`reviews://{isbn}{?limit,sort}`, `limit` ve `sort` parametrelerini `?`
işaretinin ardına koyar. Yol *hangi* kitap olduğunu belirler; sorgu onu
*nasıl* okuduğunuzu ayarlar.
Sorgu parametreleri esnek eşleştirilir: sıra önemli değildir, fazlalıklar
yok sayılır ve verilmeyen parametreler fonksiyonunuzun varsayılanlarına
düşer. Yani `reviews://978-0441172719`, `limit=10, sort="newest"` kullanır;
`reviews://978-0441172719?sort=top` ise yalnızca `sort` değerini geçersiz
kılar.
### Liste olarak yol segmentleri: `{/name*}` {#path-segments-as-a-list-name}
Her yol segmentini eğik çizgili tek bir dize yerine ayrı birer liste öğesi
olarak istiyorsanız `{/name*}` kullanın. `shelves://browse{/path*}` ile,
`shelves://browse/fiction/sci-fi` okuyan bir istemci
`browse_shelf(["fiction", "sci-fi"])` çağrısına yol açar.
### Şablon başvurusu {#template-reference}
En yaygın kalıplar:
| Kalıp | Örnek girdi | Elde ettiğiniz |
|--------------|-----------------------|-------------------------|
| `{name}` | `alice` | `"alice"` |
| `{name}` | `docs/intro.md` | *eşleşme yok* (`/` karakterinde durur) |
| `{+path}` | `docs/intro.md` | `"docs/intro.md"` |
| `{.ext}` | `.json` | `"json"` |
| `{/segment}` | `/v2` | `"v2"` |
| `{?key}` | `?key=value` | `"value"` |
| `{?a,b}` | `?a=1&b=2` | `"1"`, `"2"` |
| `{/path*}` | `/a/b/c` | `["a", "b", "c"]` |
### Ayrıştırıcının reddettikleri {#what-the-parser-rejects}
Birkaç şablon biçimi, ilk istekte başarısız olmak yerine en baştan
yakalanır. `@mcp.resource`, şablonu dekoratör çalıştığında ayrıştırır;
bu yüzden bunların hiçbiri çalışan bir sunucuya ulaşmaz.
`UriTemplate.parse()`, şu durumlarda `InvalidUriTemplate` fırlatır:
* **Aralarında hiçbir şey olmayan iki değişken.** `manuals://{+path}{ext}`
reddedilir: eşleştirme, `path` değişkeninin nerede bitip `ext`
değişkeninin nerede başladığını ayırt edemez. Aralarına bir sabit
koyun (`manuals://{+path}/{ext}`) ya da kendi ayırıcısını sağlayan bir
operatör kullanın. `manuals://{+path}{.ext}` kabul edilir, çünkü `{.ext}`
`.` karakterini kendisi getirir.
* **Birden fazla çok segmentli değişken.** Şablon başına en fazla bir
`{+var}`, `{#var}` ya da patlatılmış (exploded) değişken (`{/var*}`,
`{.var*}`, `{;var*}`). İki tanesi doğası gereği belirsizdir: fazladan
bir segmenti hangisinin yutacağına karar vermenin ilkeli bir yolu yoktur.
* **Olağan sözdizimi hataları**: kapatılmamış bir süslü parantez, iki kez
kullanılan bir değişken adı ya da SDK'nın desteklemediği bir RFC 6570
özelliği, örneğin `{var:3}` önek değiştiricisi veya `{?vars*}` sorgu
patlatması.
Bunun üstüne, bir işleyici parametresi şablonun sondaki `{?...}`/`{&...}`
dizisindeki bir sorgu değişkenine bağlı olup Python varsayılanı yoksa
`@mcp.resource` `ValueError` fırlatır. Bu değişkenler esnek eşleştirilir
(istemci herhangi birini atlayabilir); bu yüzden varsayılanı olmayan bir
parametre, onu atlayan ilk istekte yalnızca anlaşılmaz bir iç hata olarak
ortaya çıkardı. Yukarıdaki sunucudaki `reviews://{isbn}{?limit,sort}` düzgün
biçimli sürümdür: `limit` ve `sort` varsayılan taşır.
## Güvenlik {#security}
Şablon parametreleri istemciden gelir. Denetlenmeden dosya sistemi veya
veritabanı işlemlerine akarlarsa, `../../etc/passwd` gibi değerler sunmayı
amaçladığınız dizinin dışına çözümlenebilir.
### SDK'nın varsayılan olarak denetledikleri {#what-the-sdk-checks-by-default}
İşleyiciniz çalışmadan önce SDK, şu özelliklere sahip her parametreyi
reddeder:
* `..` bileşenleriyle başlangıç dizininden kaçacak olanlar
* mutlak yol (`/etc/passwd`, `C:\Windows`) ya da Windows sürücüye göreli
yol (`C:foo`) gibi görünenler. Sürücüye göreli bir değer ile `x:y` gibi
ad alanlı bir tanımlayıcı dize olarak ayırt edilemez; bu yüzden tek
harf artı iki nokta üst üste biçimindeki her değer varsayılan olarak
reddedilir. Parametre meşru olarak böyle değerler alıyorsa onu muaf tutun
* null bayt (`\x00`) içerenler
`..` denetimi alt dize taraması değil, bileşen tabanlıdır. `v1.0..v2.0` ya
da `HEAD~3..HEAD` gibi değerler geçer, çünkü orada `..` tek başına bir yol
segmenti değildir.
Bu denetimler kodu çözülmüş değere uygulanır; dolayısıyla URI içinde nasıl
kodlanmış olursa olsun dizin geçişini yakalarlar (`../etc`, `..%2Fetc`,
`%2E%2E/etc`, `..%5Cetc`, `%00` hepsi yakalanır).
!!! check
Yukarıdaki sunucudan `manuals://../etc/passwd` okuyun; istek doğrudan
reddedilir: şablon eşleştirme ilk başarısızlıkta durur, bu yüzden
sonraki (muhtemelen daha gevşek) hiçbir şablon yedek olarak denenmez.
İstemci, hiçbir şablonla eşleşmeyen bir URI için göreceği `-32602`
"Unknown resource" hatasının aynısını görür ve `read_manual` hiç
çalışmaz.
### Dosya sistemi işleyicileri: safe_join kullanın {#filesystem-handlers-use-safe_join}
Yerleşik denetimler yaygın durumları durdurur ama sizin sandbox sınırınızı
bilemez. Dosya sistemi erişimi için yolu çözümlemek ve temel dizininizin
içinde kaldığını doğrulamak üzere `safe_join` kullanın:
```python title="server.py" hl_lines="4 14"
--8<-- "docs_src/uri_templates/tutorial002.py"
```
`safe_join`, basit bir dize denetiminin kaçıracağı sembolik bağlantı
kaçışlarını, `..` dizilerini ve mutlak yol hilelerini yakalar. Çözümlenen
yol `DOCS_ROOT` dışına çıkarsa `PathEscapeError` fırlatır; bu, istemciye
`ResourceError` olarak yansır.
### Varsayılanlar engel olduğunda {#when-the-defaults-get-in-the-way}
Bazen denetimler meşru değerleri engeller. Bir katalog içe aktarma aracı
bilerek mutlak bir yol alabilir ya da bir parametre, işleyicinizin dosya
sistemine dokunmadan güvenle yorumladığı `../sibling` gibi göreli bir
başvuru olabilir. O parametreyi muaf tutun ya da politikayı tüm sunucu için
gevşetin:
```python title="server.py" hl_lines="9 16-19"
--8<-- "docs_src/uri_templates/tutorial003.py"
```
* Dekoratördeki `security=ResourceSecurity(exempt_params={"source"})`,
denetimleri yalnızca o kaynaktaki o tek parametre için atlar. Sunucunun
geri kalanı varsayılan politikayı korur.
* `MCPServer` kurucusundaki `resource_security=`, her kaynak için
varsayılanı belirler. Burada `relaxed`, `..` denetimini tamamen kapatır.
Yapılandırılabilir denetimler:
| Ayar | Varsayılan | Ne yapar |
|-------------------------|------------|-------------------------------------|
| `reject_path_traversal` | `True` | Başlangıç dizininden kaçan `..` dizilerini reddeder |
| `reject_absolute_paths` | `True` | `/foo`, `C:\foo`, UNC yollarını ve sürücüye göreli `C:foo` değerini reddeder (`x:y` de yakalanır) |
| `reject_null_bytes` | `True` | `\x00` içeren değerleri reddeder |
| `exempt_params` | boş | Denetimlerin atlanacağı parametre adları |
Bu denetimler sezgisel bir ön süzgeçtir; dosya sistemi erişimi için
kapsama sınırı `safe_join` olmaya devam eder.
!!! tip
İşleyiciniz isteği karşılayamıyorsa (dosya yok, kimlik bilinmiyor) bir
istisna fırlatın. SDK bunu bir hata yanıtına dönüştürür. Protokol hatası
ile araç hatası arasındaki fark için **[Hataları ele alma](handling-errors.md)**
sayfasına bakın.
## Düşük seviyeli Server üzerinde kaynaklar {#resources-on-the-low-level-server}
Düşük seviyeli `Server` üzerine inşa ediyorsanız (bkz. **[Düşük seviyeli
Server](../advanced/low-level-server.md)**), `resources/list` ve
`resources/read` protokol metotları için işleyicileri doğrudan kaydedersiniz.
Dekoratör yoktur; protokol türlerini kendiniz döndürürsünüz.
### Statik kaynaklar {#static-resources}
Sabit URI'ler için bir kayıt defteri tutun ve tam eşleşmeye göre yönlendirin:
```python title="server.py" hl_lines="17 21 27"
--8<-- "docs_src/uri_templates/tutorial004.py"
```
list işleyicisi istemcilere nelerin mevcut olduğunu bildirir; read işleyicisi
içeriği sunar. Önce kayıt defterinizi denetleyin, varsa şablonlara
(aşağıda) geçin, geri kalan her şey için istisna fırlatın.
### Şablonlar {#templates}
`MCPServer`'ın kullandığı şablon motoru `mcp.shared.uri_template` içinde
yaşar ve tek başına çalışır. Aynı ayrıştırma ve eşleştirmeyi alırsınız;
yönlendirmeyi ve güvenlik politikasını kendiniz kurarsınız.
```python title="server.py" hl_lines="13-16 22-25 29 33 45"
--8<-- "docs_src/uri_templates/tutorial005.py"
```
Vurgulanan satırlarda üç şey oluyor:
* **Bir kez ayrıştırın, istek başına eşleştirin.** `UriTemplate.parse()`
şablonu oluşturur; `template.match(uri)` çıkarılan değişkenleri `dict`
olarak, URI uymuyorsa `None` döndürür. URL kod çözme `match()` içinde
olur; kodu çözülmüş değerler yol güvenliği doğrulaması yapılmadan olduğu
gibi döndürülür. Değerler dize olarak çıkar: kendiniz dönüştürün
(`int(matched["id"])`, `Path(matched["path"])`).
* **Güvenlik denetimlerini kendiniz uygulayın.** `MCPServer`'ın varsayılan
olarak çalıştırdığı `..` ve mutlak yol denetimleri
`mcp.shared.path_security` içinde yaşar. `read_manual_safely`,
`MANUALS`'a dokunmadan önce bunları çağırır. Bir parametre dosya sistemi
yolu değilse (ISBN, arama sorgusu), o değer için denetimleri atlayın:
politikayı bir yapılandırma nesnesi üzerinden değil, işleyici başına siz
denetlersiniz.
* **Şablonları aynı kaynaktan listeleyin.** İstemciler şablonları
`resources/templates/list` üzerinden keşfeder. `str(template)` özgün
şablon dizesini geri verir; böylece listeleme ile eşleştirici tek bir
doğruluk kaynağını paylaşır.
## Özet {#recap}
* `{name}` tek bir segmentle eşleşir; `{+name}` eğik çizgileri korur;
`{?a,b}` sorgu dizesinden çeker; `{/name*}` segmentleri bir listeye böler.
* Aralarında hiçbir şey olmayan iki değişken ya da ikinci bir çok segmentli
değişken ayrıştırma anında reddedilir. Sondaki bir `{?...}`/`{&...}`
sorgu değişkenine bağlı parametre bir Python varsayılanı bildirmelidir.
* Parametreye tür ipucu verin (`order_id: int`); SDK dönüştürür.
* Varsayılan güvenlik politikası `..`, mutlak yolları ve null baytları
işleyiciniz çalışmadan önce reddeder; kaynak başına
`security=ResourceSecurity(...)` ile, sunucu genelinde
`resource_security=` ile geçersiz kılın.
* Dosya sistemi erişimi için kapsama sınırı `safe_join`'dur.
* Düşük seviyeli `Server` üzerinde `UriTemplate.parse()` ile ayrıştırın,
`.match()` ile eşleştirin ve `mcp.shared.path_security`'yi kendiniz
uygulayın.