docs: refresh translations, and translate pages in parallel (#3458)
This commit is contained in:
parent
08a3bc8eaf
commit
fd66270926
23 changed files with 4821 additions and 2277 deletions
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [0355618e5f4d5fe4, 1821eaf50f2d0b64, 82e0b28ebd3abf5a, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
|
||||
sections: [0355618e5f4d5fe4, 0fefa31fb7c7b585, 5c53e7487c9c70cc, 8ac39614c094f2d0, dab6ff945501ab2a, bd5565c3b2d4f959, 96819ce3d63a0487]
|
||||
tool: 1
|
||||
---
|
||||
# MCP Apps {#mcp-apps}
|
||||
|
|
@ -18,7 +18,7 @@ SDK इसे built-in `Apps` extension (`io.modelcontextprotocol/ui`) के
|
|||
|
||||
## चेहरे वाली घड़ी {#a-clock-with-a-face}
|
||||
|
||||
```python title="server.py" hl_lines="19 22 30 32"
|
||||
```python title="server.py" hl_lines="17 20 28 30"
|
||||
--8<-- "docs_src/apps/tutorial001.py"
|
||||
```
|
||||
|
||||
|
|
@ -49,15 +49,37 @@ model `content` पढ़ता है; iframe इंसानों के ल
|
|||
model को देता है, और text-only client को **सिर्फ़** वही मिलता है। इसलिए मानक
|
||||
pattern है: एक tool, दो जवाब। `get_time` को फिर से देखें:
|
||||
|
||||
```python title="server.py" hl_lines="23-27"
|
||||
```python title="server.py" hl_lines="21-25"
|
||||
--8<-- "docs_src/apps/tutorial001.py"
|
||||
```
|
||||
|
||||
`client_supports_apps(ctx)` तभी `True` होता है जब client ने
|
||||
`io.modelcontextprotocol/ui` extension declare किया हो **और** अपनी `mimeTypes`
|
||||
settings में `text/html;profile=mcp-app` सूचीबद्ध किया हो। यह field ज़रूरी है,
|
||||
इसलिए जो client इसे छोड़ देता है वह गिना नहीं जाता। इसी file में `main()` ठीक यही
|
||||
declare करता है: negotiation का client वाला आधा हिस्सा, और rich जवाब वापस आता है।
|
||||
इसलिए जो client इसे छोड़ देता है वह गिना नहीं जाता। negotiation का client वाला
|
||||
आधा हिस्सा यह रहा:
|
||||
|
||||
```python title="client.py" hl_lines="8 12"
|
||||
--8<-- "docs_src/apps/tutorial001_client.py"
|
||||
```
|
||||
|
||||
`server.py` को HTTP पर serve करें, फिर दूसरे terminal से client चलाएँ:
|
||||
|
||||
```console
|
||||
uv run mcp run server.py --transport streamable-http
|
||||
```
|
||||
|
||||
```console
|
||||
python client.py
|
||||
```
|
||||
|
||||
```text
|
||||
2026-06-26T12:00:00Z
|
||||
```
|
||||
|
||||
rich जवाब वापस आया। `Client` call से `extensions=[APPS_SUPPORT]` हटा दें, तो यही
|
||||
program इसकी जगह `The time is 2026-06-26T12:00:00Z.` print करता है, और text-only
|
||||
client को हमेशा बस इतना ही दिखता है।
|
||||
|
||||
!!! warning
|
||||
कभी भी `"[Rendered UI]"` जैसा placeholder अकेले content के रूप में न लौटाएँ।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [05891e7cc1938a13, b3c01a6af28c51ee, 7ffc91f5e38bdfe0, 717d3f235a8333a7, f471a13b2fe5d737, ed6af2df4b656dff]
|
||||
sections: [05891e7cc1938a13, b3c01a6af28c51ee, 6eba6ce094f4417e, 125f28588c85dd7b, ddd8969b698ca11c, ed6af2df4b656dff]
|
||||
tool: 1
|
||||
---
|
||||
# Extensions {#extensions}
|
||||
|
|
@ -49,7 +49,7 @@ prefix के रूप में ऐसा domain इस्तेमाल क
|
|||
|
||||
सबसे छोटा काम का extension एक tool और एक settings map है:
|
||||
|
||||
```python title="server.py" hl_lines="17 19-20 22-23 26"
|
||||
```python title="server.py" hl_lines="16 18-19 21-22 25"
|
||||
--8<-- "docs_src/extensions/tutorial003.py"
|
||||
```
|
||||
|
||||
|
|
@ -57,17 +57,23 @@ prefix के रूप में ऐसा domain इस्तेमाल क
|
|||
* `settings()` वह value है जो `capabilities.extensions["com.example/stamps"]` पर advertise होती है। बिना settings के extension advertise करने के लिए `{}` (default) लौटाएँ।
|
||||
* extension को server कभी नहीं मिलता। यह अपने योगदान data के रूप में declare करता है; `MCPServer` उन्हें consume करता है। mutate करने के लिए कोई `self.server` नहीं है।
|
||||
|
||||
और `main()` इसका सबूत है, सीधे `mcp` से जुड़ा एक in-memory client:
|
||||
इसे HTTP पर serve करें, और एक client इसका सबूत है:
|
||||
|
||||
```python title="server.py" hl_lines="29-34"
|
||||
--8<-- "docs_src/extensions/tutorial003.py"
|
||||
```console
|
||||
uv run mcp run server.py --transport streamable-http
|
||||
```
|
||||
|
||||
```python title="client.py" hl_lines="7-11"
|
||||
--8<-- "docs_src/extensions/tutorial003_client.py"
|
||||
```
|
||||
|
||||
इस page की हर `server.py` इसी command से serve होती है, और हर `client.py` उसके साथ-साथ दूसरे terminal से `python client.py` से चलती है।
|
||||
|
||||
### अपने methods serve करना {#serving-your-own-methods}
|
||||
|
||||
extension **नए request methods** register कर सकता है: उसके अपने verbs, जो spec के verbs के साथ-साथ serve होते हैं:
|
||||
|
||||
```python title="server.py" hl_lines="16-22 31 40-48"
|
||||
```python title="server.py" hl_lines="14-20 24 33-41"
|
||||
--8<-- "docs_src/extensions/tutorial004.py"
|
||||
```
|
||||
|
||||
|
|
@ -83,14 +89,15 @@ methods **सख़्ती से additive** हैं। SDK इसे constr
|
|||
|
||||
### Client side {#the-client-side}
|
||||
|
||||
उसी file का `main()` ही client की पूरी कहानी है, उसके दोनों हिस्से:
|
||||
client अपना अलग program है, और client की कहानी के दोनों हिस्से इसी में हैं:
|
||||
|
||||
```python title="server.py" hl_lines="54-58"
|
||||
--8<-- "docs_src/extensions/tutorial004.py"
|
||||
```python title="client.py" hl_lines="21-23 27-30"
|
||||
--8<-- "docs_src/extensions/tutorial004_client.py"
|
||||
```
|
||||
|
||||
* `Client(..., extensions=[advertise(EXTENSION_ID)])` extension declare करता है। ये declarations `ClientCapabilities.extensions` बन जाती हैं: 2026-07-28 connection पर यह map हर request के `_meta` envelope में जाता है, इसलिए server इसे **हर** request पर देखता है; legacy connection पर यह `initialize` handshake के साथ जाता है। server code को फ़र्क नहीं पड़ता कि कौन सा: `require_client_extension(ctx, ...)` और `ctx.session.check_client_capability(...)` दोनों रास्तों पर सही स्रोत पढ़ते हैं।
|
||||
* vendor methods एक परत नीचे `client.session.send_request(...)` पर उतरते हैं; `Client` सिर्फ़ spec verbs के लिए first-class methods जोड़ता है। `send_request` कोई भी `Request` subclass स्वीकार करता है, इसलिए vendor request जैसी है वैसी ही चली जाती है।
|
||||
* `SearchRequest` और उसके साथ जाने वाले दो models extension का wire contract हैं, इसलिए client उन्हें अपने लिए खुद declare करता है। published extension इन्हें ऐसे package में देगा जिसे दोनों पक्ष import करें।
|
||||
|
||||
### `tools/call` को intercept करना {#intercepting-toolscall}
|
||||
|
||||
|
|
@ -109,12 +116,18 @@ hook `tools/call` को wrap करता है, और कुछ नहीं
|
|||
|
||||
## client extension इस्तेमाल करना {#using-a-client-extension}
|
||||
|
||||
**client extension** वही contract है, इस्तेमाल करने वाले पक्ष से: एक identifier के पीछे client-side behaviour का bundle। instances को `Client(extensions=[...])` में पास करें और tools सामान्य तरीके से call करें:
|
||||
**client extension** वही contract है, इस्तेमाल करने वाले पक्ष से: एक identifier के पीछे client-side behaviour का bundle। यहाँ server `buy` का जवाब सामान के बजाय redeem करने लायक receipt से देता है, और सिर्फ़ उसी client को जिसने extension declare किया हो:
|
||||
|
||||
```python title="client.py" hl_lines="66-68"
|
||||
```python title="server.py" hl_lines="22-25"
|
||||
--8<-- "docs_src/extensions/tutorial006.py"
|
||||
```
|
||||
|
||||
client पर, instances को `Client(extensions=[...])` में पास करें और tools सामान्य तरीके से call करें:
|
||||
|
||||
```python title="client.py" hl_lines="33-35"
|
||||
--8<-- "docs_src/extensions/tutorial006_client.py"
|
||||
```
|
||||
|
||||
`call_tool("buy", ...)` हर दूसरे call की तरह सादा `CallToolResult` लौटाता है। extension ने जो बदला: server अब `buy` का जवाब final result के बजाय `receipt` **result shape** से दे सकता है, और `call_tool` के लौटने से पहले `Receipts` उसे पूरा कर देता है (यहाँ follow-up call से receipt redeem करके)। call site में कुछ नहीं हिलता।
|
||||
|
||||
extension हटा दें तो इनमें से कुछ भी मौजूद नहीं: server का gate उस client को मना कर देता है जिसने इसे declare नहीं किया (error -32021), और gate छोड़ने वाले server से आया claimed shape validation में fail होता है, ठीक वैसे जैसे spec अनजान `resultType` के लिए माँगता है। default रूप से बंद, wire के दोनों सिरों पर।
|
||||
|
|
@ -124,15 +137,15 @@ extension हटा दें तो इनमें से कुछ भी म
|
|||
```python
|
||||
from mcp.client import advertise
|
||||
|
||||
client = Client(mcp, extensions=[advertise("com.example/search")])
|
||||
client = Client("http://localhost:8000/mcp", extensions=[advertise("com.example/search")])
|
||||
```
|
||||
|
||||
## client extension लिखना {#writing-a-client-extension}
|
||||
|
||||
`ClientExtension` को subclass करें और सिर्फ़ वही override करें जिसकी ज़रूरत हो। योगदान के तीन प्रकार, हर एक का default: `settings()`, `claims()` और `notifications()`।
|
||||
|
||||
```python title="client.py" hl_lines="17-18 43-44 46-47"
|
||||
--8<-- "docs_src/extensions/tutorial006.py"
|
||||
```python title="client.py" hl_lines="16-17 25-26 28-29"
|
||||
--8<-- "docs_src/extensions/tutorial006_client.py"
|
||||
```
|
||||
|
||||
* identifier वही grammar मानता है जो server का, और class define होते ही validate होता है।
|
||||
|
|
@ -153,12 +166,18 @@ handler को validated params एक-एक करके, dispatch के क
|
|||
|
||||
### Extension verbs {#extension-verbs}
|
||||
|
||||
extension के अपने request methods को client-side registration की ज़रूरत नहीं। vendor request type `mcp.types.Request` को subclass करता है और `client.session.send_request` से जाता है, जैसा [अपने methods serve करना](#serving-your-own-methods) में है। एक बात और: जब किसी params key का `Mcp-Name` header में जाना ज़रूरी हो (tasks जैसे extension specs अपने verbs के लिए यह माँगते हैं), तो request type `name_param` declare करता है:
|
||||
extension के अपने request methods को client-side registration की ज़रूरत नहीं। vendor request type `mcp.types.Request` को subclass करता है और `client.session.send_request` से जाता है, जैसा [अपने methods serve करना](#serving-your-own-methods) में है। ऐसा server लें जिसका extension एक named job के बारे में एक verb serve करता है:
|
||||
|
||||
```python title="client.py" hl_lines="22-25 46-47"
|
||||
```python title="server.py" hl_lines="12-13 30"
|
||||
--8<-- "docs_src/extensions/tutorial007.py"
|
||||
```
|
||||
|
||||
client पर एक बात और जुड़ती है: जब किसी params key का `Mcp-Name` header में जाना ज़रूरी हो (tasks जैसे extension specs अपने verbs के लिए यह माँगते हैं), तो request type `name_param` declare करता है:
|
||||
|
||||
```python title="client.py" hl_lines="20-23 28-29"
|
||||
--8<-- "docs_src/extensions/tutorial007_client.py"
|
||||
```
|
||||
|
||||
session हर send path पर `params["jobId"]` को `Mcp-Name` में mirror करता है, और value न होने पर ज़रूरी header चुपचाप छोड़ने के बजाय साफ़ तौर पर fail होता है।
|
||||
|
||||
## extension क्या नहीं कर सकता {#what-an-extension-cannot-do}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [2c79b6338e09b7ac, 7edc43b3fae11314, 1086e77ce561cd7f, a3f71823df5efc31, 9fc7109f72201cae, d50fe7faead8cf68, 7bf25983df655b66, 6330e1f4c6029683, 2f1749c8c133fa1c, 8db7116fc8ddd0ee, ebc33704fbd74262, cd0e9c933350390e]
|
||||
sections: [2c79b6338e09b7ac, 9d5d10a5f0405d0a, 1086e77ce561cd7f, a3f71823df5efc31, 9fc7109f72201cae, d50fe7faead8cf68, 7bf25983df655b66, 6330e1f4c6029683, 2f1749c8c133fa1c, 8db7116fc8ddd0ee, ebc33704fbd74262, 0fde3bcea081ba3a]
|
||||
tool: 1
|
||||
---
|
||||
# Low-level Server {#the-low-level-server}
|
||||
|
|
@ -25,9 +25,9 @@ translation:
|
|||
|
||||
तीन चीज़ें बदलीं, और पूरा low-level API बस यही है:
|
||||
|
||||
* **Handlers constructor parameters हैं।** `on_list_tools=` और `on_call_tool=` `Server(...)` में जाते हैं। यहाँ नीचे कोई decorator नहीं है, और हर handler का आकार एक ही है: `async (ctx, params) -> result`।
|
||||
* **Input schema आप लिखते हैं।** `Tool.input_schema` एक सादा JSON Schema `dict` है। कोई इसे type hints से नहीं निकालता, क्योंकि निकालने के लिए type hints हैं ही नहीं।
|
||||
* **Result आप बनाते हैं।** `CallToolResult(content=[TextContent(...)])`, हाथ से। न कुछ wrap होता है, न convert, न return annotation से अनुमान लगाया जाता है।
|
||||
* **handlers constructor parameters हैं।** `on_list_tools=` और `on_call_tool=` `Server(...)` में जाते हैं। यहाँ नीचे कोई decorator नहीं है, और हर handler का आकार एक ही है: `async (ctx, params) -> result`।
|
||||
* **input schema आप लिखते हैं।** `Tool.input_schema` एक सादा JSON Schema `dict` है। कोई इसे type hints से नहीं निकालता, क्योंकि निकालने के लिए type hints हैं ही नहीं।
|
||||
* **result आप बनाते हैं।** `CallToolResult(content=[TextContent(...)])`, हाथ से। न कुछ wrap होता है, न convert, न return annotation से अनुमान लगाया जाता है।
|
||||
|
||||
`params` parse की हुई request है: `CallToolRequestParams` आपको `.name` और `.arguments` देता है। `ctx` एक `ServerRequestContext` है: client से वापस बात करने के लिए `ctx.session`, `ctx.lifespan_context`, `ctx.request_id`, और `ctx.meta`, यानी request का आने वाला `_meta`।
|
||||
|
||||
|
|
@ -36,18 +36,22 @@ translation:
|
|||
|
||||
### इसे आज़माएँ {#try-it}
|
||||
|
||||
इसके लिए कोई Inspector नहीं है: `mcp dev` और `mcp run` सिर्फ़ `MCPServer` स्वीकार करते हैं। In-memory `Client` को कोई फ़र्क नहीं पड़ता; वह low-level `Server` को ठीक वैसे ही लेता है जैसे `MCPServer` को:
|
||||
`mcp dev` और `mcp run` सिर्फ़ `MCPServer` स्वीकार करते हैं, इसलिए इसे serve आप खुद करते हैं। `server.py` की आख़िरी line इससे एक साधारण ASGI app बनाती है, और uvicorn उसे चलाता है:
|
||||
|
||||
```python title="main.py"
|
||||
```console
|
||||
uvicorn server:app --port 8000
|
||||
```
|
||||
|
||||
Inspector को, या किसी भी client को, `http://localhost:8000/mcp` पर point करें:
|
||||
|
||||
```python title="client.py"
|
||||
import asyncio
|
||||
|
||||
from mcp import Client
|
||||
|
||||
from server import server
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async with Client(server) as client:
|
||||
async with Client("http://localhost:8000/mcp") as client:
|
||||
result = await client.call_tool("search_books", {"query": "dune", "limit": 5})
|
||||
print(result.content)
|
||||
|
||||
|
|
@ -61,8 +65,10 @@ asyncio.run(main())
|
|||
|
||||
वही text जो `@mcp.tool()` वाले version ने दिया था। दो असली अंतर:
|
||||
|
||||
* `result.structured_content` `None` है। High-level server आपके लिए `-> str` को `{"result": ...}` में wrap कर देता है; यहाँ जो आपने नहीं बनाया, उसे कोई नहीं बनाता।
|
||||
* `list_tools` वही schema लौटाता है जो **आपने** type किया, अक्षर-दर-अक्षर। High-level version में हर property पर `"title": "Query"` था और root पर `"title": "search_booksArguments"`: Pydantic की देन। यहाँ नीचे, अगर कुछ wire पर है, तो उसे वहाँ आपने रखा है।
|
||||
* `result.structured_content` `None` है। high-level server आपके लिए `-> str` को `{"result": ...}` में wrap कर देता है; यहाँ जो आपने नहीं बनाया, उसे कोई नहीं बनाता।
|
||||
* `list_tools` वही schema लौटाता है जो **आपने** type किया, अक्षर-दर-अक्षर। high-level version में हर property पर `"title": "Query"` था और root पर `"title": "search_booksArguments"`: Pydantic की देन। यहाँ नीचे, अगर कुछ wire पर है, तो उसे वहाँ आपने रखा है।
|
||||
|
||||
test में आप uvicorn और port दोनों छोड़ देते हैं: `Client(server)` low-level `Server` को in-process ठीक वैसे ही लेता है जैसे `MCPServer` को, और **[Testing](../get-started/testing.md)** यही pattern है।
|
||||
|
||||
## आपके लिए कुछ जाँचा नहीं जाता {#nothing-is-checked-for-you}
|
||||
|
||||
|
|
@ -207,12 +213,12 @@ Handshake runner का है। `server/discover`, `ping`, और बाकी
|
|||
|
||||
## सारांश {#recap}
|
||||
|
||||
* Low-level `Server` अपने handlers `on_*` **constructor parameters** के रूप में लेता है; हर handler `async (ctx, params) -> result` है।
|
||||
* low-level `Server` अपने handlers `on_*` **constructor parameters** के रूप में लेता है; हर handler `async (ctx, params) -> result` है।
|
||||
* `input_schema` dict आप लिखते हैं और `CallToolResult` आप बनाते हैं। आपके लिए न कुछ derive होता है, न wrap, न validate।
|
||||
* Handler में exception `-32603` protocol error है। जिस tool error को model पढ़ सके, वह `is_error=True` वाला `CallToolResult` है जिसे **आप** लौटाते हैं।
|
||||
* Result पर `_meta` client application के नाम है, model के नहीं।
|
||||
* handler में exception `-32603` protocol error है। जिस tool error को model पढ़ सके, वह `is_error=True` वाला `CallToolResult` है जिसे **आप** लौटाते हैं।
|
||||
* result पर `_meta` client application के नाम है, model के नहीं।
|
||||
* `Server[T]` उस चीज़ में generic है जो उसका lifespan yield करता है; `ctx.lifespan_context` एक typed `T` है।
|
||||
* `add_request_handler(method, params_type, handler)` कोई भी method serve करता है। `initialize` reserved है।
|
||||
* `Server` जो capabilities advertise करता है, वे इससे निकलती हैं कि आपने कौन से handlers register किए।
|
||||
|
||||
`Client(server)` ने दोनों servers के साथ एक जैसा बर्ताव किया क्योंकि वे एक ही protocol **हैं**, और यही असली बात है। इससे नीचे की अगली layer कोई class है ही नहीं: वह **[Middleware](middleware.md)** है।
|
||||
client ने दोनों servers के साथ एक जैसा बर्ताव किया क्योंकि वे एक ही protocol **हैं**, और यही असली बात है। इससे नीचे की अगली layer कोई class है ही नहीं: वह **[Middleware](middleware.md)** है।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [a9aba7a026c7bd85, ed32bda7ba9ae33a, 7e64cc5646abb91f, 22a0129ee78b3c63, d875373c06d8d2f9]
|
||||
sections: [a9aba7a026c7bd85, 83e2a08b9d46a398, 9fd8a0aa384b3257, 22a0129ee78b3c63, d875373c06d8d2f9]
|
||||
tool: 1
|
||||
---
|
||||
# Pagination {#pagination}
|
||||
|
|
@ -31,9 +31,13 @@ Pagination उस server के लिए है जिसकी resource list
|
|||
|
||||
### इसे आज़माएँ {#try-it}
|
||||
|
||||
`Client(server)` memory में low-level `Server` से ठीक वैसे ही जुड़ता है जैसे `MCPServer` से।
|
||||
`mcp run` सिर्फ़ `MCPServer` स्वीकार करता है, इसलिए इसे आप ख़ुद serve करते हैं। `server.py` की आख़िरी line `Server` से एक साधारण ASGI app बनाती है, और uvicorn उसी को चलाता है:
|
||||
|
||||
बिना arguments के `list_resources()` call करें। आपको दस resources मिलते हैं, `book-1` से `book-10` तक, और `next_cursor` string `"10"` है।
|
||||
```console
|
||||
uvicorn server:app --port 8000
|
||||
```
|
||||
|
||||
किसी भी client (**[The Client](../client/index.md)**, या Inspector) को `http://localhost:8000/mcp` की ओर point करें और बिना arguments के `list_resources()` call करें। आपको दस resources मिलते हैं, `book-1` से `book-10` तक, और `next_cursor` string `"10"` है।
|
||||
|
||||
इसे `list_resources(cursor="10")` से वापस दें, तो पहला resource `book-11` है और नया `next_cursor` `"20"` है।
|
||||
|
||||
|
|
@ -43,7 +47,7 @@ Pagination उस server के लिए है जिसकी resource list
|
|||
|
||||
`Client` का हर `list_*` method (`list_tools`, `list_resources`, `list_resource_templates`, `list_prompts`) `cursor=` keyword लेता है। Paged list को पूरा खींचना एक `while True` है:
|
||||
|
||||
```python title="client.py" hl_lines="26-32"
|
||||
```python title="client.py" hl_lines="9-15"
|
||||
--8<-- "docs_src/pagination/tutorial002.py"
|
||||
```
|
||||
|
||||
|
|
@ -51,7 +55,7 @@ Pagination उस server के लिए है जिसकी resource list
|
|||
* `next_cursor` देखने से **पहले** extend करें: आख़िरी page में भी resources होते हैं।
|
||||
* `next_cursor is None` ही बाहर निकलने का रास्ता है। बाकी कुछ भी सीधे `cursor=` में वापस जाता है, बिना छेड़े।
|
||||
|
||||
इसका `main()` चलाएँ और यह `100 resources` print करता है: दस-दस के दस pages, एक ऐसे loop से जुड़े हुए जिसे कभी पता ही नहीं था कि दस pages थे।
|
||||
uvicorn अब भी `server.py` serve कर रहा हो, तब दूसरे terminal में `python client.py` चलाएँ। यह `100 resources` print करता है: दस-दस के दस pages, एक ऐसे loop से जुड़े हुए जिसे कभी पता ही नहीं था कि दस pages थे।
|
||||
|
||||
यह वही loop है जो **[The Client](../client/index.md)** हर `list_*` verb के लिए दिखाता है, और paging न करने वाले server पर इसकी कोई क़ीमत नहीं: पहले ही response में `next_cursor` `None` होता है और loop एक बार चलता है।
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [9e7b9a1710e5aeba, b74ca4c1d2ddddee, fa8714e61bf90c5a, 04db67a886b7271c, 857690fb8f876800]
|
||||
sections: [9e7b9a1710e5aeba, 66a2e9acc9101d54, d3d25aa802f5145a, 04db67a886b7271c, 857690fb8f876800]
|
||||
tool: 1
|
||||
---
|
||||
# Caching hints {#caching-hints}
|
||||
|
|
@ -30,7 +30,7 @@ server खुद कुछ भी cache नहीं करता। ये fiel
|
|||
|
||||
low-level `Server` पर handlers अपने results खुद बनाते हैं, और `ttl_ms` / `cache_scope` result models पर बस fields हैं। जो handler इन्हें explicitly set करता है, वह constructor map पर हमेशा भारी पड़ता है, field दर field:
|
||||
|
||||
```python title="server.py" hl_lines="10 16"
|
||||
```python title="server.py" hl_lines="11 17"
|
||||
--8<-- "docs_src/caching/tutorial002.py"
|
||||
```
|
||||
|
||||
|
|
@ -44,10 +44,24 @@ paginated lists पर एक सावधानी: protocol की माँ
|
|||
|
||||
2026-07-28 session पर `Client` आपके लिए hints का पालन करता है: इसमें built-in response cache है, जो default रूप से चालू रहता है। जो result `ttlMs` के साथ आता है, वह store हो जाता है, और उस TTL के भीतर वैसा ही call cache से serve होता है, बिना round trip के। जिस result में **कोई** hint नहीं होता, वह cache नहीं होता: बिना hint वाले results को `CacheConfig.default_ttl_ms` मिलता है, जिसका default `0` है (तुरंत stale), इसलिए जो server कुछ भी declare नहीं करता, उसे ठीक वैसा ही call-दर-call traffic दिखता है जैसा हमेशा दिखता था।
|
||||
|
||||
```python title="client.py" hl_lines="33 35 38"
|
||||
इसे होते हुए देखने के लिए, पिछले section की `server.py` को uvicorn से serve करें (उसकी आखिरी line ASGI app बनाती है)। handler जब भी सच में चलता है, एक line print करता है:
|
||||
|
||||
```console
|
||||
uvicorn server:app --port 8000
|
||||
```
|
||||
|
||||
```python title="client.py" hl_lines="20 23 28"
|
||||
--8<-- "docs_src/caching/tutorial003.py"
|
||||
```
|
||||
|
||||
दूसरे terminal से `python client.py` चलाएँ। यह वे hints print करता है जो पहले result में आए थे, handler का `ttlMs` और उसके बगल में map का `cacheScope`:
|
||||
|
||||
```text
|
||||
1000 public
|
||||
```
|
||||
|
||||
बाकी कहानी server का terminal बताता है: uvicorn के request logs के बीच `tools/list served` तीन बार दिखता है।
|
||||
|
||||
चार calls, तीन fetches। दूसरे call को fresh entry मिली और वह server तक पहुँचा ही नहीं; (inject की गई) clock को TTL से आगे बढ़ाने पर तीसरे ने फिर से fetch किया; चौथे ने `cache_mode="refresh"` कहा। यह kwarg पाँचों caching verbs पर मौजूद है (`list_tools`, `list_prompts`, `list_resources`, `list_resource_templates`, `read_resource`):
|
||||
|
||||
* `"use"` (default) fresh entry हो तो उसे serve करता है, और न हो तो fetch करके store करता है।
|
||||
|
|
@ -56,7 +70,7 @@ paginated lists पर एक सावधानी: protocol की माँ
|
|||
|
||||
एक नियम `"use"` से ऊपर है: **`meta` वाले calls हमेशा server तक पहुँचते हैं।** जिस request में `meta` set हो (progress token, tracing fields), उसे wire request की उम्मीद होती है, इसलिए `cache_mode="use"` में उसे `"refresh"` माना जाता है: cache read छोड़ दिया जाता है, और fetch किया गया result फिर भी cache की entry की जगह ले लेता है। `"bypass"` और explicit `"refresh"` हमेशा की तरह ही बर्ताव करते हैं।
|
||||
|
||||
caching पूरी तरह बंद करने के लिए `Client(server, cache=None)` से construct करें: हर call फिर से round trip है, और `cache_mode`, भले ही अब भी स्वीकार होता है, कुछ नहीं करता।
|
||||
caching पूरी तरह बंद करने के लिए `Client` construct करते समय `cache=None` pass करें: हर call फिर से round trip है, और `cache_mode`, भले ही अब भी स्वीकार होता है, कुछ नहीं करता।
|
||||
|
||||
scope का पालन भी अपने आप होता है: `"private"` entries cache के *partition* (नीचे देखें) से बँधी होती हैं, जबकि `"public"` वाली चाहें तो ज़्यादा व्यापक sharing चुन सकती हैं। और जिन entries का नाम notifications लेते हैं, ठीक उनके लिए **notifications TTL पर भारी पड़ते हैं**: `list_changed` notification मेल खाती cached listing को evict कर देता है, और `resources/updated` ठीक उसी URI के तहत store किए गए cached read को evict करता है, चाहे वे कितने भी fresh रहे हों। 2026-07-28 connection पर ये notifications `subscriptions/listen` stream पर आते हैं जिसे आप `client.listen(...)` से खोलते हैं, और eviction आपके watcher को event दिखने से पहले पूरा हो जाता है; **[Subscriptions](subscriptions.md)** वही page है।
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [adf3c545b5be46b6, 916cd3ab1c03f461, e9be7a8d0eb0a456, 565890a636288ecf, 6af7e49db9129ec3, 06b0238c174186af, 90c6043be435fcb0]
|
||||
sections: [adf3c545b5be46b6, 916cd3ab1c03f461, 32ef568335dd95a7, 565890a636288ecf, 6af7e49db9129ec3, 06b0238c174186af, 0abc5ea5cb7ff6b3]
|
||||
tool: 1
|
||||
---
|
||||
# Client callbacks {#client-callbacks}
|
||||
|
|
@ -63,7 +63,7 @@ result.content # [TextContent(type='text', text='Card issued to Ada Lovelace.')
|
|||
`Client(...)` call पर `mode="legacy"` असल में काम कर रहा है। default रूप से `Client(...)` modern
|
||||
protocol path negotiate करता है, और उस path में server-से-client requests के लिए कोई back-channel नहीं है:
|
||||
आपका callback चलने से पहले ही `ctx.elicit` fail हो जाता है। यह transport तय नहीं करता; negotiated
|
||||
protocol तय करता है, in-memory में भी और URL पर भी। जब भी आपके client को ऐसी किसी request का जवाब देना हो,
|
||||
protocol तय करता है। जब भी आपके client को ऐसी किसी request का जवाब देना हो,
|
||||
`mode="legacy"` तय करें; इस page के पीछे का हर test यही करता है। पूरी जानकारी **[Protocol versions](../protocol-versions.md)** में है।
|
||||
|
||||
2026-07-28 session पर callback बेकार नहीं होता, उसे input अलग तरीके से मिलता है: जब कोई tool
|
||||
|
|
@ -149,6 +149,6 @@ result.structured_content # {'result': ['elicitation']}
|
|||
* **callback register करना ही capability घोषित करना है।** इसके बिना SDK आपकी ओर से server की request ठुकरा देता है और पूरा call `MCPError` के साथ fail हो जाता है।
|
||||
* server पूछने से पहले `ctx.session.check_client_capability(...)` से पता कर लेता है।
|
||||
* `sampling_callback` और `list_roots_callback` इसी तरह काम करते हैं लेकिन deprecated features को serve करते हैं; modern servers इनकी जगह multi-round-trip requests इस्तेमाल करते हैं।
|
||||
* `logging_callback` और `message_handler` को notifications मिलती हैं। वे कुछ घोषित नहीं करते।
|
||||
* `logging_callback` और `message_handler` को notifications मिलते हैं। वे कुछ घोषित नहीं करते।
|
||||
|
||||
`Client(...)` का पहला argument transport object है। हर प्रकार की जानकारी **[Client transports](transports.md)** में है।
|
||||
`Client(...)` का पहला argument transport चुनता है। हर प्रकार की जानकारी **[Client transports](transports.md)** में है।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [ebef1e7a0df854f4, 8355cfaf1f76c9d5, 8e79141fc2985342, 46bdb07c7537e8a5, 80ce41579825a6fa, 5f0fa90494de8f65, 83d10514eaa62fa5, 9190555aa39a5d28, 84a4c9d8bf14dddb, 927d71cf40b58c30]
|
||||
sections: [ebef1e7a0df854f4, 7e16449f66e7dfd6, eeb0682f7d2a1079, 5713f0196a34e6e7, 0e844597859e4248, 3a97d9195ddcc92e, 1da08c483e59c141, 84702cc6e0a1fd42, 8dee7a31c86ffc5c, 83a5bce168ef23d7]
|
||||
tool: 1
|
||||
---
|
||||
# Client {#the-client}
|
||||
|
|
@ -11,13 +11,23 @@ translation:
|
|||
|
||||
## आपका पहला client {#your-first-client}
|
||||
|
||||
```python title="client.py" hl_lines="14-18"
|
||||
client को बात करने के लिए server चाहिए। इस page का हर उदाहरण इसी Bookshop से connect करता है। इसे `server.py` के नाम से save करें और HTTP पर चलता छोड़ दें:
|
||||
|
||||
```python title="server.py"
|
||||
--8<-- "docs_src/client/tutorial001.py"
|
||||
```
|
||||
|
||||
ऊपर वाला server सिर्फ़ इसलिए है ताकि connect करने के लिए कुछ हो। client वे पाँच highlighted lines हैं।
|
||||
```console
|
||||
uv run mcp run server.py --transport streamable-http
|
||||
```
|
||||
|
||||
* `Client(mcp)` को **server object ही** दिया गया है। यही in-memory transport है: न subprocess, न port, न HTTP। इस page का हर उदाहरण, और आपका लिखा हर test, इसी तरह connect करता है।
|
||||
इससे server `http://localhost:8000/mcp` पर serve होता है। client अपना अलग program है। इसे `client.py` के नाम से save करें और दूसरे terminal में `python client.py` चलाएँ:
|
||||
|
||||
```python title="client.py" hl_lines="7-11"
|
||||
--8<-- "docs_src/client/tutorial001_client.py"
|
||||
```
|
||||
|
||||
* `Client("http://localhost:8000/mcp")` को **URL** दिया गया है, इसलिए यह अभी शुरू किए गए server से Streamable HTTP पर connect करता है।
|
||||
* `async with` ही **lifecycle** है। इसमें enter करते ही connect और negotiate होता है; बाहर निकलते ही disconnect। कोई `connect()` / `close()` जोड़ी नहीं है, और block खत्म होने के बाद `Client` दोबारा इस्तेमाल नहीं हो सकता।
|
||||
* block के अंदर connection की जानकारी पहले से सादी properties के रूप में मौजूद है।
|
||||
|
||||
|
|
@ -25,10 +35,10 @@ translation:
|
|||
|
||||
`Client` एक positional argument लेता है और उसके type से transport तय करता है:
|
||||
|
||||
* `MCPServer` (या low-level `Server`) instance: **in-process** connect होता है।
|
||||
* URL string (`Client("http://localhost:8000/mcp")`): Streamable HTTP, production वाला रास्ता।
|
||||
* `StdioServerParameters`: वह command जो **subprocess** के रूप में launch होता है, और जिससे उसके stdin और stdout के ज़रिए बात होती है।
|
||||
* URL string (`Client("http://localhost:8000/mcp")`): Streamable HTTP, वह transport जिसके पीछे आप deploy करते हैं।
|
||||
* `StdioServerParameters`: वह command जो local **subprocess** के रूप में launch होता है, और जिससे उसके stdin और stdout के ज़रिए बात होती है।
|
||||
* **transport**: कोई भी चीज़ जिसे आप `async with ... as (read, write)` कर सकें, जैसे आपके अपने HTTP client के ऊपर `streamable_http_client(url, http_client=...)`।
|
||||
* `MCPServer` (या low-level `Server`) instance: **in-process** connect होता है, न subprocess, न port। यह tests के लिए है, और **[Testing](../get-started/testing.md)** इसी पर आगे बढ़ता है।
|
||||
|
||||
इस page की बाकी हर चीज़ चारों में एक जैसी है। Headers, subprocesses, timeouts और `Transport` protocol का अपना अलग page है: **[Client transports](transports.md)**।
|
||||
|
||||
|
|
@ -49,11 +59,11 @@ translation:
|
|||
|
||||
## tools की सूची लेना {#listing-tools}
|
||||
|
||||
```python title="client.py" hl_lines="15-20"
|
||||
```python title="client.py" hl_lines="8-13"
|
||||
--8<-- "docs_src/client/tutorial002.py"
|
||||
```
|
||||
|
||||
`list_tools()` एक `ListToolsResult` लौटाता है; tools `.tools` में हैं। हर एक वह पूरी definition है जो host किसी model को देगा:
|
||||
`list_tools()` एक `ListToolsResult` लौटाता है; tools `.tools` में हैं। हर एक वह पूरी definition है जो host किसी model को देगा। यह पहला है:
|
||||
|
||||
```python
|
||||
tool.name # 'search_books'
|
||||
|
|
@ -77,6 +87,8 @@ tool.description # 'Search the catalog by title or author.'
|
|||
|
||||
UI को argument form दिखाने के लिए, और model को valid arguments बनाने के लिए, जो कुछ चाहिए वह सब इसी schema में है।
|
||||
|
||||
दूसरा tool, `lookup_book`, बिना `title=` के register हुआ था, इसलिए उसका `tool.title` `None` है।
|
||||
|
||||
!!! tip
|
||||
`title` optional है, इसलिए किसी इंसान को tools दिखाने वाले UI को चुनना पड़ता है: `title` हो तो वही,
|
||||
नहीं तो `name`। `from mcp.shared.metadata_utils import get_display_name` ठीक यही करता है,
|
||||
|
|
@ -86,7 +98,7 @@ UI को argument form दिखाने के लिए, और model को
|
|||
|
||||
`call_tool(name, arguments)` tool चलाता है और आपको `CallToolResult` वापस देता है।
|
||||
|
||||
```python title="client.py" hl_lines="27-34"
|
||||
```python title="client.py" hl_lines="9-16"
|
||||
--8<-- "docs_src/client/tutorial003.py"
|
||||
```
|
||||
|
||||
|
|
@ -142,7 +154,7 @@ result.is_error # False
|
|||
|
||||
resource verbs जोड़ियों में आते हैं: सूची लेने के दो तरीके, पढ़ने का एक।
|
||||
|
||||
```python title="client.py" hl_lines="22-31"
|
||||
```python title="client.py" hl_lines="9-18"
|
||||
--8<-- "docs_src/client/tutorial004.py"
|
||||
```
|
||||
|
||||
|
|
@ -156,7 +168,7 @@ client को यह भी बताया जा सकता है कि
|
|||
|
||||
## Prompts {#prompts}
|
||||
|
||||
```python title="client.py" hl_lines="15-20"
|
||||
```python title="client.py" hl_lines="8-13"
|
||||
--8<-- "docs_src/client/tutorial005.py"
|
||||
```
|
||||
|
||||
|
|
@ -181,7 +193,7 @@ host ये messages सीधे model को दे देता है। प
|
|||
|
||||
जिस server में completion handler हो वह user के type करते-करते prompt और resource-template arguments autocomplete कर सकता है।
|
||||
|
||||
```python title="client.py" hl_lines="27-31"
|
||||
```python title="client.py" hl_lines="9-13"
|
||||
--8<-- "docs_src/client/tutorial006.py"
|
||||
```
|
||||
|
||||
|
|
@ -194,21 +206,21 @@ host ये messages सीधे model को दे देता है। प
|
|||
|
||||
हर `list_*` method एक `cursor=` keyword लेता है और हर result में `next_cursor` होता है। जब `next_cursor` `None` हो, आपके पास सब कुछ है।
|
||||
|
||||
```python title="client.py" hl_lines="22-30"
|
||||
```python title="client.py" hl_lines="7-15"
|
||||
--8<-- "docs_src/client/tutorial007.py"
|
||||
```
|
||||
|
||||
यह loop हर server के साथ सही है। `MCPServer` सब कुछ एक ही page में लौटाता है, इसलिए `next_cursor` `None` होता है और loop एक बार चलता है, यही वजह है कि ज़्यादातर code इसे कभी लिखता ही नहीं। जो servers सच में page करते हैं, और cursors जिन नियमों का पालन करते हैं, वे **[Pagination](../advanced/pagination.md)** में हैं।
|
||||
`list_all_tools` हर server के साथ सही है। `MCPServer` सब कुछ एक ही page में लौटाता है, इसलिए `next_cursor` `None` होता है और loop एक बार चलता है, यही वजह है कि ज़्यादातर code इसे कभी लिखता ही नहीं। जो servers सच में page करते हैं, और cursors जिन नियमों का पालन करते हैं, वे **[Pagination](../advanced/pagination.md)** में हैं।
|
||||
|
||||
## tests में {#in-tests}
|
||||
|
||||
बिना process और बिना port वाला `Client(mcp)` अपने आप में server के लिए test harness है।
|
||||
इस page की हर `client.py` HTTP के ज़रिए `server.py` तक पहुँची। test में आप network छोड़ देते हैं और `Client` को server object ही दे देते हैं: `from server import mcp`, फिर `Client(mcp)`। न process, न port, और ऊपर का हर method वैसे ही काम करता है।
|
||||
|
||||
इसी के लिए एक constructor flag बना है: `Client(mcp, raise_exceptions=True)`। इसका असर सिर्फ़ in-memory connections पर होता है, और **[Testing](../get-started/testing.md)** वह page है जो इसे समझाता है और इसके चारों ओर पूरा pattern बनाता है।
|
||||
इसी के लिए एक constructor flag बना है: `Client(mcp, raise_exceptions=True)`। इसका असर सिर्फ़ in-process connections पर होता है, और **[Testing](../get-started/testing.md)** वह page है जो इसे समझाता है और इसके चारों ओर पूरा pattern बनाता है।
|
||||
|
||||
## सारांश {#recap}
|
||||
|
||||
* `Client(x)` server object से in-memory connect होता है, URL string से Streamable HTTP पर, और बाकी किसी भी चीज़ से transport के ज़रिए।
|
||||
* `Client(x)` URL string से Streamable HTTP पर connect होता है, `StdioServerParameters` के लिए subprocess launch करता है, transport में सीधे enter करता है, और tests में server object ही ले लेता है।
|
||||
* `async with` ही पूरा lifecycle है। इसके अंदर `server_capabilities` और `protocol_version` पहले से भरे होते हैं; server दे तो `server_info` और `instructions` भी।
|
||||
* `list_tools()` आपको हर tool का `name`, `title`, `description` और `input_schema` देता है।
|
||||
* `call_tool()` model के लिए `content`, आपके code के लिए `structured_content`, और `is_error` लौटाता है। raise करने वाला tool एक result है, exception नहीं।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [c6899d3892bd9fa0, 79372cff3cc48a88, 63878d29e87c3e73, 13175843d3588af4, e7e2b9fd516f77de, 758f06399b513c1f, a05d7278487d610b]
|
||||
sections: [c6899d3892bd9fa0, 79372cff3cc48a88, c2dae1ebe2ebd543, 13175843d3588af4, df06056fb16b3846, 758f06399b513c1f, a05d7278487d610b]
|
||||
tool: 1
|
||||
---
|
||||
# OAuth clients {#oauth-clients}
|
||||
|
|
@ -81,18 +81,20 @@ authorization code flow को इंसान की ज़रूरत ठी
|
|||
|
||||
जब `Client` पहली बार request भेजता है, server `401` लौटाता है। provider कमान संभाल लेता है:
|
||||
|
||||
1. **Discovery.** यह `WWW-Authenticate` header पढ़ता है, `/.well-known/oauth-protected-resource` से server का Protected Resource Metadata लाता है, पता करता है कि कौन सा authorization server इस resource की रक्षा करता है, और **उस** server का metadata लाता है।
|
||||
1. **Discovery.** यह `WWW-Authenticate` header पढ़ता है, `/.well-known/oauth-protected-resource` से server का Protected Resource Metadata लाता है, पता करता है कि कौन सा authorization server इस resource की रक्षा करता है, और **उस** server का metadata लाता है। (कोई पुराना server जो resource metadata publish नहीं करता, उससे इसके बजाय उसके अपने origin पर authorization server metadata माँगा जाता है।) दोनों ही सूरतों में metadata को अपने `issuer` के रूप में उसी server का नाम देना होगा जिसके लिए उसे लाया गया था; इसके अलावा कुछ भी हो तो मना कर दिया जाता है।
|
||||
2. **Registration.** storage में कुछ नहीं है? यह आपके `OAuthClientMetadata` के साथ आपको dynamically register करता है और नतीजा store कर लेता है।
|
||||
3. **Authorization.** यह PKCE pair और `state` बनाता है, authorization URL तैयार करता है, आपके `redirect_handler` को await करता है, फिर code के लिए आपके `callback_handler` को await करता है।
|
||||
4. **Exchange.** यह code के बदले `OAuthToken` लेता है, उसे store करता है, और आपकी मूल request को `Authorization: Bearer ...` के साथ दोबारा भेजता है।
|
||||
|
||||
उसके बाद यह शांत रहता है। tokens storage से आते हैं, expire हुआ access token refresh token से refresh हो जाता है, और सिर्फ़ तब जब इनमें से कुछ काम नहीं करता, यह flow फिर से चलाता है।
|
||||
|
||||
इन सभी requests पर transport का एक नियम लागू होता है: जिस MCP request के अंदर ये चलती हैं उसी की तरह, ये किसी redirect को सिर्फ़ तभी follow करती हैं जब वह उसी origin पर रहे और method वही रखे (जैसे trailing-slash वाला 307/308), और किसी भी दूसरे redirect को ऐसे मानती हैं जैसे उस URL ने जवाब ही नहीं दिया।
|
||||
|
||||
आपने इसमें से कुछ नहीं लिखा। दो keyword arguments बचते हैं (`client_metadata_url` और `validate_resource_url`), और इस file को दोनों में से किसी की ज़रूरत नहीं। `client_metadata_url` जानने लायक है; इसका अपना section नीचे है।
|
||||
|
||||
### इसे आज़माएँ {#try-it}
|
||||
|
||||
इन docs के ज़्यादातर उदाहरण आप in-memory `Client(server)` से जाँच सकते हैं। यह नहीं: इस flow का पूरा मतलब ही HTTP `401` है, और in-memory client व उसके server के बीच कोई HTTP होता ही नहीं।
|
||||
in-memory `Client(server)`, जिसे आपके tests इस्तेमाल करते हैं, यहाँ किसी काम का नहीं: इस flow का पूरा मतलब ही HTTP `401` है, और in-memory client व उसके server के बीच कोई HTTP होता ही नहीं।
|
||||
|
||||
repository में live version मौजूद है। `examples/servers/simple-auth/` एक standalone authorization server और एक protected MCP server चलाता है; `examples/clients/simple-auth-client/` इसी page का client है, छोटी CLI में बढ़ा हुआ। उसकी README में दो commands हैं: servers शुरू करें, client को उनके सामने चलाएँ, और चारों चरण अपनी आँखों के सामने होते देखें।
|
||||
|
||||
|
|
@ -110,13 +112,14 @@ URL HTTPS होना चाहिए और उसका path root न हो;
|
|||
|
||||
`ClientCredentialsOAuthProvider` वही `httpx2.Auth` है, बस इंसान के बिना:
|
||||
|
||||
```python title="client.py" hl_lines="4 27-33"
|
||||
```python title="client.py" hl_lines="4 27-34"
|
||||
--8<-- "docs_src/oauth_clients/tutorial002.py"
|
||||
```
|
||||
|
||||
क्या बदला:
|
||||
|
||||
* न `OAuthClientMetadata`, न handlers। आप `client_id` और `client_secret` देते हैं; provider उनके इर्द-गिर्द एक न्यूनतम `client_credentials` registration बनाता है और dynamic registration पूरी तरह छोड़ देता है।
|
||||
* `issuer` उस authorization server का नाम बताता है जिसने वे credentials जारी किए; वही `issuer` value इस्तेमाल करें जो उसका `/.well-known/oauth-authorization-server` document लौटाता है। discovery अब भी ऊपर बताए तरीके से चलती है, लेकिन token requests हमेशा सिर्फ़ **उसी** issuer के metadata से बनती हैं; अगर MCP server कहीं और इशारा करता है, तो flow इसके बजाय `OAuthFlowError` के साथ रुक जाता है। इसे छोड़ देना deprecated है और 3.0 में यह ज़रूरी हो जाएगा (**[Deprecated features](../deprecated.md#deprecated-sdk-helpers)** देखें); तब तक provider warning देता है और discovery को जो भी authorization server मिलता है, उसे इस्तेमाल करता है।
|
||||
* `scope` space से अलग की गई string है, OAuth का wire format।
|
||||
* आगे का सब कुछ बिल्कुल वही है: वही `TokenStorage`, वही `httpx2.AsyncClient(auth=...)`, वही `streamable_http_client`।
|
||||
|
||||
|
|
@ -129,7 +132,7 @@ default रूप से secret token request पर HTTP Basic auth के र
|
|||
एक और provider `mcp.client.auth.extensions.client_credentials` में रहता है:
|
||||
**`PrivateKeyJWTOAuthProvider`**, उन clients के लिए जो shared secret के बजाय JWT से
|
||||
authenticate करते हैं (`private_key_jwt`, key-pair और workload-identity वाला रूप)। यह उसी
|
||||
pattern पर चलता है: एक बनाएँ, `auth=` पर लगाएँ। उसी module में
|
||||
pattern पर चलता है: एक बनाएँ (यह भी वही optional `issuer` लेता है), `auth=` पर लगाएँ। उसी module में
|
||||
`SignedJWTParameters` और `static_assertion_provider` भी हैं, दो helpers जो इसका assertion बनाते हैं।
|
||||
|
||||
बिना इंसान वाली एक और स्थिति है: client किसी enterprise का है जिसका identity provider, न कि user, तय करता है कि वह किन MCP servers तक पहुँच सकता है। वह अलग grant है, अपने trust model और अपने page के साथ, **[Identity assertion](identity-assertion.md)**।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [09c857a25a9dc37a, 43bc6a76a243a50e, 0a716022a88768df, 4b7f78042bfcfff7, c112662e61b03315, 58974ba1f489a8b4, d18adbdbb835ea73]
|
||||
sections: [09c857a25a9dc37a, 43bc6a76a243a50e, 0a716022a88768df, 4b7f78042bfcfff7, c112662e61b03315, 58974ba1f489a8b4, ed4d17e894864056]
|
||||
tool: 1
|
||||
---
|
||||
# Session groups {#session-groups}
|
||||
|
|
@ -78,7 +78,7 @@ translation:
|
|||
## सारांश {#recap}
|
||||
|
||||
* `ClientSessionGroup` कई server connections रखता है और उनके tools, resources और prompts को एक-एक `dict` में मिला देता है।
|
||||
* हर server के लिए `connect_to_server(params)`। यह transport parameters लेता है, कभी वह server object या URL नहीं जो `Client` लेता है।
|
||||
* हर server के लिए `connect_to_server(params)`। यह transport parameters लेता है, कभी वह URL या `Transport` नहीं जो `Client` लेता है।
|
||||
* `group.call_tool(name, arguments)` आपके लिए call को उस server तक पहुँचाता है जिसका वह tool है।
|
||||
* नाम पूरे group में unique होने ज़रूरी हैं; `search` tool वाले दो servers अपने आप साथ नहीं रह सकते।
|
||||
* `component_name_hook=` हर register किए गए नाम को फिर से लिखता है। dict key बदलती है, wire पर जाने वाला नाम नहीं।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [9cac816674181eb0, 0700f337babcd4dd, 2bde0dd58cdf00f5, 40b4916d82eaf1d4, 3d0832f39b0d7059, dfa4446556badef0, 5bd93be2ab2ecb9c]
|
||||
sections: [9cac816674181eb0, 7c157764133fea1f, 40b4916d82eaf1d4, 10d151f2cc75317f, 3d0832f39b0d7059, 92742ba36533633d, 0aeca6145e7bd302]
|
||||
tool: 1
|
||||
---
|
||||
# Client transports {#client-transports}
|
||||
|
|
@ -11,30 +11,15 @@ translation:
|
|||
|
||||
हर transport का **server** वाला पक्ष (`mcp.run()` क्या करता है और आप क्या deploy करते हैं) **[अपना server चलाना](../run/index.md)** में है।
|
||||
|
||||
## Memory में {#in-memory}
|
||||
|
||||
server object ही पास करें:
|
||||
|
||||
```python title="client.py" hl_lines="14"
|
||||
--8<-- "docs_src/client_transports/tutorial001.py"
|
||||
```
|
||||
|
||||
न कोई subprocess, न कोई port, न wire पर कोई bytes। client और server एक ही process में दो objects हैं, और call फिर भी असली protocol layer से होकर जाती है: `search_books` ठीक वैसे ही list, validate और invoke होता है जैसे HTTP पर होता।
|
||||
|
||||
इससे यह एक साथ दो काम करता है:
|
||||
|
||||
* **Test harness।** इस documentation का हर उदाहरण इसी तरीके से चलाया जाता है, और **[Testing](../get-started/testing.md)** page पूरा pattern इसी के इर्द-गिर्द बनाता है।
|
||||
* **Embedding API।** जो application खुद server बनाता है, उसे उसके tools call करने के लिए network hop की ज़रूरत नहीं।
|
||||
|
||||
## Streamable HTTP {#streamable-http}
|
||||
|
||||
URL string पास करें और आपको **Streamable HTTP** मिलता है, वह transport जिसके पीछे आप deploy करते हैं:
|
||||
URL string पास करें और आपको **Streamable HTTP** मिलता है, वह transport जिसके पीछे आप deploy करते हैं और जिसे सबसे पहले चुनना चाहिए:
|
||||
|
||||
```python title="client.py" hl_lines="5"
|
||||
--8<-- "docs_src/client_transports/tutorial002.py"
|
||||
```
|
||||
|
||||
पूरा production client बस इतना ही है। `Client` आपके लिए URL को `streamable_http_client(...)` में लपेट देता है, एक `httpx2.AsyncClient` के ऊपर जो MCP की ज़रूरत के हिसाब से configure किया गया है: `follow_redirects=True`, connect/write/pool के लिए 30 सेकंड का timeout, और 300 सेकंड का read timeout, क्योंकि server response stream को खुला रख सकता है।
|
||||
पूरा production client बस इतना ही है। `Client` आपके लिए URL को `streamable_http_client(...)` में लपेट देता है, एक `httpx2.AsyncClient` के ऊपर जो MCP की ज़रूरत के हिसाब से configure किया गया है: connect/write/pool के लिए 30 सेकंड का timeout, और 300 सेकंड का read timeout, क्योंकि server response stream को खुला रख सकता है।
|
||||
|
||||
!!! check
|
||||
जो `Client` आपने बनाया है वह connected **नहीं** है। बनाने से सिर्फ़ transport चुना जाता है;
|
||||
|
|
@ -50,7 +35,7 @@ URL string पास करें और आपको **Streamable HTTP** मि
|
|||
|
||||
जैसे ही आपको `Authorization` header, cookie, proxy, mTLS या कोई अलग timeout चाहिए, `httpx2.AsyncClient` खुद बनाएँ और उसे `streamable_http_client` को दें:
|
||||
|
||||
```python title="client.py" hl_lines="8-14"
|
||||
```python title="client.py" hl_lines="8-13"
|
||||
--8<-- "docs_src/client_transports/tutorial003.py"
|
||||
```
|
||||
|
||||
|
|
@ -80,9 +65,30 @@ environment variables set करें या अपने `httpx2.AsyncClient`
|
|||
!!! info
|
||||
`httpx2` जाना-पहचाना `httpx` API ही रखता है, इसलिए अगर आप `httpx` जानते हैं तो यहाँ auth,
|
||||
proxies, event hooks, retries और connection limits कैसे करने हैं, यह आप पहले से जानते हैं। SDK न ऊपर से कुछ जोड़ता है, न कुछ
|
||||
हटाता है। OAuth भी यहीं जुड़ता है:
|
||||
हटाता है, सिवाय [redirect handling](#redirects) के। OAuth भी यहीं जुड़ता है:
|
||||
`httpx2.AsyncClient(auth=OAuthClientProvider(...))`। वह पूरा flow **[OAuth clients](oauth-clients.md)** में है।
|
||||
|
||||
### Redirects {#redirects}
|
||||
|
||||
transport उसी URL से जुड़ता है जो आपने दिया, और सिर्फ़ उसी origin से।
|
||||
|
||||
* जो `307`/`308` redirect उसी scheme, host और port पर रहता है, उसे follow किया जाता है, और उसी host पर `http://` → `https://` को भी। आम `/mcp` → `/mcp/` वाला trailing-slash redirect इसी में आ जाता है।
|
||||
* कहीं और जाने वाला redirect follow **नहीं** किया जाता। call इस error के साथ fail होती है:
|
||||
|
||||
```text
|
||||
MCPError: Redirect to https://other.example.com/mcp not followed; use that URL as the endpoint if it is the intended server
|
||||
```
|
||||
|
||||
अगर वह URL वही server है जो आप चाहते थे, तो उसे अपने config में डालें। अगर नहीं, तो server या उसके आगे लगा कोई proxy गलत configure है।
|
||||
|
||||
यह आपके पास किए गए किसी भी `httpx2.AsyncClient` पर लागू होता है: MCP requests के लिए उसकी `follow_redirects` setting नहीं देखी जाती, किसी भी दिशा में। SDK के OAuth providers अपनी requests पर यही नियम लागू करते हैं।
|
||||
|
||||
!!! tip
|
||||
`Redirect to http://… not followed: it would downgrade this HTTPS endpoint to plain HTTP` का मतलब है कि
|
||||
server किसी ऐसे TLS-terminating proxy के पीछे है जिसके बारे में उसे पता नहीं, और वह `http://` redirects जारी कर रहा है।
|
||||
इसे server पर ठीक किया जाता है (**[Deploy & scale](../run/deploy.md#behind-a-tls-terminating-proxy)**),
|
||||
या ठीक वही `https://…/` URL इस्तेमाल करके जो message सुझाता है।
|
||||
|
||||
## stdio {#stdio}
|
||||
|
||||
**stdio** server एक subprocess है। client उसे launch करता है, उसके stdin पर JSON-RPC लिखता है और उसके stdout से JSON-RPC पढ़ता है। desktop host आपकी machine पर server इसी तरह चलाता है: host यही code **है**, बस ऊपर एक UI के साथ, और **[असली host से जुड़ें](../get-started/real-host.md)** यही रिश्ता host की तरफ़ से, एक config file के रूप में दिखाता है।
|
||||
|
|
@ -105,6 +111,18 @@ child का stderr आपके stderr पर जाता है। उसे
|
|||
जिस server को API key चाहिए, उसे वह वहाँ नहीं मिलेगी। उसे `env=` से explicitly पास करें; वे
|
||||
variables allow-list के ऊपर merge हो जाते हैं। ऊपर `BOOKSHOP_API_KEY` यही कर रहा है।
|
||||
|
||||
## Memory में {#in-memory}
|
||||
|
||||
test में न कुछ deploy करना है, न कुछ launch करना। server object ही पास करें:
|
||||
|
||||
```python hl_lines="14"
|
||||
--8<-- "docs_src/client_transports/tutorial001.py"
|
||||
```
|
||||
|
||||
न कोई subprocess, न कोई port, न wire पर कोई bytes। client और server एक ही process में दो objects हैं, और call फिर भी असली protocol layer से होकर जाती है: `search_books` ठीक वैसे ही list, validate और invoke होता है जैसे HTTP पर होता। **[Testing](../get-started/testing.md)** पूरा pattern इसी के इर्द-गिर्द बनाता है।
|
||||
|
||||
यही रूप embedding API का काम भी करता है: जो application खुद server बनाता है, वह बिना network hop के उसके tools call कर सकता है।
|
||||
|
||||
## SSE {#sse}
|
||||
|
||||
`mcp.client.sse` का `sse_client(url)` वह HTTP transport है जिसकी जगह Streamable HTTP ने ली। जो server अब भी इसे बोलता है, उससे बात करने के लिए इसे उसी तरह wrap करें, `Client(sse_client("http://localhost:8000/sse"))`, और इस पर कुछ नया न बनाएँ।
|
||||
|
|
@ -113,15 +131,16 @@ child का stderr आपके stderr पर जाता है। उसे
|
|||
|
||||
`Client` के लिए ऊपर की सभी चीज़ें एक ही हैं।
|
||||
|
||||
**transport** कोई भी async context manager है जो message streams का `(read, write)` जोड़ा yield करता है: औपचारिक रूप से, `mcp.client` का `Transport` protocol। `Client` अपने argument को type से resolve करता है: server object in-process जुड़ता है, `str` `streamable_http_client(url)` बन जाता है, `StdioServerParameters` `stdio_client(params)` बन जाता है, और बाकी सब कुछ सीधे transport के रूप में enter किया जाता है। यही आख़िरी नियम वजह है कि `stdio_client(...)`, `streamable_http_client(...)` और `sse_client(...)` सब उसी एक slot में बैठते हैं, और यही वजह है कि आप अपना खुद का भी लिख सकते हैं।
|
||||
**transport** कोई भी async context manager है जो message streams का `(read, write)` जोड़ा yield करता है: औपचारिक रूप से, `mcp.client` का `Transport` protocol। `Client` अपने argument को type से resolve करता है: `str` `streamable_http_client(url)` बन जाता है, `StdioServerParameters` `stdio_client(params)` बन जाता है, server object in-process जुड़ता है, और बाकी सब कुछ सीधे transport के रूप में enter किया जाता है। यही आख़िरी नियम वजह है कि `stdio_client(...)`, `streamable_http_client(...)` और `sse_client(...)` सब उसी एक slot में बैठते हैं, और यही वजह है कि आप अपना खुद का भी लिख सकते हैं।
|
||||
|
||||
## सारांश {#recap}
|
||||
|
||||
* `Client(mcp)` (server object) memory में जुड़ता है। इसे tests और embedding के लिए इस्तेमाल करें।
|
||||
* `Client("http://.../mcp")` (URL) Streamable HTTP पर जुड़ता है, जो production transport है।
|
||||
* Headers, auth, proxies और timeouts उस `httpx2.AsyncClient` पर होने चाहिए जो आप `streamable_http_client(url, http_client=...)` को पास करते हैं। कोई `headers=` keyword नहीं है।
|
||||
* Redirects सिर्फ़ URL के अपने origin के भीतर follow होते हैं (trailing-slash वाला `307`/`308`), और उसी host पर `http`→`https`। बाकी सब `Redirect to … not followed` के साथ fail होता है; final URL configure करें।
|
||||
* stdio है `Client(StdioServerParameters(...))`। इसे खुद `stdio_client(...)` में सिर्फ़ तब wrap करें जब child का stderr कहीं और भेजना हो।
|
||||
* subprocess को allow-list वाला environment मिलता है, आपका नहीं; `env=` उसमें जोड़ता है।
|
||||
* `Client(mcp)` (server object) memory में जुड़ता है। इसे tests में इस्तेमाल करें, या server को उसी application में embed करने के लिए जिसने उसे बनाया।
|
||||
* transport वह हर चीज़ है जिस पर आप `async with x as (read, write)` कर सकें। जो कुछ server object, URL या `StdioServerParameters` नहीं है, `Client` उसे सीधे उसी protocol को सौंप देता है।
|
||||
* `Client` बनाने से transport चुना जाता है। `async with` उसे खोलता है।
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
---
|
||||
translation:
|
||||
sections: [490237e61c3a7a44, 01262a123ad9501d, 429db5b574a2ac08, e2d0d273fbd2d74b, 64ab0331e868f3d4, 6c8878ce2d1f6d56, 4068f23e371bf0b3, eaef75b8725bc931]
|
||||
sections: [4c1dea378b2b1bf7, 01262a123ad9501d, 429db5b574a2ac08, e2d0d273fbd2d74b, 64ab0331e868f3d4, 6c8878ce2d1f6d56, c3d1099701156881, e185a1b8e53669f6]
|
||||
tool: 1
|
||||
---
|
||||
# Deprecated features {#deprecated-features}
|
||||
|
||||
2026-07-28 spec पाँच चीज़ों को retire करता है। SDK अब भी इनमें से हर एक को implement करता है, और अब हर एक पर **deprecation warning** लगी है। एक SDK helper अपनी अलग वजह से deprecated है और [आख़िर में](#deprecated-sdk-helpers) दिया गया है।
|
||||
2026-07-28 spec पाँच चीज़ों को retire करता है। SDK अब भी इनमें से हर एक को implement करता है, और अब हर एक पर **deprecation warning** लगी है। कुछ SDK-level deprecations अपनी अलग वजह से हैं और [आख़िर में](#deprecated-sdk-helpers) दी गई हैं।
|
||||
|
||||
नीचे दी गई table हर deprecated feature का नाम, उसके हटने की वजह, और उसकी जगह किस replacement पर build करना है, यह बताती है।
|
||||
|
||||
|
|
@ -137,11 +137,13 @@ warnings.filterwarnings("ignore", category=MCPDeprecationWarning)
|
|||
|
||||
## Deprecated SDK helpers {#deprecated-sdk-helpers}
|
||||
|
||||
ये spec के बदलाव नहीं हैं, सिर्फ़ SDK के अंदरूनी हिस्से हैं जिनका बेहतर replacement मौजूद है। ये उसी `MCPDeprecationWarning` के साथ warn करते हैं और 3.0 में हटा दिए जाएँगे।
|
||||
ये spec के बदलाव नहीं हैं, सिर्फ़ SDK के इस्तेमाल के वे तरीके हैं जिनका बेहतर replacement मौजूद है। ये उसी `MCPDeprecationWarning` के साथ warn करते हैं, और 3.0 पुराना रूप हटा देता है।
|
||||
|
||||
| Deprecated | इसके बजाय क्या करें |
|
||||
|---|---|
|
||||
| `FuncMetadata.call_fn_with_arg_validation()` | `FuncMetadata.validate_arguments()` और फिर `FuncMetadata.call_fn()`। इसे सिर्फ़ वही code call करता था जो `FuncMetadata` को सीधे चलाता है (जैसे कोई custom `Tool` subclass)। |
|
||||
| `validate_token_resource=` के बिना `AuthSettings(resource_server_url=...)` | इसे set करें: `True` पर server वे bearer tokens ठुकरा देता है जिन्हें आपका verifier `resource_server_url` के लिए जारी हुआ नहीं बताता, `False` का मतलब है कि आपका verifier token का audience ख़ुद जाँचता है (**[Authorization](run/authorization.md#a-token-verifier)** देखें)। set न होने पर व्यवहार `False` जैसा है; 3.0 में जब भी `resource_server_url` set हो, default `True` होगा। |
|
||||
| `issuer=` के बिना `ClientCredentialsOAuthProvider(...)` या `PrivateKeyJWTOAuthProvider(...)` | `issuer=` दें, जिसमें credentials जारी करने वाले authorization server का नाम हो (**[OAuth clients लिखना](client/oauth-clients.md#machine-to-machine)** देखें)। इसके बिना MCP server तय करता है कि credentials किस authorization server को मिलें; 3.0 में यह keyword ज़रूरी हो जाएगा। |
|
||||
|
||||
## सारांश {#recap}
|
||||
|
||||
|
|
@ -150,7 +152,7 @@ warnings.filterwarnings("ignore", category=MCPDeprecationWarning)
|
|||
* Deprecated होना बस सलाह भर है: wire में कोई बदलाव नहीं, 2026 से पहले के sessions पर सब कुछ काम करता रहता है, और आपको साफ़ दिखने वाली `MCPDeprecationWarning` मिलती है (यह `UserWarning` है, इसलिए default रूप से चालू है)।
|
||||
* sampling और roots को इसके अलावा back-channel चाहिए जो 2026-07-28 session के पास नहीं है। modern connection पर ये warn करते हैं और फिर raise करते हैं।
|
||||
* `warnings.filterwarnings("ignore", category=MCPDeprecationWarning)` पूरी category को चुप कराता है; pytest में `"error::mcp.MCPDeprecationWarning"` इसे test failure में बदल देता है।
|
||||
* एक SDK helper, `FuncMetadata.call_fn_with_arg_validation()`, अलग से deprecated है और 3.0 में हटाया जाएगा।
|
||||
* [SDK-level deprecations](#deprecated-sdk-helpers) पर भी यही नियम लागू है: अभी ये warn करते हैं, और 3.0 पुराना रूप हटा देता है।
|
||||
* नया code इनमें से किसी पर भी नहीं बनना चाहिए।
|
||||
|
||||
इन docs का बाकी हर page मौजूदा API सिखाता है।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [0d6c05bcbf836bf3, 59a7b14eeefc68c1, 7114d8d6daba203f, e8bbb56a98ba7bc9, 5138010f6159901c, f78da7c7c363d4c6, 220a939cab348686]
|
||||
sections: [0d6c05bcbf836bf3, 9a78b5f6b44b18ab, 7114d8d6daba203f, e8bbb56a98ba7bc9, bfd2fd1153e71dac, 1615a994ef071fdd, 65c599fae991f245]
|
||||
tool: 1
|
||||
---
|
||||
# पहले कदम {#first-steps}
|
||||
|
|
@ -17,7 +17,7 @@ translation:
|
|||
* **client** host के अंदर रहता है और MCP बोलता है। host जितने servers से जुड़ा है, हर एक के लिए एक client चलाता है।
|
||||
* **server** वह है जो आप इस SDK से बनाते हैं। यह clients को चीज़ें expose करता है। यह model से सीधे कभी बात नहीं करता।
|
||||
|
||||
server आप लिखते हैं। hosts किसी और का product हैं। SDK आपको एक `Client` भी देता है। इससे आप अपने servers test करेंगे, और यह इसी page पर आगे दिखता है।
|
||||
server आप लिखते हैं। hosts किसी और का product हैं। SDK आपको `Client` भी देता है, वही class जिससे कोई host URL से server तक पहुँचता है या उसे subprocess के रूप में launch करता है। यह इसी page पर आगे दिखता है, और इसी से आप अपने servers test भी करेंगे।
|
||||
|
||||
## तीन primitives {#the-three-primitives}
|
||||
|
||||
|
|
@ -83,22 +83,20 @@ Inspector में आपने तीन tabs देखे। उसे कै
|
|||
|
||||
जब client जुड़ता है, server अपनी **capabilities** declare करता है: requests के कौन-से परिवारों का वह जवाब देगा। client उसी declaration से तय करता है कि माँगे भी तो क्या। आपने यह कभी नहीं लिखा; `MCPServer` आपके लिए इसे declare करता है।
|
||||
|
||||
खुद देखें। SDK का `Client` सीधे server object लेता है और उससे **in memory** जुड़ता है (न subprocess, न port):
|
||||
खुद देखें। एक terminal में `server.py` को HTTP पर चलता छोड़ दें:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
```console
|
||||
uv run mcp run server.py --transport streamable-http
|
||||
```
|
||||
|
||||
from mcp import Client
|
||||
और दूसरे terminal से client को उससे जोड़ें:
|
||||
|
||||
from server import mcp
|
||||
```python title="client.py" hl_lines="7-8"
|
||||
--8<-- "docs_src/first_steps/tutorial001_client.py"
|
||||
```
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async with Client(mcp) as client:
|
||||
print(client.server_capabilities.model_dump(exclude_none=True))
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
```console
|
||||
python client.py
|
||||
```
|
||||
|
||||
```text
|
||||
|
|
@ -118,8 +116,9 @@ asyncio.run(main())
|
|||
ध्यान दें कि क्या नहीं है। `completions` (resource templates और prompts के लिए argument autocomplete) को आपका लिखा handler चाहिए, इस server में वह नहीं है, इसलिए capability मौजूद नहीं है और सलीके वाला client पूछेगा ही नहीं। हर optional चीज़ का यही नियम है: चीज़ register करें और capability आ जाती है; **[Completions](../servers/completions.md)** इसे साबित करता है।
|
||||
|
||||
!!! info
|
||||
`Client(mcp)` वही in-memory client है जिससे इन docs का हर उदाहरण test होता है, और
|
||||
इसी से आप अपने servers test करेंगे। इसे पूरा एक page मिलता है: **[Testing](testing.md)**।
|
||||
वह `client.py` पूरा MCP client है, और **[Client](../client/index.md)** उसका page है।
|
||||
test में आप terminal और port छोड़ देते हैं और `Client` को सीधे server object ही थमा देते हैं,
|
||||
`Client(mcp)`। उसे भी पूरा एक page मिलता है: **[Testing](testing.md)**।
|
||||
|
||||
## जो आपने नहीं लिखा {#what-you-did-not-write}
|
||||
|
||||
|
|
@ -128,7 +127,7 @@ asyncio.run(main())
|
|||
* JSON Schema। `a: int, b: int` **ही** `add` का schema है।
|
||||
* request handler। `tools/list`, `resources/read`, `prompts/get`: सब आपके लिए serve होते हैं।
|
||||
* capability declaration। `MCPServer` ने आपके लिए बना दिया।
|
||||
* protocol की एक भी line। version negotiation, JSON-RPC framing, capability exchange: यह सब `mcp dev` और `Client(mcp)` के अंदर हुआ, और आपने कभी देखा ही नहीं।
|
||||
* protocol की एक भी line। version negotiation, JSON-RPC framing, capability exchange: यह सब `mcp dev` और `client.py` के अंदर हुआ, और आपने कभी देखा ही नहीं।
|
||||
|
||||
यही अनुपात SDK का पूरा मतलब है।
|
||||
|
||||
|
|
@ -139,6 +138,6 @@ asyncio.run(main())
|
|||
* हर primitive के लिए एक decorator: `@mcp.tool()`, `@mcp.resource(uri)`, `@mcp.prompt()`। नाम, description और schema function से आते हैं।
|
||||
* `{param}` वाला URI resource **template** बनाता है, जो concrete resources से अलग सूची में दिखता है।
|
||||
* server की **capabilities** आपके लिए declare हो जाती हैं, और client वही माँगता है जो server declare करता है।
|
||||
* `Client(mcp)` server object से in memory जुड़ता है: पहले दिन से आपका test harness।
|
||||
* `Client("http://localhost:8000/mcp")` आपके चलते हुए server से बात करता है। उसकी जगह इसे server object थमा दें, `Client(mcp)`, और पहले दिन से यही आपका test harness है।
|
||||
|
||||
आगे है **[असली host से जुड़ें](real-host.md)**: यही server Claude Desktop या किसी IDE के अंदर, सच में। फिर **[Testing](testing.md)**: एक page, एक in-memory client, और आपको कभी अंदाज़ा नहीं लगाना पड़ेगा कि यह काम करता है या नहीं। उसके बाद हर primitive को अपना page मिलता है, शुरुआत उससे जिसे model चलाता है: **[Tools](../servers/tools.md)**।
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
translation:
|
||||
sections: ['4926721070127497', c52a1de2b6b32f40, 8e792bf8c7489ec6, 627195f7159e24ef]
|
||||
sections: [5d13c2f0ba42c0d2, c52a1de2b6b32f40, 8e792bf8c7489ec6, 38552ea228b0a04f]
|
||||
tool: 1
|
||||
---
|
||||
# Testing {#testing}
|
||||
|
||||
Python SDK में `Client` class आती है जिसके साथ **in-memory transport** मिलता है: इसे अपना server object दें और यह उससे सीधे जुड़ जाता है।
|
||||
SDK की `Client` class, वही जो किसी URL से जुड़ती है या subprocess शुरू करती है, **in memory** भी जुड़ती है: इसे अपना server object दें और यह उससे सीधे बात करती है।
|
||||
|
||||
कोई subprocess नहीं। कोई port नहीं। कोई transport ही नहीं। यह वही विचार है जो FastAPI के `TestClient` का है।
|
||||
कोई subprocess नहीं। कोई port नहीं। wire पर कुछ नहीं। यह वही विचार है जो FastAPI के `TestClient` का है।
|
||||
|
||||
## Basic usage {#basic-usage}
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ tool body के **बाहर** का failure अलग है। `Client(mcp
|
|||
|
||||
tests में इसे चालू रहने दें। production code में इसका कोई मतलब नहीं है।
|
||||
|
||||
## Default रूप से in-process {#in-process-by-default}
|
||||
## Default रूप से पीढ़ी-निरपेक्ष {#era-neutral-by-default}
|
||||
|
||||
!!! note
|
||||
`Client(mcp)` in-process जुड़ता है और default रूप से **पीढ़ी-निरपेक्ष** है: यह server को probe करता है और
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [74011e683045eea9, 9b64cc175c18b6a9, 4b41be4824030397, e3b1502da786ec33, 71e41161f143c6a9, 9ec2c1eeb8c36378, 8dd027377d46448b, f81491125dcbfe8b]
|
||||
sections: [74011e683045eea9, 9b64cc175c18b6a9, 4b41be4824030397, e3b1502da786ec33, 71e41161f143c6a9, 9ec2c1eeb8c36378, b47667184ca5b516, f81491125dcbfe8b]
|
||||
tool: 1
|
||||
---
|
||||
# Multi-round-trip requests {#multi-round-trip-requests}
|
||||
|
|
@ -164,7 +164,7 @@ low-level `Server` बिना-batteries वाला स्तर है: `MCP
|
|||
|
||||
## एक 2026-07-28 result {#a-2026-07-28-result}
|
||||
|
||||
`InputRequiredResult` सिर्फ़ protocol version **2026-07-28** पर मौजूद है। in-memory `Client(server)` इसे आपके लिए negotiate करता है; wire पर, `mode="auto"` इसे खोज लेता है। connect करने के बाद `client.protocol_version` बताता है कि आपको क्या मिला।
|
||||
`InputRequiredResult` सिर्फ़ protocol version **2026-07-28** पर मौजूद है। `Client` का default `mode="auto"` इसे किसी भी connection पर खोज लेता है। connect करने के बाद `client.protocol_version` बताता है कि आपको क्या मिला।
|
||||
|
||||
!!! warning
|
||||
pre-2026 session के पास `InputRequiredResult` रखने की कोई जगह नहीं है। `mode="legacy"` connection पर
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [5315262fe26b33e1, 9d8e98840f1b78f0, 0284b215e85366c4, 8534d8dbb4053a70, 2966fac6fe697007]
|
||||
sections: [5315262fe26b33e1, 9d8e98840f1b78f0, 52d6009a07e770ea, 8534d8dbb4053a70, 2966fac6fe697007]
|
||||
tool: 1
|
||||
---
|
||||
# Progress {#progress}
|
||||
|
|
@ -27,21 +27,19 @@ translation:
|
|||
|
||||
## Client से सुनें {#listen-for-it-from-the-client}
|
||||
|
||||
Client **हर call पर** अलग से opt in करता है, `call_tool` को `progress_callback=` देकर:
|
||||
client **हर call पर** अलग से opt in करता है, `call_tool` को `progress_callback=` देकर:
|
||||
|
||||
```python title="client.py" hl_lines="7 16"
|
||||
```python title="client.py" hl_lines="5 14"
|
||||
import anyio
|
||||
from mcp import Client
|
||||
|
||||
from server import mcp
|
||||
|
||||
|
||||
async def show(progress: float, total: float | None, message: str | None) -> None:
|
||||
print(f"{message} ({progress}/{total})")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
async with Client(mcp) as client:
|
||||
async with Client("http://localhost:8000/mcp") as client:
|
||||
result = await client.call_tool(
|
||||
"import_catalog",
|
||||
{"urls": ["https://example.com/a.json", "https://example.com/b.json"]},
|
||||
|
|
@ -53,30 +51,34 @@ async def main() -> None:
|
|||
anyio.run(main)
|
||||
```
|
||||
|
||||
Callback एक `async` function है जो ठीक वही लेता है जो server ने report किया: `progress`, `total`, `message`।
|
||||
callback एक `async` function है जो ठीक वही लेता है जो server ने report किया: `progress`, `total`, `message`।
|
||||
|
||||
!!! info
|
||||
`Client(mcp)` सीधे server object से जुड़ता है, memory में, वही client जिस पर **[Testing](../get-started/testing.md)**
|
||||
page बना है। `Client` चाहे कोई भी transport इस्तेमाल करे, `progress_callback` parameter वही रहता है;
|
||||
जो **timing** आप अभी देखने वाले हैं वह in-memory connection की है। वह आपका callback inline चलाता है,
|
||||
इसलिए हर report `call_tool` के लौटने से पहले पहुँच जाती है। असली transport पर notifications और result
|
||||
में होड़ लगती है, और एक धीमा callback `call_tool` के लौटने के बाद भी चल रहा हो सकता है।
|
||||
`Client` को आपने चाहे जो भी दिया हो, `progress_callback` parameter वही रहता है: यहाँ की तरह
|
||||
URL, कोई `StdioServerParameters`, या test में server object। हाँ, असली transport पर timing का
|
||||
ध्यान रखें। हर notification अपने आप में अलग, response के साथ-साथ पहुँचता है, इसलिए एक धीमा
|
||||
callback `call_tool` के लौटने के बाद भी चल रहा हो सकता है। सिर्फ़ in-process test connection
|
||||
ही callback को inline चलाता है और यह पक्का करता है कि हर report पहले पहुँचे।
|
||||
|
||||
### इसे आज़माएँ {#try-it}
|
||||
|
||||
`client.py` को `server.py` के बगल में रखें और चलाएँ:
|
||||
`server.py` को HTTP पर serve करें, फिर दूसरे terminal से client चलाएँ:
|
||||
|
||||
```console
|
||||
uv run mcp run server.py --transport streamable-http
|
||||
```
|
||||
|
||||
```console
|
||||
python client.py
|
||||
```
|
||||
|
||||
```text
|
||||
Imported https://example.com/a.json (1/2)
|
||||
Imported https://example.com/b.json (2/2)
|
||||
Imported https://example.com/a.json (1.0/2.0)
|
||||
Imported https://example.com/b.json (2.0/2.0)
|
||||
{'result': 'Imported 2 records.'}
|
||||
```
|
||||
|
||||
Server पर हर `await ctx.report_progress(...)` client पर `show` का एक call बना, उसी क्रम में, और दोनों lines `call_tool` के लौटने से **पहले** print हुईं। Progress result में बंडल होकर नहीं आता; tool के काम करते रहने के दौरान ही stream होता है।
|
||||
server पर हर `await ctx.report_progress(...)` client पर `show` का एक call बना, उसी क्रम में। progress result में समेटकर नहीं भेजा जाता। वह tool के काम करते रहने के दौरान ही stream होता है।
|
||||
|
||||
!!! warning
|
||||
`progress_callback` **call** का है, `Client` का नहीं। इसके लिए कोई constructor argument नहीं है,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [478fd619e5f90ef8, aef094a00e44e248, bab8cbf3449fa7e9, df1809b15a58335b, 5f9d8c2336ed0239, f54974398e43ddef, b24443dd78584870]
|
||||
sections: [d98e337bf28845e0, dd642acbba36f615, f547b3e76da19c94, 149513378588da5c, 4e149ed992046709, f54974398e43ddef, b24443dd78584870]
|
||||
tool: 1
|
||||
---
|
||||
# Protocol versions {#protocol-versions}
|
||||
|
|
@ -11,9 +11,17 @@ MCP की दो पीढ़ियाँ हैं।
|
|||
|
||||
आपको इसकी चिंता लगभग कभी नहीं करनी पड़ती, क्योंकि `Client` आपके लिए negotiate कर लेता है। यह page उस एक constructor argument के बारे में है जो इसे नियंत्रित करता है, `mode=`, और उन तीन मौकों के बारे में जब आप इसे बदलते हैं।
|
||||
|
||||
इस page का हर snippet एक `client.py` है जो **[Client](client/index.md)** वाले Bookshop `server.py` से बात करता है। उस server को एक terminal में शुरू करें:
|
||||
|
||||
```console
|
||||
uv run mcp run server.py --transport streamable-http
|
||||
```
|
||||
|
||||
फिर हर snippet को दूसरे terminal में `python client.py` से चलाएँ।
|
||||
|
||||
## `mode="auto"` {#modeauto}
|
||||
|
||||
```python title="client.py" hl_lines="14-15"
|
||||
```python title="client.py" hl_lines="7-8"
|
||||
--8<-- "docs_src/protocol_versions/tutorial001.py"
|
||||
```
|
||||
|
||||
|
|
@ -31,13 +39,14 @@ MCP की दो पीढ़ियाँ हैं।
|
|||
पूरा feature बस इतना ही है। एक `Client`, किसी भी पीढ़ी का server, और code में कोई branching नहीं।
|
||||
|
||||
!!! info
|
||||
`MCPServer` हर transport पर `server/discover` का जवाब देता है — in-memory, stdio, streamable
|
||||
HTTP — इसलिए आपके अपने server के साथ `auto` हमेशा `2026-07-28` पर पहुँचता है। fallback सिर्फ़
|
||||
असली pre-2026 server के सामने ही चलता है, और ठीक वहीं आप इसे चाहते भी हैं।
|
||||
`MCPServer` हर transport पर `server/discover` का जवाब देता है — Streamable HTTP, stdio, और वह
|
||||
in-process connection जो आपके tests इस्तेमाल करते हैं — इसलिए आपके अपने server के साथ `auto` हमेशा
|
||||
`2026-07-28` पर पहुँचता है। fallback सिर्फ़ असली pre-2026 server के सामने ही चलता है, और ठीक वहीं
|
||||
आप इसे चाहते भी हैं।
|
||||
|
||||
## `mode="legacy"` {#modelegacy}
|
||||
|
||||
```python title="client.py" hl_lines="14"
|
||||
```python title="client.py" hl_lines="7"
|
||||
--8<-- "docs_src/protocol_versions/tutorial002.py"
|
||||
```
|
||||
|
||||
|
|
@ -57,11 +66,11 @@ server-initiated request का मतलब है server का **आपको
|
|||
|
||||
`mode="auto"` handshake तभी देता है जब server इतना पुराना हो कि और कुछ चले ही नहीं। `mode="legacy"` इसकी गारंटी देता है। जब भी आप `Client(...)` को `sampling_callback`, request के रूप में चलाया जाने वाला `elicitation_callback`, या `message_handler` देते हैं, इसे चुनें। **[Client callbacks](client/callbacks.md)** में हर एक की बात विस्तार से है।
|
||||
|
||||
## Version pin करना {#pinning-a-version}
|
||||
## version pin करना {#pinning-a-version}
|
||||
|
||||
`mode` modern protocol version string भी स्वीकार करता है। आज यह set ठीक `["2026-07-28"]` है।
|
||||
|
||||
```python title="client.py" hl_lines="14"
|
||||
```python title="client.py" hl_lines="7"
|
||||
--8<-- "docs_src/protocol_versions/tutorial003.py"
|
||||
```
|
||||
|
||||
|
|
@ -94,7 +103,7 @@ probe सस्ता है, लेकिन फिर भी यह एक rou
|
|||
|
||||
इसलिए इसे संभाल कर रखें। `auto` connection के बाद `client.session.discover_result` में ठीक वही `DiscoverResult` होता है जो server ने भेजा था: उसके `supported_versions`, उसकी `capabilities`, उसके `instructions`, और वह पहचान जो server ने result के `_meta` में दर्ज की थी। अगली बार इसे `prior_discover=` के रूप में वापस दें:
|
||||
|
||||
```python title="client.py" hl_lines="15 17"
|
||||
```python title="client.py" hl_lines="8 10"
|
||||
--8<-- "docs_src/protocol_versions/tutorial004.py"
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [1062ef792791488a, 4be2b831547184a9, 374b049e770385f2, b72f6947089e6de0, b172c9db7831bb31, 70b9ece244ca1b0c, cba78e052898c3f6, f06bdb541cb0b469, fb82d526320b7cc3]
|
||||
sections: [1062ef792791488a, 4be2b831547184a9, 374b049e770385f2, b72f6947089e6de0, b172c9db7831bb31, 10394c6f16601638, cba78e052898c3f6, f06bdb541cb0b469, 6dc898ccc5a903f9]
|
||||
tool: 1
|
||||
---
|
||||
# मौजूदा app में जोड़ना {#add-to-an-existing-app}
|
||||
|
|
@ -99,7 +99,7 @@ Starlette का `Host` route इसी तरह काम करता है:
|
|||
--8<-- "docs_src/asgi/tutorial004.py"
|
||||
```
|
||||
|
||||
अब clients `/notes` से जुड़ते हैं, `/notes/mcp` से नहीं।
|
||||
अब clients `/notes/` से जुड़ते हैं, `/notes/mcp` से नहीं।
|
||||
|
||||
## browser clients के लिए CORS {#cors-for-browser-clients}
|
||||
|
||||
|
|
@ -142,4 +142,4 @@ browser-based client को आपसे दो अनुमतियाँ च
|
|||
* browser clients को CORS चाहिए: `Mcp-*` request headers के लिए `allow_headers`, response के लिए `expose_headers=["Mcp-Session-Id"]`।
|
||||
* `@mcp.custom_route()` `/mcp` के बगल में सादे, बिना authentication वाले HTTP endpoints जोड़ता है।
|
||||
|
||||
जब server असली URL पर पहुँच में आ जाए, तो **[Client](../client/index.md)** server object के बजाय उसी URL से उससे जुड़ता है।
|
||||
जब server असली URL पर पहुँच में आ जाए, तो **[Client](../client/index.md)** उसी URL से उससे जुड़ता है।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [d62c13457fc4a534, 80e73abaca6e0652, d1dc4c54cd00ec9c, 14ad3bc7904036bb, 5225f127bc1b9c77, fe1626fdd5aad1da, 4556cb7ea1a04a31]
|
||||
sections: [d62c13457fc4a534, 80e73abaca6e0652, 128a492d18295f64, 14ad3bc7904036bb, 54a6697833fcc1d9, fe1626fdd5aad1da, 811d083c1da8bcf6]
|
||||
tool: 1
|
||||
---
|
||||
# Authorization {#authorization}
|
||||
|
|
@ -23,12 +23,12 @@ OAuth की भाषा में, आपका server **resource server** ह
|
|||
|
||||
valid token कैसा दिखता है, इस बारे में SDK की कोई राय नहीं है। यह आप बताते हैं, **`TokenVerifier`** implement करके:
|
||||
|
||||
```python title="server.py" hl_lines="12-14 19-24"
|
||||
```python title="server.py" hl_lines="14-16 21-27"
|
||||
--8<-- "docs_src/authorization/tutorial001.py"
|
||||
```
|
||||
|
||||
* `TokenVerifier` एक async method वाला protocol है। `verify_token` को `Authorization` header से raw token मिलता है, और token valid हो तो यह **`AccessToken`** लौटाता है, न हो तो `None`। इसके अलावा implement करने को कुछ नहीं है।
|
||||
* यह वाला token को एक table में ढूँढता है। असली verifier JWT signature verify करता है या authorization server के token-introspection endpoint को call करता है। वह code आपका है; SDK उसे सिर्फ़ call करता है।
|
||||
* यह वाला token को एक table में ढूँढता है; हर entry में दर्ज है कि token किस resource के लिए जारी हुआ था। असली verifier JWT signature verify करता है या authorization server के token-introspection endpoint को call करता है, और `AccessToken.resource` में बताता है कि token किसके लिए जारी हुआ था (उसका `aud`)। वह code आपका है; SDK उसे सिर्फ़ call करता है।
|
||||
* `token_verifier=` और `auth=` हमेशा साथ चलते हैं। एक को दूसरे के बिना pass करें तो `MCPServer(...)` कोई request serve करने से पहले ही `ValueError` raise कर देता है।
|
||||
|
||||
`AuthSettings` आपके resource server का सार्वजनिक चेहरा है:
|
||||
|
|
@ -36,6 +36,10 @@ valid token कैसा दिखता है, इस बारे में S
|
|||
* `issuer_url`: वह authorization server जो आपके tokens जारी करता है।
|
||||
* `resource_server_url`: इस MCP endpoint का public URL। यह बताता है कि token **किस** resource के लिए है, और discovery document भी यहीं रहता है।
|
||||
* `required_scopes`: हर token में ये सभी होने ही चाहिए।
|
||||
* `validate_token_resource`: हर उस token को मना कर देता है जिसका `AccessToken.resource` `resource_server_url` नहीं है। `resource_server_url` set हो और इसे unset छोड़ दें तो warning (`MCPDeprecationWarning`) आती है और यह `False` की तरह बर्ताव करता है; 3.0 में resource servers के लिए default `True` हो जाएगा।
|
||||
* इसे तब चालू करें जब आपका authorization server tokens को उस `resource` से बाँधता हो जो client ने माँगा था, और जिसे MCP clients हमेशा भेजते हैं। `resource_server_url` ठीक वही URL रखें जिससे clients जुड़ते हैं।
|
||||
* इसे तब बंद रहने दें जब आपका authorization server अपने खुद के audience identifiers इस्तेमाल करता हो (Auth0 API identifier, Entra application ID), और इसकी जगह अपने verifier में `aud` जाँचें, और जो token इस server के लिए नहीं है उसके लिए `None` लौटाएँ।
|
||||
* अगर `aud` एक list है, तो जो entry `resource_server_url` के बराबर है उसे `resource` में रखें।
|
||||
|
||||
!!! tip
|
||||
SDK repository में `examples/servers/simple-auth/` के अंदर एक `IntrospectionTokenVerifier` है जो
|
||||
|
|
@ -91,7 +95,7 @@ authorization HTTP headers में रहता है, इसलिए यह
|
|||
|
||||
किसी भी handler के अंदर, **`get_access_token()`** वही `AccessToken` है जो आपके verifier ने मौजूदा request के लिए लौटाया था:
|
||||
|
||||
```python title="server.py" hl_lines="4 32-35"
|
||||
```python title="server.py" hl_lines="4 35-38"
|
||||
--8<-- "docs_src/authorization/tutorial002.py"
|
||||
```
|
||||
|
||||
|
|
@ -125,6 +129,6 @@ authorization server, user के consent screen पर click करने क
|
|||
* `token_verifier=` और `auth=AuthSettings(issuer_url=..., resource_server_url=..., required_scopes=[...])` हमेशा साथ चलते हैं।
|
||||
* SDK `/.well-known/oauth-protected-resource/...` पर [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) Protected Resource Metadata publish करता है और unauthenticated requests का जवाब 401 से देता है, जिसका `WWW-Authenticate` header उसी की ओर इशारा करता है। discovery की पूरी कहानी बस इतनी ही है।
|
||||
* किसी भी handler में `get_access_token()` बताता है कि call कौन कर रहा है।
|
||||
* authorization HTTP का मामला है। `stdio` और in-memory client इसे कभी नहीं देखते।
|
||||
* authorization HTTP का मामला है। `stdio` और in-memory test client इसे कभी नहीं देखते।
|
||||
|
||||
client वाला आधा हिस्सा (आपके authorization server को खोजना और आपके लिए token लाना) **[OAuth clients](../client/oauth-clients.md)** में है। और जो client user से पहचान पूछने के बजाय खुद कोई पहचान **assert** करता है, वह **[Identity assertion](../client/identity-assertion.md)** में है।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [28221886b198784f, f88ea1f1614f3a1d, ce926d686730b6d0, 3be24f8ad8bb5ab9, 3fad24032b2224ff, f25a7f860e579ecb, e758745df6fb7b0a]
|
||||
sections: [28221886b198784f, f88ea1f1614f3a1d, 2e76f5cb9df15042, ce926d686730b6d0, 3be24f8ad8bb5ab9, 3fad24032b2224ff, f25a7f860e579ecb, 697b01d95080880d]
|
||||
tool: 1
|
||||
---
|
||||
# Deploy और scale करना {#deploy-scale}
|
||||
|
|
@ -47,6 +47,22 @@ translation:
|
|||
deploy हुआ server जो हर connection ठुकरा रहा हो, उसे Host allowlist की समस्या ही मानें जब तक कुछ और साबित न हो।
|
||||
**[Troubleshooting](../troubleshooting.md)** भी यहीं से शुरू होता है।
|
||||
|
||||
## TLS-terminating proxy के पीछे {#behind-a-tls-terminating-proxy}
|
||||
|
||||
अगर TLS किसी proxy पर ख़त्म होता है (कोई ingress, load balancer, Caddy, nginx) और uvicorn उसके पीछे plain HTTP serve करता है, तो uvicorn को बताएँ कि वह proxy के `X-Forwarded-*` headers पर भरोसा करे:
|
||||
|
||||
```console
|
||||
uvicorn server:app --proxy-headers --forwarded-allow-ips='<proxy address>'
|
||||
```
|
||||
|
||||
इसके बिना app यह मान लेता है कि उसे `http://` पर serve किया जा रहा है, और वह जो भी redirect भेजता है (आम तौर पर `/mcp` → `/mcp/`) वह `http://…` की ओर इशारा करता है। Python client किसी HTTPS endpoint से plain HTTP पर जाने से इनकार कर देता है और साफ़ बताता है:
|
||||
|
||||
```text
|
||||
MCPError: Redirect to http://mcp.example.com/mcp/ not followed: it would downgrade this HTTPS endpoint to plain HTTP.
|
||||
```
|
||||
|
||||
client की तरफ़ का कामचलाऊ उपाय है ठीक वही URL configure करना जो server serve करता है (`https://mcp.example.com/mcp/`, slash सहित), ताकि कोई redirect हो ही नहीं। असली इलाज ऊपर वाला flag है। `FORWARDED_ALLOW_IPS` इसी का environment-variable रूप है; `*` हर hop पर भरोसा करता है, जो सिर्फ़ तभी सही है जब proxy के अलावा कोई uvicorn तक न पहुँच सके।
|
||||
|
||||
## Workers, और sticky किसे होना है {#workers-and-who-has-to-be-sticky}
|
||||
|
||||
जब hostname जवाब देने लगे, तो उसके पीछे एक से ज़्यादा worker लगाएँ। इसके लिए SDK में कोई knob नहीं है; Starlette app को वैसे ही scale किया जाता है जैसे किसी भी ASGI app को, object किसी ऐसी चीज़ को सौंपकर जो fork करना जानती है:
|
||||
|
|
@ -170,6 +186,7 @@ fan-out को इससे कोई मतलब नहीं कि stream
|
|||
## सारांश {#recap}
|
||||
|
||||
* बिना कुछ configure किए app सिर्फ़ उन्हीं requests का जवाब देता है जो localhost को भेजी गई हों। `transport_security=TransportSecuritySettings(allowed_hosts=[...], allowed_origins=[...])` ही go-live gate है: जब तक आप इसे नहीं देते, असली hostname के पीछे हर request `421` है और कारण सिर्फ़ server के log में है।
|
||||
* TLS-terminating proxy के पीछे uvicorn को `--proxy-headers --forwarded-allow-ips=...` के साथ चलाएँ, वरना उसके redirects `http://` की ओर इशारा करते हैं और client उन्हें ठुकरा देता है।
|
||||
* 2026-07-28 पर कोई session नहीं है और load balancer के sticky होने के लिए कुछ नहीं। `stateless_http=True` सिर्फ़ legacy का knob है क्योंकि modern request उस flag के पढ़े जाने से पहले ही route होकर जवाब पा लेती है।
|
||||
* default `requestState` key `os.urandom(32)` है, हर process में अलग बनी हुई। कोई multi-round-trip retry जो दूसरे worker तक पहुँचे, `-32602` *"Invalid or expired requestState"* के साथ fail होती है।
|
||||
* इलाज है `RequestStateSecurity(keys=[...])` **और** हर instance पर एक ही server नाम। नाम ही token का default audience claim है। वही keys, वही नाम।
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [fea8d769ff9edeba, ce8e2ad42f29ef71, 0d705efb19cf99c2, 2fd7cf825e6d2b2c, 9adc400e8c88e854, 318893ad8e2e9924, 6b63ab96b34476c0]
|
||||
sections: [fea8d769ff9edeba, ce8e2ad42f29ef71, 0d705efb19cf99c2, 9fd2357154a5b7e7, 9adc400e8c88e854, 318893ad8e2e9924, 6b63ab96b34476c0]
|
||||
tool: 1
|
||||
---
|
||||
# अपना server चलाना {#running-your-server}
|
||||
|
|
@ -64,21 +64,26 @@ Inspector ठीक वही करता है जो असली host क
|
|||
--8<-- "docs_src/run/tutorial002.py"
|
||||
```
|
||||
|
||||
वह एक line Starlette app बनाती है और उसे uvicorn से serve करती है। Clients `http://127.0.0.1:3001/mcp` से जुड़ते हैं।
|
||||
वह एक line Starlette app बनाती है और उसे uvicorn से serve करती है। clients `http://127.0.0.1:3001/mcp` से जुड़ते हैं।
|
||||
|
||||
हर transport के अपने keyword arguments हैं, सब `run()` पर:
|
||||
|
||||
* `host` / `port`: कहाँ सुनना है। Default `127.0.0.1` और `8000`।
|
||||
* `streamable_http_path`: MCP endpoint कहाँ रहता है। Default `/mcp`।
|
||||
* `host` / `port`: कहाँ सुनना है। default `127.0.0.1` और `8000`।
|
||||
* `streamable_http_path`: MCP endpoint कहाँ रहता है। default `/mcp`।
|
||||
* `json_response=True`: हर POST का जवाब SSE stream के बजाय एक अकेली JSON body से देना। उस body में सिर्फ़ response की जगह है, और कुछ नहीं, इसलिए जो tool request के बीच में client को वापस call करता है (`ctx.elicit()`, sampling), वह इस leg पर `NoBackChannelError` raise करता है, और चल रही call से जुड़े notifications (`ctx.report_progress()` का progress, per-call log messages) छोड़ दिए जाते हैं; standalone `GET` stream असंबंधित notifications अब भी ले जाती है।
|
||||
* `stateless_http=True`: हर request के लिए नया transport, कोई session tracking नहीं।
|
||||
* `max_request_body_size`: स्वीकार की जाने वाली सबसे बड़ी request body, bytes में। Default 4 MiB है; इससे बड़ी requests
|
||||
* `max_request_body_size`: स्वीकार की जाने वाली सबसे बड़ी request body, bytes में। default 4 MiB है; इससे बड़ी requests
|
||||
को parsing या session बनने से पहले ही HTTP 413 मिलता है। इसे तभी बढ़ाएँ जब जायज़ MCP messages
|
||||
उस आकार से बड़े हों।
|
||||
* `session_idle_timeout`: कोई legacy session कितने seconds तक बिना किसी चल रही request के खाली रह सकता है,
|
||||
इससे पहले कि server उसे बंद कर दे। default 1800। `None` इसे बंद कर देता है। देखें
|
||||
[session का जीवनकाल और सीमाएँ](legacy-clients.md#session-lifetime-and-limits)।
|
||||
* `max_sessions`: एक process एक साथ कितने legacy sessions रखता है। default 10 000। `None`
|
||||
सीमा हटा देता है। इसकी जानकारी भी उसी section में है।
|
||||
* `event_store`, `retry_interval`, `transport_security`: resumability और DNS-rebinding से सुरक्षा। ये इंतज़ार कर सकते हैं, जब तक आप localhost के अलावा कहीं deploy न करें; `transport_security` की जानकारी **[Deploy & scale](deploy.md)** में है।
|
||||
|
||||
!!! warning
|
||||
Transport options `run()` को जाते हैं, `MCPServer(...)` को **नहीं**। Constructor बताता है कि
|
||||
transport options `run()` को जाते हैं, `MCPServer(...)` को **नहीं**। constructor बताता है कि
|
||||
आपका server **क्या है**: name, version, instructions। `run()` बताता है कि वह कैसे serve होता है। इसे
|
||||
उल्टा करेंगे तो MCP के शामिल होने से पहले ही Python जवाब दे देता है:
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue