Files
claude-plugins/wordpress/skills/mcp-insert-headers-footers/SKILL.md
T

135 lines
8.5 KiB
Markdown

---
name: mcp-insert-headers-footers
description: MCP dedicado multi-site (node stdio, ligação `insert-headers-footers` em ~/.omp/agent/mcp.json) para ler e escrever os 3 blocos globais de código (header/body/footer) do plugin Insert Headers and Footers (agora rebrandeado "WPCode Lite", slug `insert-headers-and-footers`) via WP-CLI/SSH. ⚠️ Superfície de risco real — estes 3 blocos injectam HTML/JS/CSS arbitrário em TODAS as páginas do site (produção incluída); uma escrita mal formada pode partir o site inteiro ou abrir um vector XSS. Site é um parâmetro em cada tool. Usar quando "insert headers and footers", "wpcode lite", "header footer mcp", "injectar script no site todo", "código global wordpress", "meta pixel site inteiro", "gtm noscript global", "ihaf mcp".
layer: wiki
---
# /mcp-insert-headers-footers — MCP dedicado multi-site ao Insert Headers and Footers (WPCode Lite)
Projecto em `/media/ealmeida/Dados/Dev/mcp-insert-headers-footers/` (TypeScript, SDK MCP oficial,
stdio). Sem código PHP novo no WordPress — cada tool executa `wp eval` com um snippet PHP estático
(nunca dados do utilizador interpolados no comando) via `ssh server` sobre o WP-CLI já instalado.
**`site` é um parâmetro em cada tool**, não uma ligação fixa — o plugin está confirmado `active`
(WPCode Lite 2.3.8) nos 8 sites reais do bundle.
## ⚠️ Superfície de risco
Este plugin injecta os 3 blocos em **todas as páginas de todo o site**, via `wp_head`,
`wp_body_open` e `wp_footer`. Não há "sandbox" nem preview automático — o que estiver gravado sai
directamente no HTML servido a todos os visitantes assim que a cache de página for purgada (ou
imediatamente, se não houver cache activa). Riscos concretos:
- **Quebra visual/funcional site-wide** — uma tag HTML não fechada, um `<script>` malformado, ou
CSS inline quebrado no bloco `body` (que corre logo a seguir a `<body>`) pode desalinhar ou
esconder o site inteiro para todos os visitantes.
- **Vector XSS real** — qualquer conteúdo aqui corre sem sanitização nenhuma no frontend
(`echo wp_unslash($code)` directo, sem `esc_html`/`wp_kses`). Nunca aceitar conteúdo de origem
não confiável para estes blocos.
- **Perda de conteúdo** — a escrita é **substituição total** do bloco, não merge. Ler sempre
primeiro com `ihaf_get_blocks`; se o bloco já tiver código em produção (ex. Meta Pixel + GTM
noscript em `descomplicar`), o conteúdo completo desejado (antigo + novo) tem de ser fornecido
explicitamente.
A tool de escrita (`ihaf_set_block`) está marcada `destructiveHint:true` e a sua descrição já
inclui este aviso — mas o agente que a chama é responsável por: (1) ler o bloco antes de escrever,
(2) testar em `starter` (staging) sempre que possível antes de `descomplicar` (produção), (3)
purgar cache de página e verificar o site visualmente depois de escrever.
## Sites conhecidos
Chamar `ihaf_list_sites` para a lista actual com path e nota de estado. Todos os 8 sites do bundle
verificados `active` (WPCode Lite 2.3.8) ao vivo em 19-08-2026: `descomplicar` (produção — bloco
`body` **já em uso**: Meta Pixel + GTM noscript, não sobrescrever sem ler primeiro),
`emanuelalmeida`, `starter` (os 3 blocos vazios — site preferencial para testes), `ccv`, `care`,
`ecommerce-demo`, `e-commerce`, `ecommerce`. Também aceita um path absoluto directamente
(`/home/ealmeida/<site>`) para sites fora desta lista.
## Tools (4)
| Tool | Read-only | Uso |
|---|---|---|
| `ihaf_list_sites` | sim | Aliases conhecidos, path WP, nota de estado |
| `ihaf_get_plugin_status` | sim | Instalado/activo + versão — chamar antes se não tiver a certeza de que o plugin está activo nesse site |
| `ihaf_get_blocks` | sim | Os 3 blocos (`header`/`body`/`footer`) no texto lógico exacto que é servido ao frontend, mais `headersFootersMode` informativo. Chamar SEMPRE antes de `ihaf_set_block` |
| `ihaf_set_block` | não — `destructiveHint:true` | Substitui por inteiro UM dos 3 blocos (`block`: `header`\|`body`\|`footer`). `content` é o texto final completo, sem escaping adicional (o MCP trata do `wp_slash()` internamente) |
Todas as tools exigem `site` como primeiro parâmetro.
## Mapeamento de dados (a única documentação destes 3 blocos)
Não há skill de conhecimento separada para este plugin — este é o mapeamento completo.
Os 3 blocos vivem em **3 `wp_options` planas** (não uma option única, não uma tabela custom):
| Bloco | Option | Hook de output | Posição no HTML |
|---|---|---|---|
| `header` | `ihaf_insert_header` | `wp_head` | Dentro de `<head>` |
| `body` | `ihaf_insert_body` | `wp_body_open` (prioridade 1) | Logo a seguir à abertura de `<body>` |
| `footer` | `ihaf_insert_footer` | `wp_footer` | Antes de `</body>` |
Código fonte confirmado (`includes/global-output.php`): cada option é lida com `get_option()` cru
e impressa com `echo wp_unslash( $code )`, sem qualquer sanitização. Ficam vazias por omissão
(`get_option()` devolve `''`), o output é simplesmente ignorado se `empty( trim( $code ) )`.
Também são ignoradas em `is_admin()`, `is_feed()`, `is_robots()`, `is_trackback()`.
### ⚠️ Gotcha crítico de formato — slashing, não JSON
Diferente do gotcha do WP Fastest Cache (JSON string vs option tipada), aqui o problema é
**slashing do WordPress**, não JSON. O ecrã de admin do plugin grava directamente a partir de
`$_REQUEST` (`includes/admin/pages/class-wpcode-admin-page-headers-footers.php`):
```php
update_option( 'ihaf_insert_header', $_REQUEST['ihaf_insert_header'] );
```
Como o WordPress aplica `addslashes()` globalmente a todos os superglobais logo no arranque
(`wp_magic_quotes()`), o valor **gravado em `wp_options` fica sempre "slashed"** — aspas e
backslashes escapados. No frontend, o output faz sempre `wp_unslash( $code )` antes de imprimir.
Ou seja: **a option em repouso na base de dados nunca é o texto lógico da página** — é sempre esse
texto com `addslashes()` aplicado.
Confirmado ao vivo em produção (19-08-2026), bloco `body`:
```
BD (raw): style=\"display:none\"
Frontend: style="display:none"
```
Por isso este MCP **nunca** usa `wp option get/update` em bruto sobre estas 3 keys. Usa sempre
`wp eval` com as funções nativas `wp_unslash()` (leitura) e `wp_slash()` (escrita) — nunca
reimplementa `addslashes`/`stripslashes` em TypeScript. `ihaf_get_blocks` devolve sempre o texto
lógico (pós-`wp_unslash`, o que realmente sai no HTML); `ihaf_set_block` recebe o texto lógico
desejado e aplica `wp_slash()` do lado do servidor antes de `update_option()`. Sem este passo,
qualquer backslash literal no conteúdo (regex JS, `\n` em atributos, etc.) seria corrompido no
próximo `wp_unslash()` do frontend.
Qualquer MCP futuro sobre uma option que seja alimentada a partir de `$_REQUEST`/formulário admin
deve verificar primeiro se o plugin em causa depende deste padrão de slashing implícito do
WordPress antes de escolher `wp option get/update` directo.
## Segurança
Allowlist fixa das 3 option keys (`src/wpcli.ts`, `BLOCK_OPTION_KEYS`) — nunca `wp option update`
genérico sobre uma chave arbitrária. Conteúdo sempre via base64 em stdin, nunca interpolado no
comando SSH remoto; o único dado interpolado directamente no snippet PHP é o nome da option, que
vem de uma allowlist fixa de 3 valores (não de input do utilizador), tal como o padrão já usado em
`mcp-wpfc`. A escrita também invalida a page cache do próprio plugin quando disponível
(`wpcode_clear_all_plugins_page_cache('global')`), mas **não** purga WP Fastest Cache/Cloudflare —
isso é responsabilidade de quem chama a tool (ver `mcp-wpfc`/`mcp-cloudflare-app`).
## Verificação
Construído e testado ponta-a-ponta 19-08-2026: `ihaf_get_plugin_status` e `ihaf_get_blocks`
confirmados contra produção (`descomplicar`) — bloco `body` com Meta Pixel + GTM noscript lido
correctamente já unslashed, idêntico ao confirmado por leitura SSH directa fora do MCP
(`wp_unslash(get_option(...))`). Write round-trip testado em `starter` (staging, blocos vazios):
escrita de conteúdo com aspas e backslash duplo via `ihaf_set_block`, releitura via
`ihaf_get_blocks` confirmou o texto lógico exacto preservado (aspas e backslashes intactos),
depois revertido para `""` e confirmado por leitura SSH directa (`get_option()` == `""`) e por
`ihaf_get_blocks` limpo, sem alterar o estado original do site.
## Skills relacionadas
- `mcp-wpfc` — purga de page cache local, complementar depois de alterar estes blocos.
- `wp-cli` — padrões gerais de WP-CLI multi-site usados como base deste MCP.