8 min de lectura

¿Por qué los fallos de inicio de los servidores MCP parecen iguales?

Los fallos de inicio de un servidor MCP se pueden diagnosticar cuando separas las pruebas de inicio, handshake, descubrimiento de herramientas y acciones externas.

¿Por qué los fallos de inicio de los servidores MCP parecen iguales?

Un cliente MCP puede indicar que un servidor «no pudo iniciarse» incluso después de que el sistema operativo haya iniciado el proceso, el proceso haya leído la entrada y el servidor ya haya contactado con algo fuera de la máquina. Ese mensaje no es un diagnóstico. Es un contenedor donde se mezclan fallos de inicio, del protocolo, de descubrimiento y, a veces, de ejecución de herramientas.

Trata el inicio como una secuencia de límites que dejan pruebas. Si no puedes decir qué límite cruzó el servidor, no puedes indicar al operador si es seguro reintentar, si se pudo usar una credencial o si el cliente simplemente no mostró un servidor saludable. La solución no es aumentar el timeout. Hay que separar los estados y hacer observable cada uno.

Un único estado rojo oculta cuatro fallos distintos

El operador necesita cuatro respuestas, en este orden: ¿el cliente inició el comando configurado?, ¿ambos lados completaron la inicialización de MCP?, ¿el cliente recibió una lista de herramientas utilizable?, ¿algún código alcanzó un canal externo? Cada respuesta demuestra algo diferente.

Un proceso puede fallar antes de existir en el sentido habitual. Puede faltar el ejecutable, no existir el directorio de trabajo, fallar un ejecutor de paquetes antes de invocar tu código o terminar el proceso hijo de inmediato porque falta una variable de entorno necesaria. Llama a esto fallo de inicio. No hay una sesión MCP y es posible que el código de tu aplicación ni siquiera se haya ejecutado.

El proceso también puede existir y aun así fallar en el intercambio del protocolo. Con stdio, el proceso hijo tiene stdin y stdout conectados al cliente. El servidor debe leer JSON-RPC desde stdin y escribir únicamente mensajes JSON-RPC en stdout. Después debe responder a la solicitud initialize del cliente con una versión de protocolo compatible y las capacidades declaradas. El cliente continúa con notifications/initialized. Si la secuencia no termina, es un fallo de handshake.

Un handshake correcto no demuestra que el cliente haya conocido las herramientas. El servidor puede declarar que admite herramientas pero fallar al registrarlas, generar un esquema de entrada no válido, devolver un resultado incorrecto de tools/list o devolver una lista vacía porque su propia configuración desactivó todas las herramientas. Es un fallo de descubrimiento de herramientas. Cliente y servidor pueden estar suficientemente sanos para intercambiar mensajes, pero no habrá nada que el agente pueda llamar.

Por último, un servidor puede completar el descubrimiento y fallar solo cuando se ejecuta una herramienta. Es un fallo de ejecución de herramientas y debe constar en otro incidente. Si lo mezclas con el inicio, tarde o temprano alguien reintentará un servidor que ya envió una solicitud HTTP o abrió una conexión SSH.

La documentación de Model Context Protocol deja clara esta separación, aunque muchas interfaces de cliente no lo hagan. Sus indicaciones de depuración distinguen los problemas de proceso y configuración de los registros del protocolo, y advierten que los servidores stdio locales deben mantener stdout libre de registros normales. El ciclo de inicialización del protocolo y la solicitud tools/list son intercambios distintos. Conserva esa diferencia en tu propia telemetría en lugar de aceptar la etiqueta genérica del cliente.

El fallo de inicio termina antes de que exista MCP

Un fallo de inicio significa que el cliente no obtuvo un proceso hijo utilizable con un flujo de protocolo legible. No significa que el comando «pareciera correcto» en un terminal.

Las shells interactivas ocultan muchas cosas. Tu shell tiene un PATH, un directorio actual, gestores de versiones de lenguajes, credenciales y archivos de configuración que una aplicación de escritorio o un subproceso de agente quizá no herede. Un cliente puede iniciarse con / como directorio de trabajo en macOS. Puede usar un entorno restringido. También puede pasar el comando como un ejecutable con un array de argumentos en lugar de hacerlo mediante una shell, por lo que los alias y las redirecciones no tendrán efecto.

Captura el registro exacto del inicio antes de intentar razonar sobre MCP:

run_id=run_01JX...
phase=launch
command=/usr/local/bin/node
argv=["/Users/dev/work/acme-mcp/dist/index.js"]
cwd=/
pid=84217
started_at=2026-07-22T14:03:12.417Z

Después captura un evento terminal cuando el proceso termine o expire el plazo del handshake:

run_id=run_01JX...
phase=launch
exit_code=1
signal=null
stderr=Error: ENOENT: no such file or directory, open './config.json'

Ese registro resuelve rápidamente una discusión habitual. El servidor no «tenía un problema de MCP». Suponía que una ruta relativa se resolvería desde el directorio del proyecto, pero el cliente lo inició desde /.

Usa rutas absolutas para el ejecutable, el punto de entrada, los archivos de configuración y cualquier archivo que se lea durante el inicio. La guía de depuración de MCP señala expresamente los directorios de trabajo indefinidos de los servidores iniciados por el cliente y recomienda rutas absolutas. No es una precaución de estilo. Elimina una fuente de fallos que solo aparece cuando alguien instala la misma configuración en otra máquina.

No declares que el inicio tuvo éxito solo porque recibiste un PID. Un PID indica que el kernel creó un proceso. No dice si el programa se cargó, si la tubería stdout está intacta o si el proceso ya se convirtió en zombie a la espera de que lo recojan.

Una máquina de estados de inicio útil es pequeña:

not_requested
  -> spawn_requested
  -> spawned
  -> executable_ready
  -> handshake_pending

spawned significa que el proceso padre recibió el PID del hijo. executable_ready significa que el hijo escribió en stderr un evento deliberado de disponibilidad, no perteneciente al protocolo, después de cargar la configuración e instalar su controlador de errores fatales. No envíes ese evento a stdout. En un servidor stdio, stdout no es un canal de registro cercano al protocolo. Es el cable.

El evento de disponibilidad no debe afirmar que el servidor está conectado a una API, una base de datos o un host remoto. Solo debe indicar lo que demuestra: que el proceso alcanzó la configuración de su transporte MCP. Una línea como ready=true se vuelve peligrosa cuando los equipos la interpretan en silencio como «es seguro llamar a las herramientas». Nombra la fase.

El handshake tiene una definición precisa

Un fallo de handshake de MCP comienza después de que exista un proceso utilizable y termina antes de completar el ciclo de inicialización. No lo llames fallo de conexión sin comprobar los mensajes.

En un transporte stdio importan los primeros bytes de stdout. Un banner de inicio puede corromper el flujo antes de que tu servidor vea la solicitud. Lo mismo puede hacer una dependencia que imprime un aviso de actualización, un console.log, un print de Python, un formateador de excepciones del framework o un script envoltorio que escribe texto de estado en stdout. La guía oficial de compilación y depuración de MCP lo dice claramente: en servidores stdio, escribe los registros en stderr porque stdout transporta los mensajes del protocolo.

Esta es la traza mínima de una inicialización correcta:

{"direction":"in","id":1,"method":"initialize"}
{"direction":"out","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{}},"serverInfo":{"name":"acme","version":"1.4.0"}}}
{"direction":"in","method":"notifications/initialized"}

La versión exacta del protocolo depende de las versiones compatibles con el cliente y el servidor. Lo importante es que el servidor seleccionó una versión que el cliente acepta, devolvió un resultado válido y recibió la notificación de finalización. Guarda un evento interpretado para cada mensaje, no cargas completas que puedan contener credenciales.

Si la traza empieza así, el diagnóstico cambia:

stdout: Starting Acme MCP server
{"jsonrpc":"2.0","id":1,"method":"initialize",...}

El servidor puede ser perfectamente capaz de responder, pero el analizador JSON del cliente ya encontró una entrada no válida. Un timeout después de ese punto no indica que el servidor fuera lento. Indica que el transporte se corrompió.

Otro fallo habitual parece más saludable:

phase=handshake
initialize_received=true
initialize_response_sent=false
fatal_error=Cannot read properties of undefined (reading 'tools')

El hijo se inició, recibió la solicitud y falló mientras preparaba la respuesta. Es un error del servidor o una suposición de configuración no controlada, no una mala configuración del cliente.

Haz explícitos los límites en los registros:

phase=handshake event=initialize_received run_id=run_01JX request_id=1
phase=handshake event=initialize_responded run_id=run_01JX request_id=1 protocol_version=2025-06-18
phase=handshake event=initialized_received run_id=run_01JX

Si solo escribes connected=true, borras el dato que separa una respuesta enviada de un ciclo de inicialización completado. Los clientes pueden cerrar o reiniciar después de recibir la respuesta, pero antes de enviar la notificación. Operativamente, eso no es lo mismo que un error del analizador de inicialización.

Asigna al handshake su propio plazo. Inícialo cuando se cree el proceso, o cuando el transporte esté listo si puedes observar ese momento. Deténlo cuando llegue notifications/initialized. Al expirar, informa del último evento confirmado, como spawned_no_initialize, initialize_received_no_response o response_sent_no_initialized. Esos nombres indican al operador dónde debe mirar primero.

El descubrimiento de herramientas falla después de que el servidor ya sea accesible

Un fallo de descubrimiento de herramientas significa que cliente y servidor pueden hablar MCP, pero el cliente no recibió una respuesta utilizable a tools/list. A menudo se informa como un fallo de inicio porque muchos clientes descubren las herramientas justo después de la inicialización.

No supongas que un panel de herramientas vacío demuestra que el resultado de tools/list estaba vacío. Algunos clientes ocultan las herramientas cuando falla la validación del esquema. Otros guardan en caché los resultados del descubrimiento. Algunos solicitan las herramientas de forma diferida, solo cuando el agente empieza una tarea. Otros se conectan a un servidor MCP para obtener recursos o prompts y nunca solicitan herramientas. Tus pruebas deben capturar tanto la solicitud como la respuesta.

Una traza de descubrimiento correcta tiene esta forma:

{"direction":"in","id":2,"method":"tools/list"}
{"direction":"out","id":2,"result":{"tools":[{"name":"issue_lookup","description":"Fetch one issue by identifier","inputSchema":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}}]}}

El registro de descubrimiento debe incluir el número de herramientas y un resumen criptográfico de los esquemas normalizados. Ese resumen permite saber si dos ejecuciones anunciaron la misma interfaz sin guardar descripciones o configuraciones sensibles. También detecta cambios accidentales en los que una herramienta sigue existiendo, pero desaparecen sus parámetros obligatorios.

No construyas las definiciones de herramientas contactando con un servicio externo durante tools/list. Ese diseño convierte el descubrimiento en un efecto secundario, hace que una actualización del cliente parezca una ejecución y crea la peor pregunta posible en un incidente: «¿Enumerar las herramientas cambió algo?». El registro de herramientas debe ser local y determinista siempre que sea posible.

Un servidor puede necesitar configuración para decidir si anuncia una herramienta. Lee esa configuración al inicio y registra el resultado, pero no hagas que el descubrimiento espere a una renovación de token o a una comprobación SSH. Si una herramienta necesita una credencial, valida que exista su referencia local sin usarla. Deja la acción remota real para cuando el cliente llame a la herramienta.

Esta diferencia importa para el control de los agentes. Si un agente llega a Sallyport mediante sp mcp, un registro de descubrimiento MCP correcto solo demuestra que el puente expuso operaciones invocables, no que Sallyport realizara una acción HTTP o SSH.

Hay una razón legítima para devolver una lista vacía: la configuración actual no tiene herramientas activadas. Indícalo en una respuesta estructurada o en un registro visible para el cliente. No falles durante el registro y dejes que el cliente adivine si el conjunto de herramientas está vacío a propósito.

phase=discovery event=tools_list_responded run_id=run_01JX tool_count=0 reason=no_enabled_tools

Ese registro da al operador un problema de configuración que resolver. Un error genérico de inicio solo le da una superstición que repetir.

La accesibilidad externa necesita una prueba propia

