---
myst:
  html_meta:
    "description lang=en": |
      How to run the RedisVL MCP server, configure it, and use its search and upsert tools.
---

# Run RedisVL MCP

This guide shows how to run the RedisVL MCP server against an existing Redis index, configure its behavior, and use the MCP tools it exposes.

For the higher-level design, see {doc}`/concepts/mcp`.

## Before You Start

RedisVL MCP assumes all of the following are already true:

- you have Python 3.10 or newer
- you have Redis with Search capabilities available
- the Redis index already exists
- you know which text field and vector field the server should use
- you have installed the vectorizer provider dependencies your config needs

Install the MCP extra:

```bash
pip install redisvl[mcp]
```

If your vectorizer needs a provider extra, install that too:

```bash
pip install redisvl[mcp,openai]
```

## Start the Server

Run the server over stdio (default):

```bash
uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml
```

Run it over Streamable HTTP for remote MCP clients:

```bash
uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml --transport streamable-http --host 0.0.0.0 --port 8000
```

Run it over SSE:

```bash
uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml --transport sse --host 0.0.0.0 --port 9000
```

```{warning}
Streamable HTTP and SSE endpoints are **unauthenticated by default**. Only bind to public interfaces (`--host 0.0.0.0`) on trusted networks or behind an authenticating reverse proxy. When not using `--read-only`, the `upsert-records` tool is also exposed to any client that can reach the server.
```

Run it in read-only mode to expose search without upsert:

```bash
uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml --read-only
```

### CLI Flags

| Flag | Default | Purpose |
|------|---------|---------|
| `--config` | — | Path to the MCP YAML config (required) |
| `--transport` | `stdio` | Transport protocol: `stdio`, `sse`, or `streamable-http` |
| `--host` | `127.0.0.1` | Bind address (only used with `sse` and `streamable-http`) |
| `--port` | `8000` | Bind port (only used with `sse` and `streamable-http`) |
| `--read-only` | off | Disable the `upsert-records` tool |

### Environment Variables

You can also control boot settings through environment variables:

| Variable | Purpose |
|----------|---------|
| `REDISVL_MCP_CONFIG` | Path to the MCP YAML config |
| `REDISVL_MCP_READ_ONLY` | Disable `upsert-records` when set to `true` |
| `REDISVL_MCP_TOOL_SEARCH_DESCRIPTION` | Set the base search tool description text; RedisVL still appends schema-derived typed filter, `exists`, and `return_fields` hints |
| `REDISVL_MCP_TOOL_UPSERT_DESCRIPTION` | Override the upsert tool description |

## Connect a Remote MCP Client

When using Streamable HTTP or SSE transport, point your MCP client at the server URL:

- **Streamable HTTP**: `http://<host>:<port>/mcp`
- **SSE**: `http://<host>:<port>/sse`

> **Note:** `<host>` here is the bind address the server was started with. The default `127.0.0.1` only accepts connections from the same machine. To allow connections from other machines, start the server with `--host 0.0.0.0` and use the machine's actual IP or hostname in the client URL.

For example, to configure a remote MCP client to connect to a Streamable HTTP server running on `192.168.1.10:8000`:

```json
{
  "mcpServers": {
    "redisvl": {
      "url": "http://192.168.1.10:8000/mcp",
      "transport": "streamable-http"
    }
  }
}
```

## Example Config

This example binds one logical MCP server to one existing Redis index called `knowledge`.

The config uses `${REDIS_URL}` and `${OPENAI_API_KEY}` as environment-variable placeholders. These values are resolved when the server starts. You can also use `${VAR:-default}` to provide a fallback value.

```yaml
server:
  redis_url: ${REDIS_URL}

indexes:
  knowledge:
    redis_name: knowledge

    vectorizer:
      class: OpenAITextVectorizer
      model: text-embedding-3-small
      api_config:
        api_key: ${OPENAI_API_KEY}

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

    search:
      type: hybrid
      params:
        text_scorer: BM25STD
        stopwords: english
        vector_search_method: KNN
        combination_method: LINEAR
        linear_text_weight: 0.3

    runtime:
      text_field_name: content
      vector_field_name: embedding
      default_embed_text_field: content
      default_limit: 10
      max_limit: 25
      max_result_window: 1000
      max_upsert_records: 64
      skip_embedding_if_present: true
      startup_timeout_seconds: 30
      request_timeout_seconds: 60
      max_concurrency: 16
```

