Hooks mcp_tool, la línea directa a tu servidor MCP
Claude Code v2.1.118 permite que los hooks llamen herramientas MCP directamente. Qué cambia, cuándo encaja, cuándo no.
El problema con los hooks bash-wrapper
En la Lección 4 aprendiste cómo funcionan los hooks. Escribes un hook bash que le da un empujón al modelo: "ahora llama nex_summarize antes de parar". Funciona, pero solo porque el modelo escucha. A veces ignora el empujón. A veces llama la herramienta equivocada. Y solo lo notas días después cuando la memoria tiene huecos.
El 23 de abril de 2026, Claude Code v2.1.118 introdujo un nuevo tipo de hook que arregla esto determinísticamente: type: "mcp_tool".
Qué es el hook mcp_tool
En vez de un recordatorio bash, escribes la llamada a la herramienta directamente en ~/.claude/settings.json:
{
"type": "mcp_tool",
"server": "studiomeyer-memory",
"tool": "nex_summarize",
"input": { "session_id": "${session_id}" },
"timeout": 60,
"statusMessage": "Memory: auto-summary..."
}
Cinco campos. Claude Code dispara la llamada por sí mismo, sin que el modelo esté involucrado. Si el servidor está disponible y la herramienta responde, asunto cerrado. Si no, log y continuar. Los hooks son best-effort, nunca camino crítico.
Variables de sustitución
Dentro de input puedes interpolar valores del runtime:
${cwd}, directorio de trabajo actual${session_id}, UUID de la sesión actual${tool_input.campo}, en PostToolUse, cualquier campo del input de la herramienta${tool_name}, nombre de la herramienta (Pre/PostToolUse)${duration_ms}, tiempo de ejecución (PostToolUse)${user_prompt}, el prompt del usuario (UserPromptSubmit)
Así disparas nex_search con { "query": "${user_prompt}" } en cada input. O crm_log_interaction con { "summary": "Edited ${tool_input.file_path}" } después de cada edición. Recordatorio se vuelve acción.
La revisión de cinco puntos ANTES de cablear un hook
Los hooks se disparan en eventos del lifecycle sin aprobación explícita por llamada. Antes de poner una herramienta en un hook, DEBE cumplir los cinco:
- Idempotente. N llamadas con el mismo input producen el mismo output sin efectos secundarios acumulativos.
nex_summarizees idempotente.crm_create_companyNO lo es (crea duplicados). - Rápido. Timeout default 60 segundos, recomendado menos de 30 segundos en hooks síncronos (Stop, PreCompact). Si tu herramienta a veces tarda 90 segundos contra una conexión DB fría, el hook falla en silencio.
- Determinístico. Mismo input, mismo output (o equivalente benigno). Sin
Math.random()sin snapshot. Sin "hora actual" como output a menos que el tiempo sea input explícito. - Sin efectos secundarios sin trigger explícito del usuario. Read-tools en UserPromptSubmit NO deben persistir nada. Write-tools solo cuando el usuario disparó implícitamente (Edit/Write disparando un log está bien).
- Consciente de RGPD. El hook se dispara en cada evento que matchea. Si la herramienta envía datos a terceros (LLM API, endpoint de logging), debe documentarse en el README de la recipe.
Una herramienta que falla cualquiera de los cinco no va en un hook. Punto.
Cuándo mcp_tool, cuándo bash
No todos los workflows encajan con mcp_tool. Usa hooks bash (type: "command") cuando:
- Necesitas múltiples llamadas con lógica entre ellas
- Necesitas llamar algo que no es una herramienta MCP (CLI, curl, script propio)
- Necesitas inspeccionar o modificar la respuesta
Usa mcp_tool cuando:
- Quieres exactamente una llamada MCP con input determinístico
- La herramienta existe, es idempotente y es rápida
- No necesitas inspeccionar ni modificar el resultado
Cinco bundles concretos para nuestros SaaS MCPs
Hemos preparado 5 paquetes de hooks, uno por cada SaaS MCP de StudioMeyer:
| MCP | Qué hace el hook | Recipe |
|-----|------------------|--------|
| Memory (studiomeyer-memory) | Stop -> nex_summarize + nex_session_end. PreCompact -> nex_summarize. UserPromptSubmit -> nex_search. SubagentStop -> nex_learn. | /recipes/16.2-memory-hook-bundle |
| CRM (studiomeyer-crm) | UserPromptSubmit -> crm_search en patrones de nombre de cliente. PostToolUse(Edit|Write) con if-filter -> crm_log_interaction en drafts de email. | /recipes/16.3-crm-hook-bundle |
| GEO (studiomeyer-geo) | Stop con if=Edit(*.md) -> geo_check (auto-audit después de edición de contenido). | /recipes/16.4-geo-crew-hook-bundle |
| Crew (studiomeyer-crew) | SessionStart -> crew_activate (persona default según cwd). Stop -> crew_feedback. | /recipes/16.4-geo-crew-hook-bundle |
| Academy (mcp-academy) | SessionStart -> academy_stats + academy_next_lesson. UserPromptSubmit con frase trigger -> academy_concept_search. | /recipes/16.5-academy-hook-bundle |
Cada bundle es una recipe en la Fase 16 del academy. Haces clic, copias el bloque JSON en tu ~/.claude/settings.json, el hook está activo.
Distribución: instala todo como un plugin
La Fase 9 (Distribution) cubre cómo empaquetas tus propios servidores MCP. El mismo principio aplica para los bundles de hooks: publicamos los cinco bundles SaaS como plugins en studiomeyer-io/studiomeyer-marketplace. Un claude plugin install studiomeyer-memory-hooks y tienes los cuatro hooks de Memory activos. Las ediciones manuales de settings.json son entonces solo para power-users que quieren customizar.
Anti-pattern: automatizar todo
La tentación es grande de llenar cada evento del lifecycle con un hook. No lo hagas. Los hooks son best-effort y se disparan en silencio. Si 8 hooks paralelos disparan operaciones de memoria y uno está roto, debugeas a oscuras. Regla de oro: un hook por caso de uso claramente definido. Memory necesita 4, CRM necesita 2, GEO necesita 1, Crew necesita 2, Academy necesita 2. Eso son 11 hooks en total, más es overkill.
Siguiente lección
La Lección 11 (próximamente) es la continuación: distribución de plugins. Cómo convertir tus propios bundles de hooks en un plugin instalable. El patrón es idéntico a la distribución de servidores MCP de la Fase 8.
Fuentes
- Claude Code Hooks Reference (oficial, incluye el schema
mcp_tooldesde v2.1.118) - Recipe 16.1: mcp_tool hook intro
- Recipe 16.2: Memory hook bundle