Files
claude-plugins/wordpress/skills/emcp-tools/SKILL.md
T
Claude Code 4a55d51329 feat(wordpress): EMCP Tools workflow skills + widget catalogs + pending wordpress skills
- emcp-page-building, emcp-content-ops, emcp-site-audit: EMCP Tools MCP
  workflows verified live (atomic/legacy interplay, apply-template overwrite
  risk, create-theme-template goes live immediately, false-positive malware
  pattern in scan-security, change ledger + rollback)
- elementor-pro-widgets: 30+5 native Elementor Pro widgets (curated catalog)
- elementskit-widgets / powerpack-widgets: 42 + 97 third-party widgets,
  widgetType extracted from plugin source (not guessed by convention)
- plugin.json bumped 1.2.0 -> 1.3.0, keywords + description updated
- commits pending wordpress skills already present as untracked files
  (emcp-tools, wordfence, wp-activity-log, seguranca-descomplicar,
  webp-express, wp-fastest-cache, wp-font-perf, wp-meteor,
  wp-activity-log, redis-object-cache, app-for-cloudflare) and pending
  edits (rank-math, wp-cli, wp-content-seo-gate)
2026-08-19 03:44:00 +01:00

562 lines
43 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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/<nome-tool>`) | 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/<slug-da-tool>`.
```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).