sistema: OPERATIVO
← volver a todos los hacks
AGENTS MEDIUM NEW

Cargas ocultas en los metadatos de herramientas MCP: la brecha de fidelidad de la vista de aprobación

Un estudio de julio de 2026 muestra que caracteres Unicode TAG invisibles pueden colar instrucciones en los metadatos de herramientas MCP — presentes en el contexto del modelo, ausentes en el diálogo de aprobación que ve el usuario.

2026-07-16 // 7 min affects: model-context-protocol, mcp-clients, coding-agents, mcp-python-servers

¿Qué es esto?

El 7 de julio de 2026, Mohammadreza Rashidi publicó en arXiv Unicode TAG-Block Concealment of Tool-Metadata Payloads in the Model Context Protocol. El artículo aísla una debilidad estructural en cómo los agentes de código construidos sobre el Model Context Protocol (MCP) obtienen el consentimiento del usuario para un servidor de herramientas. Cuando un agente se conecta a un servidor, ejecuta un handshake tools/list que devuelve, por cada herramienta, un nombre, una descripción en lenguaje natural y un esquema de entrada JSON. El cliente muestra esos metadatos una sola vez, en un diálogo de aprobación, y luego los inyecta tal cual en el contexto del modelo en cada turno posterior.

El hallazgo es este: nada en el protocolo exige que la vista que aprueba el humano y los bytes entregados al modelo sean lo mismo. El autor llama a esto la brecha de fidelidad de la vista de aprobación, y demuestra que es una propiedad del mecanismo, no un fallo aislado: la ruta de código que renderiza la vista de aprobación y la que entrega los datos al modelo son independientes, y ambas se alimentan de los mismos bytes controlados por el servidor y, por tanto, no confiables.

Cómo funciona

El núcleo de la técnica es la codificación de ocultación. El bloque TAG de Unicode (puntos de código U+E0000–U+E007F) no tiene glifo asignado en los renderizadores de terminales, mensajería o IDE habituales. Un texto escrito en ese rango está, por tanto, ausente de lo que ve un revisor humano, pero sobrevive byte a byte hasta el tokenizador del modelo. Un atacante que controle un servidor de herramientas puede colocar instrucciones en el rango invisible, dentro de la descripción o el esquema de una herramienta; el usuario lee una descripción limpia e inofensiva y pulsa «permitir», mientras el modelo recibe la carga oculta completa en cada turno.

El artículo construye una prueba de concepto a nivel de protocolo que habla el MCP real en JSON-RPC sobre stdio frente a un cliente y un servidor genuinos, y evalúa ocho técnicas concretas en cinco superficies de metadatos distintas. Los resultados reportados son precisos: las ocho técnicas entregan una carga controlada por el atacante en el contexto del modelo; cuatro de ocho eluden un saneador por coincidencia de cadenas representativo; y —tal como predice el análisis del mecanismo— solo la codificación TAG es a la vez invisible en la vista de aprobación humana y entregada tal cual al modelo, lo que la convierte en la única técnica del conjunto que derrota ambas capas de defensa a la vez. Las demás técnicas cubren fallos vecinos: un servidor que conserva el nombre de una herramienta pero cambia en silencio su definición más tarde (un «rug-pull» de tipo time-of-check to time-of-use), un servidor de terceros que suplanta el nombre de una herramienta de confianza del host, y valores default o enum peligrosos de un esquema que un agente hereda sin confirmación. Cabe destacar que el protocolo no forzó una nueva aprobación en ninguna de las ocho, ni siquiera bajo el rug-pull. Describimos el mecanismo a nivel conceptual; el preprint contiene las cargas y el banco de medición.

Por qué importa

MCP se ha convertido en la forma dominante en que los agentes de código descubren e invocan herramientas externas, y esos mismos metadatos suelen ser lo único que ve un usuario antes de conceder acceso a un servidor. El diálogo de aprobación es, pues, la verdadera frontera de confianza — y una frontera que muestra una cadena distinta de la que consume el modelo no es tal frontera. Un atacante capaz de inducir a una víctima a conectarse a un servidor malicioso o comprometido, o de manipular los metadatos en tránsito, obtiene un canal de instrucciones persistente que un revisor humano atento no puede ver y que un filtro de entrada ingenuo no puede atrapar.

