@7leirbag/my-first-mcp
v1.0.2
Published
Servidor MCP educacional com tools de data/hora
Readme
@7leirbag/my-first-mcp
Servidor MCP (Model Context Protocol) educacional escrito em TypeScript. O objetivo deste projeto é demonstrar o ciclo completo de desenvolvimento de um MCP server: implementação local → build → publicação no npm → consumo via npx em qualquer cliente compatível (Kiro, Claude Desktop, etc.).
Por que TypeScript?
JavaScript puro funcionaria, mas TypeScript foi escolhido por razões práticas:
- Tipagem estática: as interfaces do SDK MCP (
McpServer,StdioServerTransport,CallToolResult) são complexas. Com tipos, o editor aponta erros antes de rodar qualquer coisa. - Autocompletar: ao implementar cada tool, o TypeScript garante que a resposta retornada tem exatamente a forma que o SDK espera —
{ content: [{ type: "text", text: string }] }. - Documentação viva: as interfaces em
src/types.tsservem como contrato entre as tools e o entry point. Qualquer desvio vira erro de compilação, não bug em produção. - Ecossistema: o SDK oficial
@modelcontextprotocol/sdké escrito em TypeScript e exporta seus tipos, então aproveitamos isso ao máximo.
O custo é um passo extra de compilação (tsc), mas o ganho em clareza e segurança compensa — especialmente num projeto educacional onde entender as interfaces é o objetivo.
Como o MCP funciona
O Model Context Protocol define um padrão de comunicação entre um cliente (ex: Kiro, Claude Desktop) e um servidor que expõe ferramentas (tools). A comunicação acontece via JSON-RPC 2.0.
O transporte usado aqui é stdio: o cliente inicia o processo do servidor e troca mensagens pelo stdin/stdout. Não há porta de rede, não há servidor HTTP — tudo é local e efêmero.
Cliente MCP (Kiro)
│
│ stdin → JSON-RPC request
▼
Processo Node.js (@7leirbag/my-first-mcp)
│
│ stdout → JSON-RPC response
▼
Cliente MCP (Kiro)Quando o cliente fecha a conexão (fecha o stdin), o processo do servidor encerra automaticamente.
Estrutura do projeto
my-first-mcp/
├── src/
│ ├── index.ts # Entry point: instancia o servidor e registra as tools
│ ├── types.ts # Interfaces compartilhadas entre as tools
│ └── tools/
│ ├── datetime.ts # Tool: get_current_datetime
│ ├── server-info.ts # Tool: get_server_info
│ └── date-diff.ts # Tool: calculate_date_diff
├── dist/ # Saída do compilador TypeScript (gerado pelo build)
├── scripts/
│ └── add-shebang.cjs # Script pós-build: injeta #!/usr/bin/env node
├── package.json
└── tsconfig.jsonPasso a passo: o que foi feito para o MCP funcionar
1. Configurar o projeto
package.json com os campos essenciais para publicação e execução:
{
"name": "@7leirbag/my-first-mcp",
"type": "module",
"main": "dist/index.js",
"bin": { "my-first-mcp": "dist/index.js" },
"files": ["dist"],
"engines": { "node": ">=18" }
}"type": "module"— habilita ES Modules, necessário paratop-level awaitno entry point"bin"— diz ao npm qual arquivo executar quando o pacote for chamado vianpx"files"— garante que apenas a pastadist/(código compilado) seja publicada no registry
tsconfig.json com configurações para Node.js moderno:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "dist",
"strict": true
}
}module: Node16— usa a resolução de módulos do Node.js, exigindo extensão.jsnos importstarget: ES2022— permitetop-level awaite outras features modernas
2. Implementar as tools
Cada tool é uma função pura em src/tools/ que recebe parâmetros e retorna um ToolResponse:
interface ToolResponse {
content: Array<{ type: "text"; text: string }>;
isError?: boolean;
}Esse é o contrato do SDK MCP: toda tool deve retornar um objeto com content contendo um array de blocos de texto. Se algo der errado, isError: true sinaliza o erro para o cliente.
get_current_datetime — usa Intl.DateTimeFormat para validar o timezone IANA e formatar a data sem offset:
export function getCurrentDatetime(params: { timezone?: string }): ToolResponseget_server_info — captura startedAt no momento do boot do módulo (variável de nível de módulo) e retorna metadados do servidor:
const startedAt = new Date().toISOString(); // capturado uma vez, no boot
export function getServerInfo(): ToolResponsecalculate_date_diff — valida ambas as datas com regex + new Date(), calcula a diferença absoluta em minutos e decompõe em dias, horas e minutos:
export function calculateDateDiff(params: { date1: string; date2: string }): ToolResponse3. Registrar as tools no servidor MCP
Em src/index.ts, o McpServer do SDK oficial é instanciado e cada tool é registrada com seu schema Zod:
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: "my-first-mcp", version: "1.0.0" });
server.tool("get_current_datetime", "Descrição...", {
timezone: z.string().optional().describe("Fuso horário IANA. Padrão: UTC"),
}, (params) => getCurrentDatetime(params));
// ... demais tools
const transport = new StdioServerTransport();
await server.connect(transport); // conecta via stdin/stdout
process.stdin.on("close", () => process.exit(0)); // encerra quando o cliente desconectaO schema Zod serve dois propósitos: validação dos parâmetros recebidos e geração automática do JSON Schema que o cliente usa para saber como chamar a tool.
4. Adicionar o shebang no arquivo compilado
O TypeScript não preserva o #!/usr/bin/env node no arquivo compilado. Sem ele, o sistema operacional não sabe que deve usar o Node.js para executar o arquivo diretamente.
O script scripts/add-shebang.cjs resolve isso após o tsc:
const fs = require("fs");
const file = "dist/index.js";
const content = fs.readFileSync(file, "utf8");
if (!content.startsWith("#!/usr/bin/env node")) {
fs.writeFileSync(file, "#!/usr/bin/env node\n" + content);
}
fs.chmodSync(file, "755"); // permissão de execuçãoO script usa extensão .cjs porque o package.json tem "type": "module" — sem isso, o Node tentaria interpretá-lo como ESM e o require() falharia.
O build completo fica:
npm run build
# equivale a: tsc && node scripts/add-shebang.cjs5. Publicar no npm
# Autenticar com um Granular Access Token (com bypass 2FA habilitado)
npm config set //registry.npmjs.org/:_authToken SEU_TOKEN
# Publicar
npm publish --access publicO campo "files": ["dist"] no package.json garante que apenas o código compilado vai para o registry — sem src/, sem node_modules/, sem arquivos de configuração desnecessários.
6. Configurar no cliente MCP (Kiro)
Em ~/.kiro/settings/mcp.json:
{
"mcpServers": {
"my-first-mcp": {
"command": "npx",
"args": ["-y", "@7leirbag/my-first-mcp"]
}
}
}O Kiro lê essa configuração, executa npx -y @7leirbag/my-first-mcp (que baixa e inicia o pacote automaticamente), e se comunica com o processo via stdin/stdout. A flag -y evita a confirmação interativa do npx.
Tools disponíveis
get_current_datetime
Retorna a data e hora atual no formato ISO 8601.
| Parâmetro | Tipo | Obrigatório | Padrão | Descrição |
|------------|--------|-------------|--------|------------------------------------|
| timezone | string | não | UTC | Identificador IANA do fuso horário |
// Resposta
{ "datetime": "2026-03-15T16:29:31", "timezone": "UTC" }get_server_info
Retorna metadados do servidor. Sem parâmetros.
// Resposta
{
"name": "@7leirbag/my-first-mcp",
"version": "1.0.0",
"status": "running",
"startedAt": "2026-03-15T16:27:29.185Z",
"tools": ["get_current_datetime", "get_server_info", "calculate_date_diff"]
}calculate_date_diff
Calcula a diferença absoluta entre duas datas.
| Parâmetro | Tipo | Obrigatório | Descrição |
|-----------|--------|-------------|------------------------------------|
| date1 | string | sim | Data no formato ISO 8601 ou YYYY-MM-DD |
| date2 | string | sim | Data no formato ISO 8601 ou YYYY-MM-DD |
// Resposta
{ "days": 73, "hours": 0, "minutes": 0, "totalMinutes": 105120 }Desenvolvimento local
# Instalar dependências
npm install
# Compilar
npm run build
# Rodar testes
npm testPara usar a versão local no Kiro sem publicar no npm, aponte o mcp.json para o arquivo compilado diretamente:
{
"mcpServers": {
"my-first-mcp-local": {
"command": "node",
"args": ["C:/caminho/para/o/projeto/dist/index.js"]
}
}
}"# my-first-mcp"
