Skip to main content
Glama
H1er0

Azure Files MCP

by H1er0

Azure Files MCP(読み取り専用)

Claude に Azure Files SMB 共有上の 1 つのフォルダーへの読み取り専用アクセスを提供するリモート MCP サーバーです。接続する各ユーザーは、自分の NTFS アクセス許可で既に許可されている内容のみを表示できます。

ツールは 2 つだけで、他には何もありません。

  • list_directory(path) - 構成されたルートフォルダー配下のファイル/フォルダーを一覧表示します。

  • read_file(path) - 構成されたルートフォルダー配下のファイルの内容を読み取ります。プレーンテキストファイルはそのまま返されます。PDF、Word(.docx)、Excel(.xlsx)は自動的にテキストに変換されます(下記の「PDF/Word/Excel ファイルの読み取り」を参照)。

このコードベースには、書き込み、削除、名前変更のツールはどこにもありません。スタブ化も、設定で無効化もされておらず、単に存在しません。

Azure/Entra のセットアップ手順の詳細については、SETUP.md を参照してください。このファイルではアーキテクチャと設計上の決定事項を説明します。SETUP.md では、ポータルでのクリック操作の詳細な手順を説明します。

なぜこれはチケットを最初に読んだときに想像する内容とは少し異なるのか

当初の設計では、非特権の Storage File Data SMB Share Reader RBAC ロールを割り当て、各ユーザー自身の OAuth トークンを使用して Azure Files の FileREST API を呼び出し、Azure がユーザーごとに NTFS ACL を自動的に適用することを想定していました。

これは機能しません。Microsoft の REST API ドキュメント(Microsoft Entra ID での承認(REST API))で直接確認しました。FileREST のすべての読み取り操作(ディレクトリとファイルの一覧表示、ファイルの取得、ファイルのプロパティの取得など)には、.../files/read と .../readFileBackupSemantics/action の両方が必要です。readFileBackupSemantics/action は、NTFS ACL の評価を明示的にスキップするモードを指す Microsoft 自身の用語であり、Storage File Data Privileged Reader/Contributor によってのみ付与されます。Storage File Data SMB Share Reader は REST のアクセス許可テーブルにはまったく表示されません。これは、実際の SMB プロトコル接続(ポート 445、Kerberos)にのみ適用され、FileREST を呼び出す Node HTTPS バックエンドでは使用できません。

したがって、REST 経由では、ロールは特権(ACL をバイパス)または無関係のいずれかです。Azure 自体に、要求ごとの REST/OAuth ベースで NTFS ACL を適用させる方法はありません。

このサーバーが代わりに行うこと: Storage File Data Privileged Reader(読み取り専用のまま、ユーザーごとに認証されます。下記参照)を使用し、Azure Files がすべてのファイル/フォルダーに対して公開する実際のセキュリティ記述子を使用して、コード内で NTFS アクセス許可を独自に適用します。

  1. ファイル/フォルダーの NTFS アクセス許可を SDDL 文字列として取得します(getPermission REST 呼び出し)。

  2. DACL を個々の ACE に解析します(src/acl/sddl.ts - 手書き。これに対応する保守された Node/TS ライブラリは存在しません)。

  3. Microsoft Graph を介して、呼び出し元ユーザー自身のオンプレミス AD SID と、そのユーザーが推移的に属するすべてのグループ SID を解決します(src/graph/sidResolver.ts)。

  4. 実際の Windows AccessCheck セマンティクスを使用して、その SID セットに対して DACL を評価します。明示的な拒否は明示的な許可よりも優先され、言及されていないビットはデフォルトで拒否されます(src/acl/evaluate.ts)。

これは真にユーザーごとの適用です。Azure の RBAC レイヤーに委任するのではなく、ここで実装されています。Azure には、それを代行してくれる REST 呼び出し可能なメカニズムがないためです。実際のセキュリティ境界はこのコードベースであり、Azure RBAC ではありません。以下のすべてを検討する際には、その点を念頭に置いてください。

Related MCP server: af-filesystem-mcp

アーキテクチャ

