Claude Skill que genera Word (.docx) desde tu plantilla

Ilustración técnica para: Crea una Claude Skill que genera Word con tu plantilla

TL;DR: Una Agent Skill es una carpeta con un SKILL.md (frontmatter YAML con name y description más instrucciones) y, si hace falta, scripts y recursos. Para generar Word con tu plantilla, guarda el .docx en assets/, escribe en él marcadores Jinja2 como {{ cliente }} y añade un script que lo rellene con docxtpl. En Claude Code la carpeta va en ~/.claude/skills/ o .claude/skills/; en claude.ai se sube como ZIP; en la API se sube con la Skills API, pero ahí no se pueden instalar paquetes y docxtpl no figura entre los preinstalados, así que conviene una variante con python-docx.

Qué es una Agent Skill y qué formato exige SKILL.md

Una skill es un directorio que Claude descubre y carga bajo demanda. El único fichero obligatorio es SKILL.md: empieza con un bloque YAML entre --- y sigue con instrucciones en Markdown. El formato está publicado como estándar abierto en agentskills.io y Anthropic lo documenta en la guía de Agent Skills.

Campo¿Obligatorio?Reglas
name Sí en claude.ai y la API; opcional en Claude Code (usa el nombre de la carpeta) Máximo 64 caracteres; solo minúsculas, números y guiones; sin etiquetas XML; no puede contener "anthropic" ni "claude". El estándar pide que coincida con el nombre de la carpeta.
description Sí (en Claude Code, recomendado) No vacío, máximo 1024 caracteres, sin etiquetas XML. Debe decir qué hace la skill y cuándo usarla.
license, compatibility, metadata, allowed-tools No Campos opcionales del estándar. compatibility admite hasta 500 caracteres para declarar requisitos (paquetes, red).
when_to_use, disable-model-invocation, argument-hint, model, context… No Solo Claude Code. Por ejemplo, disable-model-invocation: true impide que Claude la active solo y la deja como comando /nombre.

Ojo con el nombre: claude-word o anthropic-docx no son válidos para subir a claude.ai o a la API por las palabras reservadas. Un nombre como brand-docx sirve en todas partes.

Cómo decide Claude activarla

Claude carga la skill en tres niveles. Al arrancar solo lee name y description (Anthropic lo cifra en unos 100 tokens por skill). Cuando tu petición encaja con la description, lee el cuerpo de SKILL.md (recomendado por debajo de 5.000 tokens). Los ficheros auxiliares solo entran en contexto si los lee, y de los scripts que ejecuta solo entra la salida. Por eso la description decide si la skill se usa o no, y la guía oficial pide mantener el cuerpo de SKILL.md por debajo de 500 líneas y mover el detalle a ficheros aparte. Si te preocupa el consumo de contexto en general, tienes más en Claude Code y los 200k tokens.

Dónde se instala la skill: Claude Code, claude.ai y API

La sincronización entre superficies es parcial y en un solo sentido. Las skills habilitadas en tu cuenta de claude.ai se descargan en Claude Code cuando inicias sesión con esa cuenta (en el terminal, desde la versión 2.1.273, en ~/.claude/skills/synced/), pero lo que editas en Claude Code no se sube a claude.ai. La API va aparte: lo que subes a claude.ai no aparece en la API y viceversa. Además, cada entorno ejecuta los scripts con restricciones distintas, y eso afecta directamente a una skill que depende de docxtpl.

SuperficieCómo se instalaAlcancePaquetes de Python
Claude Code Carpeta en ~/.claude/skills/<nombre>/SKILL.md (personal) o .claude/skills/<nombre>/SKILL.md (proyecto). También vía plugins o sincronizada desde tu cuenta de claude.ai. Tu máquina, o todo el que clone el repo Los de tu entorno local. La doc desaconseja que la skill instale paquetes globales.
claude.ai ZIP de la carpeta en Customize > Skills. Requiere tener activada la ejecución de código. Privada al subirla; en Team y Enterprise se puede compartir con compañeros (con grupos, solo en Enterprise), publicar en la biblioteca de la organización, y los propietarios pueden aprovisionarla para todos El acceso a red depende de la configuración del usuario o del administrador; con red, puede instalar desde PyPI.
API de Claude Subida con la Skills API (/v1/skills) y referencia en container.skills junto a la herramienta de ejecución de código. Todo el workspace Sin red ni instalación en tiempo de ejecución. python-docx figura en la lista de preinstalados; docxtpl, no.

