Distribuir e instalar plugins
Distribuir e instalar plugins
Ya tienes un plugin válido: manifiesto conforme, skills en skills/, quizá un
mcp.json, y un pipeline de validación como el del
capítulo 8. Falta la última
pieza: hacer que otra persona lo instale. Aquí empieza el terreno donde más se
inventa, así que conviene fijar primero el dato duro que lo ordena todo.
1. Lo que la especificación dice sobre distribución: nada
Una búsqueda literal sobre el texto completo de spec/1.0.0.md:
| Término | Ocurrencias en la especificación |
|---|---|
marketplace | 0 |
catalog | 0 |
registry | 2, ambas en sentido negativo |
install | 4, ninguna define un comando |
Las dos apariciones de registry sirven para descartar la idea de registro
central. Están en Design Decisions, sección que la especificación marca como
no normativa —“Appendix A and Design Decisions are non-normative”—:
Plugins use filesystem directories as the package unit rather than archive formats (
.zip,.tar.gz) or registry-fetched bundles.
Reverse-domain identifiers provide a decentralized convention for avoiding collisions without requiring a central client-name registry.
Las cuatro apariciones de install son de ciclo de vida del cliente, no de línea
de comandos. La más explícita está en la tabla de terminología (§3):
Client — Plugin runtime — A tool that discovers, installs, loads, and executes plugin components.
La especificación reconoce que alguien instala y le pone nombre —el cliente—, pero no define cómo lo hace.
Conclusión que hay que sostener durante todo el capítulo: el concepto de
“marketplace” no existe en Agent Plugins 1.0.0. Todo lo que leas más abajo sobre
catálogos, marketplace.json o comandos de instalación es convención de un
producto concreto, no estándar.
1.1 Lo único normativo que toca el empaquetado
La especificación fija una cosa sobre el formato del paquete, en §4.1:
A plugin is a directory rooted at a single filesystem location.
Un plugin es un directorio. No un .zip, no un .tar.gz, no un bundle
descargado de un registro. La justificación aparece en Design Decisions:
This keeps plugins inspectable with standard tools (
ls,cat,git), editable in-place during development, and compatible with version control without special tooling.
Fíjate en la mención explícita de git: la especificación no obliga (MUST) a
usarlo, pero el formato está diseñado para que un repositorio git sea el vehículo
natural.
2. El repositorio git como unidad de distribución
Publicar un plugin es publicar un directorio. Hay dos formas de hacerlo y las dos se usan en la práctica.
2.1 Un plugin por repositorio: la raíz es la raíz del plugin
La forma más simple. El plugin.json vive en la raíz del repositorio, así que la
raíz del repositorio es la raíz del plugin (plugin root, en términos de §3).
mi-plugin/
├── plugin.json
├── mcp.json
├── skills/
│ └── revisar-pr/
│ ├── SKILL.md
│ └── references/
│ └── checklist.md
├── LICENSE
└── README.md
Un git clone deja exactamente el plugin, los tags sirven directamente como
versiones, y cualquier herramienta que sepa “clona este repo y lee plugin.json”
funciona. Además, la contención de rutas de §4.1.3 coincide con la frontera del
repositorio, así que no hay ambigüedad.
2.2 Varios plugins por repositorio: el monorepo
También puedes publicar varios plugins desde un mismo repositorio, cada uno en su
subdirectorio, cada uno con su propio plugin.json:
mis-plugins/
├── README.md
├── plugins/
│ ├── revisor/
│ │ ├── plugin.json
│ │ └── skills/
│ │ └── revisar-pr/
│ │ └── SKILL.md
│ └── despliegue/
│ ├── plugin.json
│ ├── mcp.json
│ └── skills/
│ └── publicar/
│ └── SKILL.md
└── .github/workflows/validar-plugins.yml
Aquí la raíz del repositorio no es la raíz de ningún plugin. Hay dos raíces
de plugin: plugins/revisor/ y plugins/despliegue/. Y de ahí una consecuencia
que se olvida a menudo: el directorio contenedor plugins/ no es un plugin.
No tiene plugin.json, así que ningún cliente conformante lo cargará como tal.
Es organización de repo, no formato.
Lo mismo aplica al anidamiento de skills. Si tienes una carpeta
skills/cloud/bigquery-basics/SKILL.md, ese SKILL.md está a dos niveles de
skills/, y §7.1 es tajante:
Clients MUST NOT recursively search deeper descendants for additional skills.
Dentro de un plugin, solo los hijos inmediatos de skills/ que contengan un
SKILL.md cuentan como skills. Un layout de repo con categorías intermedias es
válido siempre que ese repo no sea un plugin, como veremos con google/skills.
2.3 Fijar versiones con tags
La especificación define el campo version del manifiesto (§5.4) y RECOMIENDA
(RECOMMENDED) SemVer, pero es explícita en que el cliente NO DEBE (MUST NOT)
rechazar un manifiesto solo porque version no sea SemVer válido; lo repasamos
en el capítulo 7.
En distribución, ese version se refleja como un tag de git, y es el tag lo que
consume quien instala:
git tag -a 1.2.0 -m "Version 1.2.0"
git push origin 1.2.0
Todos los catálogos reales de la siguiente sección funcionan así: apuntan a un
repositorio y a una referencia concreta. Nunca a main.
3. Catálogos de plugins: qué son y qué no son
Un catálogo —o marketplace, en la jerga de varios productos— es un archivo JSON que lista plugins y dice de dónde bajar cada uno: traduce un nombre corto a un repositorio más una referencia concreta, y de paso permite descubrir un plugin sin que te pasen la URL por privado.
Esa traducción es justamente lo que la especificación no define, así que cada harness inventa su formato. Y como vas a ver, un mismo repositorio puede publicar dos catálogos distintos para dos harnesses distintos.
4. Caso real: el catálogo de google/skills
Diseccionemos un catálogo real. Los datos están verificados sobre el commit
92aa428 de google/skills: si lo abres hoy las cifras pueden haber cambiado,
el mecanismo no.
4.1 Qué es ese repositorio
Tres hechos verificados que evitan malentendidos. Uno: en la raíz de
google/skills no hay plugin.json, así que el repositorio no es un plugin;
es un repositorio de catálogo y de skills sueltas. Dos: bajo skills/ hay 103
archivos SKILL.md organizados por área —skills/cloud/, skills/ads/,
skills/analytics/—, que son Agent Skills, no plugins; su formato lo cubre el
curso de Agent Skills. Tres: en todo
el repositorio no existe ningún plugin.json ni mcp.json, porque un catálogo
no contiene los plugins, contiene punteros a ellos.
El recorrido completo del repositorio está en el curso de Google Skills; aquí nos quedamos con la parte de distribución.
4.2 El archivo .claude-plugin/marketplace.json
Estructura de alto nivel, transcrita del archivo real:
{
"name": "google-plugins",
"owner": {
"name": "Google LLC"
},
"metadata": {
"version": "0.0.1",
"description": "A central repository of official skills and prescriptive plugins designed to provide a high-performance experience for building and deploying agentic applications across Google products and technologies."
},
"plugins": [ ]
}
El array plugins tiene 16 entradas. Los campos que aparecen:
| Campo | Qué es |
|---|---|
name | Identificador del catálogo. Es el sufijo al instalar: <plugin>@google-plugins |
owner | Solo trae name. No hay email ni url |
metadata | version del catálogo, no de los plugins, y description |
plugins[].name | El nombre con el que se instala |
plugins[].source | De dónde se obtiene el plugin |
plugins[].description | Texto de catálogo |
Y lo que no aparece: no hay version por plugin, ni author, ni license,
ni homepage, ni keywords. La versión se codifica dentro de source.ref.
4.3 Los dos tipos de source que usa el archivo
Tipo A — github. Lo usan 15 de las 16 entradas. Es el caso “un plugin por
repositorio” de la sección 2.1:
{
"name": "alloydb",
"source": {
"source": "github",
"repo": "gemini-cli-extensions/alloydb",
"ref": "0.2.0"
},
"description": "Create, connect, and interact with an AlloyDB for PostgreSQL database and data."
}
Tres claves: source con valor github, repo en formato owner/nombre y
ref con el tag.
Tipo B — git-subdir. Lo usa 1 de las 16. Es el caso “varios plugins por
repositorio” de la sección 2.2: el plugin no está en la raíz sino en un
subdirectorio:
{
"name": "db-context-engineering",
"source": {
"source": "git-subdir",
"url": "GoogleCloudPlatform/db-context-enrichment",
"path": "plugin",
"ref": "v0.6.0"
},
"description": "Context Engineering Agent for generating and maintaining QueryData / Conversational Analytics API context sets."
}
Aquí path: "plugin" dice que la raíz del plugin es el subdirectorio plugin/
de ese repositorio: ahí vive el plugin.json.
Un detalle honesto del archivo real: en git-subdir la clave se llama url pero
el valor es owner/repo, no una URL. Es una inconsistencia del propio catálogo,
y la menciono porque ilustra el punto: estos formatos son de producto, no están
fijados por ninguna especificación, y por eso tienen esas costuras.
4.4 Las 16 entradas
name en el catálogo | source.source | repositorio | ref |
|---|---|---|---|
alloydb | github | gemini-cli-extensions/alloydb | 0.2.0 |
alloydb-omni | github | gemini-cli-extensions/alloydb-omni | 0.2.1 |
bigtable | github | GoogleCloudPlatform/cloud-bigtable-ecosystem | v0.4.0 |
cloud-sql-mysql | github | gemini-cli-extensions/cloud-sql-mysql | 0.2.0 |
cloud-sql-postgresql | github | gemini-cli-extensions/cloud-sql-postgresql | 0.4.0 |
cloud-sql-sqlserver | github | gemini-cli-extensions/cloud-sql-sqlserver | 0.2.0 |
firestore-native | github | gemini-cli-extensions/firestore-native | 0.3.1 |
data-agent-kit-starter-pack | github | gemini-cli-extensions/data-agent-kit-starter-pack | 0.6.1 |
google-cloud-storage | github | gemini-cli-extensions/google-cloud-storage | 1.2.0 |
looker | github | gemini-cli-extensions/looker | 0.3.5 |
oracledb | github | gemini-cli-extensions/oracledb | 0.2.3 |
spanner | github | gemini-cli-extensions/spanner | 0.3.1 |
knowledge-catalog | github | gemini-cli-extensions/knowledge-catalog | 0.5.2 |
dataproc | github | gemini-cli-extensions/dataproc | 0.1.0 |
bigquery | github | gemini-cli-extensions/bigquery-data-analytics | 0.2.1 |
db-context-engineering | git-subdir | GoogleCloudPlatform/db-context-enrichment con path: plugin | v0.6.0 |
Dato que se aprende leyendo la tabla: el nombre del catálogo no tiene por qué
coincidir con el del repositorio. Tres contraejemplos en la misma lista:
bigtable apunta a cloud-bigtable-ecosystem, bigquery a
bigquery-data-analytics y db-context-engineering a db-context-enrichment.
El catálogo es una capa de indirección y puede renombrar. Como además el name
del catálogo no tiene por qué ser el name del plugin.json, acabas con tres
nombres para la misma cosa: repositorio, catálogo y manifiesto. Solo el último
está normado, por §5.5.
4.5 El segundo catálogo: .agents/plugins/marketplace.json
Aquí viene el dato que casi nadie menciona: el mismo repositorio publica dos
manifiestos de catálogo, con esquemas distintos, para harnesses distintos. El
segundo cambia hasta los metadatos de cabecera —interface.displayName en lugar
de owner y metadata— y añade campos por entrada:
{
"name": "alloydb",
"source": {
"source": "url",
"url": "https://github.com/gemini-cli-extensions/alloydb.git",
"ref": "0.2.0"
},
"description": "Create, connect, and interact with an AlloyDB for PostgreSQL database and data.",
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Databases"
}
Comparación directa de los dos catálogos del mismo repositorio:
| Aspecto | .claude-plugin/marketplace.json | .agents/plugins/marketplace.json |
|---|---|---|
| Número de plugins | 16 | 15 |
| Metadatos del catálogo | owner, metadata con version y description | interface con displayName |
Valor de source.source | github o git-subdir | url en las 15 entradas |
| Cómo identifica el repo | repo: "owner/nombre" | url: "https://github.com/....git" |
| Campos extra por plugin | ninguno | policy y category |
La diferencia de 15 contra 16 no es un redondeo: el catálogo .agents/ no
incluye db-context-engineering, que es precisamente la única entrada
git-subdir. Lectura razonable: ese harness no soporta plugins en subdirectorio.
Los valores observados de policy son installation: "AVAILABLE" y
authentication: "ON_INSTALL" en las 15 entradas; los de category son seis,
desde Databases hasta Data Warehousing & Data Lakes. Ninguno existe en Agent
Plugins: policy.authentication cubre justo el terreno que
FUTURE_CONSIDERATIONS.md reconoce como fuera de 1.0.0 —permisos,
consentimiento, confianza— y category es taxonomía de catálogo, mientras que el
manifiesto solo tiene keywords, que describe al plugin y no a la tienda.
5. Los comandos de instalación, tal como los documenta el README
El README de google/skills publica una tabla con tres harnesses. Transcripción
literal:
Agent harness Install Claude Code claude plugin marketplace add google/skills, thenclaude plugin install <plugin>@google-pluginsCodex codex plugin marketplace add google/skills, then install from the/pluginsbrowserAntigravity CLI agy plugin install https://github.com/google/skills/<plugin-path>
Los tres modelos de distribución son distintos. Vamos uno por uno.
5.1 Claude Code: registrar catálogo, luego instalar por nombre
claude plugin marketplace add google/skills
claude plugin install alloydb@google-plugins
El primer paso registra el catálogo; el segundo instala una entrada de él. El
sufijo @google-plugins no es arbitrario: sale del campo raíz
"name": "google-plugins" de .claude-plugin/marketplace.json. Y el nombre a la
izquierda de la arroba tiene que ser uno de los 16 plugins[].name de §4.4.
flowchart LR
A[claude plugin marketplace add<br/>google/skills] --> B[Lee .claude-plugin/marketplace.json<br/>y registra el catalogo google-plugins]
B --> C[claude plugin install<br/>alloydb arroba google-plugins]
C --> D[Busca la entrada alloydb]
D --> E[Resuelve source github<br/>repo gemini-cli-extensions/alloydb<br/>ref 0.2.0]
E --> F[Obtiene el directorio<br/>y lee su plugin.json]
Solo la última caja es Agent Plugins. Las cuatro anteriores son Claude Code.
5.2 Codex: registrar catálogo, luego selector interactivo
codex plugin marketplace add google/skills
Después, según el README, se instala “from the /plugins browser”. El README
no documenta un codex plugin install <x> no interactivo.
5.3 Antigravity CLI: instalación directa por ruta de repositorio
La forma genérica del README raíz es
agy plugin install https://github.com/google/skills/<plugin-path>: sin
catálogo, apuntando a una ruta dentro de un repositorio. El README de
plugins/cloud/data-agent-kit/ da dos ejemplos concretos:
agy plugin install https://github.com/google/skills/plugins/cloud/data-agent-kit/alloydb
agy plugin install https://github.com/google/skills/plugins/cloud/data-agent-kit/spanner
5.4 La tercera vía: submódulos git como parche
¿Por qué existe el directorio plugins/cloud/data-agent-kit/ si los plugins ya
están en el catálogo? La respuesta está literal en su README:
Submodules are used here because Antigravity CLI does not yet support a marketplace manifest (as Claude Code and Codex do). Once marketplace support lands for
agy, these submodules can be retired in favor of the shared manifest.
Ese directorio existe solo porque un harness todavía no lee catálogos: es un
parche, declarado como tal por sus propios autores. El mecanismo es vendorizar
cada plugin como submódulo git anclado a un tag, declarado en el .gitmodules
de la raíz:
[submodule "alloydb"]
path = plugins/cloud/data-agent-kit/alloydb
url = https://github.com/gemini-cli-extensions/alloydb
branch = 0.2.0
El README es explícito sobre quién manda:
Each submodule pins to a specific release tag of its upstream plugin repository, which remains the source of truth for that plugin’s skills and MCP server definition.
Y documenta el procedimiento exacto para subir una versión fijada: hacer
git checkout <new-version> dentro del submódulo, actualizar
submodule.<plugin>.branch en .gitmodules con
git config -f .gitmodules, y commitear ambos cambios juntos.
Dos advertencias sobre este directorio. Primera: data-agent-kit no es un
plugin, es un contenedor sin plugin.json; los plugins son los submódulos de
dentro. Segunda: si clonas sin inicializar submódulos, esos 16 directorios están
vacíos en disco, así que cualquier afirmación sobre sus manifiestos hecha desde
una copia sin inicializar sería inventada; para materializarlos hace falta
git submodule update --init.
5.5 Dos cosas que el README no dice
El mismo README documenta un comando que conviene no confundir:
npx skills add google/skills
Eso instala skills, no plugins: es tooling del ecosistema Agent Skills
—skills.sh—, un flujo distinto en el mismo archivo. Un plugin puede
empaquetar skills, como vimos en el
capítulo 4, pero instalar una
skill suelta y instalar un plugin son operaciones distintas.
Y por rigor: el README no documenta desinstalación, actualización ni listado
para ninguno de los tres harnesses, ni ningún gemini extensions install, pese a
que 15 de los 16 repositorios upstream viven bajo gemini-cli-extensions.
6. La frontera, en una tabla
La tabla que deberías poder reconstruir de memoria al terminar el capítulo:
| Elemento | Qué es realmente |
|---|---|
Plugin es un directorio con plugin.json en su raíz | Especificación, §4.1 |
Ubicaciones fijas skills/ y mcp.json | Especificación, §6.1 |
| Contención de rutas dentro de la raíz del plugin | Especificación, §4.1.3 |
PLUGIN_DATA se preserva entre actualizaciones | Especificación, §9.1 |
El campo version del manifiesto | Especificación, §5.4 |
.claude-plugin/marketplace.json y .agents/plugins/marketplace.json | Formatos de catálogo de dos harnesses |
source.source, repo, path, ref | Resolución de origen de cada harness |
policy, category | Política y taxonomía de producto |
claude plugin marketplace add / install | CLI de Claude Code |
codex plugin marketplace add + /plugins | CLI de Codex |
agy plugin install <url> | CLI de Antigravity |
npx skills add | Tooling de Agent Skills, no de Agent Plugins |
Submódulos en plugins/cloud/data-agent-kit/ y layout skills/<área>/<skill>/ | Convención de ese repositorio |
6.1 Un error tentador que conviene desactivar
Suena razonable decir: “.claude-plugin/ es el directorio de extensión de
cliente que define §8”. Es falso, por dos razones independientes. Primera: §3
define un directorio de extensión como aquel cuyo nombre es exactamente un
namespace de dominio invertido —el ejemplo de la propia spec es
com.example.client— y ni .claude-plugin ni .agents lo son. Segunda: §8
aplica dentro de un plugin, y google/skills no es un plugin. Repasamos las
extensiones de cliente en el
capítulo 3.
7. Checklist para publicar tu propio plugin
Obligatorio por la especificación: plugin.json en la raíz con $schema y
name conforme a §5.5; ningún archivo referenciado escapa de la raíz; si hay
mcp.json, declara la misma versión de spec que plugin.json; sin credenciales
embebidas, que §9.2 prohíbe (MUST NOT) en env y §7.2.1 en headers.
Recomendable para distribución:
-
versionen SemVer y un tag de git por versión publicada. -
repository,homepageylicenserellenos en el manifiesto. - Validación en CI con los JSON Schema, como en el capítulo 8.
- README con instrucciones de instalación por harness, porque no hay una forma estándar.
Si además publicas un catálogo: un manifiesto por harness soportado, en la
ruta y con el nombre de archivo que ese harness espera; cada entrada fijada a un
ref concreto, nunca a main.
8. Errores frecuentes
“Publico mi plugin como .zip en releases”. El paquete es un directorio
(§4.1) y Design Decisions descarta los formatos comprimidos y los bundles de
registro. “El catálogo valida mi plugin”. No: un catálogo lista y resuelve
orígenes; quien valida el manifiesto es el cliente al cargarlo, según §5.2 y
§5.3.
“Apunto el catálogo a main para estar siempre al día”. Ningún catálogo real
hace eso: las 31 entradas de los dos manifiestos de google/skills fijan un
ref. Sin ref, quien instala no sabe qué recibe.
“Pongo un plugin.json en la raíz del monorepo”. Eso convierte la raíz en un
plugin —probablemente vacío— y el resto en subdirectorios suyos. Si el
repositorio contiene varios plugins, la raíz no lleva manifiesto.
“El name del marketplace es el name del plugin”. No necesariamente: son
tres espacios de nombres —repositorio, catálogo y manifiesto— y solo el último
tiene reglas normadas.
Resumen
- La especificación Agent Plugins 1.0.0 no define distribución:
marketplaceycatalogaparecen 0 veces en su texto, y las 2 deregistrydescartan la idea de registro central. - Lo único que fija es que el paquete es un directorio (§4.1), pensado para
ser inspeccionable con
ls,catygit. - Distribuir es publicar ese directorio: un plugin por repositorio, o varios en
subdirectorios de un monorepo. En el segundo caso el contenedor no es un
plugin, porque no tiene
plugin.json. - Un catálogo traduce un nombre a un repositorio más una
ref.google/skillspublica dos catálogos distintos —16 entradas en.claude-plugin/marketplace.json, 15 en.agents/plugins/marketplace.json— con esquemas y campos diferentes. Los tipos desourceobservados songithub,git-subdiryurl, y ninguno lo define la especificación. - Los comandos reales del README son
claude plugin marketplace addmásclaude plugin install <plugin>@google-plugins,codex plugin marketplace addmás el selector/plugins, yagy plugin install <url>. Los tres son CLIs de producto. - Los submódulos de
plugins/cloud/data-agent-kit/son un parche declarado para un harness que aún no lee catálogos, no parte de ningún estándar. policy,categoryuownerson campos de catálogo. El manifiesto solo admite los 10 campos raíz del esquema cerrado de §5.2.
Siguiente: Gobernanza, hoja de ruta y cómo contribuir