# Blog Taxonomy > Extrae, sugiere y sincroniza etiquetas y categorías de posts en los principales CMS: WordPress REST, Shopify GraphQL, Ghost, Strapi y Sanity, con umbrales mínimos de posts para evitar archivos de etiqueta vacíos. Fuente: https://skillsagentes.com/skills/agricidaniel/claude-blog/blog-taxonomy Markdown: https://skillsagentes.com/skills/agricidaniel/claude-blog/blog-taxonomy.md Repositorio: https://github.com/AgriciDaniel/claude-blog Autor: AgriciDaniel Licencia: MIT Actualizado: hace 2 meses Coste de contexto: 135 tok instalada, 2.5k tok al activarse, 2.5k tok con todos los archivos del bundle Bundle: 1 archivo, 10 KB Permisos que pide: ninguno declarado ## Instalación Un skill son archivos markdown: los mismos archivos valen para cualquier agente y lo único que cambia es el directorio de destino, es decir la bandera `--agent`. Añade `-g` para instalarlo en todos los proyectos de la máquina. ```bash # Claude Code npx -y skills add AgriciDaniel/claude-blog --skill blog-taxonomy --agent claude-code # Cursor npx -y skills add AgriciDaniel/claude-blog --skill blog-taxonomy --agent cursor # Codex npx -y skills add AgriciDaniel/claude-blog --skill blog-taxonomy --agent codex # Gemini CLI npx -y skills add AgriciDaniel/claude-blog --skill blog-taxonomy --agent gemini # Windsurf npx -y skills add AgriciDaniel/claude-blog --skill blog-taxonomy --agent windsurf # Cline npx -y skills add AgriciDaniel/claude-blog --skill blog-taxonomy --agent cline ``` ## Qué hace - Sugiere etiquetas puntuando frecuencia, presencia en encabezados y énfasis, y devuelve entre 5 y 10 candidatas ordenadas - Agrupa variantes de singular y plural, formas con y sin guion, y sinónimos bajo el término más frecuente - Sincroniza con el CMS por API autenticada y pagina correctamente en cada plataforma - Audita la taxonomía: etiquetas huérfanas sin posts, etiquetas finas con menos de 5, exceso de etiquetas y categorías de más de 3 niveles - Exige HTTPS, bloquea IPs privadas y de loopback, valida redirecciones y respeta `CMS_ALLOWED_HOSTS` cuando está definido ## Cuándo usarla - El usuario dice "etiquetas", "categorías", "taxonomía", "sincronizar tags" o "tags de WordPress" ## Qué la activa - "Sugiéreme etiquetas para este post" - "Sincroniza la taxonomía con mi WordPress" - "Audita las etiquetas de mi blog" ## Antes de instalar - Lee `CMS_TYPE`, `CMS_URL`, `CMS_API_KEY` y, en WordPress, `CMS_USERNAME` del entorno; nunca guarda credenciales en archivos. - makes network requests ## Archivos - SKILL.md — 10 KB ## SKILL.md Reproducido tal cual desde AgriciDaniel/claude-blog bajo MIT. Esta sección es el documento original y está en inglés. # Blog Taxonomy Manage tags, categories, and topic clusters across CMS platforms. ## Commands | Command | Purpose | |---------|---------| | `/blog taxonomy suggest ` | Extract candidate tags and categories from content | | `/blog taxonomy sync ` | Push taxonomy to CMS via authenticated API | | `/blog taxonomy audit [directory]` | Check for thin tags, orphan tags, taxonomy bloat | ## Tag Suggestion Workflow ### Step 1: Parse Content Structure Read the target file and extract: - All H2 and H3 headings (primary topic signals) - Bold and italic phrases (emphasis signals) - Existing frontmatter tags/categories if present ### Step 2: Frequency Analysis Scan the body text for high-frequency phrases: - 1-word terms: minimum 4 occurrences (excluding stop words) - 2-word phrases: minimum 3 occurrences - 3-word phrases: minimum 2 occurrences Exclude common non-tag words: articles, prepositions, conjunctions, pronouns. ### Step 3: Semantic Grouping Group related candidates into clusters: - Merge singular/plural variants (keep the more common form) - Merge hyphenated and non-hyphenated forms - Group synonyms under the highest-frequency term ### Step 4: Deduplicate and Rank - Fuzzy match on slugified names (Levenshtein distance <= 2) - Do not auto-merge short slugs under 5 characters using Levenshtein alone; require token overlap or manual review - Score each candidate: `(frequency * 2) + (heading_presence * 5) + (emphasis * 1)` - Return top 5-10 ranked suggestions ### Output Format ``` ## Tag Suggestions: [Post Title] | Rank | Tag | Score | Source | |------|-----|-------|--------| | 1 | content-marketing | 18 | H2 + 6 mentions | | 2 | seo-strategy | 14 | H3 + 4 mentions | | 3 | keyword-research | 11 | 5 mentions + bold | ### Suggested Categories - Primary: [best-fit category] - Secondary: [optional second category] ``` ## CMS Adapters ### Adapter Overview | CMS | API Type | Auth Method | Tags Model | |-----|----------|-------------|------------| | WordPress | REST | Application Passwords (base64) | First-class entities with IDs | | Shopify | GraphQL (Admin API) | Admin API access token | String array on Article | | Ghost | REST (Admin API) | API key with JWT signing | First-class entities | | Strapi | REST or GraphQL | API token (Bearer) | User-defined content type | | Sanity | GROQ / Mutations | Project token (Bearer) | Document type | ### WordPress Adapter **List tags**: ``` GET {CMS_URL}/wp-json/wp/v2/tags?per_page=100&search={keyword} Authorization: Basic {base64(username:app_password)} ``` **Create tag**: ``` POST {CMS_URL}/wp-json/wp/v2/tags Body: {"name": "Tag Name", "slug": "tag-name", "description": "Optional"} ``` **List categories** (hierarchical, supports parent field): ``` GET {CMS_URL}/wp-json/wp/v2/categories?per_page=100 ``` **Create category**: ``` POST {CMS_URL}/wp-json/wp/v2/categories Body: {"name": "Category", "slug": "category", "parent": 0} ``` **Assign tags to post**: ``` POST {CMS_URL}/wp-json/wp/v2/posts/{id} Body: {"tags": [1, 2, 3], "categories": [4]} ``` Pagination: follow `X-WP-TotalPages` header for full listing. ### Shopify Adapter Tags on Shopify are string arrays on the Article object, not first-class entities. **Update article tags** (GraphQL Admin API): ```graphql mutation { articleUpdate(id: "gid://shopify/Article/123", article: { tags: ["tag-one", "tag-two", "tag-three"] }) { article { id tags } userErrors { field message } } } ``` **List all tags in use** (GraphQL): ```graphql { articles(first: 250, after: $cursor) { pageInfo { hasNextPage endCursor } edges { node { id title tags } } } } ``` Auth header: `X-Shopify-Access-Token: {token}` Pagination: loop while `pageInfo.hasNextPage` is true, passing `endCursor` as the next `$cursor`. Note: REST API marked legacy Oct 2024. GraphQL required for new apps since Apr 2025. ### Ghost Adapter **List tags**: ``` GET {CMS_URL}/ghost/api/admin/tags/?limit=all Authorization: Ghost {jwt_token} ``` **Create tag**: ``` POST {CMS_URL}/ghost/api/admin/tags/ Body: {"tags": [{"name": "Tag Name", "slug": "tag-name"}]} ``` JWT generation: sign with admin API key (id:secret format), iat = now, exp = 5 min, audience = `/admin/`. ### Strapi Adapter Endpoint auto-generated from content types. Typical setup: ``` GET {CMS_URL}/api/tags?pagination[pageSize]=100 POST {CMS_URL}/api/tags Body: {"data": {"name": "Tag Name", "slug": "tag-name"}} Authorization: Bearer {api_token} ``` Pagination: increment `pagination[page]` until all pages are exhausted. Strapi v4 responses use the `data` wrapper with `attributes`; Strapi v5 uses a flatter response shape. Detect the version or normalize both shapes before deduplication. Check your content type schema for field names. ### Sanity Adapter **Query tags** (GROQ): ``` *[_type == "tag"] { _id, name, slug } ``` **Create tag** (Mutations API): ``` POST https://{project_id}.api.sanity.io/{SANITY_API_VERSION}/data/mutate/{dataset} Body: {"mutations": [{"create": {"_type": "tag", "name": "Tag", "slug": {"current": "tag"}}}]} Authorization: Bearer {token} ``` Default `SANITY_API_VERSION` to a current tested API date supplied by the project environment; do not hard-code it in generated requests. ## Taxonomy Audit Workflow ### Step 1: Inventory Scan all posts in the target directory (or fetch from CMS). Build a map: - tag_name -> [list of post files/IDs using this tag] - category_name -> [list of post files/IDs] ### Step 2: Health Checks | Check | Threshold | Action | |-------|-----------|--------| | Thin tag archives | < 5 posts per tag | Review for merge or noindex after traffic, intent, and link checks | | Orphan tags | 0 posts | Recommend deletion | | Tag bloat | More than `max(50, post_count * 0.25)` total tags, adjusted for taxonomy purpose | Recommend consolidation | | Category depth | > 3 levels | Recommend flattening | | Uncategorized posts | No category assigned | Assign to appropriate category | | Duplicate slugs | Same slug, different name | Merge into canonical version | ### Step 3: Recommendations Group findings by priority: - **Critical**: orphan tags creating empty archive pages (crawl waste) - **High**: thin tags with < 5 posts after traffic, intent, and link checks - **Medium**: tag bloat above the scaled threshold (diluted taxonomy, harder to navigate) - **Low**: naming inconsistencies (mixed case, hyphen vs space) ### Output Format ``` ## Taxonomy Audit: [Site/Directory] **Total tags**: [n] | **Total categories**: [n] **Healthy**: [n] | **Thin**: [n] | **Orphan**: [n] ### Critical Issues - [orphan tags list] ### Recommendations 1. Merge [tag-a] and [tag-b] (same topic, [n] combined posts) 2. Delete orphan tags: [list] 3. Merge or noindex tag archives with < 5 posts only after traffic, intent, and link checks ``` ## Site-Wide Guidelines - Aim for 5-10 main categories per site (broad topics) - Tags should have at least 5 posts before creating an archive page - Use consistent slug format: lowercase, hyphen-separated - Every post needs exactly 1 primary category - Tags per post: 3-8 recommended, never exceed 15 ## Environment Variables | Variable | Purpose | Example | |----------|---------|---------| | CMS_TYPE | Platform identifier | wordpress, shopify, ghost, strapi, sanity | | CMS_URL | HTTPS base URL of the CMS | https://example.com | | CMS_ALLOWED_HOSTS | Optional comma-separated allowlist for CMS hosts | example.com,admin.example.com | | CMS_USERNAME | WordPress username when using Application Passwords | editor@example.com | | CMS_API_KEY | Authentication credential | WordPress app password, API token, or key | | SANITY_API_VERSION | Sanity API date for mutations | v2026-07-01 | These must be set in the shell environment. Never store credentials in files or commit them to version control. The skill reads them via `$CMS_TYPE`, `$CMS_URL`, `$CMS_USERNAME`, `$CMS_API_KEY`, and optional platform-specific variables at runtime. Security rule for CMS calls: require HTTPS, allow only `http` and `https` parsing paths but send authenticated requests over HTTPS only, resolve DNS and block loopback/private/link-local/reserved IPs, validate redirects with the same checks or disable redirects, cap timeouts at 10 seconds, and enforce `CMS_ALLOWED_HOSTS` when set. ## Error Handling - **Missing environment variables**: If CMS_TYPE, CMS_URL, or CMS_API_KEY is unset, or if WordPress lacks CMS_USERNAME, report which variable is missing and provide the expected format - **Invalid credentials**: If the CMS API returns 401/403, report "Authentication failed - check CMS_USERNAME/CMS_API_KEY" and do not retry - **Connection timeouts**: If the CMS endpoint is unreachable after 10 seconds, report the timeout and suggest checking CMS_URL - **Duplicate tag slugs**: If a tag already exists on the CMS, skip creation and note "Tag already exists: [name]" - **Rate limits**: If the CMS API returns 429, honor `Retry-After` when present; otherwise use exponential backoff and retry once. Report if the limit persists - **Unsupported CMS**: If CMS_TYPE is not one of the 5 supported platforms, list the valid options and exit ## Dónde encaja - Categoría: [Redacción y contenido](https://skillsagentes.com/categorias/redaccion-contenido.md) — Redacción, edición y estructuración de contenido extenso. - Creador: [AgriciDaniel](https://skillsagentes.com/creators/agricidaniel.md) — 80 skills en el directorio - [Todas las skills](https://skillsagentes.com/skills.md) - [Ranking de instalaciones](https://skillsagentes.com/ranking.md) ## Otras skills del mismo repositorio - [Blog](https://skillsagentes.com/skills/agricidaniel/claude-blog/blog.md): Motor de blog de ciclo completo con 31 subskills, 12 plantillas, puntuación sobre 100 y 5 agentes. Enruta cada petición a la subskill correcta: escribir, reescribir, analizar, auditar, schema, clusters y publicación multilingüe. - [Blog Google](https://skillsagentes.com/skills/agricidaniel/claude-blog/blog-google.md): Integración con las APIs de Google para rendimiento de blog: PageSpeed Insights, CrUX con 25 semanas de histórico, Search Console, URL Inspection, Indexing API, GA4, NLP de entidades, YouTube y Keyword Planner. - [Blog Notebooklm](https://skillsagentes.com/skills/agricidaniel/claude-blog/blog-notebooklm.md): Consulta cuadernos de Google NotebookLM para obtener respuestas ancladas en tus propios documentos y con citas: gestiona la biblioteca de cuadernos, la autenticación con Google y el descubrimiento de contenido. - [Blog Audio](https://skillsagentes.com/skills/agricidaniel/claude-blog/blog-audio.md): Genera narración en audio de posts con Google Gemini TTS: resumen hablado, lectura completa o diálogo tipo pódcast a dos voces, con 30 voces y salida MP3 más el código de inserción HTML5. - [Blog Image](https://skillsagentes.com/skills/agricidaniel/claude-blog/blog-image.md): Generación y edición de imágenes con IA para contenido de blog mediante Gemini por MCP: portadas, ilustraciones, tarjetas sociales y OG, con 6 modos de dominio y retorno silencioso si el MCP no está disponible. --- Skills Agentes · [Índice de páginas en markdown](https://skillsagentes.com/sitemap.md) · [Inicio](https://skillsagentes.com/index.md)