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,130 @@
---
translation:
sections: [72f9c964769076dd, 9a2c14e10935b515, 235299eb78ab12d7, 8aee1e78c8237fb8, 9bd86acd4112138f, 55343cb7f250dc7b]
tool: 1
---
# Completions {#completions}
आपके server के ऊपर UI बना रहा कोई client चाहता है कि user के टाइप करते ही argument values अपने आप पूरे हों: भाषाओं के नाम, repositories के नाम, file paths।
**Completions** वह तरीका है जिससे server ये सुझाव देता है।
## कुछ ऐसा जो complete करने लायक हो {#something-worth-completing}
Completions ठीक दो चीज़ों पर लागू होते हैं: किसी **prompt** के arguments और किसी **resource template** के parameters। तो ऐसे server से शुरू करें जिसमें दोनों में से एक-एक हो:
```python title="server.py" hl_lines="6 12"
--8<-- "docs_src/completions/tutorial001.py"
```
यहाँ अभी तक completions के बारे में कुछ नहीं है।
* `review_code` एक `language` लेता है। user को यह अनुमान नहीं लगाना चाहिए कि आप कौन-सी वर्तनियाँ स्वीकार करते हैं।
* `github_repo` एक `owner` और एक `repo` लेता है। दोनों के लिए free-text boxes रखना खराब form बनाता है।
## Completion handler {#the-completion-handler}
`@mcp.completion()` से सजाया हुआ **एक** function जोड़ें:
```python title="server.py" hl_lines="21-29"
--8<-- "docs_src/completions/tutorial002.py"
```
* हर server में एक ही handler होता है। हर completion request यहीं आती है, और जो complete हो रहा है उसके हिसाब से आप branch करते हैं।
* इसे `async def` होना ज़रूरी है: SDK इसे await करता है।
* इसे तीन arguments मिलते हैं:
* `ref`: **कौन-सा** prompt या resource template, `PromptReference` या `ResourceTemplateReference` के रूप में। दोनों में फ़र्क `isinstance` से पता चलता है।
* `argument`: `argument.name` वह argument है जो complete हो रहा है, `argument.value` वह है जो user ने अब तक टाइप किया है।
* `context`: पहले से तय हो चुके arguments। अभी इसे नज़रअंदाज़ करें।
* आप `Completion(values=[...])` लौटाते हैं, या जब देने को कुछ न हो तो `None`।
!!! tip
`argument.value` वह prefix है जो user ने टाइप किया है। SDK आपके लिए filter **नहीं** करता: जो कुछ
आप `values` में रखते हैं, UI वही दिखाता है। `startswith` आपको खुद लिखना है।
### इसे आज़माएँ {#try-it}
इसे **[Testing](../get-started/testing.md)** वाले in-memory `Client` से चलाएँ।
`client.complete()` को `ref=PromptReference(name="review_code")` और
`argument={"name": "language", "value": "py"}` के साथ call करें:
```python
result.completion.values # ['python']
```
* `ref` वही reference type है जो आपके handler को मिलता है।
* `argument` एक सादी dict है जिसमें ठीक दो keys हैं, `name` और `value`।
खाली `value` भेजें और आपको पूरी सूची वापस मिलती है। `lang.startswith("")` हर भाषा के लिए true है:
```python
result.completion.values # ['go', 'javascript', 'python', 'rust', 'typescript']
```
`code` के बारे में पूछें (ऐसा argument जिसे handler नहीं पहचानता) और वह `None` लौटाता है, जिसे SDK खाली list में बदल देता है:
```python
result.completion.values # []
```
`None` का मतलब है **"कोई सुझाव नहीं"**, error कभी नहीं। UI सादे text box पर लौट आता है।
## एक capability जो आपने कभी declare नहीं की {#a-capability-you-never-declared}
handler register करना ही declaration है। कोई client जोड़ें और देखें:
```python
client.server_capabilities.completions # CompletionsCapability()
```
आपने `completions` कहीं नहीं लिखी। SDK ने handler देखा और आपके लिए capability declare कर दी। हर **optional** capability ऐसे ही काम करती है: handler ही declaration है। (तीनों primitives optional नहीं हैं: `MCPServer` उन्हें हमेशा declare करता है, handlers हों या न हों।)
!!! check
पहली `server.py` पर वापस जाएँ (जिसमें कोई handler नहीं है) और फिर भी उससे पूछें। call
JSON-RPC error के साथ fail होती है:
```text
Method not found
```
और `client.server_capabilities.completions` `None` है। capability का यही मतलब है:
सही ढंग से बना client इसे जाँचता है और वह request कभी नहीं भेजता जिसका जवाब आप नहीं दे सकते।
## एक-दूसरे पर निर्भर arguments {#dependent-arguments}
`github://repos/{owner}/{repo}` में दो parameters हैं, और `repo` के काम के values इस पर निर्भर करते हैं कि पहले कौन-सा `owner` चुना गया।
`context` इसी के लिए है। इसमें वे arguments होते हैं जो user **पहले ही तय कर चुका है**:
```python title="server.py" hl_lines="8-11 34-38"
--8<-- "docs_src/completions/tutorial003.py"
```
* नई branch template के `repo` parameter के लिए चलती है।
* `context.arguments` अब तक चुने गए values (यहाँ, `owner`) की `dict[str, str] | None` है।
* अभी `owner` नहीं है तो कोई समझदार सुझाव भी नहीं, इसलिए handler `None` लौटाता है।
client ये तय हो चुके values `context_arguments=` से भेजता है। इस बार `ref` है
`ResourceTemplateReference(uri="github://repos/{owner}/{repo}")`। खाली `value` के साथ
`repo` माँगें और `context_arguments={"owner": "modelcontextprotocol"}` pass करें:
```python
result.completion.values # ['python-sdk', 'typescript-sdk', 'inspector']
```
`context_arguments=` हटा दें और वही call `[]` लौटाती है। जब तक handler को owner पता न हो, वह नहीं जान सकता कि कौन-से repos सुझाए।
!!! info
`Completion` `total=` और `has_more=` भी लेता है। इन्हें तब set करें जब `values` किसी लंबी सूची का
एक हिस्सा हो, ताकि UI **"और 200 बाकी"** दिखा सके। ज़्यादातर handlers को इनकी कभी ज़रूरत नहीं पड़ती।
## सारांश {#recap}
* Completions **prompt arguments** और **resource template parameters** के लिए सुझाव हैं। और कुछ नहीं।
* `@mcp.completion()` वह एक handler register करता है। यह `async def (ref, argument, context) -> Completion | None` है।
* `isinstance(ref, ...)` और `argument.name` पर branch करें। `argument.value` से filter खुद करें।
* `None` खाली list बन जाता है। यह कभी error नहीं है।
* `context.arguments` में पहले से तय values होती हैं; client उन्हें `context_arguments=` के रूप में देता है।
* `completions` capability उसी पल आ जाती है जब आप handler register करते हैं। उसके बिना, request का जवाब `Method not found` है।
सुझाव तब काम आते हैं जब user अभी prompt या template **भर ही रहा हो**; किसी tool call के **बीच** में उससे सवाल पूछना हो तो आपको **[Elicitation](../handlers/elicitation.md)** चाहिए। text के अलावा tool जो कुछ लौटा सकता है वह सब **[Images, audio और icons](media.md)** में है।

View file

