Codex Ya Leyó Tus Plugins de Claude Code (Sin Pedirte Permiso)

Codex Ya Leyó Tus Plugins de Claude Code (Sin Pedirte Permiso)

Codex CLI 0.146.0 salió el 29 de julio con una línea que parece mantenimiento de rutina:

“Support Agent Plugins manifests, workspace plugin publishing, and additional plugin marketplaces for Amazon Bedrock and Claude Code.”

Tres features en una sola frase, enterrada entre el naming de sesiones y el forking de threads. Fácil de saltear.

No la saltees. Esa última cláusula — “additional plugin marketplaces for … Claude Code” — significa que Codex ahora lee catálogos escritos para el agente de la competencia. No a través de un converter, no a través de un bridge de la comunidad. De forma nativa, como source de primera clase.

Así que hice la prueba obvia: escribir un plugin, instalarlo en ambos harnesses y averiguar exactamente dónde deja de funcionar.

Los dos layouts de archivos

Un plugin de Codex es un directorio con un manifest:

my-first-plugin/
├── .codex-plugin/
│   └── plugin.json
└── skills/
    └── hello/
        └── SKILL.md

Y el plugin.json es casi insultantemente chico:

{
  "name": "my-first-plugin",
  "version": "1.0.0",
  "description": "Reusable greeting workflow",
  "skills": "./skills/"
}

Los campos opcionales son punteros, no configuración inline: mcpServers apunta a ./.mcp.json, hooks a ./hooks/hooks.json, apps a ./.app.json.

Ahora compara eso con un plugin de Claude Code. Mismo formato de skills (SKILL.md), mismo .mcp.json, mismo concepto de hooks. El manifest vive en .claude-plugin/ en lugar de .codex-plugin/. Esa es la diferencia principal: el nombre de un directorio.

Los catálogos divergen un poco más. Codex espera un marketplace en $REPO_ROOT/.agents/plugins/marketplace.json (o ~/.agents/plugins/marketplace.json para los personales) — fíjate en el namespace .agents/, neutral respecto del vendor, el mismo instinto que produjo AGENTS.md. Claude Code espera .claude-plugin/marketplace.json, con un objeto owner y un metadata.pluginRoot que el schema de Codex no comparte.

Salvo que Codex igual lo lee. Los docs de build describen $REPO_ROOT/.claude-plugin/marketplace.json como un legacy-compatible marketplace. Lee esa palabra de nuevo: legacy. OpenAI no está describiendo el formato de Anthropic como un estándar ajeno con el que interoperar. Lo está describiendo como una versión anterior del suyo.

Qué es portable sin fricción

Las skills son portables. Un SKILL.md es un archivo markdown con frontmatter e instrucciones — no hay nada específico del harness en la prosa. Pon el mismo directorio skills/ en cualquiera de los dos manifests y ambos agentes lo cargan.

Los hooks son más portables de lo que esperaba. Codex le pasa PLUGIN_ROOT y PLUGIN_DATA a los comandos de hook y — acá está el detalle revelador — también setea CLAUDE_PLUGIN_ROOT y CLAUDE_PLUGIN_DATA “for compatibility with existing plugin hooks”. OpenAI metió los nombres de variables de entorno de Anthropic dentro de su propio runtime. Eso no es convergencia por accidente; es alguien decidiendo que el costo de migración tenía que ser cero.

O sea: un plugin que sea skills más hooks ya es realmente portable hoy. Renombra un directorio, o publica los dos.

Dónde se rompe: los MCP servers

La costura son los MCP servers bundleados, y es un problema de resolución de paths.

Si tu plugin trae su propio MCP server y lo referencia de forma relativa en .mcp.json"./bundled-mcp-server" — Codex resuelve ese path desde el working directory del proceso, no desde el plugin root. Levanta Codex desde cualquier lugar que no sea la carpeta del propio plugin y el server no arranca. Ese es el issue #22842, abierto desde mayo contra la 0.130.0, con labels bug y plugins, todavía sin asignar. Los fixes pedidos — setear el cwd al plugin root, soportar un campo cwd, agregar una sustitución ${PLUGIN_DIR} — son todos razonables y ninguno aterrizó.

El workaround son paths absolutos en config.toml, que como aclara quien reportó el issue está bien para ti y no sirve para nada al distribuir. Si estás empaquetando un plugin para otras personas, un path absoluto de tu máquina no es un plugin.

Hay un segundo modo de falla, más ruidoso. Hay usuarios reportando que Codex auto-espeja los marketplaces de Claude Code que encuentra en disco hacia ~/.codex/.tmp/marketplaces/ sin que se lo pidan, y después intenta levantar los MCP servers de plugins que son solo de Claude al arrancar — incluyendo los que usan ${CLAUDE_PLUGIN_ROOT} dentro de .mcp.json, que no se sustituye. Resultado: errores de handshake en cada launch, de plugins que nunca instalaste en Codex. Ese es el issue #19372, reportado contra la 0.124.0. El workaround es una estrofa de config por plugin:

[plugins."claude-mem@thedotmack"]
enabled = false

Que escala exactamente tan mal como se ve. (Nota: quien reportó el issue atribuye esto a core-plugins/src/marketplace_upgrade.rs; no pude confirmar ese code path específico en el main actual, así que toma la causa raíz como atribución del reporter y el síntoma como reportado.)

Publicar un solo plugin para los dos

Con todo lo anterior, la receta práctica al día de la 0.146.0:

  1. Pon tu contenido real en skills/ y hooks/. Esas son las capas portables. Todo lo demás es packaging.
  2. Publica los dos manifests. .codex-plugin/plugin.json y .claude-plugin/plugin.json apuntando a los mismos directorios skills/ y hooks/. Son una docena de líneas cada uno; duplicar sale más barato que montar un build step.
  3. No bundlees un MCP server todavía si quieres un artefacto único que funcione en ambos. Referencia servers remotos, o documenta el setup manual en config.toml, hasta que aterrice el #22842.
  4. Publica el catálogo dos veces también. .agents/plugins/marketplace.json para Codex, .claude-plugin/marketplace.json para Claude Code. Codex lee el segundo como legacy, así que si vas a escribir uno solo, escribe ese — pero el path .agents/ es hacia donde esto va.
  5. Instala con codex plugin marketplace add owner/repo, y después codex plugin marketplace list para confirmar. Codex cachea las instalaciones en ~/.codex/plugins/cache/$MARKETPLACE_NAME/$PLUGIN_NAME/$VERSION/ — útil cuando necesitas revisar qué se bajó realmente.

La otra mitad de la 0.146.0 es el workspace publishing: en la app de Codex, Plugins > Created by you > Share, y ahí eliges miembros o grupos del workspace. Ese es el camino de distribución enterprise, y salió. El Plugin Directory público no — los docs siguen diciendo que el publishing self-serve está “coming soon”. Vale la pena tener la distinción clara cuando alguien te diga que Codex ya tiene un marketplace de plugins.

La parte que no es sobre paths de archivos

Acá está lo que me sigue dando vueltas. Durante dos años la pregunta del lock-in en esta categoría fue sobre el modelo: con qué weights te casaste, qué tan caro es cambiar de provider, qué pasa con tus prompts.

Esa pregunta se está volviendo aburrida, y rápido. Codex 0.146.0 ahora lista marketplaces de Amazon Bedrock y de Claude Code al lado del suyo. La capa del modelo se está convirtiendo en un dropdown.

Lo que no es un dropdown es el catálogo desde el que instala tu equipo, los plugins de los que dependen tus workflows, los hooks cableados en tu CI. Eso se acumula. Tiene versiones y owners y un blast radius cuando cambia. Y quien hostea ese catálogo — un owner/repo contra el que todo tu equipo corrió marketplace add — tiene agarrado algo bastante más pegajoso que una API key.

En OpenAI entienden esto perfectamente, y por eso “legacy-compatible” está haciendo tanto trabajo en esa frase. Leer el formato de la competencia con costo de migración cero no es generosidad. Es la forma más barata posible de asegurarse de que el catálogo hacia el que converge la gente sea el propio.

Las skills son portables. Los hooks son portables. Los MCP servers son la costura, y en esa costura se decide el próximo año de todo esto.

¿Y tú, ya tienes plugins instalados en más de un agente? ¿Estás manteniendo dos manifests o elegiste un harness y te quedaste ahí?