receitas-mcp-server
# receitas-mcp-server
Servidor **MCP (Model Context Protocol)** em **TypeScript + Node**, com a stack atual de mercado:
- [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk) — SDK oficial
- **TypeScript** (ESM, `NodeNext`)
- **zod** para validação de schemas das tools
- transporte **stdio** (padrão para Claude Code, Cursor e Claude Desktop)
> ⚠️ **Importante:** um servidor MCP **não é chamado direto pelo frontend** (navegador). Ele é consumido por **assistentes de IA** (Claude Code, Cursor, Claude Desktop) **ou** por um backend que aja como cliente MCP — é o caso da API `receitas-api` (pasta irmã), que expõe esse MCP como REST para o frontend.
## 🗄️ Onde as receitas ficam guardadas
O storage é escolhido **em tempo de execução** (`src/repository.ts`):
- Se as variáveis `TRELLO_API_KEY`, `TRELLO_TOKEN` e `TRELLO_LIST_ID` estiverem definidas → grava/lê cada receita como um **card do Trello** (`src/trelloStore.ts`).
- Caso contrário → usa um **arquivo JSON local** `data/receitas.json` (`src/store.ts`), ótimo para desenvolvimento.
No Trello, cada receita vira um card na lista configurada: o nome do card é o nome da receita e a descrição guarda um bloco JSON (`<!--receita:...-->`) para reconstruir a receita fielmente. Configure as credenciais em `.env` (veja `.env.example`).
### Variáveis de ambiente (`.env`)
Crie um arquivo `.env` na raiz de `receitas-mcp-server` com estas três variáveis.
**Os valores abaixo são fictícios** — troque pelos seus.
```dotenv
# Chave de API do Trello — pegue em https://trello.com/app-key
# Formato: 32 caracteres hexadecimais.
TRELLO_API_KEY=a1b2c3d4e5f60718293a4b5c6d7e8f90
# Token do Trello — gere pelo link "Token" na mesma pagina do app-key.
# Formato: cadeia longa (~64+ caracteres).
TRELLO_TOKEN=ATTAa0000example1111token2222naoreal3333abcdef4444567890ghijkl
# ID da lista do Trello onde os cards de receita serao criados.
# Formato: 24 caracteres hexadecimais.
TRELLO_LIST_ID=6634f0a1b2c3d4e5f6a7b8c9
```
| Variável | O que é | Como obter |
|----------|---------|------------|
| `TRELLO_API_KEY` | Identifica seu app no Trello | https://trello.com/app-key |
| `TRELLO_TOKEN` | Autoriza acesso à sua conta | Link **Token** na página do app-key |
| `TRELLO_LIST_ID` | Lista onde as receitas viram cards | Abra o board com `.json` no fim da URL e procure o `id` da lista, ou `GET https://api.trello.com/1/boards/{boardId}/lists?key=SUA_KEY&token=SEU_TOKEN` |
> 🔒 As três são **obrigatórias juntas** para ativar o Trello. Se qualquer uma
> faltar, o servidor cai automaticamente no storage JSON local. O `.env` está no
> `.gitignore` — nunca comite suas credenciais reais.
---
## 📖 Índice
1. [O que é isso](#o-que-é-isso)
2. [Tools disponíveis](#tools-disponíveis)
3. [Como este MCP foi criado (passo a passo)](#como-este-mcp-foi-criado-passo-a-passo)
4. [Como usar (passo a passo)](#como-usar-passo-a-passo)
5. [Estrutura de pastas](#estrutura-de-pastas)
---
## O que é isso
MCP é um protocolo que permite a uma IA (Claude, Cursor, etc.) **chamar funções e ler dados** de uma fonte externa de forma padronizada. Aqui, a fonte externa é um **catálogo de receitas**. A IA conversa com este servidor por **stdio** (entrada/saída padrão), trocando mensagens no formato **JSON-RPC 2.0**.
---
## Tools disponíveis
| Tool | O que faz |
|------|-----------|
| `listar_receitas` | Lista receitas com filtros (categoria, dificuldade, ingrediente, busca) |
| `buscar_receita` | Detalhes completos de uma receita por `id` |
| `adicionar_receita` | Cadastra uma receita (persistida em `data/receitas.json`) |
| `remover_receita` | Remove uma receita por `id` |
| `sugerir_por_ingredientes` | Sugere receitas pelo que você tem em casa |
Também expõe um **resource** `receitas://catalogo` com o catálogo completo em JSON.
---
## Como este MCP foi criado (passo a passo)
Se você quiser recriar do zero (ou entender cada peça), foi exatamente esta a sequência:
### Passo 1 — Criar a pasta e o `package.json`
Projeto Node em **ESM** (`"type": "module"`), com scripts de build/start e as dependências certas:
```json
{
"type": "module",
"bin": { "receitas-mcp": "dist/index.js" },
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsx watch src/index.ts",
"inspect": "npx @modelcontextprotocol/inspector node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.19.1",
"zod": "^3.25.76"
},
"devDependencies": {
"@types/node": "^24.7.0",
"tsx": "^4.20.6",
"typescript": "^5.9.3"
}
}
```
- `@modelcontextprotocol/sdk` → o SDK oficial que implementa o protocolo.
- `zod` → valida os argumentos que a IA envia para cada tool.
- `tsx` → roda TypeScript direto em desenvolvimento (modo watch).
### Passo 2 — Configurar o TypeScript (`tsconfig.json`)
O ponto crítico é usar `module`/`moduleResolution` = **`NodeNext`**, porque o SDK é ESM e os imports precisam terminar em `.js`:
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"resolveJsonModule": true
}
}
```
### Passo 3 — Definir os dados e os schemas (`src/types.ts`)
Uma única fonte de verdade com **zod**. O schema serve para **validar a entrada das tools** e, ao mesmo tempo, **gerar o tipo TypeScript** (`z.infer`):
```ts
export const receitaSchema = z.object({
id: z.string().regex(/^[a-z0-9-]+$/),
nome: z.string().min(1),
categoria: z.string().min(1),
tempoPreparoMin: z.number().int().positive(),
porcoes: z.number().int().positive(),
dificuldade: z.enum(["facil", "medio", "dificil"]),
ingredientes: z.array(z.string()).min(1),
passos: z.array(z.string()).min(1),
});
export type Receita = z.infer<typeof receitaSchema>;
```
### Passo 4 — Camada de dados (`src/store.ts`)
Um repositório simples que **lê e grava** o arquivo `data/receitas.json`. Isolar isso mantém o `index.ts` focado só no protocolo. Aqui ficam `listar`, `obter`, `adicionar`, `remover` e `sugerirPorIngredientes`.
### Passo 5 — O servidor MCP (`src/index.ts`)
O coração. Cria o servidor, registra cada **tool** com seu schema e handler, registra o **resource** e conecta no transporte stdio:
```ts
const server = new McpServer({ name: "receitas-mcp-server", version: "1.0.0" });
server.registerTool(
"listar_receitas",
{ title: "Listar receitas", description: "...", inputSchema: { categoria: z.string().optional() } },
async ({ categoria }) => {
const lista = await store.listar({ categoria });
return { content: [{ type: "text", text: "..." }], structuredContent: { receitas: lista } };
},
);
const transport = new StdioServerTransport();
await server.connect(transport);
```
> 🔑 **Regra de ouro:** o `stdout` é **exclusivo do protocolo**. Qualquer log seu tem que ir para `stderr` (`console.error`), senão você corrompe a comunicação com a IA.
### Passo 6 — Instalar, compilar e testar
```bash
npm install # baixa as dependências
npm run build # compila src/ -> dist/
```
O teste de fumaça enviou 3 mensagens JSON-RPC pelo stdin (`initialize` → `notifications/initialized` → `tools/list` + uma `tools/call`) e confirmou que o servidor respondeu com as 5 tools e executou uma chamada real. ✅
---
## Como usar (passo a passo)
### 1. Preparar o servidor (uma vez)
```bash
cd receitas-mcp-server
npm install
npm run build
```
> Sempre rode `npm run build` de novo depois de editar qualquer arquivo em `src/`, pois a IA aponta para `dist/index.js`.
### 2. (Opcional) Testar sozinha com o Inspector
Abre uma interface web onde você vê e dispara as tools na mão:
```bash
npm run inspect
```
### 3. Conectar no **Claude Code**
Na pasta do frontend `receitas`, rode:
```bash
claude mcp add receitas -- node "C:/Users/laiza_g/Desktop/Laiza/cursos/cursos/aulaBOOTSTRAP4/receitas-mcp-server/dist/index.js"
```
Confira se conectou:
```bash
claude mcp list
```
### 4. Conectar no **Cursor** ou **Claude Desktop**
Adicione ao arquivo de configuração de MCP (`.cursor/mcp.json` no Cursor, ou `claude_desktop_config.json` no Claude Desktop):
```json
{
"mcpServers": {
"receitas": {
"command": "node",
"args": [
"C:/Users/laiza_g/Desktop/Laiza/cursos/cursos/aulaBOOTSTRAP4/receitas-mcp-server/dist/index.js"
]
}
}
}
```
Depois **reinicie** o Cursor/Claude Desktop.
### 5. Usar no dia a dia
Com o MCP conectado, é só pedir em linguagem natural para a IA. Exemplos:
- *"Liste as receitas de sobremesa fáceis."* → chama `listar_receitas`
- *"Me mostra a receita do brigadeiro."* → chama `buscar_receita`
- *"Tenho tomate e manjericão, o que posso fazer?"* → chama `sugerir_por_ingredientes`
- *"Cadastra uma receita nova de bolo de cenoura."* → chama `adicionar_receita` (grava no JSON)
A IA escolhe a tool certa sozinha, preenche os argumentos e te devolve o resultado.
---
## Estrutura de pastas
```
receitas-mcp-server/
├── data/
│ └── receitas.json # base local (fallback quando Trello nao configurado)
├── src/
│ ├── index.ts # servidor MCP: tools + resources
│ ├── repository.ts # interface + fabrica (escolhe Trello ou JSON)
│ ├── store.ts # backend JSON local
│ ├── trelloStore.ts # backend Trello (REST API)
│ ├── filtering.ts # filtros e sugestao (funcoes puras compartilhadas)
│ └── types.ts # schemas zod + tipos
├── .env.example # credenciais do Trello
├── package.json
├── tsconfig.json
└── README.md
```
TDQS
Scored across 5 tools
Each tool has a clear and distinct purpose: listing, fetching details, adding, removing, and suggesting recipes. The overlap between listar and buscar is resolved by list returning a filtered collection while buscar returns a single full recipe.
All tool names follow a consistent Portuguese verb_noun pattern: listar_receitas, buscar_receita, adicionar_receita, remover_receita, and sugerir_por_ingredientes. The pattern is uniform and predictable.
With 5 tools, the server is well-scoped for a recipe management domain. Each tool covers a core operation without unnecessary redundancy.
The server covers list, get, create, and delete operations, but lacks an update tool for editing existing recipes. This is a minor gap that agents can work around by deleting and recreating, but it is not a fatal omission.