
Eu Quase Implementei JSON-RPC para um Servidor MCP. Oito Ferramentas Depois, Estou Feliz por Não Ter Feito.
Quando construí o servidor MCP para este projeto — que combina GitHub e DEV.to em um conjunto de ferramentas que um agente pode chamar — eu tinha uma decisão a tomar antes de escrever uma única ferramenta: conversar diretamente com o protocolo MCP de baixo nível ou usar a API de decoradores do FastMCP. Eu vi alguns artigos sobre "seu primeiro servidor MCP" ultimamente que percorrem o caminho de baixo nível porque é mais "honesto" sobre o que o MCP realmente é por trás das cenas — JSON-RPC sobre stdio, um handshake de capacidades, esquemas de solicitação/resposta tipados. Isso é verdade, e é uma coisa razoável querer entender. Mas eu quero escrever sobre o outro lado: o que realmente custa na prática uma vez que você tem mais de uma ou duas ferramentas, porque eu passei por ambos e a diferença apareceu rapidamente.
o que o caminho de baixo nível realmente pede para você escrever
Remova o decorador e o MCP é um servidor JSON-RPC. Para cada ferramenta que você adiciona, você é responsável por:
- Registrar o nome da ferramenta, descrição e um esquema JSON para suas entradas em um manipulador
list_tools - Escrever um despachante
call_toolque combina com o nome da ferramenta e desempacota argumentos manualmente - Serializar o valor de retorno nos tipos de wrapper
TextContent/ImageContentque o MCP espera - Manter o esquema que você escreveu na etapa 1 em sincronia com os argumentos que você realmente leu na etapa 2, manualmente, para sempre
Nada disso é difícil isoladamente. O problema é que é boilerplate que escala linearmente com a contagem de ferramentas e não tem nenhuma conexão com a lógica real da ferramenta. Meu servidor tem 8 ferramentas. Se eu tivesse feito manualmente, seriam 8 blocos de esquema mais uma cadeia de despachante if/elif mais 8 chamadas de empacotamento de resposta, tudo isso existe puramente para satisfazer o protocolo, não para fazer nada que uma chamada de API do GitHub ou DEV.to precise.
como é com FastMCP
Aqui está uma ferramenta real de server.py, não editada:
@mcp.tool()
def get_repo_stats(repo: str) -> dict:
"""Obter estrelas, forks, observadores, problemas abertos para enjoykumawat/<repo>."""
r = _gh(f"/repos/{GITHUB_USERNAME}/{repo}")
return {
"name": r["name"],
"stars": r["stargazers_count"],
"forks": r["forks_count"],
"watchers": r["watchers_count"],
"open_issues": r["open_issues_count"],
"language": r.get("language"),
"description": r.get("description"),
}
O tipo de dica (repo: str) se torna o esquema JSON. A docstring se torna a descrição da ferramenta que o agente vê ao decidir se deve chamá-la. O tipo de retorno se torna a forma da resposta. Não há esquema para manter em sincronia manualmente — ele é derivado da mesma assinatura que o Python já verifica quanto ao tipo. Adicionar uma nona ferramenta é: escrever uma função, colocar @mcp.tool() acima dela, feito. Nenhum despachante para atualizar, nenhum empacotamento TextContent, nenhum código de camada de protocolo tocado.
Nossa ADR de quando tomei essa decisão é direta sobre a troca:
Decisão: Usar
FastMCPdemcp.server.fastmcpcom decoradores@mcp.tool(). Chamadas HTTP viaurllibda stdlib.
Alternativas Consideradas: Servidor MCP de baixo nível → rejeitado (complexidade desnecessária para este caso de uso).
Consequências:mcp[cli]é a única dependência. O servidor pode ser executado viapython server.pyoumcp dev server.py.
"Complexidade desnecessária para este caso de uso" está fazendo muito trabalho nessa frase, e eu acho que é a lente certa: a API de baixo nível existe porque alguém precisa escrever bibliotecas de cliente e servidor MCP, ou precisa de controle em nível de protocolo (streaming de resultados parciais, negociação de capacidade personalizada, transportes não padronizados). Se você está expondo 8 wrappers de API REST a um agente, você não é essa pessoa, e o código de camada de protocolo que você escreveria não tem relação com o seu problema real.
onde a abordagem do decorador me prejudicou
Não é gratuito. Dois problemas reais de realmente executar este servidor:
A coerção do tipo de retorno é implícita e fácil de errar sutilmente. list_articles retorna um list[dict] construído por uma compreensão de lista sobre a resposta da API DEV.to. O FastMCP serializa isso bem — mas a primeira versão dessa função esqueceu de limitar per_page, e um chamador passando per_page=1000 teria recebido silenciosamente o que a API do DEV.to realmente retorna para um valor fora do intervalo (acontece que ele limita, mas eu tive que verificar, porque a geração de esquema do FastMCP a partir de per_page: int = 10 não impõe um limite superior apenas porque a docstring diz que um existe):
@mcp.tool()
def list_articles(per_page: int = 10) -> list:
"""Liste seus artigos publicados no DEV.to.Empresas brasileiras que utilizam agentes de IA podem se beneficiar da implementação simplificada de servidores MCP. A escolha entre uma abordagem de baixo nível e uma API decoradora pode impactar a eficiência e a manutenção do código. O uso de ferramentas como FastMCP pode facilitar a integração com APIs existentes.
Noticias relacionadas

Criei um Servidor MCP de Verificação de Email em 24 Horas — Veja Como
O artigo detalha a construção de um servidor MCP para verificação de emails, abordando a importância de evitar emails inválidos em produtos SaaS e como isso pode impactar a economia de ferramentas de IA.

Como encontrar servidores MCP seguros para seu assistente de codificação de IA (e por que a maioria dos diretórios mente)
Descubra a verdade sobre a segurança dos servidores MCP e como evitar listas não auditadas. Aprenda sobre o MarketNow, que audita cada servidor e oferece uma camada de confiança para agentes de IA.

Capturei um trojan no meu marketplace MCP. Aqui está a defesa em 8 camadas que construí.
Um trojan foi encontrado em um marketplace MCP, levando à construção de uma defesa em 8 camadas. O artigo detalha a arquitetura de segurança implementada para prevenir futuras infecções.
Gostou do conteudo?
Receba toda semana as principais novidades sobre WebMCP.