### What This Config Means

- `redis_name` must point to an index that already exists in Redis
- `search.type` fixes retrieval behavior for every MCP caller
- `runtime.text_field_name` is required for `fulltext` and `hybrid` search
- `runtime.vector_field_name` is required for `vector` and `hybrid` search, and optional for plain full-text deployments
- `runtime.default_embed_text_field` is only required when the server should generate embeddings during upsert
- `vectorizer` is required for query embedding and server-side embedding, but optional for fulltext-only configs
- `runtime.max_result_window` caps deep paging by limiting the maximum `offset + limit`
- `schema_overrides` is only for patching incomplete field attrs discovered from Redis

### Fulltext-Only Config

For a non-vector deployment, omit vector-only settings entirely:

```yaml
server:
  redis_url: ${REDIS_URL}

indexes:
  knowledge:
    redis_name: knowledge

    search:
      type: fulltext
      params:
        text_scorer: BM25STD
        stopwords: english

    runtime:
      text_field_name: content
      default_limit: 10
      max_limit: 25
      max_result_window: 1000
      max_upsert_records: 64
      skip_embedding_if_present: true
      startup_timeout_seconds: 30
      request_timeout_seconds: 60
      max_concurrency: 16
```

## Tool Contracts

RedisVL MCP exposes a small, implementation-owned contract.

### `search-records`

Arguments:

- `query`
- `limit`
- `offset`
- `filter`
- `return_fields`

Example request payload:

```json
{
  "query": "incident response runbook",
  "limit": 2,
  "offset": 0,
  "filter": {
    "and": [
      { "field": "category", "op": "eq", "value": "operations" },
      { "field": "rating", "op": "gte", "value": 4 }
    ]
  },
  "return_fields": ["title", "content", "category", "rating"]
}
```

Example response payload:

```json
{
  "search_type": "hybrid",
  "offset": 0,
  "limit": 2,
  "results": [
    {
      "id": "knowledge:runbook:eu-failover",
      "score": 0.82,
      "score_type": "hybrid_score",
      "record": {
        "title": "EU failover runbook",
        "content": "Restore traffic after a regional failover.",
        "category": "operations",
        "rating": 5
      }
    }
  ]
}
```

Notes:

- `search_type` is response metadata, not a request argument
- when `return_fields` is omitted, RedisVL MCP returns all non-vector fields
- returning the configured vector field is rejected
- `filter` accepts either a raw string or a JSON DSL object
- the `search-records` tool description includes schema-derived hints for typed JSON DSL filter fields, object-filter `exists` support, and valid `return_fields`
- `offset + limit` must stay within `runtime.max_result_window`
- startup rejects schemas that use MCP-reserved score metadata field names:
  `id`, `__key`, `key`, `score`, `vector_distance`, `__score`, `text_score`, `vector_similarity`, `hybrid_score`

### `upsert-records`

Arguments:

- `records`
- `id_field`
- `skip_embedding_if_present`

Example request payload:

```json
{
  "records": [
    {
      "doc_id": "doc-42",
      "content": "Updated operational guidance for failover handling.",
      "category": "operations",
      "rating": 5
    }
  ],
  "id_field": "doc_id"
}
```

Example response payload:

```json
{
  "status": "success",
  "keys_upserted": 1,
  "keys": ["knowledge:doc-42"]
}
```

Notes:

- this tool is not registered in read-only mode
- when server-side embedding is configured, records that need embedding must contain `runtime.default_embed_text_field`
- when `skip_embedding_if_present` is `true`, records that already contain the configured vector field can skip re-embedding
- when a vector field is configured but server-side embedding is disabled, callers must supply vectors explicitly

## Search Examples

### Read-Only Vector Search

Use read-only mode when assistants should only retrieve data:

```bash
uvx --from redisvl[mcp] rvl mcp --config /path/to/mcp.yaml --read-only
```

