From eae213c7f07eab64e62ddbe22a161a7398c89080 Mon Sep 17 00:00:00 2001 From: Emanuel Almeida Date: Wed, 19 Aug 2026 05:29:42 +0100 Subject: [PATCH] feat(wordpress): nova skill mcp-fluent-booking - MCP dedicado FluentBooking/FluentBooking Pro Cobre: calendarios, event types, reservas (listar/detalhe/mudar estado/reenviar confirmacao), actividade, disponibilidade, reports, definicoes gerais, anfitrioes, estado do plugin/licenca Pro. 15 tools via REST API real com Application Password (nao SSH/SQL como mcp-bit-social - o FluentBooking regista register_rest_route com permission_callback por capability). Testado ponta-a-ponta em producao 19-08-2026, incluindo escrita sem mutar dados reais (422 'No changes found' do proprio plugin). references/api-reference.md com os endpoints/filtros/enums confirmados e references/diagnostics.md com o playbook de uso (agenda, cancelamento, health check). --- wordpress/.claude-plugin/plugin.json | 4 +- wordpress/skills/mcp-fluent-booking/SKILL.md | 86 ++++++++++++++++ .../references/api-reference.md | 98 +++++++++++++++++++ .../references/diagnostics.md | 57 +++++++++++ 4 files changed, 243 insertions(+), 2 deletions(-) create mode 100644 wordpress/skills/mcp-fluent-booking/SKILL.md create mode 100644 wordpress/skills/mcp-fluent-booking/references/api-reference.md create mode 100644 wordpress/skills/mcp-fluent-booking/references/diagnostics.md diff --git a/wordpress/.claude-plugin/plugin.json b/wordpress/.claude-plugin/plugin.json index e50b9e9..1cb7dee 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.4.0", + "version": "1.5.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", "social-media"] + "keywords": ["wordpress", "woocommerce", "elementor", "crocoblock", "development", "performance", "licensing", "emcp-tools", "elementskit", "powerpack", "cloudflare", "social-media", "booking"] } diff --git a/wordpress/skills/mcp-fluent-booking/SKILL.md b/wordpress/skills/mcp-fluent-booking/SKILL.md new file mode 100644 index 0000000..9d9528f --- /dev/null +++ b/wordpress/skills/mcp-fluent-booking/SKILL.md @@ -0,0 +1,86 @@ +--- +name: mcp-fluent-booking +description: MCP dedicado (node stdio, ligação `fluent-booking` em ~/.omp/agent/mcp.json) para o FluentBooking/FluentBooking Pro (WPManageNinja) em descomplicar.pt — a agenda de marcação de reuniões (`/marcar-reuniao/`). Calendários, reservas/marcações, disponibilidade, reports, cancelar/reagendar. Usar quando "fluent booking", "marcações", "reuniões agendadas", "quem marcou reunião", "cancelar reserva", "reagendar reunião", "disponibilidade calendário", "reservas fluentbooking", "reenviar confirmação de reunião", "licença fluent booking pro". +layer: wiki +--- + +# /mcp-fluent-booking — MCP dedicado FluentBooking / FluentBooking Pro + +Projecto em `/media/ealmeida/Dados/Dev/mcp-fluent-booking/` (TypeScript, SDK MCP oficial, stdio). +Ao contrário de `mcp-bit-social`/`mcp-element-pack`, **não usa SSH/WP-CLI para os dados de +negócio** — o FluentBooking regista uma REST API real (`register_rest_route` sob +`fluent-booking/v2`, confirmado em `vendor/wpfluent/framework/.../Route.php`) com +`permission_callback` baseado em capabilities (`current_user_can`), não em nonce de sessão +wp-admin. Autentica com uma **Application Password** WordPress (Basic Auth), e cada escrita passa +pela lógica de negócio real do plugin (emails, activity log, side-effects de pagamento) em vez de +um `UPDATE` SQL directo. + +## Auth + +Application Password dedicada (utilizador `ealmeida`, nome `mcp-fluent-booking`), guardada só no +`env:` da ligação MCP (`~/.omp/agent/mcp.json`) — nunca em ficheiro commitado. Revogar em +WordPress → Utilizadores → perfil → Application Passwords se deixar de ser necessária. Recriar com +`wp user application-password create ealmeida mcp-fluent-booking --porcelain`. + +## Tools (15) + +**Estado (read-only):** +- `fb_get_plugin_status` — versões free/pro (via WP-CLI, único tool que ainda usa SSH — a API não + expõe versão) + estado da licença Pro (via API). + +**Calendários e event types (read-only):** +- `fb_list_calendars` / `fb_get_calendar` — páginas de agendamento por anfitrião. +- `fb_get_event` — um tipo de evento (ex.: "Diagnóstico Digital Gratuito, 30min"). + +**Reservas — o domínio principal:** +- `fb_list_bookings` (read-only) — filtros `period` (upcoming/completed/cancelled/pending/ + no_show/latest_bookings/**all**), `calendar_id`, `event_id`, `event_type`, `email`, `search`, + intervalo de datas, paginação. **`period` por omissão é `upcoming`** — usar `all` para histórico. +- `fb_get_booking` (read-only) — reserva completa: convidado, campos de formulário custom, local, + pagamento. +- `fb_list_activities` (read-only) — timeline de uma reserva (`booking_id`) ou log do site inteiro + (sem argumento). +- `fb_update_booking_status` (**write**) — muda `status` para + `scheduled|completed|cancelled|rejected|no_show` pela API real (`PUT /schedules/{id}`, + `column=status`). Único caminho de escrita de estado; dispara os efeitos correctos do plugin + (email de cancelamento com `cancel_reason`, log de actividade, marcação de pagamento). +- `fb_send_confirmation_email` (**write**) — reenvia o email de confirmação a um convidado real. + Envia um email de verdade — usar com intenção, nunca para "testar". + +**Disponibilidade e reports (read-only):** +- `fb_list_availability_schedules` / `fb_get_availability_schedule` — templates de horário + semanal reutilizáveis entre event types. +- `fb_get_reports` — overview (totais, últimas reservas). +- `fb_get_graph_reports` — série temporal (reservas/concluídas/canceladas por dia), intervalo + `date_from`/`date_to` opcional. +- `fb_get_general_settings` — moeda, remetente de email, cancelamento automático. +- `fb_list_hosts` — utilizadores WordPress com calendário próprio. + +## Fora do âmbito + +Equipa/permissões, métodos de pagamento, integrações Zoom/Google/CalDAV, Twilio SMS, webhooks, +cupões, activação/desactivação de licença — gestão de credenciais/integrações externas fica no +wp-admin. Criar reservas em nome de alguém (`POST /bookings/create/{event_id}`) também fica fora: +o payload depende de slots computados dinamicamente e uma reserva mal formada dispara emails reais +a um convidado. + +## Segurança + +`fb_send_confirmation_email` e o ramo `cancelled`/`rejected` de `fb_update_booking_status` +disparam emails reais a convidados verdadeiros — nunca invocar como "teste". Verificação sem +mutação real: mudar um booking para o estado que **já tem** devolve 422 "No changes found" do +próprio plugin (confirma o caminho de escrita sem alterar nada). + +## Verificação + +Construído e testado ponta-a-ponta contra produção 19-08-2026: as 15 tools exercitadas com dados +reais (1 calendário, 39 reservas no histórico, licença Pro "Agency License" válida), incluindo o +caso 422 "No changes found" acima (escrita testada sem mutar dados reais) e 404 correcto em id +inexistente (`fb_get_booking`, `fb_send_confirmation_email`). + +## Recursos adicionais + +- **`references/api-reference.md`** — endpoints REST completos, filtros (`period`, `author`, + `range`), enums, forma real das respostas (`booking`, `calendar`, `event`, `availability`). +- **`references/diagnostics.md`** — playbook: quem marcou uma reunião, cancelar/reagendar + correctamente, health check rápido, o que fazer quando `fb_list_bookings` devolve vazio. diff --git a/wordpress/skills/mcp-fluent-booking/references/api-reference.md b/wordpress/skills/mcp-fluent-booking/references/api-reference.md new file mode 100644 index 0000000..a360880 --- /dev/null +++ b/wordpress/skills/mcp-fluent-booking/references/api-reference.md @@ -0,0 +1,98 @@ +# FluentBooking — referência da REST API (confirmado em produção, 2026-08-19) + +Namespace: `fluent-booking/v2` (WP REST API real, `wp-json/fluent-booking/v2/...`). Auth: Basic +Auth com Application Password — o `permission_callback` de cada rota é uma `Policy` que verifica +`current_user_can()`/`PermissionManager`, não nonce de sessão. 100 rotas registadas entre free e +Pro; este ficheiro documenta só as usadas pelo MCP. + +## Calendários (`/calendars`) + +`GET /calendars` — lista todos, cada um com `slots[]` (event types) aninhados, incluindo +`settings.weekly_schedules` completo por dia. `GET /calendars/{id}` — um só, mesma forma. + +Campos relevantes de um calendar: `id`, `title`, `slug`, `status` (`active`/…), `type` +(`simple`/…), `visibility` (`public`/…), `author_timezone`, `public_url` +(`https://descomplicar.pt/?fluent-booking=calendar&host=`), `author_profile` (nome, avatar, +telefone). + +## Event types (`/events/{event_id}`, aninhado em `calendars[].slots[]`) + +`GET /events/{event_id}` → `{calendar_event: {...}}`. Campos: `id`, `calendar_id`, `duration` +(minutos), `title`, `slug`, `description` (HTML), `settings.schedule_type` +(`weekly_schedules`/…), `settings.weekly_schedules..{enabled, slots:[{start,end}]}`. + +**Gotcha de nomes:** `GET /calendars/event-lists` (sem id) devolve sempre +`{"code":"plugin_exception", ...}` em produção — não é um bug do MCP, o controller +`CalendarController@getCalendarEventLists` está partido standalone nesta instalação. Não usar +este endpoint; listar event types via `fb_list_calendars`/`fb_get_calendar` (já vêm aninhados). + +## Reservas (`/schedules`) + +`GET /schedules` — parâmetros via `filters[...]` (bracket notation, o MCP trata isto +automaticamente a partir de campos planos no input schema): + +| Filtro | Valores | Nota | +|---|---|---| +| `filters[period]` | `upcoming` (omissão), `completed`, `cancelled`, `pending`, `no_show`, `latest_bookings`, `all` | Valor fora da lista cai silenciosamente para `upcoming` | +| `filters[author]` | id numérico do calendário, `me`, `all` | Sem permissão "ver todas as reservas", o utilizador só vê as suas mesmo que peça `all` | +| `filters[event]` | id numérico do event type | | +| `filters[event_type]` | `single`/`group`/… | | +| `filters[email]` | email do convidado | | +| `filters[range]` | `[date_from, date_to]` | Filtra por `created_at`, não pela hora da reunião | +| `search` | texto livre | nome/email do convidado | +| `page` | inteiro | paginação Laravel-style | + +Forma de uma reserva (`schedules.data[]`): `id`, `hash`, `calendar_id`, `event_id`, `group_id` +(agrupa reservas de grupo), `host_user_id`, `start_time`/`end_time` (timezone do site), +`first_name`/`last_name`/`email`/`phone`/`message`, `location_details` +(`{type, online_meeting_link}` ou presencial), `status` +(`scheduled`/`completed`/`cancelled`/`rejected`/`no_show`), `payment_status`, `source_url` +(página onde o convidado marcou), `custom_form_data` (campos custom do formulário de marcação, +por chave), `calendar_event` (event type completo aninhado, redundante mas presente sempre), +`happening_status`, `booking_status_text`, `reschedule_url`. + +`GET /schedules/{id}` — mesma forma para uma reserva. + +`PUT /schedules/{id}` (`fb_update_booking_status`) — body `{column:"status", value:, +cancel_reason?, reject_reason?, refund_payment?}`. Colunas válidas no controller real: +`internal_note`, `email`, `phone`, `first_name`, `last_name`, `status`, `payment_status` — o MCP +só expõe `status` deliberadamente (as outras deixam editar dados do convidado sem justificação de +negócio clara; ver SKILL.md "Fora do âmbito"). Side-effects reais confirmados no código +(`SchedulesController::patchBooking`): +- `status=cancelled` → `Booking::cancelMeeting($reason, 'host', $userId)` + email de cancelamento. +- `status=rejected` → `Booking::rejectMeeting($reason, $userId)`. +- `status=scheduled` com `payment_method`+`payment_order` associados → marca a order como `paid`. +- Valor igual ao actual → `422 {"message":"No changes found"}` (não é um bug, é a validação do + próprio plugin — útil para testar o caminho de escrita sem mutar nada). + +`POST /schedules/{id}/send-confirmation-email` (`fb_send_confirmation_email`) — sem body, envia +email real. + +## Actividade (`/schedules/{id}/activities`, `/reports/activities`) + +Mesma forma nos dois: `{id, booking_id, parent_id, created_by, status, type, title, description, +created_at, updated_at}`. `/reports/activities` é o log do site inteiro (todas as reservas); +`/schedules/{id}/activities` filtra a uma só. + +## Disponibilidade (`/availability`) + +`GET /availability` — lista templates reutilizáveis (`{id, host_name, title, usage_count, +settings:{timezone, date_overrides[], weekly_schedules}}`). `GET /availability/{id}` — um só, +envelope `{schedule: {...}}` em vez de `{availabilities: {data:[...]}}`. + +## Reports (`/reports`, `/reports/graph-reports`) + +`GET /reports` → `{overview: [{title, period?, number, content, stat}], latest_books: [...]}`. +Métricas confirmadas: Total Bookings, Completed Bookings, Cancelled Bookings, Total Guests. + +`GET /reports/graph-reports?date_range[0]=YYYY-MM-DD&date_range[1]=YYYY-MM-DD` → +`{booked_stats, completed_stats, cancelled_stats}`, cada um `{"YYYY-MM-DD": count}`. Sem +`date_range`, o controller usa uma janela por omissão (~7 dias, granularidade adaptada ao +intervalo — dia/semana/mês). + +## Definições e equipa + +`GET /settings/general` → `{payments, emailing, administration}` (moeda, remetente/rodapé de +email, `auto_cancel_timing`, dia de início da semana). `GET /admin/all-hosts` → +`{hosts: [{id, name, label, avatar, calendar_id, deleted_user}]}`. `GET /settings/license` (Pro) +→ `{status, variation_title, expires, is_expired, activation_hash, renew_url, purchase_url}`. diff --git a/wordpress/skills/mcp-fluent-booking/references/diagnostics.md b/wordpress/skills/mcp-fluent-booking/references/diagnostics.md new file mode 100644 index 0000000..17fcb9e --- /dev/null +++ b/wordpress/skills/mcp-fluent-booking/references/diagnostics.md @@ -0,0 +1,57 @@ +# FluentBooking — playbook de diagnóstico + +## "Quem marcou uma reunião?" / ver a agenda + +`fb_list_bookings({period: "upcoming"})` para o que vem aí (omitir `period` já faz isto por +omissão). Para histórico completo (incluindo já realizadas/canceladas), usar +`fb_list_bookings({period: "all"})` — **omitir `period` NUNCA mostra o histórico**, só o que +ainda não aconteceu. Filtrar por convidado com `email` ou `search` (nome). + +Cada resultado já traz `location_details` (link da reunião online), `custom_form_data` (respostas +ao formulário de marcação — telefone, empresa, cargo, conforme configurado no event type) e +`source_url` (de que página do site veio a marcação). + +## Cancelar ou reagendar uma reunião + +Cancelar: `fb_update_booking_status({booking_id, status: "cancelled", cancel_reason: "..."})` — +dispara o email de cancelamento ao convidado automaticamente, não é preciso fazer mais nada. +`refund_payment: true` só faz sentido se a reserva tiver `payment_method` associado (evento pago). + +Reagendar não é uma tool deste MCP — a reserva tem um `reschedule_url` próprio +(`https://descomplicar.pt/?fluent-booking=booking&meeting_hash=...&type=reschedule`) que o +convidado usa directamente; não há endpoint admin para reatribuir hora sem passar por aí. + +## Marcar como "não compareceu" / concluída manualmente + +`fb_update_booking_status({booking_id, status: "no_show"})` ou `status: "completed"`. Sem motivo +associado (`cancel_reason`/`reject_reason` só se aplicam a `cancelled`/`rejected`). + +## Reenviar confirmação a um convidado + +`fb_send_confirmation_email({booking_id})` — só se o convidado pedir explicitamente ou perdeu o +email original. Dispara um envio real; não usar para "confirmar que a tool funciona". + +## `fb_list_bookings` devolve vazio + +Quase sempre é o `period` por omissão (`upcoming`) sem reservas futuras — confirmar com +`fb_get_reports` (overview mostra o total histórico) antes de assumir que a agenda está vazia; se +o total for >0 mas `upcoming` vier vazio, repetir com `period: "all"`. + +## Investigar uma reunião específica em detalhe + +`fb_get_booking({booking_id})` para os dados completos, depois `fb_list_activities({booking_id})` +para a timeline (emails de lembrete enviados, mudanças de estado, quem cancelou e porquê — o +`created_by` fica `null` quando a acção foi automática/pelo sistema). + +## Health check rápido + +`fb_get_plugin_status` (versões + licença Pro) + `fb_get_reports` (totais) — dois tools sem +argumentos para um snapshot inicial. `fb_list_hosts` confirma que anfitriões têm calendário activo +antes de investigar "porque é que ninguém consegue marcar reunião com X". + +## Fora do âmbito deste MCP (fazer via wp-admin) + +- Criar uma reserva em nome de alguém (payload de slots dinâmico, risco de email indevido). +- Gerir equipa/permissões, métodos de pagamento, integrações Zoom/Google Calendar/CalDAV, SMS + (Twilio), webhooks, cupões. +- Activar/desactivar a licença Pro (`fb_get_plugin_status` é read-only).