Authenticate RedisVL MCP
This guide explains how the RedisVL MCP server authenticates clients on its HTTP transports and how it gates read vs write access. It also draws the boundary between what RedisVL enforces and what belongs in a gateway or policy layer.
streamable-http, sse).
The stdio transport is a local subprocess with no network surface and is never
authenticated.What RedisVL Enforces
RedisVL validates a bearer JWT that an existing identity provider (IdP) issued. It does not run an OAuth authorization server and does not issue tokens.
On each request it checks:
- Signature, against a JWKS endpoint or a static public key.
- Issuer (
iss), so only tokens from your IdP are accepted. - Audience (
aud), so a token minted for a different service cannot be replayed against this server (RFC 8707). - Expiration: a present
expin the past is rejected, and tokens are required to carryexpandiat(configurable viarequired_claims), so a token with no expiration, which would never expire, is rejected. - Required scopes to connect, and (optionally) a read scope to call
search-recordsand a write scope to callupsert-records.
OAuth: Which Part RedisVL Handles
In OAuth terms, RedisVL MCP is a resource server: it validates access tokens that your identity provider issued. It does not run an OAuth login flow and does not issue tokens.
flowchart LR
Client -->|1 - OAuth login flow| IdP[Identity Provider]
IdP -->|2 - issues JWT access token| Client
Client -->|3 - Bearer JWT| MCP[RedisVL MCP]
MCP -->|validates token| Redis[(Redis)]
Steps 1 and 2 (obtaining the token) are handled by your client and IdP. RedisVL only performs the validation in step 3. As a result:
- It works with any OAuth 2.0 / OIDC provider (for example Auth0, Okta, Azure AD
/ Entra, Cognito, Keycloak): point
jwks_uriat the provider and validate its tokens. - RedisVL does not broker interactive "log in with…" flows and does not mint tokens.
You would only need more than token validation when you want the server itself
to drive an interactive browser login (an OAuth proxy), or to act as its own
authorization server that issues tokens. Both are out of scope today. If
interactive login is ever needed, a single generic oauth-proxy option can be
added behind the auth.type switch. For enterprise and agent deployments where
the caller already holds a token, JWT validation is sufficient.
Request Flow
sequenceDiagram
actor User
participant IdP as Identity Provider
participant MCP as RedisVL MCP Server
participant Redis
User->>IdP: Authenticate
IdP-->>User: Signed JWT (iss, aud, scopes/roles)
User->>MCP: MCP request + Bearer JWT
MCP->>MCP: Validate signature (JWKS / public key)
MCP->>MCP: Check issuer + audience
alt token invalid / wrong audience / missing connect scope
MCP-->>User: 401 Unauthorized
else token valid
MCP->>MCP: Gate tool by read / write scope
alt scope present
MCP->>Redis: Search or upsert (single configured ACL user)
Redis-->>MCP: Results
MCP-->>User: Tool result
else scope missing
MCP-->>User: Forbidden
end
end
Configure JWT Authentication
Add a server.auth block to your MCP config. Secrets can be injected with
${ENV} substitution.
server:
redis_url: ${REDIS_URL:-redis://localhost:6379}
auth:
type: jwt
jwks_uri: ${MCP_JWKS_URI} # or set public_key for a static key
issuer: ${MCP_ISSUER}
audience: api://redisvl-mcp
required_scopes: [kb.read] # required to connect
required_claims: [exp, iat] # claims every token must carry (default)
read_scope: kb.search.read # required for search-records
write_scope: kb.search.write # required for upsert-records
indexes:
knowledge:
redis_name: docs_index
search:
type: fulltext
runtime:
text_field_name: content
Every field is also settable through REDISVL_MCP_AUTH_* environment variables,
which take precedence over the YAML block.
Choosing the Authorization Claim
Different identity providers carry authorization in different claims. The
default JWT scope claim is scp (or scope). Some enterprise providers carry
authorization in a roles claim instead, which does not appear in the
standard scope set.
flowchart TD
A[Validated JWT claims] --> B{authorization_claim}
B -->|scp / scope| C["access.scopes<br/>e.g. kb.read"]
B -->|roles| D["access.claims.roles<br/>e.g. kb.search.read"]
C --> E[Check read_scope / write_scope]
D --> E
E -->|present| F[Allow tool]
E -->|absent| G[Deny tool]
Set the claim that holds your authorization values so read and write gating reads the right place:
server:
auth:
type: jwt
# ...
authorization_claim: roles # default: scp
read_scope: kb.search.read
write_scope: kb.search.write
A token like the following would then pass the read gate, because
kb.search.read is present in roles:
{
"iss": "https://your-idp.example/{tenant}/v2.0",
"aud": "api://redisvl-mcp",
"sub": "nitin",
"roles": ["kb.search.read"],
"scp": "kb.read"
}
The Authorization Boundary
RedisVL MCP authenticates the caller and gates read vs write. It does not translate token claims (such as a tenant id or role) into a specific Redis ACL user, a per-tenant index, or injected query filters. The server holds one Redis connection for one index, established at startup.
Fine-grained, per-tenant data isolation belongs in a gateway or policy layer in front of the MCP server, which validates the token, looks up a binding of claim to Redis identity, and injects credentials and filters.
flowchart LR
subgraph Gateway["Gateway / policy layer (out of scope for RedisVL)"]
T[Validate token] --> M["Map claims to<br/>Redis user + index + filters"]
end
subgraph RedisVL["RedisVL MCP (this guide)"]
A[Validate JWT] --> S[Gate read / write by scope]
end
Client --> Gateway --> RedisVL --> Redis[(Redis)]
Use RedisVL’s JWT validation for authentication and coarse read/write authorization. Layer a gateway on top when you need per-tenant Redis ACL enforcement.
See Also
- Run RedisVL MCP: run and configure the RedisVL MCP server.