docs: refresh translations for recent English changes (#3379)

This commit is contained in:
Max 2026-08-24 19:29:54 +01:00 • committed by GitHub
commit c5d7d0b6d0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 3149 additions and 1047 deletions

View file

@ -1,6 +1,6 @@
---
translation:
sections: [2c79b6338e09b7ac, 7edc43b3fae11314, 1086e77ce561cd7f, a3f71823df5efc31, 9fc7109f72201cae, 7bf25983df655b66, 6330e1f4c6029683, 2f1749c8c133fa1c, b3530fcf4d11fd56, ebc33704fbd74262, cd0e9c933350390e]
sections: [2c79b6338e09b7ac, 7edc43b3fae11314, 1086e77ce561cd7f, a3f71823df5efc31, 9fc7109f72201cae, d50fe7faead8cf68, 7bf25983df655b66, 6330e1f4c6029683, 2f1749c8c133fa1c, 8db7116fc8ddd0ee, ebc33704fbd74262, cd0e9c933350390e]
tool: 1
---
# O Server de baixo nível {#the-low-level-server}
@ -116,6 +116,17 @@ O bloco `_meta` é o carimbo de identidade do servidor: o SDK o adiciona a todo
O servidor nunca compara os dois campos. O `Client` deste SDK compara: retorne um `structured_content` que não satisfaz o `output_schema` que você declarou e `call_tool` levanta um `RuntimeError` que começa com `Invalid structured content returned by tool search_books` e segue citando a falha do `jsonschema`. Prometer um schema é barato; cumprir a promessa é com você. A escada inteira de tipos de retorno e schemas está em **[Saída estruturada](../servers/structured-output.md)**.
## O dialeto é JSON Schema 2020-12 {#the-dialect-is-json-schema-2020-12}
`input_schema` e `output_schema` são JSON Schema, e a [especificação do MCP](https://modelcontextprotocol.io/specification/latest/basic#json-schema-usage) fixa o dialeto: um schema sem a chave `$schema` é **JSON Schema 2020-12**. Os schemas que o `MCPServer` gera dependem desse padrão (o Pydantic escreve 2020-12 e omite a chave), e um dict escrito à mão também é cobrado por ele, então o vocabulário completo de 2020-12 está disponível:
```python title="server.py" hl_lines="8 14-15"
--8<-- "docs_src/lowlevel/tutorial007.py"
```
* A raiz do `input_schema` precisa ser `"type": "object"`. Ao lado dela, `oneOf`, `additionalProperties`, `anyOf`, `if`/`then`/`else`, `prefixItems`, `$defs` com `$ref`s locais e o resto das palavras-chave de 2020-12 chegam ao cliente exatamente como foram escritas.
* Nenhuma chave `$schema` é necessária. Adicione uma só para optar por um draft mais antigo: o `Client` deste SDK, que valida o `structured_content` contra o `output_schema` de uma ferramenta, escolhe o validador a partir de `$schema` e usa 2020-12 quando não há nenhuma.
## `_meta`: para a aplicação, não para o modelo {#\_meta-for-the-application-not-the-model}
`content` é a parte da resposta que o modelo lê. `structured_content` é a mesma resposta como dados tipados. `_meta` é o terceiro canal: dados que viajam junto com o resultado para a **aplicação cliente**, sem fazer parte da resposta de forma alguma.
@ -167,7 +178,7 @@ O construtor cobre os métodos que o MCP define. `add_request_handler` cobre tod
--8<-- "docs_src/lowlevel/tutorial006.py"
```
* O primeiro argumento é a string do método. Notificações têm um irmão gêmeo, `add_notification_handler`.
* O primeiro argumento é a string do método. Notificações têm um irmão gêmeo, `add_notification_handler`. Os handlers dele disparam em stdio e em conexões HTTP da era do handshake; no caminho streamable-HTTP de `2026-07-28`, o POST de notificação de um cliente é confirmado com `202` e não é despachado, porque essa revisão não define notificações de cliente para servidor sobre HTTP.
* `params_type` é o modelo contra o qual os `params` recebidos são validados **antes** de o seu handler executar, então métodos personalizados *recebem* a validação que as ferramentas não recebem. Faça subclasse de `RequestParams` para que o campo `_meta` seja parseado como o de qualquer outro método.
* O handler retorna um `BaseModel`, um `dict` ou `None`. O SDK serializa isso no resultado JSON-RPC.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [6048b4f308edbb8c, 068bda0f21ee9c1b, c3e565b61acd75c5, c62422b159c6ed09, 47204fab253cc45c]
sections: [6048b4f308edbb8c, 46056f318ef205e4, c3e565b61acd75c5, c62422b159c6ed09, 420968f514138f43]
tool: 1
---
# Middleware {#middleware}
@ -53,8 +53,11 @@ cliente enviou para estabelecer a conexão, antes de você pedir qualquer coisa.
* O estabelecimento da conexão: `server/discover`, ou `initialize` e `notifications/initialized`
em uma sessão legada.
* Toda requisição e toda notificação. Para uma notificação, `ctx.request_id is None`,
`call_next(ctx)` retorna `None` e o que quer que você retorne é descartado.
* Toda requisição e toda notificação que chega ao servidor. Para uma notificação,
`ctx.request_id is None`, `call_next(ctx)` retorna `None` e o que quer que você retorne é
descartado. (No caminho Streamable HTTP de `2026-07-28`, o POST de notificação de um cliente
recebe a confirmação `202` no transporte e nunca é despachado, então também não chega ao
middleware; essa revisão não define nenhuma notificação do cliente para o servidor sobre HTTP.)
* Até um método para o qual o servidor não tem handler: `call_next` lança o
`MCPError(-32601, "Method not found")` *através* do seu middleware a caminho do cliente.
@ -111,8 +114,8 @@ nele. Ele é um no-op até você instalar um exportador, e tem a própria págin
* Um middleware é `async (ctx, call_next) -> result`, passado como `MCPServer(middleware=[...])` (ou
adicionado a `mcp.middleware`) e adicionado a `server.middleware` no `Server` de baixo nível.
* Ele envolve **toda** mensagem de entrada (`server/discover`, `initialize`, requisições,
notificações, métodos desconhecidos) e executa do mais externo para o mais interno.
* Ele envolve **toda** mensagem de entrada que chega ao servidor (`server/discover`, `initialize`,
requisições, notificações, métodos desconhecidos) e executa do mais externo para o mais interno.
* `ctx.request_id is None` é como você distingue uma notificação de uma requisição.
* Lance uma exceção em vez de chamar `call_next` para recusar uma mensagem; a conexão sobrevive.
* O tracing do OpenTelemetry do próprio SDK também é um middleware, já na lista. Veja

View file

@ -1,6 +1,6 @@
---
translation:
sections: [ebef1e7a0df854f4, a4c687d3d627d516, 8e79141fc2985342, b345dd05b9c3c7ab, 80ce41579825a6fa, 5f0fa90494de8f65, 83d10514eaa62fa5, 9190555aa39a5d28, 84a4c9d8bf14dddb, 927d71cf40b58c30]
sections: [ebef1e7a0df854f4, 8355cfaf1f76c9d5, 8e79141fc2985342, 46bdb07c7537e8a5, 80ce41579825a6fa, 5f0fa90494de8f65, 83d10514eaa62fa5, 9190555aa39a5d28, 84a4c9d8bf14dddb, 927d71cf40b58c30]
tool: 1
---
# O cliente {#the-client}
@ -27,9 +27,10 @@ O servidor no topo só está ali para você ter algo a que se conectar. O client
* Uma instância de `MCPServer` (ou do `Server` de baixo nível): conectada **no mesmo processo**.
* Uma string de URL (`Client("http://localhost:8000/mcp")`): Streamable HTTP, o caminho de produção.
* Um **transporte**: qualquer coisa com que você possa fazer `async with ... as (read, write)`, como `stdio_client(...)` encapsulando um subprocesso.
* Um `StdioServerParameters`: o comando a iniciar como **subprocesso**, com o qual se conversa pelo stdin e stdout dele.
* Um **transporte**: qualquer coisa com que você possa fazer `async with ... as (read, write)`, como `streamable_http_client(url, http_client=...)` em volta do seu próprio cliente HTTP.
Todo o resto desta página é idêntico entre os três. Cabeçalhos, subprocessos, timeouts e o protocolo `Transport` têm sua própria página: **[Transportes do cliente](transports.md)**.
Todo o resto desta página é idêntico entre os quatro. Cabeçalhos, subprocessos, timeouts e o protocolo `Transport` têm sua própria página: **[Transportes do cliente](transports.md)**.
### O que há em um cliente conectado {#whats-on-a-connected-client}
@ -85,7 +86,7 @@ Esse schema é tudo o que uma UI precisa para renderizar um formulário de argum
`call_tool(name, arguments)` executa a ferramenta e devolve um `CallToolResult`.
```python title="client.py" hl_lines="26-33"
```python title="client.py" hl_lines="27-34"
--8<-- "docs_src/client/tutorial003.py"
```
@ -117,7 +118,7 @@ Uma ferramenta que lança uma exceção **não** lança no seu cliente. Ela volt
!!! check
Peça `"Solaris"` ao `lookup_book` (um título que não está no catálogo) e a função lança
`ValueError`. A chamada ainda retorna normalmente:
`ToolError`. A chamada ainda retorna normalmente:
```python
result.is_error # True
@ -125,9 +126,10 @@ Uma ferramenta que lança uma exceção **não** lança no seu cliente. Ela volt
result.structured_content # None
```
A mensagem da exceção foi parar em `content`, onde o **modelo** pode lê-la e tentar de novo. Isso
é proposital: um erro de ferramenta faz parte da conversa, não é um crash. Sempre olhe `is_error`
antes de confiar em `structured_content`.
A mensagem do `ToolError` foi parar em `content`, onde o **modelo** pode lê-la e tentar de novo. Isso
é proposital: um erro de ferramenta faz parte da conversa, não é um crash. (Se a ferramenta tivesse
quebrado com alguma outra exceção, `content` diria apenas `Error executing tool lookup_book`.) Sempre
olhe `is_error` antes de confiar em `structured_content`.
!!! warning
`is_error=True` cobre mais do que o seu próprio `raise`. Peça uma ferramenta que o servidor nem tem

View file

@ -1,6 +1,6 @@
---
translation:
sections: [9cac816674181eb0, 0700f337babcd4dd, 2bde0dd58cdf00f5, ff7401df479af877, 3d0832f39b0d7059, d4bf7e4479637768, 05e20c0a798860e7]
sections: [9cac816674181eb0, 0700f337babcd4dd, 2bde0dd58cdf00f5, 40b4916d82eaf1d4, 3d0832f39b0d7059, dfa4446556badef0, 5bd93be2ab2ecb9c]
tool: 1
---
# Transportes do cliente {#client-transports}
@ -87,15 +87,15 @@ sem um repositório de CAs do sistema utilizável (alguns contêineres mínimos)
Um servidor **stdio** é um subprocesso. O cliente o inicia, escreve JSON-RPC no stdin dele e lê JSON-RPC do stdout dele. É assim que um host de desktop executa um servidor na sua máquina: um host *é* este código mais uma interface, e **[Conecte a um host real](../get-started/real-host.md)** é a mesma relação vista do lado do host, como um arquivo de configuração.
Descreva o processo com `StdioServerParameters`, transforme-o em um transporte com `stdio_client` e entregue *isso* ao `Client`:
Descreva o processo com `StdioServerParameters` e entregue-o ao `Client`:
```python title="client.py" hl_lines="4-8 12"
```python title="client.py" hl_lines="3-7 11"
--8<-- "docs_src/client_transports/tutorial004.py"
```
`Client` não aceita o objeto de parâmetros sozinho. `StdioServerParameters` é configuração; `stdio_client(server)` é o transporte que sabe como iniciar um processo a partir dela. Sempre envolva.
Entrar no bloco inicia o processo. Sair dele encerra o subprocesso: fecha o stdin, espera e mata o processo se ele demorar. Você nunca limpa isso por conta própria.
Sair do bloco `async with` também encerra o subprocesso: fecha o stdin, espera e mata o processo se ele demorar. Você nunca limpa isso por conta própria.
O stderr do processo filho vai para o seu. Para mandá-lo para outro lugar, construa o transporte você mesmo com `stdio_client` (de `mcp`) e passe isso no lugar: `Client(stdio_client(server, errlog=log_file))`.
!!! warning
O processo filho **não** herda o seu ambiente. Ele recebe uma allow-list mínima (`HOME`, `LOGNAME`,
@ -113,16 +113,16 @@ Sair do bloco `async with` também encerra o subprocesso: fecha o stdin, espera
Para o `Client`, tudo acima é a mesma coisa.
Um **transporte** é qualquer gerenciador de contexto assíncrono que produz um par `(read, write)` de streams de mensagens: formalmente, o protocolo `Transport` em `mcp.client`. `Client` resolve seu argumento pelo tipo: um objeto de servidor conecta no próprio processo, uma `str` vira `streamable_http_client(url)` e qualquer outra coisa é aberta diretamente como transporte. É por causa dessa última regra que `stdio_client(...)`, `streamable_http_client(...)` e `sse_client(...)` se encaixam todos no mesmo lugar, e que você pode escrever o seu próprio.
Um **transporte** é qualquer gerenciador de contexto assíncrono que produz um par `(read, write)` de streams de mensagens: formalmente, o protocolo `Transport` em `mcp.client`. `Client` resolve seu argumento pelo tipo: um objeto de servidor conecta no próprio processo, uma `str` vira `streamable_http_client(url)`, um `StdioServerParameters` vira `stdio_client(params)` e qualquer outra coisa é aberta diretamente como transporte. É por causa dessa última regra que `stdio_client(...)`, `streamable_http_client(...)` e `sse_client(...)` se encaixam todos no mesmo lugar, e que você pode escrever o seu próprio.
## Recapitulando {#recap}
* `Client(mcp)` (o objeto do servidor) conecta em memória. Use para testes e para embutir.
* `Client("http://.../mcp")` (uma URL) conecta por Streamable HTTP, o transporte de produção.
* Headers, auth, proxies e timeouts pertencem a um `httpx2.AsyncClient` que você passa a `streamable_http_client(url, http_client=...)`. Não existe o argumento `headers=`.
* stdio é `Client(stdio_client(StdioServerParameters(...)))`, nunca o objeto de parâmetros sozinho.
* stdio é `Client(StdioServerParameters(...))`. Envolva-o em `stdio_client(...)` você mesmo apenas para redirecionar o stderr do processo filho.
* O subprocesso recebe um ambiente em allow-list, não o seu; `env=` acrescenta a ele.
* Um transporte é qualquer coisa com que você possa fazer `async with x as (read, write)`. `Client` entrega direto a esse protocolo tudo que não for um objeto de servidor ou uma URL.
* Um transporte é qualquer coisa com que você possa fazer `async with x as (read, write)`. `Client` entrega direto a esse protocolo tudo que não for um objeto de servidor, uma URL ou um `StdioServerParameters`.
* Construir um `Client` escolhe o transporte. `async with` o abre.
Depois que o transporte está aberto, os dois lados precisam concordar sobre uma versão do protocolo. Normalmente você nunca pensa nisso; quando pensar, **[Versões do protocolo](../protocol-versions.md)** é a página.

View file

@ -1,11 +1,11 @@
---
translation:
sections: [20541a40dbdd5980, 01262a123ad9501d, 429db5b574a2ac08, 56b2d49da412cb28, 6a1717123fe4513c]
sections: [490237e61c3a7a44, 01262a123ad9501d, 429db5b574a2ac08, e2d0d273fbd2d74b, 64ab0331e868f3d4, 6c8878ce2d1f6d56, 4068f23e371bf0b3, eaef75b8725bc931]
tool: 1
---
# Funcionalidades descontinuadas {#deprecated-features}
A especificação 2026-07-28 aposenta cinco coisas. O SDK ainda implementa cada uma delas, e cada uma agora carrega um **aviso de descontinuação**.
A especificação 2026-07-28 aposenta cinco coisas. O SDK ainda implementa cada uma delas, e cada uma agora carrega um **aviso de descontinuação**. Um helper do SDK está descontinuado por conta própria e aparece listado [no final](#deprecated-sdk-helpers).
A tabela abaixo nomeia cada funcionalidade descontinuada, o motivo de ela estar saindo e o substituto sobre o qual construir.
@ -55,6 +55,55 @@ MCPDeprecationWarning: The logging capability is deprecated as of 2026-07-28 (SE
em seguida. Esses dois só funcionam de ponta a ponta em uma conexão `mode="legacy"`
cujo cliente registrou o callback correspondente.
## `ping` em uma sessão legacy {#ping-on-a-legacy-session}
Um **ping** é uma requisição vazia que qualquer um dos lados pode enviar para conferir se o outro ainda está respondendo. A especificação 2026-07-28 o remove ([SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)): toda requisição que um cliente moderno envia já prova que o servidor está lá, e um servidor moderno não tem canal para enviar um. Os dois métodos do SDK ainda funcionam em uma sessão da era do handshake. Do cliente:
```python
async def main() -> None:
async with Client("http://localhost:8000/mcp", mode="legacy") as client:
await client.send_ping() # warns; returns an EmptyResult
```
E do servidor, dentro de qualquer handler:
```python
@mcp.tool()
async def check_client(ctx: Context) -> str:
"""A tool that still pings the client mid-call."""
await ctx.session.send_ping() # no warning; an EmptyResult while the client is connected
return "client answered"
```
* `client.send_ping()` avisa com `MCPDeprecationWarning` a cada chamada. Em uma conexão padrão (`2026-07-28`), o servidor responde `MCPError: Method not found` em vez disso.
* `ctx.session.send_ping()` não carrega aviso nenhum. Em uma conexão moderna, lança o mesmo erro de ausência de canal de retorno (back-channel) que qualquer outra requisição iniciada pelo servidor.
* Nenhum dos lados registra nada para responder a um ping.
## Notificações de mudança de roots {#roots-change-notifications}
Um cliente da era 2025 que declarou a capacidade roots pode contar ao servidor que as pastas do seu workspace mudaram enviando `notifications/roots/list_changed`; o servidor responde requisitando `roots/list` de novo. A especificação 2026-07-28 remove a notificação junto com o resto do fluxo de roots no estilo push. No cliente, passar `list_roots_callback=` (**[Callbacks do cliente](client/callbacks.md)**) é o que declara `"roots": {"listChanged": true}`, e uma chamada cumpre essa promessa:
```python
async def open_folder(client: Client, uri: str, name: str) -> None:
"""The user opened another folder: expose it through the roots callback, then tell the server."""
workspace.append(Root(uri=FileUrl(uri), name=name))
await client.send_roots_list_changed()
```
No servidor, é o `Server` de baixo nível que aceita o handler do lado receptor:
```python
async def roots_changed(ctx: ServerRequestContext, params: NotificationParams | None) -> None:
"""The client's roots changed: ask for the new list."""
roots = (await ctx.session.list_roots()).roots
server = Server("Bookshop", on_roots_list_changed=roots_changed)
```
* `workspace` é a lista que o seu `list_roots_callback` retorna. `client.send_roots_list_changed()` avisa, e precisa de um cliente `mode="legacy"`: em uma conexão moderna a notificação é descartada silenciosamente. Mantenha a sessão aberta depois, porque o `roots/list` de acompanhamento do servidor chega por ela.
* `MCPServer` não tem hook para a notificação. No `Server` de baixo nível, `on_roots_list_changed=` registra o handler (descontinuado também, e avisa na construção). A notificação não carrega payload, então o handler chama `ctx.session.list_roots()` para obter a nova lista.
## Silenciando o aviso {#silencing-the-warning}
Não faça isso, em código novo.
@ -75,22 +124,33 @@ A API inteira é essa. Não há uma chave por método, e você não quer uma: o
Aplique o filtro no sentido contrário e você ganha um teste de regressão de graça.
Adicione `"error::mcp.MCPDeprecationWarning"` à configuração `filterwarnings` do seu
pytest e a chamada descontinuada **lança uma exceção** em vez de avisar. Uma ferramenta
chamada `old_log` que ainda chama `ctx.info()` para de passar e começa a reportar:
chamada `old_log` que ainda chama `ctx.info()` para de passar: a chamada volta com
`is_error=True` e `Error executing tool old_log`, e o log capturado do servidor aponta o
culpado:
```text
Error executing tool old_log: The logging capability is deprecated as of 2026-07-28 (SEP-2577).
mcp.shared.exceptions.MCPDeprecationWarning: The logging capability is deprecated as of 2026-07-28 (SEP-2577).
```
Uma linha de configuração do pytest, e uma chamada descontinuada nunca mais consegue
voltar sorrateiramente ao seu código sem quebrar um teste.
## Helpers descontinuados do SDK {#deprecated-sdk-helpers}
Estas não são mudanças de especificação, apenas detalhes internos do SDK com um substituto melhor. Avisam com o mesmo `MCPDeprecationWarning` e serão removidos na 3.0.
| Descontinuado | O que fazer no lugar |
|---|---|
| `FuncMetadata.call_fn_with_arg_validation()` | `FuncMetadata.validate_arguments()` e depois `FuncMetadata.call_fn()`. Só código que conduz `FuncMetadata` diretamente (uma subclasse personalizada de `Tool`, digamos) chegou a chamá-lo. |
## Recapitulando {#recap}
* A especificação 2026-07-28 descontinua **roots**, a **amostragem** iniciada pelo servidor e o **logging** de protocolo (todos pela [SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577)), restringe o **progresso** ao sentido servidor para cliente e remove o **`ping`**.
* A coluna de substitutos indica o próximo passo: **[Requisições de múltiplas idas e voltas](handlers/multi-round-trip.md)** para amostragem e roots, **[Logging](handlers/logging.md)** para logging, **[Progresso](handlers/progress.md)** para progresso. `ping` não precisa de nada.
* Descontinuado é consultivo: sem mudanças no protocolo de transmissão, tudo continua funcionando em sessões pré-2026, e você recebe um `MCPDeprecationWarning` visível (um `UserWarning`, então está ligado por padrão).
* Amostragem e roots precisam, além disso, de um canal de retorno (back-channel) que uma sessão 2026-07-28 não tem. Em uma conexão moderna elas avisam e depois lançam uma exceção.
* Amostragem e roots precisam, além disso, de um canal de retorno que uma sessão 2026-07-28 não tem. Em uma conexão moderna elas avisam e depois lançam uma exceção.
* `warnings.filterwarnings("ignore", category=MCPDeprecationWarning)` silencia a categoria inteira; `"error::mcp.MCPDeprecationWarning"` no pytest a transforma em falha de teste.
* Um helper do SDK, `FuncMetadata.call_fn_with_arg_validation()`, está descontinuado separadamente para remoção na 3.0.
* Código novo não deve ser construído sobre nenhuma delas.
Todas as outras páginas desta documentação ensinam a API atual.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [3c4f2f06b4e978b6, 22520eecae3d1961, f4e1709db18d635a, 2eb57992049671d9, 1ba83e9af37cc1b4, 4822586344b08d9e, 1c93afef72478992, b6b448f9eddd51dc, fe55370fd931815b]
sections: [3c4f2f06b4e978b6, 51ea5fbcb0e93563, 32d8808606ffdae0, 2eb57992049671d9, 1ba83e9af37cc1b4, 4822586344b08d9e, 1c93afef72478992, b6b448f9eddd51dc, fe55370fd931815b]
tool: 1
---
# Conecte-se a um host de verdade {#connect-to-a-real-host}
@ -11,7 +11,7 @@ Ou seja, conectar a um host é um ato só: você informa a ele **o comando que i
## Um servidor, todos os hosts {#one-server-every-host}
```python title="server.py" hl_lines="3 33-34"
```python title="server.py" hl_lines="4 34-35"
--8<-- "docs_src/real_host/tutorial001.py"
```
@ -50,7 +50,7 @@ Um comando só para todos eles porque `uv run --with` resolve o SDK em um ambien
E um host nada mais é que uma aplicação com um cliente MCP dentro, então seu próprio
código Python pode fazer o papel do host: **[Transportes do cliente](../client/transports.md)**
inicia este mesmo arquivo como subprocesso com `stdio_client(...)`, e **[Testes](testing.md)**
inicia este mesmo arquivo como subprocesso com `Client(StdioServerParameters(...))`, e **[Testes](testing.md)**
se conecta a ele em memória, sem processo nenhum.
## Claude Desktop {#claude-desktop}

View file

@ -1,6 +1,6 @@
---
translation:
sections: ['4926721070127497', c52a1de2b6b32f40, 2e410b412c25f314, 627195f7159e24ef]
sections: ['4926721070127497', c52a1de2b6b32f40, 8e792bf8c7489ec6, 627195f7159e24ef]
tool: 1
---
# Testes {#testing}
@ -85,8 +85,8 @@ Pronto! Agora você pode estender seus testes para cobrir mais cenários.
Duas coisas diferentes podem dar errado, e essa flag só mexe em uma delas.
Uma exceção dentro de uma das **suas ferramentas** não é uma falha de protocolo. Ela vira um resultado normal com
`is_error=True`, e o modelo lê a mensagem. `raise_exceptions` não muda isso: com ou
sem ela, `call_tool` retorna o mesmo resultado com `is_error=True`. Há uma página inteira sobre isso:
`is_error=True` (e, se for um `ToolError`, o modelo lê a sua mensagem). `raise_exceptions` não
muda isso: com ou sem ela, `call_tool` retorna o mesmo resultado com `is_error=True`. Há uma página inteira sobre isso:
**[Tratamento de erros](../servers/handling-errors.md)**.
Uma falha **fora** do corpo de uma ferramenta é diferente. Na conexão que `Client(mcp)` entrega, o

View file

@ -1,6 +1,6 @@
---
translation:
sections: [335ca2a0b266f003, d1ad562d3fe87bc0, 0bb1396c86daeba4, d1cb1235bb9ee267, 833179c09d239c83, e5d6dec2d2e655e8]
sections: [335ca2a0b266f003, d1ad562d3fe87bc0, 25b49f89c9e6f9d0, d1cb1235bb9ee267, 833179c09d239c83, e5d6dec2d2e655e8]
tool: 1
---
# Elicitação {#elicitation}
@ -90,7 +90,8 @@ Esse schema é o formulário. `Field(description=...)` é o rótulo; um valor pa
Um schema de elicitação não é tão expressivo quanto o schema de entrada de uma ferramenta.
Só campos planos e primitivos: `str`, `int`, `float`, `bool` ou um `Literal` de strings
(que vira um `enum`). Coloque um modelo dentro do modelo e `ctx.elicit` lança uma exceção
antes de qualquer coisa ser enviada ao cliente:
antes de qualquer coisa ser enviada ao cliente. A chamada da ferramenta falha com
`Error executing tool <name>`, e o log do seu servidor tem o motivo:
```text
TypeError: Elicitation schema field 'address' rendered as {'$ref': '#/$defs/Address'}, which is not a valid PrimitiveSchemaDefinition
@ -113,8 +114,8 @@ Uma recusa não é um erro. A ferramenta decide o que recusar significa (aqui, n
!!! tip
A resposta é validada contra seu modelo antes que seu código a veja. Um cliente que envia
`"maybe"` para um `bool` não corrompe sua reserva: a chamada falha com um erro de
incompatibilidade de schema, e seu `if` nem chega a executar.
`"maybe"` para um `bool` não corrompe sua reserva: `ctx.elicit` lança `ValueError`, a
chamada falha, e seu `if` nem chega a executar.
## Envie o usuário para uma URL {#send-the-user-to-a-url}

View file

@ -1,6 +1,6 @@
---
translation:
sections: [c93a3e1aefd77955, 7851abd5ec54393b, f49d1ca2f330f9cd, c03764bd9dfeef7b, 4a0391691a674ae4, 2df5cd279eabf9f5]
sections: [c93a3e1aefd77955, 7851abd5ec54393b, f49d1ca2f330f9cd, 4cc0a00347c3f534, 4a0391691a674ae4, 2df5cd279eabf9f5]
tool: 1
---
# Logging {#logging}
@ -54,6 +54,8 @@ O padrão é `"INFO"`.
`logging.basicConfig()` nunca substitui handlers que já existem. Se você configurar o logging por conta própria antes de criar o servidor, sua configuração vence.
Você também não precisa de um `try`/`except` em cada handler só para registrar falhas. Quando uma função de ferramenta ou de recurso levanta uma exceção, o SDK faz o log para você. **[Tratando erros](../servers/handling-errors.md#any-other-exception)** explica o que é registrado e em qual nível.
## Experimente {#try-it}
Execute o servidor com o MCP Inspector:

View file

@ -1,6 +1,6 @@
---
translation:
sections: [fea8d769ff9edeba, ce8e2ad42f29ef71, 0d705efb19cf99c2, 7a53ead3e704a7f0, 9adc400e8c88e854, 318893ad8e2e9924, 6b63ab96b34476c0]
sections: [fea8d769ff9edeba, ce8e2ad42f29ef71, 0d705efb19cf99c2, 2fd7cf825e6d2b2c, 9adc400e8c88e854, 318893ad8e2e9924, 6b63ab96b34476c0]
tool: 1
---
# Executando seu servidor {#running-your-server}
@ -72,7 +72,7 @@ Cada transporte tem seus próprios argumentos nomeados, todos em `run()`:
* `streamable_http_path`: onde fica o endpoint MCP. Padrão `/mcp`.
* `json_response=True`: responde a cada POST com um único corpo JSON em vez de um fluxo SSE. Esse corpo tem espaço para a resposta e nada mais, então uma ferramenta que chama o cliente de volta no meio da requisição (`ctx.elicit()`, amostragem (sampling)) lança `NoBackChannelError` nesse trecho, e as notificações ligadas à chamada em andamento (progresso de `ctx.report_progress()`, mensagens de log por chamada) são descartadas; o fluxo `GET` avulso continua transportando as que não têm relação.
* `stateless_http=True`: um transporte novo por requisição, sem rastreamento de sessão.
* `max_request_body_size`: maior corpo de POST aceito, em bytes. O padrão é 4 MiB; requisições maiores
* `max_request_body_size`: maior corpo de requisição aceito, em bytes. O padrão é 4 MiB; requisições maiores
recebem HTTP 413 antes do parsing ou da criação da sessão. Aumente apenas quando mensagens MCP legítimas
ultrapassarem esse tamanho.
* `event_store`, `retry_interval`, `transport_security`: retomada e proteção contra DNS rebinding. Podem esperar até você fazer o deploy em algum lugar que não seja o localhost; **[Deploy e escala](deploy.md)** cobre `transport_security`.

View file

@ -1,13 +1,13 @@
---
translation:
sections: [e33d441f12d50535, 7099694c603e0f5f, c1df4cf9673433e6, c9cd294541422e6e, 6cec073617bfd037, efa92b8f99e908c8, 6a22a29e27fb4601]
sections: [7be05607887e6853, e7375894888d9750, c36f73fc7e3af13b, 2fec2d7e129e62fe, 809b0e0a7c27295a, b4395a04d2a5d906, 1a436007f5f54779, c6b2078ed1e63ba5]
tool: 1
---
# Tratando erros {#handling-errors}
Uma ferramenta (tool) pode falhar de duas maneiras, e o SDK trata cada uma de forma bem diferente.
Uma ferramenta (tool) pode falhar de três maneiras, e o SDK trata cada uma de forma diferente.
Lance uma exceção comum e é o **modelo** que a vê. Lance `MCPError` e é o **protocolo** que a vê.
Lance `ToolError` e é o **modelo** que vê a sua mensagem. Lance `MCPError` e é o **protocolo** que a vê. Lance qualquer outra coisa e é um crash: o modelo só fica sabendo que a chamada falhou, e o seu log recebe o traceback.
Esta página é sobre essa escolha.
@ -15,11 +15,11 @@ Esta página é sobre essa escolha.
Pegue uma ferramenta que faz uma consulta e deixe a consulta não encontrar nada:
```python title="server.py" hl_lines="11-12"
```python title="server.py" hl_lines="2 12-13"
--8<-- "docs_src/handling_errors/tutorial001.py"
```
Não há nada de MCP nessas duas linhas. `get_author` lança um `ValueError` comum, como qualquer função Python faria.
`ToolError`, de `mcp.server.mcpserver.exceptions`, é como uma ferramenta avisa ao modelo que algo deu errado.
Chame a ferramenta com um título que não está no catálogo e veja o resultado:
@ -30,13 +30,15 @@ result.structured_content # None
```
* A requisição **foi bem-sucedida**. Há um resultado; nada foi lançado no lado de quem chamou.
* `is_error` é `True`, e a mensagem da sua exceção (prefixada com o nome da ferramenta) está em `content`, exatamente onde o modelo lê.
* `is_error` é `True`, e a sua mensagem (prefixada com o nome da ferramenta) está em `content`, exatamente onde o modelo lê.
* `structured_content` é `None`. Uma chamada que falhou não tem valor de retorno para estruturar.
Isso é um **erro de ferramenta**, e é o padrão para *qualquer* exceção que a sua ferramenta lançar. Também é, quase sempre, o que você quer.
Isso é um **erro de ferramenta**, e é quase sempre o que você quer.
Quem chama a sua ferramenta é o modelo. Foi ele que escolheu os argumentos. Então um erro de ferramenta é um turno na conversa: o modelo lê *"No book titled 'Nothing' in the catalog."*, percebe que chutou o título errado e chama de novo com um melhor. Você escreveu um `raise` e ganhou um agente que se corrige sozinho.
No servidor, um `ToolError` é uma linha `INFO` no log, sem traceback. Você já esperava por ele, então não há nada para investigar.
!!! tip
Nunca faça `return` de uma mensagem de erro em uma ferramenta. Uma string retornada tem `is_error=False`, então, para o
modelo (e para toda interface de cliente), parece que a ferramenta funcionou e que aquela string era a resposta.
@ -44,7 +46,7 @@ Quem chama a sua ferramenta é o modelo. Foi ele que escolheu os argumentos. Ent
## Um erro que o modelo não consegue corrigir {#an-error-the-model-cannot-fix}
Agora troque `ValueError` por `MCPError`.
Agora troque `ToolError` por `MCPError`.
```python title="server.py" hl_lines="1 3 14"
--8<-- "docs_src/handling_errors/tutorial002.py"
@ -77,10 +79,10 @@ Agora troque `ValueError` por `MCPError`.
Os dois caminhos respondem a duas perguntas diferentes.
* **Lance qualquer exceção** para uma falha de *execução*: aquilo que a sua ferramenta tentou fazer não funcionou. Foi o modelo que escolheu a chamada, então é o modelo que deve ver a consequência e ter a chance de se recuperar. Um título escrito errado, uma API upstream que deu timeout, uma linha que não existe: tudo erro de ferramenta.
* **Lance `ToolError`** para uma falha de *execução*: aquilo que a sua ferramenta tentou fazer não funcionou. Foi o modelo que escolheu a chamada, então é o modelo que deve ver a consequência e ter a chance de se recuperar. Um título escrito errado, uma API upstream que deu timeout, uma linha que não existe: tudo erro de ferramenta.
* **Lance `MCPError`** quando a *própria requisição* deve ser rejeitada: o cliente não tem uma capacidade da qual a sua ferramenta depende, o servidor não está em condições de atender ninguém, quem chamou pulou uma etapa obrigatória. Nenhuma nova tentativa do modelo corrige nada disso, então não há nada a ganhar entregando a mensagem a ele.
Uma pergunta decide: **um modelo mais esperto teria evitado isso?** Sim -> exceção comum. Não -> `MCPError`.
Uma pergunta decide: **um modelo mais esperto teria evitado isso?** Sim -> `ToolError`. Não -> `MCPError`.
Por esse critério, a segunda versão de `get_author` fez a escolha errada: um título melhor resolve, então o modelo merecia ver a mensagem. Ela está ali para mostrar o mecanismo, não para recomendá-lo.
@ -89,6 +91,25 @@ Por esse critério, a segunda versão de `get_author` fez a escolha errada: um t
`data` opcional. O que você colocar neles é o que o cliente recebe: o SDK repassa um
`MCPError` lançado tal e qual, em vez de sanitizá-lo.
## Qualquer outra exceção {#any-other-exception}
Agora tire a verificação e deixe a consulta ao dicionário falhar sozinha:
```python title="server.py" hl_lines="11"
--8<-- "docs_src/handling_errors/tutorial004.py"
```
`CATALOG[title]` lança `KeyError`. Você não planejou isso, então o SDK trata como um crash:
```python
result.is_error # True
result.content # [TextContent(text="Error executing tool get_author")]
```
A chamada ainda retorna `is_error=True`, então o modelo sabe que falhou e pode seguir em frente. O que ele não recebe é o texto da exceção: um `KeyError` do seu código, ou uma pilha de SQL vinda de um driver três bibliotecas abaixo, pode descrever o funcionamento interno do seu servidor, então nunca sai do servidor.
Quem recebe é você. O servidor registra o crash em `ERROR` com o traceback completo, como `Tool 'get_author' raised an unexpected exception`. Um log de produção em `WARNING`, portanto, fica quieto a cada `ToolError` e se manifesta no instante em que algo está de fato quebrado.
## Um recurso que não existe {#a-resource-that-doesnt-exist}
Recursos fazem a mesma distinção, e vêm com uma exceção nomeada para o caso mais comum.
@ -109,7 +130,7 @@ Quando não consegue, lance `ResourceNotFoundError`. O SDK a transforma no erro
}
```
Repare que aqui não existe um meio-resultado com `is_error=True`. A leitura de um recurso ou retorna conteúdo ou falha: recursos só têm o caminho do protocolo. Templates e todo o resto sobre recursos ficam em **[Recursos](resources.md)**.
Repare que aqui não existe um meio-resultado com `is_error=True`. A leitura de um recurso ou retorna conteúdo ou falha: recursos só têm o caminho do protocolo. `ResourceError` é a mesma coisa para uma falha que não é "não encontrado" (`-32603`, com a sua mensagem), e as duas são uma linha `INFO` no seu log. Qualquer outra exceção, exceto `MCPError`, é um crash: o cliente recebe `-32603` citando apenas a URI, e o traceback vai para o seu log em `ERROR`. Templates e todo o resto sobre recursos ficam em **[Recursos](resources.md)**.
## Erros que você nunca lança {#errors-you-never-raise}
@ -120,19 +141,21 @@ Mande para `get_author` um `title` que não seja uma string e o SDK o rejeita co
Isso significa uma classe inteira de instruções `raise` que você não escreve: não revalide as suas próprias anotações de tipo.
!!! info
Tudo nesta página é o que um **cliente** vê, e o `Client` em memória com o qual você vai escrever
seus testes vê exatamente a mesma coisa. Nem `raise_exceptions=True` transforma um erro de ferramenta
de volta em traceback: no momento em que essa flag poderia agir, a sua exceção já virou o
resultado com `is_error=True`. Faça o assert no resultado. **[Testes](../get-started/testing.md)** cobre o padrão.
Tudo o que um **cliente** vê nesta página, o `Client` em memória com o qual você vai escrever
seus testes também vê. Nem `raise_exceptions=True` devolve a exceção de uma ferramenta que falhou
a quem chamou: no momento em que essa flag poderia agir, a sua exceção já virou o
resultado com `is_error=True`. Faça o assert no resultado. Se você precisar do traceback de um crash, ele está no
log do servidor, e o `caplog` do pytest o captura. **[Testes](../get-started/testing.md)** cobre o padrão.
## Recapitulando {#recap}
* Lance **qualquer exceção** em uma ferramenta -> a chamada retorna `is_error=True` com a sua mensagem em `content`. O modelo lê e pode tentar de novo. Esse é o padrão.
* Lance **`ToolError`** em uma ferramenta -> a chamada retorna `is_error=True` com a sua mensagem em `content`. O modelo lê e pode tentar de novo.
* Lance **`MCPError`** -> a própria chamada falha com um erro JSON-RPC. O modelo não vê nada; quem lida com isso é o host. `code`, `message` e `data` sobrevivem intactos.
* A pergunta que decide: *um modelo mais esperto teria evitado isso?* Sim -> exceção. Não -> `MCPError`.
* A pergunta que decide: *um modelo mais esperto teria evitado isso?* Sim -> `ToolError`. Não -> `MCPError`.
* Qualquer **outra exceção** é um crash -> `is_error=True` só com `Error executing tool <name>` para o modelo, e um registro `ERROR` com o traceback para você.
* `ResourceNotFoundError` em um handler de recurso -> o `-32602` do protocolo, com a URI em `data`.
* Argumentos inválidos são rejeitados com base no schema antes de a sua função executar; você não dá `raise` para eles.
* `from mcp import MCPError`; as constantes de código de erro vêm de `mcp.types`.
* Imports: `from mcp import MCPError`, `from mcp.server.mcpserver.exceptions import ToolError, ResourceError, ResourceNotFoundError`, e as constantes de código de erro de `mcp.types`.
Erros tratados. Isso é tudo o que um servidor *expõe*. O que cada handler pode ler, e fazer de volta ao cliente enquanto executa, é a próxima seção: **[Dentro do seu handler](../handlers/index.md)**.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 043f526230dd243d, 6ee3e9bcfd24047a]
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 864137b5e9c61e91, 043f526230dd243d, db1ef91db7d6b3f3]
tool: 1
---
# Mídia {#media}
@ -86,6 +86,24 @@ Um sufixo não reconhecido cai no padrão `application/octet-stream`.
`Audio` a partir de bytes MP3 desse jeito e o cliente recebe `mime_type="audio/wav"` e,
confiando nisso, falha ao decodificar. Quando você passar `data=`, passe `format=`.
## Embutindo um recurso {#embedding-a-resource}
Uma ferramenta também pode retornar um documento: algum texto ou bytes junto com a URI onde ele mora e um tipo MIME. Isso é um **`EmbeddedResource`**, outro tipo de bloco de conteúdo. Diferente de uma `str` simples, ele diz ao cliente o que é o conteúdo, então o cliente pode mostrá-lo como anexo ou reconhecer um recurso que já conhece.
```python title="server.py" hl_lines="7 14 16-18"
--8<-- "docs_src/media/tutorial005.py"
```
* `brand://guidelines` é um recurso comum (**[Recursos](resources.md)** trata deles). A ferramenta entrega o mesmo documento ao modelo quando pedido, e chamar `guidelines()` diretamente mantém uma única fonte da verdade.
* `EmbeddedResource` e `TextResourceContents` vêm de `mcp.types`. Não há um helper como há para imagens: o bloco que você monta entra no resultado sem alteração, e não há `structured_content`.
* Use a URI sob a qual o recurso está registrado, para que um cliente consiga perceber que o anexo e `brand://guidelines` são o mesmo documento. Qualquer URI é válida, registrada ou não.
```python
result.content # [EmbeddedResource(type="resource", resource=TextResourceContents(uri="brand://guidelines", mime_type="text/markdown", text="# Brand guidelines\n\n..."))]
```
Para conteúdo binário, use `BlobResourceContents(uri=..., mime_type=..., blob=...)` com os bytes codificados em base64 em `blob`, no lugar de `TextResourceContents`. Para enviar apenas um ponteiro que o cliente pode ler depois com `resources/read`, retorne um `ResourceLink(name=..., uri=...)`; ele também é um bloco de conteúdo.
## Ícones {#icons}
Um `Icon` é metadado, não conteúdo. Ele não carrega a imagem; aponta para uma por meio de uma URI, e um cliente pode buscá-la e mostrá-la ao lado do nome do seu servidor, de uma ferramenta, de um recurso ou de um prompt.
@ -115,6 +133,7 @@ Os ícones de uma ferramenta ficam no objeto `Tool` de `tools/list`; os de um re
* Retorne uma `Image` ou um `Audio` de uma ferramenta e o cliente recebe um bloco `ImageContent` / `AudioContent`: seus bytes codificados em base64, com um tipo MIME.
* Monte um a partir de um `path=` e deixe o sufixo decidir o tipo MIME, ou a partir de `data=` em memória mais um `format=` explícito.
* Retorne um `EmbeddedResource` para colocar um documento (texto ou um blob em base64, com sua URI e tipo MIME) no resultado, ou um `ResourceLink` para enviar só o ponteiro.
* Resultados de mídia não trazem `structured_content` nem schema de saída.
* Um `Icon` é um ponteiro: uma URI `src` mais `mime_type`, `sizes` e `theme` opcionais.
* `icons=[...]` funciona no servidor, em ferramentas, em recursos e em prompts, e os clientes os encontram nos objetos correspondentes.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, ffe823cb0fedd488, f33651add1b59094]
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, d30d3c20168b88b2, f5ef38dad59d6f76, 6e38a699ba57fbdf, 2b984a3bf37a0ddd]
tool: 1
---
# Prompts {#prompts}
@ -139,10 +139,55 @@ A entrada em `prompts/list` agora traz tudo de que um cliente precisa para desen
```
!!! info
Se você leu **[Ferramentas](tools.md)**, já sabe tudo o que está nesta página. O mesmo decorador, a mesma
Se você leu **[Ferramentas](tools.md)**, já sabe tudo até este ponto. O mesmo decorador, a mesma
docstring como descrição, o mesmo `Annotated`/`Field`. As únicas coisas que mudam são quem
dispara (o usuário) e para onde vai o resultado (para a conversa).
## Mais do que texto {#more-than-text}
`UserMessage` e `AssistantMessage` também aceitam um bloco de conteúdo, ou um helper `Image` / `Audio`, onde quer que aceitem uma `str`. Dois casos aparecem em prompts: anexar um documento e anexar uma imagem.
### Incorporando um arquivo {#embedding-a-file}
```python title="server.py" hl_lines="5 12 21 23"
--8<-- "docs_src/prompts/tutorial004.py"
```
* O guia de estilo é um recurso em `style://python` (**[Recursos](resources.md)** trata deles), lido de um `style-guide.md` ao lado de `server.py`. Coloque qualquer arquivo Markdown ali.
* `EmbeddedResource(resource=TextResourceContents(...))`, ambos de `mcp.types`, carrega o arquivo com sua URI e seu tipo MIME como a primeira mensagem; a instrução que faz referência a ele vem em seguida, como texto simples.
* Incorporar, em vez de colar o guia na f-string, permite que o cliente o mostre como um anexo e reabra `style://python` depois, e o modelo recebe o arquivo na íntegra. Para um arquivo binário, use `BlobResourceContents` com um `blob` em base64.
Renderizada, o `content` da primeira mensagem é um bloco `resource`:
```json
{"type": "resource", "resource": {"uri": "style://python", "mimeType": "text/markdown", "text": "* Prefer early returns.\n..."}}
```
### Anexando uma imagem {#attaching-an-image}
```python title="server.py" hl_lines="4 15"
--8<-- "docs_src/prompts/tutorial005.py"
```
* `Image` é o helper de **[Imagens, áudio e ícones](media.md)**. `UserMessage` o converte em um bloco `ImageContent` (o arquivo codificado em base64, o tipo MIME deduzido a partir de `.png`) quando o prompt é renderizado; `Audio` vira um `AudioContent` do mesmo jeito.
* Coloque qualquer PNG chamado `architecture.png` ao lado de `server.py`. Os argumentos de prompt são strings, então a imagem sempre vem do servidor; `component` só fornece as palavras.
```json
{"type": "image", "data": "iVBORw0KGgoAAAANSUhEUg...", "mimeType": "image/png"}
```
## Mudando a lista em tempo de execução {#changing-the-list-at-runtime}
Prompts podem ser adicionados enquanto clientes estão conectados, por exemplo para deixar um usuário salvar uma instrução como uma entrada de menu própria. Registre o prompt e depois notifique:
```python title="server.py" hl_lines="5 23-27"
--8<-- "docs_src/prompts/tutorial006.py"
```
* `mcp.add_prompt(Prompt.from_function(fn, name=..., description=...))` registra uma função exatamente como `@mcp.prompt()` faria, e `mcp.remove_prompt(name)` é o inverso. `add_prompt` mantém uma entrada existente com o mesmo nome em vez de sobrescrevê-la, então a ferramenta remove qualquer entrada antiga primeiro para que salvar seja uma substituição. `prompts/list` reflete a mudança imediatamente.
* `await ctx.notify_prompts_changed()` envia `notifications/prompts/list_changed` a todo cliente `2026-07-28` escutando em um stream `subscriptions/listen` (**[Assinaturas](../handlers/subscriptions.md)**). `await ctx.session.send_prompt_list_changed()` envia ao cliente que fez a chamada quando esse cliente é anterior a 2026 (**[Atendendo clientes legados](../run/legacy-clients.md)**). Chame os dois; cada um não faz nada quando não há ninguém para avisar.
* Um cliente que recebe a notificação chama `prompts/list` de novo. No `Client` Python isso é `async with client.listen(prompts_list_changed=True) as sub:`, que produz um evento `PromptsListChanged`.
## Recapitulando {#recap}
* `@mcp.prompt()` em uma função faz dela um prompt. O nome vem da função, a descrição vem da docstring.
@ -151,5 +196,7 @@ A entrada em `prompts/list` agora traz tudo de que um cliente precisa para desen
* Retorne uma `str` e ela vira uma mensagem de usuário. Retorne uma lista de `UserMessage` / `AssistantMessage` para iniciar uma conversa de vários turnos.
* `title=` e `Field(description=...)` são o que um cliente coloca na interface dele.
* Um argumento obrigatório ausente faz a requisição inteira falhar. Não existe um resultado de erro por prompt.
* Embrulhe um `EmbeddedResource` ou um `Image` em uma `UserMessage` para anexar um documento ou uma imagem.
* Adicione ou remova prompts em tempo de execução com `mcp.add_prompt(...)` / `mcp.remove_prompt(...)`, e depois `await ctx.notify_prompts_changed()` e `await ctx.session.send_prompt_list_changed()`.
O autocomplete do lado do servidor para os argumentos de um prompt (ou de um template de recurso) é assunto de **[Completions](completions.md)**.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, e866c192e11d1c14, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c6f7d2a148aa49f4, c851964bb3301907, d715db6f8dccc9cc, ef86634aa70498a7]
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, 91be9b73602abcf1, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c7eff2a5698225fa, c851964bb3301907, 8f296f1f09e4c400, d715db6f8dccc9cc, a0c344a48450dbe4]
tool: 1
---
# Saída estruturada {#structured-output}
@ -105,7 +105,7 @@ Nem toda forma merece uma classe. Um `TypedDict` produz o mesmo schema:
--8<-- "docs_src/structured_output/tutorial003.py"
```
Um `TypedDict` é um `dict` comum em tempo de execução, então é isso que você monta e retorna. O schema, a validação e o `structured_content` são idênticos aos da versão com `BaseModel` (menos as descrições, para as quais o `TypedDict` não tem lugar).
Um `TypedDict` é um `dict` comum em tempo de execução, então é isso que você monta e retorna. O schema, a validação e o `structured_content` seguem as mesmas regras da versão com `BaseModel`: adicione uma docstring à classe ou `Annotated[..., Field(description=...)]` e elas viram as descrições, e uma chave `NotRequired` que você deixa de fora do dict fica de fora do `structured_content`.
## Uma dataclass {#a-dataclass}
@ -187,18 +187,19 @@ Você não percebe enquanto monta o valor à mão: o Pydantic já garantiu que o
A anotação promete `WeatherData`. A resposta do serviço upstream parou de enviar `humidity`.
!!! check
Chame `get_weather` e ela não entrega discretamente ao cliente um objeto pela metade. A chamada falha,
e as primeiras linhas do erro dão o nome do campo:
Chame `get_weather` e ela não entrega discretamente ao cliente um objeto pela metade. A chamada falha:
o cliente recebe `is_error=True` com `Error executing tool get_weather`, então o modelo sabe que a
chamada falhou em vez de ler, com toda a confiança, um clima que não existe. O nome do campo é para você,
no log do servidor em nível `ERROR`:
```text
Error executing tool get_weather: 1 validation error for WeatherData
Tool 'get_weather' raised an unexpected exception
...
pydantic_core._pydantic_core.ValidationError: 1 validation error for WeatherData
humidity
Field required [type=missing, input_value={'temperature': 16.2, 'conditions': 'Overcast'}, input_type=dict]
```
Esse texto volta como o resultado da ferramenta com `is_error=True`, então o modelo sabe que a chamada
falhou em vez de ler, com toda a confiança, um clima que não existe.
Retornar um `dict` comum de uma ferramenta `-> WeatherData` não tem problema, aliás. É exatamente isso que `json.loads` produziu. A validação é feita sobre o valor, não sobre o tipo Python.
## Desativando {#opting-out}
@ -213,6 +214,10 @@ Sem `output_schema`, sem wrapper, sem validação. `structured_content` é `None
O oposto, `structured_output=True`, transforma a detecção automática em exigência: uma ferramenta cujo tipo de retorno não consegue produzir um schema levanta uma exceção no momento do import em vez de recair para texto.
## Blocos de conteúdo e mídia {#content-blocks-and-media}
Blocos de conteúdo e mídia (`TextContent`, `EmbeddedResource`, `Image`, `Audio` e companhia, sozinhos, como itens de uma `list`, `tuple` ou `Sequence`, ou como membros de uma união) já ficam de fora para você: eles são para o modelo ler, então a detecção automática não deriva nenhum schema deles (**[Imagens, áudio e ícones](media.md)** cobre `Image` e `Audio`). `structured_output=True` ainda força um para as classes de bloco de conteúdo.
## Uma classe sem anotações de tipo {#a-class-without-type-hints}
Existe um jeito de acabar sem estrutura sem ter pedido por isso: retornar uma classe que **não tem anotações no corpo**.
@ -245,6 +250,6 @@ Existe um jeito de acabar sem estrutura sem ter pedido por isso: retornar uma cl
* Escalares, listas, tuplas e uniões são envolvidos em `{"result": ...}`. Modelos, `TypedDict`s, dataclasses, classes anotadas e `dict[str, ...]` já são objetos e ficam como estão.
* Todo resultado carrega `content` (texto, para o modelo) **e** `structured_content` (dados, para a aplicação).
* O que você retorna é validado contra o schema. Uma divergência vira um erro de ferramenta, não um resultado corrompido.
* `structured_output=False` deixa uma ferramenta de fora. Uma classe sem anotações de tipo fica de fora em silêncio; fique atento a isso.
* `structured_output=False` deixa uma ferramenta de fora. Blocos de conteúdo, `Image` e `Audio` ficam de fora por padrão; uma classe sem anotações de tipo fica de fora em silêncio, então fique atento a isso.
Agora você domina tudo o que uma ferramenta pode dizer de volta. A seguir, a segunda primitiva: **[Recursos](resources.md)**.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [e4cc390d56573409, 8566e2b68594e9ad, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
sections: [e4cc390d56573409, f30cf8103a6e918c, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
tool: 1
---
# Ferramentas {#tools}
@ -39,6 +39,8 @@ A partir dessas anotações de tipo, o SDK gera um JSON Schema e o envia ao clie
Os dois argumentos estão em `required` porque nenhum deles tem valor padrão. Você vai resolver isso daqui a pouco. (As chaves `title` são artefatos do Pydantic; as propriedades, seus tipos e `required` são o contrato.)
Também não há chave `$schema`: o MCP trata um schema sem ela como **JSON Schema 2020-12**, que é o que o Pydantic gera. Então não há nada para escolher até você escrever schemas à mão no **[Server de baixo nível](../advanced/low-level-server.md#the-dialect-is-json-schema-2020-12)**.
!!! tip
Aqui, as anotações de tipo não são documentação. Elas são **o contrato**. Se um cliente enviar `"limit": "ten"`,
o SDK rejeita isso antes mesmo de a sua função executar.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 101ef9d14bf4ec46, 4b6c4a845438abc7, f98b46bafbee4acd]
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 317f4256a650cab6, 4b6c4a845438abc7, f98b46bafbee4acd]
tool: 1
---
# Templates de URI e segurança de caminhos {#uri-templates-and-path-safety}
@ -174,7 +174,7 @@ conhecer o limite do seu sandbox. Para acesso ao sistema de arquivos,
use `safe_join` para resolver o caminho e verificar que ele continua
dentro do seu diretório base:
```python title="server.py" hl_lines="4 14"
```python title="server.py" hl_lines="5 15"
--8<-- "docs_src/uri_templates/tutorial002.py"
```
@ -216,9 +216,10 @@ de arquivos, `safe_join` continua sendo a fronteira de contenção.
!!! tip
Se o seu handler não consegue atender à requisição (o arquivo não
existe, o id é desconhecido), levante uma exceção. O SDK a transforma
em uma resposta de erro. Veja **[Tratamento de erros](handling-errors.md)** para a
diferença entre um erro de protocolo e um erro de ferramenta.
existe, o id é desconhecido), levante `ResourceNotFoundError`, como
`read_manual` faz acima. O cliente recebe `-32602` com a sua mensagem
e a URI. Já uma exceção inesperada vira um `-32603` genérico. Veja
**[Tratamento de erros](handling-errors.md#a-resource-that-doesnt-exist)**.
## Recursos no Server de baixo nível {#resources-on-the-low-level-server}

View file

@ -1,6 +1,6 @@
---
translation:
sections: [2efaecdef109a5c5, fcacd3e66b8635a4, 25323d737dcf0261, 4835ed1772f1d113, 137454d469c867f5, 6392596bd6df54f0, 41126fa9c4fe432f, 480b6d7897e30ab4, d83bb682e708dde0, ebbed3449c499db4, 323ef84f6b4bebde, 30fd31be74169d9a, 656943c6cb567218, c2dc3b1007d2e987, 7cf5386b997d04e9, 0b59feed8384456e, 0cba47bae78d04eb, 954dc21efdb532a3]
sections: [2efaecdef109a5c5, fcacd3e66b8635a4, 25323d737dcf0261, 8a6e351ec756904d, 137454d469c867f5, 6392596bd6df54f0, 41126fa9c4fe432f, 480b6d7897e30ab4, d83bb682e708dde0, ebbed3449c499db4, 323ef84f6b4bebde, 30fd31be74169d9a, 656943c6cb567218, c2dc3b1007d2e987, 7cf5386b997d04e9, 0b59feed8384456e, 0cba47bae78d04eb, e4355f4c7cf4fb2e]
tool: 1
---
# Solução de problemas {#troubleshooting}
@ -81,11 +81,11 @@ async def main() -> None:
`__aexit__` é a desconexão, e é por isso que não existe um `client.close()` para esquecer. **[Testes](get-started/testing.md)** se baseia exatamente nesse padrão.
## `Error executing tool <name>: <message>` e `Unknown tool: <name>` {#error-executing-tool-name-message-and-unknown-tool-name}
## `Error executing tool <name>: <message>`, `Error executing tool <name>` e `Unknown tool: <name>` {#error-executing-tool-name-message-error-executing-tool-name-and-unknown-tool-name}
Você está lendo um **resultado**, não uma exceção. `call_tool` não lançou exceção, e nunca vai lançar para uma ferramenta que falha.
Chame `forecast` para uma cidade que o servidor não conhece, e a exceção que ela lança volta com a requisição marcada como *bem-sucedida*:
Chame `forecast` para uma cidade que o servidor não conhece, e o `ToolError` que ela lança volta com a requisição marcada como *bem-sucedida*:
```python
result.is_error # True
@ -97,6 +97,8 @@ result.structured_content # None
A correção está no seu cliente: **verifique `result.is_error`**. Um `try/except` em volta de `call_tool` não captura nenhum desses, porque não há nada para capturar. Isso é proposital, e é a coisa mais útil desta página para internalizar: foi o *modelo* que escolheu a chamada, então é o modelo que recebe a mensagem e uma chance de tentar de novo. **[Tratamento de erros](servers/handling-errors.md)** tem a história completa, incluindo o caminho do `MCPError` que *de fato* lança.
A forma seca, `Error executing tool <name>` sem mensagem nenhuma, significa que a ferramenta **quebrou**: uma exceção que ela não previu escapou dela (ou o valor de retorno não passou no schema de saída), e o texto dessa exceção fica fora da rede. O traceback está no **log do servidor** em `ERROR`, como `Tool '<name>' raised an unexpected exception`.
## `TypeError: The @tool decorator was used incorrectly. Did you forget to call it? Use @tool() instead of @tool` {#typeerror-the-tool-decorator-was-used-incorrectly-did-you-forget-to-call-it-use-tool-instead-of-tool}
Você escreveu `@mcp.tool` em vez de `@mcp.tool()`. `tool()` é uma *fábrica* de decoradores: sem os parênteses, o Python entrega a sua função ao parâmetro `name=` dela.
@ -409,7 +411,7 @@ mcp = MCPServer("Weather", request_state_security=RequestStateSecurity(keys=[key
## Recapitulando {#recap}
* `ExceptionGroup: unhandled errors in a TaskGroup` nunca é o erro. Leia a **última linha**; capturar `MCPError` *dentro* do bloco `async with Client(...)` pula o embrulho por completo.
* `call_tool` não lança exceção para uma ferramenta que falha. `Error executing tool ...` e `Unknown tool: ...` são resultados: verifique `result.is_error`.
* `call_tool` não lança exceção para uma ferramenta que falha. `Error executing tool ...` e `Unknown tool: ...` são resultados: verifique `result.is_error`. Nenhuma mensagem depois do nome da ferramenta significa que ela quebrou, e o traceback está no log do servidor.
* `Client must be used within an async context manager` -> use `async with`. `Use @tool() instead of @tool` -> adicione os parênteses.
* `Tool already exists:` no log do servidor é o único sinal de que duas ferramentas com o mesmo nome viraram uma só.
* Um 421, três grafias: `Server returned an error response` (o `Client` python), `421 Misdirected Request` / `Invalid Host header` (todo o resto), `Invalid Host header: <host>` (o log do servidor). Correção: `transport_security=TransportSecuritySettings(allowed_hosts=[...])`.

View file

@ -1,6 +1,6 @@
---
translation:
sections: [cfe01c0c5863dfa2, 11d93f1fa09eadf5, a7392996acf1ad8f, 875eb2889263424e]
sections: [cfe01c0c5863dfa2, 1c58c5cfcc37d455, a7392996acf1ad8f, 875eb2889263424e]
tool: 1
---
# O que há de novo na v2 {#whats-new-in-v2}
@ -47,9 +47,9 @@ A v1 entregava três camadas aninhadas: um gerenciador de contexto de transporte
--8<-- "docs_src/client/tutorial001.py"
```
`Client` recebe um objeto de servidor (em memória, sem transporte: é o cenário dos testes), uma URL (Streamable HTTP) ou qualquer gerenciador de contexto de transporte, como `stdio_client(...)`. Entrar no `async with` conecta e negocia a versão do protocolo, seja qual for a era que o servidor fale; `client.server_capabilities` e `client.protocol_version` simplesmente estão lá depois disso, e `client.server_info` também, quando o servidor se identifica (agora ele é `Implementation | None`, já que na era 2026 a identidade é opcional). Os callbacks de amostragem e de elicitação que você registrou na v1 continuam funcionando (o corpo deles passa pela mesma renomeação de atributos para snake_case que todo o resto desta página), agora também respondem às requisições-dentro-de-resultados no estilo 2026 (abaixo), e rodam de forma concorrente em vez de um por vez. `ClientSession` continua por baixo para quem quer a superfície de baixo nível, e `client.session` a entrega para você; ela também mudou (roda sobre o novo motor de dispatcher, e algumas das próprias assinaturas dela mudaram), então leia o **[Guia de migração](migration.md#clientsession-now-runs-on-jsonrpcdispatcher-basesession-removed)** antes de descer de nível.
`Client` recebe um objeto de servidor (em memória, sem transporte: é o cenário dos testes), uma URL (Streamable HTTP), um `StdioServerParameters` (um subprocesso stdio) ou qualquer outro gerenciador de contexto de transporte, como `sse_client(...)`. Entrar no `async with` conecta e negocia a versão do protocolo, seja qual for a era que o servidor fale; `client.server_capabilities` e `client.protocol_version` simplesmente estão lá depois disso, e `client.server_info` também, quando o servidor se identifica (agora ele é `Implementation | None`, já que na era 2026 a identidade é opcional). Os callbacks de amostragem e de elicitação que você registrou na v1 continuam funcionando (o corpo deles passa pela mesma renomeação de atributos para snake_case que todo o resto desta página), agora também respondem às requisições-dentro-de-resultados no estilo 2026 (abaixo), e rodam de forma concorrente em vez de um por vez. `ClientSession` continua por baixo para quem quer a superfície de baixo nível, e `client.session` a entrega para você; ela também mudou (roda sobre o novo motor de dispatcher, e algumas das próprias assinaturas dela mudaram), então leia o **[Guia de migração](migration.md#clientsession-now-runs-on-jsonrpcdispatcher-basesession-removed)** antes de descer de nível.
**[O Client](client/index.md)** o apresenta, **[Transportes do cliente](client/transports.md)** cobre as três formas de conexão, **[Callbacks do cliente](client/callbacks.md)** cobre os callbacks em si, e **[Testes](get-started/testing.md)** mostra o padrão em memória que substitui o helper `create_connected_server_and_client_session()` da v1.
**[O Client](client/index.md)** o apresenta, **[Transportes do cliente](client/transports.md)** cobre as quatro formas de conexão, **[Callbacks do cliente](client/callbacks.md)** cobre os callbacks em si, e **[Testes](get-started/testing.md)** mostra o padrão em memória que substitui o helper `create_connected_server_and_client_session()` da v1.
### O `Server` de baixo nível foi reconstruído, não renomeado {#the-low-level-server-was-rebuilt-not-renamed}
@ -135,7 +135,7 @@ Nesses tipos, todo atributo Python agora é snake_case: `result.is_error`, `tool
As renomeações se anunciam sozinhas. Estas aqui, não:
* **Funções síncronas rodam em uma thread de trabalho.** Uma ferramenta `def` (ou recurso, prompt ou resolvedor) não bloqueia mais o loop de eventos; a contrapartida é que o corpo dela não roda mais *na* thread do loop de eventos, o que importa para código com afinidade de thread. Handlers `async def` ficam intocados. **[Guia de migração](migration.md#sync-handler-functions-now-run-on-a-worker-thread)**.
* **`MCPError` (o `McpError` da v1) lançado dentro de uma ferramenta agora é um erro de protocolo.** O modelo nunca o vê. Toda outra exceção continua virando um resultado `is_error=True` que o modelo pode ler e ao qual pode reagir. **[Tratando erros](servers/handling-errors.md)** explica a divisão.
* **`MCPError` (o `McpError` da v1) lançado dentro de uma ferramenta agora é um erro de protocolo.** O modelo nunca o vê. Toda outra exceção continua virando um resultado `is_error=True`, mas só a mensagem de um `ToolError` chega ao modelo: qualquer outra exceção agora aparece como `Error executing tool <name>`, com o traceback no log do seu servidor. **[Tratando erros](servers/handling-errors.md)** explica a divisão.
* **Os resultados são validados antes de sair.** Uma `Tool` montada à mão cujo `input_schema` é `{}` agora falha em `tools/list` (a especificação exige `"type": "object"`). Servidores construídos com `@mcp.tool()` nunca veem isso; o SDK escreve os schemas deles.
* **O seu cliente valida o que recebe.** `list_tools()` e `call_tool()` conferem a resposta do servidor contra a versão de protocolo negociada, então um servidor quase válido que o parsing tolerante da v1 aceitava agora lança `pydantic.ValidationError`. Se você se conecta a servidores que não controla, espere ser você quem os descobre; o **[Guia de migração](migration.md#client-validates-inbound-traffic-against-the-protocol-schema)** tem os detalhes.
* **Templates de URI agora são RFC 6570 de verdade.** `{+path}`, `{?query}` e companhia funcionam, a correspondência é exata em vez de frouxa à base de regex, e path traversal nos valores extraídos é rejeitado por padrão. Templates mais rígidos falham no momento da decoração, não na primeira requisição. **[Templates de URI](servers/uri-templates.md)**.