No uses credenciales al iniciar
Sallyport mantiene las claves de API y SSH en su bóveda cifrada y solo devuelve los resultados de las acciones.

La pregunta «¿el proceso alcanzó un canal externo?» no puede responderse con un PID, un handshake correcto ni una lista de herramientas completa. Necesitas un evento en el límite donde tu código intenta realizar la acción externa.

Define «canal externo» de forma estricta. Aquí incluye una solicitud HTTP saliente, la invocación de un asistente SSH, una conexión a una base de datos fuera del proceso local, una renovación de credenciales en la nube, la publicación en una cola de mensajes o cualquier llamada que pueda crear un efecto o revelar información fuera de la sesión MCP. Leer un archivo de configuración local no cuenta. Cargar una credencial desde un almacén local protegido tampoco cuenta por sí solo. Enviarla en una solicitud sí.

Registra el intento antes de iniciar la llamada y después registra su resultado. Usa un ID de acción opaco que pueda relacionarse con el ID de solicitud MCP y el ID de ejecución del servidor.

run_id=run_01JX phase=execution event=external_attempt action_id=act_8Qf tool=issue_lookup channel=https host=api.example.test
run_id=run_01JX phase=execution event=external_result action_id=act_8Qf status=200 duration_ms=184

No incluyas en esos registros encabezados de autorización, tokens bearer, URL firmadas, argumentos de comandos que contengan secretos ni cuerpos completos de respuestas. Un registro de incidente que filtre la credencial que debía investigar empeora el incidente.

La ubicación de external_attempt no es un detalle académico. Si lo colocas demasiado pronto, afirmarás que hubo una llamada externa cuando el código solo creó un objeto de solicitud. Si lo colocas demasiado tarde, un timeout o un fallo del proceso puede dejar un hueco después de que los bytes ya hayan salido de la máquina. Emítelo justo antes de la llamada de biblioteca que puede iniciar actividad de red o SSH. Si la biblioteca ofrece un hook de conexión o solicitud de bajo nivel, registra allí un segundo evento solo si puedes hacerlo sin confundir el significado de «intento».

Un caso práctico muestra por qué importa. Un operador añade un servidor MCP que lee un token del sistema de incidencias durante la inicialización del módulo y llama a un endpoint «who am I» para validarlo. El proceso hijo se inicia, escribe una línea de depuración en stdout y corrompe el primer mensaje MCP. El cliente muestra «el servidor no pudo iniciarse». El equipo reinicia el cliente dos veces.

Sin registros por fase, concluyen que ninguna solicitud salió de la máquina porque el servidor nunca apareció en la interfaz del cliente. La conclusión es falsa. La llamada de inicialización del módulo se ejecutó antes de que el cliente enviara initialize y contactó tres veces con el sistema de incidencias. El estado de la interfaz no decía nada sobre la accesibilidad externa.

Mueve la comprobación de identidad a una herramienta deliberadamente de solo lectura o conviértela en parte de la primera acción que realmente necesite el servicio remoto. Después regístrala como ejecución de herramienta. El servidor puede iniciarse, inicializarse y listar herramientas sin tocar la red. El operador puede distinguir ahora entre «el servidor está disponible» y «la credencial y el servicio remoto funcionan». Son hechos distintos y deben mantenerse separados.

Un registro de fases convierte incidentes vagos en afirmaciones comprobables

Crea una entrada por ejecución y añade eventos de fase inmutables. No necesitas un motor de reglas complejo. Necesitas nombres estables, marcas de tiempo y suficientes campos de correlación para reconstruir lo ocurrido.

Usa esta estructura:

{
  "run_id": "run_01JX",
  "server_name": "acme",
  "pid": 84217,
  "phase": "discovery",
  "event": "tools_list_responded",
  "request_id": 2,
  "tool_count": 4,
  "at": "2026-07-22T14:03:13.083Z"
}

El registro debe guardar eventos, no conclusiones pegadas en una cadena. phase=handshake y event=initialize_received se pueden contar, consultar y probar. message="MCP parece atascado" no.

