feat(wordpress): novas skills seriously-simple-podcasting + mcp-seriously-simple-podcasting
Documenta as 14 tools do MCP dedicado (mcp-seriously-simple-podcasting, porta 3203) para episódios/shows/definições de show em emanuelalmeida.pt, complementado pela skill de conhecimento seriously-simple-podcasting (achado central: REST nativo do plugin é escrita-capaz para episódios mas read-only para definições de show).
This commit is contained in:
@@ -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.7.0",
|
||||
"version": "1.7.1",
|
||||
"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", "booking", "fluentcrm", "seo"]
|
||||
"keywords": ["wordpress", "woocommerce", "elementor", "crocoblock", "development", "performance", "licensing", "emcp-tools", "elementskit", "powerpack", "cloudflare", "social-media", "booking", "fluentcrm", "seo", "podcast"]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
name: mcp-seriously-simple-podcasting
|
||||
description: MCP dedicado (HTTP StreamableHTTP local, ligação `seriously-simple-podcasting` em ~/.claude.json, porta 3203) para o Seriously Simple Podcasting em emanuelalmeida.pt (produção, 39 episódios reais em 8 shows) — episódios (CPT `podcast`), shows/séries (taxonomia `series`) e definições de show (título, owner, categorias iTunes, explicit/complete). Escritas sempre via bootstrap WordPress (`wp_insert_post`/`wp_update_post`/`wp_insert_term`/`update_option`), nunca SQL directo. Usar quando "episódio podcast", "criar episódio", "show podcast", "definições de show", "categoria itunes", "feed rss emanuelalmeida", "apagar episódio", "estatísticas podcast", ou qualquer gestão de conteúdo de podcast em emanuelalmeida.pt.
|
||||
layer: wiki
|
||||
---
|
||||
|
||||
# /mcp-seriously-simple-podcasting — MCP dedicado Seriously Simple Podcasting (emanuelalmeida.pt)
|
||||
|
||||
Projecto em `/media/ealmeida/Dados/Dev/mcp-seriously-simple-podcasting/` (TypeScript, SDK MCP
|
||||
oficial, StreamableHTTP). O REST API nativo do plugin é escrita-capaz para episódios mas
|
||||
**read-only para definições de show** (`wp/v2/series` só regista `get_callback`, achado
|
||||
confirmado lendo `Rest_Api_Controller::create_api_series_fields()`) — por isso este MCP usa um
|
||||
único mecanismo consistente para tudo (episódios e shows): o bootstrap WordPress completo via
|
||||
`wp eval`, nunca REST nem SQL directo para escritas.
|
||||
|
||||
**Zona-alvo: emanuelalmeida.pt (produção), 39 episódios reais em 8 shows.**
|
||||
|
||||
## Arquitectura — sem tocar no filesystem do servidor
|
||||
|
||||
Leituras: SQL directo via `$wpdb` (rápido, tipado). Escritas: SEMPRE pelas funções nativas do
|
||||
WordPress (`wp_insert_post`/`wp_update_post`/`wp_delete_post`, `wp_insert_term`/`wp_delete_term`,
|
||||
`wp_set_post_terms`, `update_post_meta`, `update_option`) — nunca INSERT/UPDATE directo, para
|
||||
preservar hooks de feed/permalinks/sanitização nativa. Ambas correm por SSH via `wp eval`, com o
|
||||
corpo PHP + dados de entrada codificados em base64 dentro de uma variável de ambiente remota —
|
||||
**nunca ficheiros escritos/apagados no servidor**, o que evitaria deliberadamente o GATE 5.1 de
|
||||
mutação de infra-estrutura. Ver `src/wp-bridge.ts` (`runPhp()`) — mesmo mecanismo do
|
||||
`mcp-fluent-crm`.
|
||||
|
||||
## Tools (14)
|
||||
|
||||
**Leitura (7):** `ssp_list_episodes` (filtros série/estado/pesquisa), `ssp_get_episode`,
|
||||
`ssp_list_series`, `ssp_get_series_settings` (corrige o bug do REST nativo para
|
||||
explicit/complete), `ssp_get_stats`, `ssp_get_feed_url`, `ssp_get_itunes_categories` (referência
|
||||
estática, sem chamada SSH).
|
||||
|
||||
**Escrita (7):** `ssp_create_episode`, `ssp_update_episode`, `ssp_delete_episode` (trash por
|
||||
omissão, `force=true` apaga permanentemente), `ssp_set_episode_series`, `ssp_create_series`,
|
||||
`ssp_update_series_settings`, `ssp_delete_series` (destructive — ver guarda abaixo).
|
||||
|
||||
## Guardas de segurança
|
||||
|
||||
- `ssp_update_series_settings`/`ssp_create_series` validam `category`/`category2`/`category3` e
|
||||
`subcategory`/`subcategory2`/`subcategory3` contra a lista oficial Apple Podcasts **antes** de
|
||||
qualquer chamada SSH (rejeita categoria inexistente ou par categoria/subcategoria incoerente com
|
||||
erro accionável).
|
||||
- `ssp_delete_series` recusa remover um show com episódios associados sem `force=true` — **mas
|
||||
atenção:** a contagem usada (`term->count`) só reflecte episódios **publicados**; um show só com
|
||||
episódios em `draft` passa como "vazio" mesmo tendo conteúdo associado. Confirmar sempre com
|
||||
`ssp_list_episodes({series_id, status: 'any'})` antes de forçar a remoção.
|
||||
- `ssp_delete_episode` por omissão move para o lixo (recuperável); `force=true` apaga
|
||||
permanentemente.
|
||||
|
||||
## Fora do v1 (decisão deliberada)
|
||||
|
||||
Gestão da ligação Castos/hosting (não aplicável — desligada neste site), upload directo de
|
||||
ficheiros de áudio (o plugin só aceita URL, sem endpoint de upload próprio — usar Media Library
|
||||
via ferramentas WordPress genéricas e passar o URL resultante), CRUD de tags normais (`post_tag`)
|
||||
— usar ferramentas WordPress genéricas para isso, o CPT `podcast` é standard.
|
||||
|
||||
## Credenciais
|
||||
|
||||
`.env` local (`WP_SSH_HOST=server`, `WP_PATH=/home/ealmeida/emanuelalmeida.pt`), não versionado.
|
||||
Sem tokens/API keys — a autenticação é a própria sessão SSH root já configurada em
|
||||
`~/.ssh/config`.
|
||||
|
||||
## Verificação
|
||||
|
||||
Construído e testado ponta-a-ponta contra a zona real 19-08-2026: todas as 14 tools exercitadas
|
||||
manualmente (incl. guarda de categoria inválida e guarda de show com episódio publicado) + 13/13
|
||||
evaluations automatizadas (`npm run eval:ci`), que criam e limpam os seus próprios artefactos
|
||||
(`__mcp_eval__`) sem tocar em dados reais. Deploy como processo `hub` persistente
|
||||
(`mcp-seriously-simple-podcasting`, porta 3203, `http://127.0.0.1:3203/mcp`), registado em
|
||||
`~/.claude.json` como `seriously-simple-podcasting`.
|
||||
|
||||
## Skills relacionadas
|
||||
|
||||
- `seriously-simple-podcasting` — conhecimento profundo do plugin: arquitectura CPT/taxonomia,
|
||||
achado central do REST nativo (episódios escrita-capaz, shows read-only), schema de
|
||||
`wp_options` das definições de show (com o gotcha explicit/complete), categorias iTunes, feed
|
||||
RSS, wp-admin — consultar antes de desenhar uma integração ou entender um comportamento que este
|
||||
MCP não cobre.
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
name: seriously-simple-podcasting
|
||||
description: Conhecimento profundo do Seriously Simple Podcasting (Castos, plugin `seriously-simple-podcasting`) — arquitectura, CPT `podcast` (episódios) e taxonomia `series` (shows), meta fields custom, REST API nativo (o que é escrita-capaz vs read-only), wp_options de definições de show (`ss_podcasting_*`), categorias iTunes/Apple Podcasts, feed RSS, e o estado real em emanuelalmeida.pt (39 episódios, 8 shows, sem addon Castos/Pro). Usar quando "seriously simple podcasting", "podcast wordpress", "episódio podcast", "show podcast", "feed rss podcast", "categorias itunes", "apple podcasts categoria", "castos", "ss_podcasting", "cpt podcast", ou qualquer trabalho de configuração/estratégia de podcast que não seja apenas chamar o MCP. Para execução directa (CRUD de episódios/shows), usar a tool MCP `mcp-seriously-simple-podcasting`.
|
||||
layer: wiki
|
||||
---
|
||||
|
||||
# /seriously-simple-podcasting — Seriously Simple Podcasting (Castos) — conhecimento de plugin
|
||||
|
||||
Plugin `seriously-simple-podcasting` (Free, `SSP_VERSION` 3.17.0), instalado e activo em
|
||||
**emanuelalmeida.pt**. **Sem addon Castos/Pro** — não há `fluentcampaign-pro`-equivalente neste
|
||||
site (confirmado: pasta `php/` do plugin não tem addon Pro, `ssp_is_connected_to_castos()` devolve
|
||||
falso, sem hospedagem/sincronização externa de ficheiros). Mapeamento feito por leitura completa
|
||||
do código-fonte (`wp-content/plugins/seriously-simple-podcasting/php/`) via SSH, 19-08-2026, antes
|
||||
de construir o MCP `mcp-seriously-simple-podcasting`.
|
||||
|
||||
---
|
||||
|
||||
## 0. Free vs addon Castos (não instalado aqui)
|
||||
|
||||
| | Free (activo) | Addon Castos/Pro (ausente) |
|
||||
|---|---|---|
|
||||
| Episódios (CPT `podcast`), shows (taxonomia `series`), feed RSS | ✅ core | — |
|
||||
| Upload/hosting de ficheiros de áudio | ❌ (URL externo só) | ✅ hosting Castos + analytics avançado |
|
||||
| Sincronização automática (`podmotor_*` meta) | campos existem mas vazios (nunca usados) | ✅ |
|
||||
| Transcrições, capítulos | não encontrado no código lido (sem módulo dedicado nesta versão) | — |
|
||||
|
||||
Todos os 39 episódios reais têm `audio_file` a apontar para `episodes.castos.com` (CDN legado de
|
||||
uma ligação Castos anterior, hoje desligada) — os ficheiros continuam a servir de lá, mas o plugin
|
||||
já não está autenticado/sincronizado com a conta.
|
||||
|
||||
---
|
||||
|
||||
## 1. Onde vivem os dados
|
||||
|
||||
### 1.1 Episódios — CPT `podcast` (constante `SSP_CPT_PODCAST`)
|
||||
|
||||
Post standard (`show_in_rest => true`), **sem tabela própria** — tudo em `wp_posts`/`wp_postmeta`.
|
||||
Meta fields registados via `register_meta()` (todos `show_in_rest => true`, confirmado em
|
||||
`CPT_Podcast_Handler::register_meta()`):
|
||||
|
||||
| Meta key | Tipo | Nota |
|
||||
|---|---|---|
|
||||
| `episode_type` | `audio`\|`video` | |
|
||||
| `audio_file` | URL | ficheiro (ou stream) do episódio |
|
||||
| `cover_image` / `cover_image_id` | URL / attachment ID | imagem quadrada, `cover_image_id` é interno (não escrever directamente) |
|
||||
| `duration` | texto | ex. `00:32:14`, calculado automaticamente se possível, mas aceita override manual |
|
||||
| `filesize` / `filesize_raw` | texto / bytes | `filesize_raw` é interno/calculado, usado no RSS — não escrever directamente |
|
||||
| `date_recorded` | `Y-m-d` | distinto de `post_date` (data de publicação) |
|
||||
| `explicit` / `block` | checkbox (`'on'`\|`''`) | `block` esconde de directórios iTunes/Google |
|
||||
| `itunes_episode_number` / `itunes_season_number` | número | tags iTunes WWDC 2017 |
|
||||
| `itunes_title` | texto | título específico para o feed iTunes (sem número de episódio/show) |
|
||||
| `itunes_episode_type` | `full`\|`trailer`\|`bonus` | |
|
||||
| `podmotor_file_id` / `podmotor_episode_id` / `castos_file_data` / `sync_status` | — | só-Castos, sempre vazios neste site |
|
||||
|
||||
### 1.2 Shows — taxonomia `series` (função `ssp_series_taxonomy()`, devolve `'series'`)
|
||||
|
||||
Termo hierárquico, `show_in_rest => true`, `rest_base => 'series'`. Um episódio pode pertencer a
|
||||
0-N shows (`wp_set_post_terms`). Tags normais (`post_tag`) também estão ligadas ao CPT por
|
||||
omissão (filtro `ssp_use_post_tags`, `true` neste site) — existe também uma taxonomia alternativa
|
||||
`podcast_tags`, mas só é registada se `ssp_use_post_tags` for desligado (não é o caso aqui).
|
||||
|
||||
### 1.3 Definições de show — `wp_options`, NUNCA numa tabela/post
|
||||
|
||||
Cada show tem um bloco de opções `ss_podcasting_data_{campo}_{series_id}` (título, subtítulo,
|
||||
descrição, autor, imagem, owner_name/email, idioma, copyright, category/subcategory 1-3) **mais**
|
||||
duas excepções sem o infixo `data_`: `ss_podcasting_explicit_{series_id}` e
|
||||
`ss_podcasting_complete_{series_id}` (gotcha — ver §6). Confirmado por SQL directo em
|
||||
`emanuelalmeida.pt`: 18 opções distintas escritas para o show "Afirmações" (id 120).
|
||||
|
||||
Existe também um conjunto **sem sufixo `_{series_id}`** — os defaults globais do site (usados
|
||||
quando um show não tem override próprio), lidos em
|
||||
`Rest_Api_Controller::get_default_podcast_settings()`.
|
||||
|
||||
---
|
||||
|
||||
## 2. REST API — o que é escrita-capaz vs read-only (achado central)
|
||||
|
||||
| Endpoint | Leitura | Escrita |
|
||||
|---|---|---|
|
||||
| `GET/POST/PUT /wp/v2/podcast` | ✅ episódios completos + todos os meta fields do §1.1 | ✅ (CPT standard, `show_in_rest`, capacidades `edit_podcasts` mapeadas para editor/administrator) |
|
||||
| `GET/POST/PUT /wp/v2/series` | ✅ termos + **campos extra injectados** (title/subtitle/author/owner_name/owner_email/language/copyright/image/category1-3/explicit_option/complete_option) | ❌ os campos extra são só-leitura — `register_rest_field()` em `Rest_Api_Controller::create_api_series_fields()` só regista `get_callback`, **sem `update_callback`**. Confirmado lendo o código-fonte; um `PUT` no termo não altera nenhuma destas definições. |
|
||||
| `GET /ssp/v1/episodes` | ✅ query cross-post-type (namespace próprio) | endpoint dedicado só para `podmotor_episode_id`/`audio_file` (uso interno Castos) |
|
||||
|
||||
**Implicação prática:** episódios podem ser geridos 100% via REST nativo (Application Password),
|
||||
mas **definições de show só podem ser escritas via `wp_options` directamente** (WP-CLI `wp option
|
||||
update` ou, como o MCP faz, `update_option()` dentro de um `wp eval`). É por isto que
|
||||
`mcp-seriously-simple-podcasting` usa o bootstrap WordPress completo (`wp eval`) para tudo, em vez
|
||||
de misturar REST para episódios e SQL para shows — um único mecanismo, sempre consistente.
|
||||
|
||||
---
|
||||
|
||||
## 3. wp-admin — páginas principais
|
||||
|
||||
| Página | Conteúdo |
|
||||
|---|---|
|
||||
| **Podcasting → All Episodes** | lista de episódios (CPT `podcast`), com metabox de todos os campos do §1.1 |
|
||||
| **Podcasting → Add New Episode** | criação, campo de show (taxonomia `series`) na sidebar |
|
||||
| **Podcasting → Settings → General** | defaults globais do site |
|
||||
| **Podcasting → Settings → Feed Details** | por show (selector "feed-series" no topo) — título/subtítulo/owner/categorias/idioma/copyright/imagem — é aqui que se editam manualmente as opções do §1.3 |
|
||||
| **Podcasting → Settings → Hosting** | ligação Castos (desligada neste site) |
|
||||
| **Podcasting → Settings → Import** | importar de outro feed RSS |
|
||||
|
||||
---
|
||||
|
||||
## 4. Categorias iTunes/Apple Podcasts
|
||||
|
||||
19 categorias de topo (`Arts`, `Business`, `Comedy`, `Education`, `Fiction`, `Government`,
|
||||
`History`, `Health & Fitness`, `Kids & Family`, `Leisure`, `Music`, `News`,
|
||||
`Religion & Spirituality`, `Science`, `Society & Culture`, `Sports`, `Technology`, `True Crime`,
|
||||
`TV & Film`), a maioria com subcategorias próprias (`Government`/`History`/`Technology`/
|
||||
`True Crime` não têm) — é a taxonomia oficial Apple Podcasts, lida de
|
||||
`php/config/settings/feed-categories.php` e `feed-subcategories.php`. Cada show suporta até 3
|
||||
pares categoria/subcategoria (`category`/`subcategory`, `category2`/`subcategory2`,
|
||||
`category3`/`subcategory3`). Lista completa disponível na tool `ssp_get_itunes_categories` do MCP
|
||||
(sem chamada SSH — é referência estática).
|
||||
|
||||
---
|
||||
|
||||
## 5. Feed RSS
|
||||
|
||||
URL construído a partir de `home_url()` + `feed/podcast` (ou `?feed=podcast` sem permalinks
|
||||
bonitos) + `/{slug-do-show}` para o feed de um show específico. Cada show tem o seu próprio feed
|
||||
filtrado (`Episode_Repository::get_feed_url()`); o feed "geral" (sem `series_id`) inclui episódios
|
||||
de todos os shows.
|
||||
|
||||
---
|
||||
|
||||
## 6. Estado real em emanuelalmeida.pt (verificado 19-08-2026)
|
||||
|
||||
- **39 episódios**, todos `publish`, distribuídos por **8 shows**: Meditação (11), DDE Desafio
|
||||
Detox Emocional (10), Afirmações (8), Reprogramação mental (6), Motivacionais (3), + 3 shows
|
||||
menores/vazios (incl. "Emanuel Almeida", 0 episódios).
|
||||
`audio_file` sempre aponta para o CDN legado `episodes.castos.com` (ligação Castos desligada,
|
||||
ficheiros continuam a servir de lá).
|
||||
- **Nenhum episódio marcado `explicit`** (único valor visto na coluna: `''`).
|
||||
- Plugins activos relevantes no mesmo site: Rank Math SEO PRO, Wordfence, WP Meteor, WP Fastest
|
||||
Cache, `seguranca-descomplicar` — nenhum interfere com REST/feeds do SSP nesta verificação
|
||||
(`curl` a `/wp-json/wp/v2/podcast` e `/feed/podcast/...` devolveram 200 sem bloqueio).
|
||||
|
||||
---
|
||||
|
||||
## 7. Gotchas
|
||||
|
||||
| Sintoma | Causa | Nota |
|
||||
|---|---|---|
|
||||
| `wp/v2/series` devolve `explicit_option`/`complete_option` sempre vazios ou iguais ao default global, mesmo com override por show definido | Bug do próprio plugin: `series_get_field_value()` faz fallback genérico para `ss_podcasting_data_{campo}_{series_id}`, mas `explicit`/`complete` são gravados em `ss_podcasting_{campo}_{series_id}` (sem `data_`) — o REST nunca encontra o override | Ler via `ssp_get_series_settings` do MCP (lê directamente com o prefixo correcto) em vez de confiar no REST nativo para estes dois campos |
|
||||
| `PUT /wp/v2/series/{id}` com `title`/`owner_email`/etc no payload não faz nada | Campos são `get_callback`-only (§2) | Escrever via `ssp_update_series_settings` do MCP (`update_option`) |
|
||||
| `ssp_delete_series`/qualquer verificação de "show tem episódios" baseada em `term->count` dá 0 mesmo com episódios associados | `term_taxonomy.count` do WordPress só conta posts **publicados** — episódios em `draft`/`pending`/`future` não entram na contagem | Não assumir "count=0 → seguro apagar"; confirmar sempre com `ssp_list_episodes` incluindo `status=any` antes de remover um show |
|
||||
| Nenhum registo de transcrições/capítulos encontrado no código | Este site usa a versão Free sem addon; não há módulo de transcrição na árvore lida (`grep -i transcript` só encontra uma referência de string solta num metabox, sem implementação funcional associada) | Não assumir suporte — verificar de novo se o plugin for actualizado/o addon Castos for religado |
|
||||
|
||||
---
|
||||
|
||||
## Fonte
|
||||
|
||||
Leitura completa do código-fonte `seriously-simple-podcasting` 3.17.0 via SSH em
|
||||
emanuelalmeida.pt (`php/classes/handlers/class-cpt-podcast-handler.php`,
|
||||
`class-series-handler.php`, `php/classes/repositories/class-episode-repository.php`,
|
||||
`php/classes/rest/class-rest-api-controller.php`, `class-episodes-rest-controller.php`,
|
||||
`php/classes/controllers/class-settings-controller.php`, `php/config/settings/*.php`,
|
||||
`php/includes/ssp-functions.php`), cruzada com `SHOW TABLES`/`DESCRIBE`/agregações reais via SQL e
|
||||
chamadas REST reais (`curl`), 19-08-2026 — sessão de construção do MCP
|
||||
`mcp-seriously-simple-podcasting`. Ver essa skill para a lista completa de 14 tools programáticas.
|
||||
Reference in New Issue
Block a user