--- name: mcp-dev description: MCP server development and testing. Creates new MCP servers following Descomplicar standards with SSE transport. Use when user mentions "mcp development", "novo mcp", "criar mcp", "mcp server", "mcp testing". author: Descomplicar® Crescimento Digital version: 1.3.0 quality_score: 75 user_invocable: true desk_task: 1476 allowed-tools: Grep --- # /mcp-dev - Desenvolvimento de MCPs Skill para criação, configuração e gestão de servidores MCP customizados. --- ## Comandos | Comando | Descrição | |---------|-----------| | `/mcp-dev create ` | Criar novo MCP (scaffold TypeScript) | | `/mcp-dev config ` | Configurar MCP em ~/.claude.json | | `/mcp-dev test ` | Testar conexão e ferramentas | | `/mcp-dev docs ` | Gerar documentação Obsidian | | `/mcp-dev list` | Listar MCPs instalados | | `/mcp-dev status` | Estado dos MCPs activos | --- ## Estrutura MCP Padrão ``` ~/mcp-servers// ├── package.json ├── tsconfig.json ├── src/ │ ├── index.ts # Entry point │ ├── tools/ # Ferramentas MCP │ │ └── example.ts │ └── types.ts # Tipos TypeScript ├── README.md ├── .env.example └── .gitignore ``` --- ## /mcp-dev create Cria scaffold completo para novo MCP. ### Execução ``` 1. Criar pasta ~/mcp-servers// 2. Gerar package.json com dependências MCP 3. Gerar tsconfig.json 4. Criar src/index.ts com estrutura base 5. Criar README.md 6. Criar .env.example 7. npm install 8. Sugerir configuração ``` ### Template package.json ```json { "name": "mcp-", "version": "1.0.0", "type": "module", "main": "dist/index.js", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "tsx src/index.ts" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0", "zod": "^3.22.0" }, "devDependencies": { "@types/node": "^20.0.0", "typescript": "^5.0.0", "tsx": "^4.0.0" } } ``` ### Template index.ts ```typescript #!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; const server = new Server( { name: 'mcp-', version: '1.0.0', }, { capabilities: { tools: {}, }, } ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [ { name: 'example_tool', description: 'Descrição da ferramenta', inputSchema: { type: 'object', properties: { param: { type: 'string', description: 'Parâmetro exemplo', }, }, required: ['param'], }, }, ], })); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case 'example_tool': return { content: [ { type: 'text', text: `Resultado: ${args?.param}`, }, ], }; default: throw new Error(`Unknown tool: ${name}`); } }); // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('MCP Server running on stdio'); } main().catch(console.error); ``` --- ## /mcp-dev config Adiciona MCP ao ficheiro de configuração. ### Claude Code (~/.claude.json) ```json { "mcpServers": { "": { "command": "node", "args": ["/home/ealmeida/mcp-servers//dist/index.js"], "env": {} } } } ``` ### Claude Desktop (~/.config/claude/claude_desktop_config.json) ```json { "mcpServers": { "": { "command": "node", "args": ["/home/ealmeida/mcp-servers//dist/index.js"] } } } ``` ### Verificação ```bash # Testar se compila cd ~/mcp-servers/ && npm run build # Testar manualmente echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js ``` --- ## /mcp-dev test Testa conexão e lista ferramentas disponíveis. ### Output Esperado ``` 🔧 MCP Test: ────────────────── Status: ✅ Conectado Versão: 1.0.0 Transport: stdio Ferramentas (3): • tool_1 - Descrição • tool_2 - Descrição • tool_3 - Descrição Recursos (0): (nenhum) Prompts (0): (nenhum) ``` --- ## /mcp-dev docs Gera documentação Obsidian para o MCP. ### Output Cria `Stack/Claude Code/MCPs/MCP-.md`: ```markdown --- title: MCP date: YYYY-MM-DD type: mcp status: active tags: [mcp, ] --- # MCP Descrição do MCP. ## Ferramentas | Tool | Descrição | Parâmetros | |------|-----------|------------| | tool_1 | ... | param1, param2 | ## Configuração ... ## Exemplos ... ``` --- ## /mcp-dev list Lista todos os MCPs em ~/mcp-servers/. ### Output ``` 📋 MCPs Instalados ────────────────── ~/mcp-servers/ ├── desk-crm-v3/ ✅ Configurado ├── memory-supabase/ ✅ Configurado ├── google-workspace/ ✅ Configurado ├── novo-mcp/ ⚠️ Não configurado └── teste/ ❌ Não compila Total: 5 | Activos: 3 | Pendentes: 2 ``` --- ## /mcp-dev status Estado dos MCPs activos na sessão. ### Output ``` 🔌 MCPs Activos ─────────────── ✅ desk-crm-v3 (15 tools) ✅ memory-supabase (5 tools) ✅ google-workspace (42 tools) ✅ filesystem (8 tools) ⚠️ youtube-uploader (auth required) Total: 5 | OK: 4 | Problemas: 1 ``` --- ## Padrões de Desenvolvimento ### Limites de Nomes de Tools > **REGRA 31/01/2026:** Nomes de tools MCP têm limite de caracteres. | Limite | Valor | Cálculo | |--------|-------|---------| | Tool name | **≤40 chars** | Nome da função | | Total com prefixo | **≤64 chars** | `mcp____` | **Exemplo:** ``` mcp__desk-crm-v3__get_customer_notes └─────────┘ └────────────────┘ server=12 tool_name=18 = 30 chars ✅ mcp__desk-crm-v3__get_customer_alternate_addresses_with_details └─────────┘ └──────────────────────────────────────────┘ server=12 tool_name=46 = 58 chars ❌ (tool > 40) ``` **Validação recomendada:** ```typescript function validateToolName(name: string): void { if (name.length > 40) { throw new Error(`Tool name "${name}" exceeds 40 char limit (${name.length})`); } } // Aplicar a todas as tools no array allTools.forEach(t => validateToolName(t.name)); ``` --- ### Error Handling ```typescript try { // operação } catch (error) { return { content: [{ type: 'text', text: `Erro: ${error instanceof Error ? error.message : 'Unknown error'}`, }], isError: true, }; } ``` ### Validação com Zod ```typescript import { z } from 'zod'; const InputSchema = z.object({ param: z.string().min(1), optional: z.number().optional(), }); // No handler const validated = InputSchema.parse(args); ``` ### Logging ```typescript // Usar console.error (não console.log - interfere com stdio) console.error(`[MCP] Processing: ${param}`); ``` --- ## Transporte HTTP (StreamableHTTP) - RECOMENDADO **Padrão oficial MCP SDK.** Usar para todos os novos MCPs. ### Vantagens HTTP vs SSE | Aspecto | HTTP (StreamableHTTP) | SSE (deprecated) | |---------|----------------------|------------------| | Padrão | ✅ Oficial MCP SDK | ⚠️ Deprecated | | Stateless | ✅ Suportado | ❌ Sempre stateful | | Simplicidade | ✅ Um endpoint | ❌ /sse + /message | | Gate ready | ✅ REST-like | ❌ Streaming complexo | ### Template index-http.ts (PADRÃO) ```typescript #!/usr/bin/env node /** * MCP - HTTP Server Mode * StreamableHTTP transport for web/remote access * @author Descomplicar® | @link descomplicar.pt | @copyright 2026 */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { ListToolsRequestSchema, CallToolRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import * as http from 'http'; import { URL } from 'url'; import { randomUUID } from 'crypto'; const PORT = parseInt(process.env.MCP_HTTP_PORT || '32XX', 10); const HOST = process.env.MCP_HTTP_HOST || '127.0.0.1'; const STATEFUL = process.env.MCP_STATEFUL !== 'false'; // Track active sessions (stateful mode) const sessions = new Map(); // Define tools array const allTools = [ { name: 'example_tool', description: 'Descrição da ferramenta', inputSchema: { type: 'object', properties: { param: { type: 'string', description: 'Parâmetro exemplo' } }, required: ['param'] }, handler: async (args: any) => ({ content: [{ type: 'text', text: `Resultado: ${args.param}` }] }) } ]; function createMcpServer(): Server { const server = new Server( { name: 'mcp-nome', version: '1.0.0' }, { capabilities: { tools: {} } } ); server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: allTools.map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })) })); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const tool = allTools.find(t => t.name === name); if (!tool) { return { content: [{ type: 'text', text: `Tool not found: ${name}` }] }; } try { return await tool.handler(args); } catch (error) { return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } }); return server; } async function main() { const httpServer = http.createServer(async (req, res) => { // CORS headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Mcp-Session-Id'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } const url = new URL(req.url || '/', `http://${HOST}:${PORT}`); // Health check endpoint if (url.pathname === '/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', transport: 'streamable-http', version: '1.0.0', sessions: sessions.size, stateful: STATEFUL, tools: allTools.length })); return; } // Stats endpoint if (url.pathname === '/stats') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ totalTools: allTools.length, activeSessions: sessions.size }, null, 2)); return; } // MCP endpoint if (url.pathname === '/mcp') { try { const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: STATEFUL ? () => randomUUID() : undefined }); const server = createMcpServer(); if (STATEFUL && transport.sessionId) { sessions.set(transport.sessionId, { transport }); transport.onclose = () => { if (transport.sessionId) { sessions.delete(transport.sessionId); } }; } await server.connect(transport); await transport.handleRequest(req, res); } catch (error) { if (!res.headersSent) { res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Internal server error' })); } } return; } // 404 res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found' })); }); httpServer.listen(PORT, HOST, () => { console.log(`MCP HTTP Server v1.0.0`); console.log(` Endpoint: http://${HOST}:${PORT}/mcp`); console.log(` Health: http://${HOST}:${PORT}/health`); console.log(` Mode: ${STATEFUL ? 'Stateful' : 'Stateless'}`); console.log(` Tools: ${allTools.length}`); }); // Graceful shutdown const shutdown = () => { httpServer.close(() => process.exit(0)); }; process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); } main().catch(console.error); ``` ### Configuração HTTP ```json // ~/.claude.json - HTTP transport { "mcpServers": { "meu-mcp": { "type": "http", "url": "http://127.0.0.1:32XX/mcp" } } } ``` ### Portas HTTP Reservadas | Porta | MCP | Estado | |-------|-----|--------| | 3200 | outline-postgresql | ✅ HTTP | | 3201+ | disponíveis | - | ### Scripts package.json (HTTP) ```json { "scripts": { "start:http": "node dist/index-http.js", "dev:http": "tsx src/index-http.ts", "reload:http": "npm run build && systemctl --user restart mcp-nome && sleep 2 && curl -s http://127.0.0.1:32XX/health" } } ``` --- ## Transporte SSE (Server-Sent Events) - DEPRECATED ⚠️ **DEPRECATED:** Usar HTTP (StreamableHTTP) para novos MCPs. SSE mantido apenas para retrocompatibilidade. Para MCPs que precisam de conexao persistente (ex: Claude Code com multiplos terminais). **IMPORTANTE:** Verificar MCPs existentes (desk-crm-v3, moloni) antes de implementar! ### Configuracao SSE ```json // ~/.claude.json { "mcpServers": { "meu-mcp": { "type": "sse", "url": "http://127.0.0.1:31XX/sse" } } } ``` ### Template index-sse.ts (http nativo - RECOMENDADO) ```typescript #!/usr/bin/env node /** @author Descomplicar | @link descomplicar.pt | @copyright 2026 */ import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema } from '@modelcontextprotocol/sdk/types.js'; import * as http from 'http'; import { URL } from 'url'; const PORT = parseInt(process.env.MCP_SSE_PORT || '31XX'); const HOST = process.env.MCP_SSE_HOST || '127.0.0.1'; // Track active sessions const sessions = new Map(); // Define tools array const allTools = [ // ... your tools ]; // Create MCP server for each session function createMcpServer(): Server { const server = new Server({ name: 'mcp-nome', version: '1.0.0' }); // Set capabilities (server as any)._capabilities = { tools: {}, resources: {}, prompts: {} }; // List tools server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: allTools.map((t) => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })) })); // List resources (empty) server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] })); // List prompts (empty) server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] })); // Call tool server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const tool = allTools.find((t) => t.name === name); if (!tool) { return { content: [{ type: 'text', text: 'Tool not found' }] }; } try { return await tool.handler(args); } catch (error) { return { content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }] }; } }); return server; } async function main() { const httpServer = http.createServer(async (req, res) => { // CORS headers res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } const url = new URL(req.url || '/', `http://${HOST}:${PORT}`); // Health check if (url.pathname === '/health') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', sessions: sessions.size, tools: allTools.length })); return; } // SSE endpoint - new session if (url.pathname === '/sse' && req.method === 'GET') { const transport = new SSEServerTransport('/message', res); const sessionId = transport.sessionId; // USAR sessionId do transport! sessions.set(sessionId, transport); // Criar NOVO server por sessao const server = createMcpServer(); transport.onclose = () => { sessions.delete(sessionId); }; await server.connect(transport); return; } // Message endpoint if (url.pathname === '/message' && req.method === 'POST') { const sessionId = url.searchParams.get('sessionId'); if (!sessionId) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Missing sessionId' })); return; } const transport = sessions.get(sessionId); if (!transport) { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Session not found' })); return; } await transport.handlePostMessage(req, res); return; } // 404 res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found' })); }); httpServer.listen(PORT, HOST, () => { console.log(`MCP SSE Server running at http://${HOST}:${PORT}/sse`); }); // Graceful shutdown process.on('SIGTERM', () => httpServer.close(() => process.exit(0))); process.on('SIGINT', () => httpServer.close(() => process.exit(0))); } main().catch(console.error); ``` ### Pontos Criticos SSE | Aspecto | Correcto | Errado | |---------|----------|--------| | sessionId | `transport.sessionId` | `Date.now().toString()` | | Server | Novo por sessao | Reutilizar mesmo | | HTTP | `http.createServer` | Express (overhead) | | /message | Com `?sessionId=` | Sem sessionId | ### Template systemd Service Criar em `~/.config/systemd/user/mcp-nome.service`: ```ini [Unit] Description=MCP Nome SSE Server After=network.target [Service] Type=simple WorkingDirectory=/home/ealmeida/mcp-servers/mcp-nome ExecStart=/home/ealmeida/.nvm/versions/node/v22.18.0/bin/node dist/index.js --sse --port 31XX Restart=on-failure RestartSec=5 Environment=NODE_ENV=production Environment=LOG_LEVEL=error StandardOutput=journal StandardError=journal [Install] WantedBy=default.target ``` ### Comandos systemd ```bash # Activar systemctl --user daemon-reload systemctl --user enable mcp-nome.service systemctl --user start mcp-nome.service # Ver estado systemctl --user status mcp-nome # Ver logs journalctl --user -u mcp-nome -f # Reiniciar apos alteracoes systemctl --user restart mcp-nome ``` ### Portas SSE Reservadas | Porta | MCP | |-------|-----| | 3100 | desk-crm-v3 | | 3101 | memory-supabase | | 3102 | wikijs | | 3103 | ssh-unified | | 3104 | dify-kb | | 3105 | n8n | | 3106 | cwp | | 3107 | youtube-research | | 3108 | moloni | | 3109+ | disponiveis | ### Script de Desenvolvimento (reload:sse) Adicionar ao `package.json`: ```json { "scripts": { "start:sse": "node dist/index.js --sse", "reload:sse": "npm run build && systemctl --user restart mcp-nome && sleep 2 && curl -s http://127.0.0.1:31XX/health" } } ``` --- ## Padrões SQL (Perfex CRM) Para MCPs que integram com Perfex CRM: ### Campos FK (Foreign Keys) ```typescript // CORRECTO: Perfex usa 0 como "não definido", NÃO NULL client_id || 0, project_id || 0, country || 0, assigned || 0, // EXCEPÇÃO: source em leads precisa ID válido (buscar da BD) let leadSource = source; if (!leadSource) { const [defaultSource] = await client.query( 'SELECT id FROM tblleads_sources ORDER BY id ASC LIMIT 1' ); leadSource = defaultSource?.id || 1; } ``` ### Padrão de Fallback ```typescript // Para campos que aceitam 0 como default field || 0 // Para campos que precisam valor válido da BD const defaultValue = await getDefaultFromDB(); field || defaultValue ``` ### Tabelas Perfex (prefixo tbl) ``` tblclients, tblprojects, tbltasks, tblinvoices, tblleads, tblleads_sources, tblleads_status, tblstaff, tbltaskstimers, tblexpenses ``` --- ## Checklist de Segurança MCP **Baseado em audit MCP Outline PostgreSQL (164 tools, 7 bugs corrigidos).** ### Antes de Commit - [ ] **SQL Injection:** Todos os inputs validados antes de entrar em queries? ```typescript // ERRADO: interpolação directa const query = `ORDER BY ${field} ASC`; // CORRECTO: validar primeiro validateFieldName(field); // rejeita keywords SQL const query = `ORDER BY ${field} ASC`; ``` - [ ] **Transações:** Operações multi-query relacionadas em transação? ```typescript // ERRADO: queries separadas await query('DELETE FROM children WHERE parentId = $1', [id]); await query('DELETE FROM parents WHERE id = $1', [id]); // CORRECTO: transação await withTransaction(async (client) => { await client.query('DELETE FROM children WHERE parentId = $1', [id]); await client.query('DELETE FROM parents WHERE id = $1', [id]); }); ``` - [ ] **Recursos:** Aquisição tem `finally` com release? ```typescript // ERRADO: release pode não executar const client = await pool.connect(); await client.query('SELECT 1'); client.release(); // CORRECTO: finally garante release let client = null; try { client = await pool.connect(); await client.query('SELECT 1'); } finally { if (client) client.release(); } ``` - [ ] **Cleanup:** Código de cleanup tem try-catch próprio? ```typescript // ERRADO: ROLLBACK pode falhar e mascarar erro catch (error) { await client.query('ROLLBACK'); throw error; } // CORRECTO: ROLLBACK com try-catch catch (error) { try { await client.query('ROLLBACK'); } catch (rollbackError) { console.error('Rollback failed', rollbackError); } throw error; } ``` - [ ] **Random:** Usando `crypto.randomBytes()` e não `Math.random()`? - [ ] **Versão:** Sincronizada com package.json? ### Grep de Validação ```bash # Verificar interpolação SQL perigosa grep -rn '`.*\${.*}`' src/ | grep -i 'select\|insert\|update\|delete\|order' # Verificar Math.random em produção grep -rn 'Math.random' src/ # Verificar .connect() sem finally grep -rn '\.connect()' src/ ``` --- ## MCPs Existentes (Referência) | MCP | Path | Tools | |-----|------|-------| | desk-crm-v3 | ~/mcp-servers/desk-crm-v3/ | 150+ | | memory-supabase | ~/mcp-servers/memory-supabase/ | 5 | | google-workspace | (npm package) | 42 | | filesystem | (npm package) | 8 | --- ## Integração com Agente O agente `mcp-protocol-developer` é invocado para: - Desenvolvimento complexo de ferramentas - Debug de problemas MCP - Optimização de performance - Implementação de recursos avançados --- ## Datasets Dify ``` mcp__dify-kb__dify_kb_retrieve_segments dataset:"MCP Servers" query:"..." mcp__dify-kb__dify_kb_retrieve_segments dataset:"Claude Code" query:"..." mcp__dify-kb__dify_kb_retrieve_segments dataset:"Desenvolvimento de Software" query:"..." ``` --- ## Referências - [MCP SDK Documentation](https://modelcontextprotocol.io/) - [[AGT-Sistema-Agentes|mcp-protocol-developer]] - [[Stack/Claude Code/MCPs/|MCPs Documentados]] --- --- ## Changelog ### v1.3.0 (2026-01-31) - **HTTP StreamableHTTP** como padrão oficial (template completo) - SSE marcado como DEPRECATED (retrocompatibilidade) - **Checklist de Segurança MCP** (baseado em audit 164 tools) - SQL injection prevention - Transacções para operações multi-query - Resource release em finally blocks - Cleanup error handling - crypto.randomBytes() vs Math.random() - Grep de validação para code review - Portas HTTP reservadas (3200+) ### v1.2.0 (2026-01-27) - Template SSE completo com http nativo (nao Express) - Pontos criticos SSE documentados (sessionId, Server por sessao) - Template systemd service para MCPs SSE - Tabela de portas SSE reservadas - Comandos systemd documentados ### v1.1.0 (2026-01-27) - Adicionada seccao SSE (Server-Sent Events) - Script reload:sse para desenvolvimento - Padroes SQL Perfex CRM (FKs usam 0, nao NULL) - Workflow de desenvolvimento documentado ### v1.0.0 (2026-01-27) - Versao inicial --- *Skill v1.3 | Descomplicar®* --- ## Instrumentação Automática Esta skill grava métricas automaticamente para análise PDCA. ### Query para Gravar (executar no final) ```sql INSERT INTO tblskill_agent_metrics ( type, name, duration_ms, status, staff_id, kb_consulted, kb_cache_hit, tool_calls, project_id ) VALUES ( 'skill', '/$SKILL_NAME', {DURACAO_MS}, '{STATUS}', 25, {KB_CONSULTADO}, {CACHE_HIT}, {TOOL_CALLS}, {PROJECT_ID} ); ``` --- **Instrumentação**: Activa | **Data**: 2026-02-03 --- ## Quando NÃO Usar - Para tarefas fora do domínio de especialização desta skill - Quando outra skill mais específica está disponível - Para operações que requerem confirmação manual do utilizador ## Protocolo 1. Analisar requisitos da tarefa 2. Verificar disponibilidade de ferramentas necessárias 3. Executar operações de forma incremental 4. Validar resultados antes de concluir 5. Reportar status e próximos passos