Hay una pregunta que desmonta la mayoría de demos de “chatea con tus datos”: ¿con qué permisos consulta el agente?
La respuesta habitual es incómoda. El agente se conecta al almacén de datos con una identidad de servicio — un service principal, una managed identity, una API key — que puede leerlo todo. El usuario pregunta, el agente consulta como superusuario, y la seguridad a nivel de fila (RLS), los permisos por workspace y todo el modelo de gobernanza que costó años construir se evaporan en el primer salto.
Mientras eso sea así, el agente no sale de la demo. Ningún responsable de datos serio despliega a toda la organización un sistema donde preguntar es saltarse los permisos.
Este artículo cuenta cómo resolvimos ese problema para una compañía energética española: un agente de datos en producción donde la pregunta viaja con la identidad de quien la hace desde Teams, Claude o VSCode hasta Microsoft Fabric — y la RLS se aplica exactamente igual que si el usuario consultara a mano.
Antes del cómo, el resultado: un analista preguntando en producción — con su identidad — y la respuesta en segundos, con las tablas consultadas al pie.

El patrón: un servidor MCP, tres clientes, una identidad
La arquitectura tiene tres planos:
- Canales de acceso — donde el usuario ya trabaja: M365 Copilot (en Teams, vía declarative agent), Claude (Claude Code y claude.ai) y VSCode. Tres clientes, cero aplicaciones nuevas que aprender.
- Servidor MCP — un único servidor remoto (FastAPI sobre Azure Container Apps) que expone las herramientas del agente por HTTP usando el Model Context Protocol. Todos los canales hablan con el mismo servidor.
- Plano de datos — el lakehouse corporativo en Microsoft Fabric: OneLake para metadatos Delta y el SQL Analytics Endpoint para consultas.

La decisión estructural importante no es ninguna caja del diagrama — es la flecha. Cada petición que llega al servidor MCP trae el token OAuth del usuario que pregunta, y el servidor nunca lo sustituye por una identidad propia.
La cadena OBO, paso a paso
El mecanismo es OAuth 2.0 On-Behalf-Of (OBO), un flujo estándar de Entra ID pensado exactamente para esto: un servicio intermedio que necesita llamar a APIs downstream en nombre del usuario, no en nombre propio.

