Distribuir e instalar plugins

Por: Artiko
agent-skillsplugins-de-agentesia-agentesagent-pluginsdistribucionmarketplacegoogle-skillsgit

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érminoOcurrencias en la especificación
marketplace0
catalog0
registry2, ambas en sentido negativo
install4, 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:

CampoQué es
nameIdentificador del catálogo. Es el sufijo al instalar: <plugin>@google-plugins
ownerSolo trae name. No hay email ni url
metadataversion del catálogo, no de los plugins, y description
plugins[].nameEl nombre con el que se instala
plugins[].sourceDe dónde se obtiene el plugin
plugins[].descriptionTexto 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álogosource.sourcerepositorioref
alloydbgithubgemini-cli-extensions/alloydb0.2.0
alloydb-omnigithubgemini-cli-extensions/alloydb-omni0.2.1
bigtablegithubGoogleCloudPlatform/cloud-bigtable-ecosystemv0.4.0
cloud-sql-mysqlgithubgemini-cli-extensions/cloud-sql-mysql0.2.0
cloud-sql-postgresqlgithubgemini-cli-extensions/cloud-sql-postgresql0.4.0
cloud-sql-sqlservergithubgemini-cli-extensions/cloud-sql-sqlserver0.2.0
firestore-nativegithubgemini-cli-extensions/firestore-native0.3.1
data-agent-kit-starter-packgithubgemini-cli-extensions/data-agent-kit-starter-pack0.6.1
google-cloud-storagegithubgemini-cli-extensions/google-cloud-storage1.2.0
lookergithubgemini-cli-extensions/looker0.3.5
oracledbgithubgemini-cli-extensions/oracledb0.2.3
spannergithubgemini-cli-extensions/spanner0.3.1
knowledge-cataloggithubgemini-cli-extensions/knowledge-catalog0.5.2
dataprocgithubgemini-cli-extensions/dataproc0.1.0
bigquerygithubgemini-cli-extensions/bigquery-data-analytics0.2.1
db-context-engineeringgit-subdirGoogleCloudPlatform/db-context-enrichment con path: pluginv0.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 plugins1615
Metadatos del catálogoowner, metadata con version y descriptioninterface con displayName
Valor de source.sourcegithub o git-subdirurl en las 15 entradas
Cómo identifica el reporepo: "owner/nombre"url: "https://github.com/....git"
Campos extra por pluginningunopolicy 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 harnessInstall
Claude Codeclaude plugin marketplace add google/skills, then claude plugin install <plugin>@google-plugins
Codexcodex plugin marketplace add google/skills, then install from the /plugins browser
Antigravity CLIagy 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:

ElementoQué es realmente
Plugin es un directorio con plugin.json en su raízEspecificación, §4.1
Ubicaciones fijas skills/ y mcp.jsonEspecificación, §6.1
Contención de rutas dentro de la raíz del pluginEspecificación, §4.1.3
PLUGIN_DATA se preserva entre actualizacionesEspecificación, §9.1
El campo version del manifiestoEspecificación, §5.4
.claude-plugin/marketplace.json y .agents/plugins/marketplace.jsonFormatos de catálogo de dos harnesses
source.source, repo, path, refResolución de origen de cada harness
policy, categoryPolítica y taxonomía de producto
claude plugin marketplace add / installCLI de Claude Code
codex plugin marketplace add + /pluginsCLI de Codex
agy plugin install <url>CLI de Antigravity
npx skills addTooling 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:

  • version en SemVer y un tag de git por versión publicada.
  • repository, homepage y license rellenos 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: marketplace y catalog aparecen 0 veces en su texto, y las 2 de registry descartan la idea de registro central.
  • Lo único que fija es que el paquete es un directorio (§4.1), pensado para ser inspeccionable con ls, cat y git.
  • 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/skills publica dos catálogos distintos —16 entradas en .claude-plugin/marketplace.json, 15 en .agents/plugins/marketplace.json— con esquemas y campos diferentes. Los tipos de source observados son github, git-subdir y url, y ninguno lo define la especificación.
  • Los comandos reales del README son claude plugin marketplace add más claude plugin install <plugin>@google-plugins, codex plugin marketplace add más el selector /plugins, y agy 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, category u owner son 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