Files
ealmeida e709130a1f docs(skills): adiciona mcp-wpforms — MCP dedicado ao WPForms + Form Abandonment
Documenta o novo MCP standalone /media/ealmeida/Dados/Dev/mcp-wpforms/ (12 tools,
descomplicar.pt + emanuelalmeida.pt): formularios, entries (listar/ler/contar/
marcar lida/estrela/spam), entries abandonadas com contexto capturado, settings
gerais e estado Akismet. Sem skill de conhecimento wpforms separada — esta skill
inclui o mapeamento completo de tabelas/options (unica documentacao).
2026-08-19 06:06:58 +01:00

11 KiB

name, description, layer
name description layer
mcp-wpforms MCP dedicado multi-site (node stdio, ligação `wpforms` em ~/.omp/agent/mcp.json) para gerir o WPForms + WPForms Form Abandonment em descomplicar.pt e emanuelalmeida.pt via WP-CLI/SQL/SSH — formulários, entries (listar/ler/contar/marcar lida/estrela/spam), entries abandonadas com contexto capturado, settings gerais (captcha, licença), estado Akismet/spam. Site é um parâmetro em cada tool. Única documentação de mapeamento de dados WPForms — não existe skill de conhecimento separada. Usar quando "wpforms mcp", "entries wpforms", "formulário abandonado", "form abandonment", "spam wpforms", "akismet wpforms", "schema formulário wpforms", "wpforms_entries", "marcar entry como lida", "settings wpforms". wiki

/mcp-wpforms — MCP dedicado multi-site ao WPForms + Form Abandonment

Projecto em /media/ealmeida/Dados/Dev/mcp-wpforms/ (TypeScript, SDK MCP oficial, stdio). Sem código PHP novo no WordPress: cada tool executa wp db query (SELECTs JSON_OBJECT/JSON_ARRAYAGG e UPDATEs de 1 coluna), wp option get ou wp post get via ssh server sobre o WP-CLI já instalado. site é um parâmetro em cada tool, não uma ligação fixa — como mcp-wpfc/mcp-wpmeteor.

Não existe skill de conhecimento "wpforms" separada (só o EMCP Tools referencia o ícone do addon Form Abandonment, nada mais). Esta skill é a única documentação do mapeamento de dados WPForms neste repositório — inclui aqui o schema das tabelas e das options que noutros MCPs (ex. wp-fastest-cache ↔ mcp-wpfc) viveria numa skill "irmã".

Âmbito

Formulários (leitura do schema), entries (listar/ler/contar, marcar lida/estrela/spam), entries abandonadas do addon Form Abandonment, settings gerais (captcha, comportamento) e licença (mascarada), estado Akismet. Não cobre: criação/edição do schema de campos de um formulário (risco de corromper o formulário — fora de âmbito por desenho), reenvio de notificações de email, tabelas de analytics/payments/tasks_meta/file_restrictions/protected_files do WPForms (não usadas por nenhuma tool), o plugin styler-for-wpforms (inactivo em emanuelalmeida.pt).

Sites conhecidos

Chamar wpforms_list_sites para a lista actual. Ao contrário de mcp-wpfc, não aceita um path arbitrário — as queries SQL directas precisam do prefixo de tabela real do site (table_prefix), que varia por instalação e não pode ser adivinhado com segurança. Só sites explicitamente verificados são aceites. Confirmado ao vivo 19-08-2026 (wp plugin list, wp config get table_prefix):

Alias Domínio Path table_prefix WPForms Form Abandonment
descomplicar descomplicar.pt (produção) /home/ealmeida/public_html wpah_ 2.0.0.4, activo, licença Elite 1.16.0, activo
emanuelalmeida emanuelalmeida.pt /home/ealmeida/emanuelalmeida.pt wpne_ 2.0.0.4, activo não instalado

Akismet não está instalado em nenhum dos dois sites (wpforms_get_spam_status devolve akismet_plugin_installed: false).

Mapeamento de dados (confirmado por leitura directa do código-fonte e da BD)