Mantén el modelo de estados deliberadamente sencillo:

  1. spawn_requested, spawned, executable_ready y exited pertenecen al inicio.
  2. initialize_received, initialize_responded e initialized_received pertenecen al handshake.
  3. tools_list_received y tools_list_responded pertenecen al descubrimiento.
  4. tool_call_received, external_attempt y external_result pertenecen a la ejecución.
  5. revoked, terminated y client_disconnected describen una interrupción, no un éxito.

La distinción que los equipos suelen confundir es establecimiento de sesión frente a autorización para actuar. Un servidor puede establecer una sesión MCP sin tener permiso para usar una credencial o conectarse a un sistema remoto. Si tratas ambos estados como uno solo, un evento de aprobación puede parecer un evento de conectividad y una acción denegada puede parecer un inicio fallido.

Mantén los eventos de autorización junto a la llamada que controlan. Por ejemplo, registra authorization_requested y authorization_granted después de tool_call_received, pero antes de external_attempt. Así, el operador puede afirmar con pruebas que llegó la solicitud de herramienta, que una persona la denegó y que no hubo ningún intento externo. Es mucho más preciso que decir que la solicitud «no se completó».

Usa un ID de ejecución que solo dure lo que dura un proceso hijo. No reutilices el nombre del servidor como identificador de correlación. Un cliente puede iniciar dos copias del mismo servidor, reiniciar una después de un timeout y conservar metadatos antiguos de herramientas. Los identificadores reutilizados convierten esos intentos separados en una historia inventada.

Aplica hashes o redacta los valores que revelen datos de usuario. Por lo general necesitas el nombre de la herramienta, el host del endpoint, la clase de estado, la clase de error y la duración. Rara vez necesitas una cadena de consulta, un cuerpo de solicitud o una respuesta. El operador debe demostrar que se cruzó el límite, no reproducir datos del usuario a partir de los registros.

Prueba los límites sin confiar en el cliente completo

Usa SSH sin compartir las claves
Su asistente sp-ssh integrado ejecuta comandos SSH sin que el agente reciba la clave SSH.

Un cliente completo sirve para las pruebas de integración, pero es un primer testigo poco fiable. Su interfaz puede comprimir errores, guardar capacidades en caché, reiniciar procesos hijos y aplicar su propio timeout. Prueba cada límite por una ruta más estrecha antes de culpar al servidor o al cliente.

Empieza con el comando exacto, el entorno y el directorio de trabajo que usa el cliente. No sustituyas el comando configurado por npm run dev. No lo ejecutes desde la carpeta del proyecto si el cliente lo inicia desde otro lugar. Redirige stderr a un archivo para inspeccionarlo, pero deja stdout intacto si otro proceso hablará MCP a través de él.

Para un servidor stdio, usa MCP Inspector como primera prueba del protocolo. La documentación de MCP recomienda Inspector para probar servidores en distintos transportes, y el proyecto Inspector puede iniciar directamente un comando stdio. Permite observar el intercambio de inicialización e invocar tools/list sin adivinar qué hizo un cliente de escritorio con el resultado.

Después reduce la prueba a tres comprobaciones:

1. ¿El comando configurado permanece activo el tiempo suficiente para recibir initialize?
2. ¿Devuelve una respuesta initialize válida y recibe initialized?
3. ¿tools/list devuelve los nombres y esquemas de herramientas esperados?

Solo después de superar esas comprobaciones debes llamar a una herramienta que alcance un sistema externo. Elige una acción de solo lectura con un objetivo inofensivo. Confirma que el registro de ejecución contiene un external_attempt y un resultado final. Si una llamada puede modificar datos, pruébala en un entorno desechable o proporciona una operación de simulación específica que no contacte con el endpoint de producción.

El repositorio oficial de MCP Inspector resulta útil porque hace visible el límite del transporte. No es un proxy de interceptación de red para el tráfico de tu servidor. Actúa como cliente MCP del servidor seleccionado y ofrece una interfaz de navegador para la prueba. La diferencia importa al investigar una corrupción del transporte: Inspector puede reproducir el lado cliente del protocolo, pero no puede demostrar qué escribió en la tubería otro cliente de producción.

