🔌 Proyecto 2: MCP y hooks entre Claude y Codex
Las herramientas viajan; los eventos, no. Este proyecto registra en Codex los MCP que hoy solo ve Claude (magnific, metricool, klingai, cerebro-vip), desbloquea las 15 skills de adaptador que dependían de ellos y después enfrenta la parte que no tiene equivalente: los hooks. Codex tiene PostToolUse y Stop, pero no tiene SessionStart — y es justamente en SessionStart donde vive el fable-mindset. Lo que no se convierte en evento se convierte en texto leído, y los 7 subagentes se convierten en skills de rol.
🎯 El proyecto en una pantalla
~/.codex/AGENTS.md y 7 skills de rol en .agents/skills/.codex mcp list muestra los servidores; una skill de adaptador se ejecuta en Codex; grep -R 'sk-' ~/.codex no encuentra nada; y una sesión nueva de Codex puede asumir uno de los 7 roles citando el archivo.🧾 Inventario: quién tiene qué herramienta
Antes de tocar cualquier cosa, el inventario. El diagnóstico del 2026-09-14 midió ambos runtimes lado a lado y el resultado para MCP es corto y brutal: Claude tiene magnific y metricool globales, más dos MCP registrados por proyecto — wifi → klingai y 2cerebrox → cerebro-vip. Codex no tiene ninguno. No es "tiene menos": es cero. Toda skill tuya que llame a una de esas herramientas simplemente no existe del lado de Codex, por muy bien escrita que esté.
Conviene separar dos ejes que la gente suele confundir. MCP es herramienta: un proceso externo que el agente llama para hacer algo en el mundo (generar una imagen, programar una publicación, consultar una métrica). Hook es evento: un gancho del harness que se dispara solo en un momento del ciclo de vida de la sesión. El primero es un contrato de protocolo y por eso viaja entre runtimes. El segundo es un detalle de implementación del harness y por eso no viaja — y cuando no viaja, la única salida es transformar el efecto del hook en algo que el agente lee.
El panel de la izquierda es la buena noticia: MCP es un protocolo, así que el mismo servidor atiende a Claude y a Codex sin duplicar nada. El de la derecha es la mala: el hook es implementación del harness, muere en la frontera. El bloque morado de abajo es la salida de este proyecto: lo que no puede ser evento tiene que convertirse en lectura obligatoria.
📊 El inventario medido
| Alcance | Claude Code | Codex CLI |
|---|---|---|
| MCP global | magnific, metricool | ninguno |
| MCP por proyecto | wifi → klingai, 2cerebrox → cerebro-vip | ninguno |
| Hooks | 2 SessionStart (context-mode, fable-mindset) | PostToolUse + Stop (impeccable) |
| Subagentes | 7 en ~/.claude/agents | sin equivalente |
| Skills bloqueadas por esto | — | 15 de adaptador + 2 nativas |
Objetivo: reproducir el inventario en tu máquina antes de cambiar cualquier cosa, para tener una línea de base.
# MCP que Claude ve (global + por proyecto)
claude mcp list
# esperado en esta máquina: magnific, metricool
# los MCP por proyecto están en el .mcp.json de cada repo
ls ~/projetos/wifi/.mcp.json ~/projetos/2cerebrox/.mcp.json 2>/dev/null
# MCP que Codex ve
codex mcp list
# esperado HOY: lista vacía — esta es la brecha del proyecto
# hooks de ambos lados
grep -rho '"SessionStart"\|"PostToolUse"\|"Stop"' ~/.claude/settings.json ~/.claude/plugins/cache 2>/dev/null | sort | uniq -c
grep -o '"SessionStart"\|"PostToolUse"\|"Stop"' ~/.codex/hooks.json | sort | uniq -c
Cómo verificar: anota las dos listas en context/overview.md del proyecto en el que estás trabajando. Al final de este módulo, codex mcp list tiene que dejar de estar vacío, y esa es la única métrica que importa en el tema 2.
Regla de oro del inventario: no confíes en la memoria. Un MCP "que jurabas haber registrado" y no aparece en el list es un MCP que no existe para el agente. Lo mismo vale para los hooks: si no está en el archivo de settings, no se dispara, y vas a pasar una hora depurando un comportamiento que nunca se activó.
Conceptos clave
Herramienta externa accesible por protocolo; cualquier runtime que hable el protocolo puede usarla.
Gancho de eventos del harness; no es protocolo, es implementación local.
Un MCP puede valer en toda la máquina o solo dentro de un repo.
El estado medido antes del cambio; sin él no existe "mejoró".
🔑 codex mcp add sin copiar un solo secreto
El comando es simple: codex mcp add <nome> -- <comando>. Lo que exige cuidado es lo que va con él. La regla global de esta máquina es explícita: las API keys siempre viven en ~/projetos/openpcbotv2/.env o ~/projetos/wifi/.env, se cargan en runtime y nunca se duplican en otro lugar. Registrar un MCP pegando la key en el argumento del comando viola esto dos veces: crea una segunda copia del secreto y la guarda en un archivo de configuración que terminarás sincronizando, versionando o pegando en un chat.
La salida es indirecta: el comando registrado no es el servidor, es un wrapper de tres líneas que carga el .env y solo entonces ejecuta el servidor con exec. El patrón de carga es set -a; source .env; set +a: el set -a hace que toda variable asignada se convierta en variable de entorno exportada, el source lee el archivo y el set +a lo desactiva. El secreto pasa por la memoria del proceso y nunca toca la configuración de Codex.
Descubrir el comando real
En Claude, cada MCP ya tiene un comando de inicio. Copia el comando, no la key.
Escribir el wrapper
Un .sh por servidor en ~/.local/bin/, con set -a; source ...; set +a y exec al final.
Registrar apuntando al wrapper
codex mcp add magnific -- ~/.local/bin/mcp-magnific.sh. La configuración guarda una ruta, no un secreto.
Probar que no se filtró
Un grep en el árbol ~/.codex buscando prefijos de key. Cero resultados es la aceptación.
Objetivo: registrar magnific y metricool en Codex referenciando las keys del .env, sin guardar ningún valor.
# 1. wrapper que carga el .env en runtime y le pasa la posta al servidor
mkdir -p ~/.local/bin
cat > ~/.local/bin/mcp-magnific.sh <<'SH'
#!/usr/bin/env bash
set -euo pipefail
set -a; . "$HOME/projetos/openpcbotv2/.env"; set +a # carga, no copia
exec npx -y @magnific/mcp-server # reemplázalo por el comando real de tu MCP
SH
chmod 700 ~/.local/bin/mcp-magnific.sh
# 2. registrar en Codex: la config guarda una RUTA, nunca una key
codex mcp add magnific -- "$HOME/.local/bin/mcp-magnific.sh"
codex mcp add metricool -- "$HOME/.local/bin/mcp-metricool.sh"
# 3. verificar
codex mcp list
Cómo verificar: codex mcp list deja de salir vacío y muestra los dos nombres. Después abre codex en cualquier carpeta y pide "lista las herramientas de MCP disponibles": los nombres tienen que aparecer en la respuesta, no solo en el archivo.
Objetivo: probar que ningún secreto terminó en la configuración; esta es la prueba que cierra el tema.
# busca prefijos típicos de key dentro de la config de Codex
grep -rIl -e 'sk-' -e 'API_KEY=' -e 'TOKEN=' ~/.codex 2>/dev/null
# esperado: NINGUNA línea de salida
# verifica que el wrapper no sea legible por otros usuarios
stat -c '%a %n' ~/.local/bin/mcp-*.sh
# esperado: 700 en todos
# verifica que el .env siga siendo la única fuente
grep -c '=' ~/projetos/openpcbotv2/.env # solo el conteo; nunca imprimas el contenido
Cómo verificar: el primer comando tiene que terminar en silencio. Si imprime cualquier ruta, detén todo: alguna key fue copiada. Elimina el registro con codex mcp remove <nome>, rota la key y rehazlo con el wrapper.
✓ Forma correcta de llevar la key
- ✓El wrapper hace
sourcedel.enven runtime, en cada inicio. - ✓La configuración de Codex guarda solo la ruta del wrapper.
- ✓Rotar la key es editar un solo archivo; nada más necesita cambiar.
- ✓
chmod 700en el wrapper y.envfuera de cualquier repo publicado.
✗ Forma que genera deuda
- ✗Pegar la key en
codex mcp add ... --env KEY=sk-...: se vuelve texto en disco. - ✗Duplicar el
.envdentro de~/.codex"solo para facilitar". - ✗Exportar la key en el
.bashrc: empieza a filtrarse a todos los procesos de la máquina. - ✗Imprimir el valor para "verificar que está bien": el terminal queda en el historial y en el log de la sesión.
Los dos MCP por proyecto: klingai (en wifi) y cerebro-vip (en 2cerebrox) siguen exactamente el mismo patrón, pero registrados desde dentro del repo, para mantener el alcance local. La pregunta que decide el alcance es simple: "¿algún otro proyecto va a querer llamar esto?" Si la respuesta es no, mantenlo por proyecto: un MCP global es superficie de ataque y ruido de contexto en cada sesión.
Conceptos clave
Script corto que carga el entorno y hace exec al servidor real.
set -aHace que el source exporte todo; set +a lo desactiva justo después.
Apuntar a la fuente del secreto; nunca crear una segunda fuente.
La aceptación es un grep que no encuentra nada: ausencia verificada.
🔓 Las 15 skills de adaptador se desbloquean después
La auditoría de skills clasificó en cuatro clases 89 skills que solo existen en Claude. 72 son reutilizables: Markdown y scripts comunes, que se portan sin cambios. 15 son de adaptador: dependen de un MCP o de un plugin de Claude y por eso solo tienen sentido en Codex después de registrar la herramienta correspondiente. Dos son nativas (dependen del hook SessionStart) y a una le falta el SKILL.md.
Por eso este proyecto va antes de la migración de skills en lote. Portar una skill de adaptador sin su MCP da el peor resultado posible: se descubre, se elige y falla a mitad de la ejecución, cuando el agente ya le prometió al usuario que iba a generar el video. Primero la herramienta, después la skill. El orden no es cuestión de gusto: es lo que separa "no instalado" de "instalado y roto".
🧩 Las 15 de adaptador, por dependencia
Ocho de ellas son variantes de printing-press y cuentan como una sola dependencia. Resolver tres MCP (heygen, comfy y el de espiona-ads) libera el bloque entero.
✓ Lista para portar ya
- ✓Solo lee y escribe archivos, o llama a binarios que ya existen en la máquina.
- ✓El
SKILL.mdno cita ningúnmcp__…en el cuerpo de las instrucciones. - ✓Los scripts auxiliares son bash o node sin dependencia de plugins.
- ✓El camino feliz completo se puede ejecutar sin red.
✗ Espera al MCP correspondiente
- ✗El
SKILL.mdindica "llama amcp__magnific__images_generate". - ✗Depende de un plugin de Claude (superpowers, context-mode, claude-mem).
- ✗Depende de un subagente: "delega a
analista-neutro". - ✗Da por hecho
AskUserQuestionu otro recurso de interfaz exclusivo de Claude.
Objetivo: clasificar automáticamente tus skills entre "reutilizable" y "adaptador", para saber qué portar hoy.
# adaptador = el SKILL.md menciona MCP, plugin o subagente
cd ~/.claude/skills
for d in */; do
s="${d%/}"
if grep -qE 'mcp__|AskUserQuestion|subagent_type|superpowers:|context-mode:' "$s/SKILL.md" 2>/dev/null; then
echo "ADAPTADOR $s"
else
echo "portavel $s"
fi
done | sort | tee ~/classificacao-skills.txt | awk '{print $1}' | uniq -c
# ver solo las bloqueadas y por qué dependencia
grep '^ADAPTADOR' ~/classificacao-skills.txt | awk '{print $2}' | while read s; do
echo "== $s"; grep -ohE 'mcp__[a-z0-9_]+' "$s/SKILL.md" | sort -u
done
Cómo verificar: el conteo debe coincidir con el orden de magnitud del diagnóstico: decenas de portables frente a unos 15 adaptadores. Si salen 80 adaptadores, tu grep está captando menciones en ejemplos; ajústalo para que mire solo las líneas de instrucción.
Consejo de secuencia: después de registrar magnific y metricool en el tema 2, vuelve a esta lista y pasa a "portable" solo las skills cuya única dependencia era uno de esos dos. Las de heygen y comfy siguen bloqueadas hasta que registres los MCP correspondientes, y eso es tarea de otra sesión, no una excepción para abrir ahora.
Conceptos clave
Skill cuyo valor depende de una herramienta externa registrada.
Peor que no tenerla: la skill se elige y se rompe después de la promesa.
El orden que evita instalar algo roto en Codex.
8 variantes de printing-press se desbloquean con un solo MCP.
🪝 Hooks: la correspondencia real entre los dos
Aquí la noticia es mixta. Claude tiene dos hooks de SessionStart (context-mode y fable-mindset), y de ellos viene buena parte del comportamiento que consideras "la forma de trabajar de Claude". Codex también tiene hooks, pero en otros momentos: el ~/.codex/hooks.json de esta máquina registra un PostToolUse (matcher Edit|Write|apply_patch, timeout 5s) y un Stop (timeout 30s), y los dos llaman al mismo hook.mjs de impeccable. Lo que no existe es el gancho de apertura: SessionStart no tiene equivalente en Codex.
La línea es el ciclo de vida de una sesión. Los dos puntos cian (PostToolUse y Stop) ya existen en los dos runtimes y no dan trabajo. El punto morado (SessionStart) solo existe en Claude, y la flecha punteada hacia abajo es la única respuesta posible: cambiar el evento automático por una lectura obligatoria al inicio de la sesión.
🗺️ La tabla de mapeo
| Evento | Claude | Codex | Qué hacer |
|---|---|---|---|
SessionStart | ✓ 2 hooks | ✗ no existe | Convertirlo en texto en el AGENTS.md + skill de prime |
PostToolUse | ✓ | ✓ impeccable | Nada; revisar el matcher |
Stop | ✓ | ✓ impeccable | Nada; revisar el timeout de 30s |
| Plugins | ✓ 7 | ✗ 1 (github) | Residuo de Claude; no se migra |
Objetivo: leer lo que realmente está registrado en ambos lados, en lugar de suponerlo.
# qué eventos tiene registrados Codex en esta máquina
python3 -c "import json;print(list(json.load(open('$HOME/.codex/hooks.json'))['hooks']))"
# esperado: ['PostToolUse', 'Stop']
# el matcher y el timeout de cada uno
grep -E '"matcher"|"timeout"|"statusMessage"' ~/.codex/hooks.json
# ¿existe el destino de los dos hooks? (el hook está escrito para fallar en silencio si no existe)
ls -l ~/.agents/skills/impeccable/scripts/hook.mjs
# lo que Claude dispara en el SessionStart — esto es lo que NO tiene equivalente
grep -rl '"SessionStart"' ~/.claude/settings.json ~/.claude/plugins/cache 2>/dev/null
Cómo verificar: la lista de Codex sale con dos nombres y ninguno es SessionStart. Si el ls del hook.mjs falla, los hooks de Codex están registrados pero inertes: el [ ! -f ... ] || al inicio del comando hace que terminen sin decir nada.
Por qué el hook falla en silencio a propósito: fíjate en el formato [ ! -f "…/hook.mjs" ] || node "…/hook.mjs". Si el archivo desaparece, el comando devuelve éxito y la sesión continúa. Eso es deseable para un hook cosmético como el de impeccable, y pésimo para un hook que carga contexto: creerías que leíste el AGENTS.md y no lo leíste. Es un argumento más para no depender de eventos en algo que necesita estar garantizado.
Conceptos clave
Gancho de apertura; solo existe en Claude, y es el que más duele perder.
Filtro de qué herramientas disparan el PostToolUse.
Hook escrito para no romper la sesión cuando el destino desaparece.
La única traducción posible cuando el gancho no existe del otro lado.
📝 fable-mindset se convierte en una sección de texto en el AGENTS.md
El fable-mindset es una de las dos skills clasificadas como nativas: existe como hook de SessionStart, que inyecta un playbook de comportamiento al inicio de cada sesión de Claude. Como Codex no tiene SessionStart, no hay adónde portar el mecanismo. Pero el contenido del playbook es solo texto, y el texto es lo más portátil que existe. La conversión es directa: lo que se inyectaba por evento pasa a ser una sección del ~/.codex/AGENTS.md, que se lee porque el archivo se lee.
El playbook destilado de este análisis cabe en pocas líneas, y su núcleo es lo que podemos llamar regla de ritmo: piensa antes de actuar, cierra el ciclo verificando. Dos mitades que se sostienen mutuamente. La primera evita la sesión que empieza a editar archivos antes de entender el problema; la segunda evita la sesión que declara "listo" sin ejecutar nada. Escrito así, sin jerga de harness, funciona en cualquier runtime, incluso en el dsh del próximo proyecto, que no tiene ningún hook.
Objetivo: agregar la regla de ritmo al ~/.codex/AGENTS.md, reemplazando el hook por lectura.
# respaldo antes de tocar la base global (el Proyecto 1 creó este archivo)
cp ~/.codex/AGENTS.md ~/.codex/AGENTS.md.bak-$(date +%Y%m%d)
cat >> ~/.codex/AGENTS.md <<'MD'
## Regla de ritmo (era el hook fable-mindset en Claude)
- **Piensa antes de actuar.** Antes de la primera edición, di en una frase
cuál es el problema y cuál es el cambio mínimo que lo resuelve. Si no
lo sabes, lee más; no empieces a editar para descubrirlo.
- **Cierra el ciclo verificando.** Todo cambio termina con un comando que
demuestra el resultado (test, `grep`, `status`, readback) y con la salida
pegada en la respuesta. "Debería funcionar" no cierra el ciclo.
- **Una corrección a la vez.** Si la respuesta fue reescribirlo todo,
probablemente faltaba una protección: regístralo en el FALHAS.md.
MD
# comprobar que se agregó y que el archivo sigue siendo corto
tail -14 ~/.codex/AGENTS.md
wc -l ~/.codex/AGENTS.md
Cómo verificar: ejecuta codex exec "Antes de editar cualquier archivo, ¿qué debes decir primero? Cita la fuente." desde ~. La respuesta tiene que mencionar la frase sobre el problema y el cambio mínimo, citando el AGENTS.md. Si responde bien sin citar la fuente, es coincidencia del modelo, no lectura, y no cuenta como aceptación.
✓ Se convierte bien en texto
- ✓Playbook de comportamiento: ritmo, orden de trabajo, qué hacer antes de declarar listo.
- ✓Prohibiciones estrictas y preferencias de estilo.
- ✓Orden de lectura de archivos al inicio de la sesión.
- ✓Criterios de aceptación y formato de handoff.
✗ No se convierte en texto
- ✗Lo que el hook calcula: minar 2,3 GB de JSONL para generar el playbook.
- ✗Bloqueo real de acciones: el texto pide, el hook impide.
- ✗Inyección garantizada: el texto puede ser recortado del contexto en una sesión larga.
- ✗Enrutamiento automático de llamadas a un subagente.
El costo honesto de la conversión: el hook es garantía, el texto es petición. Al cambiar uno por otro pierdes determinismo, y es exactamente por eso que la regla de ritmo tiene que ser corta. Tres viñetas que el modelo lee al inicio de cada sesión valen más que cuarenta líneas que va a atravesar sin leer. La misma lógica se aplica al silver-platter, la otra skill nativa.
Conceptos clave
Depende de un recurso del harness; solo migra el contenido.
Piensa antes de actuar; cierra el ciclo verificando.
El hook se ejecuta siempre; el texto depende de que lo lean y lo obedezcan.
Una instrucción larga no es más fuerte, es más ignorada.
🎭 Los 7 subagentes se convierten en skills de rol
Claude tiene 7 subagentes en ~/.claude/agents: advogado-do-diabo, analista-neutro, estrategista-otimista, mestre-do-conselho, diretor-ecossistema, web-research-assistant y triple-x-responder. Codex no tiene equivalente: el diagnóstico es categórico, los subagentes quedan como residuo de Claude. Pero cada uno de esos archivos es, en el fondo, un rol descrito en prosa: lo que el agente asume, lo que busca, el formato de la salida. Eso se convierte en skill.
El destino es .agents/skills/: la misma carpeta que Codex ya recorre y que sync-skills.sh replica en la instalación. Lo que se pierde es el paralelismo: en Claude, tres miembros del consejo corren al mismo tiempo en contextos separados. Con una skill de rol, el mismo agente asume los roles en secuencia, en el mismo contexto: más barato, más lento y con contaminación entre las voces. Es una pérdida real, y conviene decirlo en la descripción de la skill en lugar de fingir equivalencia.
Objetivo: convertir los 7 subagentes en skills de rol, preservando el cuerpo del prompt y cambiando solo el encabezado.
mkdir -p ~/.agents/skills
for a in advogado-do-diabo analista-neutro estrategista-otimista \
mestre-do-conselho diretor-ecossistema web-research-assistant \
triple-x-responder; do
src=~/.claude/agents/$a.md
[ -f "$src" ] || { echo "falta: $a"; continue; }
mkdir -p ~/.agents/skills/papel-$a
{
echo "---"
echo "name: papel-$a"
echo "description: Asume el rol de $a. Úsalo cuando la tarea pida esa voz explícitamente."
echo "---"
echo
echo "> Rol derivado del subagente \`$a\` de Claude Code."
echo "> Aquí NO hay ejecución paralela: asume el rol en el contexto actual y"
echo "> deja claro en la respuesta cuándo estás hablando por él."
echo
sed '1{/^---$/!b}; 1,/^---$/d' "$src" # quita solo el front-matter antiguo
} > ~/.agents/skills/papel-$a/SKILL.md
echo "papel-$a"
done
Cómo verificar: ls ~/.agents/skills | grep -c '^papel-' devuelve 7. Después, codex exec "Usa el papel-advogado-do-diabo para atacar esta decisión: migrar todo de una vez.": la respuesta tiene que citar el archivo de la skill y sostener la voz de la contradicción de principio a fin.
✅ Criterios de aceptación (márcalos con evidencia)
- ☐
codex mcp listmuestra magnific y metricool. Evidencia: salida del comando. - ☐
grep -rIl -e 'sk-' -e 'API_KEY=' ~/.codexno devuelve nada. Evidencia: salida vacía pegada en el handoff. - ☐Una skill de adaptador que dependía solo de magnific corre en Codex de punta a punta. Evidencia: el artefacto generado.
- ☐La regla de ritmo está en
~/.codex/AGENTS.mdy se cita en uncodex exec. Evidencia: la respuesta con la cita. - ☐7 skills
papel-*en~/.agents/skills, y una de ellas fue ejercitada. Evidencia:ls+ la respuesta del rol. - ☐Nada se rompió en Claude:
claude mcp listy los hooks de SessionStart siguen iguales. Evidencia: comparación con la línea base del tema 1.
⚠️ Riesgos de este proyecto
- •Key copiada dentro de
~/.codexpor prisa: el riesgo más caro de la página. - •MCP registrado de forma global cuando debía ser por proyecto: ruido de contexto en cada sesión.
- •Skill de adaptador portada antes del MCP: falla a mitad de la ejecución.
- •Creer que una skill de rol es igual a un subagente: se pierde el paralelismo y el aislamiento de contexto.
- •Servidor MCP descargado por
npxsin versión fija que cambia sin que lo notes.
↩️ Rollback en un comando
- ✓MCP:
codex mcp remove magnific(y metricool) deja la lista vacía. - ✓Wrappers:
rm ~/.local/bin/mcp-*.sh; el.envnunca se tocó. - ✓AGENTS.md:
mv ~/.codex/AGENTS.md.bak-AAAAMMDD ~/.codex/AGENTS.md. - ✓Roles:
rm -rf ~/.agents/skills/papel-*; los originales en~/.claude/agentsquedan intactos. - ✓Si una key se filtró, el rollback no basta: rota la key antes que cualquier otra cosa.
Conceptos clave
El prompt del subagente convertido en instrucción que el agente asume.
Roles en secuencia en el mismo contexto, no en contextos separados.
.agents/skills/Carpeta que Codex recorre y que sync-skills replica.
Documentar lo que se pierde vale más que fingir paridad.
Autoevaluación (opcional): quieres que Codex registre el MCP magnific, que necesita una API key guardada en ~/projetos/openpcbotv2/.env. ¿Cuál es el camino correcto?
🎯 Resumen del proyecto
set -a; source .env; set +a; la configuración guarda una ruta, nunca la key.Próximo proyecto:
3.3 — Memoria curada: el agente propone, tú apruebas