このサーバーは独自の OAuth 2.1 認可サーバーです。Claude は Entra と直接通信することはありません。これは意図的な設計上の選択であり、明白な選択ではありません。その理由を説明する価値があります。MCP 仕様では、クライアントが MCP サーバー自身の URL に等しい RFC 8707 の resource パラメータを送信する必要がありますが、Entra はアプリ登録の検証済み識別子 URI に一致する resource 値のみを受け入れます。Entra は *.azurewebsites.net の URL を(未検証ドメインとして)登録することを断固として拒否します。これは AADSTS9010010 としてライブで確認済みであり、ポータルのどの設定でも修正できません。そのため、代わりに Claude はこのサーバーに対して認可し(自身の URL が resource チェックを簡単に満たす)、サーバーは舞台裏で実際のサインインを Entra にリレーし(src/auth/mcpOAuthProvider.ts)、Entra の実際の変更されていないアクセストークンを Claude に渡します。その受け渡しの後、すべては通常の Entra 発行のベアラートークンで、他の場合とまったく同じように機能します。

すべての読み取り要求:

  1. Claude は、このサーバー自身の /authorize および /token エンドポイント(src/auth/mcpOAuthProvider.ts)を通じて認可します。これらは /oauth/callback ルートを介して実際のサインインを Entra にリレーし、Entra の実際のアクセストークン(対象者 = このアプリの Entra アプリ登録)を返します。このサーバーは、受信要求のトークンを検証するだけです(src/auth/tokenVerifier.ts - Entra の JWKS による署名、発行者、対象者、有効期限)。トークンを発行したり署名したりすることはありません。

  2. path は正規化され、構成されたルートフォルダーに対してチェックされます(src/files/pathScope.ts)。これはAzure 呼び出しの前に行われます。ルートの外側に解決されるパスは、トークンが他に何を許可していても拒否されます。

  3. 受信したユーザートークンは、OAuth2 オンビハーフオブフロー(src/auth/obo.ts、@azure/identity の OnBehalfOfCredential)を介して、https://storage.azure.com/.default をスコープとする新しいトークンと交換されます。Azure Files のすべての呼び出しは、このユーザーごとのトークンで行われます(src/files/shareClient.ts)。共有サービスプリンシパルや静的キーは使用されません。

  4. ファイル/ディレクトリの NTFS アクセス許可が取得され、ユーザーが保持する SID に対して評価されます(src/graph/sidResolver.ts + src/acl/)。アクセスが許可されていない場合、ツールはアクセス拒否エラーを返し、それ以外は何も返しません。

  5. アクセスが許可された場合のみ、ツールはステップ 3 で既に取得したディレクトリ一覧またはファイルの内容を返します。PDF/Word/Excel の場合は、最初にプレーンテキストに変換されます(下記参照)。

  6. すべての呼び出し(許可、拒否、エラー)は、ユーザー、要求されたパス、結果を記録する 1 つの構造化監査ログ行(src/audit/log.ts)を出力します。「監査ログ」を参照してください。

ユーザー自身の SID/グループ SID の解決には、OBO ではなくアプリ専用の Graph クライアント資格情報呼び出し(src/graph/sidResolver.ts)を使用します。これは意図的です。これは ID メタデータ(このユーザーがどのグループに属しているか)であり、ファイルデータではないため、そこで共有アプリ ID を使用しても「ファイルデータの読み取りに共有資格情報を使用しない」という原則に違反しません。実際の Azure Files の読み取りは、全体を通して厳密にユーザーごとです。

resolveHeldSids はすべての list_directory/read_file 呼び出しで実行されるため、その結果(ユーザー自身の SID とすべての推移的なグループ SID)は、ユーザーごとに SID_CACHE_TTL_MS(デフォルト 5 分、.env.example を参照)の間、メモリ内にキャッシュされます。キャッシュヒットは Microsoft Graph を完全にスキップします。グループメンバーシップの変更はまれであるため、これにより要求ごとのレイテンシと Graph の負荷が大幅に削減され、アクセス許可の変更に関する古さのウィンドウが実質的に広がることはありません。キャッシュを無効にするには SID_CACHE_TTL_MS=0 を設定します(例: 反映されないアクセス許可の変更をデバッグしている場合)。

ステップ 1 の OAuth リレーは、進行中のサインインを 2 つの短命で使い捨てのメモリ内マップ(mcpOAuthProvider.ts の pendingAuthorizations、issuedCodes)で追跡します。これは単一の App Service インスタンスには問題ありませんが、このサーバーを 1 つのインスタンスを超えてスケーリングしてはならないことを意味します。その状態を共有ストア(例: Redis)に移動しない限り、2 番目のインスタンスは、開始されたインスタンスとは異なるインスタンスで終了したサインインをランダムに失敗させます。

PDF/Word/Excel ファイルの読み取り

read_file は、いくつかの一般的なバイナリドキュメント形式をサーバー側でプレーンテキストに変換します(src/files/textExtract.ts)。これは、このコネクタの結果をレンダリングする MCP クライアントが、Claude が推論できるバイナリ「リソース」ブロブを自身で解析できないためです。チャットで実際に読み取れるのはテキストコンテンツのみです。対応形式: .pdf、.docx、.xlsx。非対応形式: レガシーバイナリの .doc/.xls(2007 以前の Office 形式)。これらは読み取れないブロブを返すフォールバックになります。また、スキャンされた画像のみの PDF は、ガベージではなく明確な「抽出可能なテキストがありません」というメッセージを返します(OCR なし)。抽出出力は、生のファイルサイズ上限(MAX_READ_FILE_BYTES)とは独立して制限されます。密度の高いスプレッドシートのテキスト形式は、バイナリサイズを超える可能性があるためです。

監査ログ

読み取りアクセスは、Azure RBAC ではなく、このサーバー自身のコードで完全に適用されます(上記参照)。したがって、他の場所には監査証跡はありません。このサーバーの監査ログ(src/audit/log.ts)がそれです。すべての list_directory/read_file 呼び出しは、結果に関係なく、stdout に正確に 1 つの JSON 行を出力します。タイムスタンプ、ツール名、要求されたパス、呼び出し元ユーザーの oid と UPN、決定(granted / denied / error)、granted 以外の理由、呼び出しにかかった時間。これはロギングライブラリではなく、プレーンな console.log JSON 行として書き込まれるため、追加の配線なしで、デプロイ先が既に stdout を収集しているログパイプライン(例: Azure App Service のログストリーム / Log Analytics)に流れ込みます。

必要な Azure/Entra 構成(このリポジトリでは自動化されていません)

詳細なクリック操作の手順は SETUP.md にあります。実際に必要なものの概要:

  • RBAC: このコネクタを使用できるようにする Entra グループに Storage File Data Privileged Reader(読み取り専用。Contributor は使用しないでください)をストレージアカウント自体をスコープとして割り当てます。これは元のチケットの SMB Share Reader ロールを置き換えます。上記の根拠を参照してください。これは「この人がそもそも要求できるか」という大まかなゲートであり、実際のアクセス許可チェックではありません。実際の NTFS アクセス許可(コードで適用、上記参照)が、各ユーザーが実際に表示する内容を引き続き決定します。

  • アプリ登録: 1 つのアプリ登録が 3 つの役割を果たします。Claude の OAuth クライアント、Azure Storage へのオンビハーフオブ交換の ID、そして(通常は)Graph のアプリ専用 ID。必要なもの:

    • API の公開: アプリケーション ID URI api://<client-id>(デフォルト)、access_as_user という名前のスコープ。

    • 認証: 正確に 1 つの Web リダイレクト URI、<PUBLIC_BASE_URL>/oauth/callback - これは Claude ではなく、このサーバー自身のコールバックです。Claude のプラットフォーム全体のコールバック(https://claude.ai/api/mcp/auth_callback)は Entra に一切登録されません。理由は上記の「アーキテクチャ」を参照してください。

    • クライアントシークレット。

    • このサーバーは動的クライアント登録をサポートしていません。常に 1 つのクライアント(このアプリ登録自身のクライアント ID/シークレット)のみを認識します。これは、Claude でカスタムコネクタとして追加するときに OAuth クライアント ID/シークレットとして構成するものと同じです。

  • 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)は、後でこのサーバーを別のフォルダー、共有、またはクライアントに向け直すために変更する必要がある唯一のものです。これはプロセス起動時に 1 回読み取られ、ツールパラメータとして受け入れられることはないため、呼び出し元が実行時にスコープを広げる方法はありません。

別のフォルダー/共有に向け直すには:

  1. App Service の構成で STORAGE_ACCOUNT_NAME、SHARE_NAME、ROOT_PATH を更新します。

  2. ターゲットの Entra グループが新しいストレージアカウントで Storage File Data Privileged Reader を持っていることを確認します。

  3. アプリを再起動します。コードやビルドの変更は不要です。