With a `search.type` of `vector`, callers send only the query, filters, pagination, and field projection:

```json
{
  "query": "cache invalidation incident",
  "limit": 3,
  "return_fields": ["title", "content", "category"]
}
```

### Raw String Filter

Pass a raw Redis filter string through unchanged:

```json
{
  "query": "science",
  "filter": "@category:{science}",
  "return_fields": ["content", "category"]
}
```

### JSON DSL Filter

The DSL supports logical operators and type-checked field operators:

```json
{
  "query": "science",
  "filter": {
    "and": [
      { "field": "category", "op": "eq", "value": "science" },
      { "field": "rating", "op": "gte", "value": 4 }
    ]
  },
  "return_fields": ["content", "category", "rating"]
}
```

### Pagination and Field Projection

```json
{
  "query": "science",
  "limit": 1,
  "offset": 1,
  "return_fields": ["content", "category"]
}
```

### Hybrid Search With `schema_overrides`

Use `schema_overrides` when Redis inspection cannot recover complete vector attrs, then keep hybrid behavior in config:

```yaml
schema_overrides:
  fields:
    - name: embedding
      type: vector
      attrs:
        algorithm: flat
        dims: 1536
        datatype: float32
        distance_metric: cosine

search:
  type: hybrid
  params:
    text_scorer: BM25STD
    stopwords: english
    vector_search_method: KNN
    combination_method: LINEAR
    linear_text_weight: 0.3
```

The MCP caller still sends the same request shape:

```json
{
  "query": "legacy cache invalidation flow",
  "filter": { "field": "category", "op": "eq", "value": "release-notes" },
  "return_fields": ["title", "content", "release_version"]
}
```

## Upsert Examples

### Auto-Embed New Records

If a record does not include the configured vector field, RedisVL MCP embeds `runtime.default_embed_text_field` and writes the result:

```json
{
  "records": [
    {
      "content": "First upserted document",
      "category": "science",
      "rating": 5
    },
    {
      "content": "Second upserted document",
      "category": "health",
      "rating": 4
    }
  ]
}
```

### Update Existing Records With `id_field`

```json
{
  "records": [
    {
      "doc_id": "doc-1",
      "content": "Updated content",
      "category": "engineering",
      "rating": 5
    }
  ],
  "id_field": "doc_id"
}
```

### Control Re-Embedding With `skip_embedding_if_present`

```json
{
  "records": [
    {
      "doc_id": "doc-2",
      "content": "Existing content",
      "category": "science",
      "rating": 4
    }
  ],
  "id_field": "doc_id",
  "skip_embedding_if_present": false
}
```

Set `skip_embedding_if_present` to `false` when you want the server to regenerate embeddings during upsert. In most cases, the caller should omit the vector field and let the server manage embeddings from `runtime.default_embed_text_field`.

### Plain Writes Without Embedding

For fulltext-only indexes, `upsert-records` can write records without any vectorizer or vector field configuration:

```json
{
  "records": [
    {
      "content": "Updated FAQ entry",
      "category": "support",
      "rating": 5
    }
  ]
}
```

If you configure a vector field but omit server-side embedding support, the caller must send vectors in each record instead of relying on the server to generate them.

## Troubleshooting

### Missing MCP Dependencies

If `rvl mcp` reports missing optional dependencies, install the MCP extra:

```bash
pip install redisvl[mcp]
```

If the configured vectorizer needs a provider SDK, install that provider extra too. Fulltext-only configs can omit the vectorizer entirely.

### Configured Redis Index Does Not Exist

The server only binds to an existing index. Create the index first, then point `indexes.<id>.redis_name` at that index name.

### Missing Required Environment Variables

YAML values support `${VAR}` and `${VAR:-default}` substitution. Missing required variables fail startup before the server registers tools.

### Vectorizer Dimension Mismatch

If the vectorizer dims do not match the configured vector field dims, startup fails. Make sure the embedding model and the effective vector field dimensions are aligned.

### Hybrid Config Requires Native Runtime Support

Some hybrid params depend on native hybrid support in Redis and redis-py. If your environment does not support that path, remove native-only params such as `knn_ef_runtime` or upgrade Redis and redis-py.
