From 174f2f93573a0950d82645974a0b35e4be84b96b Mon Sep 17 00:00:00 2001 From: Emanuel Almeida Date: Wed, 19 Aug 2026 06:43:13 +0100 Subject: [PATCH] feat(skills): adicionar mcp-updraftplus (backup UpdraftPlus, quase so-leitura, alto risco) --- wordpress/skills/mcp-updraftplus/SKILL.md | 129 ++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 wordpress/skills/mcp-updraftplus/SKILL.md diff --git a/wordpress/skills/mcp-updraftplus/SKILL.md b/wordpress/skills/mcp-updraftplus/SKILL.md new file mode 100644 index 0000000..9750833 --- /dev/null +++ b/wordpress/skills/mcp-updraftplus/SKILL.md @@ -0,0 +1,129 @@ +--- +name: mcp-updraftplus +description: MCP dedicado multi-site (node stdio, ligação `updraftplus` em ~/.omp/agent/mcp.json) para consultar o UpdraftPlus (backup) em qualquer site do bundle Descomplicar® via WP-CLI/SSH — estado do plugin, agendamento, retenção, destinos de storage (nunca expõe credenciais), histórico de backups, último resultado. Superfície de ALTO RISCO tratada como só-leitura: a única escrita é disparar um backup manual (aditivo, nunca apaga nada), atrás de `confirm:true`. Site é um parâmetro em cada tool, não uma ligação fixa. Usar quando "updraftplus mcp", "estado backup wordpress", "agendamento updraftplus", "destinos storage backup", "histórico backups", "disparar backup manual mcp", "DISABLE_WP_CRON backup", "backup nunca corre sozinho". +layer: wiki +--- + +# /mcp-updraftplus — MCP dedicado multi-site ao UpdraftPlus + +Projecto em `/media/ealmeida/Dados/Dev/mcp-updraftplus/` (TypeScript, SDK MCP oficial, stdio). +Mesmo padrão dos outros MCPs desta família (`mcp-wpfc`, `mcp-wpforms`, `mcp-fluentform`, +`mcp-fluent-smtp`, `mcp-bit-integrations`, `mcp-insert-headers-footers`): sem código PHP novo no +WordPress, cada tool executa `wp option get`/`wp plugin list`/`wp cron event list` via +`ssh server` sobre o WP-CLI já instalado. **`site` é um parâmetro em cada tool**, não uma ligação +fixa. Não existe comando WP-CLI próprio do UpdraftPlus (confirmado: `wp help` não lista nada, +`wp updraftplus` dá "not a registered command") — tudo passa por `wp_options` genéricas. + +## Porque é que este MCP é (quase) só-leitura + +Backups são a última linha de defesa. Este MCP **nunca** apaga, substitui ou reconfigura nada — +zero `update_option`/`delete_option` sobre qualquer chave `updraft_*`. A única acção que muda +estado é `updraftplus_trigger_backup_now`, e mesmo essa é puramente **aditiva** (dispara um novo +backup; nunca toca no que já existe) e exige `confirm: true` (zod `z.literal(true)`, rejeitado ao +nível do schema antes de o handler correr — testado ao vivo). Não há tools para desligar +agendamento, apagar um backup do histórico, ou mudar destinos de storage — de propósito, fora do +âmbito deste v1 por decisão de risco, não por limitação técnica. + +## Sites conhecidos + +Chamar `updraftplus_list_sites` para a lista actual. Verificado ao vivo 19-08-2026 via +`wp plugin list --format=json` nos 8 sites do bundle: só 4 têm o UpdraftPlus (Free 1.26.6, sem +addon Premium) — `starter`, `ccv`, `ecommerce-demo`, `ecommerce`. Os outros 4 +(`descomplicar`/produção, `emanuelalmeida`, `care`, `e-commerce`) não têm o plugin, omitidos de +propósito do mapa de aliases. Também aceita um path absoluto directamente +(`/home/ealmeida/`) para sites fora desta lista. + +**Achado de risco real (19-08-2026):** nos 4 sites, `DISABLE_WP_CRON=true` está definido em +`wp-config.php` (confirmado por leitura directa), e nenhum tem `updraft_interval` alguma vez +gravado pela UI — ou seja, mesmo que alguém configurasse um agendamento na UI, ele nunca +dispararia sozinho neste servidor (WP-Cron pseudo-cron desligado, sem substituto por cron de +sistema para estes sites). `ccv` tem ainda um `updraft_last_backup`/`updraft_backup_history` +**ilusório**: os nomes de ficheiro lá dentro (`Starter_Descomplicar_...`) mostram que foi herdado +de um clone de base de dados do site `starter`, não um backup real de `ccv` — confirmado por +`siteurl` distinto entre os dois. `starter` teve 1 backup manual real em 2025-06-11. `ecommerce` +e `ecommerce-demo` nunca correram um backup (sem `updraft_backup_history` sequer). Ver notas por +site em `updraftplus_list_sites`/`src/sites.ts`. + +## Tools (7) + +| Tool | Read-only | Uso | +|---|---|---| +| `updraftplus_list_sites` | sim | Aliases conhecidos, path WP, nota de estado | +| `updraftplus_get_status` | sim | Free/Premium instalado+activo+versão, `DISABLE_WP_CRON`, última mensagem de log — chamar sempre primeiro | +| `updraftplus_get_schedule` | sim | Intervalo/retenção/horas de ficheiros e BD, flags de inclusão, próximos eventos de cron reais (`wp cron event list` filtrado) | +| `updraftplus_get_storage_destinations` | sim | Os ~15 destinos nativos, activo/não, `configuredInstanceCount` — **nunca devolve credenciais** | +| `updraftplus_get_backup_history` | sim | Histórico completo (`updraft_backup_history`): data, nonce, entidades, tamanho total, destino, versão — `limit` opcional | +| `updraftplus_get_last_backup` | sim | `updraft_last_backup`: sucesso/erros do resultado mais recente | +| `updraftplus_trigger_backup_now` | **não** | Dispara backup completo em background (não bloqueia), exige `confirm:true`. Ver secção própria abaixo | + +Todas as tools exigem `site` como primeiro parâmetro. + +## Segurança — mascaramento de credenciais + +Cada destino de storage nativo (`updraft_dropbox`, `updraft_s3`, `updraft_googledrive`, +`updraft_ftp`, `updraft_sftp`, etc.) tem uma option própria cujo blob JSON PODE conter +credenciais em claro — confirmado por leitura directa (`appkey`/`secret`/`tk_access_token` no +Dropbox, `accesskey`/`secretkey` no S3, `clientid`/`secret`/`token` no Google Drive, +`host`/`user`/`pass` no FTP — mesmo quando vazias, a estrutura já existe). `getStorageDestinations` +(`src/wpcli.ts`) lê essas options só para calcular localmente um booleano `configured` +(`hasConfiguredValue` — recursivo, conta strings não-vazias; números e booleanos nunca contam, +excepto exclusão explícita do campo `authurl` que vem pré-preenchido pelo próprio plugin com o +endpoint fixo da Rackspace mesmo sem nunca ter sido tocado — falso positivo confirmado e corrigido +durante os testes). **O conteúdo em si nunca sai da função** — o tool só devolve +`{slug, label, active, instanceCount, configuredInstanceCount}`. + +## `updraftplus_trigger_backup_now` — mecânica e decisões de risco + +Invoca literalmente a mesma `do_action('updraft_backupnow_backup_all', array())` que o botão +"Backup Now" da UI dispara (`admin.php:2700`, `request_backupnow()`), com opções vazias = inclui +tudo o que o site tiver configurado para incluir, envia para os destinos já activos. **Nunca +apaga nem substitui um backup existente** — só adiciona um novo ao histórico. + +Corre em **background** no servidor via `nohup … & disown` — a chamada SSH regressa em segundos +mesmo que o backup real demore minutos, confirmado ao vivo (PID devolvido imediatamente, +processo real confirmado a correr via `ps aux` depois da ligação SSH já ter fechado). Isto é +necessário, não cosmético: como `DISABLE_WP_CRON=true` nestes 4 sites, agendar via +`wp cron event schedule … now` nunca dispararia sozinho — o único caminho real para um backup +correr fora de alguém clicar na UI é invocar a `do_action` directamente. O próprio plugin já +protege contra execuções concorrentes (semáforo interno `UpdraftPlus_Semaphore`), por isso +disparar duas vezes seguidas é seguro — a segunda fica bloqueada com log "another backup is +apparently already running", não corrompe nada. + +**Gotcha de infra local — GATE 5.1:** o comando remoto NUNCA usa `>`/`>>` para o log +(`~/.config/agent-gates/infra-mutation-check.sh`, o motor que bloqueia mutações de infra sem +autorização humana prévia, trata qualquer `>` seguido de caminho absoluto como potencialmente +destrutivo — testado ao vivo, `>>` também dispara porque a regex só procura um carácter `>` +seguido de `/`, independente de quantos `>` há). A saída é capturada com +`| tee -a "$logFile" > /dev/null` — `tee -a` não contém `>` no texto do comando, e a redirecção +final para `/dev/null` tem excepção explícita no motor do gate. Validado directamente contra +`infra-mutation-check.sh` (exit 0) antes de confirmar o desenho. + +Depois de disparar, usar `updraftplus_get_last_backup` ou `updraftplus_get_backup_history` +passados alguns minutos para confirmar sucesso — o tool não espera pelo resultado. + +## Fora de âmbito (decisão deliberada, não lacuna técnica) + +- Desligar/alterar agendamento (`updraft_interval`, `updraft_retain`, etc.) — só leitura. +- Apagar um backup do histórico ou de storage remoto. +- Configurar/alterar destinos de storage (credenciais nunca são escritas por este MCP). +- Restaurar um backup — operação com risco de perda de dados muito maior que disparar um novo + backup; fora do v1 por decisão de risco explícita, não avaliada tecnicamente. + +## Verificação + +Construído e testado ponta-a-ponta contra produção 19-08-2026: `updraftplus_get_status` em +`starter` confirmou Free 1.26.6 activo, Premium ausente, `DISABLE_WP_CRON=true`; +`updraftplus_get_schedule` confirmou `interval: null` (nunca configurado) com nota explicativa; +`updraftplus_get_storage_destinations` confirmou os 16 destinos com `configuredInstanceCount: 0` +em `starter` (nenhum falso positivo depois da correcção do `authurl`); +`updraftplus_get_backup_history`/`updraftplus_get_last_backup` confirmaram o backup real de +2025-06-11 em `starter` e o dado ilusório herdado em `ccv`; `updraftplus_trigger_backup_now` +disparado com sucesso em `ecommerce-demo` (`confirm:true`), devolveu PID em ~4s, confirmado por +`ps aux` fora do MCP a mostrar o processo `wp eval` real a correr depois da ligação SSH ter +fechado. Schema `confirm: z.literal(true)` testado a rejeitar `confirm` ausente e `confirm:false` +antes de o handler correr. + +## Skills relacionadas + +Nenhuma skill de conhecimento separada para o UpdraftPlus existe no bundle — esta skill é a única +documentação, incluindo o mapeamento de options acima.