Para transportes HTTP, añade pruebas HTTP sin confundirlas con el estado de MCP. Registra el método de la solicitud, la ruta del endpoint, el estado, el identificador de sesión cuando exista y si la respuesta contenía JSON o comenzaba un flujo de eventos. Una conexión TCP o un HTTP 200 no significan automáticamente que se completara la inicialización de MCP. Aplica los mismos registros del ciclo de vida después de que la solicitud HTTP llegue a tu servidor.

Mantén en tu conjunto de pruebas un servidor de prueba que falle deliberadamente en cada límite. Un dispositivo termina antes de leer la entrada. Otro escribe hello en stdout antes de responder. Un tercero responde a initialize y después devuelve un esquema de herramienta no válido. Un cuarto enumera una herramienta cuyo controlador registra un intento externo y devuelve un error controlado. Si tu integración de cliente convierte los cuatro casos en la misma alerta, corrige la integración antes de que un servidor real te obligue a depurar a ciegas.

Los timeouts y los reintentos necesitan una fase responsable

Controla las llamadas sin reglas de políticas
La escalera de decisiones fija ofrece un control de bóveda, aprobación de sesión y aprobación de clave para cada llamada.

Un único timeout de inicio fomenta la reparación equivocada. Hace que una descarga lenta de paquetes, un error del analizador de inicialización, una excepción del esquema y una espera de la API remota parezcan lo mismo. Usa plazos separados porque cada uno pertenece a un responsable diferente.

El iniciador es responsable del periodo entre spawn_requested y spawned. El servidor y el transporte lo son del periodo entre el inicio y initialized_received. La ruta de registro del servidor es responsable del descubrimiento. El controlador de la herramienta y su dependencia remota lo son de la ejecución. Nombra el timeout según su responsable y emite el último evento de fase confirmado.

error=handshake_timeout last_event=initialize_received run_id=run_01JX

Eso permite actuar. Indica al responsable del servidor que revise la creación de la respuesta y stderr, no la API remota.

El reintento automático solo es seguro cuando la fase fallida no tiene efectos externos. Puede ser aceptable reintentar un proceso que no se creó porque el ejecutable no estaba disponible temporalmente. Reintentar una solicitud de descubrimiento suele ser aceptable si el descubrimiento es local y puro. Reintentar una llamada de herramienta después de external_attempt es peligroso, salvo que la operación remota tenga un mecanismo de idempotencia documentado y se le asigne un valor de idempotencia.

No ocultes el reintento detrás de un reinicio del servidor. Si el código de inicio renueva un token, crea un túnel, envía telemetría o valida una identidad remota, el reinicio ya es una acción externa. Es otra razón para mantener el inicio local y trasladar el trabajo remoto a herramientas explícitas.

Cuando un cliente termina un proceso hijo al vencer un plazo, emite un evento de interrupción antes de terminarlo si puedes. Es posible que el servidor no llegue a vaciarlo. El proceso padre debe registrar la solicitud de terminación como su propio evento, incluido el último evento del hijo que observó. Así queda un registro honesto: el proceso quizá estaba a punto de responder, pero no afirmas que lo hiciera.

Haz que el inicio sea aburrido antes de hacerlo rápido

Un buen servidor MCP puede iniciarse sin red, sin usar credenciales, sin efectos secundarios mutables y sin ambigüedad sobre el estado de su protocolo. Carga la configuración local, instala su transporte, responde a la inicialización y anuncia una interfaz determinista. Ese comportamiento es más fácil de operar y más seguro de reintentar.

La recomendación errónea más popular es «verificarlo todo al iniciar». Parece responsable porque los errores aparecen pronto. En la práctica mezcla configuración local, identidad, disponibilidad remota y autorización en un único ritual opaco. También hace que los clientes reintenten acciones externas bajo la etiqueta de error de inicio.

Valida localmente todo lo que puedas. Informa de la accesibilidad remota mediante una herramienta explícita o mediante la primera operación que la necesite. Mantén estables los nombres de las fases. En stdio, reserva stdout exclusivamente para el protocolo. Prueba un servidor roto deliberadamente para cada límite que afirmes observar.

