docs: publish translated docs in twelve languages and the tool that maintains them (#3280)

This commit is contained in:
Max 2026-08-14 17:07:34 +01:00 • committed by GitHub
commit 52ad0a8876
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 89737 additions and 54 deletions

View file

@ -0,0 +1,130 @@
---
translation:
sections: [72f9c964769076dd, 9a2c14e10935b515, 235299eb78ab12d7, 8aee1e78c8237fb8, 9bd86acd4112138f, 55343cb7f250dc7b]
tool: 1
---
# Completions {#completions}
Um cliente que monta uma UI em cima do seu servidor quer autocompletar os valores dos argumentos enquanto o usuário digita: nomes de linguagens, nomes de repositórios, caminhos de arquivo.
É com as **completions** que o seu servidor fornece essas sugestões.
## Algo que valha a pena completar {#something-worth-completing}
As completions se aplicam a exatamente duas coisas: os argumentos de um **prompt** e os parâmetros de um **template de recurso**. Então comece com um servidor que tenha um de cada:
```python title="server.py" hl_lines="6 12"
--8<-- "docs_src/completions/tutorial001.py"
```
Ainda não há nada de completions aqui.
* `review_code` recebe um `language`. O usuário não deveria precisar adivinhar quais grafias você aceita.
* `github_repo` recebe um `owner` e um `repo`. Campos de texto livre para os dois resultam em um formulário ruim.
## O handler de completion {#the-completion-handler}
Adicione **uma** função decorada com `@mcp.completion()`:
```python title="server.py" hl_lines="21-29"
--8<-- "docs_src/completions/tutorial002.py"
```
* Existe um handler por servidor. Toda requisição de completion chega aqui, e você ramifica de acordo com o que está sendo completado.
* Ele precisa ser `async def`: o SDK faz o await dele.
* Ele recebe três argumentos:
* `ref`: *qual* prompt ou template de recurso, como um `PromptReference` ou um `ResourceTemplateReference`. É com `isinstance` que você distingue um do outro.
* `argument`: `argument.name` é o argumento que está sendo completado, `argument.value` é o que o usuário digitou até agora.
* `context`: os argumentos já resolvidos. Ignore-o por enquanto.
* Você retorna um `Completion(values=[...])`, ou `None` quando não tem nada a oferecer.
!!! tip
`argument.value` é o prefixo que o usuário digitou. O SDK **não** filtra para você: o que
você colocar em `values` é o que a UI mostra. O `startswith` é você quem escreve.
### Experimente {#try-it}
Use o `Client` em memória de **[Testes](../get-started/testing.md)** para exercitá-lo. Chame
`client.complete()` com `ref=PromptReference(name="review_code")` e
`argument={"name": "language", "value": "py"}`:
```python
result.completion.values # ['python']
```
* `ref` é o mesmo tipo de referência que o seu handler recebe.
* `argument` é um dict simples com exatamente duas chaves, `name` e `value`.
Envie um `value` vazio e você recebe a lista inteira de volta. `lang.startswith("")` é verdadeiro para toda linguagem:
```python
result.completion.values # ['go', 'javascript', 'python', 'rust', 'typescript']
```
Pergunte sobre `code` (um argumento que o seu handler não reconhece) e ele retorna `None`, que o SDK transforma em uma lista vazia:
```python
result.completion.values # []
```
`None` significa *"sem sugestões"*, nunca um erro. A UI recorre a uma caixa de texto simples.
## Uma capacidade que você nunca declarou {#a-capability-you-never-declared}
Registrar o handler é a declaração. Conecte um cliente e veja:
```python
client.server_capabilities.completions # CompletionsCapability()
```
Você não listou `completions` em lugar nenhum. O SDK viu o handler e declarou a capacidade por você. Toda capacidade *opcional* funciona assim: o handler é a declaração. (As três primitivas não são opcionais: o `MCPServer` sempre as declara, com ou sem handlers.)
!!! check
Volte ao primeiro `server.py` (aquele sem handler) e pergunte mesmo assim. A chamada falha
com um erro JSON-RPC:
```text
Method not found
```
E `client.server_capabilities.completions` é `None`. É para isso que a capacidade existe: um
cliente bem-comportado a confere e nunca envia uma requisição que você não tem como responder.
## Argumentos dependentes {#dependent-arguments}
`github://repos/{owner}/{repo}` tem dois parâmetros, e os valores úteis para `repo` dependem de qual `owner` foi escolhido antes.
É para isso que serve o `context`. Ele carrega os argumentos que o usuário **já resolveu**:
```python title="server.py" hl_lines="8-11 34-38"
--8<-- "docs_src/completions/tutorial003.py"
```
* O novo ramo é acionado para o parâmetro `repo` do template.
* `context.arguments` é um `dict[str, str] | None` com os valores escolhidos até agora (aqui, `owner`).
* Sem `owner` ainda, não há sugestões que façam sentido, então o handler retorna `None`.
O cliente envia esses valores resolvidos com `context_arguments=`. Desta vez, `ref` é um
`ResourceTemplateReference(uri="github://repos/{owner}/{repo}")`. Peça `repo` com um
`value` vazio e passe `context_arguments={"owner": "modelcontextprotocol"}`:
```python
result.completion.values # ['python-sdk', 'typescript-sdk', 'inspector']
```
Tire o `context_arguments=` e a mesma chamada retorna `[]`. O handler não tem como saber quais repositórios oferecer antes de saber quem é o owner.
!!! info
`Completion` também aceita `total=` e `has_more=`. Defina-os quando `values` for uma fatia de uma
lista maior, para que a UI possa mostrar *"e mais 200"*. A maioria dos handlers nunca precisa deles.
## Recapitulando {#recap}
* Completions são sugestões para **argumentos de prompt** e **parâmetros de template de recurso**. Nada mais.
* `@mcp.completion()` registra o único handler. Ele é `async def (ref, argument, context) -> Completion | None`.
* Ramifique com base em `isinstance(ref, ...)` e em `argument.name`. Filtre por `argument.value` você mesmo.
* `None` vira uma lista vazia. Nunca é um erro.
* `context.arguments` guarda os valores já resolvidos; o cliente os fornece como `context_arguments=`.
* A capacidade `completions` aparece no momento em que você registra o handler. Sem ele, a requisição dá `Method not found`.
As sugestões ajudam enquanto o usuário ainda está *preenchendo* um prompt ou template; para fazer uma pergunta a ele no *meio* de uma chamada de ferramenta, o que você quer é a **[Elicitação](../handlers/elicitation.md)** (elicitation). Tudo o que uma ferramenta pode retornar além de texto está em **[Imagens, áudio e ícones](media.md)**.

View file

@ -0,0 +1,139 @@
---
translation:
sections: [e33d441f12d50535, 7099694c603e0f5f, c1df4cf9673433e6, c9cd294541422e6e, 6cec073617bfd037, efa92b8f99e908c8, 6a22a29e27fb4601]
tool: 1
---
# Tratando erros {#handling-errors}
Uma ferramenta (tool) pode falhar de duas maneiras, e o SDK trata cada uma de forma bem diferente.
Lance uma exceção comum e é o **modelo** que a vê. Lance `MCPError` e é o **protocolo** que a vê.
Esta página é sobre essa escolha.
## Um erro que o modelo consegue corrigir {#an-error-the-model-can-fix}
Pegue uma ferramenta que faz uma consulta e deixe a consulta não encontrar nada:
```python title="server.py" hl_lines="11-12"
--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.
Chame a ferramenta com um título que não está no catálogo e veja o resultado:
```python
result.is_error # True
result.content # [TextContent(text="Error executing tool get_author: No book titled 'Nothing' in the catalog.")]
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ê.
* `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.
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.
!!! 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.
Use `raise`. A flag é o sinal.
## Um erro que o modelo não consegue corrigir {#an-error-the-model-cannot-fix}
Agora troque `ValueError` por `MCPError`.
```python title="server.py" hl_lines="1 3 14"
--8<-- "docs_src/handling_errors/tutorial002.py"
```
`MCPError` é o **erro de protocolo** do SDK. É a única exceção que o wrapper da ferramenta *não* captura: ela se propaga, e a requisição `tools/call` inteira falha com um erro JSON-RPC em vez de um resultado.
```json
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog."
}
```
* **Não há resultado**. Sem `content`, sem `is_error`: nada para o modelo ler.
* Quem recebe o erro é a aplicação **host**, do mesmo jeito que receberia se a ferramenta nem existisse.
* `code`, `message` e `data` chegam intactos. `INVALID_PARAMS` é `-32602`; `mcp.types` exporta esse e os outros códigos de erro JSON-RPC (`INVALID_REQUEST`, `INTERNAL_ERROR`, ...) como constantes, para que você nunca precise digitar um número mágico.
!!! check
Mesma consulta, mesma falha, mas agora a chamada *lança* a exceção no lado do cliente em vez de retornar:
```text
mcp.shared.exceptions.MCPError: No book titled 'Nothing' in the catalog.
```
A primeira versão entregou ao modelo uma frase à qual ele podia reagir. Esta não entrega nada.
Para `get_author` isso é estritamente pior, e é esse o ponto da próxima seção.
## Qual delas lançar {#which-one-to-raise}
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 `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`.
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.
!!! info
`MCPError` fica em `from mcp import MCPError` e recebe `code`, `message` e um payload
`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.
## 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.
```python title="server.py" hl_lines="2 13"
--8<-- "docs_src/handling_errors/tutorial003.py"
```
`books://{title}` é um **template**. Ele casa com *qualquer* título, então "a URI está bem formada" e "o livro existe" são duas perguntas diferentes, e só a sua função consegue responder à segunda.
Quando não consegue, lance `ResourceNotFoundError`. O SDK a transforma no erro de protocolo que a especificação atribui a um recurso ausente: `-32602` com a URI requisitada em `data`, para que o cliente saiba *qual* leitura falhou.
```json
{
"code": -32602,
"message": "No book titled 'Nothing' in the catalog.",
"data": {"uri": "books://Nothing"}
}
```
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)**.
## Erros que você nunca lança {#errors-you-never-raise}
Um argumento inválido nunca chega à sua função.
Mande para `get_author` um `title` que não seja uma string e o SDK o rejeita com base no schema de entrada **antes** de chamar você, como o mesmo tipo de erro de ferramenta com `is_error=True` que o modelo consegue ler e corrigir. **[Ferramentas](tools.md)** mostra a mesma rejeição com uma restrição `Field(le=50)`.
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.
## 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 **`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`.
* `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`.
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)**.
O texto exato dos erros do SDK que você tem mais chance de encontrar, o que cada um significa e a correção de um passo só para cada um estão em **[Solução de problemas](../troubleshooting.md)**.

View file

@ -0,0 +1,35 @@
---
translation:
sections: [09defc170a0da89d]
tool: 1
---
# Servidores {#servers}
Um `MCPServer` expõe três primitivas a um cliente conectado. O que as
distingue é quem decide usá-las:
* Uma **[ferramenta](tools.md)** (tool) é uma ação que o *modelo* escolhe e chama. Esta é
a página que a maioria das pessoas procura primeiro, e
**[Saída estruturada](structured-output.md)** é a referência que a acompanha:
tudo sobre o formato do que uma ferramenta retorna.
* Um **[recurso](resources.md)** é um dado somente leitura que a *aplicação*
escolhe ler. **[Templates de URI](uri-templates.md)** é a referência que o
acompanha: a sintaxe completa de endereçamento e as regras de segurança de caminhos.
* Um **[prompt](prompts.md)** é um template de mensagem que uma *pessoa* invoca pelo
nome, a partir de um menu ou de um comando de barra.
Em torno das três primitivas, o restante do que um servidor declara:
* **[Autocompletar](completions.md)** (completions) é o preenchimento automático, feito no
servidor, dos argumentos de prompts e de templates de recurso.
* **[Imagens, áudio e ícones](media.md)** cobre tudo o que uma ferramenta pode
retornar além de texto, e os ícones que um cliente mostra ao lado do seu servidor.
* **[Tratamento de erros](handling-errors.md)** explica a diferença entre um
erro do qual o modelo consegue se recuperar e um que ele nunca deve ver.
Cada página aqui é independente; vá direto para a que você precisa. Se você ainda não
construiu um servidor, comece antes por **[Primeiros passos](../get-started/first-steps.md)**.
O que acontece *dentro* das funções que você registra (o `Context`, a injeção de dependência,
pedir mais informações ao usuário no meio de uma chamada) é o assunto da próxima seção,
**[Dentro do seu handler](../handlers/index.md)**.

View file

@ -0,0 +1,122 @@
---
translation:
sections: [496394d24d221bf1, 4ceb4591180dc6c3, 0fd63e4682d02e0c, 969ede0bd3686a16, 043f526230dd243d, 6ee3e9bcfd24047a]
tool: 1
---
# Mídia {#media}
Texto não é a única coisa que uma ferramenta pode retornar.
O SDK traz dois helpers para resultados binários (**`Image`** e **`Audio`**) e um tipo **`Icon`** para dar uma cara ao seu servidor, às ferramentas, aos recursos e aos prompts na interface do cliente.
## Retornando uma imagem {#returning-an-image}
Anote o tipo de retorno como `Image`, aponte para um arquivo e retorne:
```python title="server.py" hl_lines="8 12 14"
--8<-- "docs_src/media/tutorial001.py"
```
* `Image` recebe exatamente um entre `path` (um arquivo a ser lido) ou `data` (bytes brutos).
* O tipo MIME que o cliente vê é inferido a partir do sufixo: `logo.png` é anunciado como `image/png`.
* Não há nada aqui específico de logos. Qualquer PNG ao lado de `server.py` funciona: um gráfico que seu código renderizou, um diagrama, uma foto.
`Image` é uma conveniência do SDK, não um tipo do protocolo. Na rede, o seu valor de retorno vira um bloco **`ImageContent`** (os bytes do arquivo codificados em base64, mais o tipo MIME):
```python
result.content # [ImageContent(type="image", data="iVBORw0KGgoAAAANSUhEUg...", mime_type="image/png")]
result.structured_content # None
```
Repare em duas coisas:
* `data` é base64. Você nunca tocou nos bytes; o SDK leu o arquivo e fez a codificação.
* `structured_content` é `None`. Uma `Image` é conteúdo para o modelo olhar, não dados para a aplicação interpretar: não há schema de saída. (Compare com **[Saída estruturada](structured-output.md)**, onde a anotação de retorno *é* o schema.)
!!! info
`ImageContent` e `AudioContent` ficam em `mcp.types`, bem ao lado do `TextContent`
em que um resultado `str` simples se transforma (**[Ferramentas](tools.md)**). O resultado de uma ferramenta é uma lista de blocos de conteúdo; `Image` e `Audio` são
o caminho mais curto para produzir os dois tipos binários.
### Experimente {#try-it}
Coloque qualquer PNG ao lado de `server.py`, dê a ele o nome `logo.png` e execute:
```console
uv run mcp dev server.py
```
Abra a aba **Tools** e chame `logo`. O resultado não é uma string: é um bloco de conteúdo `image`, e o Inspector renderiza sua imagem. Tudo o que aconteceu entre o arquivo no disco e os pixels na tela foi obra do SDK.
## Retornando áudio {#returning-audio}
`Audio` segue o mesmo molde. Mantenha `logo.png` onde estava e coloque qualquer WAV ao lado dele como `chime.wav`:
```python title="server.py" hl_lines="18-21"
--8<-- "docs_src/media/tutorial002.py"
```
O resultado é um bloco **`AudioContent`**:
```python
result.content # [AudioContent(type="audio", data="UklGR...", mime_type="audio/wav")]
result.structured_content # None
```
Funciona do mesmo jeito: entra um arquivo em disco, saem base64 e um tipo MIME, nenhum schema de saída.
## Bytes ou um arquivo {#bytes-or-a-file}
Os dois helpers também aceitam `data=` (bytes brutos) em vez de `path=`. Esse é o modo para bytes que nunca vieram de um arquivo próprio — uma coluna de banco de dados, uma resposta HTTP, algo que o Pillow acabou de desenhar:
```python title="server.py" hl_lines="14 15"
--8<-- "docs_src/media/tutorial003.py"
```
Com `path=` não há nada a declarar: o arquivo é lido quando o resultado é montado, e o tipo MIME é inferido a partir do sufixo:
* `Image`: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`.
* `Audio`: `.wav`, `.mp3`, `.ogg`, `.flac`, `.aac`, `.m4a`.
Um sufixo não reconhecido cai no padrão `application/octet-stream`.
!!! check
Com `data=` não há nome de arquivo, então não há de onde inferir nada. Esqueça o `format=` e
o SDK recorre a um padrão: `image/png` para imagens, `audio/wav` para áudio. Monte um
`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=`.
## Í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.
```python title="server.py" hl_lines="4-5 7 10 16"
--8<-- "docs_src/media/tutorial004.py"
```
* `src` é uma URI que o cliente consegue resolver: `https:`, ou uma URI `data:` se você quiser o ícone embutido, sem uma busca extra.
* `mime_type` e `sizes` (`"48x48"`, ou `"any"` para um formato escalável) permitem que o cliente escolha o certo quando você oferece vários.
* `theme="light"` ou `theme="dark"` marca um ícone para um único esquema de cores.
`MCPServer(...)`, `@mcp.tool()`, `@mcp.resource()` e `@mcp.prompt()` aceitam o mesmo argumento nomeado `icons=[...]`.
### Onde um cliente os vê {#where-a-client-sees-them}
Os ícones viajam junto com aquilo que decoram. Os do servidor chegam quando o cliente se conecta, em `client.server_info` (opcional em conexões da era 2026, então restrinja o tipo primeiro):
```python
assert client.server_info is not None # python-sdk servers identify themselves by default
client.server_info.icons # [Icon(src="https://example.com/brand-kit.png", mime_type="image/png", sizes=["48x48"])]
```
Os ícones de uma ferramenta ficam no objeto `Tool` de `tools/list`; os de um recurso, no `Resource` de `resources/list`; os de um prompt, no `Prompt` de `prompts/list`. O campo sempre se chama `icons`.
## Recapitulando {#recap}
* 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.
* 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.
Isso é tudo o que uma ferramenta pode colocar *dentro* de um resultado. O que acontece quando uma ferramenta *falha* (e quem deve ficar sabendo) está em **[Tratando erros](handling-errors.md)**.

View file

@ -0,0 +1,155 @@
---
translation:
sections: [d65c098f37f5b6c3, dd0c2724d6f2877e, 6835bb3570c6714c, ffe823cb0fedd488, f33651add1b59094]
tool: 1
---
# Prompts {#prompts}
Um **prompt** é um template de mensagem que o usuário escolhe.
Ferramentas são para o modelo. Um prompt é o oposto: o usuário escolhe um em um menu do seu cliente (um comando de barra, um botão), preenche os argumentos, e as mensagens renderizadas entram na conversa como se ele mesmo as tivesse digitado.
Para declarar um, coloque `@mcp.prompt()` em uma função que retorna o texto.
## Seu primeiro prompt {#your-first-prompt}
```python title="server.py" hl_lines="6-9"
--8<-- "docs_src/prompts/tutorial001.py"
```
O SDK lê as mesmas três coisas que lê de uma ferramenta:
* O **nome** é o nome da função: `review_code`.
* A **descrição** que o cliente exibe é a docstring: `Review a piece of code.`
* Os **argumentos** vêm dos parâmetros. `code` não tem valor padrão, então é obrigatório.
É isso que um cliente recebe de volta de `prompts/list`:
```json
{
"name": "review_code",
"description": "Review a piece of code.",
"arguments": [
{"name": "code", "required": true}
]
}
```
Não há JSON Schema aqui. Os argumentos de um prompt são uma lista plana de **strings nomeadas**: um formulário que uma pessoa preenche, não um payload que um modelo constrói.
### Renderizando {#rendering-it}
O cliente renderiza o template com `prompts/get`, passando os argumentos. Sua função executa e a `str` que você retorna vira **uma mensagem de usuário**:
```json
{
"description": "Review a piece of code.",
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Please review this code:\n\ndef add(a, b): return a + b"
}
}
],
"resultType": "complete"
}
```
Essa é a vida inteira de um prompt: listado pelo nome, renderizado sob demanda, colocado no chat.
!!! check
`required` é verificado antes que sua função execute. Renderize `review_code` sem `code` e a
própria requisição falha com um erro JSON-RPC (código `-32603`):
```text
mcp.shared.exceptions.MCPError: Internal server error
```
Não há um resultado de erro no estilo das ferramentas para devolver a um modelo, porque não há
nenhum modelo envolvido: a chamada levanta uma exceção. O motivo (`Missing required arguments: {'code'}`) vai parar no log do seu servidor.
### Experimente {#try-it}
Execute o servidor com o MCP Inspector:
```console
uv run mcp dev server.py
```
Abra a aba **Prompts** e selecione `review_code`. O Inspector desenha um formulário com um único campo obrigatório, `code`. Preencha, renderize e você recebe de volta exatamente a mensagem de usuário acima.
## Mais de uma mensagem {#more-than-one-message}
Uma revisão de código é uma mensagem só. Uma sessão de depuração é uma conversa, e um prompt pode iniciar a coisa toda.
Retorne uma lista de mensagens em vez de uma `str`:
```python title="server.py" hl_lines="2 13-20"
--8<-- "docs_src/prompts/tutorial002.py"
```
* `UserMessage` e `AssistantMessage` vêm de `mcp.server.mcpserver.prompts.base`. Passe uma `str` para elas e elas a embrulham em `TextContent` para você. O papel (role) é o nome da classe.
* `Message` é a base comum delas. Use-a como anotação de retorno.
Renderizar `debug_error` agora produz três mensagens, nesta ordem:
```json
{
"description": "Start a debugging conversation.",
"messages": [
{"role": "user", "content": {"type": "text", "text": "I'm seeing this error:"}},
{"role": "user", "content": {"type": "text", "text": "TypeError: 'int' object is not iterable"}},
{
"role": "assistant",
"content": {"type": "text", "text": "I'll help debug that. What have you tried so far?"}
}
],
"resultType": "complete"
}
```
Repare na última. Pré-preencher um turno de `assistant` é como você direciona a *próxima* resposta do modelo sem fazer o usuário digitar esse direcionamento por conta própria.
## Títulos e descrições dos argumentos {#titles-and-argument-descriptions}
`review_code` é um nome de função, não um rótulo. Dê ao cliente algo melhor para colocar no botão e descreva cada argumento para que o formulário se explique sozinho:
```python title="server.py" hl_lines="10-13"
--8<-- "docs_src/prompts/tutorial003.py"
```
* `title="Code review"` é o nome legível por humanos, exatamente como o `title` de uma ferramenta.
* `Annotated[str, Field(description=...)]` é o mesmo padrão que **[Ferramentas](tools.md)** usa para descrever os parâmetros de uma ferramenta. Aqui a descrição vai parar no argumento, e não em um schema.
* `language` tem um valor padrão, então deixa de ser obrigatório.
A entrada em `prompts/list` agora traz tudo de que um cliente precisa para desenhar um bom formulário:
```json
{
"name": "review_code",
"title": "Code review",
"description": "Review a piece of code.",
"arguments": [
{"name": "code", "description": "The code to review.", "required": true},
{"name": "language", "description": "The language the code is written in.", "required": false}
]
}
```
!!! info
Se você leu **[Ferramentas](tools.md)**, já sabe tudo o que está nesta página. 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).
## Recapitulando {#recap}
* `@mcp.prompt()` em uma função faz dela um prompt. O nome vem da função, a descrição vem da docstring.
* Prompts são **controlados pelo usuário**: o cliente os lista, o usuário escolhe um e preenche os argumentos.
* Os argumentos são uma lista plana de strings nomeadas (sem schema). Um parâmetro com valor padrão é opcional.
* 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.
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

@ -0,0 +1,146 @@
---
translation:
sections: [09df998c2a799f78, 0cf131146d16d4f9, 4e6b91e3f8025346, 8fe4eef576db17ed, 0d0d1ed43e3d0a53]
tool: 1
---
# Recursos {#resources}
Um **recurso** (resource) é um dado que você expõe para a aplicação ler.
A divisão é essa. Uma ferramenta é algo que o **modelo** decide chamar. Um recurso é algo que a **aplicação** decide carregar (um arquivo de configuração, um registro, um documento) e colocar diante do modelo como contexto.
Você declara um colocando `@mcp.resource(uri)` em uma função Python comum.
## Seu primeiro recurso {#your-first-resource}
```python title="server.py" hl_lines="6-8"
--8<-- "docs_src/resources/tutorial001.py"
```
Tem o mesmo formato de uma ferramenta, com uma coisa a mais: a **URI**. Recursos têm endereço, não nome. Um cliente pede `config://app`, nunca `get_config`.
O SDK ainda lê o restante a partir da função:
* O **nome** é o nome da função: `get_config`.
* A **descrição** que o cliente vê é a docstring.
* O **conteúdo** é o que você retornar.
Durante `resources/list`, o cliente recebe isto:
```json
{
"name": "get_config",
"uri": "config://app",
"description": "The active shop configuration.",
"mimeType": "text/plain"
}
```
E quando ele lê `config://app`, sua função roda e o valor de retorno volta como texto:
```python
result.contents # [TextResourceContents(uri="config://app", mime_type="text/plain", text="theme=dark\nlanguage=en")]
```
!!! tip
Listar é barato. Sua função **não** é chamada durante `resources/list`, só durante
`resources/read`, e apenas para a URI que foi pedida. Exponha mil recursos
e você só paga pelos que alguém abrir.
### Experimente {#try-it}
Execute o servidor com o MCP Inspector:
```console
uv run mcp dev server.py
```
Abra a URL que ele imprime e vá até a aba **Resources**. `config://app` está na lista, com sua descrição. Clique nele e o Inspector o lê: ali estão suas duas linhas de configuração.
## Templates de recurso {#resource-templates}
Uma URI por registro não escala. Coloque um **placeholder** na URI e um parâmetro correspondente na função:
```python title="server.py" hl_lines="12-13"
--8<-- "docs_src/resources/tutorial002.py"
```
`{user_id}` na URI, `user_id: str` na função. O contrato inteiro é esse.
Agora isso é um **template de recurso** (resource template), e ele se muda: sai de `resources/list` e passa a aparecer em `resources/templates/list`, como um padrão em vez de um endereço:
```json
{
"name": "get_user_profile",
"uriTemplate": "users://{user_id}/profile",
"description": "A customer's profile.",
"mimeType": "text/plain"
}
```
O cliente preenche o placeholder e lê uma URI concreta: `users://42/profile`, `users://ada/profile`. Uma única função responde a todas elas, com o valor capturado passado como `user_id`:
```python
result.contents # [TextResourceContents(uri="users://42/profile", text="User 42: 12 orders since 2021.")]
```
Repare na `uri` do resultado. É a URI **concreta** que o cliente pediu, não o template.
!!! check
Os placeholders e os parâmetros precisam bater. Renomeie o parâmetro da função para
`user` enquanto a URI ainda diz `{user_id}` e o decorador se recusa **em tempo de importação**,
antes que qualquer cliente chegue perto:
```text
ValueError: Mismatch between URI parameters {'user_id'} and function parameters {'user'}
```
Uma divergência dessas só pode ser bug, então o SDK torna impossível iniciar o servidor com uma.
A sintaxe dos placeholders é a da [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570): `{+path}` para valores com vários segmentos, `{?q,lang}` para parâmetros de query opcionais, e mais. O SDK também aplica, por padrão, verificações de segurança de caminho aos valores extraídos. Veja **[Templates de URI e segurança de caminhos](uri-templates.md)** para a referência completa.
`get_user_profile` também pode receber um parâmetro anotado com `Context`. O SDK o injeta sem nunca tratá-lo como parâmetro da URI, e a página **[O Context](../handlers/context.md)** cobre o que ele oferece a você.
## O que você retorna {#what-you-return}
Você não está limitado a `str`. Dê a cada recurso um `mime_type` e retorne o que fizer sentido:
```python title="server.py" hl_lines="8-9 14-15 20-21"
--8<-- "docs_src/resources/tutorial003.py"
```
* `readme` retorna uma `str`, então ela é enviada como está. Esse é o caso comum.
* `catalog_stats` retorna um `dict`, então o SDK o serializa em **texto JSON** para você:
```json
{
"books": 1204,
"authors": 391
}
```
* `placeholder_cover` retorna `bytes`, então o cliente recebe um `BlobResourceContents` em vez de um `TextResourceContents`, com seus bytes codificados em base64 no campo `blob`.
A mesma regra vale para qualquer outra coisa serializável em JSON: uma lista, um modelo Pydantic, uma dataclass. Se não é `str` nem `bytes`, vira JSON.
O `mime_type` é você quem declara, e o padrão é `text/plain`. O SDK nunca inspeciona o que você retorna para adivinhá-lo, então um recurso `dict` que você não rotula continua sendo anunciado como texto puro.
!!! tip
`@mcp.resource()` também aceita `name=`, `title=` e `description=` quando você não
quer derivá-los da função. E quando não há função nenhuma a escrever,
`mcp.server.mcpserver.resources` tem classes `Resource` prontas (`TextResource`,
`BinaryResource`, `FileResource`, `HttpResource`, `DirectoryResource`) que você registra
com `mcp.add_resource(...)`.
Um cliente também pode **assinar** um recurso e ser notificado quando ele muda; essa metade da história é do cliente e está em **[O cliente](../client/index.md)**.
## Recapitulando {#recap}
* `@mcp.resource(uri)` em uma função a transforma em um recurso. A URI é o endereço, o valor de retorno é o conteúdo, a docstring é a descrição.
* Um `{placeholder}` na URI a transforma em um **template**: ele é listado em `resources/templates/list` e uma única função atende a toda URI que corresponder.
* Os nomes dos placeholders devem ser iguais aos nomes dos parâmetros da função. Erre isso e você descobre em tempo de importação, não em produção.
* Sua função roda quando o recurso é **lido**, não quando é listado.
* `str` vira texto, `bytes` vira um blob em base64, qualquer outra coisa vira texto JSON. `mime_type=` é como você rotula isso.
* Ferramentas são para o modelo agir. Recursos são para a aplicação ler.
A terceira primitiva, aquela que uma pessoa escolhe em um menu, são os **[Prompts](prompts.md)**.

View file

@ -0,0 +1,250 @@
---
translation:
sections: [a838d57f003aed44, 857d03886a0137ed, 42d9efcb9f542867, 2290ff08435b5573, e866c192e11d1c14, 6cdbad079f7b47f0, d4b607372fb28b51, 18dbf726ac45e0b7, c6f7d2a148aa49f4, c851964bb3301907, d715db6f8dccc9cc, ef86634aa70498a7]
tool: 1
---
# Saída estruturada {#structured-output}
Uma ferramenta (tool) que retorna uma `str` simples produz o resultado duas vezes: como texto em `content` e como `{"result": "..."}` em `structured_content`.
Esta página trata desse segundo canal: de onde ele vem, todas as formas que ele pode assumir e como o SDK garante que ele seja confiável.
A versão curta: **a anotação do tipo de retorno é o schema de saída**. Você já a escreveu.
## O schema de saída {#the-output-schema}
```python title="server.py" hl_lines="9"
--8<-- "docs_src/structured_output/tutorial001.py"
```
A linha que importa é a assinatura: `-> int`.
Por causa dela, a ferramenta que o SDK envia durante `tools/list` carrega um `output_schema` ao lado do schema de entrada que ele monta a partir dos seus parâmetros (**[Ferramentas](tools.md)** cobre esse):
```json
{
"properties": {
"result": {"title": "Result", "type": "integer"}
},
"required": ["result"],
"title": "get_temperatureOutput",
"type": "object"
}
```
Um `int` sozinho não é um objeto JSON, então o SDK o **envolve** em `{"result": ...}`. Chame a ferramenta e os dois canais vêm preenchidos:
```python
result.content # [TextContent(text="17")]
result.structured_content # {"result": 17}
```
Todo escalar recebe o mesmo wrapper: `str`, `int`, `float`, `bool`, `bytes`, `None`.
## Dois canais {#two-channels}
Por que enviar o mesmo valor duas vezes?
* `content` é para o **modelo**. Um modelo de linguagem lê texto; essa é a única parte do resultado que ele vê.
* `structured_content` é para a **aplicação** dentro da qual o modelo roda: código que quer `17`, não uma frase contendo "17".
* `output_schema` é o contrato entre os dois, publicado antes mesmo de a ferramenta ser chamada.
Você retorna um único valor Python. O SDK preenche os três.
## Retorne um modelo {#return-a-model}
Declare a forma como um `BaseModel` do Pydantic e retorne uma instância:
```python title="server.py" hl_lines="8-11 15"
--8<-- "docs_src/structured_output/tutorial002.py"
```
`WeatherData` agora **é** o schema. Sem wrapper, sem chave `result`:
```json
{
"properties": {
"temperature": {"description": "Degrees Celsius.", "title": "Temperature", "type": "number"},
"humidity": {"description": "Relative humidity, 0 to 1.", "title": "Humidity", "type": "number"},
"conditions": {"title": "Conditions", "type": "string"}
},
"required": ["temperature", "humidity", "conditions"],
"title": "WeatherData",
"type": "object"
}
```
`structured_content` é o objeto, campo por campo:
```python
result.structured_content # {"temperature": 16.2, "humidity": 0.83, "conditions": "Overcast"}
```
E o modelo não fica de fora. O SDK serializa o mesmo objeto como texto JSON para `content`:
```json
{
"temperature": 16.2,
"humidity": 0.83,
"conditions": "Overcast"
}
```
Repare que o `Field(description=...)` em `temperature` e `humidity` foi parar no schema. O mesmo `Field` que descrevia as suas **entradas** descreve as suas saídas.
!!! info
Se você já usou o `response_model` do FastAPI, já conhece isso: um modelo Pydantic como a resposta
declarada, serializado e documentado para você. A única diferença é que aqui a anotação de retorno
é a declaração inteira.
## Um `TypedDict` {#a-typeddict}
Nem toda forma merece uma classe. Um `TypedDict` produz o mesmo schema:
```python title="server.py" hl_lines="8"
--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).
## Uma dataclass {#a-dataclass}
Dataclasses também funcionam, assim como qualquer classe comum cujos atributos tenham anotações de tipo. O SDK monta um modelo Pydantic a partir das anotações por baixo dos panos.
```python title="server.py" hl_lines="8-9"
--8<-- "docs_src/structured_output/tutorial004.py"
```
Três formas de escrever, um schema só. Use a que a sua base de código já tem.
## Listas {#lists}
Uma `list[...]` também não é um objeto JSON, então ela recebe o wrapper `{"result": ...}`, com o tipo dos seus itens como uma referência `$defs` dentro dele:
```python title="server.py" hl_lines="15"
--8<-- "docs_src/structured_output/tutorial005.py"
```
```json
{
"$defs": {
"WeatherData": {
"properties": {
"temperature": {"title": "Temperature", "type": "number"},
"humidity": {"title": "Humidity", "type": "number"},
"conditions": {"title": "Conditions", "type": "string"}
},
"required": ["temperature", "humidity", "conditions"],
"title": "WeatherData",
"type": "object"
}
},
"properties": {
"result": {"items": {"$ref": "#/$defs/WeatherData"}, "title": "Result", "type": "array"}
},
"required": ["result"],
"title": "get_forecastOutput",
"type": "object"
}
```
Peça uma previsão de dois dias e `structured_content` vem como `{"result": [{...}, {...}]}`. `content` vira **dois** blocos `TextContent`, um por item: uma lista é achatada para o modelo em vez de ser despejada como uma única string.
`tuple[...]`, uniões e `Optional[...]` são envolvidos da mesma forma.
## Dicionários {#dictionaries}
`dict[str, ...]` é o único genérico que já *é* um objeto JSON, então ele não é envolvido:
```python title="server.py" hl_lines="9"
--8<-- "docs_src/structured_output/tutorial006.py"
```
```json
{
"additionalProperties": {"type": "number"},
"title": "get_temperaturesDictOutput",
"type": "object"
}
```
```python
result.structured_content # {"London": 16.2, "Reykjavik": 4.4}
```
As chaves precisam ser `str`. Um `dict[int, float]` não pode ser um objeto JSON, então ele recai no wrapper `{"result": ...}`.
## Validação {#validation}
`output_schema` não é documentação. O que quer que a sua função retorne é **validado contra ele** antes de sair do servidor.
Você não percebe enquanto monta o valor à mão: o Pydantic já garantiu que o seu `WeatherData` era um `WeatherData`. Você percebe no dia em que os dados vêm de algum lugar que você não controla:
```python title="server.py" hl_lines="9 21"
--8<-- "docs_src/structured_output/tutorial007.py"
```
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:
```text
Error executing tool get_weather: 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}
Às vezes a anotação de retorno é para o seu verificador de tipos, não para o protocolo. Passe `structured_output=False` e a ferramenta fica só com texto:
```python title="server.py" hl_lines="6"
--8<-- "docs_src/structured_output/tutorial008.py"
```
Sem `output_schema`, sem wrapper, sem validação. `structured_content` é `None` e `content` é a string que você retornou.
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.
## 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**.
```python title="server.py" hl_lines="6-9"
--8<-- "docs_src/structured_output/tutorial009.py"
```
`Station` define `name` e `online` dentro de `__init__`, mas a *classe* não declara nada. O SDK lê as anotações da classe, não encontra nenhuma e desiste.
!!! warning
Ele desiste **em silêncio**. `output_schema` é `None`, `structured_content` é `None`, e o texto
que o modelo lê é o `repr` do objeto:
```text
"<server.Station object at 0x7f539d75b230>"
```
Nenhum erro, nenhum aviso, uma ferramenta inútil. Mova as anotações para o corpo da classe, ou passe
`structured_output=True`, que transforma isso em um erro de verdade no momento em que o módulo é importado:
`Function get_station: return type <class 'server.Station'> is not serializable for structured output`.
!!! tip
Precisa de controle total (montar o `CallToolResult` você mesmo, ou anexar um `_meta` que a
aplicação enxerga mas o modelo não)? Isso está em **[O Server de baixo nível](../advanced/low-level-server.md)**.
## Recapitulando {#recap}
* A **anotação do tipo de retorno** é o schema de saída. Ela é publicada em `tools/list` como `output_schema`.
* 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.
Agora você domina tudo o que uma ferramenta pode dizer de volta. A seguir, a segunda primitiva: **[Recursos](resources.md)**.

View file

@ -0,0 +1,177 @@
---
translation:
sections: [e4cc390d56573409, 8566e2b68594e9ad, 2c97b9f888398951, 048e5471dfa71aea, 3076b1e16ad95950, edbedf2a16e71311, 3d8ef8da89fa87c1, f6c0e02e6ea5a363]
tool: 1
---
# Ferramentas {#tools}
Uma **ferramenta** (tool) é uma função que o modelo pode chamar.
Você declara uma colocando `@mcp.tool()` em uma função Python comum. A API inteira é essa.
## Sua primeira ferramenta {#your-first-tool}
```python title="server.py" hl_lines="6-8"
--8<-- "docs_src/tools/tutorial001.py"
```
Veja o que você escreveu. Não há schemas, nem JSON, nem protocolo, só uma função. O SDK lê três coisas dela:
* O **nome** da ferramenta é o nome da função: `search_books`.
* A **descrição** que o modelo vê é a docstring: `Search the catalog by title or author.`
* Os **argumentos** que o modelo pode passar vêm das anotações de tipo: `query: str` e `limit: int`.
### O schema de entrada {#the-input-schema}
A partir dessas anotações de tipo, o SDK gera um JSON Schema e o envia ao cliente durante `tools/list`:
```json
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"title": "Limit", "type": "integer"}
},
"required": ["query", "limit"],
"title": "search_booksArguments"
}
```
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.)
!!! 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.
### O que o modelo recebe de volta {#what-the-model-gets-back}
Chame a ferramenta com `{"query": "dune", "limit": 5}` e o resultado tem duas partes:
```python
result.content # [TextContent(text="Found 3 books matching 'dune' (showing up to 5).")]
result.structured_content # {'result': "Found 3 books matching 'dune' (showing up to 5)."}
```
`content` é o texto que o **modelo** lê. `structured_content` são dados tipados para a **aplicação cliente**. Ele está ali porque você declarou o tipo de retorno como `-> str`.
Não se preocupe com `structured_content` por enquanto. Retorne objetos Python de verdade das suas ferramentas e a coisa certa acontece; a página **[Saída estruturada](structured-output.md)** trata exatamente disso.
### Experimente {#try-it}
Execute o servidor com o MCP Inspector:
```console
uv run mcp dev server.py
```
Abra a URL que ele imprime, vá até a aba **Tools** e chame `search_books`.
O Inspector renderiza um formulário com um campo de texto obrigatório `query` e um campo numérico obrigatório `limit`. Ele montou esse formulário a partir das suas anotações de tipo. Todos os outros clientes MCP vão fazer o mesmo.
## Argumentos opcionais {#optional-arguments}
Dê um valor padrão a um parâmetro e ele deixa de ser obrigatório. É só isso. É apenas Python.
```python title="server.py" hl_lines="7"
--8<-- "docs_src/tools/tutorial002.py"
```
O schema acompanha:
```json
{
"type": "object",
"properties": {
"query": {"title": "Query", "type": "string"},
"limit": {"default": 10, "title": "Limit", "type": "integer"}
},
"required": ["query"],
"title": "search_booksArguments"
}
```
`limit` saiu de `required` e ganhou `"default": 10`. Um cliente que o omite recebe `10`, exatamente como aconteceria em Python.
## Schemas mais ricos com `Field` {#richer-schemas-with-field}
As anotações de tipo levam você longe, mas às vezes você quer *descrever* um argumento, ou restringi-lo.
Envolva o tipo em `Annotated` e adicione um `Field` do Pydantic:
```python title="server.py" hl_lines="12-14"
--8<-- "docs_src/tools/tutorial003.py"
```
Três novidades, todas nos parâmetros:
* `Field(description=...)`: uma descrição por argumento que o modelo lê junto com a docstring.
* `Field(ge=1, le=50)`: limites numéricos. Eles entram no schema como `"minimum": 1, "maximum": 50`.
* `Literal["fiction", "non-fiction", "poetry"]`: um enum. O modelo só pode escolher um desses valores.
!!! check
Restrições não são enfeite. Chame a ferramenta com `limit=999` e o SDK responde com um
erro de ferramenta **antes de a sua função executar**:
```text
Input should be less than or equal to 50
```
Esse erro volta para o modelo como o resultado da ferramenta, e o modelo o lê e tenta de novo com
um valor válido. Você escreveu `le=50` uma vez e ganhou de graça agentes que se corrigem sozinhos.
!!! info
Se você já usou FastAPI ou Pydantic, já sabe tudo isso. É o mesmo `Field`,
o mesmo `Annotated`, a mesma validação. Não há nada específico de MCP para aprender aqui.
## Um modelo como parâmetro {#a-model-as-a-parameter}
Quando uma ferramenta recebe mais do que alguns poucos argumentos, agrupe-os em um modelo Pydantic:
```python title="server.py" hl_lines="8-11 15"
--8<-- "docs_src/tools/tutorial004.py"
```
O schema de `Book` fica aninhado dentro do schema de entrada da ferramenta (como uma referência em `$defs`), o modelo o preenche como um objeto JSON, e sua função recebe uma **instância real de `Book`**, já validada, com os atributos `.title`, `.author` e `.year`.
Você pode misturar à vontade: parâmetros simples ao lado de parâmetros de modelo, modelos aninhados, listas de modelos. É Pydantic de ponta a ponta.
## `async def` {#async-def}
Se uma ferramenta faz I/O (chama uma API, lê um arquivo, consulta um banco de dados), declare-a como `async def` e use `await` dentro dela. O SDK se encarrega de aguardá-la.
Uma ferramenta com `def` comum também funciona: o SDK a executa em uma thread, então ela nunca bloqueia o servidor.
Não há mais nada para configurar.
## Nomes, títulos e anotações {#names-titles-and-annotations}
Tudo o que o SDK infere, você pode sobrescrever no decorador:
```python title="server.py" hl_lines="7-10"
--8<-- "docs_src/tools/tutorial005.py"
```
* `title` é um nome legível por humanos, pensado para interfaces. Os clientes mostram *"Search the catalog"* em vez de `search_books`.
* `annotations` são **dicas** de comportamento para o cliente:
* `read_only_hint=True`: esta ferramenta não altera nada.
* `open_world_hint=False`: ela opera sobre um conjunto fechado de coisas (este catálogo), não sobre a web aberta.
* As outras duas, `destructive_hint` e `idempotent_hint`, descrevem uma ferramenta que *escreve*: ela pode
apagar alguma coisa? E chamá-la duas vezes dá no mesmo que chamá-la uma vez? A especificação define as duas
apenas para ferramentas que não são somente leitura, então elas não diriam nada em `search_books`.
Um cliente bem-comportado as usa para decidir coisas como *"preciso perguntar ao usuário antes de executar isto?"*. São dicas, não segurança. Nunca conte com um cliente respeitando-as.
!!! tip
`@mcp.tool()` também aceita `name=` e `description=` se você não quiser derivá-los
do nome da função e da docstring. Na maioria das vezes você quer.
## Recapitulando {#recap}
* `@mcp.tool()` em uma função a transforma em ferramenta. O nome vem da função, a descrição vem da docstring.
* As anotações de tipo **são** o schema de entrada. Valores padrão tornam os argumentos opcionais.
* `Annotated[..., Field(...)]` adiciona descrições e restrições; `Literal` adiciona enums.
* Um modelo Pydantic como parâmetro é a forma de receber um "corpo" estruturado.
* Argumentos inválidos são rejeitados para você, com um erro que o modelo consegue ler e do qual consegue se recuperar.
* `async def` para I/O, `def` comum para todo o resto.
**[Saída estruturada](structured-output.md)** é o que acontece com o valor que você devolve no `return`.

View file

@ -0,0 +1,291 @@
---
translation:
sections: [4a7033e1ed8ad602, 55dcbfff0c6271bf, 101ef9d14bf4ec46, 4b6c4a845438abc7, f98b46bafbee4acd]
tool: 1
---
# Templates de URI e segurança de caminhos {#uri-templates-and-path-safety}
Esta é a referência da sintaxe de templates de URI que
[`@mcp.resource`](resources.md) aceita e da política de segurança de
caminhos que o SDK aplica aos valores extraídos. Para uma introdução ao
que são recursos e quando usá-los, comece por
**[Recursos](resources.md)**; esta página parte do princípio de que você já
está à vontade declarando um recurso e quer o conjunto completo de
operadores, os controles de segurança ou a integração de baixo nível.
A sintaxe dos templates é a [RFC 6570](https://datatracker.ietf.org/doc/html/rfc6570).
O SDK dá suporte a um subconjunto escolhido para casar as URIs de
`resources/read` que chegam, mais uma camada de segurança que rejeita
valores que seriam resolvidos para fora do diretório que você pretende
servir. Para os detalhes no nível do protocolo (formatos de mensagem,
ciclo de vida, paginação), veja a
[especificação de recursos do MCP](https://modelcontextprotocol.io/specification/latest/server/resources).
## O conjunto completo de operadores {#the-full-operator-set}
O placeholder simples, `{user_id}`, é o que **[Recursos](resources.md)** apresenta. Existem mais
quatro formas de operador; aqui estão todas em um só servidor, para você
vê-las lado a lado:
```python title="server.py" hl_lines="16-17 22-23 28-29 34-35 40-41"
--8<-- "docs_src/uri_templates/tutorial001.py"
```
Cada decorador destacado é um jeito diferente de recortar a URI.
As seções abaixo percorrem todos eles, de cima para baixo.
### Expansão simples: `{name}` {#simple-expansion-name}
`books://{isbn}` é a forma simples, do dia a dia. O placeholder mapeia
para o parâmetro `isbn`, então um cliente que lê `books://978-0441172719`
chama `get_book("978-0441172719")`.
Um `{name}` simples para na primeira `/`. `books://978/extra` não casa
porque a barra depois de `978` encerra a captura e `/extra` fica
sobrando.
### Conversão de tipos {#type-conversion}
Os valores extraídos chegam como strings, mas você pode declarar um tipo
mais específico e o SDK converte. `orders://{order_id}` vai parar em uma
função cujo parâmetro é `order_id: int`, então ler `orders://12345` chama
`get_order(12345)`, e não `get_order("12345")`. O handler faz contas com
ele (`order_id + 1`) sem precisar de cast.
### Caminhos com vários segmentos: `{+name}` {#multi-segment-paths-name}
Para capturar um valor que contém barras, use `{+name}`. Com
`manuals://{+path}`:
* `manuals://returns.md` dá `path = "returns.md"`
* `manuals://printing/setup.md` dá `path = "printing/setup.md"`
Recorra a `{+name}` sempre que o valor for hierárquico: caminhos do
sistema de arquivos, chaves de objetos aninhados, caminhos de URL que
você repassa como proxy.
### Parâmetros de query: `{?a,b,c}` {#query-parameters-abc}
`reviews://{isbn}{?limit,sort}` coloca `limit` e `sort` depois do `?`.
O caminho identifica *qual* livro; a query ajusta *como* você o lê.
O casamento dos parâmetros de query é tolerante: a ordem não importa, os
extras são ignorados e os parâmetros omitidos caem nos valores padrão da
sua função. Então `reviews://978-0441172719` usa `limit=10, sort="newest"`,
e `reviews://978-0441172719?sort=top` sobrescreve apenas `sort`.
### Segmentos de caminho como lista: `{/name*}` {#path-segments-as-a-list-name}
Se você quer cada segmento do caminho como um item separado de uma lista,
em vez de uma única string com barras, use `{/name*}`. Com
`shelves://browse{/path*}`, um cliente que lê
`shelves://browse/fiction/sci-fi` chama
`browse_shelf(["fiction", "sci-fi"])`.
### Referência de templates {#template-reference}
Os padrões mais comuns:
| Padrão | Entrada de exemplo | Você recebe |
|--------------|-----------------------|-------------------------|
| `{name}` | `alice` | `"alice"` |
| `{name}` | `docs/intro.md` | *não casa* (para na `/`) |
| `{+path}` | `docs/intro.md` | `"docs/intro.md"` |
| `{.ext}` | `.json` | `"json"` |
| `{/segment}` | `/v2` | `"v2"` |
| `{?key}` | `?key=value` | `"value"` |
| `{?a,b}` | `?a=1&b=2` | `"1"`, `"2"` |
| `{/path*}` | `/a/b/c` | `["a", "b", "c"]` |
### O que o parser rejeita {#what-the-parser-rejects}
Alguns formatos de template são barrados logo de início, em vez de
falharem na primeira requisição. `@mcp.resource` faz o parse do template
quando o decorador executa, então nenhum deles chega a um servidor em
execução.
`UriTemplate.parse()` levanta `InvalidUriTemplate` nestes casos:
* **Duas variáveis sem nada entre elas.** `manuals://{+path}{ext}`
é rejeitado: o casamento não tem como saber onde `path` termina e
`ext` começa. Coloque um literal entre elas (`manuals://{+path}/{ext}`)
ou use um operador que traga seu próprio delimitador.
`manuals://{+path}{.ext}` é aceito porque `{.ext}` contribui ele mesmo
com o `.`.
* **Mais de uma variável de vários segmentos.** No máximo uma entre
`{+var}`, `{#var}` ou uma variável explodida (`{/var*}`, `{.var*}`,
`{;var*}`) por template. Duas são inerentemente ambíguas: não há um
critério consistente para decidir qual delas absorve um segmento extra.
* **Os erros de sintaxe de sempre**: uma chave não fechada, um nome de
variável usado duas vezes ou uma funcionalidade da RFC 6570 à qual o
SDK não dá suporte, como o modificador de prefixo `{var:3}` ou a
explosão de query `{?vars*}`.
Além disso, `@mcp.resource` levanta `ValueError` quando um parâmetro do
handler está vinculado a uma variável de query na sequência
`{?...}`/`{&...}` do final do template, mas não tem valor padrão em
Python. O casamento dessas variáveis é tolerante (um cliente pode deixar
qualquer uma delas de fora), então um parâmetro sem valor padrão só
apareceria como um erro interno opaco na primeira requisição que o
omitisse. `reviews://{isbn}{?limit,sort}` no servidor acima é a versão
bem formada: `limit` e `sort` têm, ambos, valores padrão.
## Segurança {#security}
Os parâmetros de template vêm do cliente. Se eles chegarem sem
verificação a operações de sistema de arquivos ou de banco de dados,
valores como `../../etc/passwd` podem ser resolvidos para fora do
diretório que você pretendia servir.
### O que o SDK verifica por padrão {#what-the-sdk-checks-by-default}
Antes de o seu handler executar, o SDK rejeita qualquer parâmetro que:
* escaparia do seu diretório de partida por meio de componentes `..`
* parece um caminho absoluto (`/etc/passwd`, `C:\Windows`) ou um caminho
do Windows relativo a uma unidade (`C:foo`). Um valor relativo a
unidade e um identificador com namespace como `x:y` são
indistinguíveis como strings, então qualquer valor do tipo uma letra
seguida de dois-pontos é rejeitado por padrão; isente o parâmetro se
ele recebe valores assim de forma legítima
* contém um byte nulo (`\x00`)
A verificação de `..` é feita por componente, não por busca de
substring. Valores como `v1.0..v2.0` ou `HEAD~3..HEAD` passam porque ali
`..` não é um segmento de caminho isolado.
Essas verificações se aplicam ao valor decodificado, então elas pegam
tentativas de path traversal independentemente de como foram codificadas
na URI (`../etc`, `..%2Fetc`, `%2E%2E/etc`, `..%5Cetc`, `%00` são todos
barrados).
!!! check
Leia `manuals://../etc/passwd` do servidor acima e a requisição é
rejeitada na hora: o casamento de templates para na primeira falha,
então nenhum template posterior (potencialmente mais permissivo) é
tentado como fallback. O cliente vê o mesmo erro `-32602` "Unknown
resource" que veria para uma URI que não casa com nenhum template, e
`read_manual` nunca executa.
### Handlers de sistema de arquivos: use safe_join {#filesystem-handlers-use-safe_join}
As verificações embutidas barram os casos comuns, mas não têm como
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"
--8<-- "docs_src/uri_templates/tutorial002.py"
```
`safe_join` pega escapes por link simbólico, sequências `..` e truques
com caminhos absolutos que uma verificação simples de string deixaria
passar. Se o caminho resolvido escapa de `DOCS_ROOT`, a função levanta
`PathEscapeError`, que chega ao cliente como um `ResourceError`.
### Quando o comportamento padrão atrapalha {#when-the-defaults-get-in-the-way}
Às vezes as verificações bloqueiam valores legítimos. Uma ferramenta de
importação de catálogo pode receber de propósito um caminho absoluto, ou
um parâmetro pode ser uma referência relativa como `../sibling` que o
seu handler interpreta com segurança sem tocar no sistema de arquivos.
Isente esse parâmetro ou afrouxe a política para o servidor inteiro:
```python title="server.py" hl_lines="9 16-19"
--8<-- "docs_src/uri_templates/tutorial003.py"
```
* `security=ResourceSecurity(exempt_params={"source"})` no decorador
pula as verificações só para aquele parâmetro, só naquele recurso. O
resto do servidor mantém a política padrão.
* `resource_security=` no construtor de `MCPServer` define o padrão
para todos os recursos. Aqui, `relaxed` desliga por completo a
verificação de `..`.
As verificações configuráveis:
| Configuração | Padrão | O que faz |
|-------------------------|---------|-------------------------------------|
| `reject_path_traversal` | `True` | Rejeita sequências `..` que escapam do diretório de partida |
| `reject_absolute_paths` | `True` | Rejeita `/foo`, `C:\foo`, caminhos UNC e o `C:foo` relativo a unidade (também pega `x:y`) |
| `reject_null_bytes` | `True` | Rejeita valores que contêm `\x00` |
| `exempt_params` | vazio | Nomes de parâmetros para os quais pular as verificações |
Essas verificações são um pré-filtro heurístico; para acesso ao sistema
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.
## Recursos no Server de baixo nível {#resources-on-the-low-level-server}
Se você está construindo sobre o `Server` de baixo nível (veja **[O Server
de baixo nível](../advanced/low-level-server.md)**), registra handlers diretamente para os
métodos de protocolo `resources/list` e `resources/read`. Não há
decorador; quem retorna os tipos do protocolo é você.
### Recursos estáticos {#static-resources}
Para URIs fixas, mantenha um registro e despache por casamento exato:
```python title="server.py" hl_lines="17 21 27"
--8<-- "docs_src/uri_templates/tutorial004.py"
```
O handler de listagem diz aos clientes o que está disponível; o handler
de leitura serve o conteúdo. Consulte primeiro o seu registro, caia nos
templates (abaixo) se tiver algum, e levante uma exceção para qualquer
outra coisa.
### Templates {#templates}
O motor de templates que o `MCPServer` usa fica em
`mcp.shared.uri_template` e funciona sozinho. Você ganha o mesmo parse e
o mesmo casamento; o roteamento e a política de segurança, você monta por
conta própria.
```python title="server.py" hl_lines="13-16 22-25 29 33 45"
--8<-- "docs_src/uri_templates/tutorial005.py"
```
Três coisas acontecem nas linhas destacadas:
* **Faça o parse uma vez, case a cada requisição.** `UriTemplate.parse()`
monta o template; `template.match(uri)` retorna as variáveis extraídas
como um `dict`, ou `None` se a URI não se encaixa. A decodificação de
URL acontece dentro de `match()`; os valores decodificados são
retornados como estão, sem validação de segurança de caminho. Os
valores saem como strings: converta-os você mesmo
(`int(matched["id"])`, `Path(matched["path"])`).
* **Aplique você mesmo as verificações de segurança.** As verificações de
`..` e de caminho absoluto que o `MCPServer` executa por padrão ficam em
`mcp.shared.path_security`. `read_manual_safely` as chama antes de
tocar em `MANUALS`. Se um parâmetro não é um caminho do sistema de
arquivos (um ISBN, uma consulta de busca), pule as verificações para
esse valor: você controla a política por handler, e não por meio de um
objeto de configuração.
* **Liste os templates a partir da mesma fonte.** Os clientes descobrem
templates por meio de `resources/templates/list`. `str(template)`
devolve a string original do template, então a listagem e o casamento
compartilham uma única fonte da verdade.
## Recapitulando {#recap}
* `{name}` casa com um segmento; `{+name}` mantém as barras; `{?a,b}`
puxa da query string; `{/name*}` divide os segmentos em uma lista.
* Duas variáveis sem nada entre elas, ou uma segunda variável de vários
segmentos, são rejeitadas na hora do parse. Um parâmetro vinculado a
uma variável de query em um `{?...}`/`{&...}` final deve declarar um
valor padrão em Python.
* Anote o parâmetro (`order_id: int`) e o SDK converte.
* A política de segurança padrão rejeita `..`, caminhos absolutos e bytes
nulos antes de o seu handler executar; sobrescreva por recurso com
`security=ResourceSecurity(...)` ou no servidor inteiro com
`resource_security=`.
* Para acesso ao sistema de arquivos, `safe_join` é a fronteira de
contenção.
* No `Server` de baixo nível, faça o parse com `UriTemplate.parse()`,
case com `.match()` e aplique `mcp.shared.path_security` você mesmo.