8 min de lectura

¿Puede la presión de vuelta de MCP stdio bloquear tu agente?

La presión de vuelta de MCP stdio puede bloquear un agente con resultados de herramientas demasiado grandes. Reproduce bloqueos de tuberías, limita resultados, vacía la salida de forma segura y prueba la recuperación tras una cancelación.

¿Puede la presión de vuelta de MCP stdio bloquear tu agente?

Un resultado grande de una herramienta MCP puede bloquear un proceso de agente aunque cada línea de la implementación del protocolo sea técnicamente válida. El fallo aparece cuando se trata stdio como un bus de mensajes infinitamente rápido. En realidad es un flujo de bytes con capacidad limitada entre dos procesos, y ambas direcciones pueden detenerse cuando cualquiera de los dos deja de leer.

Esto importa más en las herramientas de agentes que en los programas de línea de comandos habituales. Una herramienta puede producir un resultado de búsqueda enorme, un archivo codificado, una respuesta completa de una API o la transcripción detallada de un comando. Después, el agente puede tokenizarlo, resumirlo, esperar una aprobación o decidir simplemente que ya tiene suficiente contexto. Si el host deja de vaciar stdout del servidor durante cualquiera de esas tareas, el servidor puede bloquearse a mitad de la respuesta. Una vez bloqueado, quizá ya no lea una notificación de cancelación ni una solicitud posterior desde stdin.

La solución no es una sola configuración. Necesitas un contrato de respuesta que mantenga los resultados pequeños, un bucle de transporte que vacíe stdout de forma independiente del trabajo del agente y una ruta de cancelación que llegue al trabajo que produce el resultado. Prueba las tres cosas con cargas deliberadamente problemáticas.

Una tubería bloqueada puede parecer un fallo del agente

Una tubería stdio bloqueada produce síntomas que llevan a los equipos en la dirección equivocada. El agente parece congelarse después de una llamada a una herramienta. El servidor sigue siendo un proceso activo. El uso de CPU puede ser bajo. Se activa un tiempo de espera, pero el reintento también se cuelga. Alguien culpa al tiempo de ejecución del modelo, al SDK de MCP o a un bloqueo en la implementación de la herramienta.

A menudo la herramienta ya ha terminado su trabajo. Está atascada en write() mientras intenta entregar una respuesta que el host ya no está leyendo. La capacidad de la tubería es limitada y depende de la plataforma. La documentación de procesos secundarios de Node dice exactamente lo que los programadores de shell saben desde hace décadas: cuando un subproceso escribe más de lo que cabe en una tubería y el proceso padre no captura la salida, el subproceso se bloquea hasta que la tubería acepta más bytes.

En este incidente hay dos colas distintas, y confundirlas lleva a aplicar malas soluciones.

  • La tubería del sistema operativo contiene los bytes de stdout sin procesar entre el servidor MCP y su host.
  • La cola de la aplicación del host contiene los mensajes JSON-RPC analizados que esperan al código del agente, al código de la interfaz, al registro o al ensamblado del contexto.

Aumentar una cola de la aplicación no sirve de nada si nadie está leyendo la tubería. Aumentar el búfer de una tubería o de un flujo puede retrasar el bloqueo, pero da al servidor más espacio para crear una respuesta que el agente nunca debería haber recibido. El presupuesto de resultados decide qué pertenece a la conversación. La gestión de la presión de vuelta decide qué ocurre cuando una de las partes es más lenta. Son problemas distintos.

La guía oficial de depuración de MCP también ofrece un límite de diagnóstico sencillo: los servidores stdio locales deben mantener los registros fuera de stdout. Envía los diagnósticos a stderr. Si stdout contiene un banner, un rastreo de pila o una línea de progreso que no sea un dato del protocolo, ya tienes un fallo de entramado antes de llegar siquiera al problema de los resultados grandes.

stdout debe seguir siendo legible mientras continúa el trabajo

El host tiene la regla más importante: conecta el lector de stdout y mantenlo funcionando durante toda la vida del proceso del servidor. No lo vincules a una promesa que espere a que el agente termine de considerar el resultado de una herramienta. No lo pauses durante un diálogo de aprobación. No esperes a un renderizador, una escritura en la base de datos o una solicitud al modelo antes de aceptar los bytes siguientes.

Usa un bucle de transporte con responsabilidades limitadas:

  1. Lee stdout como bytes en cuanto el sistema operativo los proporcione.
  2. Entrega esos bytes al analizador de tramas del protocolo.
  3. Rechaza las tramas mal formadas o demasiado grandes como fallos de transporte.
  4. Entrega los mensajes completos a un distribuidor acotado separado del lector.
  5. Sigue vaciando la salida o termina deliberadamente el proceso hijo cuando el distribuidor no pueda aceptar más trabajo.

La palabra importante es «separado». Un lector que ejecuta el procesamiento costoso de mensajes directamente acabará convirtiéndose en un lector que deja de leer. El análisis de JSON también puede causar problemas cuando un mensaje es enorme, pero el error habitual ocurre antes: el lector espera una devolución de llamada que está haciendo un trabajo ajeno.

El host debería registrar al menos estos valores por sesión de servidor: bytes recibidos en stdout, tamaño de la trama completa más grande, fallos de análisis, tiempo de espera para obtener capacidad del distribuidor, solicitudes de cancelación enviadas y salidas de procesos. Esas cifras resuelven las discusiones rápidamente. Si los bytes de stdout dejan de aumentar a mitad de un resultado y el servidor sigue activo, sospecha del lado de escritura del servidor. Si los bytes siguen llegando pero se detiene la distribución de mensajes completos, sospecha de la cola del host o de su consumidor.

No hagas que el control del flujo de stdout dependa de si una respuesta de herramienta resulta útil para el modelo. El lector debe recibir el mensaje completo del protocolo antes de poder descartarlo, informarlo o dirigirlo de forma segura. Un host que decide a mitad de una trama que el mensaje es demasiado grande y deja de leer ha creado el interbloqueo por sí mismo.

Un límite de resultados necesita dos límites

Establece un límite de transporte y otro de contenido. Un único recuento de caracteres no protege el proceso, porque el escape de JSON, la codificación base64 y la estructura que rodea la respuesta cambian el número de bytes de stdout.

El límite de transporte es el tamaño máximo en bytes de un mensaje JSON-RPC serializado completo. Aplícalo en el analizador de tramas antes de analizar JSON arbitrario. Protege la memoria y el tiempo de análisis del host. El límite de contenido es la cantidad máxima útil que devuelve una herramienta dentro de content o structuredContent. Aplícalo en el controlador de la herramienta antes de serializar el resultado. Protege el contexto del agente y mantiene la respuesta útil.

Ninguno de los dos límites debe aparecer solo en la descripción de la herramienta. De vez en cuando, un modelo solicitará una búsqueda sin límite, un listado recursivo o un documento completo. El servidor debe gestionar esa solicitud de forma predecible.

Una respuesta práctica de herramienta indica qué omitió y cómo puede continuar el agente. Es mejor que cortar silenciosamente una cadena, porque una truncación silenciosa parece evidencia completa.

{
  "jsonrpc": "2.0",
  "id": 41,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Returned 50 of 4,382 matching records. Results are sorted by updated time. Use cursor \"eyJvZmZzZXQiOjUwfQ\" to continue, or add a narrower path or query."
      }
    ],
    "structuredContent": {
      "items": [
        {"path": "src/auth.ts", "line": 18, "summary": "reads token from environment"}
      ],
      "nextCursor": "eyJvZmZzZXQiOjUwfQ",
      "truncated": true,
      "totalEstimate": 4382
    }
  }
}

El texto ofrece al modelo una explicación sencilla. El contenido estructurado proporciona al cliente un token estable para continuar y una señal de truncación legible por máquina. No envíes un total inventado si contar todos los registros resulta costoso o imposible. Indica truncated: true y omite el recuento. La falsa precisión hace perder más tiempo que un resultado incompleto pero honesto.

