
Construindo um servidor MCP em Python: o que aprendi sobre design de ferramentas
A primeira versão do meu servidor MCP funcionou e ainda era inútil. Cada ferramenta retornava dados corretos quando eu a chamava manualmente no Inspector, e então apontei Claude para ele e assisti o modelo fazer a coisa errada cerca de um terço das vezes. Ele procurava um valor que já tinha um nome de coluna exato, percorria as linhas uma a uma ou inventava uma coluna chamada Name para uma planilha cuja coluna era Student Name.
Nada disso era um bug no meu código. Os manipuladores estavam bem. O que estava errado era tudo que eu havia escrito sobre os manipuladores: os nomes das ferramentas, as descrições, a documentação dos argumentos e as mensagens de erro. O modelo só vê isso. Ele nunca vê sua implementação, então sua implementação não é o que ele está raciocinando.
MCP, o Protocolo de Contexto do Modelo, é a maneira padrão como clientes de IA como Claude e Cursor se comunicam com dados externos. Um servidor MCP anuncia uma lista fixa de ferramentas, e o cliente só pode chamar o que essa lista contém. Eu construo PasteSheet, que publica uma Planilha do Google como um servidor MCP somente leitura, então toda a minha superfície é três ferramentas sobre uma planilha. Isso acaba sendo suficiente para errar de muitas maneiras instrutivas.
Uma divulgação antes do código. Meu servidor está escrito em PHP, não em Python. As lições abaixo são em nível de protocolo e não em nível de linguagem, e estou mostrando-as em Python porque o SDK oficial é o que a maioria das pessoas procura quando constrói um desses. Tudo aqui é uma decisão real que implementei, traduzida.
Configuração
O SDK é uma instalação, e a superfície da ferramenta são funções simples com dicas de tipo.
uv add "mcp[cli]" # ou: pip install "mcp[cli]"
from typing import Annotated, Literal
from mcp.server import MCPServer
from mcp.types import ToolAnnotations
from pydantic import Field
mcp = MCPServer("Sheet")
if __name__ == "__main__":
mcp.run(transport="streamable-http", port=3001)
Você não escreve JSON Schema. As dicas de tipo são o esquema, o que é agradável até o momento em que você percebe que o modelo está lendo sua prosa e não seus tipos.
Dê ao modelo uma maneira barata de aprender seus dados
Meu primeiro erro foi assumir que o modelo conhecia a forma da planilha. Ele não conhece, então ele adivinha, e um nome de coluna adivinhado significa um resultado vazio que o modelo então relata como "não há linhas correspondentes." Esse é o pior modo de falha disponível para você, porque parece uma resposta.
A correção é uma ferramenta de descoberta separada e barata cuja docstring diz quando usá-la.
@mcp.tool(
title="Obter as colunas da planilha",
annotations=ToolAnnotations(read_only_hint=True, idempotent_hint=True),
)
def get_schema() -> dict:
"""Obter as colunas da planilha conectada.
Chame isso antes de query_rows para que os filtros usem nomes de coluna reais.
"""
return {"columns": ["Student Name", "Grade", "Status", "Enrolled On"]}
Aquela segunda linha da docstring fez mais pela precisão do que qualquer mudança que fiz na lógica da consulta. Note as anotações também: read_only_hint e idempotent_hint informam ao cliente que essa chamada não muda nada, o que permite que alguns clientes parem de pedir ao usuário para aprovar cada leitura.
Quando dois argumentos se sobrepõem, diga qual deles vence
Essa foi a lição cara. Minha ferramenta de leitura de linhas aceita filtros exatos, correspondência parcial sem diferenciação entre maiúsculas e minúsculas e uma pesquisa de texto completo em todas as colunas. Ofereça ao modelo todos os três e ele escolherá o mais solto, toda vez. A pesquisa de texto completo é a opção que não pode falhar completamente, então é a escolha segura do ponto de vista do modelo, e também é a mais lenta e menos precisa.
Você não pode corrigir isso documentando em outro lugar. O único texto que o modelo lê são as descrições em si, então a orientação deve viver dentro delas e deve ser mútua, com cada argumento mais solto apontando de volta para o mais apertado.
@mcp.tool(
title="Ler linhas",
annotations=ToolAnnotations(read_only_hint=True, idempotent_hint=True),
)
def query_rows(
filters: Annotated[
dict[str, str] | None,
Field(
deEmpresas brasileiras que utilizam IA para manipulação de dados podem se beneficiar ao entender como a documentação e a estrutura de ferramentas impactam a eficácia dos modelos. A implementação correta do MCP pode otimizar a interação entre IA e dados, melhorando a precisão nas respostas.
Noticias relacionadas
Servidor MCP 1.1.0 da endoflife.ai: Exposição KEV, verificações SBOM e dispositivos de borda para agentes de IA
O servidor MCP da endoflife.ai agora possui dez ferramentas de leitura. Novas funcionalidades incluem exposição a vulnerabilidades conhecidas e status de dispositivos de borda, essenciais para a segurança de versões de software.

O que os agentes de IA realmente veem ao buscar seu Ator Apify
O artigo explora como os agentes de IA interagem com o servidor MCP da Apify, destacando a importância da descrição e otimização dos Ators para serem encontrados. Inclui uma análise de como os resultados de busca diferem entre humanos e agentes.
Como o Protocolo de Contexto do Modelo (MCP) muda para sempre o lançamento de recursos em SaaS
O Protocolo de Contexto do Modelo (MCP) é mais do que leitura de dados; sua aplicação mais poderosa é a orquestração de aplicativos em tempo de execução, permitindo que agentes de IA gerenciem anúncios e guias de onboarding sem código efêmero.
Gostou do conteudo?
Receba toda semana as principais novidades sobre WebMCP.