plugin.json: el manifiesto al detalle

Por: Artiko
agent-skillsplugins-de-agentesia-agentesplugin-jsonjson-schemamanifiestoagent-plugins

plugin.json: el manifiesto al detalle

En el capítulo 2 escribiste un plugin.json de dos campos y funcionó. Ese archivo es el único punto de contacto obligatorio entre tu paquete y cualquier cliente conformante. La sección no normativa Design Decisions lo titula literalmente “Why root-level plugin.json is the conformance floor”: el piso de conformidad. La regla que lo sostiene sí es normativa y está en §5.1: todo cliente DEBE (MUST) buscar un manifiesto en plugin.json en la raíz del plugin.

Este capítulo recorre el manifiesto entero: cada propiedad permitida, su tipo, si es obligatoria, qué restricciones tiene, qué pasa cuando falta y qué pasa cuando sobra. Las dos fuentes son la sección 5 de spec/1.0.0.md y el archivo schemas/1.0.0/plugin.schema.json. Cuando ambas se contradicen, la propia especificación resuelve el empate:

“The specification text is authoritative if it conflicts with the schema.”

El texto normativo manda; el JSON Schema es la versión legible por máquina de una parte de ese texto.

1. Dónde vive y cuándo se carga

La sección 5.1 fija tres hechos que conviene tener claros antes de mirar campos.

Uno. El manifiesto está en plugin.json, en la raíz del plugin. No hay ruta alternativa, ni variable de entorno que la cambie, ni convención de “si no existe, prueba en config/”.

Dos. Hay exactamente un manifiesto portable por plugin:

“The Agent Plugins core specification defines exactly one portable manifest per plugin. No other file can replace, supplement, or override the core fields in root plugin.json.”

Ningún archivo propio de un cliente y ninguna extensión pueden sobrescribir name o version. Los datos específicos de cliente van bajo extensions o en un directorio de namespace, que veremos más abajo.

Tres. El manifiesto se carga y valida antes de descubrir componentes o aplicar comportamiento específico del cliente. Ese orden importa: si el manifiesto es inválido de forma fatal, el cliente ni siquiera llega a mirar skills/ ni mcp.json.

flowchart TD
    A["Directorio del plugin"] --> B{"Existe plugin.json en la raiz"}
    B -- No --> R1["No es un plugin"]
    B -- Si --> C{"Resuelve dentro del plugin root"}
    C -- No --> R2["MUST rechazar el plugin"]
    C -- Si --> D{"Es JSON y objeto de nivel superior"}
    D -- No --> R2
    D -- Si --> E{"Valor de dollar-schema reconocido"}
    E -- No --> R3["MUST rechazar y SHOULD reportar version no soportada"]
    E -- Si --> F["Validar el esquema cerrado"]
    F --> G["Descubrir componentes en skills y mcp.json"]

2. El esquema es cerrado: diez campos y ni uno más

La sección 5.2 es tajante:

“Its schema is closed: the only permitted top-level fields are $schema, name, version, description, author, homepage, repository, license, keywords, and extensions.”

El JSON Schema lo materializa con "additionalProperties": false en el objeto raíz. Diez nombres, en total. Dos obligatorios, ocho opcionales.

¿Por qué cerrado? La sección no normativa Design Decisions lo explica: un manifiesto cerrado habilita validación estricta, detección de erratas y autocompletado guiado por esquema. Los experimentos de cada cliente no pueden reclamar campos de nivel superior arbitrarios; quedan contenidos bajo claves de dominio inverso dentro de extensions.

2.1 Tabla de referencia rápida

CampoTipoObligatorioRestricciones del esquemaPara qué sirve
$schemastringconst con el identificador canónicoDeclara la versión de Agent Plugins que el paquete apunta
namestringminLength: 1, maxLength: 64, patrón de nomenclaturaNombre legible del plugin
versionstringNosolo type: stringCadena de versión; SemVer RECOMENDADO
descriptionstringNosolo type: stringDescripción breve del propósito
authorobjectNoobjeto cerrado con name, email, urlDatos del autor
homepagestringNosolo type: stringURL de documentación o página
repositorystringNosolo type: stringURL del repositorio fuente
licensestringNosolo type: stringIdentificador de licencia; SPDX RECOMENDADO
keywordsstring[]Noarray de stringsEtiquetas de búsqueda y descubrimiento
extensionsobjectNovalores de miembro deben ser objetosDatos específicos de cliente por namespace