Evita la recomendación popular de «devuelve solo una ruta de archivo». Solo funciona cuando el host y el servidor comparten un sistema de archivos, la ruta está autorizada, el agente puede leerla y el artefacto sigue presente. En entornos remotos o aislados, es una promesa rota. Una referencia puede ser útil, pero necesita una operación de lectura o exportación correspondiente, con sus propios límites.

Devuelve decisiones, no todo el ruido

La mayoría de las respuestas demasiado grandes proceden de herramientas cuyo modelo de salida se copió de una línea de comandos. grep -R, git diff, una API de listado en la nube y una consulta a una base de datos tienen sentido para una persona frente a un terminal. No se convierten en buenas interfaces para agentes solo porque las envuelvas en JSON.

Por lo general, un agente necesita suficiente evidencia para elegir su siguiente acción. Dale un conjunto acotado de coincidencias, los campos relevantes y una forma de precisar la solicitud. Guarda el artefacto completo para una ruta explícita de exportación o recuperación, en la que quien llama opte por la paginación o por un rango limitado.

Para buscar en un repositorio, devuelve rutas de archivos, rangos de líneas, fragmentos breves y la consulta utilizada. No devuelvas todas las líneas coincidentes de un monorepo. Para un cliente HTTP, devuelve el estado, algunos encabezados seleccionados, una vista previa acotada del cuerpo y un identificador de respuesta si tu producto puede conservarlo de forma segura. No codifiques una descarga arbitraria en base64 dentro de content. Para SSH, devuelve una parte final limitada de stdout y stderr, además del estado de salida. Un comando que imprime un archivo generado enorme ya te ha indicado que produjo un archivo generado enorme. El agente rara vez necesita todos los bytes en su contexto inmediato.

Coloca los límites cerca del origen de la expansión. Un servidor que llama a una API remota debe enviar la paginación y los selectores de campos a esa API. Un servidor que ejecuta un proceso debe limitar la captura del subproceso mientras sigue vaciando stdout y stderr. Un servidor que busca archivos debe detenerse al alcanzar su presupuesto de resultados, no recopilar todas las coincidencias para recortar después la cadena final.

Esta distinción importa porque truncar después de recopilar protege la transmisión MCP, pero no protege la máquina que realiza el trabajo. Un comando recursivo todavía puede consumir memoria, disco y CPU antes de que la capa de respuesta descarte su salida.

Sallyport resulta útil aquí porque mantiene las credenciales HTTP y SSH fuera del agente mientras las acciones se ejecutan mediante su pasarela local. Ese límite no hace segura una respuesta de API o una transcripción de shell sin límites, por lo que los autores de herramientas siguen necesitando presupuestos de salida explícitos en el límite de la acción.

Reproduce el bloqueo antes de afirmar que lo has solucionado

Bloquea la pasarela de acciones
Cuando la bóveda está bloqueada, Sallyport deniega todas las acciones HTTP y SSH.

No puedes probar este fallo llamando a una herramienta y comprobando si un resultado grande acaba apareciendo. Construye un arnés que deje de vaciar stdout intencionadamente y demuestra que el servidor entra en estado bloqueado. Después, demuestra que el host normal nunca se comporta así.

Este pequeño accesorio de Node escribe una respuesta JSON-RPC válida con una carga lo bastante grande como para superar la capacidad habitual de una tubería cuando su proceso padre ignora stdout. Recibe una línea de solicitud desde stdin y escribe la respuesta en fragmentos. La espera de drain es la evidencia: indica cuándo el tiempo de ejecución ha aplicado presión de vuelta al escritor de stdout del servidor.

// oversized-server.mjs
import readline from "node:readline";
import { once } from "node:events";

const rl = readline.createInterface({ input: process.stdin });

