Azure Files MCP
Azure Files MCP (solo lectura)
Un servidor MCP remoto que le da a Claude acceso de solo lectura a una carpeta en un recurso compartido SMB de Azure Files, donde cada usuario que se conecta solo ve lo que sus propios permisos NTFS ya permiten.
Dos herramientas, nada más:
list_directory(path)- lista archivos/carpetas bajo la carpeta raíz configurada.read_file(path)- lee el contenido de un archivo bajo la carpeta raíz configurada. Los archivos de texto plano se devuelven tal cual; PDF, Word (.docx) y Excel (.xlsx) se convierten a texto automáticamente (ver "Lectura de archivos PDF/Word/Excel" más abajo).
No hay ninguna herramienta de escritura, borrado o renombrado en todo este código base: no está simulada, no está deshabilitada por configuración, simplemente no existe.
Para instrucciones paso a paso de configuración de Azure/Entra, consulta SETUP.md. Este archivo cubre la arquitectura y las decisiones de diseño; SETUP.md cubre el recorrido clic a clic por el Portal.
Por qué esto no es exactamente lo que sugiere una primera lectura del ticket
El diseño original asumía: asignar el rol RBAC no privilegiado Storage File Data SMB Share Reader, luego llamar a la API FileREST de Azure Files con el token OAuth de cada usuario, y Azure aplicaría las ACL NTFS por usuario automáticamente.
Eso no funciona. Verificado directamente contra la documentación de la API REST de Microsoft (Authorize with Microsoft Entra ID (REST API)): cada operación de lectura de FileREST (List Directories and Files, Get File, Get File Properties, ...) requiere tanto .../files/read como .../readFileBackupSemantics/action. readFileBackupSemantics/action - el propio término de Microsoft para un modo que omite explícitamente la evaluación de ACL NTFS - solo lo otorga Storage File Data Privileged Reader/Contributor. Storage File Data SMB Share Reader no aparece en absoluto en la tabla de permisos REST - solo se aplica a conexiones genuinas del protocolo SMB (puerto 445, Kerberos), que un backend HTTPS de Node que llama a FileREST no puede usar.
Así que sobre REST, el rol es o privilegiado (omite ACL) o irrelevante. No hay forma de que Azure mismo aplique las ACL NTFS por solicitud REST/OAuth.
Lo que este servidor hace en su lugar: usa Storage File Data Privileged Reader (sigue siendo de solo lectura, sigue autenticado por usuario - ver más abajo), y aplica los permisos NTFS él mismo, en código, usando el descriptor de seguridad real que Azure Files expone para cada archivo/carpeta:
Obtiene el permiso NTFS del archivo/carpeta como una cadena SDDL (llamada REST
getPermission).Analiza la DACL en ACE individuales (
src/acl/sddl.ts- hecho a mano; no existe una biblioteca Node/TS mantenida para esto).Resuelve el SID de AD local del usuario que llama y cada SID de grupo al que pertenece transitivamente, a través de Microsoft Graph (
src/graph/sidResolver.ts).Evalúa la DACL contra ese conjunto de SID usando semántica real de AccessCheck de Windows: la denegación explícita supera a la concesión explícita, los bits no mencionados se deniegan por defecto (
src/acl/evaluate.ts).
Esto es una aplicación genuinamente por usuario, solo que implementada aquí en lugar de delegada a la capa RBAC de Azure, porque Azure no tiene un mecanismo invocable por REST que lo haga por ti. El límite de seguridad real es este código base, no el RBAC de Azure - tenlo en cuenta al razonar sobre cualquier cosa a continuación.
Arquitectura
Este servidor es su propio servidor de autorización OAuth 2.1 - Claude nunca habla directamente con Entra. Es una elección de diseño deliberada, no la obvia, por lo que vale la pena explicar por qué: la especificación MCP requiere que los clientes envíen un parámetro resource RFC 8707 igual a la URL del propio servidor MCP, y Entra solo acepta un valor de resource que coincida con un URI de identificador verificado en el registro de la aplicación. Entra se niega rotundamente a registrar cualquier URL *.azurewebsites.net como una (dominio no verificado) - confirmado en vivo como AADSTS9010010, e imposible de arreglar con cualquier configuración del Portal. Así que, en su lugar, Claude se autoriza contra este servidor (cuya propia URL satisface trivialmente la comprobación de resource), y el servidor retransmite el inicio de sesión real a Entra entre bastidores (src/auth/mcpOAuthProvider.ts), devolviendo a Claude el token de acceso real e inalterado de Entra. Después de esa entrega, todo funciona con un token de portador normal emitido por Entra exactamente como lo habría hecho de otra manera.
Cada solicitud de lectura:
Claude se autoriza a través de los endpoints
/authorizey/tokende este servidor (src/auth/mcpOAuthProvider.ts), que retransmiten el inicio de sesión real a Entra a través de una ruta/oauth/callbacky devuelven el token de acceso real de Entra (audiencia = el registro de la aplicación de Entra de esta aplicación). Este servidor solo valida tokens en las solicitudes entrantes (src/auth/tokenVerifier.ts- firma a través de JWKS de Entra, emisor, audiencia, expiración) - nunca emite ni firma uno él mismo.pathse normaliza y se comprueba contra la carpeta raíz configurada (src/files/pathScope.ts) antes de cualquier llamada a Azure - una ruta que se resuelve fuera de la raíz se rechaza independientemente de lo que el token permitiría de otro modo.El token de usuario entrante se intercambia, a través del flujo on-behalf-of de OAuth2 (
src/auth/obo.ts,OnBehalfOfCredentialde@azure/identity), por un nuevo token con ámbitohttps://storage.azure.com/.default. Cada llamada a Azure Files se hace con este token por usuario (src/files/shareClient.ts) - nunca con una entidad de servicio compartida o clave estática.El permiso NTFS del archivo/directorio se obtiene y se evalúa contra los SID que posee el usuario (
src/graph/sidResolver.ts+src/acl/). Si no se concede el acceso, la herramienta devuelve un error de permiso denegado y nada más.Solo si se concede el acceso, la herramienta devuelve el listado del directorio o el contenido del archivo ya obtenido en el paso 3 - convertido a texto plano primero para PDF/Word/Excel (ver más abajo).
Cada invocación - concedida, denegada o con error - emite una línea de registro de auditoría estructurada (
src/audit/log.ts) que registra el usuario, la ruta solicitada y el resultado. Ver "Registro de auditoría" más abajo.
Resolver el SID propio del usuario / SID de grupo usa una llamada de credenciales de cliente de Graph solo de aplicación (src/graph/sidResolver.ts), no OBO. Es deliberado: son metadatos de identidad (a qué grupos pertenece este usuario), no datos de archivo, por lo que una identidad de aplicación compartida allí no viola "nunca usar una credencial compartida para leer datos de archivo" - las lecturas reales de Azure Files siguen siendo estrictamente por usuario en todo momento.
Debido a que resolveHeldSids se ejecuta en cada llamada a list_directory/read_file, su resultado (el SID propio del usuario más cada SID de grupo transitivo) se almacena en caché en memoria por usuario durante SID_CACHE_TTL_MS (por defecto 5 minutos, ver .env.example) - un acierto de caché omite Microsoft Graph por completo. Los cambios de pertenencia a grupos ocurren con poca frecuencia, por lo que esto reduce materialmente la latencia por solicitud y la carga de Graph sin ampliar significativamente la ventana de obsolescencia en un cambio de permisos. Establece SID_CACHE_TTL_MS=0 para deshabilitar el almacenamiento en caché (por ejemplo, mientras depuras un cambio de permisos que no aparece).
El relé OAuth en el paso 1 rastrea los inicios de sesión en curso en dos mapas en memoria de un solo uso y corta duración (pendingAuthorizations, issuedCodes en mcpOAuthProvider.ts). Eso está bien para una sola instancia de App Service, pero significa que este servidor no debe escalarse más allá de una instancia sin antes mover ese estado a un almacén compartido (por ejemplo, Redis) - una segunda instancia fallaría aleatoriamente los inicios de sesión que comenzaron en una instancia diferente de la que terminaron.
Lectura de archivos PDF/Word/Excel
read_file convierte algunos formatos de documentos binarios comunes a texto plano en el servidor (src/files/textExtract.ts), ya que el cliente MCP que renderiza los resultados de este conector no puede analizar por sí mismo un "recurso" binario para que Claude razone sobre él - solo el contenido de texto es realmente legible en el chat. Manejados: .pdf, .docx, .xlsx. No manejados: .doc/.xls binarios heredados (formatos de Office anteriores a 2007), que se degradan a devolver un blob ilegible, y PDFs escaneados/solo imagen, que devuelven un mensaje claro de "sin texto extraíble" en lugar de basura (sin OCR). La salida de extracción está limitada independientemente del límite de tamaño de archivo bruto (MAX_READ_FILE_BYTES), ya que la forma de texto de una hoja de cálculo densa puede exceder su tamaño binario.
Registro de auditoría
El acceso de lectura se aplica enteramente en el propio código de este servidor (ver más arriba), no por RBAC de Azure, por lo que no hay rastro de auditoría en ningún otro lugar - el registro de auditoría de este servidor (src/audit/log.ts) es ese rastro. Cada llamada a list_directory/read_file, independientemente del resultado, emite exactamente una línea JSON a stdout: marca de tiempo, nombre de la herramienta, ruta solicitada, oid y UPN del usuario que llama, la decisión (granted / denied / error), una razón para cualquier cosa que no sea granted, y cuánto tiempo tomó la llamada. Se escribe como una línea JSON de console.log simple en lugar de a través de una biblioteca de registro, para que fluya hacia cualquier pipeline de registro que el destino de despliegue ya recoja de stdout (por ejemplo, el flujo de registros de Azure App Service / Log Analytics) sin cableado adicional.
Configuración requerida de Azure/Entra (no automatizada por este repositorio)
Los pasos completos clic a clic están en SETUP.md. Resumen de lo que realmente se necesita:
RBAC: asigna Storage File Data Privileged Reader (solo lectura; no uses Contributor) al grupo de Entra cuyos miembros deberían poder usar este conector, con ámbito en la propia cuenta de almacenamiento. Esto reemplaza el rol SMB Share Reader del ticket original - ver la justificación más arriba. Esta es una puerta gruesa de "¿puede esta persona siquiera preguntar?", no la comprobación de permisos real - los permisos NTFS reales (aplicados en código, ver más arriba) siguen gobernando lo que cada usuario realmente ve.
Registro de aplicación: un registro de aplicación hace tres trabajos - cliente OAuth para Claude, identidad para el intercambio On-Behalf-Of a Azure Storage, y (normalmente) la identidad solo de aplicación para Graph. Necesita:
Exponer una API: URI de ID de aplicación
api://<client-id>(el predeterminado), con un ámbito llamadoaccess_as_user.Autenticación: exactamente un URI de redirección web,
<PUBLIC_BASE_URL>/oauth/callback- el propio callback de este servidor, no el de Claude. El callback a nivel de plataforma de Claude (https://claude.ai/api/mcp/auth_callback) nunca se registra en Entra en absoluto; ver "Arquitectura" más arriba para saber por qué.Un secreto de cliente.
Este servidor no admite el Registro Dinámico de Clientes - solo reconoce un cliente (el ID de cliente/secreto del propio registro de aplicación), que es también lo que configuras como ID de Cliente OAuth/Secreto al agregar esto como conector personalizado en Claude.
Permisos de Graph API (aplicación, con consentimiento de administrador) en el registro de aplicación al que apunte
GRAPH_CLIENT_ID:User.Read.AllyGroupMember.Read.All(o el más amplioDirectory.Read.All) - necesarios para leeronPremisesSecurityIdentifierpara usuarios y sus pertenencias a grupos transitivas.
Configuración
Todos los ajustes son variables de entorno - ver .env.example y la tabla de referencia completa en SETUP.md. Lo importante para la reutilización: ROOT_PATH (más STORAGE_ACCOUNT_NAME/SHARE_NAME) es lo único que necesita cambiar para reorientar este servidor a una carpeta, recurso compartido o cliente diferente más adelante. Se lee una vez al inicio del proceso y nunca se acepta como parámetro de herramienta, por lo que no hay forma de que un llamador amplíe el ámbito en tiempo de ejecución.
Para reorientar a una carpeta/recurso compartido diferente:
Actualiza
STORAGE_ACCOUNT_NAME,SHARE_NAME,ROOT_PATHen la configuración de App Service.Asegúrate de que el grupo de Entra objetivo tenga
Storage File Data Privileged Readeren la nueva cuenta de almacenamiento.Reinicia la aplicación. No se necesitan cambios de código ni de compilación.
Ejecución local
npm install
cp .env.example .env # fill in real values
npm run devCompilar, verificación de tipos, pruebas
npm run build # tsc type-check + emit to dist/
npm test # vitest - sddl parser, ACE evaluator, path-scope, SID cache, audit log unit testsDespliegue en Azure App Service
Ver SETUP.md para el recorrido completo. Versión corta:
Empaqueta
src/,package.json,package-lock.jsonytsconfig.jsonen un zip: nunca undist/onode_modules/precompilado. El compilador Oryx de Azure lo compila desde cero, en el servidor, en cada implementación (requiere el App SettingSCM_DO_BUILD_DURING_DEPLOYMENT=true).Implementa el zip en un plan de App Service Linux, Node 20+, exactamente una instancia (consulta la nota sobre el estado del relay OAuth en memoria en "Arquitectura" más arriba).
Configura todas las variables de
.env.examplecomo Application Settings del App Service (no un archivo.envcommiteado).PUBLIC_BASE_URLdebe ser la URL HTTPS real del App Service, sin barra final: una barra final produce dobles barras en las URLs generadas y rompe la coincidencia del redirect URI de Entra.Verifica antes de conectar Claude:
GET /healthzdevuelveok, yGET /.well-known/oauth-protected-resource/mcpdevuelve un documento JSON de metadatos (el sufijo/mcpes obligatorio según RFC 9728, ya que la URL del servidor de recursos tiene un componente de ruta/mcp).El endpoint MCP al que se conecta Claude es
POST {PUBLIC_BASE_URL}/mcp.
Este servidor implementa OAuth manualmente (validación de JWT, los endpoints de metadatos
/.well-known/oauth-*, y ahora el relay completo del servidor de autorización, mediante el
mcpAuthRouter del SDK de MCP) en lugar de depender de la integración MCP "Easy Auth"
integrada del App Service. Esa integración es real pero aún está en vista previa, y la
documentación de Microsoft advierte explícitamente contra el reenvío de su token validado a
un recurso downstream; de todos modos tendrías que escribir el intercambio on-behalf-of para
Storage, así que no habría reducido la cantidad de código aquí, solo habría añadido riesgo de
etapa de vista previa.
Limitaciones conocidas
Los grupos AD locales de dominio pueden no resolverse. Las ACL de NTFS se comparan con los SIDs de AD local, resueltos mediante
onPremisesSecurityIdentifierde Microsoft Graph. Los grupos locales de dominio no se sincronizan/escriben de manera confiable en Entra ID, por lo que una ACE que otorga acceso a un grupo local de dominio no sincronizado no se puede comparar. Esto falla de forma cerrada: un SID de grupo no resuelto nunca puede satisfacer una ACE de ALLOW, por lo que el peor caso es que un usuario vea menos de lo que le corresponde, nunca más (src/graph/sidResolver.ts,src/acl/evaluate.ts). Si las ACL de una carpeta de destino usan grupos locales de dominio, verifica durante las pruebas que esto no sub-permita a usuarios reales; si lo hace, la solución es re-ACLar con grupos universales/globales o agregar una búsqueda de respaldo LDAP (no implementada aquí).El recorrido de directorios (
FILE_TRAVERSE) en carpetas ancestras no se verifica por separado. Windows otorga "bypass traverse checking" a Usuarios Autenticados por defecto en la mayoría de las implementaciones reales, por lo que esto coincide con el comportamiento típico del mundo real, pero si el entorno de un cliente tiene restricciones de recorrido no predeterminadas en las carpetas entre la raíz del recurso compartido yROOT_PATH, vuelve a verificar durante las pruebas.Solo una instancia de App Service - consulta la nota sobre el relay OAuth en "Arquitectura" más arriba.
Sin escritura/eliminación/renombrado, por diseño: no es una brecha, es una restricción deliberada.
Los archivos binarios heredados
.doc/.xlsy los PDFs escaneados o solo imagen no son legibles - consulta "Lectura de archivos PDF/Word/Excel" más arriba.Requiere que el AD local del tenant de destino esté sincronizado con Entra (Entra Connect / Cloud Sync) con los SIDs locales fluyendo: todo el modelo de enforcement de NTFS por usuario depende de ello. No funcionará para un tenant solo en la nube, nativo de Entra, sin AD local.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Read-only MCP access to sessions, funnels, campaigns, errors, live visitors, and anomalies.
Read-only CVE intelligence, remediation playbooks, and agent setup guides. Not a scanner.
Read-only access to your VortexIQ store data: audits, KPIs, alerts, Brand DNA, reports, Ask VIQ.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/H1er0/Azure-Files-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server