@ -0,0 +1,139 @@
---
translation:
sections: [e33d441f12d50535, 7099694c603e0f5f, c1df4cf9673433e6, c9cd294541422e6e, 6cec073617bfd037, efa92b8f99e908c8, 6a22a29e27fb4601]
tool: 1
---
# errors संभालना {#handling-errors}
tool दो तरीकों से fail हो सकता है, और SDK दोनों के साथ बहुत अलग बर्ताव करता है।
साधारण exception raise करें तो उसे **model** देखता है। `MCPError` raise करें तो उसे **protocol** देखता है।
यह page इन दोनों में से चुनने के बारे में है।
## ऐसा error जिसे model ठीक कर सकता है {#an-error-the-model-can-fix}
ऐसा tool लें जो कुछ खोजता है, और खोज को नाकाम होने दें:
```python title="server.py" hl_lines="11-12"
--8<-- "docs_src/handling_errors/tutorial001.py"
```
उन दो lines में MCP जैसा कुछ नहीं है। `get_author` सादा `ValueError` raise करता है, जैसे कोई भी Python function करता।
इसे ऐसे title से call करें जो catalog में नहीं है और result देखें:
```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
```
* request **सफल रही**। result मौजूद है; caller की तरफ़ कुछ raise नहीं हुआ।
* `is_error` `True` है, और आपके exception का message (आगे tool का नाम लगा हुआ) `content` में है, ठीक वहीं जहाँ model पढ़ता है।
* `structured_content` `None` है। fail हुए call के पास structure करने को कोई return value नहीं होती।
यह **tool error** है, और आपका tool **कोई भी** exception raise करे, default यही है। और लगभग हमेशा आप यही चाहते भी हैं।
आपके tool को call करने वाला model ही है। arguments उसी ने चुने। इसलिए tool error बातचीत का एक turn है: model *"No book titled 'Nothing' in the catalog."* पढ़ता है, समझ जाता है कि उसने title का गलत अंदाज़ा लगाया, और बेहतर title के साथ फिर call करता है। आपने एक `raise` लिखा और बदले में खुद को सुधारने वाला agent मिल गया।
!!! tip
tool से कभी error message `return` न करें। लौटाई गई string का `is_error=False` होता है, इसलिए
model को (और हर client UI को) लगता है कि tool ठीक चला और वही string जवाब थी।
`raise` करें। flag ही संकेत है।
## ऐसा error जिसे model ठीक नहीं कर सकता {#an-error-the-model-cannot-fix}
अब `ValueError` की जगह `MCPError` रखें।
```python title="server.py" hl_lines="1 3 14"
--8<-- "docs_src/handling_errors/tutorial002.py"
```
`MCPError` SDK का **protocol error** है। यही वह एक exception है जिसे tool wrapper catch **नहीं** करता: यह ऊपर propagate होता है, और पूरी `tools/call` request result के बजाय JSON-RPC error के साथ fail हो जाती है।
```json
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog."
}
```
* कोई **result नहीं** है। न `content`, न `is_error`: model के पढ़ने के लिए कुछ भी नहीं।
* इसके बजाय error **host** application को मिलता है, ठीक वैसे ही जैसे tool के बिल्कुल मौजूद न होने पर मिलता।
* `code`, `message`, और `data` जस के तस पहुँचते हैं। `INVALID_PARAMS` `-32602` है; `mcp.types` इसे और बाकी JSON-RPC error codes (`INVALID_REQUEST`, `INTERNAL_ERROR`, ...) को constants के रूप में export करता है, ताकि आपको कभी magic number न लिखना पड़े।
!!! check
वही lookup, वही चूक, लेकिन अब call client की तरफ़ लौटने के बजाय **raise** होता है:
```text
mcp.shared.exceptions.MCPError: No book titled 'Nothing' in the catalog.
```
पहले version ने model को एक वाक्य थमाया जिस पर वह कुछ कर सकता था। यह version उसे कुछ नहीं देता।
`get_author` के लिए यह साफ़ तौर पर बदतर है, और यही अगले section का मुद्दा है।
## कौन सा raise करें {#which-one-to-raise}
दोनों रास्ते दो अलग-अलग सवालों का जवाब देते हैं।
* **कोई भी exception raise करें** जब नाकामी **execution** की हो: आपके tool ने जो करने की कोशिश की, वह नहीं हुआ। call model ने चुना था, इसलिए नतीजा भी model को दिखना चाहिए और उसे संभलने का मौका मिलना चाहिए। गलत वर्तनी वाला title, timeout हो गया upstream API, ऐसी row जो मौजूद नहीं: सब tool errors।
* **`MCPError` raise करें** जब **request खुद** ठुकराई जानी चाहिए: client के पास वह capability नहीं जिस पर आपका tool निर्भर है, server किसी को भी serve करने की हालत में नहीं है, caller ने कोई ज़रूरी चरण छोड़ दिया। model का कोई retry इनमें से किसी को ठीक नहीं करता, इसलिए उसे message थमाने से कुछ हासिल नहीं।
एक सवाल से फ़ैसला हो जाता है: **क्या ज़्यादा समझदार model इससे बच सकता था?** हाँ -> साधारण exception। नहीं -> `MCPError`।
इस कसौटी पर `get_author` के दूसरे version ने गलत चुनाव किया: बेहतर title से बात बन जाती है, इसलिए model message देखने का हक़दार था। वह version आपको mechanism दिखाने के लिए है, उसकी सिफ़ारिश करने के लिए नहीं।
!!! info
`MCPError` `from mcp import MCPError` पर मिलता है और `code`, `message`, और एक optional
`data` payload लेता है। इनमें आप जो भी रखें, client को वही मिलता है: SDK raise किए गए
`MCPError` को sanitise करने के बजाय जस का तस आगे भेज देता है।
## ऐसा resource जो मौजूद नहीं है {#a-resource-that-doesnt-exist}
resources भी यही रेखा खींचते हैं, और आम मामले के लिए एक नाम वाला exception साथ देते हैं।
```python title="server.py" hl_lines="2 13"
--8<-- "docs_src/handling_errors/tutorial003.py"
```
`books://{title}` एक **template** है। यह **किसी भी** title से match करता है, इसलिए "URI सही बना है" और "किताब मौजूद है" दो अलग सवाल हैं, और दूसरे का जवाब सिर्फ़ आपका function दे सकता है।
जब वह न दे सके, `ResourceNotFoundError` raise करें। SDK इसे उस protocol error में बदल देता है जो spec ने गायब resource के लिए तय किया है: `-32602`, और `data` में माँगा गया URI, ताकि client को पता रहे कि **कौन सा** read fail हुआ।
```json
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog.",
"data": {"uri": "books://Nothing"}
}
```
ध्यान दें, यहाँ कोई `is_error=True` वाला आधा-अधूरा result नहीं है। resource read या तो contents लौटाता है या fail होता है: resources के पास सिर्फ़ protocol वाला रास्ता है। templates और resources के बारे में बाकी सब कुछ **[Resources](resources.md)** में है।
## ऐसे errors जो आप कभी raise नहीं करते {#errors-you-never-raise}
गलत argument आपके function तक कभी पहुँचता ही नहीं।
`get_author` को ऐसा `title` भेजें जो string नहीं है, और SDK आपको call करने से **पहले** ही उसे input schema के आधार पर ठुकरा देता है, उसी तरह के `is_error=True` tool error के रूप में जिसे model पढ़ और सुधार सकता है। **[Tools](tools.md)** यही अस्वीकृति `Field(le=50)` constraint के साथ दिखाता है।
इसका मतलब है `raise` statements की एक पूरी श्रेणी जो आपको लिखनी नहीं पड़ती: अपने ही type hints को दोबारा validate न करें।
!!! info
इस page पर सब कुछ वही है जो **client** को दिखता है, और जिस in-memory `Client` से आप
tests लिखेंगे, उसे भी ठीक यही दिखता है। `raise_exceptions=True` भी tool error को वापस
traceback में नहीं बदलता: जब तक वह flag कुछ कर पाता, आपका exception पहले ही
`is_error=True` result बन चुका होता है। result पर assert करें। **[Testing](../get-started/testing.md)** में यह pattern बताया गया है।
## सारांश {#recap}
* tool में **कोई भी exception** raise करें -> call `is_error=True` लौटाता है, `content` में आपके message के साथ। model उसे पढ़ता है और retry कर सकता है। यही default है।
* **`MCPError`** raise करें -> call खुद JSON-RPC error के साथ fail हो जाता है। model को कुछ नहीं दिखता; host इससे निपटता है। `code`, `message`, और `data` जस के तस बचे रहते हैं।
* फ़ैसला करने वाला सवाल: **क्या ज़्यादा समझदार model इससे बच सकता था?** हाँ -> exception। नहीं -> `MCPError`।
* resource handler से `ResourceNotFoundError` -> protocol का `-32602`, `data` में URI के साथ।
* गलत arguments आपका function चलने से पहले ही schema के आधार पर ठुकरा दिए जाते हैं; उनके लिए आप `raise` नहीं करते।
* `from mcp import MCPError`; error-code constants `mcp.types` से आते हैं।
errors संभल गए। server जो कुछ **expose** करता है, वह सब यही है। हर handler क्या पढ़ सकता है, और चलते-चलते client के साथ वापस क्या कर सकता है, यह अगला section है: **[आपके handler के अंदर](../handlers/index.md)**।
जिन SDK errors से आपका सामना होने की सबसे ज़्यादा संभावना है, उनका हूबहू text, हर एक का मतलब, और हर एक का एक-कदम वाला हल **[Troubleshooting](../troubleshooting.md)** में है।

View file

@ -0,0 +1,35 @@
---
translation:
sections: [09defc170a0da89d]
tool: 1
---
# Servers {#servers}
`MCPServer` जुड़े हुए client को तीन primitives देता है। इनमें फ़र्क इस बात का है कि
इन्हें इस्तेमाल करने का फ़ैसला कौन करता है:
* **[tool](tools.md)** वह action है जिसे **model** चुनता और call करता है। यही
वह page है जो ज़्यादातर लोग सबसे पहले चाहते हैं, और
**[Structured Output](structured-output.md)** इसका reference साथी है:
tool जो लौटाता है उसके आकार से जुड़ी हर बात वहाँ है।
* **[resource](resources.md)** read-only data है जिसे **application**
पढ़ना चुनता है। **[URI templates](uri-templates.md)** इसका reference
साथी है: addressing का पूरा syntax और path-safety के नियम।
* **[prompt](prompts.md)** एक message template है जिसे कोई **इंसान** नाम से
invoke करता है, menu से या slash command से।
इन तीन primitives के इर्द-गिर्द वह सब है जो server और declare करता है:
* **[Completions](completions.md)** prompt और resource-template के arguments
के लिए server-side autocomplete है।
* **[Images, audio & icons](media.md)** में वह सब है जो tool text के अलावा
लौटा सकता है, और वे icons जो client आपके server के बगल में दिखाता है।
* **[Handling errors](handling-errors.md)** समझाता है कि जिस error से model
उबर सकता है और जिसे model को कभी नहीं देखना चाहिए, उन दोनों में क्या फ़र्क है।
यहाँ का हर page अपने आप में पूरा है; सीधे उसी पर जाएँ जिसकी ज़रूरत है। अगर अभी तक
कोई server नहीं बनाया है, तो इसके बजाय **[पहले कदम](../get-started/first-steps.md)** से शुरू करें।
जो functions आप register करते हैं उनके **अंदर** क्या होता है (`Context`, dependency injection,
call के बीच में user से और input माँगना), वह अगला section है,
**[आपके handler के अंदर](../handlers/index.md)**।