for await (const line of rl) {
  const request = JSON.parse(line);
  const text = "x".repeat(8 * 1024 * 1024);
  const response = JSON.stringify({
    jsonrpc: "2.0",
    id: request.id,
    result: { content: [{ type: "text", text }] }
  }) + "\n";

  for (let start = 0; start < response.length; start += 16 * 1024) {
    const chunk = response.slice(start, start + 16 * 1024);
    if (!process.stdout.write(chunk)) {
      process.stderr.write("stdout backpressure observed\n");
      await once(process.stdout, "drain");
    }
  }
}

Ahora ejecútalo con stdout canalizado y deja deliberadamente child.stdout sin leer. Sigue leyendo stderr para poder ver la marca de presión de vuelta. Envía una solicitud y espera brevemente. El proceso hijo debería seguir activo y no terminar de escribir. Es el comportamiento esperado, no un defecto de Node.

// blocked-parent.mjs
import { spawn } from "node:child_process";

const child = spawn(process.execPath, ["oversized-server.mjs"], {
  stdio: ["pipe", "pipe", "pipe"]
});

child.stderr.setEncoding("utf8");
child.stderr.on("data", chunk => process.stderr.write(chunk));

child.stdin.write(JSON.stringify({
  jsonrpc: "2.0",
  id: 1,
  method: "tools/call",
  params: { name: "large", arguments: {} }
}) + "\n");

setTimeout(() => {
  console.error("child still running:", child.exitCode === null);
  child.kill("SIGTERM");
}, 1000);

No copies este patrón del proceso padre en producción. Su objetivo es hacer evidente el fallo. Sustituye el consumidor de stdout ausente por tu analizador de tramas real y ejecuta el mismo accesorio. El hijo debería completar la respuesta o el cliente debería rechazarla por superar un límite declarado, pero no debe quedar atascado porque el padre ignoró stdout.

Ejecuta la prueba con cargas que contengan comillas, caracteres multibyte y cadenas largas sin separaciones. Esos casos detectan analizadores que cuentan caracteres de JavaScript en lugar de bytes UTF-8, o que suponen que cada fragmento leído termina en el límite de un mensaje.

Rechaza una trama enorme sin volver a crear el bloqueo

La comprobación del tamaño de una trama tiene una trampa: un cliente que deja de consumir la trama demasiado grande bloqueará el servidor con la misma seguridad que un cliente que nunca conectó un lector. El cliente necesita una regla de recuperación.

Si tu protocolo de entramado ofrece un marcador claro de final, sigue leyendo hasta ese marcador mientras descartas la trama problemática. Después informa de una infracción del protocolo y decide si la sesión puede continuar. Si la codificación del protocolo no permite recuperar el entramado de forma segura después de superar el límite, termina el proceso del servidor, cierra stdin e inicia una sesión nueva. Puede parecer una medida drástica, pero intentar adivinar dónde termina un mensaje JSON corrupto es peor.

MCP stdio utiliza mensajes JSON-RPC sobre un flujo de bytes local. Tu implementación debe tratar el entramado como código de transporte, no como una cómoda llamada a split("\n") después de recopilar texto sin límite. Mantén un contador de bytes mientras acumulas un posible mensaje. Con cada fragmento entrante, encuentra los límites de las tramas completas y distribúyelas, o falla cuando el candidato supere el máximo.

No analices un mensaje gigantesco solo para descubrir que es gigantesco. JSON.parse necesita una cadena completa en memoria y puede reservar más espacio que el tamaño de su entrada. El objetivo de un límite de transporte es evitar precisamente ese trabajo.

Si controlas ambos extremos y utilizas JSON delimitado por saltos de línea, exige un objeto JSON por línea, rechaza los saltos de línea literales dentro de cadenas mediante la codificación JSON normal y escribe exactamente un delimitador después de cada objeto serializado completo. El servidor nunca debe escribir diagnósticos humanos en stdout. La documentación de depuración de MCP indica que los servidores locales deben enviar los registros a stderr, porque stdout pertenece al protocolo.

El lector también debe aplicar un límite al número de mensajes completos que esperan el procesamiento de la aplicación. Si la cola se llena, no pauses stdout indefinidamente. Puedes rechazar nuevas solicitudes, cancelar el trabajo cuando sea posible o finalizar la sesión. La acción correcta frente a la sobrecarga depende del host, pero dejar stdout sin leer nunca es un valor predeterminado seguro.

La cancelación solo funciona si llega al productor

Mantén los secretos fuera de los resultados
Sallyport ejecuta la solicitud y devuelve el resultado sin entregar las credenciales al agente.

Un tiempo de espera en la interfaz del agente no es una cancelación. Solo cambia la opinión de la interfaz sobre la solicitud. El servidor puede seguir consultando una base de datos, descargando una respuesta, ejecutando un proceso y escribiendo megabytes en stdout.

MCP define notifications/cancelled para una solicitud emitida anteriormente en la misma dirección. La notificación incluye el ID de la solicitud original y puede incluir un motivo. El esquema de MCP indica que el receptor debe detener el procesamiento asociado, liberar recursos y tratar el resultado como no utilizado. También advierte que la cancelación puede competir con la finalización. Por eso el cliente debe tolerar una respuesta tardía y el servidor debe tolerar una cancelación de una solicitud que ya ha terminado.

Para una llamada a tools/call, el cliente envía una notificación como esta mediante la misma sesión stdio:

{
  "jsonrpc": "2.0",
  "method": "notifications/cancelled",
  "params": {
    "requestId": 41,
    "reason": "result exceeded the client budget"
  }
}

El servidor necesita un mapa del ID de cada solicitud al trabajo activo. Cada entrada debería contener un controlador de cancelación o su equivalente en el lenguaje, un estado de finalización y cualquier proceso hijo, solicitud HTTP o cursor que posea. Cuando llega la notificación de cancelación, cancela el trabajo, deja de producir contenido para el resultado, limpia la entrada del mapa y evita enviar una respuesta normal si todavía no ha comenzado a hacerlo.

Aquí los equipos cometen un error sutil. Añaden una señal de cancelación al controlador de la herramienta, pero el controlador espera un proceso hijo cuya captura de stdout ignora esa señal. O cancelan una obtención HTTP y dejan una transformación serializando un enorme arreglo en memoria. La cancelación solo es real cuando llega a cada productor y a cada operación que está esperando.

Trata el momento posterior al inicio de la escritura de una respuesta como un estado distinto. La cancelación puede llegar después de que algunos bytes ya estén en la tubería. No puedes retirarlos. El host debe seguir leyendo lo suficiente para conservar la salud de la sesión y después ignorar la respuesta tardía asociada al ID cancelado. El servidor debe detener el trabajo adicional en cuanto detecte la cancelación, pero no puede prometer que no existan bytes tardíos.

Una prueba de cancelación necesita una segunda solicitud

Una buena prueba de cancelación demuestra la recuperación, no solo que se activó un temporizador. Inicia una herramienta que produzca salida lo bastante despacio como para que el cliente pueda cancelarla mientras está activa. Envía la cancelación. Después presenta una solicitud pequeña y no relacionada en la misma sesión MCP. Esa segunda solicitud debe completarse rápidamente.

La siguiente secuencia de prueba detecta los fallos importantes:

  1. Inicia un tools/call cuyo controlador emita una respuesta grande en fragmentos o invoque un productor deliberadamente lento.
  2. Espera hasta que el arnés observe suficientes bytes de stdout para saber que la respuesta ha comenzado.
  3. Envía notifications/cancelled para ese ID de solicitud.
  4. Comprueba que el productor termina o informa de su ruta de cancelación dentro del plazo elegido.
  5. Envía una solicitud pequeña con un ID nuevo, como una herramienta de estado o una herramienta de eco acotada.

La solicitud final es la prueba. Revela si el servidor sigue bloqueado al escribir, si su lector de stdin se quedó sin tiempo de ejecución, si una tarea cancelada retuvo un bloqueo global o si el host dejó de vaciar stdout después de decidir cancelar.

Prueba también la cancelación antes de que comience el trabajo, a mitad de una operación de E/S remota, mientras se ejecuta un subproceso y después de que haya comenzado la respuesta final. Son estados distintos. Una implementación que gestiona uno correctamente puede fallar en otro.

No afirmes que la cancelación siempre impide una respuesta. La especificación de MCP permite estas condiciones de carrera. Comprueba que el host sigue siendo correcto si llega una respuesta después de la cancelación y que el trabajo del servidor se detiene cuando la cancelación llega a tiempo para tener efecto.

La salida de los procesos necesita su propia ruta de vaciado

Rastrea cada acción de herramienta
Su diario de actividad registra cada llamada HTTP y SSH en la pista de auditoría cifrada.

Los servidores MCP suelen invocar herramientas de línea de comandos. Eso añade otro par de tuberías dentro del servidor: este debe consumir stdout y stderr del proceso hijo mientras realiza el trabajo de MCP. Si solo lee el flujo hijo hasta un límite y después se detiene, el hijo puede bloquearse antes de terminar. El servidor MCP exterior puede entonces parecer que ignora la cancelación porque está esperando a un hijo que no puede avanzar.

Captura una vista previa limitada, pero sigue vaciando el flujo después de alcanzar el límite. Marca la salida como truncada y descarta los bytes restantes. Si el comando admite su propio límite de resultados, pásalo antes de iniciar el comando. Por ejemplo, pide a una utilidad de búsqueda un número máximo de coincidencias, solicita una página limitada a la base de datos o limita un comando de registros a las últimas entradas. Seguir vaciando después del límite es la red de seguridad, no la estrategia principal de resultados.

Mantén stderr separado de stdout. Una herramienta puede escribir diagnósticos útiles en stderr y devolver al mismo tiempo un estado de salida normal. Limita y vacía ambos flujos de forma independiente. Nunca mezcles una salida de proceso arbitraria con stdout del servidor MCP. El canal stdout exterior tiene una sola función: mensajes MCP serializados.

La misma regla se aplica a los comandos SSH. Un comando remoto puede imprimir datos hasta que su canal se bloquee. Captura una cantidad limitada, sigue consumiendo los flujos remotos hasta que termine o se cancele y explica con honestidad en el resumen devuelto qué descartaste. Una transcripción completa pertenece a un flujo de artefactos diseñado para ello, no a un resultado de herramienta improvisado.

Incluye el fallo en la puerta de calidad de la versión

Es fácil eliminar accidentalmente los límites de resultados y la cancelación. Una refactorización puede sustituir un lector en streaming por readFile, convertir una llamada a una API paginada en una llamada sin límite o trasladar el análisis a una devolución de llamada de la interfaz. Conserva el accesorio de resultados grandes en la suite de pruebas.

La puerta de calidad de tu versión debe cubrir una respuesta válida justo por debajo del límite de transporte, otra justo por encima, un campo individual enorme, muchos bloques pequeños de contenido, JSON mal formado, stderr ruidoso, un registro accidental en stdout y una cancelación durante la salida. Ejecuta las pruebas con la ruta real de creación del proceso, no solo con un transporte en memoria. Las pruebas en memoria no pueden mostrar la presión de vuelta de una tubería.

Registra el comportamiento esperado en un lenguaje sencillo: el cliente vacía stdout continuamente; nunca analiza por encima del límite de trama configurado; informa de un fallo por resultado acotado; la cancelación llega al trabajo activo; y otra solicitud puede continuar después de la recuperación. Los ingenieros podrán cambiar los detalles de implementación sin debilitar esas garantías.

Si solo haces un cambio, que sea este: separa el lector de stdout del procesamiento del agente y pruébalo con un servidor que escriba mucho más de lo que cualquier herramienta razonable debería devolver. Esa prueba convierte un informe impreciso de «el agente se congeló» en un fallo que puedes reproducir, medir y mantener fuera de la siguiente versión.

FAQ

¿Por qué se bloquea un servidor MCP después de devolver un resultado de herramienta grande?

