--- name: emcp-tools description: Auditoria e gestão de postura de segurança do EMCP Tools (extensão do WP MCP Adapter da Elementskit — expõe dados Elementor, widgets e ferramentas de operação como MCP tools para agentes de IA). Cobre inventário completo das ~162 abilities registadas por categoria (activas vs desligadas, verificado via wp_get_abilities()), os 9 módulos do plugin (prompts, brand-kits, templates, themer, redirects, agent-skills, cloud, image-optimization, svg-support) e o que cada um expõe, o mecanismo de deny-list incremental versionado (34 migrações), activação granular e segura de uma ferramenta individual (ex: só leitura Rank Math), avaliação de risco por categoria, e arquitectura de registo MCP multi-site. Usar quando "emcp tools", "emcp-tools", "mcp adapter wordpress", "ferramentas mcp elementor", "que tools o agente pode chamar", "activar tool emcp", "desligar tool mcp", "postura default-deny mcp", "agentes de ia no wordpress", "módulos emcp tools", "inventário completo mcp tools", "quantas tools estão activas". --- # /emcp-tools — Auditoria e Postura de Segurança do EMCP Tools (MCP para Elementor/WP) Plugin `emcp-tools` (Mian Shahzad Raza) — extensão do WP MCP Adapter que expõe dados Elementor, widgets, page design tools e um conjunto amplo de operações WordPress como **ferramentas MCP** chamáveis por agentes de IA. Versão verificada `3.12.1`, instalado e activo em `emanuelalmeida.pt`, `descomplicar.pt` (`public_html`) e `starter.descomplicar.pt`. **Fonte:** `Hub/04-Stack/02.04-Sistemas/71.Seguranca/CONFIG-Plugins-Referencia.md` §10 (EMCP Tools) + `BUNDLE-Excelencia-WP.md` + verificação SSH ao vivo nesta sessão (16-08-2026) nos três sites do ecossistema. --- ## Arquitectura — como o agente chega às tools deste plugin O plugin regista um servidor MCP próprio, `emcp-tools-server` (nome visível "MCP Tools for Elementor Server"), lado a lado com outros servidores do mesmo WP MCP Adapter (`elementskit-mcp-server`, `mcp-adapter-default-server`, e nalguns sites `fluent-crm`). A ligação do agente ao site faz-se por SSH + STDIO, uma entrada por site em `~/.omp/agent/mcp.json`: ```json "emcp-emanuelalmeida": { "type": "stdio", "command": "ssh", "args": ["-T", "-o", "BatchMode=yes", "server", "sudo -u ealmeida /usr/local/bin/wp mcp-adapter serve --server=emcp-tools-server --user=ealmeida --path=/home/ealmeida/emanuelalmeida.pt"], "timeout": 60000, "enabled": true } ``` **Três instâncias activas no ecossistema**, uma ligação MCP por site (cada uma com o seu próprio `--path` e, portanto, a sua própria config `emcp_tools_*` independente — **não presumir que a postura de um site se aplica a outro**, ver §3): | Ligação em `mcp.json` | Site | `--path` | |---|---|---| | `emcp-descomplicar` | descomplicar.pt | `/home/ealmeida/public_html` | | `emcp-tools` | starter.descomplicar.pt | `/home/ealmeida/starter.descomplicar.pt` | | `emcp-emanuelalmeida` | emanuelalmeida.pt | `/home/ealmeida/emanuelalmeida.pt` | Comando nativo WP-CLI para inspecção do lado do servidor (sem passar pelo protocolo MCP): ```bash wp mcp-adapter list --path=$PATH --allow-root wp mcp-adapter list --format=json --path=$PATH --allow-root ``` ⚠️ **Achado desta sessão — a coluna `Tools` de `wp mcp-adapter list` NÃO é um proxy fiável do nº de ferramentas realmente activas.** Verificado ao vivo nos 3 sites (16-08-2026): | Site | `emcp_tools_disabled_tools` (desligadas) | `wp mcp-adapter list` → coluna `Tools` do `emcp-tools-server` | Activas REAIS (`wp_get_abilities()`, §7) | |---|---|---|---| | descomplicar.pt | 78 | `3` | **165** (achado crítico — §8.2) | | starter.descomplicar.pt | 131 | `3` | 114 | | emanuelalmeida.pt | 131 | `115` | 115 | Não há correlação directa (menos desligadas em `descomplicar.pt` devia implicar mais activas, mas mostra o valor mais baixo). A contagem desta coluna parece depender de estado de registo/cache do lado do WP-CLI, não do filtro `emcp_tools_disabled_tools` aplicado em tempo de pedido MCP. **Não usar esta coluna para avaliar postura de segurança** — usar sempre o conteúdo real de `emcp_tools_disabled_tools` (§2) para saber o que está fechado, e o protocolo MCP (`tools/list` via um cliente MCP ligado, ex. a própria sessão do agente) para saber com exactidão o que está disponível para chamada num momento dado. A 4ª coluna acima é a contagem definitiva, obtida com o método de §7 (`wp eval` + `wp_get_abilities()` filtrado, cruzado slug-a-slug contra `emcp_tools_disabled_tools`) — coincide exactamente com a coluna `Tools` só em `emanuelalmeida.pt`; nos outros dois sites a coluna do WP-CLI é enganadora nos dois sentidos (mostra `3` quando o real é 114 **e** 165). --- ## 1. Config verificada (`emanuelalmeida.pt`, `wp_options`) ```bash wp option list --search='emcp_tools*' --path=$PATH --allow-root ``` | Opção | Valor real | Nota | |---|---|---| | `emcp_tools_sandbox_location` | `emcp-sandbox` | Directório isolado para operações de sandbox | | `emcp_tools_active_modules` | `["prompts","brand-kits","templates","themer","redirects","agent-skills","cloud"]` (7 de 9 semeados) | `image-optimization` e `svg-support` estão **semeados mas não activos** — idêntico nos 3 sites verificados | | `emcp_tools_disabled_tools` | array indexado com **131** ferramentas desligadas (formato `emcp-tools/`) | Ver lista completa §2 | | `emcp_tools_defaults_applied` | `34` | Contador interno de defaults aplicados na instalação | | `emcp_tools_modules_seeded` / `emcp_tools_legacy_meta_migrated` / `emcp_tools_slug_namespace_migrated` | flags internas de migração | Sem acção | | `emcp_tools_changelog_db_version` / `emcp_tools_oauth_db_version` / `emcp_tools_redirects_db_version` / `emcp_tools_search_index_db_version` | versões de schema interno | Sem acção | | `emcp_tools_themer_index` / `emcp_tools_themer_index_healed` | índice interno do módulo Themer | Sem acção | Outros dois sites (`descomplicar.pt`, `starter.descomplicar.pt`) partilham o mesmo `emcp_tools_active_modules`; `starter.descomplicar.pt` partilha exactamente os mesmos 131 nomes desligados que `emanuelalmeida.pt`; `descomplicar.pt` tem apenas 78 desligados (config mais permissiva — **verificar antes de assumir postura igual entre sites**). Note-se também que `templates` e `agent-skills` são módulos **Pro** (`tier() === 'pro'`) semeados como "activos" em todos os 3 sites por omissão do próprio plugin, mas nenhum destes sites tem licença Pro (`emcp_tools_fs()->can_use_premium_code() === false`), logo `is_available()` devolve `false` e **nenhum dos dois módulos corre de facto** — ver tabela completa dos 9 módulos em §6, e o achado crítico específico de `descomplicar.pt` em §8.2 (78 desligados não significa "mais permissivo apenas um pouco" — significa **zero** tools nativas fechadas). --- ## 2. Lista completa das 131 ferramentas desligadas por omissão Confirmada ao vivo (`emanuelalmeida.pt` e `starter.descomplicar.pt`, formato idêntico): array indexado (não associativo) em `emcp_tools_disabled_tools`, cada entrada `emcp-tools/`. ```bash wp option get emcp_tools_disabled_tools --format=json --path=$PATH --allow-root ``` ### 2.1 Ficheiros, sistema e execução remota — risco máximo, nunca activar sem supervisão directa ``` write-file, edit-file, delete-file, run-wp-cli, dispatch-wp-cli, get-wp-cli-job, list-wp-cli-jobs ``` ### 2.2 Base de dados directa ``` insert-row, update-rows, delete-rows ``` ### 2.3 Gestão de plugins e temas ``` install-plugin, activate-plugin, deactivate-plugin, update-plugin, delete-plugin, install-theme, switch-theme, update-theme, delete-theme ``` ### 2.4 Utilizadores ``` create-user, update-user ``` ### 2.5 Redirecções e migração de site ``` create-redirect, update-redirect, delete-redirect, migrate-site, sync-to-live, sync-content-item ``` ### 2.6 Integrações de terceiros — leitura e escrita (granularidade por par read/write) ``` acf-write woo-read, woo-write metabox-write essential-addons-read, premium-addons-read, uae-read, uae-write cf7-write wpforms-read, wpforms-write gravityforms-read, gravityforms-write fluentforms-read, fluentforms-write ninjaforms-read, ninjaforms-write formidable-read, formidable-write metform-read, metform-write sureforms-read, sureforms-write forminator-read, forminator-write ``` ### 2.7 SEO — TODOS os plugins, leitura E escrita fechadas (Rank Math dono único da superfície SEO neste bundle) ``` slimseo-write yoast-read, yoast-write rankmath-read, rankmath-write aioseo-read, aioseo-write seopress-read, seopress-write seoframework-read, seoframework-write surerank-read, surerank-write ``` ### 2.8 Temas/frameworks de blocos ``` theme-write astra-write spectra-write kadence-write, kadence-blocks-write generatepress-read, generatepress-write generateblocks-read, generateblocks-write blocksy-blocks-read, blocksy-blocks-write, blocksy-extensions-read, blocksy-extensions-write ``` ### 2.9 Elementor avançado (widgets Pro, templates de tema, popups, dynamic tags, CSS/classes globais) ``` add-pro-widget, create-elementor-theme-template, set-elementor-template-conditions, list-dynamic-tags, set-dynamic-tag, create-popup, set-popup-settings, delete-media, add-custom-css, create-global-class, update-global-class, delete-global-class, reorder-global-classes ``` ### 2.10 Snippets PHP — execução de código arbitrário, risco máximo absoluto ``` add-code-snippet, list-code-snippets, validate-php-snippet, create-php-snippet, update-php-snippet, get-php-snippet, list-php-snippets, delete-php-snippet ``` ### 2.11 Memória do agente ``` recall, remember, save-session-summary ``` ### 2.12 Templates PHP de tema ``` create-theme-php-template, list-theme-php-templates, get-theme-php-template, update-theme-php-template, delete-theme-php-template ``` ### 2.13 SEO/A11y automatizados (geração de conteúdo/schema/contraste) ``` audit-page-seo, extract-keywords-from-content, generate-meta-tags, generate-schema-markup, set-social-image, audit-page-a11y, fix-color-contrast, add-alt-text-from-context ``` ### 2.14 Widgets e blocos custom (criação/edição/eliminação) ``` list-control-types, validate-widget-spec, create-custom-widget, update-custom-widget, get-custom-widget, list-custom-widgets, set-widget-status, delete-custom-widget, list-block-control-types, validate-block-spec, create-custom-block, update-custom-block, get-custom-block, list-custom-blocks, set-block-status, delete-custom-block ``` --- ## 3. Como listar ferramentas activas vs desligadas num site ```bash PATH=/home/USER/SITE # Quantas e quais estão desligadas wp option get emcp_tools_disabled_tools --format=json --path=$PATH --allow-root # Módulos activos (nível mais alto — prompts/brand-kits/templates/themer/redirects/agent-skills/cloud/image-optimization/svg-support) wp option get emcp_tools_active_modules --format=json --path=$PATH --allow-root # Servidores MCP registados neste site (não usar a coluna Tools como medida de activo — ver aviso acima) wp mcp-adapter list --path=$PATH --allow-root ``` **Para saber com exactidão quais tools estão disponíveis para chamada num dado momento** (a lista real, não a config): ligar via o protocolo MCP (a própria ligação `emcp-*` em `mcp.json`) e pedir `tools/list` — a config `emcp_tools_disabled_tools` é aplicada como filtro em tempo de pedido pelo plugin, não é reflectida de forma fiável pelo WP-CLI. --- ## 4. Activar uma ferramenta específica com segurança (granularidade individual) O array é **indexado** (não associativo), por isso `wp option patch` não serve para remover/adicionar uma entrada isolada — usar `wp eval` com `array_diff`/`array_values` para manter o array limpo e sem duplicados. ### Activar (remover do deny-list) ```bash wp eval ' $tool = "emcp-tools/rankmath-read"; $disabled = get_option("emcp_tools_disabled_tools", []); $before = count($disabled); $disabled = array_values(array_diff($disabled, [$tool])); update_option("emcp_tools_disabled_tools", $disabled); printf("antes=%d depois=%d removida=%s\n", $before, count($disabled), $before !== count($disabled) ? "sim" : "nao (ja estava activa ou nome errado)"); ' --allow-root --path=$PATH ``` **Exemplo do pedido típico — activar só leitura Rank Math sem abrir escrita:** activar `emcp-tools/rankmath-read` e deixar `emcp-tools/rankmath-write` no array (continua desligada). A granularidade é sempre ao nível do slug individual — nunca existe um toggle "Rank Math on/off" only por read/write, exactamente como aparece na lista §2.7. ### Reverter (voltar a fechar) ```bash wp eval ' $tool = "emcp-tools/rankmath-read"; $disabled = get_option("emcp_tools_disabled_tools", []); if (!in_array($tool, $disabled, true)) { $disabled[] = $tool; } update_option("emcp_tools_disabled_tools", $disabled); echo count($disabled) . " tools desligadas\n"; ' --allow-root --path=$PATH ``` ### Verificar sempre depois, de forma independente ```bash wp option get emcp_tools_disabled_tools --format=json --path=$PATH --allow-root | grep -o 'rankmath-read' || echo "confirmado: activa (nao esta no deny-list)" ``` --- ## 5. Avaliação de risco por categoria — o que activar e o que nunca activar sem supervisão | Categoria | Postura recomendada | Justificação | |---|---|---| | Leitura de design Elementor (o que fica activo por omissão — não está no deny-list) | ✅ Seguro por omissão | Propósito central do plugin; só leitura/inspecção, sem mutação | | SEO leitura isolada (ex.: só `rankmath-read`) | ✅ Seguro activar caso a caso | Não muta nada; útil para um agente auditar sem escrever — mas Rank Math continua a ser gerido preferencialmente via `/rank-math` (WP-CLI directo), não via MCP | | SEO escrita (qualquer `*-write` de SEO) | 🔴 Nunca activar sem supervisão | Contorna o fluxo de auditoria/rollback do `/rank-math`; risco de meta/schema incoerente sem revisão humana | | Redirecções/migração (`create-redirect`, `migrate-site`, `sync-to-live`) | 🔴 Nunca activar sem supervisão | Mutação estrutural com impacto em produção e SEO (redirects mal configurados = perda de ranking) | | Gestão de plugins/temas (`install-plugin`, `activate-plugin`, …) | 🔴 Nunca activar sem supervisão | Um agente autónomo instalar/remover plugins é a superfície mais perigosa depois de execução de código | | Snippets PHP (`create-php-snippet`, `validate-php-snippet`, `update-php-snippet`) | 🔴🔴 Nunca activar — risco máximo absoluto | **Execução de PHP arbitrário** persistido no site; equivalente a dar shell ao agente. Único candidato a "nunca, em circunstância nenhuma, sem revisão humana linha a linha antes de guardar" | | Ficheiros/sistema (`write-file`, `edit-file`, `delete-file`, `run-wp-cli`, `dispatch-wp-cli`) | 🔴🔴 Nunca activar — risco máximo | Escrita directa no filesystem e execução WP-CLI arbitrária via MCP; contorna toda a auditoria de shell/SSH existente | | Base de dados directa (`insert-row`, `update-rows`, `delete-rows`) | 🔴🔴 Nunca activar — risco máximo | SQL de escrita sem passar por validação da camada WP; risco de corrupção silenciosa | | Widgets/blocos custom (`create-custom-widget`, `create-custom-block`, …) | 🟡 Só com supervisão activa | Cria código PHP/JS executado no frontend; menos grave que snippets PHP mas ainda superfície de execução | | Utilizadores (`create-user`, `update-user`) | 🔴 Nunca activar sem supervisão | Escalada de privilégios / criação de contas fora do fluxo humano | | Memória do agente (`recall`, `remember`, `save-session-summary`) | 🟡 Avaliar por site | Sem risco de segurança directo, mas cria estado persistente fora do controlo humano — activar só se o workflow do site precisar de continuidade entre sessões MCP | | Integrações de terceiros — leitura (`woo-read`, `wpforms-read`, …) | 🟡 Seguro activar caso a caso, um a um | Só expõe dados existentes; activar apenas as integrações realmente instaladas no site, nunca em bloco | | Integrações de terceiros — escrita (`woo-write`, `cf7-write`, …) | 🔴 Nunca activar sem supervisão | Mutação em dados de negócio (encomendas, submissões de formulário) sem revisão humana | **Regra geral (revista após leitura completa do código-fonte, §6-§8):** a **concepção** do plugin é default-deny bem desenhada — o deny-list incremental descrito em §8.1 fecha metodicamente cada nova categoria de risco assim que é lançada. Mas a **postura real, verificada ao vivo, NÃO é uniforme entre os 3 sites**: `emanuelalmeida.pt` e `starter.descomplicar.pt` reflectem essa concepção (115/162 e 114/160 tools activas, respectivamente, com todas as categorias 🔴/🔴🔴 de facto fechadas); `descomplicar.pt` diverge de forma crítica — **165/165 tools registadas estão activas**, incluindo filesystem, base de dados directa, WP-CLI, PHP snippets e gestão de plugins/utilizadores (ver achado detalhado em §8.2). Qualquer activação deve ser: (1) ao nível de uma única tool, nunca em bloco; (2) reversível e verificada de imediato (§4, com o método correcto de §7/§8.3 — nunca por contagem de arrays); (3) documentada aqui ou em nota do site, para não perder rasto de porque é que uma excepção existe. --- ## 6. Módulos — os 9 blocos que a página *Modules* liga/desliga Módulo ≠ ferramenta individual: é um interruptor de alto nível (`emcp_tools_active_modules`) que liga/desliga uma funcionalidade inteira (CPT, hooks de front-end, tab de admin e, nalguns casos, o próprio grupo de MCP tools). Cada módulo estende `EMCP_Tools_Module` (`includes/modules/class-module.php`) e expõe `tier()` (`free`/`pro`), `default_active()` (semeado uma única vez, via `EMCP_Tools_Modules_Registry::apply_defaults()`, marcador `emcp_tools_modules_seeded`) e `is_available()` (gate adicional, ex. licença Pro). **Um módulo `pro` pode estar "activo" no option e mesmo assim não fazer nada** — `is_available()` exige `emcp_tools_fs()->can_use_premium_code()`; nos 3 sites verificados (build Free, sem licença), `templates` e `agent-skills` estão semeados activos mas `is_available()` devolve `false`, logo não correm. | Módulo (`id`) | Tier | Activo por omissão | Disponível nesta sessão (build Free, sem licença) | O que liga quando activo+disponível | |---|---|---|---|---| | `prompts` | free | sim | sim | Mostra a tab de admin **Prompts** (blueprints de prompts prontos a usar para construir páginas; amostras grátis + biblioteca Pro). Não regista MCP tools — é só UI de admin (`register()` vazio). | | `brand-kits` | free | sim | sim | Mostra a tab **Brand Kits** (kits de cor+tipografia de um clique; kits grátis + biblioteca Pro). Também só UI de admin, sem MCP tools próprias. | | `cloud` | free | sim | sim | Arranca `EMCP_Tools_Cloud_Connect::init()` (fluxo OAuth de ligação à conta EMCP Cloud) e — só quando `EMCP_Tools_Cloud::is_connected()` for verdadeiro — regista as 7 ferramentas `cloud-*` (§5 tabela de categorias). Nos 3 sites: módulo activo mas nenhum ligado a uma conta Cloud, logo 0 tools `cloud-*` registadas. | | `templates` | **pro** | sim (seeded) | **não** (sem licença) | Quando disponível: mostra a tab **Templates** (templates de página Elementor prontos a aplicar). Metadata-only (`register()` vazio) — vive na árvore free só como descrição, a lógica Pro real está fora deste build. | | `themer` | free | sim | sim | O módulo mais substancial do bundle free: regista o CPT `EMCP_Tools_Themer_CPT`, o índice de condições, o controlador de render no front-end, a metabox de admin, blocos Gutenberg dinâmicos, widgets Elementor dinâmicos e templates PHP (Themer PHP, sub-toggle próprio). Desligar este módulo é kill-switch total — pára o CPT, o front-end takeover, a tab, **e** o registrador de abilities omite as 8 tools `*-theme-template*`/`*-template*` (§5). | | `redirects` | free | sim | sim | Instala a tabela do Redirect Manager, liga o handler de 301/302 no front-end, o scan de links partidos e as sugestões de redirect ao apagar/renomear uma página. Desligar pára tudo isto **e** remove as 5 tools `*-redirect*`/`find-broken-links` do registo. | | `agent-skills` | **pro** | sim (seeded) | **não** (sem licença) | Quando disponível: expõe `list-skills`/`get-skill` aos agentes ligados + injecta um catálogo `## Skills` (~900 tokens) no contexto de discovery. Metadata-only na árvore free. | | `image-optimization` | free | **não** (opt-in) | depende (precisa de editor de imagem com suporte WebP) | Comprime uploads (`wp_generate_attachment_metadata`) e gera/serve WebP (rewriter de URL); regista `resize-media`. 5 opções próprias: `compress`, `webp`, `webp_serve`, `quality` (0-100, default 60), `max_dimension`, `keep_originals`. Um optimizador retomável trata a biblioteca já existente a partir do card do módulo. | | `svg-support` | free | **não** (opt-in — superfície de segurança) | depende (precisa da lib `enshrined/svg-sanitize` bundled) | Permite upload de SVG (mime `svg`) para quem tiver a capability necessária, corrige `wp_check_filetype_and_ext()` para REST/sideload, e sanitiza cada SVG (fail-closed: um SVG que não possa ser limpo é rejeitado). Uma opção: `admin_only` (restringe upload a administradores). Nota: se o Elementor já permitir SVG (como normalmente acontece), este módulo é redundante excepto pela sanitização extra. | --- ## 7. Inventário verificado das ~162 abilities registadas (build Free, Elementor activo) — por categoria **Correcção a uma hipótese de partida:** subtrair ingenuamente 162 − 131 = "~31 activas" está **errado** — 84 dos 131 slugs em `emcp_tools_disabled_tools` são nomes de ferramentas **Pro** (WooCommerce, 6 SEO plugins além do Slim SEO, 5 dos 6 plugins de formulários, frameworks de tema além do Astra, memória do agente, Themer PHP, SEO/A11y automático, Widget/Block Builder — ver lista completa em §2) que **nunca chegam a registar-se** neste build Free — estão pré-semeados no option para o dia em que o site tiver licença Pro, mas `wp_get_abilities()` não os devolve porque as classes Pro não existem na árvore free. Contá-los como "desligados" infla artificialmente a contagem de fechadas e desinfla a de activas. **Método de verificação usada** (a única fonte fiável — ver aviso sobre `wp mcp-adapter list` no topo deste documento): ```bash wp eval ' $abilities = wp_get_abilities(); $emcp = array_filter(array_keys($abilities), function($k){ return strpos($k, "emcp-tools/") === 0; }); $disabled = get_option("emcp_tools_disabled_tools", []); $active = array_diff($emcp, $disabled); echo "registadas=" . count($emcp) . " desligadas(option)=" . count($disabled) . " activas=" . count($active) . "\n"; ' --path=$PATH --allow-root ``` **Resultado real, verificado ao vivo (16-08-2026):** | Site | Abilities `emcp-tools/*` registadas | Entradas em `emcp_tools_disabled_tools` | … das quais correspondem a uma ability REALMENTE registada | Activas (registada E não desligada) | |---|---|---|---|---| | `emanuelalmeida.pt` | 162 | 131 | 47 | **115** | | `starter.descomplicar.pt` | 160 | 131 | ~46 | **114** | | `descomplicar.pt` | 165 | 78 | **0** | **165** (ver achado crítico, §8) | A tabela abaixo usa `emanuelalmeida.pt` (162 registadas) como referência — é o site com a config mais representativa da postura default-deny pretendida pelo plugin (`starter` é quase idêntico; `descomplicar.pt` diverge de forma crítica, ver §8). Por categoria: ferramentas **ACTIVAS** (chamáveis agora), **DESLIGADAS** (bloqueadas pelo deny-list, mas registadas — activáveis via §4), e, quando aplicável, ferramentas do grupo que **nem chegam a registar-se** neste site (dependem de um plugin/tema de terceiros ausente). | Categoria | O que faz | Activas | Desligadas | |---|---|---|---| | **Compact tool mode (dispatcher)** | `list-tools`/`get-tool-schema`/`call-tool` — o modo de despacho compacto (3 ferramentas que expõem indirectamente todas as outras); descrito na arquitectura do servidor MCP no início deste documento. | `call-tool`, `get-tool-schema`, `list-tools` | — | | **Elementor — descoberta/query (P0)** | Fundação de leitura: percorre containers/elementos, resolve schemas de widget/container, lista páginas Elementor e templates disponíveis. | `find-element`, `get-container-schema`, `get-element-settings`, `get-page-structure`, `get-widget-schema`, `list-pages`, `list-templates`, `list-widgets` | — | | **Elementor — páginas (P1 CRUD)** | Cria/edita páginas Elementor, exporta o conteúdo de uma página, importa um template para uma página, e ajusta page settings (SEO básico, layout). | `create-page`, `delete-page-content`, `export-page`, `import-template`, `update-page-settings` | — | | **Elementor — layout/container** | Manipula a árvore de containers/flexbox/elementos: adicionar, mover, duplicar, remover, reordenar, actualizar propriedades, edição em lote (`batch-update`). | `add-container`, `batch-update`, `duplicate-element`, `move-element`, `remove-element`, `reorder-elements`, `set-element-label`, `update-container`, `update-element` | — | | **Elementor — widgets clássicos (catálogo)** | Coloca widgets Elementor gratuitos no layout e actualiza as suas definições; `add-pro-widget` (Elementor Pro) fica desligado por omissão. | `add-free-widget`, `update-widget` | `add-pro-widget` | | **Elementor — templates/popups/dynamic tags** | `apply-template`/`save-as-template` activos (aplicar/gravar templates de página); criação de theme templates, popups, dynamic tags e condições de exibição ficam desligados (mutação estrutural). | `apply-template`, `save-as-template` | `create-elementor-theme-template`, `create-popup`, `list-dynamic-tags`, `set-dynamic-tag`, `set-elementor-template-conditions`, `set-popup-settings` | | **Elementor — configurações globais (cores/tipografia)** | Lê e actualiza o kit global (cores e tipografia do site) — `get-global-settings` está agrupado na categoria Query (P0) mas afecta as mesmas definições. | `get-global-settings`, `update-global-colors`, `update-global-typography` | — | | **Elementor — build-page composto** | `build-page` — ferramenta de alto nível que constrói uma página inteira a partir de uma especificação estruturada (composição de containers+widgets num único pedido). | `build-page` | — | | **Elementor — SVG icons** | `upload-svg-icon` — envia um SVG para a biblioteca de ícones Elementor (sanitizado). | `upload-svg-icon` | — | | **Elementor — custom code (CSS/JS/snippets)** | `add-custom-js` activo (injecta JS Elementor); `add-custom-css`, `add-code-snippet` e `list-code-snippets` ficam desligados (execução/injecção persistente). | `add-custom-js` | `add-code-snippet`, `add-custom-css`, `list-code-snippets` | | **Elementor 4.0+ — atomic widgets** | Widgets atómicos do Elementor 4.0+ (heading, paragraph, button, image, svg, youtube, video, divider) e o genérico `add-atomic-widget`/`update-atomic-widget` — todos activos. | `add-atomic-button`, `add-atomic-divider`, `add-atomic-heading`, `add-atomic-image`, `add-atomic-paragraph`, `add-atomic-svg`, `add-atomic-video`, `add-atomic-widget`, `add-atomic-youtube`, `update-atomic-widget` | — | | **Elementor 4.0+ — atomic layout (flexbox/div-block)** | `add-div-block`, `add-flexbox` (containers atómicos) e `detect-elementor-version` (detecta se o site suporta Elementor 4.0+ atomic). | `add-div-block`, `add-flexbox`, `detect-elementor-version` | — | | **Elementor 4.0+ — global classes (leitura)** | `list-global-classes` — lista as classes CSS globais do Elementor 4.0+. | `list-global-classes` | — | | **Elementor 4.0+ — global classes (escrita)** | Criar/actualizar/eliminar/reordenar classes globais — todas desligadas por omissão (mutação de CSS partilhado entre todas as páginas). | — | `create-global-class`, `delete-global-class`, `reorder-global-classes`, `update-global-class` | | **Gutenberg (blocos nativos)** | Conjunto completo de leitura+escrita para blocos Gutenberg nativos (descoberta de blocos/patterns, inserir pattern, CRUD de blocos numa página) — todo activo por omissão (Gutenberg é considerado superfície de baixo risco face ao Elementor neste bundle). | `add-block`, `duplicate-block`, `get-block-schema`, `get-post-blocks`, `insert-pattern`, `list-blocks`, `list-patterns`, `move-block`, `remove-block`, `update-block` | — | | **Page Snapshot** | `get-page-snapshot` — digest normalizado de uma página (base para outras ferramentas de leitura); sempre activo. | `get-page-snapshot` | — | | **Transações/rollback (change ledger)** | `get-change`/`list-changes`/`rollback-change` — ledger de alterações feitas via MCP com possibilidade de reverter; sempre activo (é a rede de segurança das escritas). | `get-change`, `list-changes`, `rollback-change` | — | | **Pesquisa de conteúdo (índice léxico)** | `search-content`/`reindex-search` — índice léxico sobre páginas/templates/widgets/globals. | `reindex-search`, `search-content` | — | | **Content mirror (export/restore git-friendly)** | `export-content`/`list-content-exports`/`restore-content` — exporta conteúdo de página como ficheiros rastreáveis em git e permite restaurar. | `export-content`, `list-content-exports`, `restore-content` | — | | **WordPress — conteúdo (posts/CPT/taxonomias)** | CRUD completo de posts/CPT (criar, ler, listar, actualizar, eliminar), listar post types/taxonomias, definir termos — activo por omissão. | `create-post`, `delete-post`, `get-post`, `list-post-types`, `list-posts`, `list-taxonomies`, `set-post-terms`, `update-post` | — | | **WordPress — media library** | Ler/listar/actualizar/carregar media activos; `delete-media` desligado (eliminação permanente). | `get-media`, `list-media`, `update-media`, `upload-media` | `delete-media` | | **WordPress — resize de imagem** | `resize-media` só regista quando o módulo Image Optimization está activo (reutiliza o motor de compressão/WebP) — não registado nesta sessão porque o módulo está desligado por omissão. | *(nenhuma — grupo não registado: `resize-media`)* | *(n/a — ver nota)* | | **WordPress — settings** | `get-settings`/`update-settings` — leitura e escrita de um conjunto curado de definições do site (não é `wp_options` livre). | `get-settings`, `update-settings` | — | | **WordPress — plugins** | Listar/pesquisar plugins fica activo; instalar/activar/desactivar/actualizar/eliminar plugin ficam desligados (superfície de maior risco depois de execução de código). | `list-plugins`, `search-plugins` | `activate-plugin`, `deactivate-plugin`, `delete-plugin`, `install-plugin`, `update-plugin` | | **WordPress — temas (gestão)** | Listar/pesquisar temas activo; instalar/mudar/actualizar/eliminar tema desligados. | `list-themes`, `search-themes` | `delete-theme`, `install-theme`, `switch-theme`, `update-theme` | | **WordPress — utilizadores** | Ler/listar utilizadores activo; criar/actualizar utilizador desligados (escalada de privilégios). | `get-user`, `list-users` | `create-user`, `update-user` | | **WordPress — menus de navegação** | `menu-read`/`menu-write` — ambos activos por omissão (menus, itens, theme locations, render). | `menu-read`, `menu-write` | — | | **Tema activo (framework-agnostic)** | `theme-read` activo (`get-theme-context`, `get-mods` — identidade do tema, theme_mods); `theme-write` desligado (`set-mods`, `create-child-theme`). | `theme-read` | `theme-write` | | **Astra (tema)** | Só regista quando o Astra é o tema activo. `astra-read` activo (definições Astra Customizer); `astra-write` desligado. | `astra-read` | `astra-write` | | **ACF (Advanced Custom Fields)** | Dispatcher `acf-read`/`acf-write` + 15 operações (field groups, post types, taxonomias, options pages) — só regista se ACF (free ou Pro) estiver activo no site; não registado nesta sessão (ACF ausente). | *(nenhuma — grupo não registado: `acf-read`, `acf-write`, `create-acf-field-group`, `create-acf-post-type`, `create-acf-taxonomy`, `get-acf-field-group`, `get-acf-fields`, `get-acf-post-type`, `get-acf-taxonomy`, `list-acf-field-groups`, `list-acf-options-pages`, `list-acf-post-types`, `list-acf-taxonomies`, `update-acf-field-group`, `update-acf-fields`, `update-acf-post-type`, `update-acf-taxonomy`)* | *(n/a — ver nota)* | | **Meta Box** | Dispatcher `metabox-read`/`metabox-write` — só regista se o Meta Box estiver activo; não registado nesta sessão. | *(nenhuma — grupo não registado: `metabox-read`, `metabox-write`)* | *(n/a — ver nota)* | | **Themer (free) — templates de header/footer/single/archive** | CPT + condições de exibição para header/footer/single/archive/search/404 com qualquer page builder. Todo o grupo activo por omissão: listar/obter/criar/actualizar/eliminar template, listar alvos de condição, definir condições, resolver qual template se aplica a um contexto. | `create-theme-template`, `delete-theme-template`, `get-theme-template`, `list-condition-targets`, `list-theme-templates`, `resolve-template`, `set-template-conditions`, `update-theme-template` | — | | **Themer PHP templates (toggle próprio)** | Sub-funcionalidade do Themer para templates PHP brutos, atrás de um toggle independente (`EMCP_Tools_Themer_PHP::enabled()`) — não activo nesta sessão, por isso as 5 tools não estão sequer registadas. | *(nenhuma — grupo não registado: `create-theme-php-template`, `delete-theme-php-template`, `get-theme-php-template`, `list-theme-php-templates`, `update-theme-php-template`)* | *(n/a — ver nota)* | | **Redirect Manager** | `list-redirects`/`find-broken-links` activos (leitura + scan de links partidos); `create/update/delete-redirect` desligados (mutação de routing com impacto directo em SEO). | `find-broken-links`, `list-redirects` | `create-redirect`, `delete-redirect`, `update-redirect` | | **Stock images (busca + sideload + widget)** | `search-images`/`sideload-image`/`add-stock-image` — pesquisa num provider de stock, descarrega para a Media Library, e coloca como widget de imagem; todo activo (não muta nada fora da Media Library). | `add-stock-image`, `search-images`, `sideload-image` | — | | **Performance Analyzer** | `analyze-performance` — auditoria de performance só de leitura. | `analyze-performance` | — | | **Security & Malware Scanner** | `scan-security` — scanner de malware/postura de segurança, só leitura, sempre activo. | `scan-security` | — | | **Filesystem** | `list-directory`/`read-file`/`search-files` activos; `write-file`/`edit-file`/`delete-file` desligados (escrita directa no filesystem). | `list-directory`, `read-file`, `search-files` | `delete-file`, `edit-file`, `write-file` | | **Base de dados directa** | `describe-table`/`list-tables`/`query` (SELECT) activos; `insert-row`/`update-rows`/`delete-rows` desligados (escrita SQL directa). | `describe-table`, `list-tables`, `query` | `delete-rows`, `insert-row`, `update-rows` | | **WP-CLI (run + jobs)** | `run-wp-cli`/`dispatch-wp-cli`/`get-wp-cli-job`/`list-wp-cli-jobs` — todo o grupo desligado por omissão (execução arbitrária de comandos WP-CLI). | — | `dispatch-wp-cli`, `get-wp-cli-job`, `list-wp-cli-jobs`, `run-wp-cli` | | **PHP Snippets (Sandbox)** | `validate/create/update/get/list/delete-php-snippet` — todo o grupo desligado por omissão (execução de PHP arbitrário persistido). | — | `create-php-snippet`, `delete-php-snippet`, `get-php-snippet`, `list-php-snippets`, `update-php-snippet`, `validate-php-snippet` | | **Sandbox — export/import cloud (bundle)** | `export-sandbox-artifact`/`import-sandbox-artifact` — exporta/importa um artefacto de sandbox (bloco/widget/snippet) como bundle; ambos activos (o bundle em si não executa nada, é só serialização). | `export-sandbox-artifact`, `import-sandbox-artifact` | — | | **EMCP Cloud (sync remoto)** | `cloud-status`/`cloud-backup`/`cloud-list`/`cloud-pull`/`cloud-config-sync`/`cloud-marketplace-list`/`cloud-marketplace-install` — só registam quando o módulo Cloud está activo E o site está ligado a uma conta EMCP Cloud; não registadas nesta sessão porque nenhum dos 3 sites está ligado a uma conta Cloud (ver módulo `cloud`, §6). | *(nenhuma — grupo não registado: `cloud-backup`, `cloud-config-sync`, `cloud-list`, `cloud-marketplace-install`, `cloud-marketplace-list`, `cloud-pull`, `cloud-status`)* | *(n/a — ver nota)* | --- ## 8. Mecanismo do deny-list incremental — e um achado crítico ao vivo em `descomplicar.pt` ### 8.1 Como o plugin decide o que fecha por omissão O deny-list não é estático: `includes/admin/class-admin.php`, método `maybe_apply_default_disabled_tools()` (chamado no boot do admin), aplica **34 blocos de migração incremental** (`DEFAULTS_VERSION = 34`, contador `emcp_tools_defaults_applied`), cada um introduzido numa versão do plugin para fechar uma nova categoria de tool assim que ela é lançada — ex.: v6 fecha escrita de plugins/temas, v9 fecha filesystem, v10 fecha base de dados, v18 fecha WP-CLI, v28 fecha escrita de global classes, v33-34 fecham as tools de migração/sync. A função só adiciona slugs (nunca remove uma escolha do utilizador) e só corre os blocos cujo número é maior que o `$applied` já gravado: ```php if ( $applied < N ) { $add = array_merge( $add, /* slugs da versão N */ ); } // ... update_option( self::OPTION_DISABLED_TOOLS, array_values( array_unique( array_merge( $existing, $add ) ) ) ); update_option( self::OPTION_DEFAULTS_APPLIED, (string) self::DEFAULTS_VERSION ); ``` Consequência arquitectural importante: **um site cujo `emcp_tools_defaults_applied` já esteja em 34 nunca mais recebe fecho automático de tools novas**, mesmo que o plugin lance uma v35 no futuro — nesse cenário a nova categoria nasceria **activa** nesse site até alguém a fechar à mão. E mais relevante ainda para o presente: se o valor de `emcp_tools_disabled_tools` for alterado manualmente (via `wp eval`/`array_diff`, como o método §4 deste documento ensina, ou por qualquer outro caminho) **removendo** entradas em massa, o contador de versão não volta atrás — o site fica com um deny-list mais pequeno do que o "de fábrica" para essa versão, sem qualquer aviso. ### 8.2 🔴 Achado crítico verificado ao vivo (16-08-2026) — `descomplicar.pt` Ao aplicar o método de verificação do §7 aos 3 sites, `descomplicar.pt` destacou-se: `active=165` = `registered=165` — **zero ferramentas efectivamente fechadas**, apesar de `emcp_tools_disabled_tools` ter 78 entradas. Inspeccionando essas 78 entradas (`wp option get emcp_tools_disabled_tools --format=json --path=/home/ealmeida/public_html`): são **exclusivamente** slugs de integrações de terceiros que não estão instaladas neste site (WooCommerce, Essential/Premium Addons, UAE, os 6 plugins de formulários, os 7 plugins de SEO, Astra/Spectra/Kadence/GeneratePress/Blocksy, `recall`/`remember`/`save-session-summary`, os 4 achados SEO/A11y automáticos, e os 16 slugs do Widget/Block Builder Pro) — **nenhuma** das categorias nativas mais perigosas (filesystem, base de dados, WP-CLI, PHP snippets, gestão de plugins/temas, gestão de utilizadores) aparece nessa lista. Confirmado directamente, ferramenta a ferramenta: | Tool | Registada em `descomplicar.pt`? | Está no deny-list? | **Chamável agora via MCP?** | |---|---|---|---| | `emcp-tools/write-file` | sim | não | **🔴 SIM** | | `emcp-tools/edit-file` | sim | não | **🔴 SIM** | | `emcp-tools/delete-file` | sim | não | **🔴 SIM** | | `emcp-tools/run-wp-cli` | sim | não | **🔴 SIM** | | `emcp-tools/dispatch-wp-cli` | sim | não | **🔴 SIM** | | `emcp-tools/insert-row` | sim | não | **🔴 SIM** | | `emcp-tools/update-rows` | sim | não | **🔴 SIM** | | `emcp-tools/delete-rows` | sim | não | **🔴 SIM** | | `emcp-tools/create-php-snippet` | sim | não | **🔴 SIM** | | `emcp-tools/update-php-snippet` | sim | não | **🔴 SIM** | | `emcp-tools/install-plugin` | sim | não | **🔴 SIM** | | `emcp-tools/activate-plugin` | sim | não | **🔴 SIM** | | `emcp-tools/create-user` | sim | não | **🔴 SIM** | | `emcp-tools/update-user` | sim | não | **🔴 SIM** | Todas as 34 versões de migração já correram (`emcp_tools_defaults_applied = 34`, igual aos outros 2 sites), o que exclui "instalação nunca migrada" como explicação — a hipótese mais provável, dado o padrão exacto de remoção (sobrevivem só integrações de terceiros; desaparecem só as categorias nativas de risco máximo/alto de §5), é que o array foi reescrito manualmente nalgum momento — por exemplo com uma variante do próprio comando de §4 aplicada de forma demasiado ampla (ex.: `array_diff` contra uma lista de slugs "de terceiros" em vez de um único slug) — sem que a subtracção tenha sido revertida depois. **Isto não foi corrigido nesta tarefa** (o âmbito desta expansão de skill é só leitura — ver constraints). **Requer decisão humana urgente**: ou (a) confirmar que a abertura destas 14+ tools nativas em `descomplicar.pt` foi intencional e está devidamente supervisionada, ou (b) alinhar `descomplicar.pt` com o deny-list dos outros 2 sites, repondo pelo menos as categorias 🔴🔴 de §5 (filesystem, base de dados, WP-CLI, snippets PHP) com o padrão do método §4, aplicado entrada a entrada. ### 8.3 Regra de verificação a aplicar sempre, daqui em diante **Nunca confiar na contagem de entradas em `emcp_tools_disabled_tools` isoladamente** — nem para avaliar "quão fechado" está um site (v. `descomplicar.pt`, 78 entradas e 0 tools realmente fechadas), nem para o inverso (a lista de 131 em `emanuelalmeida.pt`/`starter` também tem 84 entradas "mortas" que nunca vão fechar nada neste build). O único método correcto é o `wp eval` + `wp_get_abilities()` do §7, comparado slug a slug contra `emcp_tools_disabled_tools` — nunca o tamanho dos arrays, nunca a coluna `Tools` de `wp mcp-adapter list` (ver aviso na arquitectura, que mostra `3` para `descomplicar.pt` quando o valor real é 165). --- ## Fonte `Hub/04-Stack/02.04-Sistemas/71.Seguranca/CONFIG-Plugins-Referencia.md` §10 (EMCP Tools, mapeado 16-08-2026) + `BUNDLE-Excelencia-WP.md` (contexto do bundle e registo dos 3 sites) + verificação SSH ao vivo nesta tarefa (`wp option list/get`, `wp plugin get`, `wp mcp-adapter list`, `~/.omp/agent/mcp.json`) em `descomplicar.pt`, `starter.descomplicar.pt` e `emanuelalmeida.pt` **+ leitura completa do código-fonte do plugin (build Free `emcp-tools` 3.12.1) em `emanuelalmeida.pt`**: todos os ficheiros em `includes/abilities/` (46 classes de abilities + integrações forms/seo), `includes/modules/` (9 módulos + sub-módulos Image Optimization e SVG Support), `includes/admin/class-admin.php` (catálogo de tools, `maybe_apply_default_disabled_tools()`, as 34 versões do deny-list incremental), `includes/modules/class-modules-registry.php` + `class-module.php` (ciclo de vida dos módulos), `includes/class-migration.php` (migração `elementor-mcp` → `emcp-tools`), `emcp-tools.php` (bootstrap, guarda Free⇄Pro) — e verificação ao vivo via `wp eval` + `wp_get_abilities()` filtrado a `emcp-tools/*` cruzado slug-a-slug contra `emcp_tools_disabled_tools`, nos 3 sites (método completo em §7).