Introdução ao desenvolvimento de MCP Server: crie seu primeiro serviço MCP do zero

Introdução: Ao programar no Cursor, você já quis que a IA consultasse diretamente a versão mais recente de uma dependência do projeto? Ou, ao analisar dados no Claude, desejou que ele pudesse ler informações do seu banco de dados? Sem um padrão comum, seria preciso criar uma integração para cada ferramenta de IA. Com um MCP Server, você implementa uma vez e qualquer cliente compatível com MCP pode usar o serviço. Neste artigo, você vai criar do zero um MCP Server completo em TypeScript.
O que é MCP? Entenda o conceito principal em 3 minutos
A história de uma porta USB
Quem usava eletrônicos há alguns anos provavelmente se lembra de uma época pouco prática: o mouse tinha um conector redondo, o teclado outro formato e a impressora usava uma porta paralela. Cada dispositivo exigia uma entrada específica. Então surgiu o USB, padronizando tudo em uma única interface.
O MCP (Model Context Protocol) está se tornando o “padrão USB” do universo das ferramentas de IA.
Sem MCP, para permitir que uma IA acesse uma fonte de dados, você precisa criar uma camada de integração para cada ferramenta: um plugin para Claude, uma extensão para Cursor, outra para Windsurf e assim por diante. A complexidade é N x M (N fontes de dados x M ferramentas de IA).
Com MCP, basta criar um MCP Server para que todos os clientes compatíveis possam usá-lo diretamente. A complexidade cai para N+M.
A arquitetura de três camadas é simples:
+-------------+ +-------------+ +-------------+
| Host | -> | Client | -> | Server |
| (Claude) | | (cliente MCP)| | (seu serviço)|
+-------------+ +-------------+ +-------------+
- Host: o próprio aplicativo de IA, como Claude Desktop ou Cursor
- Client: o cliente MCP responsável pela comunicação com o Host
- Server: o serviço que você desenvolve para oferecer funcionalidades específicas
Os três tipos de recurso oferecidos por um MCP Server
Um MCP Server pode disponibilizar três tipos diferentes de funcionalidade:
| Recurso | Finalidade | Exemplo |
|---|---|---|
| Tools (ferramentas) | Executar ações | Consultar o tempo, enviar mensagens, ler um banco de dados |
| Resources (recursos) | Fornecer dados | Conteúdo de arquivos, respostas de API, configurações |
| Prompts | Oferecer modelos predefinidos | Modelo de revisão de código, modelo de relatório diário |
Pense em Tools como “funções”: a IA pode chamá-las para executar uma ação. Resources são “fontes de dados” cujo conteúdo pode ser lido pela IA. Prompts são “modelos” que ajudam a IA a entender uma tarefa mais rapidamente.
Diferença em relação a outros artigos: se você já leu outros tutoriais sobre MCP, talvez tenha visto implementações em Python com FastMCP. Este artigo usa o SDK oficial para TypeScript, mais adequado para desenvolvedores frontend e full stack. As duas opções oferecem recursos equivalentes; escolha a linguagem com a qual você tem mais familiaridade.
"https://modelcontextprotocol.io"
Preparação do ambiente de desenvolvimento
Pré-requisitos
Este artigo pressupõe que você:
- tenha instalado Node.js 18+ ou Bun 1.0+
- já tenha usado TypeScript e saiba o que são
interfaceeasync/await - tenha o Claude Desktop ou outro cliente compatível com MCP, como Cursor ou Windsurf
Se você nunca usou Bun, vale a pena experimentar. Ele é muito mais rápido que npm e oferece suporte integrado a TypeScript, sem exigir configuração adicional de ts-node.
Inicialização do projeto
# Criar o diretório do projeto
mkdir mcp-weather-server && cd mcp-weather-server
# Inicializar (com Bun ou npm)
bun init -y
# ou npm init -y
# Instalar o MCP TypeScript SDK
bun add @modelcontextprotocol/sdk zod
# ou npm install @modelcontextprotocol/sdk zod
O projeto usa duas dependências:
@modelcontextprotocol/sdk: SDK oficial do MCP para TypeScriptzod: validação de tipos em tempo de execução para TypeScript, usada para definir o schema dos parâmetros das ferramentas
Pontos importantes da configuração do TypeScript
Se você usar bun init, o arquivo tsconfig.json já estará configurado. Em uma configuração manual, preste atenção às opções abaixo:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true
}
}
moduleResolution: "bundler" é importante para módulos ESM. Sem essa opção, você pode encontrar erros como “xxx is not defined”.
Na prática: criando um MCP Server de consulta do tempo
Neste tutorial, você vai desenvolver um MCP Server completo capaz de:
- Receber solicitações de uma IA
- Consultar a API do OpenWeatherMap para obter o tempo em tempo real
- Retornar o resultado formatado
Estrutura do projeto
mcp-weather-server/
+-- src/
| +-- index.ts # Arquivo de entrada
| +-- weather.ts # Implementação da ferramenta de clima
| +-- resources.ts # Definição dos recursos
+-- package.json
+-- tsconfig.json
Na prática, todo o código pode ficar em index.ts, como faremos neste artigo. Mesmo assim, separar a implementação em módulos facilita a manutenção.
Etapa 1: crie a estrutura básica do MCP Server
Comece pelo mais simples: um MCP Server que consegue iniciar.
// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Criar a instância do servidor
const server = new McpServer({
name: "weather-service",
version: "1.0.0",
});
// Registrar uma ferramenta (Tools)
server.tool(
"get_weather",
"获取指定城市的当前天气信息",
{
city: z.string().describe("城市名称,如:北京、上海"),
},
async ({ city }) => {
// A implementação será detalhada na próxima seção
return { content: [{ type: "text", text: `查询 ${city} 的天气...` }] };
}
);
// Iniciar o servidor
const transport = new StdioServerTransport();
await server.connect(transport);
McpServer é a classe principal fornecida pelo SDK; você precisa informar name e version. O método tool() registra uma ferramenta: o primeiro argumento é o nome, o segundo é a descrição, o terceiro é o schema dos parâmetros e o último é a função de execução.
Etapa 2: implemente a ferramenta de consulta do tempo (código principal)
Agora vamos fazer a ferramenta funcionar de verdade usando a API gratuita do OpenWeatherMap:
// src/weather.ts
import { z } from "zod";
// Definir o tipo da resposta da API do OpenWeatherMap
interface WeatherResponse {
name: string;
main: { temp: number; feels_like: number; humidity: number };
weather: [{ description: string }];
wind: { speed: number };
}
// Implementação da ferramenta de consulta do tempo
server.tool(
"get_weather",
"获取指定城市的当前天气信息",
{
city: z.string().describe("城市名称,如:北京、上海"),
},
async ({ city }) => {
const API_KEY = process.env.OPENWEATHER_API_KEY;
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric&lang=zh_cn`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`API 请求失败:${response.status}`);
}
const data: WeatherResponse = await response.json();
// Retornar o resultado formatado
return {
content: [
{
type: "text",
text: JSON.stringify({
city: data.name,
temperature: `${data.main.temp}°C`,
feels_like: `${data.main.feels_like}°C`,
description: data.weather[0].description,
humidity: `${data.main.humidity}%`,
wind_speed: `${data.wind.speed} m/s`,
}, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `查询失败:${error instanceof Error ? error.message : '未知错误'}`,
},
],
isError: true,
};
}
}
);
Observe três pontos:
- A API Key é lida de uma variável de ambiente: nunca deixe uma chave fixa no código
- Tratamento de erros: retornar
isError: trueinforma ao cliente que a chamada falhou - Definição de tipos: a interface
WeatherResponsepermite que o TypeScript verifique a estrutura dos dados
Crie uma conta gratuita no OpenWeatherMap. Depois de obter a API Key, defina a variável de ambiente:
export OPENWEATHER_API_KEY=your_api_key_here
Etapa 3: adicione Resources (opcional, mas recomendado)
Resources permite que seu Server forneça dados “somente leitura”. Por exemplo, você pode disponibilizar um recurso para a IA consultar o status do servidor:
// src/resources.ts
// Fornecer informações sobre o status do servidor
server.resource(
"server-status",
"status://server",
async (uri) => ({
contents: [
{
uri: uri.href,
text: JSON.stringify({
name: "Weather Service",
version: "1.0.0",
status: "running",
timestamp: new Date().toISOString(),
}, null, 2),
},
],
})
);
// Fornecer a documentação da API
server.resource(
"api-docs",
"docs://api",
async (uri) => ({
contents: [
{
uri: uri.href,
text: `
# Weather MCP Server API
## Tools
- get_weather(city: string): 获取指定城市天气
## Resources
- status://server - 服务器状态
- docs://api - API 文档
`.trim(),
},
],
})
);
Os dois primeiros argumentos de resource() são o nome e a URI do recurso; o terceiro é a função de leitura. A URI pode usar qualquer scheme, como status:// ou docs://, desde que permita distinguir os recursos.
Etapa 4: adicione Prompts (recurso avançado)
Prompts são modelos predefinidos de conversa. Você pode criar, por exemplo, um modelo de “relatório do tempo” que preenche automaticamente o nome da cidade:
// Modelo predefinido de relatório do tempo
server.prompt(
"weather_report",
"生成一份格式化的天气报告",
{
city: z.string().describe("城市名称"),
include_tips: z.boolean().optional().describe("是否包含穿衣建议"),
},
({ city, include_tips }) => ({
messages: [
{
role: "user",
content: {
type: "text",
text: `请为${city}生成一份天气报告。${include_tips ? "同时提供穿衣建议。" : ""}`,
},
},
],
})
);
O retorno de prompt() é um array de mensagens, cada uma com role e content. Assim, ao usar o prompt, a IA recebe diretamente o contexto predefinido.
Etapa 5: finalize o arquivo de entrada
Reúna todo o código acima em src/index.ts e adicione o tratamento de erros:
// src/index.ts (versão completa)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "weather-service",
version: "1.0.0",
});
// Registrar todas as ferramentas, recursos e prompts
// ... (código mostrado acima)
// Tratamento de erros
process.stdin.on("error", (err) => {
console.error("标准输入错误:", err);
process.exit(1);
});
process.stdout.on("error", (err) => {
console.error("标准输出错误:", err);
process.exit(1);
});
// Encerramento controlado
process.on("SIGINT", async () => {
await server.close();
process.exit(0);
});
// Iniciar o servidor
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP Weather Server 已启动,等待连接...");
StdioServerTransport usa a entrada e a saída padrão para comunicação; por isso, é importante tratar erros de stdin/stdout. O tratamento de SIGINT permite encerrar o serviço de forma controlada com Ctrl+C.
Execute bun run src/index.ts. Se a mensagem de inicialização aparecer, está tudo funcionando.
Configuração do cliente: conecte o Claude ao seu Server
Com o Server pronto, agora vamos permitir que Claude Desktop ou Cursor o chamem.
Configuração do Claude Desktop
Localize o arquivo de configuração:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Adicione a configuração do seu Server:
{
"mcpServers": {
"weather": {
"command": "bun",
"args": ["run", "/absolute/path/to/mcp-weather-server/src/index.ts"],
"env": {
"OPENWEATHER_API_KEY": "你的 API Key"
}
}
}
}
Atenção: o caminho em args deve ser absoluto. Um caminho relativo fará o Server falhar ao iniciar.
Configuração do Cursor e do Windsurf
A configuração no Cursor e no Windsurf é semelhante. Nas preferências da IDE, localize a configuração de MCP e adicione uma entrada para o servidor usando o mesmo formato acima.
O arquivo de configuração do Cursor costuma ficar em:
- macOS:
~/Library/Application Support/Cursor/User/globalStorage/state.vscdb - ou, na IDE: Configurações -> AI -> MCP -> Adicionar servidor
Teste seu Server
- Reinicie o Claude Desktop ou o Cursor
- Digite na conversa: “Consulte o tempo em Pequim”
- O Claude deve chamar automaticamente seu MCP Server
Se você vir um resultado semelhante ao exemplo abaixo, deu certo:
{
"city": "北京",
"temperature": "18°C",
"feels_like": "16°C",
"description": "多云",
"humidity": "65%",
"wind_speed": "3.2 m/s"
}
Solução de problemas comuns
| Problema | Possível causa | Solução |
|---|---|---|
| Server não conectado | Caminho incorreto | Verifique o caminho absoluto em args |
| API Key inválida | Variável de ambiente não repassada | Confirme se a configuração de env está correta |
| Sem resposta | Erro de compilação do TypeScript | Compile primeiro com bun build ou tsc |
| Erro de permissão | Permissões do arquivo de configuração | Verifique se o arquivo pode ser lido |
"https://github.com/modelcontextprotocol/typescript-sdk"
Sugestões para expansão e implantação
Adicione mais ferramentas
A consulta do tempo é apenas o começo. Você também pode criar:
- Consulta do histórico meteorológico: chamar uma API de dados históricos para obter o tempo em uma data anterior
- Comparação entre cidades: consultar várias cidades de uma só vez e retornar uma tabela comparativa
- Alertas meteorológicos: verificar a existência de alertas de condições severas
O registro dessas ferramentas funciona exatamente como em get_weather; apenas a lógica de implementação muda.
Comparação das opções de implantação
Se você quiser compartilhar o Server com sua equipe, o transporte stdio local não será suficiente. Veja algumas opções:
| Forma de implantação | Cenário indicado | Vantagens | Desvantagens |
|---|---|---|---|
| stdio local | Uso pessoal e testes de desenvolvimento | Simples e seguro | Não permite compartilhamento |
| HTTP/SSE | Equipes e vários usuários | Acesso remoto | Exige autenticação |
| Serverless | Ambiente de produção | Escalabilidade automática | Latência na inicialização a frio |
Cuidados no ambiente de produção
Autenticação: em uma implantação HTTP, a autenticação é obrigatória. O MCP oferece suporte a OAuth 2.1, mas você também pode usar uma API Key simples:
// Verificar a API Key no cabeçalho da requisição
const apiKey = request.headers.get("Authorization");
if (apiKey !== `Bearer ${process.env.API_KEY}`) {
return new Response("Unauthorized", { status: 401 });
}
Limitação de requisições: impeça que chamadas mal-intencionadas esgotem sua cota de API. Você pode usar express-rate-limit ou a limitação integrada do Cloudflare Workers.
Logs: use pino ou winston para registrar as chamadas das ferramentas e facilitar a investigação de problemas:
import pino from "pino";
const logger = pino();
server.tool("get_weather", /* ... */, async ({ city }) => {
logger.info({ city }, "查询天气");
// ...
});
Monitoramento: acompanhe a taxa de sucesso e o tempo de resposta das chamadas. Prometheus + Grafana é uma combinação comum.
Conclusão
Este artigo mostrou como criar um MCP Server do zero usando TypeScript. Você aprendeu a:
- entender os conceitos principais e a arquitetura de três camadas do MCP
- criar um servidor com o MCP TypeScript SDK
- implementar uma ferramenta de consulta do tempo (Tools)
- adicionar um recurso de status do servidor (Resources)
- definir um modelo de relatório do tempo (Prompts)
- configurar Claude Desktop e Cursor para chamar o Server
Agora você pode:
- Criar wrappers MCP para as APIs que usa com frequência, como GitHub, Slack e Notion
- Criar interfaces MCP para sistemas internos, como CRM e bancos de dados
- Explorar o que a comunidade MCP já desenvolveu
Recursos para continuar aprendendo:
Para entender melhor como o protocolo MCP funciona, leia também Entenda os fundamentos do protocolo MCP.
FAQ
Que conhecimentos são necessários para desenvolver um MCP Server?
Qual é a diferença entre MCP Server e FastMCP?
Como testar se o MCP Server está funcionando?
É possível implantar um MCP Server em um servidor remoto?
11 min de leitura · Publicado em: 19 mar 2026 · Atualizado em: 8 set 2026
Guia prático MCP
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Protocolo MCP: como funciona e como criar um servidor com FastMCP
Entenda como o protocolo MCP padroniza a integração entre ferramentas de IA e fontes de dados e crie, com FastMCP, um servidor de consulta do clima.
Parte 1 de 4
Próximo
Tutorial prático de MCP: guia completo para consultar bancos de dados e chamar APIs diretamente no Cursor
Aprenda passo a passo a configurar um MCP Server para que Cursor e Claude consultem bancos SQLite/PostgreSQL e chamem APIs diretamente, com exemplos completos e soluções para problemas comuns em apenas 15 minutos.
Parte 3 de 4



Comentários
Entre com GitHub para comentar