Formulários — wp_posts (post_type = wpforms)

O schema de cada formulário (campos + settings) vive em post_content como texto JSON puro (não serializado PHP) — {"fields": {...}, "settings": {...}, "id", "field_id", "search_terms", "providers", "meta"}. fields é um objecto indexado por field-id (não um array), cada entrada com pelo menos id, type, label, required. As settings interessantes: form_abandonment ("1" = activo), form_abandonment_fields ("" = só guarda se tiver email/telefone, "all" = guarda sempre), form_abandonment_duplicates ("1" = evita duplicados na mesma hora), store_spam_entries ("0"/"1"), akismet (só existe se o plugin Akismet estiver instalado — nenhum dos 2 sites tem). wpforms_get_form_schema extrai fields para uma lista simplificada (id/type/label/required/ description) e devolve settings completo tal-e-qual.

wpforms_list_forms usa JSON_EXTRACT/JSON_UNQUOTE directamente sobre post_content (sem descodificar o JSON completo) para expor form_abandonment_enabled/store_spam_entries de forma barata em massa — truque reutilizável para qualquer outra flag de settings sem custo de ler o schema completo por formulário.

Entries — tabelas custom {prefix}wpforms_entries / entry_fields / entry_meta / logs

wpforms_entries: entry_id, form_id, post_id, user_id, status, type, viewed (tinyint), starred (tinyint), fields (longtext — JSON {field_id: {name, value, id, type}}, os dados submetidos tal como o WPForms os mostra no admin), meta (longtext, não usado em produção — sempre vazio, não confundir com a tabela entry_meta), date, date_modified, ip_address, user_agent, user_uuid.

status — valores confirmados por leitura do código-fonte (não documentados centralmente pelo próprio plugin, espalhados por src/Pro/AntiSpam/SpamEntry.php e pelo addon Form Abandonment):

  • '' (string vazia) — entry normal.
  • 'abandoned' — capturada pelo addon Form Abandonment (WPFormsFormAbandonment\Plugin::process_entries).
  • 'spam' — marcada como spam (SpamEntry::ENTRY_STATUS). Desmarcar spam repõe sempre '', mesmo que a entry estivesse antes abandoned/trash — é o comportamento nativo de SpamEntry::set_as_not_spam, replicado tal-e-qual em wpforms_set_entry_spam.
  • 'trash' — enviada para o lixo.

entry_fields: uma linha por (entry, field) para pesquisa/filtragem indexada — não usada por nenhuma tool deste MCP (a coluna fields da própria entry já tem os mesmos dados, mais legível).

entry_meta: linhas {entry_id, form_id, user_id, status, type, data, date}. type observados: log (histórico tipo "Entry read."), e — só em entries abandoned — page_url, page_title, page_id, url_referer, user_id (o contexto capturado pelo Form Abandonment no momento do abandono). wpforms_list_abandoned_entries faz o pivot destes 4 tipos para campos directos na resposta.

logs ({prefix}wpforms_logs): log administrativo do plugin (título/mensagem/tipo), não exposto por nenhuma tool — fora do âmbito pedido.

Settings / licença — wp_options

wpforms_settings — array PHP nativo (a:20:{...}), seguro usar --format=json em leitura (confirmado por wp option get wpforms_settings --format=json, sem dupla codificação). Contém captcha-provider (recaptcha/hcaptcha/turnstile/vazio), chaves reCAPTCHA/hCaptcha/Turnstile, modern-markup, etc. wpforms_license — também array nativo: key, type (elite em descomplicar.pt), is_expired. wpforms_get_settings mascara sempre a chave de licença (só os últimos 4 caracteres visíveis), nunca a devolve em claro.

Tools (12)

