
Qdrant RBAC
de.fidonis由 fidonis.de 發布v0.4.0更新於 Oct 24, 2026
Role-based access to your Qdrant vector store, via MCP. OIDC in, scoped JWTs out.
概覽
一個自架的 MCP 伺服器,在 Qdrant 向量資料庫前提供 OIDC 登入與角色型存取控制,並為每位使用者簽發限定範圍的 Qdrant JWT。
- 可以做什麼
- 列出、檢視並搜尋集合,包含透過 OpenAI 相容端點將查詢向量化的自然語言搜尋,以及分頁瀏覽原始資料點與依來源檔案統計區塊數量的文件清單。在呼叫者有寫入權限的集合中寫入與刪除資料點。管理授權:依角色列出授權、授予或撤銷某角色對某集合的存取、強制重新載入 ACL 快取。透過選用文件策略在每次讀取時轉為 Qdrant 載荷篩選器,實現文件層級可見性控制。
- 適用情境
- 當多個使用者或團隊共用一個 Qdrant 執行個體,希望每位使用者只能檢視與修改其身分提供者角色所允許的集合與文件,而不想把存取規則寫死在設定檔時使用。適合已運行 Keycloak 等 OIDC 提供者、並啟用 JWT RBAC 的 Qdrant 的自架環境。
- 執行需求
- 需要可連線的 OIDC 提供者(如 Keycloak),並設定用戶端受眾與角色;需要可連線且已啟用 JWT RBAC 的 Qdrant 執行個體。必要環境變數:OIDC_ISSUER_URL、OIDC_AUDIENCE、QDRANT_URL,以及密鑰 QDRANT_JWT_SECRET,其值必須與 Qdrant 的服務 API 金鑰一致。以容器或 Python 3.11+ 搭配 uv 執行;用戶端透過 streamable HTTP 連線並攜帶 OIDC 持有者權杖。文字搜尋另需一個向量化端點。
安裝
在 SourceWeft 中
- 開啟 儀表板中的 Qdrant RBAC,將其新增到工作區。
- 為需要使用其工具的對話啟用該服務。
Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。
其他 MCP 客戶端
參照 儲存庫 中的啟動說明。
README
qdrant-mcp-rbac
[License: MIT] Maintained by Fidonis · See TRADEMARK.md for the trademark notice.
[Same MCP tool call, two identities, two different result sets — server-side enforcement.]
A FastMCP server that exposes a Qdrant vector database over streamable HTTP with OIDC-based authentication (Keycloak-compatible) and role-based access control mapped onto Qdrant's native JWT access rules.
Role grants are stored as data inside a dedicated Qdrant collection, not in configuration files — admins manage them at runtime via MCP tools.
Quick start (Docker)
/mcp is the streamable-HTTP endpoint, /health is unauthenticated.
See the full docker/.env.example for every variable.
How it works
- The MCP client sends a request with an OIDC access token (
Authorization: Bearer …). - The
OIDCAuthMiddlewarevalidates the token against the OIDC provider's JWKS (signature, expiry, audience, issuer; see Token validation). - The
JWTBuilderderives a Qdrant JWT for the user:- If the user is in the break-glass admin role (
RBAC_ADMIN_ROLE), a global-manage token is minted directly. - Otherwise, the
AclResolverreturns the cachedrole -> [grants]mapping built from the ACL collection (RBAC_ACL_COLLECTION, default_rbac_acl).
- If the user is in the break-glass admin role (
- The Qdrant JWT is attached to the request scope.
- Each MCP tool checks the user's effective access for the targeted collection and forwards the request to Qdrant using the derived JWT.
Token validation
Tokens are validated with PyJWT against the keys
published by the issuer ({OIDC_ISSUER_URL}/.well-known/openid-configuration,
then its jwks_uri):
- Signature algorithm comes from the signing key (its
alg, orRS256/ES256for an RSA / EC key without one), never from the token header. OnlyRS256,RS384,RS512,ES256,ES384andES512are accepted; symmetric (HS*) andnonetokens are rejected. - Required claims:
exp,aud(must containOIDC_AUDIENCE),iss(must equal the issuer in the discovery document) and a non-emptysub. A token withoutexporaudis rejected.nbfis enforced when present. iatis not checked, so clock skew between this host and the identity provider does not reject freshly issued tokens.- Key rotation: keys are cached for
OIDC_JWKS_CACHE_TTLseconds. A token with an unknownkidtriggers exactly one JWKS refresh before it is rejected. - Roles are read from
realm_access.rolesand from everyresource_access.<client>.roles.
ACL collection schema
Each grant is one point in _rbac_acl. The point id is a deterministic
uuid5(role, collection), so re-granting is idempotent. Payload:
access is one of:
r– read-onlyrw– read & writem– manage (global;collectionis informational, convention:"*")
The collection is auto-created on first read by the server using a self-minted service token.
Document-level access control (doc_policy)
Collection-level access decides whether a role can read a collection at all.
A grant may additionally carry an optional doc_policy that restricts which
individual documents (points) inside that collection the role can see. The
server translates the policy into a Qdrant payload filter and injects it as a
must clause on every read — search_collection,
search_collection_by_text, scroll_collection, and list_documents
(faceting only over documents the role may see). Caller-supplied query_filter
values are preserved; the doc filter is added alongside, so both apply together.
Grants without doc_policy behave exactly as before — every document in the
collection is visible.
Schema
default: "allow"— every document is visible by default;denyconditions hide matching ones.default: "deny"— no documents are visible by default;allowconditions expose matching ones.values: ["*"]— wildcard meaning "all documents". On anallowcondition (default-deny) it cancels the filter, exposing the whole collection; on adenycondition (default-allow) it returns an empty result set.default: "deny"with noallowconditions → empty result set.
Multiple conditions of the same mode are unioned (a document is hidden by any deny condition; a document is exposed by any allow condition).
Multi-role merge
When a user holds several grants on the same collection (via different roles), the server merges their policies using the most-permissive rule:
- If any matching grant has no
doc_policy, the user is unrestricted on that collection. - If any matching grant has
default: "allow", the merged policy isdefault: "allow"with the union of those grants' deny conditions (default-deny grants are ignored). - Otherwise all grants are
default: "deny"and their allow conditions are unioned.
Recommended payload field: acl_tags
A new optional acl_tags: list[str] field is reserved for documents that
need access control. It is never required — documents without acl_tags
remain fully functional:
- In default-allow grants, an untagged document is never matched by a
denycondition onacl_tags→ always visible. - In default-deny grants, an untagged document never satisfies an
allowcondition onacl_tags→ implicitly hidden.
The bootstrap script demo/bootstrap/vectorize.py reads an optional
demo/data/acl_tags.yml sidecar mapping each Markdown source (POSIX path
relative to the data directory) to a list of tags applied to every chunk
ingested from that file.
Examples
Default-allow, deny confidential documents:
Default-deny, allow only public documents:
Filtering on the source field (no acl_tags required):
grant_access accepts the same shape:
Layout
The repo separates the production server (src/) from demo material (demo/).
src/ and each project under demo/ is its own uv project with its own
pyproject.toml and .venv — there is no virtual environment in the
repo root.
Setup
Prerequisites
- Python 3.11+
- uv installed
- A reachable OIDC provider (e.g. Keycloak)
- A reachable Qdrant instance with JWT RBAC enabled
Install
This creates src/.venv with all dependencies. The repo root has no
.venv of its own.
Configure
Copy the example env file and adjust values:
The server reads src/.env (resolved relative to config.py), so keep
it inside src/ regardless of the working directory you run from.
Important variables:
Run
Run with Docker
To run the server itself in a container instead of on the host, use the
compose file in docker/:
This builds the image from docker/Dockerfile and starts the mcp-server
container (published on :8000, with a /health health-check) alongside
Qdrant. Keycloak is not included — point OIDC_ISSUER_URL in docker/.env
at an external OIDC provider. Every variable is documented in
docker/.env.example.
Bootstrapping access
- Assign one user the
qdrant-adminrealm role in Keycloak. - Start the server.
- As that user, call the
grant_accesstool to provision other roles:
- Other users with the
data-scientistrole now get anrwJWT scoped toembeddings_v2.
Tools
Data tools
search_collection_by_text accepts a natural-language query, embeds it via
the configured OpenAI-compatible endpoint using the model recorded at
bootstrap time, and runs the resulting vector search.
scroll_collection lists raw points (chunks) without a query. It pages
through the collection via the next_offset cursor it returns (null on the
last page). Use it to inspect the actual stored points.
list_documents answers "which / how many documents are in collection X?". It
aggregates points by their source payload field server-side (Qdrant faceting)
and returns one deduplicated entry per document with its chunk count —
prefer it over scroll_collection for inventory questions, since a single
document spans many chunks. Faceting requires a keyword payload index on
source; the bootstrap creates it, and the server also creates it on first use
if it is missing, so no manual step is needed.
upsert_points and delete_points refuse to operate on system collections
(_rbac_acl, _collection_meta) — use the admin tools or bootstrap instead.
ACL admin tools (require global manage)
Mutations invalidate the local cache immediately; out-of-band edits to the ACL
collection (e.g. via the Qdrant API directly) become visible after at most
RBAC_ACL_CACHE_TTL seconds.
Local development with docker-compose
To start only Qdrant for local server development, use the qdrant service
from docker/docker-compose.yml. Pass src/.env so that Compose can read
QDRANT_JWT_SECRET:
This starts:
- Qdrant on
:6333(with JWT RBAC enabled, signing key fromQDRANT_JWT_SECRET)
Create a realm, a confidential client with audience mcp-server, the
qdrant-admin role, and any per-team roles you plan to grant access to.
Assign roles to test users.
Qdrant JWT setup
qdrant-mcp-rbac signs JWTs with QDRANT_JWT_SECRET (HS256). Qdrant must be
configured to accept JWTs and use the same secret as its API key:
The derived JWT structure for a non-admin user is:
For admins (or any grant with access: "m"):
Calling from an MCP client
Pass the OIDC access token in the Authorization header:
About Fidonis
qdrant-mcp-rbac is built and maintained by Fidonis. We help companies run their own AI infrastructure end to end — open source, open standards, no vendor lock-in. This project covers one specific piece of the picture: enforce who can read what in your vector store without baking ACLs into config files.
If you are building a similar self-hosted stack and want to talk shop, drop by at fidonis.de.
License
qdrant-mcp-rbac is released under the MIT license — Copyright (c) 2026 Fidonis GmbH (in Gründung) and contributors.
- See
TRADEMARK.mdfor the trademark notice covering the name "Fidonis" and the project nameqdrant-mcp-rbac. - See
THIRD_PARTY_LICENSES.mdfor the licenses of the third-party Python dependencies bundled with this project. - See
CONTRIBUTING.mdfor the Inbound = Outbound (MIT) rule and the list of license categories that contributions may not introduce without prior approval.
"Qdrant" is a registered trademark of Qdrant Solutions GmbH. Use of the name in this project is nominative — it describes the upstream component the server is built on and does not imply endorsement.
來源:README.md,提交 dde806d
工具
0版本歷史
1- v0.4.0最新Oct 11, 2026
評分
已滿足適用檢查項的 50%
該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。
- 近 30 天內可連通25 分
請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。
- 已宣告認證方式10 分
請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。
- 已列出工具15 分
請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。
- 描述必需15 分
- 圖示10 分
- 文件15 分
- 授權條款10 分