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).
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 antesabandoned/trash— é o comportamento nativo deSpamEntry::set_as_not_spam, replicado tal-e-qual emwpforms_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_formsemdescomplicar(1 formulário, "Pedido de Orçamento", 26 entries) eemanuelalmeida(6 formulários, ex. "Formulário de contacto" com 17 entries) — confirmados porSELECT COUNT(*)/wp post listdirectos antes de qualquer chamada ao MCP.wpforms_count_entriesemdescomplicar:total=26, normal=11, abandoned=13, spam=0, trash=2— igual aoGROUP BY statusfeito directamente na BD.wpforms_get_entry(entry 37) ewpforms_list_abandoned_entries— campos submetidos (email, telefone, empresa) e contexto de abandono (page_url,page_title) conferem com as colunasfields/entry_metalidas directamente.wpforms_get_form_schema(formulário 16446) — 157 campos extraídos correctamente dopost_content, incluindolayout/pagebreak.wpforms_get_settings—captcha-provider: "recaptcha"e licençatype: "elite"com chave mascarada, confirmados contrawp option get wpforms_settings/wpforms_license --format=json.wpforms_get_spam_statusem ambos os sites — Akismet correctamente reportado como não instalado,store_spam_entriesextraído porJSON_EXTRACTa bater com opost_contentbruto.- 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.