Tool Read-only Uso
wpforms_list_sites sim Aliases conhecidos, path, table_prefix, se tem Form Abandonment
wpforms_list_forms sim Todos os formulários (qualquer post_status): título, datas, form_abandonment_enabled, store_spam_entries, entry_count
wpforms_get_form_schema sim Schema completo de 1 formulário: campos simplificados + settings completo do post_content
wpforms_list_entries sim Entries com filtros form_id/status (normal/abandoned/spam/trash)/date_from/date_to, paginado — resumo sem fields/meta
wpforms_get_entry sim 1 entry completa: dados + fields submetidos + toda a entry_meta (log, contexto de abandono, razão de spam)
wpforms_count_entries sim Contagem por estado (total/normal/abandoned/spam/trash), agrupada por formulário
wpforms_list_abandoned_entries sim Entries abandoned com fields parciais + page_url/page_title/url_referer/captured_user_id (pivot de entry_meta)
wpforms_get_settings sim wpforms_settings (captcha, geral) + resumo de licença (chave sempre mascarada)
wpforms_get_spam_status sim Akismet instalado/activo, contagem global de spam, store_spam_entries + contagem de spam por formulário
wpforms_set_entry_viewed não entries.viewed — mirror da acção "Read"/"Unread"
wpforms_set_entry_starred não entries.starred — mirror da acção "Star"/"Unstar"
wpforms_set_entry_spam não entries.status — mirror exacto de SpamEntry::set_as_spam/set_as_not_spam (ver nota sobre repor '')

Segurança

Escritas restritas a UPDATE de 1 coluna (viewed, starred, status) sobre entries, sempre precedidas de leitura para confirmar que a entry existe (requireEntry, lança erro claro se não). Nunca escreve schema de formulário nem post_content. Identificadores de tabela/coluna validados por assertSafeIdentifier (allowlist [a-z0-9_]+); filtros de texto livre (datas) escapados por sqlString; texto SQL viaja sempre pelo stdin do processo SSH, nunca interpolado na linha de comando remota. Site restrito à allowlist de sites.ts (sem path arbitrário) precisamente para impedir uma query SQL correr contra um prefixo de tabela errado.

Verificação

Construído e testado ponta-a-ponta contra produção 19-08-2026, via chamadas reais ao protocolo MCP (stdio) e confirmação cruzada por SQL directo fora do MCP:

  • wpforms_list_forms em descomplicar (1 formulário, "Pedido de Orçamento", 26 entries) e emanuelalmeida (6 formulários, ex. "Formulário de contacto" com 17 entries) — confirmados por SELECT COUNT(*)/wp post list directos antes de qualquer chamada ao MCP.
  • wpforms_count_entries em descomplicar: total=26, normal=11, abandoned=13, spam=0, trash=2 — igual ao GROUP BY status feito directamente na BD.
  • wpforms_get_entry (entry 37) e wpforms_list_abandoned_entries — campos submetidos (email, telefone, empresa) e contexto de abandono (page_url, page_title) conferem com as colunas fields/entry_meta lidas directamente.
  • wpforms_get_form_schema (formulário 16446) — 157 campos extraídos correctamente do post_content, incluindo layout/pagebreak.
  • wpforms_get_settings — captcha-provider: "recaptcha" e licença type: "elite" com chave mascarada, confirmados contra wp option get wpforms_settings/wpforms_license --format=json.
  • wpforms_get_spam_status em ambos os sites — Akismet correctamente reportado como não instalado, store_spam_entries extraído por JSON_EXTRACT a bater com o post_content bruto.
  • Escrita testada e confirmada por SQL directo antes/depois: wpforms_set_entry_viewed(descomplicar, entry_id=37, viewed=true) sobre uma entry já viewed=1 — round-trip idempotente, valor inalterado em produção, sem qualquer efeito colateral.
  • Erros testados: formulário/entry inexistente devolvem mensagem clara (isError: true) em vez de rebentar ou devolver dados parciais.

Skills relacionadas

Nenhuma skill de conhecimento "wpforms" existe neste repositório — ver secção "Mapeamento de dados" acima para o schema completo. mcp-wpfc/mcp-wpmeteor — mesmo padrão de MCP dedicado multi-site sobre WP-CLI/SSH, para outros plugins do bundle.