docs: refresh translations for recent English changes (#3379)
This commit is contained in:
parent
d8b638304e
commit
c5d7d0b6d0
6 changed files with 3149 additions and 1047 deletions
|
|
@ -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)**.
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)**.
|
||||
|
|
|
|||
|
|
@ -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)**.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue