Publicado el
- 14 min read
Depuración de repositorios MCP: una guía práctica de resolución de problemas para fallos en el mundo real
Depuración de repositorios MCP: Guía práctica para resolver fallos reales
¿Tienes poco tiempo? La verdad es simple: la mayoría de los problemas en repositorios MCP se reducen a contratos desalineados, fallos silenciosos en el transporte o deriva invisible de la configuración. Vamos a solucionarlo.
Qué entendemos por “repositorios MCP”
Cuando los equipos dicen “repositorio MCP”, normalmente se refieren a una base de código que implementa servicios del Model Context Protocol: un servidor MCP que expone herramientas, recursos y prompts a un cliente compatible con MCP mediante JSON-RPC. El repo puede contener código del servidor, bindings del protocolo (TypeScript, Python u otros SDKs), manifiestos, fixtures de pruebas, arneses de integración y scripts de despliegue. En otras palabras, es la superficie del contrato donde la automatización se encuentra con la solicitud del usuario. Depurarlo bien implica saber dónde pueden romperse los contratos: protocolo, transporte, lógica de negocio, entorno y proceso de release.
Empieza con un bucle corto y repetible
Tu mejor herramienta es un bucle de retroalimentación rápido. Antes de sumergirte en trazas de paquetes, crea un repro conciso y repetible:
- Define el comando o acción del cliente que falla con lo mínimo posible.
- Fija sus entradas: variables de entorno, secretos, payloads de ejemplo.
- Captura salidas estables: logs, intercambios JSON, mensajes de error.
- Limita experimentos a diez minutos; si no hay progreso, baja una capa más (protocolo, luego transporte, luego lógica del servicio, luego runtime).
Este bucle te protege de dar palos de ciego y obliga a formular hipótesis observables.
Lista de comprobación de salud en cinco minutos
Si solo tienes cinco minutos, haz esto:
- Versiones: ¿SDK, runtime, cliente y servidor alineados? ¿Hay cambios incompatibles en las release notes?
- Puntos de entrada: ¿El servidor arranca sin errores? ¿Hay avisos en el inicio? ¿El cliente descubre capacidades?
- Transporte: ¿Se establecen conexiones? ¿Hay advertencias de TLS o framing? ¿Time outs?
- Permisos: ¿El servidor tiene acceso a rutas del sistema de ficheros, salida de red y permisos para crear procesos?
- Secretos: ¿Tokens presentes, no caducados y con el scope correcto?
- Límites de tasa: ¿Hay 429s o throttling del proveedor en los logs?
- CORS/proxy: Para transportes WebSocket o HTTP, ¿algún proxy reescribe cabeceras?
- Catálogos de recursos: ¿Puede el cliente listar recursos y prompts sin error?
- Prueba rápida de herramienta: Invoca una herramienta trivial o no-op. Si eso falla, es sistémico.
Entender los dominios de fallo
Piensa por capas. Cada capa tiene síntomas característicos.
- Capa de protocolo (JSON-RPC, esquemas MCP): nombres de métodos mal tipeados, parámetros erróneos, campos faltantes, formas de resultado inesperadas, desajustes de ID de correlación.
- Capa de transporte (stdio, WebSocket, orquestación de procesos): deadlocks por buffering, finales de línea, límites de tamaño de payload, lectores lentos, proxies rotos.
- Capa de lógica de negocio (herramientas, recursos, prompts): errores de permisos, fallos de dependencias, errores en APIs externas.
- Capa de entorno (SO, contenedor, CI): problemas de codificación, diferencias de rutas, locales faltantes, timeouts, DNS.
- Capa de ciclo de vida (build, versionado, release): artefactos inconsistentes, cachés obsoletos, código generado incompatible.
La mayoría de los bugs “de protocolo” son en realidad problemas de entorno o transporte disfrazados. Investiga la capa más baja que puedas probar con evidencias.
Diagnósticos a nivel de protocolo que realmente ayudan
- Repite el contrato: Imprime las peticiones y respuestas con IDs, nombres de método y parámetros truncados. Conserva logs completos para reproducibilidad, pero redacta secretos.
- Valida el esquema: Usa validación JSON estricta en builds de desarrollo. Falla ruidosamente ante campos desconocidos o claves requeridas ausentes.
- Correlación: Etiqueta cada línea de log con el id JSON-RPC. Sin correlación, la concurrencia oculta la cadena real de errores.
- Deriva de capacidades: En el arranque, vuelca las capacidades anunciadas, nombres de herramientas y rutas de recursos. Si el cliente consulta una herramienta que el servidor nunca anunció, has encontrado un desajuste (posiblemente un manifiesto obsoleto o un directorio no montado).
- Idempotencia y reintentos: Si el cliente reintenta por fallos de red, asegúrate de que las acciones de la herramienta sean idempotentes o detecta y desduplica invocaciones duplicadas.
Atento a estos “olores” de protocolo:
- “Method not found”: Tipografía, desajuste de versión o handler no registrado.
- “Invalid params”: Formas, tipos o claves anidadas erróneas. Compara el esquema que el código espera con los payloads reales.
- “Internal error”: Captura y registra excepciones originales con trazas en el límite.
Peligros del transporte y soluciones rápidas
Servidores basados en stdio:
- Flush agresivo: Si tu runtime hace buffering de stdout, el cliente puede quedarse bloqueado. Desactiva el buffering o flush tras cada mensaje.
- Finales de línea: Inconsistencias CRLF vs LF entre plataformas pueden romper lectores ingenuos.
- Deadlocks: Nunca registres en stdout si stdout es el canal del protocolo. Envía logs a stderr o a un fichero, o usa un logger estructurado que segregue streams.
- Payloads grandes: Fragmenta o streamnea resultados grandes; algunos lectores imponen límites de tamaño.
Servidores basados en WebSocket:
- Ping/pong: Las keepalives importan. Configura timeouts y heartbeats; descarta conexiones obsoletas con gracia.
- Proxies: Los proxies corporativos suelen romper o reescribir cabeceras. Prueba una conexión directa o un túnel de confianza.
- Framing: Asegúrate de enviar frames de texto para JSON; frames binarios pueden confundir clientes que esperan texto.
General:
- Backpressure: Un cliente lento puede bloquear escrituras. Usa colas acotadas y timeouts; muestra avisos cuando descargas o difieres mensajes.
- Timeouts: Define timeouts explícitos en cliente y servidor y regístralos de forma distinta a los fallos de negocio.
- TLS: SNI o certificados mal configurados pueden funcionar localmente pero fallar en CI. Registra sujetos de certificado y su caducidad en el arranque.
Arreglar el “funciona en mi máquina”
Comprueba la realidad del entorno:
- Locale y encoding: Diferencias de locale o manejo de Unicode pueden corromper JSON o rutas de ficheros.
- Permisos de ficheros: Herramientas que crean ficheros temporales pueden necesitar directorios escribibles; asegura TMPDIR/TEMP consistentes.
- Salida de red: Los runners de CI en subredes privadas suelen requerir NAT o proxy específicos. Prueba con una llamada externa mínima para verificar egress.
- Desfase horario: Tokens con ventanas estrechas fallan si el reloj del sistema se desequilibra. Sincroniza hora o verifica NTP.
Builds en contenedor:
- Fija versiones: Las imágenes base y mirrors cambian. Usa tags explícitos y lockfiles.
- Fugas multi-stage: Dependencias solo de build no deben ser necesarias en runtime; si lo son, producción falla aunque dev funcione.
- Problemas por no ser root: Al bajar privilegios, verifica propiedad de rutas y directorios R/W.
Diagnosticar fallos en la ejecución de herramientas
Las herramientas invocadas por un servidor MCP se comportan como mini servicios. Fallos comunes:
- PATH y virtualenv: La herramienta usa un runtime no instalado en producción. Muestra el PATH efectivo y la versión del intérprete al arrancar la herramienta.
- Permisos: Los sandboxes restringen forks, red o filesystem. Proporciona mensajes de error claros cuando las reglas del sandbox bloquean acciones.
- Agotamiento de recursos: Pools de hilos y subprocesses pueden hacer deadlock bajo carga. Añade timeouts por herramienta y mata invocaciones colgadas.
- APIs externas: Añade circuit breakers para proveedores inestables. Cachea metadatos estables. Expone un modo “dry run” que valida credenciales sin efectos secundarios.
Buena higiene:
- Distingue errores de usuario de errores del sistema. Los errores de usuario deben ser accionables (entrada errónea), los errores del sistema deben producir un código único para triage.
- Emite métricas: conteos de éxitos, fallos por tipo, duraciones y profundidad de colas.
Catálogos de recursos y prompts que no mienten
Al listar recursos o prompts:
- Regenera catálogos al inicio y al recargar en caliente. Listas obsoletas causan “resource not found” aunque el fichero exista.
- Normaliza rutas: Sensibilidad a mayúsculas y rutas relativas difieren entre SO. Almacena rutas canónicas absolutas internamente.
- Control de acceso: Filtra catálogos por cliente o rol de forma determinista; documenta por qué un recurso está oculto.
Si falla la lectura de un recurso, incluye la ruta resuelta, los permisos y el resultado de stat del fichero en los logs de depuración.
Depuración de rendimiento sin adivinar
Mide antes de optimizar:
- Presupuesto de latencia: Descompón la latencia de extremo a extremo en transporte, colas, ejecución de herramientas y llamadas externas. Registra cada segmento.
- Cold starts: Imports perezosos pueden añadir segundos. Precarga módulos y calienta caches durante la inicialización del servidor.
- Concurrencia: Define límites realistas. Demasiada paralelización provoca thrashing; muy poca detiene llamadas de alta latencia.
- Caché: Memoiza resultados deterministas costosos. Acota tamaños de caché para evitar presión de memoria.
- Tamaño del payload: Comprime o pagina resultados grandes. Ofrece muestreo de recursos para uso exploratorio.
Panel de fusilamiento para tests inestables
Los flakes son generalmente bugs de timing u orden:
- Race en init: El cliente envía peticiones antes de que el servidor anuncie capacidades. Bloquea hasta completar el arranque o devuelve una respuesta clara de “not ready”.
- Cancelaciones: Confirma que respetas y propagas señales de cancelación; las tareas zombis persisten si no.
- Excepciones async silenciadas: Asegúrate de que las excepciones no manejadas hagan fallar los tests o al menos marquen fallos; tareas silenciosas destruyen la confianza.
- Puertos aleatorios: Usa rangos de puertos fijos o lógica de pre-bind para evitar colisiones raras en CI.
Ejecuta tests con configuraciones de estrés: más concurrencia, latencia artificial e inyección de fallos. Captura seeds para reproducibilidad.
Haz que los logs paguen alquiler
Unos buenos logs acortan las caídas:
- Estructura: Usa logs JSON con campos para ids de correlación, método, herramienta, duración, estado, id de usuario/sesión (si aplica) y clase de error.
- Niveles que importan: info para flujo, warn para rutas degradadas, error para garantías rotas. Evita spam de debug en prod; actívalo a demanda.
- Muestreo: Mantén logs de payloads detallados muestreados para evitar costes preservando utilidad forense.
- Redacción: Borra automáticamente tokens y PII. Un solo secreto filtrado puede convertir un incidente menor en una brecha.
Introduce una taxonomía consistente de errores. Por ejemplo: EPROTO (protocolo), EXTERNAL (proveedor), ERESOURCE (filesystem), ECONFIG (secrets/config), ERATE (throttling), ECANCELED (cancelación). La taxonomía acelera el triage.
Observabilidad más allá de los logs
- Métricas: Contadores de llamadas, errores por clase, golpes de rate limit; histogramas de latencias; gauges de profundidad de cola y herramientas activas.
- Tracing: Span boundaries en recepción del protocolo, dispatch, inicio de herramienta, llamada externa y envío de respuesta. Propaga ids de trace en los logs.
- Alertas: Umbrales en tasa de error sostenida, latencia P95/P99 y bucles de reconexión. La fatiga por alertas es real; ajusta umbrales semanalmente hasta estabilizar.
Versionado, esquemas y seguridad del contrato
- Versionado semántico: Trata cambios relacionados con el protocolo como breaking salvo que se demuestre lo contrario. No cueles cambios de esquema en releases de parche.
- Feature flags: Controla nuevas capacidades; permite a los clientes optar. Anuncia flags en la carga de capacidades.
- Tests de contrato: Mantén fixtures doradas de request/response. Ejecútalas en cada build para evitar deriva accidental.
Config y secretos en los que puedas confiar
- Fuente única: Mantén la configuración en un único lugar por entorno. Evita magia de variables de entorno por script.
- Config tipada: Valida al arranque y falla rápido con lista clara de campos faltantes o malformados.
- Higiene de secretos: Rota regularmente, limita scope, y registra los cuatro últimos caracteres para identificación. Añade una prueba que falle si faltan secretos pero pase si hay placeholders en dev.
Guardarraíles de CI/CD que detectan regresiones
- Smoke tests: Arranca el servidor, lista capacidades, llama a una herramienta no-op y apaga. Ejecuta en cada commit.
- Chequeos de contrato: Repite transcripciones JSON-RPC doradas; diff de respuestas. Falla si hay campos inesperados o faltan claves requeridas.
- Fijado de dependencias: Bloquea dependencias a versiones exactas; renueva en un calendario.
- Imágenes reproducibles: Construye una vez, promociona entre entornos; no reconstruyas por etapa.
Playbooks de incidentes que realmente seguirás
Prepara runbooks concisos y vivos:
- El cliente no se conecta: Comprueba servidor, transporte, auth y capacidades. Prueba una consulta trivial de capacidades antes de llamadas a herramientas.
- Invocaciones de herramientas agotando tiempo: Mide profundidad de cola y latencias externas; aumenta timeouts temporalmente y activa logs de depuración.
- Alta tasa de errores tras un release: Haz rollback, biseca cambios, reproduce tests de contrato, inspecciona diff de dependencias.
- Tormentas de rate limit: Retrocede, cachea y prioriza rutas críticas. Coordina con proveedores externos.
Incluye árboles de decisión y comandos para copiar/pegar. El tiempo ahorrado en incidentes se paga solo.
Photo by Galina Nelyubova on Unsplash
Formas seguras de inspeccionar tráfico en vivo
- Mirra, no mutes: Si añades logging de peticiones y respuestas, escríbelo a un sink fuera de banda, no al canal del protocolo.
- Usa endpoints de depuración dedicados en non-prod: Reproduce peticiones capturadas allí.
- Para WebSocket: Un proxy transparente puede revelar framing y tiempos. Si no es posible, instrumenta ambos extremos para registrar tiempos y tamaños de envío/recepción.
Ten cuidado con captura de paquetes en enlaces TLS; prefiere observación a nivel de aplicación para evitar la complejidad del manejo de claves.
Mensajes de error comunes y lo que suelen significar
- “Method not found”: El servidor nunca registró el handler, o el cliente llama a una herramienta renombrada. Revisa la lista de capacidades anunciadas y descarta desajuste de versión.
- “Invalid params”: El cliente envió una forma de payload que el servidor no acepta. Compara versiones de esquema; añade un volcado de las peticiones con tipos.
- “Timeout”: Timeout de cliente demasiado agresivo o servidor con escasos recursos. Inspecciona histogramas de latencia y profundidad de cola.
- “Connection closed”: Red intermitente o crash de proceso. Revisa códigos de salida del servidor y límites de memoria; monitoriza reinicios.
- “Resource not found”: Catálogo obsoleto, desajuste de normalización de rutas, o filtro de control de acceso aplicado. Regenera el catálogo y registra la ruta resuelta.
- “Permission denied”: ACLs del filesystem, mismatch de usuario en el contenedor o restricciones del sandbox. Registra UID/GID efectivos y permisos de directorio.
Arnés de reproducción: tu arma secreta
Crea un pequeño “protocol loopback” tester dentro del repo:
- Alimenta requests conocidos-buenos y afirma respuestas exactas.
- Aleatoriza el orden de campos de payload para detectar parsers frágiles.
- Inyecta condiciones tipo red: lecturas retardadas, frames partidos y escrituras parciales.
- Proporciona un toggle que cambie transportes (stdio vs WebSocket) para comparar comportamientos.
Mantén este arnés como un CLI que se ejecute localmente y en CI.
Cuándo hacer bisect y cómo
Si un bug apareció “recientemente”, no debatas: biseca:
- Congela dependencias al último conjunto bueno y el conjunto actual. Si el bug cambia con las dependencias, has localizado la causa sin tocar código.
- Usa git bisect en commits de merge, no en squash merges, para estrechar la superficie.
- Biseca con tu smoke test, no con un test e2e complejo.
Una pequeña caja de herramientas que rinde mucho
- [jq] — Parse and filter JSON-RPC logs; build reproducible extracts for bug reports.
- [websocat] — Quick WebSocket tests; verify connectivity and message echoes.
- [mitmproxy] — Inspect and replay HTTP/WS flows in non-prod environments.
- [ripgrep] — Search code and logs fast; find silent “TODO” or experimental flags still enabled.
- [just] — Codify repeatable dev commands; standardize how the server starts, tests run, and logs collect.
- [direnv] — Keep per-repo env vars consistent; reduce “works on my machine.”
- [docker compose] — Recreate multi-service environments reliably for local reproduction.
- [gh CLI] — Fetch artifacts, PR diffs, and CI logs directly; speed up bisecting and rollback.
Documenta el uso de cada herramienta en CONTRIBUTING o DEVNOTES del repo.
Haz los mensajes de error humanos
Redacta mensajes que respondan a tres preguntas:
- ¿Qué falló exactamente?
- ¿Qué puede hacer el usuario a continuación?
- ¿Dónde pueden mirar los mantenedores para obtener más contexto?
Ejemplo: “Tool invocation failed: EXTERNAL/403 from provider. Check token scope ‘read:files’. See logs with correlation id 8f2a for full trace.”
Seguridad mientras depuras
- Nunca registres secretos en bruto. Enmascara por defecto; permite ver valores sin enmascarar solo en modo local seguro.
- Evita copiar payloads de producción en issues públicos. Reproduce con fixtures depuradas.
- Al adjuntar logs, comparte ventanas mínimas y redacta IDs. Una transcripción concisa y redactada vence a un volcado de 50 MB.
Documentación que evita tickets
Un README excelente para un repo MCP incluye:
- Comandos de arranque rápido para local y Docker.
- Versiones de SDK soportadas y su tabla de compatibilidad.
- Requests y responses de ejemplo para cada herramienta y recurso.
- Taxonomía de errores con mensajes de ejemplo.
- Listas de comprobación de troubleshooting y problemas conocidos.
- Cómo habilitar modo debug de forma segura en producción.
Añade una insignia o fecha de “Última verificación” para mantener la confianza en la doc.
Gobernanza: quién es responsable de qué
La claridad acelera las correcciones:
- Propietarios por herramienta: lista de mantenedores y vías de escalado.
- SLOs: Define objetivos de disponibilidad y latencia; alinea alertas en consecuencia.
- Tren de releases: Decide cadencia de corte (semanal, quincenal) y cúmplela. Las sorpresas causan outages.
- Deprecaciones: Puertas de versión y anuncios para retiradas. Proporciona notas de migración con ejemplos.
Recetas de ganancia rápida
- Lecturas stdio atascadas: Cambia logs a stderr, configura escrituras con buffer por línea, añade flush tras cada mensaje JSON. Confirma con un pequeño test de echo.
- “Method not found” misterioso: Vuelca la lista de capacidades al inicio y tras hot-reload. Compárala con la vista en caché del cliente.
- CI inestable: Fija seeds, bloquea puertos, serializa tests con races conocidas y añade un 10% de margen a los timeouts mientras investigas.
- Límites de proveedor: Cachea metadatos, backoff exponencial y muestra hints de “retry after” en mensajes de error al cliente.
Construye una cultura de cambios pequeños y visibles
Los cambios grandes y silenciosos generan outages en sábado. Prefiere:
- Pull requests pequeños con notas de release precisas.
- Feature flags con nombres de responsables.
- Bumps de versión obvios para modificaciones que afectan al protocolo.
- Smoke tests post-merge que puedan revertir automáticamente si fallan.
Un empujón final: mide y decide
Depurar repositorios MCP es menos cuestión de heroicidad y más de pensamiento sistémico. Instrumenta los bordes del protocolo, controla el entorno y respeta los contratos. Una vez que puedas ver el sistema con claridad, la mayoría de fallos “aleatorios” se convierten en arreglos de una línea: vaciar un buffer, fijar una versión o rechazar un payload inválido con gracia.
External Links
Debug MCP Server Like a PRO with MCP Inspector - YouTube How to Debug MCP Server with Anthropic Inspector? - Snyk The fastest way to debug MCP servers : r/modelcontextprotocol MCP Inspector: Test and Debug your MCP Server Locally - YouTube Debugging - Model Context Protocol