From 70b2866edfbec4dde6fb7fbcec080228360710ad Mon Sep 17 00:00:00 2001 From: Emanuel Almeida Date: Wed, 19 Aug 2026 05:11:24 +0100 Subject: [PATCH] feat(wordpress): nova skill mcp-bit-social - MCP dedicado BitSocial/BitSocial Pro Cobre: contas sociais ligadas (+ activar/desactivar), schedules de auto-post e Share Now, logs de publicacao, resumo de falhas por plataforma, analytics, estado da licenca Pro. 12 tools, testado ponta-a-ponta em producao 19-08-2026. references/data-model.md com o schema completo wpah_bit_social_* e references/diagnostics.md com o playbook de investigacao de falhas (inclui o caso real recorrente do Pinterest sem imagem). --- wordpress/.claude-plugin/plugin.json | 4 +- wordpress/skills/mcp-bit-social/SKILL.md | 81 +++++++++++ .../mcp-bit-social/references/data-model.md | 126 ++++++++++++++++++ .../mcp-bit-social/references/diagnostics.md | 54 ++++++++ 4 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 wordpress/skills/mcp-bit-social/SKILL.md create mode 100644 wordpress/skills/mcp-bit-social/references/data-model.md create mode 100644 wordpress/skills/mcp-bit-social/references/diagnostics.md diff --git a/wordpress/.claude-plugin/plugin.json b/wordpress/.claude-plugin/plugin.json index 6f7fcca..e50b9e9 100644 --- a/wordpress/.claude-plugin/plugin.json +++ b/wordpress/.claude-plugin/plugin.json @@ -1,12 +1,12 @@ { "name": "wordpress", "description": "WordPress development, maintenance and optimization - plugins, themes, WooCommerce, Elementor, Crocoblock, EMCP Tools MCP (page building, content ops, security/performance audit), Elementor Pro/ElementsKit/PowerPack widget catalogs. Backed by NotebookLM notebooks.", - "version": "1.3.0", + "version": "1.4.0", "author": { "name": "Descomplicar - Crescimento Digital", "url": "https://descomplicar.pt" }, "homepage": "https://git.descomplicar.pt/ealmeida/descomplicar-plugins", "license": "MIT", - "keywords": ["wordpress", "woocommerce", "elementor", "crocoblock", "development", "performance", "licensing", "emcp-tools", "elementskit", "powerpack", "cloudflare"] + "keywords": ["wordpress", "woocommerce", "elementor", "crocoblock", "development", "performance", "licensing", "emcp-tools", "elementskit", "powerpack", "cloudflare", "social-media"] } diff --git a/wordpress/skills/mcp-bit-social/SKILL.md b/wordpress/skills/mcp-bit-social/SKILL.md new file mode 100644 index 0000000..a94a018 --- /dev/null +++ b/wordpress/skills/mcp-bit-social/SKILL.md @@ -0,0 +1,81 @@ +--- +name: mcp-bit-social +description: MCP dedicado (node stdio, ligação `bit-social` em ~/.omp/agent/mcp.json) para o BitSocial/BitSocial Pro (BitApps) em descomplicar.pt — a automação que publica os artigos do blog em Facebook, LinkedIn, Pinterest e Tumblr. Contas sociais ligadas, schedules de auto-post e "Share Now", logs de publicação, resumo de falhas por plataforma, analytics, estado da licença. Usar quando "bitsocial", "porque falhou a publicação no Pinterest/Facebook/LinkedIn", "contas sociais ligadas", "logs de publicação social", "auto-post settings", "schedule bitsocial", "desactivar conta social", "licença bitsocial pro". +layer: wiki +--- + +# /mcp-bit-social — MCP dedicado BitSocial / BitSocial Pro + +Projecto em `/media/ealmeida/Dados/Dev/mcp-bit-social/` (TypeScript, SDK MCP oficial, stdio). +Independente porque as rotas HTTP do próprio plugin (`backend/routes/*.php`) exigem sessão +wp-admin autenticada + nonce (`Route::noAuth()->middleware('nonce:admin')`) — sem caminho headless +razoável. Sem código PHP novo no WordPress: cada tool corre `wp db query`/`wp option get` via +`ssh server` sobre o WP-CLI já instalado, contra o schema `wpah_bit_social_*` e as `wp_options` do +plugin. + +## Âmbito + +Diagnóstico e gestão de **contas, schedules e logs de publicação** — o domínio onde as falhas +reais acontecem (produção tem 175 schedules, 1825 logs, ~50% falha histórica em Pinterest por +imagem em falta). Não cobre a UI de composição/edição de templates de post nem o fluxo OAuth de +ligar uma conta nova — isso continua a fazer-se no wp-admin. + +## Tools (12) + +**Estado e analytics (read-only):** +- `bs_get_plugin_status` — versões free/pro, db versions, licença Pro (chave mascarada), cron externo. +- `bs_get_analytics` — contas activas, schedules activos, publicações OK/falhadas (soma de logs). + +**Contas:** +- `bs_list_accounts` (read-only) — filtra por `platform`/`status`. Nunca devolve `details` + (contém o access_token OAuth em claro). +- `bs_set_account_status` — única write tool; liga/desliga uma conta (mirror 1:1 de + `AccountController::updateStatus`, só a coluna `status`). Uma conta desactivada sai da rotação + de auto-post/share-now sem perder a ligação OAuth. + +**Configuração (read-only):** +- `bs_list_custom_apps` — apps de API próprias (nunca `credential`, segredo cifrado). +- `bs_list_groups` — grupos de contas (Pro), com as contas membro. +- `bs_get_auto_post_settings` — `bit_social_auto_post_settings`: post types/taxonomias que + disparam auto-post, contas/grupos alvo, atraso de publicação. +- `bs_get_social_templates` — `bit_social_templates_settings`: template de conteúdo por + plataforma (imagem destacada / link card / só texto). + +**Schedules e diagnóstico (read-only):** +- `bs_list_schedules` — `schedule_type=schedule_share` (auto-post recorrente) e `direct_share` + ("Share Now") vivem na mesma tabela; filtra por status/tipo/nome, paginado. +- `bs_get_schedule` — detalhe completo de um schedule, incluindo `config` (filtros de posts, + contas alvo, templates) e `published_post_ids`. +- `bs_list_logs` — um log por (schedule × plataforma) tentado; `details` inclui o erro da + plataforma em caso de falha. Ferramenta principal de diagnóstico. +- `bs_get_failure_summary` — falhas dos últimos N dias agrupadas por plataforma, até 5 mensagens + de erro distintas por plataforma. + +## Segurança + +Nunca selecciona `accounts.details` (access_token OAuth em claro) nem `custom_apps.credential` +(segredo cifrado). `bs_get_plugin_status` mascara a chave de licença Pro (só os últimos 4 +caracteres). Texto livre (pesquisa por nome) viaja como SQL escapado (`sqlString`), nunca +interpolado na linha de comando SSH. + +## Gotcha de infra-estrutura + +`wp db query` shella para o `mysql` CLI, que em modo batch **re-escapa backslashes** nos valores — +sem a flag `--raw`, qualquer `JSON_OBJECT`/`JSON_ARRAYAGG` que aninhe uma coluna já-JSON (ex.: +`config`, `details`) fica com JSON inválido (`\"` vira `\\"`). O MCP já corre sempre com `--raw`; +relevante só ao escrever queries SQL BitSocial novas fora deste MCP. + +## Verificação + +Construído e testado ponta-a-ponta contra produção 19-08-2026: as 12 tools exercitadas com dados +reais (4 contas, 175 schedules, 1825 logs), incluindo o caso de falha real recorrente +("Must have image for create pin in Pinterest.", 39 ocorrências/60 dias) e um round-trip de +escrita idempotente em `bs_set_account_status` (confirmado no `updated_at` da BD). + +## Recursos adicionais + +- **`references/data-model.md`** — schema completo das tabelas `wpah_bit_social_*`, enums + (`Schedule::status`/`scheduleType`, `Account::accountType`), forma dos campos JSON aninhados + (`config`, `details`, `bit_social_auto_post_settings`, `bit_social_templates_settings`). +- **`references/diagnostics.md`** — playbook: investigar uma publicação falhada, pausar uma conta + sem a desligar, ver o que está configurado para auto-post. diff --git a/wordpress/skills/mcp-bit-social/references/data-model.md b/wordpress/skills/mcp-bit-social/references/data-model.md new file mode 100644 index 0000000..ae24bf3 --- /dev/null +++ b/wordpress/skills/mcp-bit-social/references/data-model.md @@ -0,0 +1,126 @@ +# BitSocial — modelo de dados (confirmado em produção, 2026-08-19) + +Prefixo real de tabela em descomplicar.pt: `wpah_` (WP table prefix) + `bit_social_` (`Config::VAR_PREFIX`). +Todas as tabelas usam `Schema::withPrefix(...)`, `timestamps()` standard (`created_at`/`updated_at`). + +## Tabelas + +### `wpah_bit_social_accounts` (4 linhas) + +| Coluna | Tipo | Notas | +|---|---|---| +| `id` | bigint PK | | +| `custom_app_id` | bigint, nullable, FK → `custom_apps.id` | null quando usa a app proxy da BitApps | +| `profile_id` | string | ID do perfil/utilizador na plataforma | +| `account_id` | string | ID da conta/página na plataforma (ex.: Facebook page id) | +| `account_name` | string | | +| `details` | longtext (JSON) | **NUNCA seleccionar** — contém `access_token` OAuth em claro, `icon`, `category`, etc. | +| `platform` | string | `facebook`, `linkedin`, `pinterest`, `tumblr`, `instagram`, `twitter`, `discord`, `threads`, `tiktok`, `bluesky`, `telegram`, `googleBusinessProfile`, … | +| `account_type` | int | `Account::accountType`: `DEFAULT=1` (app proxy BitApps), `CUSTOM=2` (app própria), `AI_PLATFORM=3` | +| `status` | int (bool) | `1` activo, `0` inactivo | + +### `wpah_bit_social_custom_apps` (1 linha) + +| Coluna | Tipo | Notas | +|---|---|---| +| `id` | bigint PK | | +| `name` | string | | +| `platform` | string | | +| `credential` | longtext | **NUNCA seleccionar** — segredo de app cifrado (base64 de payload AES) | +| `status` | int (bool) | | + +### `wpah_bit_social_schedules` (175 linhas) + +Cobre **dois fluxos diferentes na mesma tabela**, distinguidos por `schedule_type`: + +| Coluna | Tipo | Notas | +|---|---|---| +| `id` | bigint PK | | +| `name` | string | Ex.: `"Auto Post - Post ID: 60261"` (auto-post) | +| `config` | longtext (JSON) | Ver forma abaixo. Coluna aninhada — precisa de segundo `JSON.parse` no lado do cliente. | +| `published_post_ids` | longtext (JSON, array de ints) | Posts já publicados por este schedule | +| `repeat_schedule` | bool | | +| `schedule_type` | int | `Schedule::scheduleType`: `SCHEDULE_SHARE=1` (auto-post recorrente/agendado), `DIRECT_SHARE=2` ("Share Now") | +| `status` | int | `Schedule::status`: `INACTIVE=0`, `ACTIVE=1`, `COMPLETED=2`, `DRAFT=3`, `MISSED=4` | +| `cron_status` | int | `INACTIVE=0`, `ACTIVE=1` | +| `started_at`, `ended_at`, `last_published_at`, `next_published_at` | timestamp, nullable | | + +Forma real de `config` (exemplo de um schedule de auto-post): + +```json +{ + "settings": { "started_at": "2026-08-11 11:50:34" }, + "post_filters": { "post_type": "post", "specific_postIds": [60261] }, + "accounts": { "accountIds": [1, 4, 5, 6], "groupIds": [] }, + "templates": { + "facebook": { "postingType": "isFeaturedImage", "content": "{post_title}", "trimMessage": false }, + "pinterest": { "postingType": "isLinkCard", "content": "{post_title}", "trimMessage": true, "isLinkCard": false }, + "...": "uma entrada por plataforma, mesma forma que bit_social_templates_settings" + } +} +``` + +### `wpah_bit_social_logs` (1825 linhas) + +Uma linha por tentativa de publicação (schedule × plataforma). + +| Coluna | Tipo | Notas | +|---|---|---| +| `id` | bigint PK | | +| `schedule_id` | bigint, nullable, FK → `schedules.id` | | +| `details` | longtext (JSON) | Ver forma abaixo | +| `platform` | string | | +| `status` | int (bool) | `1` sucesso, `0` falha — confirmado em `AnalyticsController::index` (`Log::where('status', true)`) | + +Forma de `details` em sucesso: + +```json +{ + "account_id": "164743196715367", + "account_name": "Descomplicar - Agência de Aceleração Digital", + "response": { "id": "164743196715367_122245726064096762", "post_supports_client_mutation_id": true }, + "post_id": 60261, + "post_url": "https://fb.com/164743196715367_122245726064096762" +} +``` + +Forma de `details` em falha (o caso real mais comum, Pinterest): + +```json +{ + "account_id": "1051238806710440771", + "account_name": "Blog Descomplicar", + "post_id": 60261, + "response": { "status": "error", "error_msg": "Must have image for create pin in Pinterest." }, + "post_url": null +} +``` + +O erro está sempre em `details.response.error_msg` nos casos observados; `extractErrorMessage()` +no MCP também tenta `response.message`, `error`, `message` como fallback para plataformas com +forma diferente. + +### `wpah_bit_social_groups` / `wpah_bit_social_groups_accounts` (Pro, 0 linhas em produção) + +`groups`: `id`, `name`, `status`. `groups_accounts`: pivot `group_id` × `account_id`, cascade on +delete. Um grupo pode substituir uma lista explícita de `accountIds` num `config.accounts.groupIds`. + +## `wp_options` relevantes + +| Chave | Conteúdo | +|---|---| +| `bit_social_version` / `bit_social_pro_version` | string, ex. `"1.16.0"` | +| `bit_social_db_version` / `bit_social_pro_db_version` | string | +| `bit_social_installed` / `bit_social_pro_installed` | bool | +| `bit_social_auto_post_settings` | `{isEnabled, keepLogs, taxonomies[], accounts:{accountIds[],groupIds[]}, postType[], postDelay:{every,unit}}` | +| `bit_social_templates_settings` | um objecto por plataforma, mesma forma que `config.templates` num schedule | +| `bit_social_pro_settings` | `{cron:{isExternalCronEnabled}}` | +| `bit_social_pro_license_data` | `{key, status, expireIn}` — `key` **nunca** deve ser devolvida sem máscara | +| `bit_social_secret_key` | segredo interno do plugin — nunca ler/expor | + +## Análogo AnalyticsController (referência, não reimplementação 1:1) + +`AnalyticsController::index` só calcula `active_account_count`, `published_post_count` (logs com +`status=true`) e `active_schedule_count`. `bs_get_analytics` estende com `failed_post_count` +(logs com `status=0`) porque o controller original omite essa contagem apesar de ser a métrica +mais accionável. diff --git a/wordpress/skills/mcp-bit-social/references/diagnostics.md b/wordpress/skills/mcp-bit-social/references/diagnostics.md new file mode 100644 index 0000000..341ce3f --- /dev/null +++ b/wordpress/skills/mcp-bit-social/references/diagnostics.md @@ -0,0 +1,54 @@ +# BitSocial — playbook de diagnóstico + +## Investigar porque uma publicação falhou + +1. Encontrar o schedule pelo nome do post (`bs_list_schedules({search: "Post ID: 60261"})`) ou + pelo id se já conhecido. +2. `bs_get_schedule({schedule_id})` — confirmar `status` (`completed`/`missed`/…), `config.accounts` + (que contas estavam alvo) e `config.templates` (que tipo de conteúdo cada plataforma ia usar, + ex. `postingType: "isLinkCard"` para Pinterest). +3. `bs_list_logs({schedule_id})` — uma linha por plataforma tentada; `status: 0` é falha, + `details.response.error_msg` tem a razão devolvida pela API da plataforma. +4. Cruzar com `bs_list_accounts({platform})` para confirmar que a conta ainda está `active` — uma + conta desligada não é a causa de uma falha já registada em log (o log só existe se a tentativa + chegou a correr), mas explica ausência de tentativa nenhuma. + +## Falha recorrente conhecida: Pinterest sem imagem + +`"Must have image for create pin in Pinterest."` é o erro mais comum em produção (39 ocorrências +em 60 dias, confirmado com `bs_get_failure_summary({days: 60})`). Causa: o `postingType` do +template Pinterest (`bs_get_social_templates`) está como `isLinkCard` +(`"isLinkCard": false` no exemplo real — nome do campo enganador, o efectivo é o `postingType`) +mas o post de origem não tem imagem destacada elegível para a Pinterest API. Não é um bug do +MCP nem do plugin — é um requisito da API do Pinterest (todo pin precisa de imagem). Diagnóstico +correcto: confirmar se o post WordPress em causa tem featured image antes de o auto-post disparar; +o MCP não cobre a correcção (isso é conteúdo/publicação, fora do âmbito de `mcp-bit-social`). + +## Ver o que está configurado para disparar auto-post + +`bs_get_auto_post_settings` — confirma `isEnabled`, que `postType`s (ex.: `["post", "podcast"]`) e +taxonomias disparam publicação automática, atraso (`postDelay`) e que contas/grupos são alvo por +omissão. Cruzar com `bs_get_social_templates` para ver o formato de conteúdo por plataforma. + +## Pausar uma conta sem a desligar (perder OAuth) + +`bs_set_account_status({account_id, status: "inactive"})` — sai da rotação de auto-post e "Share +Now" imediatamente (mirror de `AccountController::updateStatus`), sem apagar a ligação OAuth +(`accounts.details` continua intacto). Reactivar com o mesmo tool e `status: "active"`. +Não confundir com apagar a conta (não coberto por este MCP — fazer via wp-admin se necessário, +apagar tem cascade sobre `groups_accounts` e pode invalidar schedules já criados que a referenciam). + +## Saúde geral rápida + +`bs_get_plugin_status` (versões/licença) + `bs_get_analytics` (contas activas, schedules activos, +publicações OK/falhadas) — dois tools, sem argumentos, para um snapshot inicial antes de investigar +mais fundo. + +## Fora do âmbito deste MCP (fazer via wp-admin) + +- Repetir uma publicação falhada (`RetryController` faz um pedido HTTP real à plataforma social — + lógica de negócio, não um `UPDATE` SQL). +- Mudar o estado de um schedule directamente (`ScheduleController::updateStatus` tem checks + condicionais de `COMPLETED`/`repeat` que não são um simples `UPDATE status=…`). +- Ligar uma conta social nova (fluxo OAuth) ou criar uma custom app. +- Editar o conteúdo/template de publicação por plataforma.