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:
- Coloque seu conteúdo real em
skills/ehooks/. Essas são as camadas portáveis. Tudo o mais é packaging. - Publique os dois manifests.
.codex-plugin/plugin.jsone.claude-plugin/plugin.jsonapontando para os mesmos diretóriosskills/ehooks/. São uma dúzia de linhas cada um; duplicar sai mais barato que montar um build step. - 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. - Publique o catálogo duas vezes também.
.agents/plugins/marketplace.jsonpara Codex,.claude-plugin/marketplace.jsonpara 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. - Instale com
codex plugin marketplace add owner/repo, e depoiscodex plugin marketplace listpara 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í?