{
  "id": "mcp",
  "title": "RedisVL MCP",
  "url": "https://redis.io/docs/latest/develop/ai/redisvl/0.23.0/concepts/mcp/",
  "summary": "",
  "content": "\n\nRedisVL includes an MCP server that exposes a Redis-backed retrieval surface through a small, deterministic tool contract. It is designed for AI applications that want to search or maintain data in one or more existing Redis indexes without each client reimplementing Redis query logic.\n\n## What RedisVL MCP Does\n\nThe RedisVL MCP server sits between an MCP client and Redis:\n\n1. It connects to one or more existing Redis Search indexes.\n2. It inspects each index at startup and reconstructs its schema.\n3. It initializes vector capabilities only when the configured search or upsert behavior needs them.\n4. It exposes stable MCP tools for discovery, search, and optionally upsert.\n\nThis keeps each Redis index as the source of truth for its search behavior while giving MCP clients a predictable interface.\n\n## How RedisVL MCP Runs\n\nRedisVL MCP works with a focused model:\n\n- One server process binds to one *or several* existing Redis indexes, each addressed by a logical id.\n- The server supports stdio (default), Streamable HTTP, and SSE transports.\n- Search behavior is owned by per-index configuration, not by MCP callers.\n- Vector search and server-side embedding are optional capabilities configured explicitly per index.\n- Upsert is optional and can be disabled globally with read-only mode or per index with a `read_only` flag.\n\nA single-index server remains the simplest deployment: when exactly one index is configured, callers can omit the index selector entirely and every tool call targets that index. Multi-index support is fully formal — it adds discovery and explicit routing without changing the single-index contract.\n\n## Config-Owned Search Behavior\n\nMCP callers can control:\n\n- `query`\n- `limit`\n- `offset`\n- `filter`\n- `return_fields`\n\nThese request-time controls are still bounded by runtime config. In particular,\ndeep paging is limited by a configured maximum result window, enforced as\n`offset + limit`.\n\nOn a multi-index server, callers also choose **which index to target** through an optional `index` argument (see [Index Selection]()). Callers do not choose:\n\n- whether retrieval is `vector`, `fulltext`, or `hybrid`\n- query tuning parameters such as hybrid fusion or vector runtime settings\n\nThat behavior lives in the per-index server config under `indexes.\u003cid\u003e.search`. The response includes `search_type` as informational metadata, but it is not a request parameter.\n\n## Single and Multiple Index Bindings\n\nThe YAML config uses an `indexes` mapping. Each entry is a logical binding keyed by an id (for example `knowledge` or `tickets`) that points to an existing Redis index through `redis_name`. The mapping may contain one entry or several; each binding is inspected, validated, and given its own search config, runtime limits, and optional vectorizer independently at startup. Startup is all-or-nothing — if any binding fails to initialize, the server does not start.\n\nA single-binding config is the simplest case and behaves exactly as before: the lone binding is the implicit target of every call. With multiple bindings the server stays a single process and endpoint, but callers select a binding per call.\n\n## Index Selection and Discovery\n\nOn a multi-index server, every tool call must say which logical index it targets:\n\n- `search-records` and `upsert-records` accept an optional `index` argument naming the logical id.\n- When exactly one index is configured, `index` may be omitted and resolves to that sole binding (backward compatible).\n- When multiple indexes are configured, omitting `index` is an `invalid_request`; the caller must name one.\n- An unknown logical id is an `invalid_request`.\n- Both tools echo the resolved `index` in their response so clients can confirm routing.\n\nBecause a client cannot guess the configured logical ids, multi-index servers expose a `list-indexes` discovery tool. **Clients should call `list-indexes` first** to enumerate the available indexes and their filterable fields, then pass the chosen id as `index` on subsequent calls.\n\n## Schema Inspection and Overrides\n\nRedisVL MCP is inspection-first:\n\n- the Redis index must already exist\n- the server reconstructs the schema from Redis metadata at startup\n- runtime field mappings remain explicit in config\n\nIn some environments, Redis metadata can be incomplete for vector field attributes. When that happens, `schema_overrides` can patch missing attrs for fields that were already discovered. It does not create new fields or change discovered field identity.\n\nStartup also validates that the inspected schema does not collide with\nMCP-reserved score metadata field names for the configured search mode.\n\n## Read-Only and Read-Write Modes\n\nRedisVL MCP always registers `search-records` and `list-indexes`.\n\nWrite availability is enforced at two levels:\n\n- **Global read-only mode** disables writes across every binding. It is controlled by the CLI flag `--read-only` or the environment variable `REDISVL_MCP_READ_ONLY=true`.\n- **Per-index read-only** disables writes for a single binding via `indexes.\u003cid\u003e.read_only: true`, while other bindings stay writable.\n\nThese combine into each binding’s *effective* write availability: a binding is read-only if global read-only is on **or** that binding sets `read_only: true`. The `upsert-records` tool is registered only when at least one binding is writable, so a fully read-only server does not advertise it at all. When the tool is registered, a write to a read-only binding is rejected with `forbidden` before any data is changed. `list-indexes` reports each binding’s effective write availability as `upsert_available`.\n\nUse read-only mode when Redis is serving approved content to assistants and another system owns ingestion — globally when no binding should accept writes, or per index when only some indexes are writable.\n\n## Authentication and Authorization\n\nThe HTTP transports can require a JWT bearer token issued by an existing identity provider. The server validates the token signature, issuer, and audience, and can gate read vs write by scope or role claim. This is coarse, per-tool authorization; it does not map token claims to Redis ACL users or per-tenant filters, which remain a gateway concern. The `stdio` transport is local and is never authenticated.\n\nFor configuration and the gateway boundary, see [Authenticate RedisVL MCP](https://redis.io/docs/latest/../user_guide/how_to_guides/mcp_authentication).\n\n## Tool Surface\n\nRedisVL MCP exposes up to three tools:\n\n- `list-indexes` enumerates the configured logical indexes for discovery (always available)\n- `search-records` searches a selected index using that index’s server-owned search mode\n- `upsert-records` validates and upserts records into a selected writable index, embedding them only when that capability is configured\n\nThese tools follow a stable contract:\n\n- request validation happens before query or write execution\n- the resolved logical `index` is echoed in every `search-records` and `upsert-records` response\n- filters support either raw strings or a RedisVL-backed JSON DSL\n- on a single-index server, `search-records` describes the inspected schema by advertising typed JSON DSL filter fields, object-filter `exists` support, and valid `return_fields`; on a multi-index server those hints are ambiguous, so the description instead directs clients to call `list-indexes` and pass `index`\n- error codes are mapped into a stable set of MCP-facing categories\n\n### `list-indexes`\n\n`list-indexes` returns one entry per configured binding so clients can route subsequent calls. Each entry reports:\n\n- the logical `id`\n- an optional `description` (only when configured)\n- `upsert_available`, reflecting the binding’s effective write availability\n- `fields`, the filterable fields discovered from the index\n- `limits`, only the runtime limits that were explicitly configured\n\nThe discovery payload is deliberately minimal:\n\n- the underlying Redis index name (`redis_name`) is **never** exposed\n- the vector field and the configured embed-source text field are **omitted** from `fields`, since they are implementation inputs rather than fields a client filters on\n- `limits` shows only explicitly set values (such as `max_limit` or `max_upsert_records`); defaults are not echoed\n\n## Why Use MCP Instead of Direct RedisVL Calls\n\nUse RedisVL MCP when you want a standard tool boundary for agent frameworks or assistants that already speak MCP.\n\nUse direct RedisVL client code when your application should own index lifecycle, search construction, data loading, or richer RedisVL features directly in Python.\n\nRedisVL MCP is a good fit when:\n\n- multiple assistants should share one approved retrieval surface\n- you want search behavior fixed by deployment config\n- you need a read-only or tightly controlled write boundary\n- you want to reuse an existing Redis index without rebuilding retrieval logic in every client\n\nFor setup steps, config, commands, and examples, see [Run RedisVL MCP](https://redis.io/docs/latest/../user_guide/how_to_guides/mcp).\n",
  "tags": [],
  "last_updated": "2026-07-27T11:50:23+02:00"
}