Dos detalles hacen de esto más que un incidente aislado. Primero, la evasión del saneador y la de la revisión humana son independientes: la coincidencia de cadenas inspecciona la misma superficie mostrada que el humano, así que una carga renderizada como vacía en pantalla también está vacía para el filtro. Segundo, para comprobar si el resultado dependía de una sola base de código, el autor reimplementó todo el catálogo de técnicas frente a tres bibliotecas de servidores MCP en Python desarrolladas de forma independiente, y encontró acuerdo total en las treinta y dos celdas de resultados entre bibliotecas — evidencia de que la brecha se deriva del diseño del protocolo y no de una implementación concreta. Un control sobre veinticinco descripciones de herramientas benignas no marcó ninguna: el filtro de referencia no rechaza todo por defecto.

Defensas

El artículo presenta las correcciones como cuatro cambios estructurales independientes, cada uno cerrando una técnica precisa en lugar de un parche cosmético.

  • Hacer que el consentimiento sea fiel a los bytes. Una vista de aprobación debe mostrar los bytes exactos que recibirá el modelo, o bien normalizarlos o rechazarlos explícitamente. Pase cada cadena que se muestre en el diálogo por el mismo paso de decodificación que usa el tokenizador y, después, muestre un marcador visible para cualquier punto de código fuera de la cobertura de la fuente —como los navegadores muestran un glifo «.notdef» (tofu) para que la entrada inusual sea visible en lugar de estar silenciosamente ausente— o rehúse mostrar metadatos que contengan tales puntos de código hasta que estén normalizados.
  • Volver a pedir consentimiento ante una mutación. Fije un hash de los metadatos aprobados y vuelva a preguntar al usuario cuando un servidor devuelva después una definición distinta bajo el mismo nombre de herramienta. Trate la identidad como nombre de la herramienta más definición, igual que los gestores de paquetes fijan los hashes de dependencias y las plataformas móviles vuelven a pedir permiso cuando los permisos declarados de una app cambian en una actualización.
  • Delimitar los nombres de herramientas por procedencia. No permita que un servidor de terceros suplante el nombre de una herramienta de confianza provista por el host. Base la identidad de una herramienta en su origen (qué servidor registró ese nombre) en lugar de un espacio de nombres plano y global, de modo que un servidor malicioso no pueda registrar read_file y capturar una llamada destinada a la implementación del host.
  • Los valores por defecto de un esquema no son consentimiento. Todo campo cuyo propósito sea orientar a un llamador autónomo —una descripción, un default, un enum— es un canal de instrucciones alcanzable por el atacante y debe presentarse en la vista de aprobación con la misma fidelidad, nunca heredarse en silencio.

El autor también señala que estas correcciones del lado del renderizado son complementarias a las defensas del lado de la autoría, como la generación de descripciones de confianza o los esquemas de firma/versionado: una herramienta legítimamente firmada y legítimamente redactada aún puede portar una carga TAG que un renderizador no fiel a los bytes no mostrará. Un cliente que despliega solo una clase de defensa no está protegido frente a la superficie que cubre la otra.

Estado

ElementoValor
TipoInvestigación en seguridad — brecha de fidelidad entre la vista de aprobación y los metadatos de herramientas MCP
Modelo de amenazaServidor de herramientas MCP malicioso o comprometido (o metadatos manipulados en tránsito) que un usuario es inducido a aprobar
EfectoInstrucciones ocultas llegan al modelo tal cual mientras están ausentes de la vista de aprobación; filtros de cadenas y revisión humana eludidos
Alcance8 técnicas en 5 superficies de metadatos; nueva aprobación forzada en 0/8; confirmado en 3 bibliotecas de servidores MCP en Python independientes
Corrección estructuralVistas de aprobación fieles a los bytes; reconsentimiento ante mutación; nombres delimitados por procedencia; valores por defecto presentados como consentimiento
Publicación7 de julio de 2026 (arXiv:2607.05744v1), CC BY 4.0
EstadoPreprint académico; ni vulnerabilidad de producto ni CVE

Fechas clave: 7 de julio de 2026 — envío del preprint (arXiv:2607.05744). El trabajo prolonga la documentación previa sobre la ocultación mediante caracteres Unicode Tag en las entradas de LLM (Embrace The Red, 2024) y la sitúa específicamente en el flujo de aprobación de herramientas de MCP. Los resultados y el método son los que se enuncian en el preprint.

Sources