ClickHouse MCP y los límites del modo solo lectura: acceso estructurado para agentes
El acceso MCP de solo lectura impide las escrituras, pero no los números incorrectos, las fugas de datos del inquilino ni la exposición del esquema. Vea qué muestra el benchmark de ClickHouse y cómo las herramientas de métricas estructuradas reducen el riesgo.
Casi todos los tutoriales sobre cómo conectar un agente de IA a una base de datos terminan con la misma recomendación: utilizar una conexión de solo lectura. Active readonly=1, otorgue únicamente permisos de SELECT, y el agente podrá inspeccionar los datos pero nunca modificarlos. Eso es razonable, pero es la respuesta a una pregunta que no era la verdadera preocupación.
Recientemente, ClickHouse realizó pruebas de rendimiento con 28 modelos frente a 201 preguntas reales extraídas de su agente analítico interno. El modelo mejor clasificado acertó el 76,6% de las veces. Pocas de las respuestas incorrectas se debieron a consultas fallidas; la mayoría fueron consultas que se ejecutaron sin problemas pero devolvieron cifras erróneas, y una conexión de solo lectura no puede evitar ni una sola de ellas.
El modo solo lectura determina si un agente puede escribir. No dice nada sobre qué puede solicitar el agente. Al entregarle a un modelo una herramienta SQL, se delega la autoría de las consultas: el agente elige las tablas, las uniones, el nivel de agregación, los filtros y la cantidad de filas que se devuelven. El modo solo lectura no afecta nada de esto. Evita que tus datos se destruyan, pero no hace nada para evitar que estén incorrectos. El eje importante no es lectura versus escritura, sino arbitrario versus estructurado.
Qué hace el servidor oficial MCP de ClickHouse
El servidor oficial, ClickHouse/mcp-clickhouse, incluye las funciones list_databases, list_tables y run_query. Por defecto, solo permite lectura. Habilitar la escritura requiere una variable de entorno, y sentencias peligrosas como DROP necesitan otra variable adicional. Para los transportes HTTP y SSE, la autenticación es obligatoria desde el inicio (funcionan tanto los tokens de portador como OAuth/OIDC), y el arranque fallará hasta que se configure. La documentación recomienda tratar la cuenta de base de datos MCP como cualquier cliente externo con privilegios mínimos.
Hay dos detalles que merecen atención. El tiempo de espera de las consultas se aplica en el lado del cliente, por lo que el servidor MCP deja de esperar, pero la consulta sigue ejecutándose en tu clúster. Además, readonly=1 se aplica a cada consulta y ofrece poca protección. Conéctate como usuario de ClickHouse con un perfil laxo y permisos amplios, ya que esa marca no protege nada.
Incluso con todo configurado a la perfección, una conexión de solo lectura con restricciones sigue permitiendo que un agente genere consultas SQL arbitrarias contra su almacén. Ese es el problema real.
Un fallo que ninguna configuración puede solucionar
La corrección es lo más importante, ya que ninguna opción de ClickHouse ayuda con eso. Permitir que un agente use SQL presupone que los modelos son buenos para escribirlo. Eso es parcialmente cierto, pero la parte falsa es la peligrosa.
La mejor prueba proviene de ClickHouse mismo. Su Agentic Analytics Benchmark, publicado el 1 de septiembre de 2026, reprodució 201 consultas del tráfico de producción de su asistente de análisis interno contra una reconstrucción sintética de su propio almacén. El modelo mejor calificado obtuvo un 76.6% de precisión.
Esas preguntas fueron formuladas por empleados de la empresa, y alguien que conoce el área correspondiente leía cada resultado. Cuando alrededor de una cuarta parte de las respuestas era incorrecta, una persona involucrada en el proceso detectaba la mayoría de los errores derivados. Si se aplica el mismo modelo a una función orientada al cliente, nadie puede verificar la precisión de los resultados ni siquiera sospechar que puedan estar equivocados. La exactitud sigue siendo la misma, pero el riesgo es mucho mayor.
La distribución de los fallos es más reveladora que la puntuación más alta. Los errores en tiempo de ejecución prácticamente han desaparecido, ya que solo cuatro de los 28 modelos presentaron alguno. En todos los modelos, la causa principal de fallo fue la estrategia utilizada: elegir el enfoque incorrecto explicó entre el 53% y el 82% de los errores. Según ClickHouse, la sintaxis y la ejecución ya están resueltas, por lo que ahora lo importante es el modelado de datos y el contexto del esquema.
El mejor rendimiento lo ilustra claramente. Fable 5.1 presentó la menor proporción de planes incorrectos entre los diez principales, pero también la tasa más alta de fallos por datos erróneos en general. Este modelo elabora un buen plan y luego elige el campo o la granularidad inadecuados. Ninguno de estos problemas puede ser corregido por un modelo más inteligente. Ambos desaparecen cuando las dimensiones, mediciones y granularidades temporales disponibles están predefinidas.
Por eso estos errores son peligrosos, no solo molestos. Un plan incorrecto que se ejecuta sin problemas genera una consulta que se analiza, se ejecuta, devuelve filas y produce un número que parece plausible. No hay excepciones ni líneas rojas en los registros de actividad. Alguien lo lee en un panel de control o en un resumen de Slack y toma medidas en base a él.
ClickHouse mismo señala una reserva. Las respuestas de referencia se obtuvieron cuando tres modelos de proveedores diferentes coincidieron, no mediante una auditoría humana; solo alrededor de la mitad de esas coincidencias fueron unánimes, y se considera que el jurado es indulgente. El consenso no es prueba, por lo que el 76,6% es incierto y probablemente esté ligeramente sobreestimado. El caso aquí depende de cómo se presenten los errores, no del porcentaje exacto.
ClickHouse también complica las cosas en comparación con muchas bases de datos. Las uniones son costosas y complicadas; su documentación recomienda denormalizar los datos y limitar el número de uniones que se utilizan. En la mayoría de los algoritmos de unión, la tabla de la derecha se almacena en memoria como una tabla hash. Con una unión basada en diccionario para relaciones uno-a-muchos, ClickHouse mantiene silenciosamente una fila que coincide y descarta el resto, lo que genera una respuesta incorrecta pero sin generar error alguno. Un agente que examine su esquema caerá con facilidad en tales trampas. Ni readonly=1, ni un perfil de configuración, ni una cuota sirven de ayuda. Solo quitando la autoría de las consultas al modelo se soluciona el problema.
El ámbito de tenant como parámetro, nunca como límite
El segundo tipo de fallo es aquel que conduce a la divulgación de información confidencial. En un producto multiinquilino, la identidad del inquilino es el predicado más importante en cada consulta. Con una herramienta SQL arbitraria, el agente escribe dicho predicado en una cláusula WHERE que se compone en tiempo de ejecución, por lo que el modelo puede omitirlo, interpretarlo incorrectamente o verse influenciado negativamente por contenido leído anteriormente en la conversación.
Aquí se esconde un problema estructural más profundo. Si se proporciona a un servidor MCP una cadena de conexión, este inicia sesión como un único usuario de ClickHouse vinculado a un único conjunto de políticas de filas, sin tener idea de qué cliente está detrás de cada solicitud. Esa información se encuentra en la aplicación, en la sesión autenticada, y una herramienta SQL genérica ya la ha perdido antes de que el agente escriba siquiera una línea.
Discovery entrega el esquema
El tercer problema es más sutil. Para consultar un almacén, un agente primero debe verlo, por lo que llama a list_tables, el cual devuelve cada tabla con sus columnas, el conteo de filas y la instrucción completa CREATE TABLE. Los nombres de las columnas, el vocabulario interno de la empresa y las tablas que se habían olvidado que existían terminan todos en el contexto del modelo, y luego pasan a los registros, seguimientos y proveedores de inferencia externos. Esto no es una fuga teórica, ya que es el primer paso necesario. Permitir SQL arbitrario y exponer el esquema completo son la misma decisión.
Acceso estructurado
Una herramienta SQL más potente no es la alternativa; no tener ninguna sí lo es. Anote una sola vez su vocabulario analítico, es decir, dimensiones, mediciones, métricas nombradas, reglas de arrendatario y cómo funciona el tiempo, y ofrezca al agente únicamente eso. El modelo selecciona entre un catálogo de preguntas permitidas y nunca escribe una consulta.
@hypequery/mcp, una biblioteca para esto, ofrece cuatro herramientas:
list_datasets: conjuntos de datos registrados con el conteo de sus dimensiones, mediciones y métricas.get_dataset_schema: los campos, métricas nombradas, filtros y límites de un conjunto de datos.query_metric: ejecuta una métrica nombrada.query_dataset: una consulta ad hoc sobre las dimensiones y mediciones seleccionadas.
No existe una función run_sql, ni siquiera una versión restringida. Una llamada se realiza de esta manera:
{
"name": "query_metric",
"arguments": {
"dataset": "orders",
"metric": "revenue",
"dimensions": ["country"],
"filters": [
{ "field": "status", "operator": "eq", "value": "completed" }
],
"orderBy": [{ "field": "revenue", "direction": "desc" }],
}
}
El agente seleccionó una métrica, un agrupamiento y un filtro. No decidió qué significa revenue. Esa definición se encuentra en su base de código, en TypeScript, revisada y versionada como todo lo demás, y su API y panel de control utilizan la misma. Los operadores de filtro y los intervalos de tiempo provienen de conjuntos fijos, y los campos se validan antes de que se ejecute cualquier operación. El registro constituye el límite:
export const datasets = {
orders: {
...Orders,
metrics: { revenue },
},
};
Un conjunto de datos ausente en ese objeto no puede ser listado, inspeccionado ni consultado, por lo que el conjunto de preguntas que puede hacer un agente se convierte en un objeto revisable en su repositorio. El alcance del inquilino también deja de ser algo que proporciona el modelo:
await createMCPServer({
datasets,
analytics,
tenantId: 'tenant_123',
});
Ese valor proviene del proceso anfitrión, que ya sabe quién está autenticado. Si se registra un conjunto de datos con clave de inquilino sin proporcionarla, el servidor se niega a iniciarse:
MCP server tenantId is required for tenant-scoped datasets
Por defecto, las expresiones SQL generadas y las expresiones de campos basadas en SQL también se eliminan de las respuestas, de modo que el modelo solo ve números y nombres de campos, y no la estructura de su consulta.
Algo sobre el costo
El costo es un problema secundario. El servidor oficial solo envía datos readonly al cliente, sin establecer valores para max_rows_to_read, max_bytes_to_read o max_memory_usage, ni tampoco existe un tiempo límite de ejecución impuesto por el servidor. En los servicios de cómputo facturados según el uso, un agente que repite escaneos de tablas completas en bucle genera un incidente de facturación, pero un conjunto limitado de consultas reduce los daños. Sin embargo, LIMIT restringe las filas devueltas, no los bytes escaneados, y una consulta agrupada en un amplio rango de tiempo sigue leyendo todo lo necesario. Los límites de acceso estructurado determinan qué consultas son posibles, no cuánto cuesta cada una. Sea cual sea la opción que elija, establezca límites reales para filas, bytes, memoria y tiempo de ejecución en un perfil de configuración para el usuario MCP, y añada una cuota.
Lo que no resuelve
Nada de esto es nuevo. La letal tríada de Simon Willison y la demostración MCP de Supabase en 2025 sostuvieron ambas que el acceso solo de lectura no constituye una barrera de seguridad, y Cube junto con dbt han promovido desde hace tiempo la capa semántica para agentes.
El compromiso honesto es la flexibilidad. Una superficie estructurada solo responde a lo que alguien modeló, por lo que una pregunta fuera del catálogo no obtiene respuesta alguna, mientras que un agente con SQL podría improvisar algo útil. Tampoco cierra todo lo demás: las etiquetas y descripciones de los conjuntos de datos llegan al modelo y moldean su comportamiento, una versión más limitada de la superficie de manipulación de herramientas y no una eliminada, aunque esas cadenas de texto se encuentran en TypeScript revisado y no en un servidor de terceros. Además, una métrica regulada no impedirá que un modelo interprete incorrectamente un número correcto en su resumen.
Comenzando por lo pequeño
No es necesario modelar todo el almacén. Elija una métrica de la que dependa su producto, aquella para la cual una respuesta incorrecta pueda causar daños; defínala una vez, guárdela y úsela únicamente con el agente. Luego compare lo que devuelve el agente con lo que muestra su panel de control. Si las cifras coinciden, la definición ahora se encuentra en un lugar al que ambos pueden acceder, y esa es toda la idea.