docs: refresh translations, and translate pages in parallel (#3458)

This commit is contained in:
Max 2026-09-07 13:35:53 +01:00 • committed by GitHub
commit fd66270926
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 4821 additions and 2277 deletions

View file

@ -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 @@ Le SDK fournit cela sous la forme de l’extension intégrée `Apps` (`io.modelc
## Une horloge avec un cadran {#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"
```
@ -31,7 +31,7 @@ Quatre étapes :
Le HTML lui-même écoute le `postMessage` de l’hôte et affiche le résultat. Pour de vraies applications, utilisez dans votre HTML le SDK navigateur officiel [`@modelcontextprotocol/ext-apps`](https://github.com/modelcontextprotocol/ext-apps). Il vous donne `ontoolresult`, `callServerTool`, `getHostContext` et `onhostcontextchanged` au lieu d’événements de message bruts.
## Dégradation gracieuse {#graceful-degradation}
## Dégradation élégante {#graceful-degradation}
Tous les clients n’affichent pas les apps. La spécification dit sans détour ce que cela implique pour vous :
@ -39,11 +39,31 @@ Tous les clients n’affichent pas les apps. La spécification dit sans détour
Le modèle lit `content` ; l’iframe est pour les humains. Un hôte capable d’afficher une interface transmet quand même le résultat textuel au modèle, et un client purement textuel ne reçoit *que* cela. Le schéma canonique est donc : un outil, deux réponses. Regardez à nouveau `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)` ne vaut `True` que lorsque le client a déclaré l’extension `io.modelcontextprotocol/ui` **et** listé `text/html;profile=mcp-app` dans ses paramètres `mimeTypes`. Le champ est obligatoire, donc un client qui l’omet ne compte pas. C’est exactement ce que déclare `main()` dans le même fichier : la moitié client de la négociation, et la réponse riche revient.
`client_supports_apps(ctx)` ne vaut `True` que lorsque le client a déclaré l’extension `io.modelcontextprotocol/ui` **et** listé `text/html;profile=mcp-app` dans ses paramètres `mimeTypes`. Le champ est obligatoire, donc un client qui l’omet ne compte pas. Voici la moitié client de la négociation :
```python title="client.py" hl_lines="8 12"
--8<-- "docs_src/apps/tutorial001_client.py"
```
Servez `server.py` en HTTP, puis lancez le client depuis un second terminal :
```console
uv run mcp run server.py --transport streamable-http
```
```console
python client.py
```
```text
2026-06-26T12:00:00Z
```
La réponse riche est revenue. Retirez `extensions=[APPS_SUPPORT]` de l’appel à `Client` et le même programme affiche `The time is 2026-06-26T12:00:00Z.` à la place, c’est-à-dire tout ce qu’un client purement textuel voit jamais.
!!! warning
Ne renvoyez jamais un texte de substitution comme `"[Rendered UI]"` pour seul contenu. Si le texte de repli est inutile, l’outil est inutile pour tout client purement textuel et pour le modèle lui-même. Écrivez la phrase.

View file

@ -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 @@ Utilisez comme préfixe un domaine que vous contrôlez. `io.modelcontextprotocol
La plus petite extension utile, c’est un outil et une table de paramètres :
```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 @@ La plus petite extension utile, c’est un outil et une table de paramètres :
* `settings()` est la valeur annoncée sous `capabilities.extensions["com.example/stamps"]`. Renvoyez `{}` (la valeur par défaut) pour annoncer l’extension sans paramètres.
* L’extension ne reçoit jamais le serveur. Elle déclare ses contributions sous forme de données ; `MCPServer` les consomme. Il n’y a pas de `self.server` à modifier.
Et `main()` en est la preuve, un client en mémoire branché directement sur `mcp` :
Servez-la en HTTP, et un client en est la preuve :
```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"
```
Chaque `server.py` de cette page est servi avec cette commande, et chaque `client.py` tourne à côté, lancé avec `python client.py` depuis un second terminal.
### Servir vos propres méthodes {#serving-your-own-methods}
Une extension peut enregistrer de **nouvelles méthodes de requête** : ses propres verbes, servis à côté de ceux de la spécification :
```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 @@ Les méthodes sont **strictement additives**. Le SDK le fait respecter à la con
### Le côté client {#the-client-side}
Le `main()` du même fichier raconte toute l’histoire côté client, ses deux moitiés :
Le client est un programme à part entière, et il porte les deux moitiés de l’histoire côté 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)])` déclare l’extension. Les déclarations deviennent `ClientCapabilities.extensions` : sur une connexion 2026-07-28, la table voyage dans l’enveloppe `_meta` de chaque requête, donc le serveur la voit sur **chaque** requête ; sur une connexion historique, elle transite par la poignée de main `initialize`. Le code serveur ne s’en soucie pas : `require_client_extension(ctx, ...)` et `ctx.session.check_client_capability(...)` lisent la bonne source dans les deux cas.
* Les méthodes propres à un éditeur descendent d’un niveau, vers `client.session.send_request(...)` ; `Client` n’acquiert de méthodes de premier rang que pour les verbes de la spécification. `send_request` accepte n’importe quelle sous-classe de `Request`, donc la requête de l’éditeur passe telle quelle.
* `SearchRequest` et les deux modèles qu’elle transporte constituent le contrat de liaison de l’extension, donc le client les déclare de son côté. Une extension publiée les fournirait dans un paquet que les deux côtés importent.
### Intercepter `tools/call` {#intercepting-toolscall}
@ -109,12 +116,18 @@ Le hook enveloppe `tools/call` et rien d’autre. Pour ce qui concerne chaque me
## Utiliser une extension client {#using-a-client-extension}
Une **extension client**, c’est le même contrat vu du côté consommateur : un ensemble de comportements côté client derrière un seul identifiant. Passez des instances à `Client(extensions=[...])` et appelez les outils normalement :
Une **extension client**, c’est le même contrat vu du côté consommateur : un ensemble de comportements côté client derrière un seul identifiant. Ici, le serveur répond à `buy` par un reçu à échanger au lieu de la marchandise, et seulement pour un client qui a déclaré l’extension :
```python title="client.py" hl_lines="66-68"
```python title="server.py" hl_lines="22-25"
--8<-- "docs_src/extensions/tutorial006.py"
```
Côté client, passez des instances à `Client(extensions=[...])` et appelez les outils normalement :
```python title="client.py" hl_lines="33-35"
--8<-- "docs_src/extensions/tutorial006_client.py"
```
`call_tool("buy", ...)` renvoie un simple `CallToolResult`, comme tout autre appel. Ce que l’extension a changé : le serveur peut désormais répondre à `buy` par une **forme de résultat** `receipt` au lieu d’un résultat final, et `Receipts` la termine (ici en échangeant le reçu via un appel de suivi) avant que `call_tool` ne renvoie. Rien ne bouge au point d’appel.
Retirez l’extension et rien de tout cela n’existe : le garde-fou du serveur refuse un client qui ne l’a pas déclarée (erreur -32021), et une forme revendiquée venant d’un serveur qui saute le garde-fou échoue à la validation, exactement comme la spécification l’exige pour un `resultType` non reconnu. Désactivé par défaut, aux deux bouts de la liaison.
@ -124,15 +137,15 @@ Pour annoncer un identifiant **sans aucun** comportement côté client (le serve
```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")])
```
## Écrire une extension client {#writing-a-client-extension}
Dérivez `ClientExtension` et ne redéfinissez que ce dont vous avez besoin. Trois types de contributions, chacun avec une valeur par défaut : `settings()`, `claims()` et `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"
```
* L’identifiant suit la même grammaire que celui du serveur, validé au moment où la classe est définie.
@ -153,12 +166,18 @@ Deux règles discrètes. Les claims ne sont actifs que sur les connexions 2026-0
### Verbes d’extension {#extension-verbs}
Les méthodes de requête propres à une extension n’ont besoin d’aucun enregistrement côté client. Un type de requête d’éditeur dérive de `mcp.types.Request` et passe par `client.session.send_request`, comme dans [Servir vos propres méthodes](#serving-your-own-methods). Un ajout : lorsqu’une clé des paramètres doit transiter par l’en-tête `Mcp-Name` (des spécifications d’extension comme tasks l’exigent pour leurs verbes), le type de requête déclare `name_param` :
Les méthodes de requête propres à une extension n’ont besoin d’aucun enregistrement côté client. Un type de requête d’éditeur dérive de `mcp.types.Request` et passe par `client.session.send_request`, comme dans [Servir vos propres méthodes](#serving-your-own-methods). Prenez un serveur dont l’extension sert un seul verbe portant sur un job nommé :
```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"
```
Un ajout côté client : lorsqu’une clé des paramètres doit transiter par l’en-tête `Mcp-Name` (des spécifications d’extension comme tasks l’exigent pour leurs verbes), le type de requête déclare `name_param` :
```python title="client.py" hl_lines="20-23 28-29"
--8<-- "docs_src/extensions/tutorial007_client.py"
```
La session reflète `params["jobId"]` dans `Mcp-Name` sur chaque chemin d’envoi, et une valeur manquante échoue bruyamment au lieu d’omettre silencieusement un en-tête obligatoire.
## Ce qu’une extension ne peut pas faire {#what-an-extension-cannot-do}

View file

@ -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
---
# Le Server de bas niveau {#the-low-level-server}
@ -36,18 +36,22 @@ Trois choses ont changé, et elles constituent toute l’API de bas niveau :
### Essayer {#try-it}
Pas d’Inspector pour celui-ci : `mcp dev` et `mcp run` n’acceptent qu’un `MCPServer`. Le `Client` en mémoire s’en moque ; il accepte un `Server` de bas niveau exactement comme il accepte un `MCPServer` :
`mcp dev` et `mcp run` n’acceptent qu’un `MCPServer`, vous servez donc celui-ci vous-même. La dernière ligne de `server.py` construit à partir de lui une application ASGI ordinaire, et uvicorn l’exécute :
```python title="main.py"
```console
uvicorn server:app --port 8000
```
Pointez l’Inspector, ou n’importe quel client, vers `http://localhost:8000/mcp` :
```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)
@ -64,6 +68,8 @@ Le même texte que celui produit par la version `@mcp.tool()`. Deux différences
* `result.structured_content` vaut `None`. Le serveur de haut niveau enveloppe pour vous un `-> str` dans `{"result": ...}` ; ici, personne ne construit ce que vous n’avez pas construit.
* `list_tools` renvoie le schéma que **vous** avez saisi, caractère pour caractère. La version de haut niveau avait `"title": "Query"` sur chaque propriété et un `"title": "search_booksArguments"` à la racine : des artefacts de Pydantic. À ce niveau, si quelque chose est sur la liaison, c’est vous qui l’y avez mis.
Dans un test, vous vous passez d’uvicorn et du port : `Client(server)` accepte un `Server` de bas niveau dans le même processus exactement comme il accepte un `MCPServer`, et **[Tests](../get-started/testing.md)** décrit précisément ce schéma.
## Rien n’est vérifié pour vous {#nothing-is-checked-for-you}
`MCPServer` rejette un mauvais argument avant même que votre fonction s’exécute, en validant l’appel par rapport au schéma qu’il a généré (**[Outils](../servers/tools.md)**).
@ -215,4 +221,4 @@ Chacun d’eux correspond à une idée pour laquelle vous avez désormais le voc
* `add_request_handler(method, params_type, handler)` sert n’importe quelle méthode. `initialize` est réservée.
* Les capacités qu’annonce un `Server` découlent des gestionnaires que vous avez enregistrés.
`Client(server)` a traité les deux serveurs de façon identique parce qu’ils *sont* le même protocole, et c’est tout l’intérêt. La couche suivante vers le bas n’est pas une classe du tout : c’est le **[Middleware](middleware.md)**.
Le client a traité les deux serveurs de façon identique parce qu’ils *sont* le même protocole, et c’est tout l’intérêt. La couche suivante vers le bas n’est pas une classe du tout : c’est le **[Middleware](middleware.md)**.

View file

@ -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 @@ La pagination sert au serveur dont la liste de ressources est en réalité une b
### Essayer {#try-it}
`Client(server)` se connecte à un `Server` de bas niveau en mémoire exactement comme il se connecte à un `MCPServer`.
`mcp run` n’accepte qu’un `MCPServer`, vous servez donc celui-ci vous-même. La dernière ligne de `server.py` construit une application ASGI ordinaire à partir du `Server`, et uvicorn l’exécute :
Appelez `list_resources()` sans argument. Vous obtenez dix ressources, de `book-1` à `book-10`, et `next_cursor` vaut la chaîne `"10"`.
```console
uvicorn server:app --port 8000
```
Pointez n’importe quel client (**[Le client](../client/index.md)**, ou l’Inspector) vers `http://localhost:8000/mcp` et appelez `list_resources()` sans argument. Vous obtenez dix ressources, de `book-1` à `book-10`, et `next_cursor` vaut la chaîne `"10"`.
Renvoyez-la avec `list_resources(cursor="10")` : la première ressource est `book-11`, le nouveau `next_cursor` vaut `"20"`.
@ -43,7 +47,7 @@ La dixième page revient avec `next_cursor` à `None`. Terminé.
Chaque méthode `list_*` de `Client` (`list_tools`, `list_resources`, `list_resource_templates`, `list_prompts`) accepte un argument nommé `cursor=`. Vider une liste paginée tient en un `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 @@ Chaque méthode `list_*` de `Client` (`list_tools`, `list_resources`, `list_reso
* Étendez la liste **avant** de regarder `next_cursor` : la dernière page contient elle aussi des ressources.
* `next_cursor is None` est la sortie. Toute autre valeur repart directement dans `cursor=`, telle quelle.
Lancez son `main()` et il affiche `100 resources` : dix pages de dix, assemblées par une boucle qui n’a jamais su qu’il y avait dix pages.
Pendant qu’uvicorn sert toujours `server.py`, lancez `python client.py` dans un second terminal. Il affiche `100 resources` : dix pages de dix, assemblées par une boucle qui n’a jamais su qu’il y avait dix pages.
C’est la même boucle que montre **[Le client](../client/index.md)** pour chaque verbe `list_*`, et elle ne coûte rien face à un serveur qui ne pagine pas : `next_cursor` vaut `None` dès la première réponse et la boucle s’exécute une seule fois.