View file

@ -0,0 +1,122 @@
---
translation:
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 043f526230dd243d, 6ee3e9bcfd24047a]
tool: 1
---
# Media {#media}
tool सिर्फ़ text ही लौटा सके, ऐसा नहीं है।
SDK में binary results के लिए दो helpers (**`Image`** और **`Audio`**) हैं, और एक **`Icon`** type है जो client के UI में आपके server, tools, resources और prompts को एक चेहरा देता है।
## image लौटाना {#returning-an-image}
return type को `Image` से annotate करें, उसे किसी file की ओर point करें, और लौटा दें:
```python title="server.py" hl_lines="8 12 14"
--8<-- "docs_src/media/tutorial001.py"
```
* `Image` इन दोनों में से ठीक एक लेता है: `path` (पढ़ने के लिए file) या `data` (raw bytes)।
* client को जो MIME type दिखता है, उसका अंदाज़ा suffix से लगाया जाता है: `logo.png` को `image/png` बताया जाता है।
* यहाँ logos में कुछ खास नहीं है। `server.py` के बगल में रखी कोई भी PNG चलेगी: आपके code का render किया हुआ chart, कोई diagram, कोई photo।
`Image` SDK की सुविधा है, protocol type नहीं। wire पर आपकी return value एक **`ImageContent`** block बन जाती है (file के bytes base64-encoded, साथ में MIME type):
```python
result.content # [ImageContent(type="image", data="iVBORw0KGgoAAAANSUhEUg...", mime_type="image/png")]
result.structured_content # None
```
दो बातें ध्यान देने लायक हैं:
* `data` base64 है। आपने bytes को छुआ तक नहीं; SDK ने file पढ़ी और encoding की।
* `structured_content` `None` है। `Image` model के देखने के लिए content है, application के parse करने के लिए data नहीं: कोई output schema नहीं है। (इसकी तुलना **[Structured output](structured-output.md)** से करें, जहाँ return annotation **ही** schema है।)
!!! info
`ImageContent` और `AudioContent` `mcp.types` में रहते हैं, ठीक उस `TextContent` के बगल में
जो एक सादा `str` result बन जाता है (**[Tools](tools.md)**)। tool result content blocks की list होता है; दो binary
किस्मों को बनाने का सबसे छोटा रास्ता `Image` और `Audio` हैं।
### इसे आज़माएँ {#try-it}
कोई भी PNG `server.py` के बगल में रखें, उसका नाम `logo.png` रखें, और चलाएँ:
```console
uv run mcp dev server.py
```
**Tools** tab खोलें और `logo` को call करें। result कोई string नहीं है: यह `image` content block है, और Inspector आपकी तस्वीर render करता है। disk पर रखी file से लेकर screen पर दिखते pixels तक, बीच का सारा काम SDK ने किया।
## audio लौटाना {#returning-audio}
`Audio` का आकार भी वही है। `logo.png` को जहाँ था वहीं रहने दें, और कोई भी WAV उसके बगल में `chime.wav` नाम से रख दें:
```python title="server.py" hl_lines="18-21"
--8<-- "docs_src/media/tutorial002.py"
```
result एक **`AudioContent`** block है:
```python
result.content # [AudioContent(type="audio", data="UklGR...", mime_type="audio/wav")]
result.structured_content # None
```
वही बात: अंदर disk पर रखी file जाती है, बाहर base64 और MIME type आते हैं, कोई output schema नहीं।
## bytes या file {#bytes-or-a-file}
दोनों helpers `path=` की जगह `data=` (raw bytes) भी लेते हैं। यह उन bytes के लिए है जो कभी अपनी किसी file से आए ही नहीं — कोई database column, कोई HTTP response, कुछ जो Pillow ने अभी-अभी बनाया:
```python title="server.py" hl_lines="14 15"
--8<-- "docs_src/media/tutorial003.py"
```
`path=` के साथ कुछ declare करने की ज़रूरत नहीं: result बनते समय file पढ़ी जाती है, और MIME type का अंदाज़ा suffix से लगाया जाता है:
* `Image`: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`.
* `Audio`: `.wav`, `.mp3`, `.ogg`, `.flac`, `.aac`, `.m4a`.
जिस suffix को यह नहीं पहचानता, वह `application/octet-stream` पर लौट आता है।
!!! check
`data=` के साथ कोई filename नहीं होता, इसलिए अंदाज़ा लगाने के लिए कुछ नहीं है। `format=` भूल जाएँ तो
SDK default पर आ जाता है: images के लिए `image/png`, audio के लिए `audio/wav`। इस तरह
MP3 bytes से `Audio` बनाएँ तो client को `mime_type="audio/wav"` बताया जाता है, और फिर
वह ईमानदारी से उसे decode करने में नाकाम रहता है। जब `data=` दें, तो `format=` भी दें।
## Icons {#icons}
`Icon` metadata है, content नहीं। इसमें image नहीं होती; यह URI से किसी image की ओर इशारा करता है, और client उसे fetch करके आपके server के नाम, किसी tool, resource या prompt के बगल में दिखा सकता है।
```python title="server.py" hl_lines="4-5 7 10 16"
--8<-- "docs_src/media/tutorial004.py"
```
* `src` ऐसा URI है जिसे client resolve कर सके: `https:`, या `data:` URI अगर आप icon को बिना किसी अतिरिक्त fetch के embed करना चाहें।
* `mime_type` और `sizes` (`"48x48"`, या scalable format के लिए `"any"`) से client सही icon चुन पाता है जब आप कई icons दें।
* `theme="light"` या `theme="dark"` किसी icon को एक colour scheme के लिए चिह्नित करता है।
यही `icons=[...]` keyword `MCPServer(...)`, `@mcp.tool()`, `@mcp.resource()` और `@mcp.prompt()` सब लेते हैं।
### client इन्हें कहाँ देखता है {#where-a-client-sees-them}
icons उसी चीज़ के साथ चलते हैं जिसे वे सजाते हैं। server के icons client के connect होने पर `client.server_info` पर आते हैं (2026 पीढ़ी के connections पर यह optional है, इसलिए पहले इसे narrow करें):
```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"])]
```
tool के icons `tools/list` से मिले `Tool` object पर होते हैं, resource के `resources/list` से मिले `Resource` पर, और prompt के `prompts/list` से मिले `Prompt` पर। field का नाम हमेशा `icons` होता है।
## सारांश {#recap}
* tool से `Image` या `Audio` लौटाएँ तो client को `ImageContent` / `AudioContent` block मिलता है: आपके bytes base64-encoded, MIME type के साथ।
* इसे `path=` से बनाएँ और suffix को MIME type तय करने दें, या in-memory `data=` और स्पष्ट `format=` से बनाएँ।
* media results में न `structured_content` होता है, न output schema।
* `Icon` एक pointer है: `src` URI और साथ में optional `mime_type`, `sizes` और `theme`।
* `icons=[...]` server पर, tools पर, resources पर और prompts पर काम करता है, और clients इन्हें संबंधित objects पर पाते हैं।
tool किसी result **में** जो कुछ डाल सकता है, वह सब यही है। जब tool **नाकाम** होता है तब क्या होता है (और किसे पता चलना चाहिए), यह **[errors संभालना](handling-errors.md)** में है।

View file

@ -0,0 +1,155 @@
---
translation:
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, ffe823cb0fedd488, f33651add1b59094]
tool: 1
---
# Prompts {#prompts}
**Prompt** एक message template है जिसे user चुनता है।
Tools model के लिए होते हैं। Prompt इसका उल्टा है: user अपने client के menu (slash command, button) से कोई prompt चुनता है, उसके arguments भरता है, और render हुए messages बातचीत में ऐसे जुड़ जाते हैं मानो user ने खुद type किए हों।
Prompt declare करने के लिए text लौटाने वाले function पर `@mcp.prompt()` लगाएँ।
## आपका पहला prompt {#your-first-prompt}
```python title="server.py" hl_lines="6-9"
--8<-- "docs_src/prompts/tutorial001.py"
```
SDK वही तीन चीज़ें पढ़ता है जो वह tool से पढ़ता है:
* **Name** function का नाम है: `review_code`।
* Client जो **description** दिखाता है, वह docstring है: `Review a piece of code.`
* **Arguments** parameters से आते हैं। `code` का कोई default नहीं है, इसलिए वह required है।
`prompts/list` से client को यही वापस मिलता है:
```json
{
"name": "review_code",
"description": "Review a piece of code.",
"arguments": [
{"name": "code", "required": true}
]
}
```
यहाँ कोई JSON Schema नहीं है। Prompt arguments **named string values** की एक flat list हैं: ऐसा form जिसे इंसान भरता है, ऐसा payload नहीं जिसे model बनाता है।
### इसे render करना {#rendering-it}
Client arguments pass करते हुए `prompts/get` से template render करता है। आपका function चलता है और जो `str` आप लौटाते हैं, वह **एक user message** बन जाता है:
```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"
}
```
Prompt का पूरा जीवन बस इतना ही है: नाम से list होना, माँगे जाने पर render होना, chat में डाल दिया जाना।
!!! check
`required` आपके function के चलने से पहले ही enforce होता है। `review_code` को `code` के बिना render करें और
request खुद JSON-RPC error (code `-32603`) के साथ fail हो जाती है:
```text
mcp.shared.exceptions.MCPError: Internal server error
```
Model को लौटाने के लिए tool जैसा कोई error result नहीं है, क्योंकि यहाँ कोई model शामिल ही नहीं है:
call raise करता है। वजह (`Missing required arguments: {'code'}`) आपके server के log में जाती है।
### इसे आज़माएँ {#try-it}
Server को MCP Inspector के साथ चलाएँ:
```console
uv run mcp dev server.py
```
**Prompts** tab खोलें और `review_code` चुनें। Inspector एक required `code` field वाला form बनाता है। इसे भरें, render करें, और आपको ठीक ऊपर वाला user message वापस मिलता है।
## एक से ज़्यादा messages {#more-than-one-message}
Code review एक message है। Debugging session एक बातचीत है, और prompt पूरी बातचीत की शुरुआत कर सकता है।
`str` की जगह messages की list लौटाएँ:
```python title="server.py" hl_lines="2 13-20"
--8<-- "docs_src/prompts/tutorial002.py"
```
* `UserMessage` और `AssistantMessage`, `mcp.server.mcpserver.prompts.base` से आते हैं। इन्हें `str` दें और ये उसे आपके लिए `TextContent` में wrap कर देते हैं। Role class का नाम है।
* `Message` इनका साझा base है। इसे return annotation के रूप में इस्तेमाल करें।
`debug_error` को render करने पर अब तीन messages इसी क्रम में बनते हैं:
```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"
}
```
आख़िरी message पर ध्यान दें। `assistant` turn पहले से भरना ही वह तरीका है जिससे आप model के **अगले** जवाब की दिशा तय करते हैं, बिना user से वह निर्देश खुद type करवाए।
## Titles और argument descriptions {#titles-and-argument-descriptions}
`review_code` function का नाम है, label नहीं। Client को button पर लगाने के लिए कुछ बेहतर दें, और हर argument का description लिखें ताकि form खुद ही समझ में आ जाए:
```python title="server.py" hl_lines="10-13"
--8<-- "docs_src/prompts/tutorial003.py"
```
* `title="Code review"` इंसानों के पढ़ने लायक नाम है, ठीक tool के `title` की तरह।
* `Annotated[str, Field(description=...)]` वही pattern है जो **[Tools](tools.md)** tool के parameters describe करने के लिए इस्तेमाल करता है। यहाँ description schema में जाने के बजाय argument पर लगता है।
* `language` का default है, इसलिए वह अब required नहीं रहता।
`prompts/list` entry में अब वह सब है जो client को अच्छा form बनाने के लिए चाहिए:
```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
अगर आपने **[Tools](tools.md)** पढ़ लिया है, तो इस page की हर बात आप पहले से जानते हैं। वही decorator, वही
docstring-as-description, वही `Annotated`/`Field`। बदलता सिर्फ़ इतना है कि इसे
trigger कौन करता है (user) और result कहाँ जाता है (बातचीत में)।
## सारांश {#recap}
* Function पर `@mcp.prompt()` लगाने से वह prompt बन जाता है। नाम function से, description docstring से।
* Prompts **user-controlled** हैं: client इन्हें list करता है, user कोई एक चुनता है और arguments भरता है।
* Arguments named strings की flat list हैं (कोई schema नहीं)। Default वाला parameter optional है।
* `str` लौटाएँ और वह एक user message बन जाता है। Multi-turn बातचीत की शुरुआत करने के लिए `UserMessage` / `AssistantMessage` की list लौटाएँ।
* `title=` और `Field(description=...)` वही हैं जो client अपने UI में दिखाता है।
* कोई required argument छूट जाए तो पूरी request fail होती है। हर prompt का अलग error result नहीं होता।
Prompt के (या resource template के) arguments के लिए server-side autocomplete **[Completions](completions.md)** में है।

View file

@ -0,0 +1,146 @@
---
translation:
sections: [09df998c2a799f78, 0cf131146d16d4f9, 4e6b91e3f8025346, 8fe4eef576db17ed, 0d0d1ed43e3d0a53]
tool: 1
---
# Resources {#resources}
**resource** वह data है जिसे आप application के पढ़ने के लिए expose करते हैं।
फ़र्क बस यही है। tool वह है जिसे call करने का फ़ैसला **model** करता है। resource वह है जिसे load करने का फ़ैसला **application** करता है (कोई config file, कोई record, कोई document) और फिर model के सामने context के रूप में रखता है।
किसी सादे Python function पर `@mcp.resource(uri)` लगाकर आप resource declare करते हैं।
## आपका पहला resource {#your-first-resource}
```python title="server.py" hl_lines="6-8"
--8<-- "docs_src/resources/tutorial001.py"
```
इसका आकार बिल्कुल tool जैसा है, बस एक चीज़ और है: **URI**। resources के पते होते हैं, नाम नहीं। client `config://app` माँगता है, `get_config` कभी नहीं।
बाकी सब SDK अब भी function से ही पढ़ता है:
* **नाम** function का नाम है: `get_config`।
* client को दिखने वाला **description** docstring है।
* **content** वही है जो आप लौटाते हैं।
`resources/list` के दौरान client को यह मिलता है:
```json
{
"name": "get_config",
"uri": "config://app",
"description": "The active shop configuration.",
"mimeType": "text/plain"
}
```
और जब client `config://app` पढ़ता है, तो आपका function चलता है और return value text के रूप में वापस आती है:
```python
result.contents # [TextResourceContents(uri="config://app", mime_type="text/plain", text="theme=dark\nlanguage=en")]
```
!!! tip
listing सस्ती है। आपका function `resources/list` के दौरान call **नहीं** होता, सिर्फ़
`resources/read` के दौरान होता है, और वह भी सिर्फ़ उसी URI के लिए जो माँगा गया हो। हज़ार resources
expose करें, कीमत सिर्फ़ उन्हीं की चुकानी पड़ती है जिन्हें कोई खोलता है।
### इसे आज़माएँ {#try-it}
server को MCP Inspector के साथ चलाएँ:
```console
uv run mcp dev server.py
```
यह जो URL print करता है उसे खोलें और **Resources** tab पर जाएँ। `config://app` अपने description के साथ सूची में है। उस पर click करें और Inspector उसे पढ़ लेता है: config की आपकी दोनों lines सामने हैं।
## Resource templates {#resource-templates}
हर record के लिए एक अलग URI बड़े पैमाने पर नहीं चलता। URI में एक **placeholder** रखें और function पर उससे मेल खाता parameter:
```python title="server.py" hl_lines="12-13"
--8<-- "docs_src/resources/tutorial002.py"
```
URI में `{user_id}`, function पर `user_id: str`। पूरा contract बस इतना ही है।
अब यह **resource template** है, और इसका ठिकाना बदल जाता है: यह `resources/list` छोड़ देता है और उसकी जगह `resources/templates/list` में दिखता है, पते के बजाय pattern के रूप में:
```json
{
"name": "get_user_profile",
"uriTemplate": "users://{user_id}/profile",
"description": "A customer's profile.",
"mimeType": "text/plain"
}
```
client placeholder भरता है और एक ठोस URI पढ़ता है: `users://42/profile`, `users://ada/profile`। एक ही function इन सबका जवाब देता है, और match हुई value `user_id` के रूप में pass की जाती है:
```python
result.contents # [TextResourceContents(uri="users://42/profile", text="User 42: 12 orders since 2021.")]
```
result में `uri` पर ध्यान दें। यह वही **ठोस** URI है जो client ने माँगा था, template नहीं।
!!! check
placeholders और parameters का मेल खाना ज़रूरी है। function parameter का नाम बदलकर
`user` कर दें जबकि URI में अब भी `{user_id}` लिखा हो, तो decorator **import time पर ही** मना कर देता है,
किसी client के उसके पास पहुँचने से पहले:
```text
ValueError: Mismatch between URI parameters {'user_id'} and function parameters {'user'}
```
ऐसा mismatch सिर्फ़ bug ही हो सकता है, इसलिए SDK mismatch के साथ server शुरू होने ही नहीं देता।
placeholder syntax [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) है: कई segments वाली values के लिए `{+path}`, वैकल्पिक query parameters के लिए `{?q,lang}`, और भी बहुत कुछ। SDK निकाली गई values पर default रूप से path-safety जाँच भी लागू करता है। पूरा reference **[URI templates और path safety](uri-templates.md)** में देखें।
`get_user_profile` `Context` से annotate किया गया parameter भी ले सकता है। SDK उसे inject करता है और उसे कभी URI parameter नहीं मानता, और वह आपको क्या देता है यह **[Context](../handlers/context.md)** page बताता है।
## आप क्या लौटाते हैं {#what-you-return}
आप `str` तक सीमित नहीं हैं। हर resource को `mime_type` दें और जो सही बैठे वह लौटाएँ:
```python title="server.py" hl_lines="8-9 14-15 20-21"
--8<-- "docs_src/resources/tutorial003.py"
```
* `readme` `str` लौटाता है, इसलिए वह जस का तस भेजा जाता है। यही आम मामला है।
* `catalog_stats` `dict` लौटाता है, इसलिए SDK उसे आपके लिए **JSON text** में serialise कर देता है:
```json
{
"books": 1204,
"authors": 391
}
```
* `placeholder_cover` `bytes` लौटाता है, इसलिए client को `TextResourceContents` की जगह `BlobResourceContents` मिलता है, जिसके `blob` field में आपके bytes base64-encoded होते हैं।
यही नियम हर उस चीज़ पर लागू होता है जो JSON-serialisable है: list, Pydantic model, dataclass। अगर वह `str` नहीं है और `bytes` नहीं है, तो वह JSON बन जाता है।
`mime_type` declare करना आपका काम है, और इसका default `text/plain` है। इसका अंदाज़ा लगाने के लिए SDK कभी यह नहीं जाँचता कि आप क्या लौटाते हैं, इसलिए जिस `dict` resource पर आप label नहीं लगाते वह अब भी plain text के रूप में ही advertise होता है।
!!! tip
जब आप इन्हें function से derive नहीं करना चाहते, तब `@mcp.resource()` `name=`, `title=` और `description=` भी
स्वीकार करता है। और जब लिखने को कोई function ही न हो, तब
`mcp.server.mcpserver.resources` में तैयार `Resource` classes हैं (`TextResource`,
`BinaryResource`, `FileResource`, `HttpResource`, `DirectoryResource`) जिन्हें आप
`mcp.add_resource(...)` से register करते हैं।
client किसी resource को **subscribe** भी कर सकता है और उसके बदलने पर notification पा सकता है; यह कहानी का client वाला हिस्सा है और **[Client](../client/index.md)** में है।
## सारांश {#recap}
* function पर `@mcp.resource(uri)` उसे resource बना देता है। URI पता है, return value content है, docstring description है।
* URI में `{placeholder}` उसे **template** बना देता है: यह `resources/templates/list` के तहत list होता है और एक ही function हर मेल खाते URI को serve करता है।
* placeholder के नाम function के parameter नामों के बराबर होने चाहिए। गलती करें तो पता import time पर चलता है, production में नहीं।
* आपका function तब चलता है जब resource **पढ़ा** जाता है, तब नहीं जब उसे list किया जाता है।
* `str` text बनता है, `bytes` base64 blob बनता है, बाकी सब JSON text बनता है। label आप `mime_type=` से लगाते हैं।
* tools model के काम करने के लिए हैं। resources application के पढ़ने के लिए हैं।
तीसरा primitive, जिसे कोई इंसान menu से चुनता है, **[Prompts](prompts.md)** है।

View file

@ -0,0 +1,250 @@
---
translation:
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, e866c192e11d1c14, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c6f7d2a148aa49f4, c851964bb3301907, d715db6f8dccc9cc, ef86634aa70498a7]
tool: 1
---
# Structured output {#structured-output}
जो tool सादा `str` लौटाता है, वह result दो बार देता है: `content` में text के रूप में, और `structured_content` में `{"result": "..."}` के रूप में।
यह page उसी दूसरे channel के बारे में है: यह कहाँ से आता है, यह किन-किन रूपों में हो सकता है, और SDK इसे भरोसेमंद कैसे रखता है।
संक्षेप में: **return type annotation ही output schema है**। वह आप पहले ही लिख चुके हैं।
## Output schema {#the-output-schema}
```python title="server.py" hl_lines="9"
--8<-- "docs_src/structured_output/tutorial001.py"
```
जो line मायने रखती है वह signature है: `-> int`।
इसी की वजह से `tools/list` के दौरान SDK जो tool भेजता है, उसमें input schema के साथ-साथ `output_schema` भी होता है। input schema आपके parameters से बनता है (उसकी जानकारी **[Tools](tools.md)** में है):
```json
{
"properties": {
"result": {"title": "Result", "type": "integer"}
},
"required": ["result"],
"title": "get_temperatureOutput",
"type": "object"
}
```
अकेला `int` JSON object नहीं है, इसलिए SDK उसे `{"result": ...}` में **wrap** कर देता है। tool को call करें और दोनों channel भर जाते हैं:
```python
result.content # [TextContent(text="17")]
result.structured_content # {"result": 17}
```
हर scalar को यही wrapper मिलता है: `str`, `int`, `float`, `bool`, `bytes`, `None`।
## दो channel {#two-channels}
एक ही value दो बार क्यों भेजें?
* `content` **model** के लिए है। language model text पढ़ता है; result का यही हिस्सा उसे दिखता है।
* `structured_content` उस **application** के लिए है जिसके अंदर model चलता है: वह code जिसे `17` चाहिए, न कि ऐसा वाक्य जिसमें "17" आता हो।
* `output_schema` इन दोनों के बीच का करार है, जो tool के पहली बार call होने से पहले ही publish हो जाता है।
आप एक Python value लौटाते हैं। SDK तीनों भर देता है।
## Model लौटाना {#return-a-model}
आकार को Pydantic `BaseModel` के रूप में declare करें और उसका instance लौटाएँ:
```python title="server.py" hl_lines="8-11 15"
--8<-- "docs_src/structured_output/tutorial002.py"
```
अब `WeatherData` ही schema **है**। न कोई wrapper, न `result` key:
```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` वही object है, field दर field:
```python
result.structured_content # {"temperature": 16.2, "humidity": 0.83, "conditions": "Overcast"}
```
और model को भी नहीं भूला गया। SDK उसी object को `content` के लिए JSON text में serialize करता है:
```json
{
"temperature": 16.2,
"humidity": 0.83,
"conditions": "Overcast"
}
```
ध्यान दें, `temperature` और `humidity` पर लगा `Field(description=...)` schema में पहुँच गया। जो `Field` आपके **inputs** का वर्णन करता था, वही आपके outputs का भी वर्णन करता है।
!!! info
अगर आपने FastAPI का `response_model` इस्तेमाल किया है तो यह आपको पहले से पता है: declared
response के रूप में Pydantic model, जो आपके लिए serialize और document हो जाता है। फ़र्क सिर्फ़ इतना है कि यहाँ return annotation
ही पूरा declaration है।
## `TypedDict` {#a-typeddict}
हर आकार के लिए class बनाना ज़रूरी नहीं। `TypedDict` से भी वही schema बनता है:
```python title="server.py" hl_lines="8"
--8<-- "docs_src/structured_output/tutorial003.py"
```
runtime पर `TypedDict` सादा `dict` होता है, इसलिए आप वही बनाते और लौटाते हैं। schema, validation और `structured_content` ठीक `BaseModel` वाले version जैसे हैं (descriptions को छोड़कर, जिनके लिए `TypedDict` में कोई जगह नहीं)।
## Dataclass {#a-dataclass}
dataclasses भी काम करते हैं, और हर वह साधारण class भी जिसके attributes पर type hints हों। SDK अंदर ही अंदर annotations से Pydantic model बना लेता है।
```python title="server.py" hl_lines="8-9"
--8<-- "docs_src/structured_output/tutorial004.py"
```
तीन लिखावटें, एक schema। जो आपके codebase में पहले से है, वही इस्तेमाल करें।
## Lists {#lists}
`list[...]` भी JSON object नहीं है, इसलिए इसे भी `{"result": ...}` wrapper मिलता है, और आपका item type उसके अंदर `$defs` reference के रूप में आता है:
```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"
}
```
दो दिन का forecast माँगें और `structured_content` होगा `{"result": [{...}, {...}]}`। `content` **दो** `TextContent` blocks बन जाता है, हर item के लिए एक: model के लिए list को एक string में dump करने के बजाय सपाट कर दिया जाता है।
`tuple[...]`, unions और `Optional[...]` भी इसी तरह wrap होते हैं।
## Dictionaries {#dictionaries}
`dict[str, ...]` वह इकलौता generic है जो पहले से ही JSON object **है**, इसलिए यह wrap नहीं होता:
```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}
```
keys का `str` होना ज़रूरी है। `dict[int, float]` JSON object नहीं बन सकता, इसलिए यह वापस `{"result": ...}` wrapper पर आ जाता है।
## Validation {#validation}
`output_schema` documentation नहीं है। आपका function जो भी लौटाता है, server से बाहर जाने से पहले उसे **इसके मुक़ाबले validate** किया जाता है।
जब तक आप value हाथ से बनाते हैं, इसका पता नहीं चलता: Pydantic पहले ही पक्का कर चुका होता है कि आपका `WeatherData` सच में `WeatherData` है। पता उस दिन चलता है जब data ऐसी जगह से आता है जो आपके हाथ में नहीं:
```python title="server.py" hl_lines="9 21"
--8<-- "docs_src/structured_output/tutorial007.py"
```
annotation `WeatherData` का वादा करता है। upstream response ने `humidity` भेजना बंद कर दिया।
!!! check
`get_weather` को call करें और यह चुपचाप client को आधा-खाली object नहीं थमाता। call fail होता है,
और error की पहली lines field का नाम बताती हैं:
```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]
```
यह text `is_error=True` के साथ tool result बनकर लौटता है, ताकि model को पता रहे कि call fail हुआ है,
बजाय इसके कि वह पूरे भरोसे से ऐसा मौसम पढ़े जो है ही नहीं।
वैसे, `-> WeatherData` वाले tool से सादा `dict` लौटाना ठीक है। `json.loads` ने ठीक वही तो बनाया था। validation value पर होता है, Python type पर नहीं।
## इससे बाहर रहना {#opting-out}
कभी-कभी return annotation आपके type checker के लिए होता है, protocol के लिए नहीं। `structured_output=False` pass करें और tool सिर्फ़ text वाला हो जाता है:
```python title="server.py" hl_lines="6"
--8<-- "docs_src/structured_output/tutorial008.py"
```
न `output_schema`, न wrapping, न validation। `structured_content` `None` है और `content` वह string है जो आपने लौटाई।
इसका उल्टा, `structured_output=True`, automatic detection को शर्त बना देता है: जिस tool का return type schema नहीं बना सकता, वह text पर वापस आने के बजाय import के समय ही raise करता है।
## बिना type hints वाली class {#a-class-without-type-hints}
बिना माँगे unstructured रह जाने का एक तरीका है: ऐसी class लौटाना जिसकी **body पर कोई annotations न हों**।
```python title="server.py" hl_lines="6-9"
--8<-- "docs_src/structured_output/tutorial009.py"
```
`Station` `__init__` के अंदर `name` और `online` set करती है, लेकिन **class** ख़ुद कुछ declare नहीं करती। SDK class annotations पढ़ता है, कोई नहीं मिलता, और हार मान लेता है।
!!! warning
वह **चुपचाप** हार मानता है। `output_schema` `None` है, `structured_content` `None` है, और जो text
model पढ़ता है वह object का `repr` है:
```text
"<server.Station object at 0x7f539d75b230>"
```
न error, न warning, बस एक बेकार tool। annotations को class body पर ले जाएँ, या
`structured_output=True` pass करें, जो module के import होते ही इसे hard error बना देता है:
`Function get_station: return type <class 'server.Station'> is not serializable for structured output`।
!!! tip
पूरा control चाहिए (`CallToolResult` ख़ुद बनाना, या ऐसा `_meta` जोड़ना जो
application देख सके पर model नहीं)? उसके लिए **[Low-level Server](../advanced/low-level-server.md)** है।
## सारांश {#recap}
* **return type annotation** ही output schema है। यह `tools/list` में `output_schema` के रूप में publish होता है।
* scalars, lists, tuples और unions `{"result": ...}` में wrap होते हैं। models, `TypedDict`, dataclasses, annotated classes और `dict[str, ...]` पहले से object हैं और जैसे हैं वैसे ही रहते हैं।
* हर result में `content` (text, model के लिए) **और** `structured_content` (data, application के लिए) होता है।
* आप जो लौटाते हैं वह schema के मुक़ाबले validate होता है। मेल न खाना tool error है, ख़राब result नहीं।
* `structured_output=False` tool को इससे बाहर रखता है। बिना type hints वाली class चुपचाप बाहर हो जाती है; इस पर नज़र रखें।
अब tool जो कुछ भी जवाब में कह सकता है, वह सब आपके हाथ में है। आगे, दूसरा primitive: **[Resources](resources.md)**।

View file

@ -0,0 +1,177 @@
---
translation:
sections: [e4cc390d56573409, 8566e2b68594e9ad, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
tool: 1
---
# Tools {#tools}
**tool** ऐसा function है जिसे model call कर सकता है।
किसी सादे Python function पर `@mcp.tool()` लगाकर आप tool declare करते हैं। पूरा API बस इतना ही है।
## आपका पहला tool {#your-first-tool}
```python title="server.py" hl_lines="6-8"
--8<-- "docs_src/tools/tutorial001.py"
```
देखें आपने क्या लिखा। न कोई schema, न JSON, न protocol, बस एक function। SDK इससे तीन चीज़ें पढ़ता है:
* tool का **नाम** function का नाम है: `search_books`।
* model को जो **description** दिखता है वह docstring है: `Search the catalog by title or author.`
* model जो **arguments** pass कर सकता है वे type hints से आते हैं: `query: str` और `limit: int`।
### Input schema {#the-input-schema}
इन्हीं type hints से SDK एक JSON Schema बनाता है और `tools/list` के दौरान client को भेजता है:
```json
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"title": "Limit", "type": "integer"}
},
"required": ["query", "limit"],
"title": "search_booksArguments"
}
```
दोनों arguments `required` में हैं क्योंकि किसी का भी default नहीं है। इसे आप थोड़ी ही देर में ठीक करेंगे। (`title` keys Pydantic की देन हैं; properties, उनके types और `required` ही असली contract हैं।)
!!! tip
यहाँ type hints documentation नहीं हैं। वे ही **contract** हैं। अगर कोई client `"limit": "ten"` भेजता है,
तो SDK उसे आपके function के चलने से पहले ही reject कर देता है।
### model को क्या वापस मिलता है {#what-the-model-gets-back}
tool को `{"query": "dune", "limit": 5}` के साथ call करें और result के दो हिस्से होते हैं:
```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` वह text है जो **model** पढ़ता है। `structured_content` **client application** के लिए typed data है। यह इसलिए मौजूद है क्योंकि आपने return type `-> str` declare किया।
`structured_content` की अभी चिंता न करें। अपने tools से असली Python objects लौटाएँ और सही चीज़ अपने-आप होती है; **[Structured Output](structured-output.md)** page पूरा इसी बारे में है।
### इसे आज़माएँ {#try-it}
server को MCP Inspector के साथ चलाएँ:
```console
uv run mcp dev server.py
```
यह जो URL print करे उसे खोलें, **Tools** tab पर जाएँ, और `search_books` call करें।
Inspector एक form दिखाता है जिसमें एक required `query` text field और एक required `limit` number field है। यह form उसने आपके type hints से बनाया। बाकी हर MCP client भी यही करेगा।
## Optional arguments {#optional-arguments}
किसी parameter को default value दें और वह required नहीं रहता। बस इतना ही। यह सिर्फ़ Python है।
```python title="server.py" hl_lines="7"
--8<-- "docs_src/tools/tutorial002.py"
```
schema भी साथ बदलता है:
```json
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"default": 10, "title": "Limit", "type": "integer"}
},
"required": ["query"],
"title": "search_booksArguments"
}
```
`limit` `required` से बाहर हो गया और उसे `"default": 10` मिल गया। जो client इसे छोड़ देता है उसे `10` मिलता है, ठीक वैसे ही जैसे Python में होता।
## `Field` के साथ ज़्यादा विस्तृत schemas {#richer-schemas-with-field}
type hints से काफ़ी काम चल जाता है, लेकिन कभी-कभी आप किसी argument की **description देना** चाहते हैं, या उस पर constraints लगाना।
type को `Annotated` में लपेटें और एक Pydantic `Field` जोड़ें:
```python title="server.py" hl_lines="12-14"
--8<-- "docs_src/tools/tutorial003.py"
```
तीन नई चीज़ें, सब parameters पर:
* `Field(description=...)`: हर argument की अपनी description, जिसे model docstring के साथ पढ़ता है।
* `Field(ge=1, le=50)`: संख्या की सीमाएँ। ये schema में `"minimum": 1, "maximum": 50` बनकर पहुँचती हैं।
* `Literal["fiction", "non-fiction", "poetry"]`: एक enum। model इन्हीं में से कोई एक चुन सकता है।
!!! check
constraints सजावट नहीं हैं। tool को `limit=999` के साथ call करें और SDK
**आपके function के चलने से पहले ही** tool error के साथ जवाब देता है:
```text
Input should be less than or equal to 50
```
यह error tool result के रूप में model के पास वापस जाता है, model इसे पढ़ता है और सही value के साथ
दोबारा कोशिश करता है। आपने एक बार `le=50` लिखा और खुद को सुधारने वाले agents मुफ़्त में मिल गए।
!!! info
अगर आपने FastAPI या Pydantic इस्तेमाल किया है, तो यह सब आप पहले से जानते हैं। वही `Field`,
वही `Annotated`, वही validation। यहाँ MCP से जुड़ा कुछ नया सीखने को नहीं है।
## parameter के रूप में model {#a-model-as-a-parameter}
जब कोई tool दो-तीन से ज़्यादा arguments लेता है, तो उन्हें एक Pydantic model में समेट लें:
```python title="server.py" hl_lines="8-11 15"
--8<-- "docs_src/tools/tutorial004.py"
```
`Book` schema tool के input schema के अंदर nested होता है (एक `$defs` reference के रूप में), model इसे JSON object के रूप में भरता है, और आपके function को एक **असली `Book` instance** मिलता है, पहले से validated, जिसमें `.title`, `.author` और `.year` attributes हैं।
आप इन्हें मिला-जुला सकते हैं: model parameters के साथ सादे parameters, nested models, models की lists। नीचे तक सब Pydantic ही है।
## `async def` {#async-def}
अगर कोई tool I/O करता है (कोई API call करता है, file पढ़ता है, database से query करता है), तो उसे `async def` declare करें और उसके अंदर `await` करें। SDK उसे await करता है।
सादा `def` tool भी चलता है: SDK उसे एक thread में चलाता है ताकि वह server को कभी block न करे।
और कुछ configure करने को नहीं है।
## नाम, titles और annotations {#names-titles-and-annotations}
SDK जो कुछ भी अनुमान लगाता है, उसे आप decorator में override कर सकते हैं:
```python title="server.py" hl_lines="7-10"
--8<-- "docs_src/tools/tutorial005.py"
```
* `title` UIs के लिए इंसानों के पढ़ने लायक नाम है। clients `search_books` की जगह *"Search the catalog"* दिखाते हैं।
* `annotations` client के लिए व्यवहार से जुड़े **hints** हैं:
* `read_only_hint=True`: यह tool कुछ नहीं बदलता।
* `open_world_hint=False`: यह चीज़ों के एक बंद set (इस catalog) पर काम करता है, खुले web पर नहीं।
* बाकी दो, `destructive_hint` और `idempotent_hint`, ऐसे tool के बारे में बताते हैं जो **लिखता** है: क्या वह
कुछ delete कर सकता है, और क्या उसे दो बार call करना एक बार call करने जैसा ही है? spec दोनों को
सिर्फ़ non-read-only tools के लिए define करता है, इसलिए `search_books` पर ये कुछ नहीं कहते।
सलीकेदार client "क्या इसे चलाने से पहले मुझे user से पूछना होगा?" जैसी बातें इन्हीं से तय करता है। ये hints हैं, security नहीं। कभी इस भरोसे न रहें कि client इनका पालन करेगा।
!!! tip
अगर आप इन्हें function के नाम और docstring से नहीं निकालना चाहते, तो `@mcp.tool()` `name=` और `description=` भी
स्वीकार करता है। ज़्यादातर वक्त आप उन्हीं से निकालना चाहेंगे।
## सारांश {#recap}
* function पर `@mcp.tool()` उसे tool बना देता है। नाम function से, description docstring से।
* type hints **ही** input schema हैं। defaults arguments को optional बनाते हैं।
* `Annotated[..., Field(...)]` descriptions और constraints जोड़ता है; `Literal` enums जोड़ता है।
* structured "body" लेने का तरीका Pydantic model parameter है।
* गलत arguments आपके लिए reject कर दिए जाते हैं, ऐसे error के साथ जिसे model पढ़ सके और संभल सके।
* I/O के लिए `async def`, बाकी सब के लिए सादा `def`।
जो value आप `return` करते हैं उसका क्या होता है, यह **[Structured Output](structured-output.md)** में है।

View file

@ -0,0 +1,190 @@
---
translation:
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 101ef9d14bf4ec46, 4b6c4a845438abc7, f98b46bafbee4acd]
tool: 1
---
# URI templates और path safety {#uri-templates-and-path-safety}
यह उस URI-template syntax का reference है जिसे
[`@mcp.resource`](resources.md) स्वीकार करता है, और उस path-safety policy का भी जो SDK निकाली गई values पर लागू करता है। resources क्या हैं और उन्हें कब इस्तेमाल करना है, इसके परिचय के लिए **[Resources](resources.md)** से शुरू करें; यह page मानकर चलता है कि आप resource declare करने में पहले से सहज हैं और अब पूरा operator set, security से जुड़े विकल्प, या low-level wiring जानना चाहते हैं।
template syntax [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570) है।
SDK इसका एक subset support करता है जो आने वाले `resources/read` URIs को match करने के लिए चुना गया है, साथ ही एक security layer भी है जो ऐसी values को reject करती है जो उस directory के बाहर resolve होतीं जिसे आप serve करना चाहते हैं। protocol-स्तर के विवरण (message formats, lifecycle, pagination) के लिए
[MCP resources specification](https://modelcontextprotocol.io/specification/latest/server/resources) देखें।
## पूरा operator set {#the-full-operator-set}
सादा placeholder, `{user_id}`, वही है जिसका परिचय **[Resources](resources.md)** देता है। operator के चार और रूप हैं; यहाँ वे एक ही server पर हैं ताकि आप उन्हें साथ-साथ देख सकें:
```python title="server.py" hl_lines="16-17 22-23 28-29 34-35 40-41"
--8<-- "docs_src/uri_templates/tutorial001.py"
```
हर highlighted decorator URI को बाँटने का अलग तरीका है।
नीचे के sections इन्हें ऊपर से नीचे तक एक-एक करके समझाते हैं।
### Simple expansion: `{name}` {#simple-expansion-name}
`books://{isbn}` सादा, रोज़मर्रा का रूप है। placeholder `isbn` parameter से जुड़ता है, इसलिए `books://978-0441172719` पढ़ने वाला client
`get_book("978-0441172719")` call करता है।
सादा `{name}` पहले `/` पर रुक जाता है। `books://978/extra` match नहीं करता क्योंकि `978` के बाद का slash capture को खत्म कर देता है और `/extra` बचा रह जाता है।
### Type conversion {#type-conversion}
निकाली गई values strings के रूप में आती हैं, लेकिन आप ज़्यादा सटीक type declare कर सकते हैं और SDK convert कर देगा। `orders://{order_id}` ऐसे function में पहुँचता है जिसका parameter `order_id: int` है, इसलिए `orders://12345` पढ़ने पर
`get_order(12345)` call होता है, `get_order("12345")` नहीं। handler बिना cast के उस पर arithmetic करता है (`order_id + 1`)।
### Multi-segment paths: `{+name}` {#multi-segment-paths-name}
ऐसी value capture करने के लिए जिसमें slashes हों, `{+name}` इस्तेमाल करें।
`manuals://{+path}` के साथ:
* `manuals://returns.md` से `path = "returns.md"` मिलता है
* `manuals://printing/setup.md` से `path = "printing/setup.md"` मिलता है
जब भी value hierarchical हो, `{+name}` चुनें: filesystem paths, nested object keys, वे URL paths जिन्हें आप proxy कर रहे हैं।
### Query parameters: `{?a,b,c}` {#query-parameters-abc}
`reviews://{isbn}{?limit,sort}` `limit` और `sort` को `?` के बाद रखता है।
path बताता है **कौन-सी** किताब; query तय करती है उसे **कैसे** पढ़ना है।
query params उदारता से match होते हैं: क्रम मायने नहीं रखता, अतिरिक्त params नज़रअंदाज़ होते हैं, और छोड़े गए params आपके function defaults पर आ जाते हैं। इसलिए
`reviews://978-0441172719` `limit=10, sort="newest"` इस्तेमाल करता है, और
`reviews://978-0441172719?sort=top` सिर्फ़ `sort` को override करता है।
### List के रूप में path segments: `{/name*}` {#path-segments-as-a-list-name}
अगर आप हर path segment को slashes वाली एक string की जगह list के अलग-अलग item के रूप में चाहते हैं, तो `{/name*}` इस्तेमाल करें। `shelves://browse{/path*}` के साथ, `shelves://browse/fiction/sci-fi` पढ़ने वाला client
`browse_shelf(["fiction", "sci-fi"])` call करता है।
### Template reference {#template-reference}
सबसे आम patterns:
| Pattern | उदाहरण input | आपको मिलता है |
|--------------|-----------------------|-------------------------|
| `{name}` | `alice` | `"alice"` |
| `{name}` | `docs/intro.md` | **कोई match नहीं** (`/` पर रुकता है) |
| `{+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"]` |
### Parser क्या reject करता है {#what-the-parser-rejects}
template के कुछ आकार पहली request पर fail होने की बजाय शुरू में ही पकड़ लिए जाते हैं। `@mcp.resource` decorator चलते समय template को parse करता है, इसलिए इनमें से कोई भी चलते हुए server तक कभी नहीं पहुँचता।
`UriTemplate.parse()` इनके लिए `InvalidUriTemplate` raise करता है:
* **दो variables जिनके बीच कुछ न हो।** `manuals://{+path}{ext}`
reject होता है: matching यह नहीं बता सकती कि `path` कहाँ खत्म होता है और `ext` कहाँ शुरू होता है।
उनके बीच कोई literal रखें (`manuals://{+path}/{ext}`), या ऐसा operator इस्तेमाल करें जो अपना delimiter खुद देता हो। `manuals://{+path}{.ext}`
स्वीकार होता है क्योंकि `{.ext}` खुद `.` जोड़ता है।
* **एक से ज़्यादा multi-segment variable।** हर template में `{+var}`,
`{#var}`, या exploded variable (`{/var*}`, `{.var*}`, `{;var*}`) में से ज़्यादा से ज़्यादा एक। दो होना स्वभाव से ही अस्पष्ट है: यह तय करने का कोई सिद्धांत-सम्मत तरीका नहीं है कि अतिरिक्त segment किसमें समाए।
* **आम syntax errors**: बिना बंद किया brace, दो बार इस्तेमाल हुआ variable नाम, या RFC 6570 का कोई ऐसा feature जिसे SDK support नहीं करता, जैसे `{var:3}` prefix modifier या `{?vars*}` query explode।
इसके अलावा, `@mcp.resource` `ValueError` raise करता है जब handler का कोई parameter template के आखिरी `{?...}`/`{&...}` हिस्से के किसी query variable से बँधा हो लेकिन उसका कोई Python default न हो। वे variables उदारता से match होते हैं (client उनमें से कोई भी छोड़ सकता है), इसलिए बिना default वाला parameter सिर्फ़ उसे छोड़ने वाली पहली request पर एक अस्पष्ट internal error के रूप में सामने आता। ऊपर के server में `reviews://{isbn}{?limit,sort}` सही बना हुआ रूप है: `limit` और `sort` दोनों के defaults हैं।
## Security {#security}
template parameters client से आते हैं। अगर वे बिना जाँच के filesystem या database operations में चले जाएँ, तो `../../etc/passwd` जैसी values उस directory के बाहर resolve हो सकती हैं जिसे आप serve करना चाहते थे।
### SDK default रूप से क्या जाँचता है {#what-the-sdk-checks-by-default}
आपका handler चलने से पहले, SDK हर उस parameter को reject करता है जो:
* `..` components के ज़रिए अपनी शुरुआती directory से बाहर निकलता हो
* absolute path जैसा दिखता हो (`/etc/passwd`, `C:\Windows`) या
Windows का drive-relative path हो (`C:foo`)। drive-relative value और `x:y` जैसा namespaced identifier strings के रूप में एक-दूसरे से अलग नहीं किए जा सकते, इसलिए एक-अक्षर-और-colon वाली कोई भी value default रूप से reject होती है; अगर parameter को वाजिब तौर पर ऐसी values मिलती हैं तो उसे exempt करें
* null byte (`\x00`) रखता हो
`..` की जाँच component-आधारित है, substring scan नहीं। `v1.0..v2.0` या `HEAD~3..HEAD` जैसी values pass होती हैं क्योंकि वहाँ `..` कोई अलग path segment नहीं है।
ये जाँचें decoded value पर लागू होती हैं, इसलिए traversal URI में चाहे जैसे भी encode किया गया हो, पकड़ा जाता है (`../etc`, `..%2Fetc`,
`%2E%2E/etc`, `..%5Cetc`, `%00` सब पकड़े जाते हैं)।
!!! check
ऊपर के server से `manuals://../etc/passwd` पढ़ें और request सीधे reject हो जाती है: template matching पहली विफलता पर ही रुक जाती है, इसलिए बाद का कोई (संभवतः ज़्यादा उदार) template fallback के रूप में आज़माया नहीं जाता। client को वही `-32602` "Unknown resource" error दिखता है जो किसी भी template से match न करने वाले URI के लिए दिखता, और `read_manual` कभी नहीं चलता।
### Filesystem handlers: safe_join इस्तेमाल करें {#filesystem-handlers-use-safe_join}
built-in जाँचें आम मामलों को रोकती हैं लेकिन आपकी sandbox सीमा नहीं जान सकतीं। filesystem access के लिए, path resolve करने और यह पक्का करने के लिए कि वह आपकी base directory के अंदर ही रहे, `safe_join` इस्तेमाल करें:
```python title="server.py" hl_lines="4 14"
--8<-- "docs_src/uri_templates/tutorial002.py"
```
`safe_join` symlink escapes, `..` sequences, और absolute-path की वे चालें पकड़ता है जो सादी string जाँच से छूट जातीं। अगर resolved path `DOCS_ROOT` से बाहर निकलता है, तो यह `PathEscapeError` raise करता है, जो client तक `ResourceError` के रूप में पहुँचता है।
### जब defaults आड़े आएँ {#when-the-defaults-get-in-the-way}
कभी-कभी ये जाँचें वाजिब values को रोक देती हैं। catalog-import tool जानबूझकर absolute path ले सकता है, या कोई parameter `../sibling` जैसा relative reference हो सकता है जिसे आपका handler filesystem छुए बिना सुरक्षित रूप से समझता है। उस parameter को exempt करें, या पूरे server के लिए policy ढीली करें:
```python title="server.py" hl_lines="9 16-19"
--8<-- "docs_src/uri_templates/tutorial003.py"
```
* decorator पर `security=ResourceSecurity(exempt_params={"source"})`
उस एक resource के उस एक parameter के लिए जाँचें छोड़ देता है। बाकी server default policy रखता है।
* `MCPServer` constructor पर `resource_security=` हर resource के लिए default तय करता है। यहाँ `relaxed` `..` की जाँच पूरी तरह बंद कर देता है।
configure की जा सकने वाली जाँचें:
| Setting | Default | यह क्या करता है |
|-------------------------|---------|-------------------------------------|
| `reject_path_traversal` | `True` | शुरुआती directory से बाहर निकलने वाले `..` sequences reject करता है |
| `reject_absolute_paths` | `True` | `/foo`, `C:\foo`, UNC paths, और drive-relative `C:foo` reject करता है (`x:y` भी पकड़ता है) |
| `reject_null_bytes` | `True` | `\x00` वाली values reject करता है |
| `exempt_params` | खाली | वे parameter नाम जिनके लिए जाँचें छोड़नी हैं |
ये जाँचें एक heuristic pre-filter हैं; filesystem access के लिए,
`safe_join` ही containment boundary बना रहता है।
!!! tip
अगर आपका handler request पूरी नहीं कर सकता (file मौजूद नहीं है, id अनजान है), तो exception raise करें। SDK उसे error response में बदल देता है। protocol error और tool error के बीच के फ़र्क़ के लिए **[errors संभालना](handling-errors.md)** देखें।
## Low-level Server पर resources {#resources-on-the-low-level-server}
अगर आप low-level `Server` पर बना रहे हैं (देखें **[Low-level
Server](../advanced/low-level-server.md)**), तो आप `resources/list` और `resources/read` protocol methods के लिए handlers सीधे register करते हैं। कोई decorator नहीं है; protocol types आप खुद लौटाते हैं।
### Static resources {#static-resources}
तय URIs के लिए, एक registry रखें और exact match पर dispatch करें:
```python title="server.py" hl_lines="17 21 27"
--8<-- "docs_src/uri_templates/tutorial004.py"
```
list handler clients को बताता है कि क्या उपलब्ध है; read handler content serve करता है। पहले अपनी registry जाँचें, अगर आपके पास templates (नीचे) हैं तो उन पर जाएँ, फिर बाकी सब के लिए raise करें।
### Templates {#templates}
`MCPServer` जो template engine इस्तेमाल करता है वह `mcp.shared.uri_template` में रहता है और अपने आप में काम करता है। आपको वही parsing और matching मिलती है; routing और security policy आप खुद जोड़ते हैं।
```python title="server.py" hl_lines="13-16 22-25 29 33 45"
--8<-- "docs_src/uri_templates/tutorial005.py"
```
highlighted lines में तीन चीज़ें हो रही हैं:
* **एक बार parse करें, हर request पर match करें।** `UriTemplate.parse()` template बनाता है; `template.match(uri)` निकाले गए variables को `dict` के रूप में लौटाता है, या URI फ़िट न हो तो `None`। URL decoding `match()` के अंदर होती है; decoded values बिना path-safety validation के जस की तस लौटाई जाती हैं। values strings के रूप में निकलती हैं: उन्हें खुद convert करें (`int(matched["id"])`, `Path(matched["path"])`)।
* **safety जाँचें खुद लागू करें।** `..` और absolute-path की जो जाँचें `MCPServer` default रूप से चलाता है वे `mcp.shared.path_security` में रहती हैं।
`read_manual_safely` `MANUALS` को छूने से पहले उन्हें call करता है। अगर कोई parameter filesystem path नहीं है (ISBN, search query), तो उस value के लिए जाँचें छोड़ दें: policy आप config object के ज़रिए नहीं बल्कि हर handler के स्तर पर नियंत्रित करते हैं।
* **templates को उसी source से list करें।** clients
`resources/templates/list` के ज़रिए templates खोजते हैं। `str(template)` मूल template string वापस देता है, इसलिए listing और matcher का source of truth एक ही रहता है।
## सारांश {#recap}
* `{name}` एक segment match करता है; `{+name}` slashes रखता है; `{?a,b}`
query string से लेता है; `{/name*}` segments को list में बाँटता है।
* दो variables जिनके बीच कुछ न हो, या दूसरा multi-segment variable, parse के समय reject होते हैं। आखिरी `{?...}`/`{&...}` query variable से बँधे parameter को Python default declare करना ज़रूरी है।
* parameter को annotate करें (`order_id: int`) और SDK convert कर देता है।
* default security policy आपका handler चलने से पहले `..`, absolute paths, और null bytes reject करती है; हर resource के लिए `security=ResourceSecurity(...)` से या पूरे server के लिए `resource_security=` से override करें।
* filesystem access के लिए, `safe_join` ही containment boundary है।
* low-level `Server` पर, `UriTemplate.parse()` से parse करें, `.match()` से match करें, और `mcp.shared.path_security` खुद लागू करें।