Azure Files MCP
Azure Files MCP (только чтение)
Удалённый MCP-сервер, который даёт Claude доступ только для чтения к одной папке на SMB-ресурсе Azure Files, где каждый подключающийся пользователь видит только то, что уже разрешают его собственные разрешения NTFS.
Два инструмента, и ничего больше:
list_directory(path)— список файлов/папок в настроенной корневой папке.read_file(path)— чтение содержимого файла в настроенной корневой папке. Обычные текстовые файлы возвращаются как есть; PDF, Word (.docx) и Excel (.xlsx) автоматически преобразуются в текст (см. «Чтение PDF/Word/Excel файлов» ниже).
В этом коде нет ни одного инструмента для записи, удаления или переименования — не заглушек, не отключённых конфигурацией, просто отсутствуют.
Пошаговые инструкции по настройке Azure/Entra см. в SETUP.md. Этот файл описывает архитектуру и проектные решения; в SETUP.md — пошаговое прохождение по порталу.
Почему это не совсем то, что можно предположить при первом чтении задачи
Первоначальный дизайн предполагал: назначить непривилегированную роль RBAC Storage File Data SMB Share Reader, затем вызывать FileREST API Azure Files с собственным OAuth-токеном каждого пользователя, и Azure будет автоматически применять NTFS ACL для каждого пользователя.
Это не работает. Проверено напрямую по документации REST API Microsoft
(Authorize with Microsoft Entra ID (REST API)):
каждая операция чтения FileREST (List Directories and Files, Get File, Get File
Properties, ...) требует как .../files/read, так и .../readFileBackupSemantics/action.
readFileBackupSemantics/action — собственный термин Microsoft для режима, который
явно пропускает оценку NTFS ACL — предоставляется только Storage File Data Privileged
Reader/Contributor. Storage File Data SMB Share Reader вообще не появляется в таблице
разрешений REST — он применяется только к настоящим подключениям по протоколу SMB
(порт 445, Kerberos), которые Node HTTPS-бэкенд, вызывающий FileREST, использовать не
может.
Таким образом, через REST роль либо привилегированная (обходит ACL), либо неактуальна. Невозможно заставить сам Azure применять NTFS ACL на основе каждого запроса REST/OAuth.
Что вместо этого делает этот сервер: использует Storage File Data Privileged Reader (по-прежнему только чтение, по-прежнему аутентифицирован для каждого пользователя — см. ниже) и сам применяет разрешения NTFS в коде, используя реальный дескриптор безопасности, который Azure Files предоставляет для каждого файла/папки:
Получает разрешение NTFS файла/папки в виде строки SDDL (вызов REST
getPermission).Разбирает DACL на отдельные ACE (
src/acl/sddl.ts— написан вручную; не существует поддерживаемой Node/TS библиотеки для этого).Разрешает собственный локальный AD SID вызывающего пользователя и все SID групп, в которые он транзитивно входит, через Microsoft Graph (
src/graph/sidResolver.ts).Оценивает DACL против этого набора SID, используя реальную семантику Windows AccessCheck — явный запрет побеждает явное разрешение, неуказанные биты по умолчанию запрещены (
src/acl/evaluate.ts).
Это действительно применение прав для каждого пользователя — просто реализовано здесь, а не делегировано уровню RBAC Azure, потому что у Azure нет механизма, вызываемого через REST, который сделал бы это за вас. Реальная граница безопасности — этот код, а не RBAC Azure — имейте это в виду, рассуждая о любом из нижеследующего.
Архитектура
Этот сервер — собственный сервер авторизации OAuth 2.1 — Claude никогда не общается с
Entra напрямую. Это осознанное проектное решение, а не очевидное, поэтому стоит
объяснить, почему: спецификация MCP требует, чтобы клиенты отправляли параметр
resource RFC 8707, равный URL самого MCP-сервера, а Entra принимает только значение
resource, соответствующее проверенному URI идентификатора в регистрации приложения.
Entra категорически отказывается регистрировать любой URL *.azurewebsites.net как
таковой (непроверенный домен) — подтверждено вживую как AADSTS9010010, и это нельзя
исправить никакими настройками портала. Поэтому вместо этого Claude авторизуется против
этого сервера (чей собственный URL тривиально удовлетворяет проверке resource), а
сервер ретранслирует реальный вход в Entra за кулисами (src/auth/mcpOAuthProvider.ts),
возвращая Claude реальный, неизменённый токен доступа Entra. После этой передачи всё
работает с обычным токеном-носителем, выданным Entra, точно так же, как и должно было бы.
Каждый запрос на чтение:
Claude авторизуется через собственные конечные точки
/authorizeи/tokenэтого сервера (src/auth/mcpOAuthProvider.ts), которые ретранслируют фактический вход в Entra через маршрут/oauth/callbackи возвращают реальный токен доступа Entra (аудитория = регистрация приложения Entra этого приложения). Этот сервер только проверяет токены во входящих запросах (src/auth/tokenVerifier.ts— подпись через JWKS Entra, издатель, аудитория, срок действия) — сам он никогда не выпускает и не подписывает токены.pathнормализуется и проверяется относительно настроенной корневой папки (src/files/pathScope.ts) до любого вызова Azure — путь, ведущий за пределы корня, отклоняется независимо от того, что в противном случае разрешил бы токен.Входящий пользовательский токен обменивается через поток on-behalf-of OAuth2 (
src/auth/obo.ts,OnBehalfOfCredentialиз@azure/identity) на новый токен с областьюhttps://storage.azure.com/.default. Каждый вызов Azure Files выполняется с этим токеном конкретного пользователя (src/files/shareClient.ts) — никогда с общим субъектом службы или статическим ключом.Разрешение NTFS файла/каталога извлекается и оценивается относительно SID, которыми владеет пользователь (
src/graph/sidResolver.ts+src/acl/). Если доступ не предоставлен, инструмент возвращает ошибку отказа в доступе и ничего больше.Только если доступ предоставлен, инструмент возвращает список каталога или содержимое файла, уже полученные на шаге 3 — сначала преобразованные в обычный текст для PDF/Word/Excel (см. ниже).
Каждый вызов — предоставлен, отклонён или с ошибкой — генерирует одну строку структурированного журнала аудита (
src/audit/log.ts), записывающую пользователя, запрошенный путь и результат. См. «Журнал аудита» ниже.
Разрешение собственного SID/SID групп пользователя использует только приложение
Graph client-credentials вызов (src/graph/sidResolver.ts), а не OBO. Это осознанно:
это метаданные идентификации (в какие группы входит этот пользователь), а не данные
файлов, поэтому общая идентичность приложения там не нарушает «никогда не использовать
общие учётные данные для чтения данных файлов» — фактические чтения Azure Files остаются
строго попользовательскими на протяжении всего процесса.
Поскольку resolveHeldSids выполняется при каждом вызове list_directory/read_file,
его результат (собственный SID пользователя плюс все транзитивные SID групп)
кэшируется в памяти для каждого пользователя на SID_CACHE_TTL_MS (по умолчанию 5
минут, см. .env.example) — попадание в кэш полностью пропускает Microsoft Graph.
Изменения членства в группах происходят достаточно редко, чтобы это существенно
сокращало задержку на запрос и нагрузку на Graph, не слишком расширяя окно устаревания
при изменении разрешений. Установите SID_CACHE_TTL_MS=0, чтобы отключить кэширование
(например, при отладке изменения разрешений, которое не проявляется).
Ретрансляция OAuth на шаге 1 отслеживает выполняющиеся входы в двух недолговечных
одноразовых картах в памяти (pendingAuthorizations, issuedCodes в
mcpOAuthProvider.ts). Это нормально для одного экземпляра App Service, но означает,
что этот сервер нельзя масштабировать более чем на один экземпляр без переноса
этого состояния в общее хранилище (например, Redis) — второй экземпляр будет случайным
образом проваливать входы, начатые на другом экземпляре, чем тот, на котором они
завершились.
Чтение PDF/Word/Excel файлов
read_file преобразует несколько распространённых бинарных форматов документов в
обычный текст на стороне сервера (src/files/textExtract.ts), поскольку MCP-клиент,
отображающий результаты этого коннектора, сам не может разобрать бинарный «ресурс»
для анализа Claude — в чате читаемо только текстовое содержимое. Обрабатываются: .pdf,
.docx, .xlsx. Не обрабатываются: устаревшие бинарные .doc/.xls (форматы Office
до 2007 года), которые возвращают нечитаемый блоб, и сканированные/только изображения
PDF, которые возвращают понятное сообщение «нет извлекаемого текста», а не мусор (без
OCR). Выходные данные извлечения ограничены независимо от ограничения размера
необработанного файла (MAX_READ_FILE_BYTES), поскольку текстовая форма плотной
электронной таблицы может превышать её бинарный размер.
Журнал аудита
Доступ на чтение обеспечивается полностью в собственном коде этого сервера (см. выше),
а не RBAC Azure, поэтому нигде больше нет журнала аудита — журнал аудита этого сервера
(src/audit/log.ts) и есть он. Каждый вызов list_directory/read_file, независимо от
результата, генерирует ровно одну строку JSON в stdout: временная метка, имя
инструмента, запрошенный путь, oid и UPN вызывающего пользователя, решение (granted /
denied / error), причина для всего, кроме granted, и сколько времени занял вызов.
Он записывается как обычная строка JSON через console.log, а не через библиотеку
логирования, чтобы он попадал в любой конвейер журналов, который целевое развёртывание
уже собирает из stdout (например, поток журналов Azure App Service / Log Analytics) без
дополнительной настройки.
Требуемая конфигурация Azure/Entra (не автоматизируется этим репозиторием)
Полные пошаговые инструкции — в SETUP.md. Краткое изложение того, что фактически нужно:
RBAC: назначьте Storage File Data Privileged Reader (только чтение; не используйте Contributor) группе Entra, члены которой должны иметь возможность использовать этот коннектор, с областью действия на саму учётную запись хранения. Это заменяет роль SMB Share Reader из исходной задачи — см. обоснование выше. Это грубый шлюз «может ли этот человек вообще спросить», а не настоящая проверка разрешений — реальные разрешения NTFS (применяемые в коде, см. выше) по-прежнему определяют, что фактически видит каждый пользователь.
Регистрация приложения: одна регистрация приложения выполняет три задачи — OAuth-клиент для Claude, идентичность для обмена On-Behalf-Of с Azure Storage и (обычно) идентичность только для приложения для Graph. Ей нужно:
Expose an API: URI идентификатора приложения
api://<client-id>(по умолчанию) с областью с именемaccess_as_user.Authentication: ровно один URI перенаправления Web,
<PUBLIC_BASE_URL>/oauth/callback— собственный обратный вызов этого сервера, а не Claude. Общеплатформенный обратный вызов Claude (https://claude.ai/api/mcp/auth_callback) вообще не регистрируется в Entra; см. «Архитектура» выше, почему.Секрет клиента.
Этот сервер не поддерживает Dynamic Client Registration — он распознаёт только один клиент (собственный идентификатор клиента/секрет этой регистрации приложения), который также настраивается как OAuth Client ID/Secret при добавлении этого коннектора в Claude.
Разрешения Graph API (приложение, с согласия администратора) на той регистрации приложения, на которую указывает
GRAPH_CLIENT_ID:User.Read.AllиGroupMember.Read.All(или более широкоеDirectory.Read.All) — необходимо для чтенияonPremisesSecurityIdentifierдля пользователей и их транзитивных членств в группах.
Конфигурация
Все настройки — переменные окружения — см. .env.example и полную
справочную таблицу в SETUP.md. Важная для повторного использования:
ROOT_PATH (плюс STORAGE_ACCOUNT_NAME/SHARE_NAME) — единственное, что нужно
изменить, чтобы позже перенаправить этот сервер на другую папку, ресурс или клиента.
Он читается один раз при запуске процесса и никогда не принимается как параметр
инструмента, поэтому у вызывающей стороны нет способа расширить область действия во
время выполнения.
Чтобы перенаправить на другую папку/ресурс:
Обновите
STORAGE_ACCOUNT_NAME,SHARE_NAME,ROOT_PATHв конфигурации App Service.Убедитесь, что целевая группа Entra имеет
Storage File Data Privileged Readerна новой учётной записи хранения.Перезапустите приложение. Изменения кода или сборки не требуются.
Локальный запуск
npm install
cp .env.example .env # fill in real values
npm run devСборка, проверка типов, тесты
npm run build # tsc type-check + emit to dist/
npm test # vitest - sddl parser, ACE evaluator, path-scope, SID cache, audit log unit testsРазвёртывание в Azure App Service
Полное прохождение см. в SETUP.md. Краткая версия:
Упакуйте
src/,package.json,package-lock.jsonиtsconfig.jsonв zip-архив — ни в коем случае не предварительно собранныеdist/илиnode_modules/. Сборщик Oryx от Azure компилирует их заново на стороне сервера при каждом развертывании (требуется параметр приложенияSCM_DO_BUILD_DURING_DEPLOYMENT=true).Разверните zip-архив в плане App Service на Linux, Node 20+, ровно один экземпляр (см. примечание о состоянии ретрансляции OAuth в памяти в разделе «Архитектура» выше).
Задайте все переменные из
.env.exampleкак параметры приложения App Service (а не как закоммиченный файл.env).PUBLIC_BASE_URLдолжен быть реальным HTTPS-URL App Service без завершающего слэша — иначе в генерируемых URL появятся двойные слэши, и это нарушит сопоставление redirect URI в Entra.Перед подключением Claude проверьте:
GET /healthzвозвращаетok, аGET /.well-known/oauth-protected-resource/mcpвозвращает JSON-документ с метаданными (суффикс/mcpобязателен согласно RFC 9728, поскольку сам URL сервера ресурсов содержит компонент пути/mcp).Конечная точка MCP, к которой подключается Claude, — это
POST {PUBLIC_BASE_URL}/mcp.
Этот сервер реализует OAuth вручную (валидация JWT, конечные точки метаданных /.well-known/oauth-* и теперь полная ретрансляция сервера авторизации — через mcpAuthRouter из MCP SDK), а не полагается на встроенную интеграцию MCP «Easy Auth» от App Service. Эта интеграция реальна, но всё еще находится в предварительной версии, и в документации Microsoft прямо предупреждают о недопустимости пересылки проверенного токена нижестоящему ресурсу — в любом случае пришлось бы самостоятельно писать обмен от имени пользователя (on-behalf-of) для Storage, так что это не сократило бы объем кода, а лишь добавило бы риски стадии предварительной версии.
Известные ограничения
Локальные доменные группы AD могут не разрешаться. Списки управления доступом NTFS сопоставляются с SID локального AD, которые разрешаются через
onPremisesSecurityIdentifierиз Microsoft Graph. Локальные доменные группы ненадежно синхронизируются/записываются обратно в Entra ID, поэтому ACE, предоставляющий доступ несинхронизированной локальной доменной группе, не может быть сопоставлен. Это завершается закрыто: неразрешенный SID группы никогда не может удовлетворить ACE типа ALLOW, поэтому худший случай — пользователь видит меньше, чем ему положено, но никогда больше (src/graph/sidResolver.ts,src/acl/evaluate.ts). Если в ACL целевой папки используются локальные доменные группы, проверьте во время тестирования, что это не занижает права реальных пользователей; если занижает, исправление — либо перенастройка ACL с универсальными/глобальными группами, либо добавление резервного поиска через LDAP (здесь не реализовано).Обход каталога (
FILE_TRAVERSE) для родительских папок отдельно не проверяется. Windows по умолчанию предоставляет «обход проверки обхода» (bypass traverse checking) аутентифицированным пользователям в большинстве реальных развертываний, поэтому это соответствует типичному реальному поведению, но если в окружении клиента на папках между корнем общего ресурса иROOT_PATHдействуют нестандартные ограничения обхода, перепроверьте это во время тестирования.Только один экземпляр App Service — см. примечание о ретрансляции OAuth в разделе «Архитектура» выше.
Нет записи/удаления/переименования — это осознанное проектное решение, а не пробел.
Устаревшие бинарные файлы
.doc/.xlsи отсканированные PDF-файлы, содержащие только изображения, не читаются — см. раздел «Чтение PDF/Word/Excel» выше.Требуется, чтобы локальный AD целевого клиента был синхронизирован с Entra (Entra Connect / Cloud Sync) с передачей локальных SID — на этом строится вся модель принудительного применения NTFS для каждого пользователя. Не будет работать для арендатора только в облаке, нативном для Entra, без локального AD.
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