Qdrant RBAC について
Qdrant RBACは、OIDC認証とロールに応じたアクセス制御を備え、Qdrantのコレクションを公開するFastMCPサーバーです。権限付与情報をQdrantに保存し、コレクション単位の権限に加えてドキュメント単位のポリシーも適用できます。
主な機能
- 01OIDCトークンの検証と範囲限定Qdrant JWTの生成
- 02MCPツールによるロール権限の管理
- 03コレクションごとの読み取り・書き込み・管理権限
- 04ペイロードポリシーによるドキュメントアクセスの絞り込み
- 05Qdrantポイントの検索・閲覧・挿入・削除
活用シーン
- 01チームのアクセスを特定のQdrantコレクションに限定
- 02タグやフィールドで閲覧可能なドキュメントを制限
- 03実行時にロール権限を付与または取り消し
- 04MCPクライアントに認証付きベクトル検索を提供
MCP 対応
ストリーミングHTTPのMCPサーバーは、Qdrantのコレクションとポイントを操作するツール、およびロール権限の管理ツールを提供します。クライアントのOIDCトークンを検証し、各リクエストに対応する範囲の権限を適用します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
docker run -i --rm -e OIDC_ISSUER_URL -e OIDC_AUDIENCE -e QDRANT_URL -e QDRANT_JWT_SECRET ghcr.io/fidonis/qdrant-mcp-rbac:0.4.0OIDC_ISSUER_URLOIDC issuer / realm URL, e.g. https://kc.example.com/realms/myrealmOIDC_AUDIENCEExpected aud claim of incoming access tokens, e.g. mcp-serverQDRANT_URLQdrant HTTP endpoint, e.g. http://qdrant:6333QDRANT_JWT_SECRETシークレットHS256 signing secret, at least 32 bytes; must equal Qdrant's service.api_keyローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ghcr.io/fidonis/qdrant-mcp-rbac:0.4.0Streamable HTTPツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
qdrant-mcp-rbac
Fidonis がメンテナンスしています · 商標に関する通知は TRADEMARK.md をご覧ください。

