Pular para o conteúdo

Tutorial 2 de 3 · Intermediário · 20 min

Como criar um servidor MCP em TypeScript

Um servidor MCP completo em um arquivo: uma ferramenta que valida CPF, sem depender de API externa. O código desta página é o que foi executado no teste, com a resposta real logo abaixo.

Testado em com @modelcontextprotocol/server 2.3.0, zod 4.6.5, TypeScript 7.0.2, Node.js 24.19.

O que vamos construir

Um servidor chamado utilitarios-br, com uma ferramenta, validar_cpf, que confere os dígitos verificadores de um CPF. O exemplo é pequeno de propósito: a lógica cabe em dez linhas, e o resto é o que todo servidor tem. Você precisa do Node.js 20 ou mais recente.

1. Crie o projeto

Terminal
mkdir utilitarios-br
cd utilitarios-br
npm init -y
npm install @modelcontextprotocol/server zod
npm install -D @types/node typescript
mkdir src

O pacote @modelcontextprotocol/server é o SDK oficial para servidores em TypeScript. O zod descreve os parâmetros de cada ferramenta.

2. Ajuste package.json e tsconfig.json

O projeto precisa ser um módulo ES e ter um script de build. Deixe o package.json com estes campos (o npm install já acrescentou as dependências):

package.json
{
  "name": "utilitarios-br",
  "version": "1.0.0",
  "type": "module",
  "scripts": { "build": "tsc" }
}
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "types": ["node"],
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

3. Escreva o servidor

src/index.ts
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";

const server = new McpServer({ name: "utilitarios-br", version: "1.0.0" });

function cpfValido(cpf: string): boolean {
  const d = cpf.replace(/\D/g, "");
  if (d.length !== 11 || /^(\d)\1{10}$/.test(d)) return false;
  const digito = (n: number) => {
    let soma = 0;
    for (let i = 0; i < n; i++) soma += Number(d[i]) * (n + 1 - i);
    const resto = (soma * 10) % 11;
    return resto === 10 ? 0 : resto;
  };
  return digito(9) === Number(d[9]) && digito(10) === Number(d[10]);
}

server.registerTool(
  "validar_cpf",
  {
    description: "Confere os dígitos verificadores de um CPF. Não consulta a Receita Federal.",
    inputSchema: z.object({
      cpf: z.string().describe("CPF com ou sem pontuação, ex.: 529.982.247-25"),
    }),
  },
  async ({ cpf }) => ({
    content: [{ type: "text", text: cpfValido(cpf) ? "CPF com dígitos válidos." : "CPF inválido." }],
  }),
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("utilitarios-br rodando em stdio");
}

main().catch((erro) => {
  console.error("Erro fatal:", erro);
  process.exit(1);
});

Quatro partes merecem atenção:

  • new McpServer dá nome e versão ao servidor. O cliente mostra esse nome.
  • registerTool recebe o nome da ferramenta, a descrição com o esquema de entrada e a função. A descrição é lida pelo modelo: diga o que a ferramenta faz e também o que ela não faz.
  • A função devolve content, uma lista de blocos. Aqui, um bloco de texto.
  • StdioServerTransport liga o servidor à entrada e saída padrão. Por isso o aviso de início vai em console.error: a saída padrão é só do protocolo.

4. Compile

Terminal
npm run build

O resultado é build/index.js.

5. Teste antes de ligar ao cliente

Um servidor stdio conversa em JSON-RPC, uma mensagem por linha. No nosso teste, enviamos tools/list e tools/call direto ao processo. Esta é a troca registrada:

Teste por stdio
→ {"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"validar_cpf","arguments":{"cpf":"529.982.247-25"}}}
← {"result":{"content":[{"type":"text","text":"CPF com dígitos válidos."}]},"jsonrpc":"2.0","id":2}

Para explorar o servidor com uma interface, use o MCP Inspector, a ferramenta de depuração oficial: npx @modelcontextprotocol/inspector node build/index.js.

6. Ligue ao cliente

No Claude Desktop, acrescente o servidor ao arquivo de configuração, com o caminho absoluto do arquivo compilado:

claude_desktop_config.json
{
  "mcpServers": {
    "utilitarios-br": {
      "command": "node",
      "args": ["<CAMINHO_ABSOLUTO>/utilitarios-br/build/index.js"]
    }
  }
}

Reinicie o aplicativo e peça: “O CPF 529.982.247-25 é válido?”. Em outros clientes muda o arquivo, e não o servidor: veja os guias por cliente.

Próximos passos

  • Acrescente outra ferramenta com um segundo registerTool.
  • Para chamar uma API com chave, leia a chave de process.env e declare a variável em env, na configuração do cliente. Nunca escreva a chave no código.
  • Antes de dar a um servidor acesso a dados reais, leia Segurança no MCP.
  • O mesmo servidor em Python está no tutorial seguinte.

Perguntas frequentes

Por que não posso usar console.log no servidor?

Em um servidor stdio, a saída padrão é o canal das mensagens JSON-RPC. Qualquer texto escrito ali corrompe o protocolo. Use console.error, que escreve na saída de erro e aparece nos logs do cliente.

Qual pacote instalar: @modelcontextprotocol/server ou @modelcontextprotocol/sdk?

O tutorial oficial, lido em 03/10/2026, usa @modelcontextprotocol/server, e foi com a versão 2.3.0 dele que este exemplo rodou. O pacote @modelcontextprotocol/sdk é o da geração anterior e continua publicado.

Preciso publicar o servidor no npm?

Não. Para uso próprio, o cliente pode executar o arquivo compilado direto com node e o caminho absoluto. Publicar só é necessário para que outras pessoas instalem com npx.

Como exponho uma API da minha empresa?

Troque o corpo da ferramenta por uma chamada à API, lendo a credencial de uma variável de ambiente. Descreva bem a ferramenta e os parâmetros: é esse texto que o modelo usa para decidir quando chamá-la.

Na sequência

Continue por aqui