Fíjate en la columna de restricciones: salvo $schema y name, el esquema no impone nada más allá del tipo JSON. Eso es deliberado y lo desarrollamos en el apartado 5.

3. El campo $schema: para qué sirve de verdad

$schema no es decoración ni una ayuda para tu editor. Es el selector de contrato.

Para Agent Plugins 1.0.0 su valor DEBE ser exactamente el identificador canónico:

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json"
}

En el JSON Schema esto se expresa como "const", no como "format": "uri": cualquier otra cadena falla la validación.

3.1 Las cuatro reglas de $schema

Regla 1 — selecciona reglas locales. Los clientes DEBEN usar un valor de $schema reconocido para seleccionar las reglas de validación e interpretación del manifiesto que soportan localmente. No se trata solo de validar la forma: el identificador selecciona el contrato completo, incluyendo requisitos que JSON Schema no puede expresar (containment de rutas, orden de carga, comportamiento ante fallos).

Regla 2 — compatibilidad solo explícita. Un cliente PUEDE (MAY) mapear varios identificadores canónicos a la misma implementación, pero “only when it explicitly recognizes those Agent Plugins versions as compatible”. Nada de deducir compatibilidad parseando el número de versión de la URL.

Regla 3 — prohibido descargar el esquema. “Clients MUST NOT retrieve a schema while loading a plugin.” La URL es un identificador, no un endpoint que se resuelve en tiempo de carga. Cargar un plugin nunca implica tráfico de red hacia agent-plugins.org.

Regla 4 — versión no soportada, plugin rechazado. Si el cliente no soporta la versión declarada ni una explícitamente reconocida como compatible, DEBE rechazar el plugin y DEBERÍA (SHOULD) reportar la versión no soportada.

3.2 Coherencia con mcp.json

La sección 10.1 añade una regla de coherencia interna del paquete: cuando existe mcp.json, la versión de su $schema DEBE coincidir con la que declara plugin.json. Un desajuste invalida la configuración MCP —el cliente deshabilita MCP para ese plugin— pero no invalida los demás tipos de componente. Los skills siguen cargando. Esa asimetría la desarrollamos en el capítulo 5.

Cada release de la especificación DEBE publicar ambos esquemas con la misma versión que la spec, incluso si las reglas de validación de uno no cambiaron. Y los identificadores canónicos publicados NO DEBEN (MUST NOT) reasignarse a contenidos de esquema distintos. El capítulo 7 entra en el porqué.

4. El campo name y las reglas de nomenclatura

name es el segundo campo obligatorio. La sección 5.5 impone cuatro restricciones acumulativas:

RestricciónRequisitoDetalle
Longitud1 a 64 caracteresEl nombre DEBE medir entre 1 y 64 caracteres, ambos inclusive
Juego de caracteresa-z, 0-9, -, .Solo alfanuméricos en minúscula, guiones y puntos
Inicio y finAlfanuméricoEl primer y el último carácter DEBEN ser alfanuméricos
RepeticiónNi -- ni ..Guiones consecutivos y puntos consecutivos no están permitidos

La especificación aclara explícitamente que “Periods are allowed in plugin names”: el punto es un carácter de primera clase, no un accidente.

4.1 El patrón, traducido

El esquema codifica las cuatro restricciones en una sola expresión regular:

^(?!.*(?:--|\.\.))[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$

Leída por partes:

  • (?!.*(?:--|\.\.)) es un lookahead negativo: rechaza la cadena si en cualquier posición aparece -- o ...
  • [a-z0-9] obliga a que el primer carácter sea alfanumérico en minúscula.
  • (?:[a-z0-9.-]*[a-z0-9])? permite un cuerpo con letras, dígitos, puntos y guiones, siempre terminado en alfanumérico. El ? final hace que un nombre de un solo carácter sea válido.

Las cotas minLength: 1 y maxLength: 64 viven aparte, como propiedades del esquema.

4.2 Nombres válidos e inválidos

La propia especificación lista sus ejemplos. Estos son válidos:

my-plugin
acme.tools
lint3r
a

Y estos, con la razón exacta del rechazo:

NombreRazón del rechazo
My-PluginMayúsculas: el juego de caracteres es solo a-z
-startGuion inicial: el primer carácter debe ser alfanumérico
has--doubleGuiones consecutivos
too.many..dotsPuntos consecutivos
cadena vacíaIncumple la longitud mínima de 1

Un name que viole cualquiera de estas reglas hace el manifiesto inválido, y un manifiesto inválido por un campo obligatorio es fatal: el cliente rechaza el plugin entero.

4.3 name no es un identificador global

Un matiz que ahorra malentendidos: la especificación describe name como “Human-readable plugin name”. No define un registro central, ni unicidad global, ni relación obligatoria entre el name del manifiesto y el nombre del directorio en disco. Cómo se resuelven colisiones entre plugins instalados es asunto del cliente y del mecanismo de distribución, no del formato portable. Volvemos sobre esto en el capítulo 9.

5. Campos de metadatos: validación por tipo, nada más

Los siete campos opcionales de metadatos son version, description, author, homepage, repository, license y keywords.

5.1 La regla de no-sobrevalidación

Esta es probablemente la regla del manifiesto que más gente implementa mal. La sección 5.4 dice, literal:

“Except where this specification states an explicit constraint, metadata fields are validated only by their JSON types.”

Y luego enumera lo que un cliente NO DEBE hacer. Un cliente NO DEBE rechazar un manifiesto solamente porque:

  • version no es un Semantic Versioning válido.
  • homepage, repository o author.url no son URLs reconocidas.
  • author.email no es una dirección de correo reconocida.
  • license no es un identificador SPDX.

SemVer y SPDX son RECOMENDADOS (RECOMMENDED), no obligatorios. Si tu validador rechaza "version": "2026-Q3" estás siendo más estricto que la especificación, y un plugin perfectamente conformante deja de cargar en tu cliente. Esa estrictez extra rompe portabilidad.

flowchart LR
    A["Campo de metadato"] --> B{"El tipo JSON es correcto"}
    B -- No --> C["Manifiesto invalido: fatal"]
    B -- Si --> D{"La spec impone una restriccion explicita"}
    D -- Si --> E["Aplicar esa restriccion"]
    D -- No --> F["Aceptar el valor tal cual"]

Dicho eso: que el cliente no pueda rechazarte no significa que debas ignorar las recomendaciones. version en SemVer y license en SPDX son lo que hacen que las herramientas de catálogo y actualización funcionen bien.

5.2 version

Cadena. Sin patrón en el esquema. La especificación le da un propósito operativo concreto: “Used for update checks and cache freshness”. Los clientes PUEDEN usar version para determinar si hay actualizaciones disponibles y si las cachés están rancias.

La sección 10.2 da la tabla de semántica esperada cuando se usa SemVer:

SegmentoSignificadoDescripción
MajorCambio incompatibleCambio de comportamiento o de esquema no compatible
MinorFuncionalidad compatibleComportamiento nuevo sin romper clientes ni usuarios existentes
PatchCorrección compatibleCambio correctivo sin ruptura de comportamiento intencionada

Ojo con la ambigüedad: version es la versión de tu plugin. La versión de la especificación va en $schema y en ningún otro sitio.

5.3 description

Cadena. “Short description of plugin purpose.” Sin longitud máxima en el esquema.

Un punto de precisión que conviene fijar porque se confunde a menudo: el description del manifiesto no es el description del frontmatter de un SKILL.md. El de la skill tiene un papel operativo definido por la especificación de Agent Skills. El del manifiesto es metadato del paquete. Agent Plugins no le asigna ningún papel en la selección de componentes.

5.4 author

Objeto. Y es un objeto cerrado, con la misma disciplina que el manifiesto raíz:

“The author object MAY contain only the name, email, and url fields, each with a string value. Any other field or value type makes the manifest invalid.”

En el esquema: "additionalProperties": false con tres propiedades de tipo string, ninguna requerida. Un author vacío ({}) es válido. Un author con github o twitter no lo es, y el fallo es fatal.

{
  "author": {
    "name": "Author Name",
    "email": "[email protected]",
    "url": "https://example.com"
  }
}

Este es el punto donde más manifiestos caen, porque package.json de npm admite ahí una cadena tipo "Nombre <mail> (url)". Aquí no: author es objeto o nada.

5.5 homepage, repository y license

Tres cadenas planas. homepage es la URL de documentación o página del proyecto. repository es la URL del repositorio fuente. license es un identificador de licencia, con SPDX RECOMENDADO"MIT", "Apache-2.0", "CC-BY-4.0"—.

No hay format en el esquema para ninguna de las tres. Se aplica la regla de no-sobrevalidación del apartado 5.1.

5.6 keywords

Array de cadenas. Sin minItems, sin maxItems, sin uniqueItems. Un array vacío es válido. Sirve para búsqueda y descubrimiento en catálogos.

Lo que no es válido es un array con elementos que no sean cadenas: "keywords": ["cli", 3] viola el tipo de los items y es fatal.

6. El campo extensions

extensions es la válvula de escape del manifiesto cerrado, y la única.

Regla de la sección 8.1: los datos de manifiesto específicos de cliente DEBEN representarse bajo un namespace de dominio inverso dentro de extensions. El campo DEBE ser un objeto cuyos nombres de miembro son namespaces y cuyos valores de miembro son objetos.

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "example-plugin",
  "extensions": {
    "com.example.client": {
      "setting": true
    }
  }
}

Puntos clave:

  • Un cliente DEBERÍA basar su namespace en un dominio que controle y DEBERÍA mantenerlo estable. Quien controla example.com usa com.example.client.
  • Agent Plugins “assigns no semantics to namespace object contents”. El formato no define descubrimiento, validación, carga ni fallos para el contenido de un namespace. Cada cliente define lo suyo.
  • Un cliente DEBE ignorar entradas de namespaces que no implementa sin validar el contenido de sus valores. No es “las valido y las descarto”; es “no las miro”.
  • El mismo namespace puede usarse también como directorio de nivel superior para archivos específicos de cliente: com.example.client/. Un cliente PUEDE usar cualquiera de las dos representaciones, o ambas.

6.1 El caso especial: extensions que no es objeto

Aquí hay una excepción a la fatalidad general, y es importante. Si extensions no es un objeto —es una cadena, un array, un número— el cliente DEBE reportar e ignorar el campo y continuar cargando componentes. No rechaza el plugin.

Es una de las dos únicas violaciones de esquema no fatales del manifiesto. La otra son los campos desconocidos, que vemos ahora.

7. Campos desconocidos y campos ausentes

7.1 Un campo de nivel superior desconocido no mata el plugin

Este es el comportamiento más contraintuitivo del manifiesto, y está escrito con toda deliberación:

“If plugin.json contains any other top-level field, it does not conform to the schema. Clients MUST report and ignore each unknown field and MUST continue loading the plugin if the manifest otherwise satisfies this section. Clients MUST NOT assign semantics to unknown fields.”

Descompuesto en tres obligaciones:

  1. El campo desconocido es una violación de esquema. No se legaliza.
  2. El cliente DEBE reportarlo e ignorarlo, y DEBE seguir cargando el plugin si el resto del manifiesto está bien.
  3. El cliente NO DEBE asignarle semántica. Nada de “veo commands, lo interpreto como…”.

El razonamiento aparece en Design Decisions: los campos desconocidos siguen siendo violaciones de esquema, pero los clientes los reportan e ignoran en vez de rechazar un plugin por lo demás válido. Se gana tolerancia sin abrir la puerta a extensiones de facto en el espacio de nombres raíz.

7.2 Un campo obligatorio ausente sí mata el plugin

La sección 5.3 no deja margen:

“If a required field is missing, has the wrong type, is empty, or otherwise violates its requirements, the manifest is invalid. Clients MUST reject the plugin and MUST NOT discover or execute any of its components. Clients SHOULD report which required field is invalid.”

Cuatro causas —ausente, tipo incorrecto, vacío, o violación de sus requisitos— y una consecuencia: rechazo total. Ni skills, ni servidores MCP, nada.

7.3 Campos opcionales ausentes

No pasa nada. Un manifiesto sin version, sin author y sin keywords es perfectamente conformante. El manifiesto mínimo de la especificación es exactamente eso:

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "minimal-plugin"
}

La especificación no define valores por defecto para los campos opcionales ausentes. Un campo ausente es ausente, no es cadena vacía.

7.4 La matriz de fatalidad completa

SituaciónConsecuencia normativa
Campo de nivel superior desconocidoNo fatal: reportar, ignorar, continuar
extensions no es un objetoNo fatal: reportar, ignorar el campo, continuar cargando componentes
Namespace de extensions no implementadoNo fatal: ignorar sin validar su contenido
$schema ausenteFatal: rechazar el plugin
$schema con versión no soportada ni reconocida como compatibleFatal: rechazar y SHOULD reportar la versión
name ausente, vacío o que viola la nomenclaturaFatal: rechazar el plugin
Campo permitido con tipo JSON equivocadoFatal: rechazar el plugin
Campo desconocido dentro de authorFatal: rechazar el plugin
plugin.json no es JSON válidoFatal: rechazar el plugin
plugin.json no resuelve dentro del plugin rootFatal: rechazar el plugin

La sección 11.3 lo resume en una frase: “An unknown top-level field or a non-object extensions field is non-fatal under §5.2 and §8.1. Any other plugin.json schema violation is fatal to the plugin.”

flowchart TD
    A["Violacion detectada en plugin.json"] --> B{"Es un campo top-level desconocido"}
    B -- Si --> N["No fatal: reportar, ignorar, continuar"]
    B -- No --> C{"Es extensions con tipo distinto de objeto"}
    C -- Si --> N
    C -- No --> F["Fatal: rechazar el plugin y no ejecutar ningun componente"]

8. Manifiestos válidos e inválidos, con la razón exacta

8.1 El manifiesto completo de la especificación

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "plugin-name",
  "version": "1.2.0",
  "description": "Brief plugin description",
  "author": {
    "name": "Author Name",
    "email": "[email protected]",
    "url": "https://example.com"
  },
  "homepage": "https://docs.example.com/plugin",
  "repository": "https://github.com/example/plugin",
  "license": "MIT",
  "keywords": ["keyword1", "keyword2"],
  "extensions": {
    "com.example.client": {
      "setting": true
    }
  }
}

Los diez campos permitidos, cada uno con su tipo correcto.

8.2 Válido aunque parezca raro

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "a",
  "version": "build-2026-08-07",
  "license": "una licencia propietaria sin identificador",
  "author": {},
  "keywords": []
}

Por qué es válido, campo a campo:

  • name: "a" — un carácter alfanumérico cumple longitud, juego de caracteres, inicio/fin y no repetición.
  • version no es SemVer, pero el cliente NO DEBE rechazar por eso.
  • license no es SPDX; misma regla.
  • author: {} — objeto sin propiedades; ninguna de las tres es requerida.
  • keywords: [] — array vacío de strings; el esquema no impone minItems.

8.3 Válido con advertencias reportadas

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "tolerant-plugin",
  "commands": ["deploy", "rollback"],
  "extensions": "com.example.client"
}

El cliente carga este plugin y descubre sus componentes. Reporta dos problemas:

  • commands es un campo de nivel superior desconocido: violación de esquema no fatal, se reporta y se ignora, y el cliente NO DEBE asignarle semántica alguna.
  • extensions es una cadena, no un objeto: la excepción de la sección 8.1 aplica, se reporta y se ignora el campo.

8.4 Inválido: $schema ausente

{
  "name": "sin-schema"
}

Razón exacta: $schema está en required. Un campo obligatorio ausente hace el manifiesto inválido. Fatal.

8.5 Inválido: $schema con otro valor

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "name": "schema-equivocado"
}

Razón exacta: el esquema declara $schema como const con el identificador canónico de Agent Plugins 1.0.0. Cualquier otra cadena falla. Y por la sección 5.2, si el cliente no soporta la versión declarada, DEBE rechazar el plugin y DEBERÍA reportar la versión no soportada.

8.6 Inválido: name con mayúsculas

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "Deploy-Tools"
}

Razón exacta: el juego de caracteres permitido es a-z, 0-9, - y .. D y T quedan fuera del patrón. Fatal.

8.7 Inválido: name con separadores repetidos

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "acme..tools"
}

Razón exacta: puntos consecutivos. El lookahead negativo (?!.*(?:--|\.\.)) rechaza la cadena completa. Lo mismo con has--double. Fatal.

8.8 Inválido: author con un campo extra

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "author-extra",
  "author": {
    "name": "Artiko",
    "github": "artiko"
  }
}

Razón exacta: author es un objeto cerrado que PUEDE contener solo name, email y url. Cualquier otro campo hace el manifiesto inválido. Ojo con la asimetría: un campo desconocido en la raíz es no fatal, pero un campo desconocido dentro de author es fatal. La excepción de la sección 5.2 aplica exclusivamente al nivel superior.

8.9 Inválido: tipo equivocado en un campo permitido

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "tipos-mal",
  "keywords": "cli,deploy",
  "version": 2
}

Razones exactas: keywords debe ser un array de strings, no una cadena. version debe ser string, no número. Ninguna de las dos entra en las excepciones no fatales. Fatal.

8.10 Inválido: un valor de extensions que no es objeto

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "ext-mal",
  "extensions": {
    "com.example.client": "activado"
  }
}

Distínguelo del caso 8.3. Ahí extensions entero no era un objeto, y eso está cubierto por la excepción no fatal. Aquí extensions sí es un objeto, pero uno de sus valores de miembro no lo es. El esquema exige additionalProperties: {"type": "object"} y la sección 8.1 dice que los valores de miembro DEBEN ser objetos. Es una violación de esquema que no cae en ninguna de las dos excepciones: fatal.

9. El manifiesto no configura componentes

Vale la pena cerrar con lo que plugin.json no hace, porque es tan definitorio como lo que hace.

La sección 6.1 lo dice sin rodeos: plugin.json cannot override these locations or contain inline component configuration.” Los componentes se descubren en ubicaciones fijas —skills/ y mcp.json— y el manifiesto no puede cambiarlas ni sustituirlas.

Y la sección 7.2.1 lo repite para MCP: la configuración MCP NO DEBE declararse inline en plugin.json ni cargarse desde ninguna ruta core alternativa.

Así que no busques en el manifiesto un array de skills ni un objeto mcpServers. No existen, y añadirlos los convierte en campos desconocidos que el cliente reporta e ignora.

flowchart LR
    M["plugin.json"] --> I["Identidad y metadatos del paquete"]
    S["skills/"] --> C1["Componentes: Agent Skills"]
    J["mcp.json"] --> C2["Componentes: servidores MCP"]
    I -.- X["El manifiesto no configura componentes"]

El capítulo 4 entra en skills/ y el capítulo 5 en mcp.json.

10. Cómo validar esto tú mismo

El identificador canónico de $schema no se descarga en tiempo de carga, pero sí puedes usar el archivo del repositorio oficial en tu pipeline de desarrollo. Cualquier validador JSON Schema draft 2020-12 sirve:

# Con ajv-cli, contra una copia local del esquema oficial
ajv validate \
  -s schemas/1.0.0/plugin.schema.json \
  -d my-plugin/plugin.json \
  --spec=draft2020

Recuerda las dos limitaciones. Primero, el esquema no expresa todo el contrato: reglas como el containment de rutas, el orden de carga o la coincidencia de versión entre plugin.json y mcp.json viven solo en la prosa normativa. Segundo, un validador genérico te dirá “campo adicional no permitido” ante un campo desconocido de nivel superior, mientras que un cliente conformante debe reportarlo e ignorarlo sin rechazar el plugin. Tu validador de CI puede ser más estricto que el cliente a propósito; el cliente no puede serlo. El capítulo 8 desarrolla esta distinción con automatización.

Para ver manifiestos reales en un catálogo publicado, el curso hermano de Google Skills recorre plugins y skills del repositorio público de Google.

Resumen

  • plugin.json en la raíz del plugin es el piso de conformidad. Hay exactamente uno por plugin y ningún otro archivo puede reemplazar, complementar ni sobrescribir sus campos core.
  • El esquema es cerrado: solo diez campos de nivel superior, $schema, name, version, description, author, homepage, repository, license, keywords y extensions.
  • Obligatorios: $schema y name. $schema DEBE ser exactamente https://agent-plugins.org/schemas/1.0.0/plugin.schema.json, y los clientes NO DEBEN descargarlo al cargar un plugin.
  • name: 1 a 64 caracteres, solo a-z, 0-9, - y ., inicio y fin alfanuméricos, sin -- ni .. consecutivos.
  • Los metadatos se validan solo por su tipo JSON. Un cliente NO DEBE rechazar un manifiesto porque version no sea SemVer, license no sea SPDX o las URLs no parsen.
  • author es un objeto cerrado con name, email y url. Un campo extra ahí dentro es fatal, a diferencia de un campo extra en la raíz.
  • Solo dos violaciones de esquema son no fatales: un campo de nivel superior desconocido y un extensions que no sea objeto. En ambos casos el cliente reporta, ignora y sigue. Cualquier otra violación hace que el cliente rechace el plugin y no descubra ni ejecute ninguno de sus componentes.
  • El manifiesto describe el paquete; no configura componentes. Skills y servidores MCP se descubren en skills/ y mcp.json, y plugin.json no puede alterar esas ubicaciones ni declarar configuración inline.

Siguiente: Empaquetar Agent Skills dentro de un plugin