Primeiros passos
Esta página foi traduzida por máquina
As traduções desta documentação são geradas automaticamente a partir das páginas em inglês, e a versão em inglês desta página é a oficial.
Encontrou um problema na tradução? Veja como as traduções funcionam e como reportar um problema.
A página inicial vai direto ao ponto: escreva um servidor, execute-o, chame 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
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 agente. É com ele que o usuário conversa.
- Um cliente vive dentro do host e fala MCP. O host roda um cliente para cada servidor ao qual está conectado.
- Um servidor é o que você constrói com este SDK. Ele expõe coisas para os clientes. Nunca fala diretamente com o modelo.
Você escreve o servidor. Os hosts são produto de outra pessoa. O SDK também te dá um Client. Você vai usá-lo para testar seus servidores, e ele aparece mais adiante nesta página.
Os três primitivos
Um servidor expõe exatamente três tipos de coisa. O que separa um do outro é quem decide usá-los:
| Primitivo | Controlado por | O que é | Exemplo |
|---|---|---|---|
| Ferramentas | O modelo | Uma função que o modelo chama para executar uma ação | Uma chamada de API, uma escrita em banco |
| 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 |
"Controlado por" é a razão de ser dessa 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 escolheu usá-lo.
Info
Se você já construiu uma API web, boa parte da intuição já está aí: um recurso é um GET
(carrega dados e não muda nada) e uma ferramenta é um POST (faz trabalho e pode ter
efeitos colaterais). Um prompt não tem análogo em HTTP; está mais para uma query salva que o
usuário executa pelo nome.
Um servidor, os três
from mcp.server import MCPServer
mcp = MCPServer("Demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
@mcp.prompt()
def summarize(text: str) -> str:
"""Summarize a piece of text in one sentence."""
return f"Summarize the following text in one sentence:\n\n{text}"
Três funções simples, três decoradores. Cada decorador é o registro inteiro:
@mcp.tool()transformaaddem uma ferramenta.@mcp.resource("greeting://{name}")transformagreetingem um template de recurso: o{name}na URI é o parâmetro da função.@mcp.prompt()transformasummarizeem um prompt. A string que ele retorna vira uma mensagem do 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 type hints. 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
Execute com o MCP Inspector:
uv run mcp dev server.py
Abra a URL que ele imprime. O Inspector tem uma aba por primitivo; percorra elas na ordem.
Tools. 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.
Resources. A lista Resources está vazia. greeting está em Resource Templates, porque greeting://{name} tem um parâmetro: não existe um recurso único para listar até alguém fornecer um name. Informe World e leia:
Hello, World!
Prompts. Uma entrada: summarize, com um único argumento obrigatório text. Busque-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 sobre stdio, um dos transportes que um servidor MCP pode falar. Você ainda não precisa escolher um; Executando seu servidor é a página para isso.
Capacidades
Você viu três abas no Inspector. Como ele soube que eram três?
Quando um cliente se conecta, o servidor declara suas capacidades: quais famílias de requisições ele vai atender. O cliente usa essa declaração para decidir o que sequer vale a pena pedir. Você nunca escreveu isso; o MCPServer declara por você.
Veja com seus próprios olhos. O Client do SDK aceita o objeto do servidor diretamente e se conecta a ele em memória (sem subprocesso, sem porta):
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())
{'prompts': {'list_changed': True}, 'resources': {'subscribe': True, 'list_changed': True}, 'tools': {'list_changed': True}}
Esse dicionário são as capacidades declaradas 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 os três primitivos, então os três são sempre declarados.
Repare no que não está 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 está ausente e um cliente bem-comportado não vai pedir. Essa é a regra para tudo que é opcional: registre a coisa e a capacidade aparece; Completions comprova isso.
Info
Client(mcp) é o mesmo cliente em memória com que todos os exemplos destes docs são testados, e
é assim que você vai testar os seus. Ele tem uma página inteira: Testes.
O que você não escreveu
Releia esta página. Você escreveu três funções Python pequenas. Você não escreveu:
- Um JSON Schema.
a: int, b: inté o schema deadd. - Um handler de requisição.
tools/list,resources/read,prompts/get: todos servidos para você. - Uma declaração de capacidades. O
MCPServerfez isso por você. - Uma linha de protocolo. A negociação de versão, o enquadramento JSON-RPC, a troca de capacidades: tudo aconteceu dentro do
mcp deve doClient(mcp), e você nunca viu.
Essa proporção é a razão de ser do SDK.
Recapitulando
- 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 primitivo:
@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 são declaradas por você automaticamente, e um cliente só pede aquilo que o servidor declara.
Client(mcp)se conecta ao objeto do servidor em memória: sua bancada de testes desde o primeiro dia.
O próximo passo é Conectar a um host real: esse servidor dentro do Claude Desktop ou de uma IDE, para valer. Depois, Testes: uma página, um cliente em memória, e você nunca mais fica no chute sobre se aquilo funciona. Em seguida, cada primitivo ganha sua própria página, começando por aquele que o modelo comanda: Ferramentas.