ストリーミング可能な HTTP と OIDC ベースの認証(Keycloak 互換)を使用し、 ロールベースのアクセス制御を Qdrant ネイティブの JWT アクセスルールに マッピングして、Qdrant ベクトルデータベースを公開する FastMCP サーバーです。
ロールの権限付与は設定ファイルではなく、専用の Qdrant コレクション内にデータとして 保存されます。管理者は MCP ツールを使って、実行時に管理できます。
┌──────────┐ OIDC Bearer ┌─────────────────┐ Qdrant JWT ┌──────────┐
│ MCP host │ ──────────────▶ │ qdrant-mcp-rbac │ ──────────────▶ │ Qdrant │
│ (client) │ │ (FastMCP) │ │ │
└──────────┘ └─────────────────┘ │ ┌─────┐ │
│ │ │_rbac│ │
▼ │ │_acl │ │
Keycloak │ └─────┘ │
(JWKS, OIDC discovery) └──────────┘
クイックスタート(Docker)
docker run --rm -p 8000:8000 \
-e OIDC_ISSUER_URL=https://kc.example.com/realms/myrealm \
-e OIDC_AUDIENCE=mcp-server \
-e QDRANT_URL=http://qdrant:6333 \
-e QDRANT_JWT_SECRET=<same secret as Qdrant's api-key> \
ghcr.io/fidonis/qdrant-mcp-rbac:latest
/mcp はストリーミング可能な HTTP エンドポイント、/health は認証不要です。
すべての変数については、docker/.env.example をご覧ください。
仕組み
- MCP クライアントは OIDC アクセストークン(
Authorization: Bearer …)を付けてリクエストを送信します。 OIDCAuthMiddlewareは OIDC プロバイダーの JWKS に照らしてトークンを検証します (署名、有効期限、オーディエンス、発行者については、トークンの検証をご覧ください)。JWTBuilderはユーザー用の Qdrant JWT を生成します。- ユーザーが 緊急用管理者ロール(
RBAC_ADMIN_ROLE)に属する場合、 グローバル管理トークンが直接発行されます。 - それ以外の場合、
AclResolverは ACL コレクション(RBAC_ACL_COLLECTION、 デフォルトは_rbac_acl)から構築された、キャッシュ済みのrole -> [grants]マッピングを返します。
- ユーザーが 緊急用管理者ロール(
- Qdrant JWT がリクエストスコープに付加されます。
- 各 MCP ツールは、対象コレクションに対するユーザーの実効アクセス権を確認し、 生成された JWT を使ってリクエストを Qdrant に転送します。
トークンの検証
トークンは PyJWT を使って、発行者が公開する鍵
({OIDC_ISSUER_URL}/.well-known/openid-configuration、続いてその jwks_uri)に照らして検証されます。
- 署名アルゴリズムは署名鍵(その
alg、または指定がない RSA / EC 鍵の場合はRS256/ES256)から取得し、トークンヘッダーからは取得しません。受け入れられるのはRS256、RS384、RS512、ES256、ES384、ES512のみです。対称鍵 (HS*)およびnoneトークンは拒否されます。 - 必須クレーム:
exp、aud(OIDC_AUDIENCEを含む必要があります)、iss(ディスカバリードキュメント内の発行者と一致する必要があります)、空でないsub。expまたはaudがないトークンは拒否されます。nbfが存在する場合は検証されます。 iatは検証されません。そのため、このホストと ID プロバイダー間の時刻のずれによって、発行直後のトークンが拒否されることはありません。- 鍵のローテーション:鍵は
OIDC_JWKS_CACHE_TTL秒間キャッシュされます。不明なkidを持つトークンがあると、拒否する前に JWKS がちょうど 1 回再取得されます。 - ロールは
realm_access.rolesおよびすべてのresource_access.<client>.rolesから読み取られます。
ACL コレクションのスキーマ
各権限付与は _rbac_acl 内の 1 つのポイントです。ポイント ID は決定的な
uuid5(role, collection) なので、同じ権限を再付与しても冪等です。ペイロード:
{ "role": "data-scientist", "collection": "embeddings_v2", "access": "rw" }
access は次のいずれかです。
r– 読み取り専用rw– 読み取りと書き込みm– 管理(グローバル。collectionは参考情報です。慣例として"*"を使用します)
コレクションは、サーバーが自己発行したサービス用トークンを使って、初回読み取り時に自動作成されます。
ドキュメントレベルのアクセス制御(doc_policy)
コレクションレベルのアクセス制御では、ロールがコレクションを読み取れるかどうかを決定します。
権限付与には、さらに任意の doc_policy を設定して、そのコレクション内でロールが参照できる
**個々のドキュメント(ポイント)**を制限できます。サーバーはポリシーを Qdrant のペイロードフィルターに変換し、すべての読み取り処理
(search_collection、
search_collection_by_text、scroll_collection、list_documents)に must 句として追加します
(ファセット処理も、ロールが参照できるドキュメントだけを対象にします)。呼び出し元が指定した query_filter
の値は保持され、ドキュメントフィルターも併せて追加されるため、両方が適用されます。
doc_policy のない権限付与は従来どおり動作し、コレクション内のすべてのドキュメントを参照できます。
スキーマ
{
"default": "allow" | "deny",
"conditions": [
{ "field": "<payload field>", "mode": "allow" | "deny", "values": ["<v>", "…"] }
]
}
default: "allow"— デフォルトですべてのドキュメントが表示され、条件に一致するドキュメントはdenyによって非表示になります。default: "deny"— デフォルトではどのドキュメントも表示されず、条件に一致するドキュメントはallowによって表示されます。values: ["*"]— 「すべてのドキュメント」を意味するワイルドカードです。allow条件(デフォルトが deny)の場合はフィルターが解除され、コレクション全体が表示されます。deny条件(デフォルトが allow)の場合は、空の結果セットが返されます。allow条件がないdefault: "deny"→ 空の結果セットになります。
同じモードの条件が複数ある場合は、和集合として扱われます(いずれかの deny 条件に一致するドキュメントは非表示になり、いずれかの allow 条件に一致するドキュメントは表示されます)。
複数ロールのマージ
ユーザーが同じコレクションに対して複数の許可(異なるロール経由)を持つ場合、サーバーは最も許容的なルールを使ってポリシーをマージします。
- 一致する許可のいずれかに
doc_policyがない場合、そのコレクションに対するユーザーのアクセスは制限されません。 - 一致する許可のいずれかが
default: "allow"の場合、マージ後のポリシーはdefault: "allow"となり、それらの許可の deny 条件が統合されます(デフォルトが deny の許可は無視されます)。 - それ以外の場合、すべての許可が
default: "deny"となり、allow 条件が統合されます。
推奨ペイロードフィールド:acl_tags
アクセス制御が必要なドキュメント用に、新しい任意フィールド acl_tags: list[str] が予約されています。このフィールドは必須ではありません。acl_tags のないドキュメントも引き続き完全に機能します。
- デフォルトが allow の許可では、タグのないドキュメントが
acl_tagsに対するdeny条件に一致することはありません → 常に表示されます。 - デフォルトが deny の許可では、タグのないドキュメントが
acl_tagsに対するallow条件を満たすことはありません → 暗黙的に非表示になります。
ブートストラップスクリプト demo/bootstrap/vectorize.py は、各 Markdown ソース(データディレクトリからの相対 POSIX パス)を、そのファイルから取り込まれるすべてのチャンクに適用するタグのリストに対応付ける、任意のサイドカーファイル demo/data/acl_tags.yml を読み込みます。
例
デフォルトを allow にして、機密ドキュメントを deny する場合:
{
"role": "finance-analyst",
"collection": "finance",
"access": "r",
"doc_policy": {
"default": "allow",
"conditions": [
{ "field": "acl_tags", "mode": "deny", "values": ["confidential", "board-only"] }
]
}
}
デフォルトを deny にして、公開ドキュメントのみを allow する場合:
{
"role": "guest",
"collection": "finance",
"access": "r",
"doc_policy": {
"default": "deny",
"conditions": [
{ "field": "acl_tags", "mode": "allow", "values": ["public"] }
]
}
}
source フィールドでフィルタリングする場合(acl_tags は不要):
{
"role": "junior-analyst",
"collection": "finance",
"access": "r",
"doc_policy": {
"default": "deny",
"conditions": [
{ "field": "source", "mode": "allow", "values": ["annual-report.md", "q4-summary.md"] }
]
}
}
grant_access は同じ形式を受け付けます。
{
"tool": "grant_access",
"args": {
"role": "guest",
"collection": "finance",
"access": "r",
"doc_policy": {
"default": "deny",
"conditions": [
{ "field": "acl_tags", "mode": "allow", "values": ["public"] }
]
}
}
}
構成
リポジトリでは、本番用サーバー(src/)とデモ用素材(demo/)を分けています。src/ と demo/ 内の各プロジェクトは、それぞれ独自の pyproject.toml と .venv を持つ独立した uv プロジェクトです。リポジトリのルートには仮想環境がありません。
src/ # uv project: the FastMCP server
pyproject.toml
uv.lock
.venv/ # created by `uv sync` (gitignored)
.env # local config (gitignored)
.env.example # template for `.env`
main.py # Entry point (uvicorn)
config.py # Settings (pydantic-settings, .env)
auth/
models.py # OIDCClaims, CollectionAccess, AclEntry, QdrantToken
oidc.py # JWKS-based OIDC token validator (PyJWT)
acl.py # AclResolver: TTL-cached read of the ACL collection
jwt_builder.py # Builds Qdrant JWTs from claims + ACL mapping
qdrant/
client.py # AsyncQdrantClient factory (per-request, JWT-scoped)
collections.py # Thin async wrappers around Qdrant collection operations
meta.py # Read/write helpers for the _collection_meta system collection
mcp_app/ # named `mcp_app` to avoid shadowing the installed `mcp` SDK
server.py # FastMCP instance + Starlette app wiring
tools.py # MCP tools: data tools + ACL admin tools
middleware.py # ASGI auth middleware: OIDC → Qdrant JWT
tests/ # pytest suite, run from src/ (kept out of the Docker image)
fakes/oidc.py # In-process OIDC issuer: RSA key, JWKS, token minting
test_doc_filter.py
test_facet.py
test_jwt_builder.py
test_oidc_validator.py
test_scroll.py
demo/ # everything for the end-to-end demo
README.md # Demo quickstart (order of operations)
bootstrap/ # uv project: vectorize Markdown → Qdrant collections
pyproject.toml
uv.lock
.venv/
vectorize.py
client/ # uv project: OIDC + LLM CLI client
pyproject.toml
uv.lock
.venv/
client.py
agent.py
oidc.py
config.py
data/ # demo seed data (Markdown, collection = file stem up to the first "-")
finance-2025.md
finance-2026.md
finance-aquise.md
it.md
sales.md
セットアップ
前提条件
- Python 3.11 以降
- uv がインストールされていること
- 接続可能な OIDC プロバイダー(例:Keycloak)
- JWT RBAC が有効になっている、接続可能な Qdrant インスタンス
インストール
cd src
uv sync
これにより、すべての依存関係を含む src/.venv が作成されます。リポジトリのルートには独自の .venv はありません。
設定
サンプルの環境変数ファイルをコピーし、値を調整します。
cp src/.env.example src/.env
サーバーは config.py を基準に解決した src/.env を読み込むため、実行時の作業ディレクトリにかかわらず、ファイルは src/ 内に置いてください。
主な変数:
| 変数 | 用途 |
|---|---|
OIDC_ISSUER_URL | Keycloak のレルム URL。例:https://kc.example.com/realms/myrealm |
OIDC_AUDIENCE | 想定される aud クレーム。例:mcp-server |
OIDC_JWKS_CACHE_TTL | OIDC ディスカバリードキュメントと JWKS のキャッシュ TTL(デフォルトは 3600 秒) |
QDRANT_URL | Qdrant の HTTP エンドポイント |
QDRANT_JWT_SECRET | 署名シークレット — Qdrant の service.api_key と一致させる必要があります。32 バイト以上を使用してください。短い HS256 キーについては PyJWT が警告します(例:openssl rand -hex 32) |
QDRANT_JWT_TTL | リクエストごとに生成される Qdrant JWT の有効期間(デフォルトは 3600 秒、最小は 60 秒) |
MCP_HOST / MCP_PORT / MCP_PATH | リッスンするアドレスとパス(デフォルトは 0.0.0.0:8000/mcp) |
LOG_LEVEL | ログレベル(デフォルトは INFO) |
EMBEDDING_API_URL | OpenAI 互換の埋め込み API のベース URL(デフォルトは http://localhost:11434/v1) |
EMBEDDING_API_KEY | 埋め込みエンドポイント用の任意のベアラートークン |
EMBEDDING_META_COLLECTION | コレクションごとの埋め込みメタデータを保存するコレクション(デフォルトは _collection_meta) |
RBAC_ADMIN_ROLE | グローバルな管理権限を付与する OIDC ロール(デフォルトは qdrant-admin) |
RBAC_ACL_COLLECTION | 権限付与情報を保存する Qdrant コレクション(デフォルトは _rbac_acl) |
RBAC_ACL_CACHE_TTL | メモリ内 ACL キャッシュの TTL(デフォルトは 60 秒) |
RBAC_SERVICE_TOKEN_TTL | ACL の読み取り用にサーバーが発行するサービストークンの TTL(デフォルトは 300 秒) |
実行
cd src
uv run python main.py
Docker で実行
ホスト上ではなくコンテナー内でサーバー自体を実行するには、docker/ 内の Compose ファイルを使用します。
cp docker/.env.example docker/.env # then edit secrets and OIDC_ISSUER_URL
docker compose -f docker/docker-compose.yml up -d
これにより、docker/Dockerfile からイメージをビルドし、Qdrant とともに mcp-server コンテナーを起動します(:8000 で公開し、/health のヘルスチェックを実施します)。Keycloak は含まれていません。docker/.env の OIDC_ISSUER_URL には外部 OIDC プロバイダーを指定してください。各変数の説明は docker/.env.example に記載されています。
アクセスの初期設定
- Keycloak で、1 人のユーザーに
qdrant-adminレルムロールを割り当てます。 - サーバーを起動します。
- そのユーザーとして
grant_accessツールを呼び出し、他のロールに権限を付与します。
// MCP tool call
{
"tool": "grant_access",
"args": { "role": "data-scientist", "collection": "embeddings_v2", "access": "rw" }
}
data-scientistロールを持つ他のユーザーは、embeddings_v2にスコープされたrwJWT を取得できるようになります。
ツール
データツール
| ツール | 必要なアクセス権 |
|---|---|
list_collections | (任意)— 呼び出し元が読み取り可能なコレクションのみを返します |
get_collection_info | 対象コレクションに対する r |
search_collection | 対象コレクションに対する r |
search_collection_by_text | 対象コレクションに対する r |
scroll_collection | 対象コレクションに対する r |
list_documents | 対象コレクションに対する r |
upsert_points | 対象コレクションに対する rw |
delete_points | 対象コレクションに対する rw |
search_collection_by_text は自然言語のクエリを受け付け、設定された OpenAI 互換エンドポイントを使って初期設定時に記録されたモデルで埋め込みを生成し、そのベクトルで検索を実行します。
scroll_collection は、クエリを使わずに生のポイント(チャンク)を一覧表示します。返された next_offset カーソルを使ってコレクション内をページングします(最後のページでは null)。実際に保存されているポイントを調べる際に使用してください。
list_documents は、「コレクション X に含まれるドキュメントはどれか/いくつか」という質問に答えます。サーバー側で source ペイロードフィールドを使ってポイントを集計し(Qdrant のファセット機能)、ドキュメントごとのチャンク数とともに、重複のないエントリを 1 つずつ返します。1 つのドキュメントが多数のチャンクにまたがるため、一覧を確認する質問では scroll_collection よりこちらを優先してください。ファセット機能には source のキーワードペイロードインデックスが必要です。初期設定時に作成され、見つからない場合はサーバーも初回使用時に作成するため、手動での作業は不要です。
upsert_points と delete_points はシステムコレクション(_rbac_acl、_collection_meta)に対する操作を拒否します。代わりに管理ツールまたは初期設定を使用してください。
ACL 管理ツール(グローバルな管理権限が必要)
| ツール | 効果 |
|---|---|
list_acl(role?) | すべての許可を返します。必要に応じてロールで絞り込めます |
grant_access(role, collection, access) | 1 件の許可を冪等に追加または更新します |
revoke_access(role, collection) | 許可を削除します |
refresh_acl() | インメモリキャッシュを強制的に再読み込みします |
変更を加えるとローカルキャッシュは直ちに無効化されます。ACL コレクションへの範囲外の編集(Qdrant API を直接使用する場合など)は、最長でも
RBAC_ACL_CACHE_TTL 秒後に反映されます。
docker-compose でのローカル開発
ローカルでのサーバー開発用に Qdrant のみを起動するには、docker/docker-compose.yml の qdrant サービスを使用します。
Compose が QDRANT_JWT_SECRET を読み込めるように、src/.env を渡します。
docker compose --env-file src/.env -f docker/docker-compose.yml up -d qdrant
これにより、以下が起動します。
- JWT RBAC が有効で、
QDRANT_JWT_SECRETの署名キーを使用する Qdrant(:6333)
レルム、オーディエンスが mcp-server の機密クライアント、qdrant-admin ロール、およびアクセスを許可する予定のチームごとのロールを作成します。
テストユーザーにロールを割り当てます。
Qdrant JWT のセットアップ
qdrant-mcp-rbac は QDRANT_JWT_SECRET を使って JWT に署名します(HS256)。Qdrant が JWT を受け入れ、API キーと同じシークレットを使用するように設定する必要があります。
# qdrant config.yaml
service:
api_key: <same value as QDRANT_JWT_SECRET>
jwt_rbac: true
管理者以外のユーザーに対して生成される JWT の構造は次のとおりです。
{
"exp": 1735689600,
"access": [
{ "collection": "col_a", "access": "r" },
{ "collection": "col_b", "access": "rw" }
]
}
管理者の場合(または access: "m" の許可の場合)は次のとおりです。
{ "exp": 1735689600, "access": "m" }
MCP クライアントからの呼び出し
OIDC アクセストークンを Authorization ヘッダーに渡します。
TOKEN=$(curl -s -X POST \
-d "client_id=mcp-cli" -d "username=alice" -d "password=…" \
-d "grant_type=password" \
https://kc.example.com/realms/myrealm/protocol/openid-connect/token | jq -r .access_token)
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: text/event-stream" \
http://localhost:8000/mcp
Fidonis について
qdrant-mcp-rbac は Fidonis が開発・保守しています。オープンソース、オープンスタンダードを採用し、ベンダーロックインのない AI インフラストラクチャーを、企業がエンドツーエンドで運用できるよう支援しています。このプロジェクトでは、その全体像の一部として、ACL を設定ファイルに埋め込まずに、ベクトルストア内のデータを「誰が読めるか」を制御します。
同様のセルフホスト可能なスタックを構築していて、情報交換をご希望の場合は、fidonis.de にお越しください。
ライセンス
qdrant-mcp-rbac は MIT ライセンスのもとで公開されています — Copyright (c) 2026 Fidonis GmbH (in Gründung) and contributors.
- 商標に関する通知(「Fidonis」とプロジェクト名
qdrant-mcp-rbacを対象)については、TRADEMARK.mdをご覧ください。 - このプロジェクトに含まれるサードパーティ製 Python 依存関係のライセンスについては、
THIRD_PARTY_LICENSES.mdをご覧ください。 - Inbound = Outbound(MIT) ルールと、事前承認なしにコントリビューションへ追加できないライセンスカテゴリの一覧については、
CONTRIBUTING.mdをご覧ください。
「Qdrant」は Qdrant Solutions GmbH の登録商標です。このプロジェクトでの名称の使用は記述的なものであり、このサーバーが基盤としている上流コンポーネントを示すもので、推奨や支持を意味するものではありません。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。