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}
@ -25,7 +25,7 @@ e depois volte aqui.
## Um relógio com uma cara {#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"
```
@ -56,15 +56,36 @@ O modelo lê `content`; o iframe é para humanos. Um host com suporte a UI ainda
o resultado em texto para o modelo, e um cliente só de texto recebe *apenas* isso. Então o
padrão canônico é uma ferramenta, duas respostas. Olhe `get_time` de novo:
```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` somente quando o cliente declarou a
extensão `io.modelcontextprotocol/ui` **e** listou `text/html;profile=mcp-app`
nas suas configurações `mimeTypes`. O campo é obrigatório, então um cliente que o omite
não conta. É exatamente isso que `main()` no mesmo arquivo declara: a
metade cliente da negociação, e a resposta rica volta.
não conta. Aqui está a metade cliente da negociação:
```python title="client.py" hl_lines="8 12"
--8<-- "docs_src/apps/tutorial001_client.py"
```
Sirva `server.py` por HTTP e depois execute o cliente em um segundo terminal:
```console
uv run mcp run server.py --transport streamable-http
```
```console
python client.py
```
```text
2026-06-26T12:00:00Z
```
A resposta rica voltou. Tire `extensions=[APPS_SUPPORT]` da chamada a `Client`
e o mesmo programa imprime `The time is 2026-06-26T12:00:00Z.` no lugar, que é
tudo o que um cliente só de texto chega a ver.
!!! warning
Nunca retorne um placeholder como `"[Rendered UI]"` como único conteúdo. Se o

View file

@ -1,6 +1,6 @@
---
translation:
sections: [05891e7cc1938a13, b3c01a6af28c51ee, 7ffc91f5e38bdfe0, 717d3f235a8333a7, f471a13b2fe5d737, ed6af2df4b656dff]
sections: [05891e7cc1938a13, b3c01a6af28c51ee, 6eba6ce094f4417e, 125f28588c85dd7b, ddd8969b698ca11c, ed6af2df4b656dff]
tool: 1
---
# Extensões {#extensions}
@ -49,7 +49,7 @@ Use como prefixo um domínio que você controla. `io.modelcontextprotocol/*` é
A menor extensão útil é uma ferramenta e um mapa de configurações:
```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 @@ A menor extensão útil é uma ferramenta e um mapa de configurações:
* `settings()` é o valor anunciado em `capabilities.extensions["com.example/stamps"]`. Retorne `{}` (o padrão) para anunciar a extensão sem configurações.
* A extensão nunca recebe o servidor. Ela declara contribuições como dados; o `MCPServer` as consome. Não existe um `self.server` para modificar.
E `main()` é a prova, um cliente em memória direto contra `mcp`:
Sirva-a por HTTP, e um cliente é a prova:
```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"
```
Todo `server.py` desta página é servido com esse comando, e todo `client.py` roda ao lado dele com `python client.py` em um segundo terminal.
### Servindo seus próprios métodos {#serving-your-own-methods}
Uma extensão pode registrar **novos métodos de requisição**: seus próprios verbos, servidos ao lado dos da especificação:
```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 @@ Os métodos são **estritamente aditivos**. O SDK impõe isso na construção, n
### O lado do cliente {#the-client-side}
O `main()` do mesmo arquivo é a história inteira do cliente, com as duas metades:
O cliente é um programa à parte, e carrega as duas metades da história do cliente:
```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)])` declara a extensão. As declarações viram `ClientCapabilities.extensions`: em uma conexão 2026-07-28 o mapa viaja no envelope `_meta` de cada requisição, então o servidor o vê em **toda** requisição; em uma conexão legada ele vai no handshake `initialize`. O código do servidor não se importa com qual: `require_client_extension(ctx, ...)` e `ctx.session.check_client_capability(...)` leem a fonte certa nos dois caminhos.
* Métodos de fornecedor descem uma camada para `client.session.send_request(...)`; `Client` só ganha métodos de primeira classe para verbos da especificação. `send_request` aceita qualquer subclasse de `Request`, então a requisição do fornecedor passa como está.
* `SearchRequest` e os dois modelos que ela carrega são o contrato de protocolo da extensão, então o cliente os declara por conta própria. Uma extensão publicada os distribuiria em um pacote que os dois lados importam.
### Interceptando `tools/call` {#intercepting-toolscall}
@ -109,12 +116,18 @@ O hook envolve `tools/call` e nada mais. Para preocupações que valem para toda
## Usando uma extensão de cliente {#using-a-client-extension}
Uma **extensão de cliente** é o mesmo contrato visto do lado consumidor: um pacote de comportamento do lado do cliente reunido sob um único identificador. Passe as instâncias para `Client(extensions=[...])` e chame as ferramentas normalmente:
Uma **extensão de cliente** é o mesmo contrato visto do lado consumidor: um pacote de comportamento do lado do cliente reunido sob um único identificador. Aqui o servidor responde a `buy` com um recibo a resgatar em vez da mercadoria, e só para um cliente que declarou a extensão:
```python title="client.py" hl_lines="66-68"
```python title="server.py" hl_lines="22-25"
--8<-- "docs_src/extensions/tutorial006.py"
```
No cliente, passe as instâncias para `Client(extensions=[...])` e chame as ferramentas normalmente:
```python title="client.py" hl_lines="33-35"
--8<-- "docs_src/extensions/tutorial006_client.py"
```
`call_tool("buy", ...)` retorna um `CallToolResult` comum, como toda outra chamada. O que a extensão mudou: o servidor agora pode responder a `buy` com um **formato de resultado** `receipt` em vez de um resultado final, e `Receipts` o finaliza (aqui, resgatando o recibo com uma chamada seguinte) antes de `call_tool` retornar. Nada muda no ponto da chamada.
Tire a extensão e nada disso existe: a barreira do servidor recusa um cliente que não a declarou (erro -32021), e um formato reivindicado vindo de um servidor que pula a barreira falha na validação, exatamente como a especificação exige para um `resultType` não reconhecido. Desligado por padrão, nas duas pontas da conexão.
@ -124,15 +137,15 @@ Para anunciar um identificador **sem** nenhum comportamento do lado do cliente (
```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")])
```
## Escrevendo uma extensão de cliente {#writing-a-client-extension}
Herde de `ClientExtension` e sobrescreva apenas o que precisar. Três tipos de contribuição, cada um com um padrão: `settings()`, `claims()` e `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"
```
* O identificador segue a mesma gramática do servidor, validada quando a classe é definida.
@ -153,12 +166,18 @@ Duas regras discretas. As claims ficam ativas apenas em conexões 2026-07-28, e
### Verbos de extensão {#extension-verbs}
Os métodos de requisição próprios de uma extensão não precisam de registro no lado do cliente. Um tipo de requisição de fornecedor herda de `mcp.types.Request` e passa por `client.session.send_request`, como em [Servindo seus próprios métodos](#serving-your-own-methods). Um acréscimo: quando uma chave de params precisa viajar no header `Mcp-Name` (especificações de extensão como tasks exigem isso para seus verbos), o tipo de requisição declara `name_param`:
Os métodos de requisição próprios de uma extensão não precisam de registro no lado do cliente. Um tipo de requisição de fornecedor herda de `mcp.types.Request` e passa por `client.session.send_request`, como em [Servindo seus próprios métodos](#serving-your-own-methods). Considere um servidor cuja extensão serve um único verbo sobre um job nomeado:
```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"
```
Um acréscimo no cliente: quando uma chave de params precisa viajar no header `Mcp-Name` (especificações de extensão como tasks exigem isso para seus verbos), o tipo de requisição declara `name_param`:
```python title="client.py" hl_lines="20-23 28-29"
--8<-- "docs_src/extensions/tutorial007_client.py"
```
A sessão espelha `params["jobId"]` em `Mcp-Name` em todo caminho de envio, e um valor ausente falha de forma explícita em vez de omitir silenciosamente um header obrigatório.
## O que uma extensão não pode fazer {#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
---
# O Server de baixo nível {#the-low-level-server}
@ -36,18 +36,22 @@ Três coisas mudaram, e elas são a API de baixo nível inteira:
### Experimente {#try-it}
Não existe Inspector para este aqui: `mcp dev` e `mcp run` só aceitam um `MCPServer`. O `Client` em memória não se importa; ele recebe um `Server` de baixo nível exatamente como recebe um `MCPServer`:
`mcp dev` e `mcp run` só aceitam um `MCPServer`, então este aqui você serve por conta própria. A última linha de `server.py` monta um app ASGI comum a partir dele, e o uvicorn o executa:
```python title="main.py"
```console
uvicorn server:app --port 8000
```
Aponte o Inspector, ou qualquer cliente, para `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 @@ O mesmo texto que a versão com `@mcp.tool()` produziu. Duas diferenças honesta
* `result.structured_content` é `None`. O servidor de alto nível encapsula um `-> str` em `{"result": ...}` para você; aqui ninguém monta o que você não montou.
* `list_tools` retorna o schema que **você** digitou, caractere por caractere. A versão de alto nível tinha `"title": "Query"` em cada propriedade e um `"title": "search_booksArguments"` na raiz: artefatos do Pydantic. Aqui embaixo, se está no fio, foi você quem colocou lá.
Em um teste você dispensa o uvicorn e a porta: `Client(server)` recebe um `Server` de baixo nível no mesmo processo exatamente como recebe um `MCPServer`, e **[Testes](../get-started/testing.md)** é esse padrão.
## Nada é verificado por você {#nothing-is-checked-for-you}
O `MCPServer` rejeita um argumento ruim antes mesmo de a sua função executar, validando a chamada contra o schema que ele gerou (**[Ferramentas](../servers/tools.md)**).
@ -208,11 +214,11 @@ Cada um destes é uma ideia para a qual você já tem o vocabulário; cada um te
## Recapitulando {#recap}
* O `Server` de baixo nível recebe os seus handlers como **parâmetros do construtor** `on_*`; todo handler é `async (ctx, params) -> result`.
* Você escreve o dict `input_schema` e você monta o `CallToolResult`. Nada é derivado, encapsulado ou validado por você.
* Você escreve o dict `input_schema` e você monta o `CallToolResult`. Nada é derivado, encapsulado ou validado para você.
* Uma exceção em um handler é um erro de protocolo `-32603`. Um erro de ferramenta que o modelo consegue ler é um `CallToolResult` com `is_error=True` que **você** retorna.
* O `_meta` no resultado é endereçado à aplicação cliente, não ao modelo.
* `Server[T]` é genérico no que o seu lifespan produz; `ctx.lifespan_context` é um `T` tipado.
* `add_request_handler(method, params_type, handler)` serve qualquer método. `initialize` é reservado.
* As capacidades que um `Server` anuncia são derivadas de quais handlers você registrou.
`Client(server)` tratou os dois servidores de forma idêntica porque eles *são* o mesmo protocolo, e essa é justamente a ideia. A próxima camada abaixo nem é uma classe: é **[Middleware](middleware.md)**.
O cliente tratou os dois servidores de forma idêntica porque eles *são* o mesmo protocolo, e essa é justamente a ideia. A próxima camada abaixo nem é uma classe: é **[Middleware](middleware.md)**.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [a9aba7a026c7bd85, ed32bda7ba9ae33a, 7e64cc5646abb91f, 22a0129ee78b3c63, d875373c06d8d2f9]
sections: [a9aba7a026c7bd85, 83e2a08b9d46a398, 9fd8a0aa384b3257, 22a0129ee78b3c63, d875373c06d8d2f9]
tool: 1
---
# Paginação {#pagination}
@ -31,9 +31,13 @@ O `@mcp.resource()` não tem nenhum gancho para isso. Para paginar, você mesmo
### Experimente {#try-it}
`Client(server)` se conecta a um `Server` de baixo nível em memória exatamente como se conecta a um `MCPServer`.
O `mcp run` só aceita um `MCPServer`, então este aqui você mesmo serve. A última linha de `server.py` monta um app ASGI comum a partir do `Server`, e o uvicorn executa esse app:
Chame `list_resources()` sem argumentos. Você recebe dez recursos, de `book-1` a `book-10`, e `next_cursor` é a string `"10"`.
```console
uvicorn server:app --port 8000
```
Aponte qualquer cliente (**[O cliente](../client/index.md)**, ou o Inspector) para `http://localhost:8000/mcp` e chame `list_resources()` sem argumentos. Você recebe dez recursos, de `book-1` a `book-10`, e `next_cursor` é a string `"10"`.
Devolva-a com `list_resources(cursor="10")` e o primeiro recurso é `book-11`, o novo `next_cursor` é `"20"`.
@ -43,7 +47,7 @@ A décima página volta com `next_cursor` definido como `None`. Pronto.
Todo método `list_*` do `Client` (`list_tools`, `list_resources`, `list_resource_templates`, `list_prompts`) aceita a palavra-chave `cursor=`. Esgotar uma lista paginada é um único `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 @@ Todo método `list_*` do `Client` (`list_tools`, `list_resources`, `list_resourc
* Acumule **antes** de olhar para `next_cursor`: a última página também tem recursos.
* `next_cursor is None` é a saída. Qualquer outra coisa volta direto para `cursor=`, intocada.
Execute o `main()` dele e ele imprime `100 resources`: dez páginas de dez, costuradas por um loop que nunca soube que havia dez páginas.
Com o uvicorn ainda servindo `server.py`, execute `python client.py` em um segundo terminal. Ele imprime `100 resources`: dez páginas de dez, costuradas por um loop que nunca soube que havia dez páginas.
Este é o mesmo loop que **[O cliente](../client/index.md)** mostra para todo verbo `list_*`, e ele não custa nada contra um servidor que não pagina: `next_cursor` é `None` na primeira resposta e o loop roda uma vez.