--- name: mcp-dev description: Desenvolvimento e teste de servidores MCP — criação, configuração e gestão de MCPs customizados. SDK TypeScript v2 (@modelcontextprotocol/server) e transporte HTTP são obrigatórios, sem excepção silenciosa. --- # /mcp-dev - Desenvolvimento de MCPs Skill para criação, configuração e gestão de servidores MCP customizados. ## Regra de Ouro — obrigatória, sem excepção silenciosa Todo MCP novo, e toda a edição estrutural de um MCP existente, tem de cumprir isto: 1. **SDK oficial v2** ([ts.sdk.modelcontextprotocol.io/v2](https://ts.sdk.modelcontextprotocol.io/v2/)) — pacotes `@modelcontextprotocol/server` (+ `@modelcontextprotocol/client` só em testes/evaluations). **`@modelcontextprotocol/sdk` (v1) está descontinuado nesta casa** — nunca instalar, nunca copiar código de um MCP antigo que o use sem migrar primeiro. API de alto nível `McpServer` + `registerTool`/`registerResource`/`registerPrompt` — a API de baixo nível `Server` + `setRequestHandler(Schema, …)` está **proibida** (reintroduz classes de bugs que o v2 elimina por desenho, incluindo o anti-padrão de capabilities manuais que causava o erro 471). 2. **Transporte HTTP obrigatório** — `createMcpHandler` (Streamable HTTP), tipicamente montado com `@modelcontextprotocol/express` + `@modelcontextprotocol/node`. `stdio` (`serveStdio`) só é aceitável quando o host tem de lançar o processo directamente e não existe gateway — nesse caso documentar a excepção no README do MCP com a razão concreta, e usar sempre `serveStdio` do SDK v2 (nunca `StdioServerTransport` manual). 3. **Node.js 20+, ESM (`"type": "module"`), `zod/v4` (peer `^4.2.0`)** — nunca `zod` v3, nunca `require()`. 4. **Nunca construir comandos shell/SQL por interpolação de string** quando o MCP faz ponte para SSH/CLI externos (ex.: WP-CLI). Dados do utilizador passam sempre por STDIN (texto ou base64), nunca concatenados num comando `ssh ... "..."`. Ver `references/best-practices.md` §Segurança — regra escrita depois de uma injecção de comandos real ter sido encontrada e corrigida em produção (`mcp-kivicare`, 19-08-2026, ver Healing Log no fim deste ficheiro). Se qualquer um destes 4 pontos não puder ser cumprido, **parar e perguntar** antes de prosseguir — não decidir sozinho por uma alternativa mais rápida. ## Contexto NotebookLM Consultar ANTES de executar: ``` mcp__notebooklm__notebook_query({ notebook_id: "73102308-70ef-403e-9be9-eae0cfc62d55", query: "" }) ``` **Procedimentos obrigatórios — consultar antes de criar/modificar MCPs:** - [PROC-MCP-Desenvolvimento.md](file:///media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D7-Tecnologia/MCP/PROC-MCP-Desenvolvimento.md) — Guia oficial v2.4 - [PROC-MCP-Troubleshooting-Erro-471.md](file:///media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D7-Tecnologia/MCP/PROC-MCP-Troubleshooting-Erro-471.md) - [PROC-MCP-Google-Auth.md](file:///media/ealmeida/Dados/Hub/06-Operacoes/Procedimentos/D7-Tecnologia/MCP/PROC-MCP-Google-Auth.md) > **Regra #48:** Novos MCPs são desenvolvidos **localmente no desktop** em `/media/ealmeida/Dados/Dev/` (CT 102 extinto em 20-04-2026). Após validação, mover para `~/mcp-servers/` em produção. --- ## Comandos | Comando | Descrição | |---------|-----------| | `/mcp-dev create ` | Criar scaffold TypeScript completo | | `/mcp-dev config ` | Configurar MCP em ~/.claude.json | | `/mcp-dev test ` | Testar conexão e listar ferramentas | | `/mcp-dev eval ` | Executar evaluations do MCP | | `/mcp-dev docs ` | Gerar documentação Obsidian | | `/mcp-dev list` | Listar MCPs em ~/mcp-servers/ | | `/mcp-dev status` | Estado dos MCPs activos na sessão | --- ## Workflow de 4 Fases ### Fase 1: Research 1. Consultar NotebookLM (notebook acima) 2. Verificar MCPs existentes — evitar duplicação 3. Identificar ferramentas necessárias e nomear segundo padrão 4. Transporte: **HTTP obrigatório** (Streamable HTTP via SDK v2, `createMcpHandler`). stdio só com justificação escrita explícita — ver secção Transportes. 5. Criar spike/PoC se tecnologia desconhecida **Nomenclatura de tools:** `{serviço}_{acção}_{recurso}` em snake_case, máx. 40 caracteres. ``` # Correcto get_customer_notes, create_project_task, list_invoice_items # Errado getCustomerNotes (camelCase), get_customer_notes_with_all_billing_details (>40 chars) ``` ### Fase 2: Implementation 1. Scaffold local no desktop: `/media/ealmeida/Dados/Dev//` 2. Implementar tools com **annotations obrigatórias** e validação Zod (`z.object(...)` de `zod/v4`) 3. `McpServer` deriva capabilities automaticamente de `registerTool`/`registerResource`/`registerPrompt` — nunca declarar capabilities à mão (isso é API de baixo nível v1, proibida) 4. Responses em **dual format**: Markdown (text) + JSON (structuredContent) 5. Erros **accionáveis**: causa + próximos passos **Annotations obrigatórias em cada tool** — vão directo na config de `registerTool` (sem passo de inferência separado no v2): ```typescript server.registerTool( 'get_customer', { description: 'Obtém dados de um cliente pelo ID', inputSchema: z.object({ customer_id: z.number().int().positive() }), annotations: { readOnlyHint: true, // não modifica estado destructiveHint: false, // não é destrutiva idempotentHint: true, // mesmo resultado em chamadas repetidas openWorldHint: false, // opera em sistema fechado (interno) }, }, async ({ customer_id }) => { /* ... */ } ); ``` **Dual format de resposta:** ```typescript return { content: [{ type: 'text', text: `# Cliente ${d.name}\n**Email:** ${d.email}` }], structuredContent: { id: d.id, name: d.name, email: d.email } }; ``` **Erros accionáveis:** ```typescript return { content: [{ type: 'text', text: [ `Erro ao obter cliente ID ${id}.`, `Causa: ${error.message}`, `Verificar: 1) ID existe 2) Permissões 3) Conexão BD` ].join('\n') }], isError: true }; ``` ### Fase 3: Review 1. Checklist de segurança (ver `references/best-practices.md`) 2. `pnpm audit` — sem vulnerabilidades críticas 3. Validar limites de nomes de tools (máx. 40 chars) 4. Testar build: `npm run build` 5. Testar manualmente via HTTP: `curl -s -X POST http://127.0.0.1:32XX/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'` 6. Documentar decisões em ADR se necessário **Grep de validação rápida:** ```bash grep -rn '`.*\${.*}`' src/ | grep -i 'select\|insert\|update\|delete' grep -rn 'Math.random' src/ # Comandos shell/ssh interpolados (ver Regra de Ouro §4) — qualquer resultado exige revisão manual grep -rn 'exec(\|execSync(\|spawn(.*sh\b' src/ | grep -i '\${' ``` ### Fase 4: Evaluations Criar e executar testes estruturados antes do deploy. **10 evaluations obrigatórias por MCP** (ver `references/evaluation-guide.md`): | # | Pergunta | Cenário | |---|----------|---------| | 1 | Tool principal funciona com input válido? | Input mínimo válido | | 2 | Input inválido retorna erro accionável? | Parâmetro obrigatório em falta | | 3 | Recurso inexistente retorna erro claro? | ID=99999 | | 4 | Operações idempotentes são estáveis? | Chamada repetida com mesmos params | | 5 | Operações destrutivas apagam correctamente? | Delete em recurso existente | | 6 | Tools read-only não alteram estado? | Get + verificar estado antes/depois | | 7 | Paginação funciona com listas grandes? | 1000+ registos, página 2 | | 8 | Auth inválida é rejeitada graciosamente? | API key removida | | 9 | structuredContent é consistente com text? | Comparar campos nos dois formatos | | 10 | Performance < 5s em p95? | 10 chamadas consecutivas | **Executar:** ```bash npm run eval # evaluations npm run eval:ci # build + evaluations (para CI/CD) ``` --- ## Estrutura de Projecto ``` /media/ealmeida/Dados/Dev/mcp-/ # Desenvolvimento (local, desktop) ├── package.json ├── tsconfig.json ├── src/ │ ├── server.ts # Factory McpServer + registerTool (partilhado) │ ├── http.ts # Entry point HTTP (createMcpHandler + Express) — obrigatório │ └── stdio.ts # Entry point stdio (serveStdio) — só se excepção justificada ├── eval/ │ └── run-evals.ts # Evaluations automáticas (Client + StreamableHTTPClientTransport in-process) ├── .env.example ├── README.md └── .gitignore /home/ealmeida/mcp-servers/mcp-/ # Produção ``` --- ## Configuração **~/.claude.json (HTTP — obrigatório):** ```json { "mcpServers": { "": { "type": "http", "url": "http://127.0.0.1:32XX/mcp" } } } ``` **~/.claude.json (stdio — apenas excepção documentada):** ```json { "mcpServers": { "": { "command": "node", "args": ["/home/ealmeida/mcp-servers//dist/stdio.js"] } } } ``` --- ## Transportes | Transporte | Estado | Quando usar | |------------|--------|-------------| | Streamable HTTP (SDK v2) | **Obrigatório** | Todos os MCPs novos — acesso remoto, gateway, múltiplos clientes | | stdio (SDK v2, `serveStdio`) | Excepção documentada | Só quando o host lança o processo directamente e não há gateway. Justificar no README do MCP. | | SSE | Deprecated | Retrocompatibilidade apenas — nunca usar em MCP novo | --- ## MCPs Existentes (Referência) | MCP | Path | Tools | Transporte | |-----|------|-------|------------| | desk-crm-v3 | ~/mcp-servers/desk-crm-v3/ | 150+ | SSE :3100 | | mem0 | gateway.descomplicar.pt/v1/mem0/mcp (remoto) | 5 | HTTP | | google-workspace | (npm package) | 42 | stdio | | filesystem | (npm package) | 8 | stdio | --- ## Referências | Ficheiro | Conteúdo | |----------|----------| | `references/best-practices.md` | Nomenclatura, annotations, segurança (incl. anti-injecção de comandos shell), SQL Perfex | | `references/evaluation-guide.md` | Guia completo de evaluations, script automatizado (Client v2 in-process) | | `references/templates.md` | Templates TypeScript v2 prontos (server factory, HTTP obrigatório, stdio excepcional, systemd) | **Agente especializado:** `mcp-protocol-developer` — desenvolvimento complexo, debug, optimização. --- ## Changelog ### v3.0.0 (2026-08-19) - **SDK TypeScript v2 obrigatório** (`@modelcontextprotocol/server`, ver [ts.sdk.modelcontextprotocol.io/v2](https://ts.sdk.modelcontextprotocol.io/v2/)) — `@modelcontextprotocol/sdk` v1 descontinuado nesta casa. - **Transporte HTTP obrigatório**; stdio passa a excepção documentada (antes era "válido" sem restrição). - API de baixo nível `Server` + `setRequestHandler(Schema, …)` proibida — só `McpServer` + `registerTool`/`registerResource`/`registerPrompt`. - Capabilities manuais removidas do fluxo (v2 deriva automaticamente) — a secção "anti-erro 471" do v1 já não se aplica ao padrão obrigatório. - `inferAnnotations()` removido (não existe em v2) — annotations sempre inline na config de `registerTool`. - Nova secção "Regra de Ouro" no topo do ficheiro, com 4 obrigações sem excepção silenciosa. - `references/best-practices.md` §Segurança recebeu regra de anti-injecção de comandos shell (bridge SSH/CLI), motivada por incidente real corrigido em `mcp-kivicare` (19-08-2026, 8 bugs, incl. injecção de comandos crítica). - Templates (`references/templates.md`) reescritos para a API v2 (`createMcpHandler`, `createMcpExpressApp`, `serveStdio`). ### v2.0.0 (2026-03-10) - Refactorização: SKILL.md de 1163 para <500 linhas (progressive disclosure) - **Fase 4: Evaluations** adicionada ao workflow (padrão mcp-builder Anthropic) - Nomenclatura tools: `{serviço}_{acção}_{recurso}` snake_case documentada - Annotations obrigatórias (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) - `inferAnnotations()` mencionado como padrão automático - Dual format de resposta (JSON + Markdown) documentado - Erros accionáveis com causa + próximos passos - Extracto para `references/`: best-practices, evaluation-guide, templates ### v1.3.0 (2026-01-31) - HTTP StreamableHTTP como padrão oficial - SSE marcado como deprecated - Checklist de segurança (audit 164 tools) ### v1.2.0 (2026-01-27) - Template SSE com http nativo - systemd service template ### v1.0.0 (2026-01-27) - Versão inicial --- ## Quando NÃO Usar - Para tarefas fora do domínio de desenvolvimento MCP - Quando outra skill mais específica está disponível - Para operações simples que não requerem MCP ## Protocolo 1. Consultar NotebookLM e PROCs relevantes 2. Executar as 4 fases: Research -> Implementation -> Review -> Evaluations 3. Validar resultados antes de concluir 4. Reportar status e próximos passos --- *Skill v3.0.0 | Descomplicar® | 2026-08-19* --- ## Healing Log Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar. ```jsonl {"date":"","issue":"","fix":"","source":"user|auto"} {"date":"2026-08-19","issue":"mcp-kivicare: runRawSqlQuery interpolava a query directamente num comando `ssh ... wp db query \"...\"` — injecção de comandos shell explorável","fix":"query sempre por STDIN (`wp db query < stdin`), nunca interpolada no comando remoto","source":"auto"} {"date":"2026-08-19","issue":"mcp-kivicare: hooks nativos do KiviCare (kc_appointment_status_update) disparados com um inteiro em vez do objecto do modelo — TypeError fatal num listener nativo, registo órfão em produção","fix":"recarregar o modelo (ex.: KCAppointment::find($id)) antes de disparar qualquer hook nativo; ler a assinatura real do hook no código-fonte do sistema alvo antes de o chamar","source":"auto"} {"date":"2026-08-19","issue":"mcp-kivicare: basic_data do paciente gravado como array PHP em vez do JSON que o sistema nativo espera (json_encode com JSON_UNESCAPED_UNICODE) — corrompia a leitura no admin nativo","fix":"replicar exactamente o formato de serialização do sistema nativo (grep ao código-fonte real), nunca assumir a partir da forma dos dados em memória","source":"auto"} ``` *Adicionar nova linha após cada erro corrigido.*