docs: refresh translations, and translate pages in parallel (#3458)
This commit is contained in:
parent
08a3bc8eaf
commit
fd66270926
5 changed files with 4821 additions and 2277 deletions
|
|
@ -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 जवाब दे देता है:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
translation:
|
||||
sections: [3d1663c18edc824c, d4fd37009a13f03d, af9f398a5a8b679a, 470c2dd144294d69, 8e45827e6d24e8c8, 91dfd0ce98ebb03c]
|
||||
sections: [3d1663c18edc824c, 90956965ae6a1ca1, af9f398a5a8b679a, 5ce83b1f9d88da62, 0d9b5d13fffc94e5, 8e45827e6d24e8c8, 91dfd0ce98ebb03c]
|
||||
tool: 1
|
||||
---
|
||||
# legacy clients को serve करना {#serving-legacy-clients}
|
||||
|
|
@ -21,15 +21,25 @@ SDK हर request को उसके `MCP-Protocol-Version` header के ह
|
|||
|
||||
## एक handler, दोनों पीढ़ियाँ {#one-handler-both-eras}
|
||||
|
||||
यह रहा एक tool जिसे user से कुछ पूछना है, और दोनों पीढ़ियों के client जो उसे call कर रहे हैं:
|
||||
यह रहा एक tool जिसे user से कुछ पूछना है:
|
||||
|
||||
```python title="server.py" hl_lines="24 37-38"
|
||||
```python title="server.py" hl_lines="21"
|
||||
--8<-- "docs_src/legacy_clients/tutorial001.py"
|
||||
```
|
||||
|
||||
`reserve` को एक चीज़ चाहिए जो model ने नहीं दी: कितनी copies। tool यह बात `Annotated[..., Resolve(ask_quantity)]` से declare करता है (पूरी जानकारी **[Dependencies](../handlers/dependencies.md)** में है)। `reserve` में कहीं भी न किसी version का नाम है, न capability की जाँच, न कोई branch।
|
||||
|
||||
दोनों clients **एक ही समय पर** खुले हैं, उसी `mcp` object पर। `mode="legacy"` `initialize` handshake चलाता है: ठीक वही connection जो 2026 से पहले का client खोलता है। दूसरा client default लेता है और `2026-07-28` पर पहुँचता है।
|
||||
इसे HTTP पर serve करें, और ये रहे दोनों पीढ़ियों के client जो इसे call कर रहे हैं:
|
||||
|
||||
```console
|
||||
uv run mcp run server.py --transport streamable-http
|
||||
```
|
||||
|
||||
```python title="client.py" hl_lines="14-15"
|
||||
--8<-- "docs_src/legacy_clients/tutorial001_client.py"
|
||||
```
|
||||
|
||||
दोनों clients **एक ही समय पर** खुले हैं, उसी चल रहे server पर। `mode="legacy"` `initialize` handshake चलाता है: ठीक वही connection जो 2026 से पहले का client खोलता है। दूसरा client default लेता है और `2026-07-28` पर पहुँचता है। दूसरे terminal से `python client.py` चलाएँ:
|
||||
|
||||
```text
|
||||
2025-11-25 {'result': "Reserved 2 of 'Dune'."}
|
||||
|
|
@ -61,6 +71,40 @@ routing मुफ़्त है। session नहीं।
|
|||
client को छूटे हुए SSE events फिर से भेजना), session store नहीं। यह कभी किसी session को
|
||||
दूसरे process से पहुँच लायक नहीं बनाता।
|
||||
|
||||
## session की उम्र और सीमाएँ {#session-lifetime-and-limits}
|
||||
|
||||
legacy session हमेशा के लिए नहीं जीता, और एक process अनगिनत sessions नहीं रखता।
|
||||
इसे दो settings नियंत्रित करती हैं। दोनों `run()`, `streamable_http_app()`
|
||||
और `Server.streamable_http_app()` पर keyword arguments हैं। modern (`2026-07-28`) connections और `stateless_http=True`
|
||||
में sessions होते ही नहीं, इसलिए दोनों में से कोई setting उन पर लागू नहीं होती।
|
||||
|
||||
| Setting | Default | यह क्या करती है | client को क्या दिखता है | बंद करने के लिए |
|
||||
|---|---|---|---|---|
|
||||
| `session_idle_timeout` | `1800` (30 मिनट) | ऐसे session को बंद कर देती है जिसमें इतनी देर से कुछ in flight न रहा हो। | `404 Session not found`। उसे फिर से `initialize` करना पड़ता है। | `None` |
|
||||
| `max_sessions` | `10_000` | इतने से ज़्यादा होने पर नया session खोलने से मना कर देती है। मौजूदा sessions अछूते रहते हैं और कुछ भी evict नहीं होता। | `503 Too many open sessions`, JSON-RPC code `-32603` के साथ। | `None` |
|
||||
|
||||
"in flight" किसे माना जाता है:
|
||||
|
||||
* खुला `GET` stream। SDK के clients एक खुला रखते हैं, इसलिए जुड़े हुए client का session कभी
|
||||
expire नहीं होता।
|
||||
* ऐसी request जिसका जवाब अभी दिया जा रहा है। timeout से ज़्यादा देर चलने वाला tool call बीच में
|
||||
नहीं रोका जाता, और उलटी गिनती उसके खत्म होने पर ही शुरू होती है।
|
||||
* और कुछ नहीं। requests के बीच घड़ी चलती रहती है। session पर आई कोई भी request उसे फिर से शुरू कर देती है,
|
||||
`ping` भी। एक बार session expire हो जाए, तो उसे कुछ भी वापस नहीं लाता।
|
||||
|
||||
जो client अपना session `DELETE` से खत्म करता है, वह उसे तुरंत खाली कर देता है। जिस client की
|
||||
पहली request मना कर दी गई हो, उसके साथ भी यही होता है।
|
||||
|
||||
```python
|
||||
mcp.run(transport="streamable-http", session_idle_timeout=None, max_sessions=50_000)
|
||||
```
|
||||
|
||||
दोनों घटनाएँ server log में दिखती हैं। expiry `INFO` पर `Session <id> idle timeout` के रूप में दिखती है।
|
||||
मना किया गया open `WARNING` पर `Refusing to open a new session: <n> sessions are already open` के रूप में।
|
||||
|
||||
सीमाएँ हर process की अपनी हैं। चार workers के साथ ऊपरी सीमा `max_sessions` की चार गुना है, और हर
|
||||
worker अपने sessions खुद expire करता है।
|
||||
|
||||
## इकलौता switch: `stateless_http` {#the-one-knob-stateless_http}
|
||||
|
||||
अगर stickiness ऐसी कीमत है जो आप चुकाना नहीं चाहते, तो ठीक एक चीज़ है जो आप बदल सकते हैं।
|
||||
|
|
@ -86,7 +130,7 @@ routing मुफ़्त है। session नहीं।
|
|||
|
||||
!!! check
|
||||
जानबूझकर गलत काम करें। `reserve` ठीक वही tool है जिसने अभी दोनों clients को serve किया। इसे
|
||||
`stateless_http=True` के साथ deploy करें, वही दो clients HTTP पर जोड़ें, और हर एक से इसे call करें।
|
||||
`stateless_http=True` के साथ deploy करें, वही दो clients जोड़ें, और हर एक से इसे call करें।
|
||||
|
||||
modern client को अब भी `Reserved 2 of 'Dune'.` मिलता है। modern हिस्सा नहीं बदला।
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue