Codex Leu Seus Plugins de Claude Code (Sem Pedir Permissão)

Codex Leu Seus Plugins do Claude Code (Sem Pedir Permissão)

Codex CLI 0.146.0 saiu em 29 de julho com uma linha que parece manutenção de rotina:

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

Três recursos em uma única frase, enterrados entre naming de sessões e forking de threads. Fácil de passar por cima.

Não passe. Aquela última cláusula — “additional plugin marketplaces for … Claude Code” — significa que Codex agora lê catálogos escritos para o agente do concorrente. Não através de um conversor, não através de uma bridge da comunidade. Nativamente, como fonte de primeira classe.

Então fiz o teste óbvio: escrever um plugin, instalá-lo em ambos os harnesses e descobrir exatamente onde para de funcionar.

Os dois layouts de arquivos

Um plugin do Codex é um diretório com um manifest:

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

E o plugin.json é quase insultantemente pequeno:

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

Os campos opcionais são ponteiros, não configuração inline: mcpServers aponta para ./.mcp.json, hooks para ./hooks/hooks.json, apps para ./.app.json.

Agora compare isso com um plugin do Claude Code. Mesmo formato de skills (SKILL.md), mesmo .mcp.json, mesmo conceito de hooks. O manifest fica em .claude-plugin/ em vez de .codex-plugin/. Essa é a diferença principal: o nome de um diretório.

Os catálogos divergem um pouco mais. Codex espera um marketplace em $REPO_ROOT/.agents/plugins/marketplace.json (ou ~/.agents/plugins/marketplace.json para os pessoais) — note o namespace .agents/, neutro em relação ao vendor, o mesmo instinto que produziu AGENTS.md. Claude Code espera .claude-plugin/marketplace.json, com um objeto owner e um metadata.pluginRoot que o schema do Codex não compartilha.

Exceto que Codex o lê mesmo assim. Os docs de build descrevem $REPO_ROOT/.claude-plugin/marketplace.json como um legacy-compatible marketplace. Leia essa palavra de novo: legacy. OpenAI não está descrevendo o formato da Anthropic como um padrão estrangeiro com o qual interoperar. Está descrevendo como uma versão anterior do seu.

O que é portável sem atrito

As skills são portáveis. Um SKILL.md é um arquivo markdown com frontmatter e instruções — não há nada específico do harness na prosa. Coloque o mesmo diretório skills/ em qualquer um dos dois manifests e ambos os agentes o carregam.

Os hooks são mais portáveis do que eu esperava. Codex passa PLUGIN_ROOT e PLUGIN_DATA para os comandos de hook e — aqui está o detalhe revelador — também define CLAUDE_PLUGIN_ROOT e CLAUDE_PLUGIN_DATA “for compatibility with existing plugin hooks”. OpenAI meteu os nomes de variáveis de ambiente da Anthropic dentro de seu próprio runtime. Isso não é convergência por acaso; é alguém decidindo que o custo de migração tinha que ser zero.

Ou seja: um plugin que seja skills mais hooks já é realmente portável hoje. Renomeie um diretório, ou publique os dois.

Onde quebra: os servidores MCP

A costura são os servidores MCP bundleados, e é um problema de resolução de paths.

Se seu plugin traz seu próprio servidor MCP e o referencia de forma relativa em .mcp.json — "./bundled-mcp-server" — Codex resolve esse path a partir do working directory do processo, não a partir da raiz do plugin. Levante Codex de qualquer lugar que não seja a pasta do próprio plugin e o servidor não inicia. Esse é o issue #22842, aberto desde maio contra a 0.130.0, com labels bug e plugins, ainda não atribuído. Os fixes solicitados — definir o cwd para a raiz do plugin, suportar um campo cwd, adicionar uma substituição ${PLUGIN_DIR} — são todos razoáveis e nenhum foi implementado.

O workaround são paths absolutos em config.toml, que como esclarece quem reportou o issue está bem para você e não serve para nada ao distribuir. Se você está empacotando um plugin para outras pessoas, um path absoluto da sua máquina não é um plugin.

Há um segundo modo de falha, mais ruidoso. Há usuários reportando que Codex auto-espelha os marketplaces do Claude Code que encontra em disco para ~/.codex/.tmp/marketplaces/ sem que peçam, e depois tenta iniciar os servidores MCP de plugins que são apenas do Claude ao arrancar — incluindo os que usam ${CLAUDE_PLUGIN_ROOT} dentro de .mcp.json, que não é substituído. Resultado: erros de handshake em cada launch, de plugins que você nunca instalou no Codex. Esse é o issue #19372, reportado contra a 0.124.0. O workaround é uma estrofe de config por plugin:

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

Que escala exatamente tão mal quanto parece. (Nota: quem reportou o issue atribui isso a core-plugins/src/marketplace_upgrade.rs; não consegui confirmar esse code path específico no main atual, então tome a causa raiz como atribuição do reporter e o sintoma como reportado.)

Publicar um único plugin para os dois

Com tudo acima, a receita prática a partir da 0.146.0:

  1. Coloque seu conteúdo real em skills/ e hooks/. Essas são as camadas portáveis. Tudo o mais é packaging.
  2. Publique os dois manifests. .codex-plugin/plugin.json e .claude-plugin/plugin.json apontando para os mesmos diretórios skills/ e hooks/. São uma dúzia de linhas cada um; duplicar sai mais barato que montar um build step.
  3. Não bundle um servidor MCP ainda se quiser um artefato único que funcione em ambos. Referencie servidores remotos, ou documente o setup manual em config.toml, até que aterrise o #22842.
  4. Publique o catálogo duas vezes também. .agents/plugins/marketplace.json para Codex, .claude-plugin/marketplace.json para Claude Code. Codex lê o segundo como legacy, então se vai escrever apenas um, escreva esse — mas o path .agents/ é para onde isso vai.
  5. Instale com codex plugin marketplace add owner/repo, e depois codex plugin marketplace list para confirmar. Codex cachea as instalações em ~/.codex/plugins/cache/$MARKETPLACE_NAME/$PLUGIN_NAME/$VERSION/ — útil quando você precisa revisar o que foi baixado realmente.

A outra metade da 0.146.0 é o workspace publishing: no app do Codex, Plugins > Created by you > Share, e aí você escolhe membros ou grupos do workspace. Esse é o caminho de distribuição enterprise, e saiu. O Plugin Directory público não — os docs ainda dizem que o publishing self-serve está “coming soon”. Vale a pena ter a distinção clara quando alguém disser que Codex já tem um marketplace de plugins.

A parte que não é sobre paths de arquivos

Aqui está o que continua me dando voltas. Durante dois anos a pergunta do lock-in nesta categoria foi sobre o modelo: com quais weights você se casou, quão caro é mudar de provider, o que acontece com seus prompts.

Essa pergunta está ficando chata, e rápido. Codex 0.146.0 agora lista marketplaces da Amazon Bedrock e do Claude Code ao lado do seu. A camada do modelo está se tornando um dropdown.

O que não é um dropdown é o catálogo de onde seu time instala, os plugins dos quais seus workflows dependem, os hooks ligados em seu CI. Isso se acumula. Tem versões e owners e um blast radius quando muda. E quem hospeda esse catálogo — um owner/repo contra o qual todo seu time rodou marketplace add — tem agarrado algo bastante mais pegajoso que uma API key.

Em OpenAI entendem isso perfeitamente, e por isso “legacy-compatible” está fazendo tanto trabalho naquela frase. Ler o formato do concorrente com custo de migração zero não é generosidade. É a forma mais barata possível de se certificar de que o catálogo para o qual a galera converge seja o próprio.

As skills são portáveis. Os hooks são portáveis. Os servidores MCP são a costura, e naquela costura se decide o próximo ano de tudo isso.E você, já tem plugins instalados em mais de um agente? Está mantendo dois manifests ou escolheu um harness e ficou por aí?