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
5 changed files with 89737 additions and 54 deletions

View file

@ -0,0 +1,144 @@
---
translation:
sections: [0d6c05bcbf836bf3, 59a7b14eeefc68c1, 7114d8d6daba203f, e8bbb56a98ba7bc9, 5138010f6159901c, f78da7c7c363d4c6, 220a939cab348686]
tool: 1
---
# Primeiros passos {#first-steps}
A **[página inicial](../index.md)** anda rápido: escrever um servidor, executá-lo, chamar uma ferramenta.
Esta página vai com calma, passando pelas três coisas que um servidor pode expor e dando nome a tudo pelo caminho.
## Host, cliente e servidor {#host-client-and-server}
Três palavras que você vai ver em todas as páginas daqui em diante:
* Um **host** é a aplicação de LLM: o Claude, uma IDE, um runtime de agentes. É com ele que o usuário conversa.
* Um **cliente** vive dentro do host e fala MCP. O host executa um cliente para cada servidor ao qual está conectado.
* Um **servidor** é o que você constrói com este SDK. Ele expõe coisas aos clientes. Nunca fala diretamente com o modelo.
Você escreve o servidor. Os hosts são produto de terceiros. O SDK também traz um `Client`. Você vai usá-lo para testar seus servidores, e ele aparece mais adiante nesta página.
## As três primitivas {#the-three-primitives}
Um servidor expõe exatamente três tipos de coisa. O que as distingue é **quem decide usá-las**:
| Primitiva | Quem controla | O que é | Exemplo |
|-----------------|-----------------|-----------------------------------------------------------------|----------------------------------------------------|
| **Ferramentas** | O modelo | Uma função que o modelo chama para executar uma ação | Uma chamada de API, uma escrita no banco de dados |
| **Recursos** | A aplicação | Dados que o host carrega no contexto do modelo | O conteúdo de um arquivo, uma resposta de API |
| **Prompts** | O usuário | Um template de mensagem reutilizável que o usuário invoca pelo nome | Um comando de barra, um item de menu |
"Quem controla" é justamente o sentido da divisão. Uma ferramenta roda porque o **modelo** decidiu chamá-la. Um recurso é anexado porque a **aplicação** decidiu que o modelo precisava dele. Um prompt roda porque o **usuário** o escolheu.
!!! info
Se você já construiu uma API web, já tem quase toda a intuição: um **recurso** é um `GET`
(carrega dados e não altera nada) e uma **ferramenta** é um `POST` (realiza trabalho e pode ter
efeitos colaterais). Um **prompt** não tem equivalente em HTTP; está mais para uma consulta salva
que o usuário executa pelo nome.
## Um servidor, as três {#one-server-all-three}
```python title="server.py" hl_lines="6 12 18"
--8<-- "docs_src/first_steps/tutorial001.py"
```
Três funções simples, três decoradores. Cada decorador já é o registro completo:
* `@mcp.tool()` transforma `add` em uma **ferramenta**.
* `@mcp.resource("greeting://{name}")` transforma `greeting` em um **template de recurso**: o `{name}` na URI é o parâmetro da função.
* `@mcp.prompt()` transforma `summarize` em um **prompt**. A string que ela retorna vira uma mensagem de usuário.
Todo o resto (o nome, a descrição, o schema dos argumentos) o SDK lê da própria função: o nome dela, a docstring, as anotações de tipo. Você nunca declarou nada disso separadamente.
!!! tip
As duas metades do SDK têm dois caminhos de importação: `from mcp import Client` e
`from mcp.server import MCPServer`. Não existe `from mcp import MCPServer`.
### Experimente {#try-it}
Execute com o MCP Inspector:
```console
uv run mcp dev server.py
```
Abra a URL que ele imprime. O Inspector tem uma aba por primitiva; passe por elas na ordem.
**Ferramentas.** Uma entrada: `add`, descrita como *Add two numbers.* O formulário tem um campo inteiro obrigatório para `a` e outro para `b`. Preencha, chame, e o resultado é `3`. O Inspector montou esse formulário a partir de `a: int, b: int`. Qualquer outro cliente faz o mesmo.
**Recursos.** A lista *Resources* está vazia. `greeting` fica em **Resource Templates**, porque `greeting://{name}` tem um parâmetro: não existe um recurso concreto para listar até alguém fornecer um `name`. Passe `World` e leia:
```text
Hello, World!
```
**Prompts.** Uma entrada: `summarize`, com um único argumento obrigatório, `text`. Obtenha-o com algum texto e você recebe uma mensagem com `role: user` e sua string renderizada como conteúdo. Um prompt é só isso: uma função que monta mensagens.
O Inspector executou seu servidor via **stdio**, um dos transportes que um servidor MCP sabe falar. Por enquanto você não escolhe um; **[Executando seu servidor](../run/index.md)** é a página para isso.
## Capacidades {#capabilities}
Você viu três abas no Inspector. Como ele sabia que eram três?
Quando um cliente se conecta, o servidor declara suas **capacidades**: quais famílias de requisições ele vai responder. O cliente usa essa declaração para decidir o que faz sentido pedir. Você nunca escreveu isso; o `MCPServer` declara por você.
Veja você mesmo. O `Client` do SDK aceita o objeto do servidor diretamente e se conecta a ele **em memória** (sem subprocesso, sem porta):
```python
import asyncio
from mcp import Client
from server import mcp
async def main() -> None:
async with Client(mcp) as client:
print(client.server_capabilities.model_dump(exclude_none=True))
asyncio.run(main())
```
```text
{'prompts': {'list_changed': True}, 'resources': {'subscribe': True, 'list_changed': True}, 'tools': {'list_changed': True}}
```
Esse dicionário é a declaração de **capacidades** do seu servidor. É a primeira coisa que todo cliente aprende ao se conectar:
| Capacidade | O cliente agora pode chamar |
|-------------|---------------------------------------------------------------|
| `tools` | `tools/list`, `tools/call` |
| `resources` | `resources/list`, `resources/templates/list`, `resources/read` |
| `prompts` | `prompts/list`, `prompts/get` |
O `MCPServer` serve as três primitivas, então as três são sempre declaradas.
Repare no que não aparece ali. `completions` (autocompletar de argumentos para templates de recurso e prompts) precisa de um handler escrito por você; este servidor não tem nenhum, então a capacidade fica de fora e um cliente bem-comportado nem pede. Essa é a regra para tudo que é opcional: registre a coisa e a capacidade aparece; **[Completions](../servers/completions.md)** comprova isso.
!!! info
`Client(mcp)` é o mesmo cliente em memória com que todos os exemplos desta documentação são
testados, e é assim que você vai testar os seus. Ele ganha uma página inteira: **[Testes](testing.md)**.
## O que você não escreveu {#what-you-did-not-write}
Olhe de novo esta página. Você escreveu três funções Python pequenas. Você **não** escreveu:
* Um JSON Schema. `a: int, b: int` *é* o schema de `add`.
* Um handler de requisição. `tools/list`, `resources/read`, `prompts/get`: o SDK atende todos por você.
* Uma declaração de capacidades. O `MCPServer` fez isso por você.
* Uma linha de protocolo. A negociação de versão, o enquadramento JSON-RPC, a troca de capacidades: tudo isso aconteceu dentro de `mcp dev` e `Client(mcp)`, e você nunca viu.
Essa proporção é a razão de ser do SDK.
## Recapitulando {#recap}
* Um **host** é o app de LLM, um **cliente** é a metade dele que fala MCP, um **servidor** é o que você constrói.
* Ferramentas são controladas pelo **modelo**, recursos pela **aplicação**, prompts pelo **usuário**.
* Um decorador por primitiva: `@mcp.tool()`, `@mcp.resource(uri)`, `@mcp.prompt()`. Nome, descrição e schema vêm da função.
* Uma URI com um `{param}` cria um **template** de recurso, listado separadamente dos recursos concretos.
* As **capacidades** do servidor já vêm declaradas para você, e um cliente só pede o que o servidor declara.
* `Client(mcp)` se conecta ao objeto do servidor em memória: seu ambiente de testes desde o primeiro dia.
A seguir vem **[Conecte a um host real](real-host.md)**: este servidor dentro do Claude Desktop ou de uma IDE, de verdade. Depois, **[Testes](testing.md)**: uma página, um cliente em memória, e você nunca mais fica adivinhando se funciona. Depois disso, cada primitiva ganha sua própria página, começando pela que o modelo comanda: **[Ferramentas](../servers/tools.md)**.

View file

@ -0,0 +1,57 @@
---
translation:
sections: [ed4a756b4c53c585, 97e2fb315b7fe398, 4d04f1c6f4bf6c1d, 577d73078fc62baf]
tool: 1
---
# Comece por aqui {#get-started}
Novo no MCP, ou novo neste SDK? Comece aqui. Estas páginas levam você do zero a um
servidor funcional e testado: [instale o SDK](installation.md), construa seu
[primeiro servidor](first-steps.md), [conecte-o a um host real](real-host.md) e
[teste-o](testing.md) com um cliente em memória.
## Execute o código {#run-the-code}
Todos os blocos de código podem ser copiados e usados diretamente: são arquivos completos e funcionais.
Para acompanhar, cole um bloco em um `server.py` e abra-o no MCP Inspector:
```console
uv run mcp dev server.py
```
É **ALTAMENTE recomendado** que você escreva (ou copie) o código, edite-o e execute-o localmente. Usá-lo no seu próprio editor é o que mostra de verdade qual é a ideia: o pouco que você escreve, o autocompletar, a checagem de tipos pegando erros antes de executar qualquer coisa.
## Você não vai precisar adivinhar {#you-will-not-be-guessing}
Cada exemplo nesta documentação é um arquivo completo em [`docs_src/`](https://github.com/modelcontextprotocol/python-sdk/tree/main/docs_src) no próprio repositório do SDK, e a suíte de testes do SDK exercita cada um deles por meio de um **cliente em memória**:
```python
import pytest
from mcp import Client
from server import mcp
@pytest.mark.anyio
async def test_add() -> None:
async with Client(mcp) as client:
result = await client.call_tool("add", {"a": 1, "b": 2})
assert result.structured_content == {"result": 3}
```
Sem subprocesso, sem porta, sem transporte. `Client(mcp)` se conecta diretamente ao objeto do servidor.
Se uma mudança no SDK quebrar um exemplo de uma destas páginas, o CI fica vermelho antes que a página quebre. O código que você lê aqui é o código que roda.
Você mesmo vai usar isso em [Testes](testing.md); é assim que você testa seus próprios servidores também.
## Para onde ir agora {#where-to-go-next}
Depois que você tiver um servidor rodando, o resto desta documentação é uma referência, não um curso.
Cada página se sustenta sozinha, então pule direto para o que você precisa:
* O que um servidor expõe (ferramentas, recursos, prompts) está em **[Servidores](../servers/index.md)**.
* O que está disponível dentro das funções que você registra está em **[Dentro do seu handler](../handlers/index.md)**.
* Levar o servidor até os clientes (stdio, HTTP, o app FastAPI que você já tem) está em **[Executando seu servidor](../run/index.md)**.
* Construir o outro lado, uma aplicação que *usa* servidores MCP, está em **[Clientes](../client/index.md)**.

View file

@ -0,0 +1,47 @@
---
translation:
sections: [6e2f9bab94d5ed36, 8cf653388f69e28b, 6fd9ea2f65de0df6]
tool: 1
---
# Instalação {#installation}
O SDK Python está no PyPI como [`mcp`](https://pypi.org/project/mcp/). Ele requer **Python 3.10+**.
Esta documentação descreve a **v2**, a linha de versões estável atual:
=== "uv"
```bash
uv add "mcp[cli]"
```
=== "pip"
```bash
pip install "mcp[cli]"
```
!!! note "Vindo da v1?"
A v2 é uma versão major com quebras de compatibilidade; o **[Guia de migração](../migration.md)**
cobre todas elas. Se o seu *pacote* depende de `mcp` e ainda não está pronto para migrar, mantenha um
limite superior `<2` (por exemplo `mcp>=1.28,<2`) para que uma resolução sem versão fixada continue na linha 1.x.
## O que é instalado {#what-gets-installed}
Você não precisa saber nada disso para usar o SDK, mas, se quiser saber para que serve cada dependência:
* `mcp-types`: todos os tipos do protocolo (requisições, resultados, blocos de conteúdo) em um pacote próprio, versionado em sincronia com o SDK. O código que depende de `mcp` o importa pelo alias `mcp.types` (todo `from mcp.types import ...` nesta documentação); importe `mcp_types` diretamente apenas em um projeto que instala `mcp-types` sem o SDK.
* [`anyio`](https://anyio.readthedocs.io/): o runtime assíncrono. O SDK inteiro é escrito sobre o anyio, então roda tanto com `asyncio` quanto com `trio`.
* [`pydantic`](https://docs.pydantic.dev/): a base de todos os modelos de `mcp.types`, além de toda a geração e validação de schemas.
* [`httpx2`](https://pypi.org/project/httpx2/): o cliente HTTP por trás dos transportes de *cliente* Streamable HTTP e SSE, com suporte embutido a server-sent events.
* [`starlette`](https://www.starlette.io/), [`uvicorn`](https://www.uvicorn.org/), [`sse-starlette`](https://pypi.org/project/sse-starlette/) e [`python-multipart`](https://pypi.org/project/python-multipart/): os transportes HTTP de *servidor*.
* [`jsonschema`](https://pypi.org/project/jsonschema/): valida a saída estruturada de uma ferramenta (tool) contra o schema de saída declarado por ela.
* [`pyjwt[crypto]`](https://pyjwt.readthedocs.io/): tratamento de tokens OAuth para autorização.
* [`opentelemetry-api`](https://opentelemetry-python.readthedocs.io/): apenas a API leve, então o middleware de tracing do SDK não custa nada, a menos que você mesmo instale um SDK e um exporter do OpenTelemetry.
* [`typing-extensions`](https://typing-extensions.readthedocs.io/) e [`typing-inspection`](https://pypi.org/project/typing-inspection/): funcionalidades modernas de tipagem no Python 3.10.
* [`pywin32`](https://pypi.org/project/pywin32/): somente no Windows, usado para o gerenciamento de subprocessos `stdio`.
## Extras opcionais {#optional-extras}
* `mcp[cli]` adiciona [`typer`](https://typer.tiangolo.com/) e [`python-dotenv`](https://pypi.org/project/python-dotenv/) para a ferramenta de linha de comando `mcp` (`mcp dev`, `mcp run`, `mcp install`). Você vai querer isso durante o desenvolvimento; talvez não precise dele em um servidor depois do deploy.
* `mcp[rich]` adiciona [`rich`](https://rich.readthedocs.io/) para logs de servidor mais bonitos.

View file

@ -0,0 +1,185 @@
---
translation:
sections: [3c4f2f06b4e978b6, 22520eecae3d1961, f4e1709db18d635a, 2eb57992049671d9, 1ba83e9af37cc1b4, 4822586344b08d9e, 1c93afef72478992, b6b448f9eddd51dc, fe55370fd931815b]
tool: 1
---
# Conecte-se a um host de verdade {#connect-to-a-real-host}
Um **host** é a aplicação dentro da qual seu servidor acaba rodando: Claude Desktop, Claude Code, uma IDE. O host é aquilo com que o usuário conversa. Dentro dele, um **cliente** MCP inicia seu servidor como um processo filho e fala com ele pelo stdin e stdout desse processo.
Ou seja, conectar a um host é um ato só: você informa a ele **o comando que inicia seu servidor**. Tudo nesta página (dois comandos de CLI, três arquivos JSON) é um lugar diferente para colocar esse mesmo comando.
## Um servidor, todos os hosts {#one-server-every-host}
```python title="server.py" hl_lines="3 33-34"
--8<-- "docs_src/real_host/tutorial001.py"
```
Duas ferramentas (tools) e um recurso, um arquivo só. Três coisas sobre esse arquivo importam para todos os hosts abaixo:
* `mcp.run()` sem argumentos inicia um servidor **stdio**: ele bloqueia, lê mensagens do protocolo no stdin e as escreve no stdout. Esse é o transporte que todos os hosts desta página falam. O host inicia seu arquivo como um processo filho e é dono desses dois pipes, e é por isso que conectar nunca passa de "aqui está o comando". Você nunca escolhe uma porta, e nada fica escutando em uma.
* `run()` fica dentro de `if __name__ == "__main__":`. Tudo abaixo **importa** este arquivo em vez de executá-lo, então um `run()` sem essa proteção iniciaria um servidor no instante em que qualquer coisa carregasse o módulo.
* O objeto do servidor é uma global de nível de módulo chamada `mcp`. É esse o nome que `mcp run` procura (`server` e `app` também funcionam). Dê outro nome a ele e você precisa informá-lo explicitamente: `mcp run server.py:bookshop`.
Essa é a última linha de Python nesta página. Daqui para baixo é tudo configuração de host.
## O comando de inicialização {#the-launch-command}
Todos os hosts abaixo recebem o mesmo comando:
```bash
uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
Um comando só para todos eles porque `uv run --with` resolve o SDK em um ambiente novo na hora: funciona a partir de qualquer diretório e não precisa de projeto nem de ambiente virtual para ativar. Isso importa aqui mais do que em qualquer outro lugar, porque um host inicia seu servidor a partir do diretório de trabalho *dele*, com um ambiente quase vazio, e não a partir do seu shell.
É também o comando que `mcp install` grava na configuração do Claude Desktop para você (abaixo), então o que você digita à mão e o que o utilitário gera coincidem, fora o pin exato de versão que o utilitário adiciona.
!!! tip "Se um host não encontrar o `uv`"
Um host inicia seu servidor com um `PATH` mínimo, e o `uv` pode não estar nele. Troque o
`uv` sozinho pelo caminho absoluto que `which uv` (macOS/Linux) ou `where uv` (Windows)
retorna. É exatamente isso que `mcp install` grava.
!!! note "Esta página é o cenário local"
Tudo aqui executa seu servidor na máquina em que o host está: o host inicia seu
arquivo, via stdio. Isso é exatamente o certo para uma ferramenta pessoal ou de uma máquina
só. Para entregar um servidor a pessoas que *não* têm seu arquivo, você distribui uma
**URL**, não um comando: o mesmo objeto `mcp` servido via Streamable HTTP.
**[Executando seu servidor](../run/index.md)** é essa decisão em uma tabela só, e
**[Deploy e escala](../run/deploy.md)** é o caminho dali até um hostname de verdade.
E um host nada mais é que uma aplicação com um cliente MCP dentro, então seu próprio
código Python pode fazer o papel do host: **[Transportes do cliente](../client/transports.md)**
inicia este mesmo arquivo como subprocesso com `stdio_client(...)`, e **[Testes](testing.md)**
se conecta a ele em memória, sem processo nenhum.
## Claude Desktop {#claude-desktop}
O único host que o SDK consegue configurar para você:
```bash
uv run mcp install server.py
```
É só isso. `mcp install` importa o arquivo para ler o nome do servidor, encontra o arquivo de configuração do Claude Desktop e grava o comando de inicialização nele. De passagem, já converte o caminho para absoluto, então você não precisa fazer isso.
Não há mistério nenhum. Esta é a entrada que ele grava:
```json
{
"mcpServers": {
"Bookshop": {
"command": "/absolute/path/to/uv",
"args": [
"run",
"--frozen",
"--with",
"mcp[cli]==2.0.0",
"mcp",
"run",
"/absolute/path/to/server.py"
]
}
}
}
```
É o comando de inicialização da seção acima com três acréscimos: o caminho absoluto do `uv`, `--frozen` para que o `uv` nunca reescreva um lockfile que por acaso esteja por perto, e um pin exato na versão do `mcp` que você tem instalada. Ele vai parar em `claude_desktop_config.json`, que fica em:
* **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
Você pode escrever esse arquivo à mão. `mcp install` existe para você não cometer o erro clássico (um caminho relativo) ao fazer isso.
Encerre o Claude Desktop por completo (não só a janela) e abra-o de novo.
!!! warning
`mcp install` falha com `Claude app not found` se o *diretório* de configuração do Claude
Desktop ainda não existir. Instale o Claude Desktop e execute-o uma vez: é isso que cria o
diretório.
!!! tip
O Claude Desktop inicia seu servidor em um processo próprio, então as variáveis de ambiente do
seu shell não estão lá. `uv run mcp install server.py -v API_KEY=abc123` (ou `-f .env`) as
registra no campo `env` da entrada. `--name` sobrescreve o nome da entrada; o padrão é o
`name` do servidor.
## Claude Code {#claude-code}
Não há arquivo para editar. Registre o servidor com a CLI `claude`; tudo depois de `--` é o comando de inicialização.
```bash
claude mcp add bookshop -- uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
Execute `/mcp` dentro de uma sessão do Claude Code para confirmar que `bookshop` está conectado e que suas ferramentas aparecem listadas.
## Cursor {#cursor}
Crie `.cursor/mcp.json` na raiz do seu projeto.
```json
{
"mcpServers": {
"bookshop": {
"command": "uv",
"args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
}
}
}
```
Os mesmos `command` e `args`, sob a mesma chave `mcpServers` que o Claude Desktop usa. O servidor aparece nas configurações de MCP do Cursor com as duas ferramentas listadas.
## VS Code {#vs-code}
Crie `.vscode/mcp.json` na raiz do seu projeto.
```json
{
"servers": {
"bookshop": {
"type": "stdio",
"command": "uv",
"args": ["run", "--with", "mcp[cli]", "mcp", "run", "/absolute/path/to/server.py"]
}
}
}
```
Duas diferenças em relação ao arquivo do Cursor, e são as únicas duas: a chave externa é `servers`, não `mcpServers`, e cada entrada declara seu `type`. Confirme o diálogo de confiança e, em seguida, **MCP: List Servers** na paleta de comandos mostra `bookshop` rodando.
!!! note
Você precisa do VS Code 1.99 ou posterior, com login feito na extensão **GitHub Copilot**
(o Copilot Free basta), e o Copilot Chat precisa estar no modo **Agent**, porque nenhum
outro modo chama ferramentas.
## Não aparece {#it-doesnt-show-up}
Antes de mexer em qualquer configuração de host, execute você mesmo o comando de inicialização:
```bash
uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py
```
Nada é impresso, e ele não retorna. Esse silêncio está certo: um servidor stdio está esperando um host falar primeiro no stdin (`Ctrl-C` para pará-lo). Um traceback ou uma saída imediata é o bug de verdade, e agora você consegue lê-lo em vez de tentar adivinhá-lo através de um host.
Uma vez que esse comando fica parado esperando, o que sobra é quase sempre uma de três coisas:
* **Um caminho relativo.** O host inicia seu servidor a partir do diretório de trabalho *dele*, não daquele de onde você fez o registro. `server.py` onde é preciso `/absolute/path/to/server.py` é, de longe, a falha mais comum. Se o host também não encontrar o `uv`, esse caminho precisa ser absoluto também.
* **O host ainda está rodando a configuração antiga.** Os hosts leem a configuração ao iniciar. O Claude Desktop, em particular, precisa ser *encerrado por completo* (não basta fechar a janela) e reaberto para que uma edição em `claude_desktop_config.json` tenha efeito.
* **Algo chegou ao stdout fora do intervalo de desvio.** No stdio, o stdout *é* o protocolo. O SDK desvia para o stderr a saída avulsa descarregada (com flush) enquanto está servindo, mas uma saída descarregada no stdout antes disso (um script wrapper que ecoa algo, um `print()` em tempo de importação em um processo sem buffer), ou um `print()` em buffer que só é descarregado quando o interpretador encerra, entrega ao host uma mensagem corrompida, e ele derruba a conexão. Registre os logs usando a configuração padrão do `logging`, cujo handler de stderr faz flush de cada registro; handlers personalizados também precisam evitar o stdout. **[Logging](../handlers/logging.md)** tem a história completa.
O Claude Desktop mantém um log por servidor: `mcp-server-<NAME>.log` é o stderr do seu servidor, ao lado de `mcp.log` para as conexões, em `~/Library/Logs/Claude` no macOS e `%APPDATA%\Claude\logs` no Windows.
Para qualquer coisa além dessas três, a página é **[Solução de problemas](../troubleshooting.md)**.
## Recapitulando {#recap}
* Um **host** (Claude Desktop, uma IDE) executa um cliente MCP que inicia seu servidor como processo filho via stdio. Conectar significa dar a ele um comando de inicialização.
* Esse comando é `uv run --with "mcp[cli]" mcp run /absolute/path/to/server.py`: nenhum venv para ativar, funciona a partir de qualquer diretório.
* **Claude Desktop** é o único host que `mcp install` configura para você. Ele grava esse mesmo comando (mais o caminho absoluto do `uv`, `--frozen` e um pin exato na versão que você tem instalada) em `claude_desktop_config.json`, para você nunca precisar fazer isso.
* **Claude Code** é `claude mcp add bookshop -- <launch command>`. **Cursor** é `.cursor/mcp.json` sob `mcpServers`. **VS Code** é `.vscode/mcp.json` sob `servers`, cada entrada com um `type`.
* Caminhos absolutos em todo lugar, reinicie o host depois de editar a configuração dele, e nunca deixe nada além do SDK escrever no stdout.
Todos os hosts desta página se conectaram ao mesmo arquivo, com o mesmo comando. O que esse arquivo pode *expor* é o resto desta documentação: **[Ferramentas](../servers/tools.md)**, **[Recursos](../servers/resources.md)** e todos os transportes além do stdio em **[Executando seu servidor](../run/index.md)**.

View file

@ -0,0 +1,115 @@
---
translation:
sections: ['4926721070127497', c52a1de2b6b32f40, 2e410b412c25f314, 627195f7159e24ef]
tool: 1
---
# Testes {#testing}
O SDK Python traz uma classe `Client` com um **transporte em memória**: passe a ela o objeto do seu servidor e ela se conecta diretamente a ele.
Sem subprocesso. Sem porta. Sem transporte nenhum. É a mesma ideia do `TestClient` do FastAPI.
## Uso básico {#basic-usage}
Vamos supor que você tenha um servidor simples com uma única ferramenta (tool):
```python title="server.py"
--8<-- "docs_src/testing/tutorial001.py"
```
Para executar o teste abaixo, você vai precisar de duas dependências extras (de desenvolvimento):
=== "uv"
```bash
uv add --dev pytest inline-snapshot
```
=== "pip"
```bash
pip install pytest inline-snapshot
```
!!! info
Esta documentação pressupõe que você já conhece o [`pytest`](https://docs.pytest.org/en/stable/).
O [`inline-snapshot`](https://15r10nk.github.io/inline-snapshot/latest/) é o que o teste abaixo
usa para fazer a asserção sobre o objeto de resultado inteiro em uma única linha. Ele grava a saída
de um teste como o literal `snapshot(...)` que você vê. Se preferir não usá-lo, remova o import e
faça as asserções sobre os campos que interessam (`result.content[0].text == "3"`), como em
qualquer outro teste.
Agora o teste:
```python title="test_server.py"
import pytest
from inline_snapshot import snapshot
from mcp import Client
from mcp.types import CallToolResult, TextContent
from server import mcp
@pytest.fixture
def anyio_backend(): # (1)!
return "asyncio"
@pytest.fixture
async def client(): # (2)!
async with Client(mcp, raise_exceptions=True) as c:
yield c
@pytest.mark.anyio
async def test_call_add_tool(client: Client):
result = await client.call_tool("add", {"a": 1, "b": 2})
# Drop the server identity stamp in `_meta`; it is not what this test is about.
result.meta = None
assert result == snapshot(
CallToolResult(
content=[TextContent(type="text", text="3")],
structured_content={"result": 3},
)
)
```
1. Se você estiver usando `trio`, retorne `"trio"` no lugar. Veja a [documentação do anyio](https://anyio.readthedocs.io/en/stable/testing.html#specifying-the-backends-to-run-on) para os detalhes.
2. A fixture entrega um cliente já conectado. Todo teste que recebe `client` ganha uma conexão em memória nova com o mesmo servidor.
Pronto! Agora você pode estender seus testes para cobrir mais cenários.
## Por que `raise_exceptions=True`? {#why-raise_exceptionstrue}
Duas coisas diferentes podem dar errado, e essa flag só mexe em uma delas.
Uma exceção dentro de uma das **suas ferramentas** não é uma falha de protocolo. Ela vira um resultado normal com
`is_error=True`, e o modelo lê a mensagem. `raise_exceptions` não muda isso: com ou
sem ela, `call_tool` retorna o mesmo resultado com `is_error=True`. Há uma página inteira sobre isso:
**[Tratamento de erros](../servers/handling-errors.md)**.
Uma falha **fora** do corpo de uma ferramenta é diferente. Na conexão que `Client(mcp)` entrega, o
servidor a sanitiza em um genérico `"Internal server error"` antes que o cliente a veja. Você nunca
deve vazar os detalhes de um crash inesperado para um chamador remoto. Em um teste, isso é exatamente o que
você *não* quer, e é isso que `raise_exceptions=True` muda: seu teste vê a mensagem real
em vez da sanitizada.
Deixe-a ligada nos testes. Em código de produção, ela não significa nada.
## No mesmo processo por padrão {#in-process-by-default}
!!! note
`Client(mcp)` se conecta no mesmo processo e é **neutro quanto à era** por padrão: ele sonda o servidor e
escolhe o caminho de protocolo adequado. Fixe `mode="legacy"` se o seu teste exercita semântica
específica do modo legado (push de amostragem (sampling) ou de elicitação (elicitation), `message_handler`), e remova `raise_exceptions=True`
nesse caso: uma conexão legada nem sequer sanitiza, e a flag relança a
falha dentro da task do servidor em vez de no seu teste.
Essa única linha é também o motivo pelo qual esta documentação pode prometer que os exemplos funcionam: cada
arquivo de exemplo é exercitado pela própria suíte de testes do SDK, quase todos exatamente por meio deste
cliente. Você está usando a mesma ferramenta que o SDK usa em si mesmo.
Você tem um servidor funcionando e testado. Colocá-lo dentro de uma aplicação real (Claude Desktop, uma
IDE) é o tema de **[Conecte-se a um host real](real-host.md)**; todas as outras formas de servi-lo estão em
**[Executando seu servidor](../run/index.md)**.