Definindo Ferramentas Chamáveis em um Servidor MCP
Uma ferramenta chamável é a maneira mais direta pela qual um servidor MCP dá ao modelo algo a fazer.
Busque em todas as páginas da documentação
Uma ferramenta chamável é a maneira mais direta pela qual um servidor MCP dá ao modelo algo a fazer.
Acertar uma ferramenta significa que o modelo pode encontrá-la, entender o que ela faz, chamá-la com argumentos válidos e dar sentido ao que retorna.
Uma ferramenta no SDK Python do MCP começa como uma função comum decorada com @mcp.tool().
O nome da função, seus parâmetros com dicas de tipo e sua docstring juntos se tornam o esquema que um cliente usa para decidir quando e como chamá-la.
Os manipuladores devem permanecer estreitos e previsíveis, fazendo um trabalho bem feito em vez de ramificar em vários comportamentos não relacionados com base em um argumento de flag.
Erros devem ser levantados como exceções com uma mensagem clara, não engolidos ou retornados como strings ambíguas, para que o modelo receba feedback útil quando uma chamada falhar.
Entradas estruturadas e validadas (via modelos Pydantic ou dicas de tipo simples) capturam argumentos ruins antes que o código do seu manipulador seja executado.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("tool-recipes")
@mcp.tool()
def get_weather(city: str, units: str = "celsius") -> str:
"""Get the current weather for a city.
Args:
city: The city name, e.g. "Austin" or "Tokyo".
units: Either "celsius" or "fahrenheit". Defaults to "celsius".
"""
if units not in ("celsius", "fahrenheit"):
raise ValueError('units must be "celsius" or "fahrenheit"')
return f"72 degrees {units} and sunny in {city}"Quando usar isso:
# server.py
from typing import Literal
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
mcp = FastMCP("task-tools")
# In-memory store for this example; swap for a real database in production.
_tasks: dict[int, dict] = {}
_next_id = 1
class CreateTaskInput(BaseModel):
title: str = Field(..., min_length=1, max_length=200, description="Short task title")
priority: Literal["low", "medium", "high"] = Field(
"medium", description="Task priority level"
)
@mcp.tool()
def create_task(input: CreateTaskInput) -> str:
"""Create a new task and return its id.
Use this when the user asks to add, track, or remember a to-do item.
"""
global _next_id
task_id = _next_id
_next_id += 1
_tasks[task_id] = {"title": input.title, "priority": input.priority, "done": False}
return f"Created task {task_id}: {input.title} (priority: {input.priority})"
@mcp.tool()
def complete_task(task_id: int) -> str:
"""Mark an existing task as done.
Args:
task_id: The numeric id returned by create_task.
"""
task = _tasks.get(task_id)
if task is None:
raise ValueError(f"No task found with id {task_id}")
task["done"] = True
return f"Task {task_id} ({task['title']}) marked done"
@mcp.tool()
def list_tasks(only_open: bool = True) -> str:
"""List tracked tasks.
Args:
only_open: If True, hide tasks already marked done. Defaults to True.
"""
rows = [
f"#{tid} [{t['priority']}] {t['title']}{' (done)' if t['done'] else ''}"
for tid, t in _tasks.items()
if not (only_open and t["done"])
]
return "\n".join(rows) if rows else "No tasks found."
if __name__ == "__main__":
mcp.run(transport="stdio")O que isso demonstra:
create_task, complete_task, list_tasks) em vez de uma ferramenta com um flag mode.CreateTaskInput) validando uma entrada estruturada com uma restrição de comprimento e um conjunto restrito de valores permitidos.Literal restringindo priority a exatamente três strings válidas, aplicadas antes que o corpo do manipulador seja executado.ValueError com uma mensagem nomeando o problema específico.FastMCP inspeciona cada função decorada com @mcp.tool() e constrói um Esquema JSON a partir de suas dicas de tipo e valores padrão.list_tools(), ele recebe este esquema e descrição para cada ferramenta registrada, sem que você precise escrever nenhum esquema manualmente.Literal), o SDK retorna um erro de esquema para o cliente sem nunca chamar seu manipulador.| Elemento | Boa Prática | Por que Importa |
|---|---|---|
| Nome da ferramenta | snake_case, começando com verbo (create_task, não task) | O modelo associa nomes à intenção; um verbo sinaliza uma ação |
| Primeira linha da docstring | Uma frase declarando exatamente o que a ferramenta faz | Este é o texto principal que o modelo usa para decidir a relevância |
Args: na docstring | Uma linha por parâmetro, linguagem clara | Esclarece unidades, formatos e intervalos válidos que a dica de tipo não pode expressar |
| Nomes dos parâmetros | Descritivos, não abreviados (city, não c) | Reduz chamadas ambíguas ou malformadas |
# Prefer a constrained type over a loose string when the valid set is small and known.
from typing import Literal
@mcp.tool()
def set_status(status: Literal["open", "in_progress", "closed"]) -> str:
"""Set the status of the current item."""
return f"Status set to {status}"
# For richer validation (ranges, formats, nested fields), use a Pydantic model
# as the single parameter, as shown in CreateTaskInput above.| Parâmetro | Tipo | Descrição |
|---|---|---|
| nome da função | str (via nome def) | Torna-se o identificador da ferramenta; mantenha-o estável assim que os clientes dependerem dele |
| dicas de tipo | Tipos Python / Literal / modelo Pydantic | Compilado automaticamente no Esquema JSON da ferramenta |
| docstring | str | Torna-se a descrição da ferramenta mostrada ao modelo |
| valor de retorno | str ou conteúdo estruturado | Envolto no formato de conteúdo do MCP e retornado ao cliente |
handle ou process. O modelo não tem nada para associar a intenção. Correção: use um nome específico, começando com verbo, como create_task ou send_email.mode ou action que ramifica em comportamentos não relacionados. Isso confunde a descoberta baseada em esquema e aumenta a chance de uma chamada incorreta. Correção: divida-a em ferramentas separadas e de escopo restrito.None ou uma string vazia em caso de falha em vez de levantar uma exceção. O modelo não tem sinal de que algo deu errado e pode relatar sucesso incorretamente. Correção: levante uma exceção com uma mensagem específica; deixe o SDK traduzi-la em um erro de ferramenta.Args:.Literal, restrições Field do Pydantic ou verificações explícitas no início do manipulador.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
| Recurso MCP | A operação é uma leitura pura sem efeitos colaterais | O modelo precisa tomar uma ação ou realizar uma computação |
| Prompt MCP | Você deseja padronizar instruções, não expor uma ação chamável | O cliente precisa de um valor de resultado de volta na conversa |
| Chamada de função direta (não MCP) | A capacidade será usada apenas por um aplicativo específico | Você deseja que a mesma capacidade seja reutilizável em Claude Code, Claude Desktop e aplicativos personalizados |
Um manipulador @mcp.server de baixo nível (em vez de FastMCP) | Você precisa de controle total sobre o handshake do protocolo ou fiação de transporte personalizada | Você só precisa expor um punhado de ferramentas diretas rapidamente |
FastMCP lê as dicas de tipo da função para construir um Esquema JSON para seus parâmetros.Dicas de tipo simples (str, int, bool, Literal[...]) são suficientes para ferramentas simples. Use um parâmetro BaseModel do Pydantic quando precisar de restrições em nível de campo, estruturas aninhadas ou lógica de validação reutilizável em várias ferramentas.
O SDK valida os argumentos recebidos contra o esquema gerado antes que seu manipulador seja executado. Argumentos inválidos produzem um erro de esquema retornado ao cliente, e o corpo da sua função nunca é executado.
Específica o suficiente para que um leitor não familiarizado com sua base de código possa adivinhar exatamente quando chamá-la e o que cada argumento significa, incluindo unidades, formatos ou intervalos válidos que não são óbvios a partir da dica de tipo.
Várias ferramentas estreitas, na maioria dos casos. Uma ferramenta com um flag mode ou action esconde vários comportamentos por trás de um único esquema, o que torna mais difícil para o modelo escolher corretamente e mais difícil para você testar cada comportamento isoladamente.
@mcp.tool()
def withdraw(account_id: str, amount: float) -> str:
"""Withdraw funds from an account."""
if amount <= 0:
raise ValueError("amount must be positive")
return f"Withdrew {amount} from {account_id}"Levantar uma exceção padrão com uma mensagem clara é suficiente; o SDK a converte em um erro de ferramenta que o cliente pode mostrar ao modelo.
Nada impede que um manipulador chame uma função Python comum que outra ferramenta também chama, mas as ferramentas não devem chamar umas às outras através da própria camada de protocolo MCP. Compartilhe a lógica através da composição de funções comuns em vez disso.
Chamadas concorrentes podem competir com o estado compartilhado, produzindo resultados inconsistentes ou dados corrompidos. É aceitável para uma demonstração ou protótipo, mas ferramentas de produção devem usar um armazenamento de dados real com garantias transacionais adequadas.
Por padrão, sim, FastMCP usa o nome da função como o nome da ferramenta. Mantenha-o estável assim que qualquer cliente começar a depender dele, pois uma renomeação altera o que o cliente vê durante a descoberta.
Não há limite rígido, mas se uma ferramenta precisar de muitos parâmetros opcionais para cobrir diferentes casos de uso, isso geralmente é um sinal de que ela deve ser dividida em mais de uma ferramenta focada.
Sim. Um parâmetro como units: str = "celsius" se torna um campo opcional no esquema gerado, e o cliente pode omiti-lo completamente para usar o padrão.
Versões da Stack: Escrito contra a linha de modelos Claude atual em ~junho de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (o padrão) e Claude Haiku 4.5 - e a especificação atual do Model Context Protocol. Nomes de modelos, versões de SDK e a especificação MCP mudam rapidamente - verifique os detalhes atuais em platform.claude.com/docs e modelcontextprotocol.io antes de confiar neles.
Revisado por Chris St. John·Última atualização: 16 de jul. de 2026