plugin.json: el manifiesto al detalle
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, andextensions.”
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
| Campo | Tipo | Obligatorio | Restricciones del esquema | Para qué sirve |
|---|---|---|---|---|
$schema | string | Sí | const con el identificador canónico | Declara la versión de Agent Plugins que el paquete apunta |
name | string | Sí | minLength: 1, maxLength: 64, patrón de nomenclatura | Nombre legible del plugin |
version | string | No | solo type: string | Cadena de versión; SemVer RECOMENDADO |
description | string | No | solo type: string | Descripción breve del propósito |
author | object | No | objeto cerrado con name, email, url | Datos del autor |
homepage | string | No | solo type: string | URL de documentación o página |
repository | string | No | solo type: string | URL del repositorio fuente |
license | string | No | solo type: string | Identificador de licencia; SPDX RECOMENDADO |
keywords | string[] | No | array de strings | Etiquetas de búsqueda y descubrimiento |
extensions | object | No | valores de miembro deben ser objetos | Datos 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ón | Requisito | Detalle |
|---|---|---|
| Longitud | 1 a 64 caracteres | El nombre DEBE medir entre 1 y 64 caracteres, ambos inclusive |
| Juego de caracteres | a-z, 0-9, -, . | Solo alfanuméricos en minúscula, guiones y puntos |
| Inicio y fin | Alfanumérico | El primer y el último carácter DEBEN ser alfanuméricos |
| Repetición | Ni -- 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:
| Nombre | Razón del rechazo |
|---|---|
My-Plugin | Mayúsculas: el juego de caracteres es solo a-z |
-start | Guion inicial: el primer carácter debe ser alfanumérico |
has--double | Guiones consecutivos |
too.many..dots | Puntos consecutivos |
| cadena vacía | Incumple 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:
versionno es un Semantic Versioning válido.homepage,repositoryoauthor.urlno son URLs reconocidas.author.emailno es una dirección de correo reconocida.licenseno 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:
| Segmento | Significado | Descripción |
|---|---|---|
| Major | Cambio incompatible | Cambio de comportamiento o de esquema no compatible |
| Minor | Funcionalidad compatible | Comportamiento nuevo sin romper clientes ni usuarios existentes |
| Patch | Corrección compatible | Cambio 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
authorobject MAY contain only thename,urlfields, 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.comusacom.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.jsoncontains 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:
- El campo desconocido sí es una violación de esquema. No se legaliza.
- El cliente DEBE reportarlo e ignorarlo, y DEBE seguir cargando el plugin si el resto del manifiesto está bien.
- 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ón | Consecuencia normativa |
|---|---|
| Campo de nivel superior desconocido | No fatal: reportar, ignorar, continuar |
extensions no es un objeto | No fatal: reportar, ignorar el campo, continuar cargando componentes |
Namespace de extensions no implementado | No fatal: ignorar sin validar su contenido |
$schema ausente | Fatal: rechazar el plugin |
$schema con versión no soportada ni reconocida como compatible | Fatal: rechazar y SHOULD reportar la versión |
name ausente, vacío o que viola la nomenclatura | Fatal: rechazar el plugin |
| Campo permitido con tipo JSON equivocado | Fatal: rechazar el plugin |
Campo desconocido dentro de author | Fatal: rechazar el plugin |
plugin.json no es JSON válido | Fatal: rechazar el plugin |
plugin.json no resuelve dentro del plugin root | Fatal: 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.versionno es SemVer, pero el cliente NO DEBE rechazar por eso.licenseno es SPDX; misma regla.author: {}— objeto sin propiedades; ninguna de las tres es requerida.keywords: []— array vacío de strings; el esquema no imponeminItems.
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:
commandses 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.extensionses 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.jsonen 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,keywordsyextensions. - Obligatorios:
$schemayname.$schemaDEBE ser exactamentehttps://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, soloa-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
versionno sea SemVer,licenseno sea SPDX o las URLs no parsen. authores un objeto cerrado conname,emailyurl. 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
extensionsque 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/ymcp.json, yplugin.jsonno puede alterar esas ubicaciones ni declarar configuración inline.
Siguiente: Empaquetar Agent Skills dentro de un plugin