- Login. El usuario se autentica con su cuenta corporativa de Microsoft — SSO de Teams, login del cliente MCP o
az login. No hay contraseñas nuevas ni cuentas paralelas. - Token de entrada. El cliente llama al servidor MCP con un token cuyo audience es el propio servidor. El servidor valida firma, audience e issuer.
- Intercambio OBO. El servidor presenta ese token a Entra ID con el grant
urn:ietf:params:oauth:grant-type:jwt-bearery pide tokens para las APIs de datos. Entra ID emite tokens nuevos que siguen llevando la identidad del usuario original. - Tokens de datos. Dos audiences:
https://storage.azure.com/.default(OneLake, lectura de metadatos Delta) yhttps://database.windows.net/.default(SQL Analytics Endpoint, vía pyodbc/ODBC 18). - Consulta. Fabric recibe la consulta como si la hiciera el usuario. RLS, permisos de workspace y de lakehouse se aplican sin que el agente tenga que reimplementar nada.
La app registration del servidor necesita tres permisos delegados con admin consent — y solo tres:
Microsoft Graph → User.Read (perfil tras login)
Azure Storage → user_impersonation (OBO hacia OneLake)
Azure SQL Database → user_impersonation (OBO hacia el SQL endpoint) Y el anti-patrón queda prohibido por diseño: la Managed Identity del contenedor existe solo para infraestructura (descargar la imagen, leer secretos, escribir telemetría). Si alguien propone “dale Storage Blob Data Reader a la MI sobre el lakehouse y simplificamos”, la respuesta es no — eso es exactamente el agujero por el que se escapa la RLS. Toda propuesta así se rediseña con OBO.
Tres ecosistemas, el mismo contrato
Lo interesante de MCP como capa de integración es que el trabajo de identidad se hace una vez y los clientes lo heredan:
- Claude Code / VSCode se conectan por HTTP MCP remoto con OAuth — el token del usuario viaja en cada llamada. La extensión de VSCode reutiliza la misma configuración (
.mcp.json); cero código adicional. - M365 Copilot consume el mismo servidor mediante un declarative agent — el formato actual de Microsoft para agentes en el catálogo de Teams (el Bot Framework para este caso está archivado). El manifest declara el plugin MCP y el proxy OAuth; la identidad llega igualmente.
El manifest del agente declarativo, esquematizado:
{
"version": "v1.2",
"name": "Agente de datos",
"instructions": "Respondes preguntas sobre los datos corporativos…",
"actions": [
{
"id": "mcp-plugin",
"file": "mcp-plugin.json"
}
]
} (Esquema de declarativeAgent.json, sin identificadores reales; mcp-plugin.json apunta al servidor MCP remoto y declara el proxy OAuth.)Una consecuencia práctica de compartir servidor: las respuestas difieren en latencia según el canal — en Copilot dentro de Teams llegan en minutos; en Claude, en segundos (como en la captura del inicio). La cadena de identidad es idéntica en ambos.
Decisiones de diseño que importan
Solo lectura, en el código y no en la política. El servidor acepta exclusivamente SELECT/WITH/DESCRIBE/SHOW; cualquier otra sentencia se rechaza antes de tocar el endpoint. Un agente que puede escribir es una conversación distinta — esta arquitectura la cierra de raíz.
Auditoría por consulta. Cada llamada registra quién preguntó, qué consultó y cuándo. Con OBO esto es gratis: la identidad ya viene en la petición, no hay que reconstruirla.
Errores de permiso como señal, no como fallo. Cuando Fabric devuelve un 403, el agente se lo dice al usuario tal cual (“no tienes acceso a esa tabla”) en lugar de enmascararlo. El sobre-acceso silencioso es el bug; el 403 visible es la feature.

Despliegue aburrido a propósito. Terraform, imagen en un Azure Container Registry, revisiones de Container Apps. El despliegue lo ejecuta un service principal dedicado que el cliente controla y puede revocar; el admin consent de los permisos delegados queda deliberadamente como paso manual del lado del cliente — es la frontera entre “confío en la app” y “el proveedor puede ejecutar Terraform”.
La capa de conocimiento: skills versionadas
Un agente que solo traduce lenguaje natural a SQL se equivoca en lo que importa: las convenciones. Qué tabla es la canónica, qué significa cada código, en qué unidades vienen las medidas, qué filtros aplicar por defecto.
Ese conocimiento vive como skills: documentos versionados en git, organizados en plugins por dominio, que el agente consulta en tiempo de ejecución. El mismo repositorio se distribuye a los tres ecosistemas — como plugins en Claude Code y VSCode, vendorizado en la imagen del servidor para Copilot.
El efecto operativo es el que cambia el mantenimiento: el agente mejora editando texto, no redesplegando código. Un analista corrige una convención de unidades en un markdown, hace commit, y los tres canales responden mejor.
El patrón, en tres líneas
- La identidad del usuario viaja de extremo a extremo (OBO); el agente nunca consulta como superusuario, y RLS y permisos del lakehouse se aplican intactos.
- Un único servidor MCP sirve a Copilot, Claude y VSCode — el trabajo de identidad se hace una vez.
- Solo lectura por diseño, auditoría por consulta y conocimiento de dominio versionado en git.
Si quieres la versión sin tecnicismos — qué cambia para el negocio cuando cualquier empleado puede preguntar a los datos en su idioma — está en De días a minutos: preguntar a tus datos en español.
¿Tienes un lakehouse y quieres un agente que lo respete? Hablemos.