¿Basta con la skill oficial de Word?

Anthropic ofrece una skill de Word preconstruida (docx) junto a las de Excel, PowerPoint y PDF. Está activa por defecto en claude.ai y se usa en la API con skill_id: "docx". En Claude Code llega con la sincronización si inicias sesión con tu cuenta de claude.ai (la documentación incluye en ella las skills integradas de Anthropic). Si no, su código está en el repositorio anthropics/skills y se instala como plugin con /plugin marketplace add anthropics/skills y luego /plugin install document-skills@anthropic-agent-skills. Las skills de documentos de ese repositorio son de código visible, no open source: su licencia no es la Apache 2.0 del resto.

Según su SKILL.md, la skill oficial crea documentos nuevos con la librería docx de npm y edita los existentes descomprimiendo el .docx, modificando word/document.xml y volviéndolo a empaquetar, con scripts para validar y convertir a PDF. Es genérica: puede trabajar sobre tu plantilla si se la adjuntas, pero no sabe qué campos tiene ni cuáles son tus reglas. Una skill propia guarda la plantilla dentro de la carpeta y fija el procedimiento, así que no dependes de adjuntarla ni de explicarla en cada conversación.

EnfoquePara qué sirveLímite
docxtpl (Python) Rellenar una plantilla .docx con Jinja2: variables, condiciones y bucles, también en tablas, encabezados y pies Paquete externo: no está en el contenedor de la API
python-docx Crear o editar documentos desde Python; sustituciones simples en una plantilla Sin motor de plantillas: bucles y condiciones los programas tú
docx (npm) Generar documentos desde JavaScript/TypeScript; patchDocument sustituye marcadores {{...}} en una plantilla existente Requiere Node.js
Skill oficial docx Crear, leer y editar Word sin escribir nada No conoce tu plantilla ni tus reglas

Para una plantilla de marca, docxtpl es la opción más directa: renderiza sobre el XML de tu propio .docx, así que portada, estilos, membrete y pie se quedan como los diseñaste.

Paso a paso: skill brand-docx con docxtpl

1. Estructura de carpetas

Las carpetas scripts/, references/ y assets/ son la convención del estándar para código ejecutable, documentación de consulta y recursos estáticos como plantillas:

brand-docx/
├── SKILL.md                # obligatorio: frontmatter + instrucciones
├── scripts/
│   └── fill_template.py    # rellena la plantilla
└── assets/
    └── plantilla.docx      # tu plantilla con marcadores Jinja2

2. Prepara la plantilla en Word

Abre tu plantilla y escribe los marcadores donde quieras contenido variable. Funcionan en el cuerpo, en tablas y en encabezados y pies de página:

  • Variables: {{ titulo }}, {{ cliente }}, {{ fecha }}. El texto insertado toma el formato del marcador, así que dale al marcador la fuente y el estilo que quieras ver.
  • Filas repetidas en una tabla: una fila con {%tr for e in entregables %}, debajo la fila modelo con {{ e.nombre }} y {{ e.fecha }}, y una última fila con {%tr endfor %}. Las filas de control desaparecen al renderizar.
  • Párrafos condicionales: {%p if notas %}, el párrafo con {{ notas }} y {%p endif %}, cada uno en su párrafo.

Las etiquetas Jinja2 normales ({% %}) no pueden abarcar varios párrafos, filas o runs; para eso existen las variantes {%p %}, {%tr %}, {%tc %} y {%r %}. Deja siempre un espacio después de la apertura y antes del cierre de la etiqueta.