Cuando el próximo cliente diga que un servidor MCP no pudo iniciarse, debes poder responder cuatro preguntas a partir de un único registro de ejecución: si el proceso se inició, si terminó la inicialización, si las herramientas eran descubribles y si algo llegó al mundo exterior. Si no puedes responder las cuatro, el estado sigue siendo ambiguo.

FAQ

¿Por qué mi cliente MCP muestra un solo error para varios problemas de inicio distintos?

Trata el inicio del proceso, la inicialización del protocolo, el descubrimiento de capacidades y la primera acción externa como estados separados. Un cliente puede mostrar un único error genérico aunque solo haya fallado uno de esos estados. Los registros deben indicar qué límite se cruzó en cada estado.

¿Que un proceso MCP esté en ejecución significa que el servidor se conectó correctamente?

No. Un proceso en ejecución solo demuestra que el sistema operativo lo creó y que aún no ha terminado. Puede estar bloqueado cargando la configuración, esperando una dependencia, escribiendo texto incorrecto en stdout o ignorando la solicitud initialize del cliente.

¿Los mensajes en stdout pueden romper un servidor MCP stdio?

En un servidor stdio, stdout es el canal del protocolo. Un banner, una traza de error, un aviso del gestor de paquetes o una instrucción de impresión normal puede corromper el flujo JSON-RPC antes de que el servidor responda a initialize. Envía los mensajes de diagnóstico a stderr.

¿Qué es el handshake de inicialización de MCP?

El cliente envía una solicitud initialize, el servidor devuelve una versión de protocolo y unas capacidades compatibles, y después el cliente envía la notificación notifications/initialized. Un servidor que se inicia pero no completa ese intercambio tiene un fallo de handshake, no un problema con las herramientas.

¿Qué significa que falle el descubrimiento de herramientas MCP?

Significa que el cliente completó la inicialización, pero no obtuvo un resultado utilizable de tools/list. La causa puede ser un controlador ausente, una excepción al crear los esquemas, una declaración de capacidades no compatible o que el cliente ni siquiera solicite herramientas.

¿Debe un servidor MCP contactar con una API externa durante el inicio?

No pongas una solicitud de red, un inicio de sesión SSH, una renovación de token o una búsqueda de secretos en el inicio del servidor salvo que este no pueda funcionar sin ello. Inicia primero el punto final del protocolo y realiza la llamada externa dentro del controlador de la herramienta, registrándola como una acción independiente.

¿Cómo puedo reproducir un fallo de inicio de MCP fuera de mi cliente?

Ejecuta el mismo comando de inicio con el mismo usuario, directorio de trabajo y entorno que usa el cliente. Después pruébalo con MCP Inspector o con un intercambio JSON-RPC controlado. Una prueba desde el terminal que use tu shell interactiva puede ocultar el fallo real.

¿Qué debo registrar para solucionar problemas de inicio de un servidor MCP?

Usa un ID de ejecución único desde el inicio y asócialo a los eventos de stderr, los eventos del protocolo, el descubrimiento de herramientas y cada solicitud saliente. Registra el ID del proceso, la ruta del ejecutable, el estado de salida y un ID de acción externa. No registres credenciales ni cuerpos de solicitudes por defecto.

¿Cuánto debe durar el timeout de inicio de MCP?

Un límite de tiempo es razonable, pero debe indicar la fase que protege. Usa un plazo corto para el inicio, otro para la inicialización y otro distinto para tools/list; de lo contrario, un único mensaje de timeout elimina el diagnóstico que necesitas.

¿Es seguro reintentar automáticamente el inicio fallido de un servidor MCP?

No necesariamente. Reintentar un inicio fallido puede repetir efectos secundarios si el código de inicio crea archivos, renueva tokens o contacta con servicios. Reintenta solo una fase que sepas que no tiene efectos externos y asigna un ID de ejecución distinto a cada intento.

Sallyport

Sallyport ejecuta llamadas de API y comandos SSH por tu agente de IA. Las claves se quedan en una bóveda local de tu Mac; tú apruebas cada ejecución y cada acción queda en un registro sellado.

© 2026 Sallyport · Código abierto bajo Apache-2.0 · Oleg Sotnikov