8.5 KiB
name, description, layer
| name | description | layer |
|---|---|---|
| mcp-insert-headers-footers | 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". | 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 blocobody(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, semesc_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 emdescomplicar), 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):
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.