3. El script de relleno

El script resuelve la ruta de la plantilla a partir de su propia ubicación, de modo que funciona sea cual sea el directorio de trabajo. Lee los datos de un fichero JSON (más robusto que pasar JSON como argumento de la línea de comandos, donde las comillas se rompen con facilidad) y tiene un modo --campos para que Claude consulte qué variables espera la plantilla antes de redactar.

"""Rellena assets/plantilla.docx con los datos de un JSON.

Uso:
  python scripts/fill_template.py --campos
  python scripts/fill_template.py datos.json salida.docx
Requiere: docxtpl
"""
import json
import sys
from pathlib import Path

from docxtpl import DocxTemplate
from jinja2 import Environment, StrictUndefined

PLANTILLA = Path(__file__).resolve().parent.parent / "assets" / "plantilla.docx"


def main():
    doc = DocxTemplate(str(PLANTILLA))

    if sys.argv[1:] == ["--campos"]:
        # Variables de primer nivel que usa la plantilla (cuerpo, encabezados y pies)
        print("\n".join(sorted(doc.get_undeclared_template_variables())))
        return 0

    if len(sys.argv) != 3:
        print("Uso: fill_template.py datos.json salida.docx", file=sys.stderr)
        return 2

    datos = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))

    # StrictUndefined: un campo ausente lanza un error en vez de dejar un hueco.
    # autoescape: los caracteres especiales de XML en los datos no corrompen el .docx.
    env = Environment(undefined=StrictUndefined)
    doc.render(datos, jinja_env=env, autoescape=True)
    doc.save(sys.argv[2])
    print(f"Documento generado: {sys.argv[2]}")
    return 0


if __name__ == "__main__":
    sys.exit(main())

Dos decisiones del script evitan los fallos más silenciosos. Por defecto, Jinja2 sustituye una variable que no está en los datos por una cadena vacía, y el documento sale con un hueco sin avisar; con StrictUndefined el script falla y nombra la variable, y Claude puede corregir el JSON y repetir. La contrapartida es que un campo opcional también tiene que venir en el JSON (vacío si no aplica), o la plantilla debe comprobarlo con {%p if notas is defined %}. Y docxtpl no escapa por defecto el texto que inserta, así que un "I+D & Co." o un "<" en los datos pueden dejar el XML inválido; autoescape=True lo evita.

Instala docxtpl (arrastra python-docx, jinja2 y lxml) en el entorno de Python del proyecto, por ejemplo en un entorno virtual, no de forma global.

4. El SKILL.md

---
name: brand-docx
description: Genera documentos Word (.docx) a partir de la plantilla corporativa, con su membrete, estilos y pie de página. Usar cuando el usuario pida una propuesta, informe, memo o carta en Word, mencione .docx o hable de la plantilla de marca.
---

# Documentos Word con la plantilla de marca

No construyas el documento desde cero ni cambies estilos: rellena siempre
la plantilla con el script.

1. Consulta los campos de la plantilla:
   python "${CLAUDE_SKILL_DIR}/scripts/fill_template.py" --campos
2. Redacta el contenido y guárdalo en datos.json con exactamente esos campos.
   "entregables" es una lista de objetos con "nombre" y "fecha".
3. Genera el documento:
   python "${CLAUDE_SKILL_DIR}/scripts/fill_template.py" datos.json salida.docx
4. Si el script dice que falta un campo, complétalo en datos.json y repite el paso 3.

Dependencia: docxtpl. Si no está instalado, pide al usuario que lo instale
en el entorno del proyecto; no lo instales de forma global.

La description está en tercera persona y combina qué hace con cuándo usarla, como pide la guía de buenas prácticas de Anthropic: las palabras que un usuario escribiría de verdad ("propuesta en Word", ".docx", "plantilla") son las que permiten que Claude la elija. En Claude Code, description y when_to_use se truncan juntos a 1.536 caracteres en el listado de skills.

${CLAUDE_SKILL_DIR} es una sustitución de Claude Code que apunta a la carpeta de la skill. El estándar no la define, así que en la versión que subas a claude.ai o a la API escribe la ruta relativa a la raíz de la skill: scripts/fill_template.py.

5. Pruébala

Antes de dejar que Claude la use, ejecuta el script tú con un datos.json de ejemplo:

python .claude/skills/brand-docx/scripts/fill_template.py --campos
python .claude/skills/brand-docx/scripts/fill_template.py datos.json prueba.docx

Después, en Claude Code, pide algo como "prepara una propuesta en Word para ACME con tres entregables" o invócala directamente con /brand-docx. Si la skill vive en .claude/skills/ del repositorio, versiónala con el código y revisa sus cambios como cualquier otro fichero.

Si la vas a usar en la API (o en claude.ai sin red)

El contenedor de ejecución de código de la API no tiene acceso a internet ni permite instalar paquetes. En la lista de librerías preinstaladas que publica Anthropic figura python-docx y no docxtpl, así que lo seguro es no depender de este último. Esta variante del script usa solo python-docx: sustituye marcadores {{campo}} de texto simple en el cuerpo, en las tablas (también anidadas) y en todos los encabezados y pies con sus tablas. No admite bucles ni condiciones, así que adapta la plantilla y el SKILL.md a campos simples. Tampoco recorre cuadros de texto ni notas al pie: no pongas marcadores ahí, porque ni se sustituyen ni se detectan como pendientes.

"""Variante sin docxtpl: solo python-docx.

Uso: python scripts/fill_template.py datos.json salida.docx
"""
import json
import re
import sys
from pathlib import Path

from docx import Document

PLANTILLA = Path(__file__).resolve().parent.parent / "assets" / "plantilla.docx"
MARCADOR = re.compile(r"\{\{\s*(\w+)\s*\}\}")


def parrafos(contenedor):
    # Documento, celda, encabezado y pie exponen .paragraphs y .tables;
    # la recursión entra en las tablas anidadas dentro de celdas.
    yield from contenedor.paragraphs
    for tabla in contenedor.tables:
        for fila in tabla.rows:
            for celda in fila.cells:
                yield from parrafos(celda)


def todos_los_parrafos(doc):
    yield from parrafos(doc)
    for s in doc.sections:
        for parte in (
            s.header, s.first_page_header, s.even_page_header,
            s.footer, s.first_page_footer, s.even_page_footer,
        ):
            # Si está vinculado, su contenido es el de la sección anterior
            if not parte.is_linked_to_previous:
                yield from parrafos(parte)


def main():
    if len(sys.argv) != 3:
        print("Uso: fill_template.py datos.json salida.docx", file=sys.stderr)
        return 2
    datos = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
    doc = Document(str(PLANTILLA))
    pendientes = set()
    for parrafo in todos_los_parrafos(doc):
        for run in parrafo.runs:
            nuevo = MARCADOR.sub(
                lambda m: str(datos.get(m.group(1), m.group(0))), run.text
            )
            if nuevo != run.text:
                run.text = nuevo
        # Lo que quede es un campo sin dato o un marcador partido entre runs
        pendientes.update(MARCADOR.findall(parrafo.text))
    if pendientes:
        print(f"Marcadores sin sustituir: {sorted(pendientes)}", file=sys.stderr)
        return 1
    doc.save(sys.argv[2])
    print(f"Documento generado: {sys.argv[2]}")
    return 0


if __name__ == "__main__":
    sys.exit(main())

Para subir la skill y usarla desde el SDK de Python:

import anthropic
from anthropic.lib import files_from_dir

client = anthropic.Anthropic()
skill = client.skills.create(files=files_from_dir("brand-docx"))

response = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    container={
        "skills": [{"type": "custom", "skill_id": skill.id, "version": "latest"}]
    },
    messages=[{"role": "user", "content": "Genera la propuesta para ACME en Word"}],
    tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
)

El .docx se genera dentro del contenedor, pero solo vuelve a tu aplicación si queda en el nivel superior de $OUTPUT_DIR, un directorio vacío que el contenedor crea para cada llamada Bash; lo que se guarda en otra ruta no se devuelve. Por eso, en la versión del SKILL.md para la API, el paso de generación debe escribir ahí y listar el directorio en el mismo comando:

python scripts/fill_template.py datos.json "$OUTPUT_DIR/propuesta.docx" && ls "$OUTPUT_DIR"

Cada fichero capturado aparece con su file_id en el resultado de la ejecución y se descarga con la Files API (client.files.download). La subida admite hasta 30 MB sin comprimir y cada petición puede cargar hasta 20 skills.

Errores comunes

  • La skill no se activa. La description es vaga o no menciona lo que pide el usuario. Añade los términos reales (Word, .docx, propuesta, informe, plantilla) y di cuándo usarla, no solo qué hace.
  • La subida a claude.ai o a la API falla. Revisa el name: minúsculas, números y guiones, sin "claude" ni "anthropic", y que el ZIP contenga la carpeta con SKILL.md.
  • ModuleNotFoundError: No module named 'docxtpl'. En local, falta instalarlo en el entorno que usa Claude. En la API no se puede instalar: usa la variante con python-docx.
  • Huecos vacíos en el documento. Un campo del JSON no coincide con el marcador. Con StrictUndefined el script lo detecta; ejecuta --campos para ver los nombres exactos.
  • Word dice que el archivo está dañado. Una causa conocida es un &, < o > sin escapar en los datos: renderiza con autoescape=True.
  • Error de sintaxis en un {% for %} o un {% if %}. La etiqueta abarca varios párrafos o filas; usa {%p %} o {%tr %}.
  • El script no encuentra la plantilla. Construye la ruta desde __file__, como en el ejemplo, en lugar de una ruta relativa al directorio de trabajo.

Una última precaución: una skill puede ejecutar código con los permisos de Claude. La documentación de Anthropic recomienda usar solo skills propias o de Anthropic y auditar cualquier otra antes de instalarla. Sobre cómo encajan skills, permisos y herramientas en el agente, tienes más en Agent harness en Claude Code.

Preguntas frecuentes

¿Una skill de Claude Code funciona también en claude.ai y en la API?

El formato es el mismo, pero la sincronización solo va de claude.ai a Claude Code: una skill que creas en una carpeta local de Claude Code hay que subirla tú a claude.ai (ZIP en Customize > Skills) y, por separado, a la API (Skills API). Revisa también las dependencias, porque la API no tiene red ni permite instalar paquetes.

¿Qué campos son obligatorios en el frontmatter de SKILL.md?

name (hasta 64 caracteres, minúsculas, números y guiones) y description (hasta 1024 caracteres). Claude Code acepta omitir name y usa el nombre de la carpeta, pero para subirla a claude.ai o a la API necesitas ambos.

¿Puedo usar la skill oficial de Word en Claude Code?

Sí. Si inicias sesión en Claude Code con tu cuenta de claude.ai, se sincronizan las skills habilitadas en ella, incluidas las integradas de Anthropic. Si no, puedes instalarla como plugin desde el repositorio anthropics/skills con /plugin marketplace add anthropics/skills y /plugin install document-skills@anthropic-agent-skills.

¿En qué se diferencia SKILL.md de CLAUDE.md?

CLAUDE.md se carga en cada sesión de Claude Code; el cuerpo de una skill solo se carga cuando la tarea encaja con su description. CLAUDE.md sirve para el contexto fijo del proyecto y las skills para procedimientos concretos. Para afinar el primero, consulta qué auditar en CLAUDE.md.

¿docxtpl modifica los estilos de mi plantilla?

No los reconstruye: renderiza sobre el XML de tu propio .docx y solo sustituye las etiquetas. El texto insertado hereda el formato del marcador.

Compartir

X LinkedIn
Volver al blog