Como criar um servidor MCP compatível com a ClaudIA
A tela ClaudIA > Servidores MCP permite conectar ferramentas externas aos seus agentes usando o Model Context Protocol
(MCP). Este artigo descreve exatamente o que a plataforma exige de um servidor para que ele passe no teste de conexão e
funcione em produção.
A ClaudIA usa um subconjunto pequeno e bem definido do protocolo. Se o seu servidor cumprir os pontos abaixo, ele vai
funcionar.
1. Transporte: Streamable HTTP
A ClaudIA fala apenas o transporte Streamable HTTP da especificação MCP (revisão 2025-03-26 ou mais recente).
Na prática, isso significa:
- Um único endpoint HTTP (por exemplo, https://seu-host/mcp) que aceita POST com um corpo JSON-RPC 2.0.
- A resposta pode ser Content-Type: application/json (um único objeto JSON) ou Content-Type: text/event-stream (SSE).
Os dois formatos são aceitos.
- O servidor pode ser stateless ou stateful (devolvendo o header Mcp-Session-Id no initialize). Ambos funcionam.
O que não funciona:
- Servidores stdio (o formato dos tutoriais para Claude Desktop). Eles não têm URL HTTP.
- O transporte HTTP+SSE antigo (protocolo 2024-11-05), com endpoints separados /sse e /messages. A ClaudIA não faz
fallback para esse formato.
A maioria dos quickstarts públicos ensina stdio ou SSE. Confira qual transporte o seu framework está usando antes de
testar.
2. Handshake e métodos usados
A cada uso, a ClaudIA abre uma sessão nova e executa, nesta ordem:
1. initialize: o servidor precisa responder com capabilities.tools declarado.
2. notifications/initialized
3. tools/list: para descobrir as ferramentas.
4. tools/call: quando o agente decide usar uma ferramenta.
Nenhum outro recurso do protocolo é consumido: resources, prompts, sampling, elicitation, roots e a notificação
tools/list_changed são ignorados. O servidor não precisa implementá-los.
Como cada chamada abre uma sessão nova, o servidor precisa aceitar muitas sessões curtas sem problema.
3. URL
- Cole a URL completa do endpoint MCP, com o path (ex.: https://seu-host/mcp ou https://seu-host/v1/minha-api/mcp).
- A URL é usada exatamente como colada. A plataforma não adiciona /mcp automaticamente.
- Apenas http e https são aceitos.
4. Autenticação
| Tipo escolhido na tela | O que o servidor recebe | |---|---| | Nenhum | Nenhum header de autenticação | | Bearer Token
| Authorization: Bearer <token> | | API Key | X-Api-Key: <token> |
Você também pode adicionar cabeçalhos personalizados. Os nomes Authorization, X-Api-Key e X-Authorization são reservados
e não podem ser usados como cabeçalho personalizado.
Se o servidor responder 401 ou 403, o teste de conexão mostra "Falha na autenticação".
5. Formato das ferramentas
Cada item retornado em tools/list precisa ter:
- name: identificador único. Use apenas letras minúsculas, números, _ e -. A ClaudIA prefixa o nome com o
identificador do servidor, então nomes curtos e descritivos funcionam melhor.
- description: texto claro do que a ferramenta faz e quando usar. É isso que o modelo lê para decidir chamar a
ferramenta.
- inputSchema: JSON Schema do tipo object descrevendo os parâmetros.
Exemplo mínimo:
{
"name": "consultar_pedido",
"description": "Retorna o status de um pedido a partir do número informado pelo cliente.",
"inputSchema": {
"type": "object",
"properties": {
"numero_pedido": { "type": "string", "description": "Número do pedido" }
},
"required": ["numero_pedido"]
}
}
6. Formato do resultado de tools/call
- Retorne o resultado em content como blocos {"type": "text", "text": "..."}. É esse texto que o agente lê.
- structuredContent é aceito, mas o agente usa apenas o bloco de texto. Se usar structuredContent, mantenha também o
JSON serializado em um bloco text, como a especificação recomenda.
- Para erros de negócio (pedido não encontrado, API externa indisponível), retorne isError: true com a explicação em
content. O agente recebe a mensagem e consegue reagir.
7. Tempo limite
| Etapa | Limite | |---|---| | Conexão TCP/TLS | 5 segundos | | initialize e tools/list | 30 segundos | | tools/call em
produção | 180 segundos |
Um servidor que demora mais que isso aparece como "Servidor inacessível" no teste ou como falha de ferramenta na
conversa.
8. O que o botão "Testar conexão" faz
O teste executa exatamente o mesmo initialize + tools/list que o agente executa em produção, com a URL, o tipo de
autenticação e os cabeçalhos informados na tela. Se passar, o servidor será chamado dessa mesma forma. Nada é salvo
durante o teste.
| Mensagem na tela | Causa provável | |---|---| | Servidor inacessível | DNS, porta fechada, TLS inválido, timeout, ou o
servidor respondeu 404/405 ao POST (sintoma típico de servidor SSE antigo ou stdio) | | A resposta do servidor não é uma
resposta MCP válida | O corpo não é JSON-RPC 2.0, ou o initialize não declarou capabilities.tools | | O servidor
retornou um erro | O servidor respondeu com um error JSON-RPC ao initialize ou ao tools/list | | Falha na autenticação |
401 ou 403 |
9. Exemplos mínimos
Python (FastMCP, SDK oficial)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("meu-servidor", stateless_http=True)
@mcp.tool()
def consultar_pedido(numero_pedido: str) -> str:
"""Retorna o status de um pedido a partir do número informado pelo cliente."""
return f"Pedido {numero_pedido}: em transporte"
if __name__ == "__main__":
mcp.run(transport="streamable-http")
O endpoint fica em http://host:8000/mcp. Publique atrás de HTTPS e cole essa URL na ClaudIA.
TypeScript (SDK oficial)
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";
const app = express();
app.use(express.json());
app.post("/mcp", async (req, res) => {
const server = new McpServer({ name: "meu-servidor", version: "1.0.0" });
server.registerTool(
"consultar_pedido",
{
description: "Retorna o status de um pedido a partir do número informado pelo cliente.",
inputSchema: { numero_pedido: z.string() },
},
async ({ numero_pedido }) => ({
content: [{ type: "text", text: `Pedido ${numero_pedido}: em transporte` }],
}),
);
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
res.on("close", () => { transport.close(); server.close(); });
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(3000);
Checklist rápido
- [ ] Endpoint único aceitando POST JSON-RPC (Streamable HTTP)
- [ ] initialize responde com capabilities.tools
- [ ] tools/list retorna name, description e inputSchema
- [ ] tools/call retorna content com blocos text
- [ ] Autenticação via Authorization: Bearer ou X-Api-Key, conforme escolhido na tela
- [ ] Responde em menos de 30 segundos ao initialize e ao tools/list
- [ ] URL completa, com path, em HTTPS
Referências
- Especificação MCP: transportes (Streamable HTTP)
- Especificação MCP: tools
- SDKs oficiais do MCP