ローカルでの実行

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 を参照してください。短いバージョン:

  1. src/、package.json、package-lock.json、tsconfig.json をzipにパッケージ化する - 決して ビルド済みの dist/ や node_modules/ を含めないこと。Azure の Oryx ビルダーがデプロイのたびにサーバー側で新規にコンパイルする(App Setting SCM_DO_BUILD_DURING_DEPLOYMENT=true が必要)。

  2. zip を Linux App Service プラン、Node 20+ にデプロイする。インスタンスは正確に1つ(上記「Architecture」のインメモリOAuthリレー状態に関する注記を参照)。

  3. .env.example のすべての変数を App Service の Application Settings として設定する(コミットされた .env ファイルではなく)。

  4. PUBLIC_BASE_URL は App Service の実際の HTTPS URL である必要があり、末尾スラッシュなしで指定する - 末尾スラッシュがあると生成されたURLに二重スラッシュが生じ、Entra のリダイレクトURIの一致が壊れる。

  5. Claude を接続する前に検証する: GET /healthz が ok を返すこと、および GET /.well-known/oauth-protected-resource/mcp が JSON メタデータドキュメントを返すこと(リソースサーバーのURL自体に /mcp パスコンポーネントが含まれるため、RFC 9728 に従って /mcp サフィックスが必要)。

  6. Claude が接続する MCP エンドポイントは POST {PUBLIC_BASE_URL}/mcp である。

このサーバーは、App Service の組み込み「Easy Auth」MCP 統合に頼るのではなく、OAuth を手動で実装している(JWT 検証、/.well-known/oauth-* メタデータエンドポイント、そして MCP SDK の mcpAuthRouter による完全な認可サーバーリレー)。その統合は実在するがまだプレビュー段階であり、Microsoft 自身のドキュメントでも、検証済みトークンをダウンストリームリソースに転送することに対する明示的な警告がある - いずれにせよ Storage 用の on-behalf-of 交換を自分で書く必要があるため、コード量を減らせるわけではなく、プレビュー段階のリスクが増えるだけだった。

既知の制限事項

  • ドメインローカル AD グループは解決できない場合がある。 NTFS ACL はオンプレミス AD の SID と照合され、Microsoft Graph の onPremisesSecurityIdentifier 経由で解決される。ドメインローカルグループは Entra ID への同期/ライトバックが確実ではないため、未同期のドメインローカルグループにアクセスを許可する ACE は照合できない。これは クローズド に失敗する: 未解決のグループ SID が ALLOW ACE を満たすことは決してないため、最悪のケースでもユーザーが本来の権限より少なく見えるだけで、多く見えることは決してない(src/graph/sidResolver.ts、src/acl/evaluate.ts)。対象フォルダの ACL がドメインローカルグループを使用している場合、テスト中に実際のユーザーへの権限不足が発生しないか検証すること。発生する場合は、ユニバーサル/グローバルグループでの ACL 再設定、または LDAP フォールバックルックアップの追加(ここでは未実装)が修正策となる。

  • 祖先フォルダに対するディレクトリトラバーサル(FILE_TRAVERSE)は個別にチェックされない。 Windows はほとんどの実際のデプロイ環境で Authenticated Users に「トラバースチェックのバイパス」をデフォルトで付与するため、これは一般的な実世界の動作と一致するが、クライアントの環境で共有ルートと ROOT_PATH の間のフォルダに非デフォルトのトラバース制限がある場合は、テスト中に再検証すること。

  • App Service のインスタンスは1つのみ - 上記「Architecture」の OAuth リレーに関する注記を参照。

  • 書き込み/削除/名前変更は不可、設計上の意図による - 欠陥ではなく、意図的な制約である。

  • レガシーバイナリの .doc/.xls およびスキャン済み/画像のみの PDF は読み取れない - 上記「Reading PDF/Word/Excel files」を参照。

  • 対象テナントのオンプレミス AD が Entra に同期されている必要がある(Entra Connect / Cloud Sync)オンプレミス SID が流れ込んでいること - ユーザーごとの NTFS 強制モデル全体がこれに依存する。オンプレミス AD を持たないクラウド専用の Entra ネイティブテナントでは機能しない。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables KUIK Agent to access on-premise SMB/CIFS shares on a Windows Server using the employee's own credentials, enforcing live ACLs. Supports file operations such as list, read, write, search, move, copy, delete, and upload/download without mounting or duplicating data.
    -
  • A
    license
    Not graded
    quality
    A
    maintenance
    Gives Analysis Facility users read-only browse, read, stat, and grep access to their own files on shared NFS home and Ceph data areas, with kernel-enforced per-user permissions.
    48 PyPI
    Apache 2.0
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables read-only file-management operations from AI assistants, including health checks, connection and folder listings, search, share-link resolution, metadata retrieval, and on-demand document reading.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables read-only access to private filesystem workspaces through MCP, allowing clients to list, inspect, read, and search files without uploading the workspace or opening inbound firewall ports.
    1
    MIT