Version current

MCP Server

RedisVL includes a built-in Model Context Protocol (MCP) server that exposes an existing Redis search index to AI agents and assistants through a small, stable tool contract: search-records and upsert-records.

The Model Context Protocol is an open standard that lets AI applications (Claude Desktop, IDE assistants, agent frameworks) call external tools in a uniform way. The RedisVL MCP server, built on the official MCP Go SDK, connects to one existing Redis index, embeds queries with a configured vectorizer, and lets any MCP client search — and optionally write to — that index.

Running the server

The server runs via the rvl CLI with a YAML configuration file:

rvl mcp --config mcp-config.yaml

By default it serves over stdio, which is what desktop MCP clients spawn. For remote clients, use the Streamable HTTP transport:

rvl mcp --config mcp-config.yaml --transport streamable-http --host 127.0.0.1 --port 8000

Pass --read-only when clients should only be able to search:

rvl mcp --config mcp-config.yaml --read-only

Configuration

A complete example (see examples/mcp-config.yaml in the repository):

server:
  redis_url: redis://localhost:6379

index:
  # Name of an existing Redis search index to expose
  redis_name: movies
  # Vectorizer used for query embedding and upsert embedding.
  # Classes: openai, azure_openai, cohere, mistral, voyageai, ollama.
  # API keys come from the standard environment variables.
  vectorizer:
    class: openai
    model: text-embedding-3-small

runtime:
  # Vector field of the index (required when a vectorizer is set)
  vector_field_name: embedding
  # Record field embedded during upsert
  default_embed_text_field: description
  # Optional: restrict fields returned by search-records
  # return_fields: [title, genre, rating]
  default_limit: 10
  max_limit: 100
  max_upsert_records: 64
  skip_embedding_if_present: true

# Disable the upsert tool entirely (can also pass --read-only)
read_only: false
Field Description Default

server.redis_url

Redis connection URL. Falls back to the REDIS_URL environment variable.

redis://localhost:6379

index.redis_name

Name of the existing index to expose (required).

index.vectorizer.class

Embedding provider: openai, azure_openai, cohere, mistral, voyageai, or ollama. Optional — without a vectorizer only text search is available.

index.vectorizer.model

Embedding model name.

provider default

index.vectorizer.dims

Skips the dimension probe when set.

auto-detected

runtime.text_field_name

Text field used for full-text search fallback (required when no vectorizer is configured).

runtime.vector_field_name

Vector field used for semantic search and upsert embedding (required when a vectorizer is configured).

runtime.default_embed_text_field

Record field embedded during upsert.

runtime.default_limit

Search result count when the client omits limit.

10

runtime.max_limit

Cap on the client-provided limit.

100

runtime.max_upsert_records

Cap on records per upsert call.

64

runtime.skip_embedding_if_present

Skip embedding records that already carry a vector field value.

true

runtime.return_fields

Restrict the fields returned by search.

all fields except the vector field

runtime.dtype

Vector datatype of the index field.

float32

read_only

Disable the upsert tool (equivalent to --read-only).

false

Tools

search-records

Runs a semantic search when a vectorizer is configured, otherwise a full-text search over runtime.text_field_name. Raw vector payloads are stripped from the returned records.

{
  "query": "space adventure with a robot sidekick",
  "limit": 5,
  "offset": 0,
  "filter": "@genre:{scifi}"
}

Only query is required. filter accepts a raw Redis query filter expression. The result is JSON with count and records.

upsert-records

Inserts or updates records in the index (disabled in read-only mode). When a vectorizer and default_embed_text_field are configured, text in that field is embedded automatically — unless the record already carries a vector value and skip_embedding_if_present is true.

{
  "records": [
    {"title": "Alien Robot Friends", "genre": "scifi",
     "description": "A space adventure with a robot sidekick"}
  ],
  "id_field": "title"
}

The result is JSON with upserted (count) and keys.

Transports and security

  • stdio (default) — the client spawns the server as a subprocess; nothing is exposed on the network. Never authenticated.

  • sse — the server listens on --host/--port using the HTTP+SSE transport (for clients on the older protocol revision).

  • streamable-http — the server listens on --host/--port for remote MCP clients.

Without configured authentication, binding an HTTP transport to a non-loopback host is refused unless you pass --allow-unauthenticated, and a warning is printed even then: any client that can reach the address has full access.

JWT authentication

The HTTP transports support JWT bearer authentication, configured in the server.auth block (or entirely via REDISVL_MCP_AUTH_* environment variables, which take precedence — useful for injecting settings without touching the YAML):

server:
  redis_url: redis://localhost:6379
  auth:
    type: jwt
    # exactly one of jwks_uri or public_key (PEM) is required
    jwks_uri: https://your-idp.example.com/.well-known/jwks.json
    issuer: https://your-idp.example.com/     # required
    audience: redisvl-mcp                     # required
    # algorithm: RS256                        # optional; restricts accepted alg
    required_scopes: [mcp:access]             # required on every token
    read_scope: search:read                   # gates search-records
    write_scope: search:write                 # gates upsert-records
    # authorization_claim: scp                # or e.g. "roles" for Azure AD

Tokens are validated for signature, issuer, audience, and the presence of required claims (defaults exp and iat, so tokens that would never expire are rejected). Symmetric algorithms (HS*) are not supported. Requests without a valid token receive 401 with a WWW-Authenticate challenge; tokens missing a configured read/write scope have the corresponding tool call rejected. With auth configured, non-loopback binds are permitted without --allow-unauthenticated.

Environment overrides: REDISVL_MCP_AUTH_TYPE, _JWKS_URI, _PUBLIC_KEY, _ISSUER, _AUDIENCE, _ALGORITHM, _REQUIRED_SCOPES (comma-separated), _REQUIRED_CLAIMS (comma-separated), _READ_SCOPE, _WRITE_SCOPE, _AUTHORIZATION_CLAIM, _BASE_URL. Setting REDISVL_MCP_AUTH_TYPE=none disables auth regardless of YAML.

Schema overrides

Older Redis versions omit some field attributes (notably vector dims and datatype) from FT.INFO. The index.schema_overrides block patches the inspected schema at startup:

index:
  redis_name: movies
  schema_overrides:
    fields:
      - name: embedding
        type: vector
        attrs:
          dims: 1536
          datatype: float32

Overrides can only patch attrs of fields that were discovered from FT.INFO — they cannot add fields or change a discovered field’s type or path. The configured runtime field mappings are validated against the effective (patched) schema at startup, so misconfigurations fail fast rather than at query time.

Using with Claude Desktop

For stdio MCP servers, add an entry to claude_desktop_config.json:

{
  "mcpServers": {
    "redisvl": {
      "command": "/usr/local/bin/rvl",
      "args": ["mcp", "--config", "/path/to/mcp-config.yaml", "--read-only"],
      "env": {
        "REDIS_URL": "redis://localhost:6379",
        "OPENAI_API_KEY": "sk-..."
      }
    }
  }
}

Restart Claude Desktop and the search-records tool (and upsert-records, unless read-only) appears in the tools list.

Start with --read-only and only enable upserts once you trust the client and have sized max_upsert_records appropriately.

See also