Sucede cuando el servidor escribe más rápido de lo que el host lee stdout, o cuando el host se bloquea al analizar, almacenar o reenviar un resultado. El servidor queda esperando espacio en la tubería y también puede dejar de leer stdin. Una solicitud posterior, incluida la cancelación, puede quedar atrapada detrás del bloqueo.

¿Cuál es un tamaño máximo seguro para un resultado de herramienta MCP?

No existe una cifra única segura en MCP, porque el límite útil depende del cliente, del presupuesto de contexto del modelo, de la codificación del resultado y del trabajo que se esté realizando. Elige un límite de bytes para toda la respuesta JSON-RPC serializada y otro límite semántico más pequeño para el contenido de la herramienta. Haz explícita la truncación y ofrece al usuario un cursor, una ruta, una consulta o una herramienta de seguimiento para recuperar el resto.

¿Aumentar el búfer de stdout solucionará la presión de vuelta de MCP?

No. Aumentar un búfer en memoria retrasa el fallo y puede convertir la salida bloqueada en un pico de memoria. El host debe seguir leyendo la tubería y el servidor debe evitar construir objetos de respuesta absurdamente grandes desde el principio.

¿Cómo funciona la cancelación de herramientas MCP mediante stdio?

La notificación de cancelación de MCP es notifications/cancelled, con el ID de la solicitud original y un motivo opcional. Es una indicación y puede competir con la finalización, así que el servidor debe conectarla a una señal de cancelación real y el cliente debe tolerar una respuesta que ya se haya escrito.

¿Debe un host MCP seguir leyendo stdout mientras el agente está ocupado?

Lee stdout continuamente con un analizador de tramas, aplica un tamaño máximo mientras llegan los bytes y envía los mensajes completos a una cola de trabajo acotada e independiente. Nunca dejes de leer stdout porque el agente esté procesando un resultado de herramienta anterior. Si la cola se llena, aplica una acción de sobrecarga definida en lugar de dejar el transporte sin leer.

¿Puedo escribir registros de depuración en stdout en un servidor MCP?

Registra los detalles operativos en stderr, no en stdout. stdout transporta únicamente mensajes del protocolo MCP, y una sola línea de registro puede corromper el entramado de mensajes. La documentación oficial de depuración de MCP lo indica claramente para los servidores stdio locales.

¿Qué debe hacer un cliente cuando una respuesta MCP supera su límite?

No dejes de leer al alcanzar el límite. Sigue leyendo y descartando bytes hasta el final de la trama afectada, o termina el proceso del servidor y reinicia la sesión. Detenerse justo en el límite recrea el bloqueo de la tubería que intentabas evitar.

¿Cómo pruebo la cancelación de MCP en lugar de limitarme a esperar un tiempo de espera?

Prueba tres propiedades por separado: que el servidor detecte la cancelación, que se detenga el trabajo secundario y que el cliente siga funcionando después de la solicitud cancelada. Una prueba que solo detecta una notificación de cancelación en un registro demuestra muy poco. Necesitas que una segunda solicitud pequeña se complete rápidamente después de cancelar la solicitud grande.

¿Cómo deben devolver las herramientas MCP archivos grandes o respuestas de API?

Usa paginación, filtros, resúmenes, referencias estables y herramientas de exportación explícitas. Una herramienta que devuelve un árbol de directorios, un diff de repositorio, un resultado de consulta o un cuerpo HTTP debe devolver la parte necesaria para la decisión actual y ofrecer después una forma de pedir más. Enviar un artefacto completo solo porque era fácil serializarlo es un mal diseño de herramienta.

¿Usar una pasarela de acciones elimina los riesgos de presión de vuelta de MCP por stdio?

Sallyport gestiona las acciones HTTP y SSH mediante su aplicación local y el puente sp mcp, por lo que el agente no recibe las credenciales subyacentes. Eso protege el uso de secretos, pero no cambia la física de stdout. Las herramientas y los clientes siguen necesitando límites de resultados, cancelación y pruebas que demuestren que una respuesta grande no puede bloquear la sesión.

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