Skip to content

feat(mcp): support multiple active index bindings (RAAE-1604)#629

Draft
vishal-bala wants to merge 1 commit into
feature/raae-1603-mcp-multi-indexfrom
feature/raae-1604-config-runtime-model
Draft

feat(mcp): support multiple active index bindings (RAAE-1604)#629
vishal-bala wants to merge 1 commit into
feature/raae-1603-mcp-multi-indexfrom
feature/raae-1604-config-runtime-model

Conversation

@vishal-bala

@vishal-bala vishal-bala commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Motivation

The RedisVL MCP server currently binds to exactly one Redis index per process. That single-binding assumption is enforced by a config validator and baked throughout the codebase — single-resource server state, single-binding convenience accessors on MCPConfig, and the search/upsert tools. Before the server can expose multiple logical indexes from a single endpoint (RAAE-1603), that assumption has to be removed and replaced with a real multi-binding model.

This PR (RAAE-1604) does exactly that, and nothing more: it reshapes the configuration and runtime model so the server can start, inspect, validate, and serve one or many bindings, while keeping existing single-index configs and callers behaving identically. It is the foundation the rest of the epic (discovery via list-indexes, index routing on search-records/upsert-records, docs) builds on, so it intentionally does not yet add any new request parameters or tools.

Implementation

The core of the change is a new immutable BindingRuntime (in redisvl/mcp/runtime.py) that bundles everything a tool call needs for one logical index: the binding config, the connected AsyncSearchIndex, its effective (inspected + overridden) schema, an optional vectorizer, the resolved native-hybrid-search capability, and the effective read-only flag. The server now holds a dict[str, BindingRuntime] keyed by logical id instead of a single set of _index/_vectorizer fields. Startup iterates every configured binding and inspects, validates, and initializes each one independently — each binding owns its own Redis client — with all-or-nothing teardown so a single bad binding fails startup cleanly without leaking connections.

On the config side, the "exactly one configured index binding" validator is gone (we now simply require at least one binding with non-blank ids), and the schema-inspection, runtime-mapping, and search-validation methods move from MCPConfig onto MCPIndexBindingConfig where they naturally belong per binding. The single-binding convenience accessors on MCPConfig are removed. Each binding gains optional description and read_only fields, and a binding's effective write availability is computed as global --read-only OR the per-index read_only. Tool resolution goes through a new server.resolve_binding(index_id) helper that defaults to the sole binding when one is configured (preserving backward compatibility) and returns an invalid_request error when an index is omitted with multiple bindings configured or when an unknown id is given. The search and upsert tools were re-threaded to operate on a resolved BindingRuntime rather than reaching into single-binding server accessors.

Additional notes:

  • Native-hybrid-search support is now probed eagerly per binding at startup and stored on the BindingRuntime, replacing the previous lazy single-index cache.
  • The concurrency semaphore is a single process-wide ceiling sized from the maximum max_concurrency across bindings; the request timeout is sourced per-binding and passed explicitly into run_guarded.
  • get_index() / get_vectorizer() are retained as thin convenience wrappers over resolve_binding(None).
  • Implemented test-first: new coverage for multi-binding config loading, description/read_only defaults, resolve_binding routing semantics, semaphore sizing, per-binding teardown, and three integration tests (multi-binding startup, global read-only override, and a single invalid binding failing startup), alongside the updated single-index tests that confirm backward compatibility.

Verification

  • mypy clean across all source files; black/isort formatted.
  • 182 MCP unit tests pass.
  • 44 MCP integration tests pass (2 skipped on Redis-version gates) against Redis 8.

🤖 Generated with Claude Code

Treat the MCP `indexes` config as a real multi-binding map instead of
enforcing exactly one logical binding. This is the foundation for
multi-index MCP support (RAAE-1603); single-index configs and callers
behave identically.

Config:
- Drop the "exactly one configured index binding" validator; require at
  least one binding with non-blank ids.
- Add per-binding `description` and `read_only` fields.
- Move schema inspection / runtime-mapping / search validation methods
  onto MCPIndexBindingConfig and remove the single-binding convenience
  accessors from MCPConfig.

Runtime/server:
- Introduce an immutable BindingRuntime bundling a binding's config,
  index, effective schema, vectorizer, native-hybrid support, and
  effective read-only flag.
- Replace single-resource server state with a dict of BindingRuntime;
  startup inspects/validates/initializes every binding independently
  (one client per binding) with all-or-nothing teardown.
- Resolve native-hybrid support eagerly per binding.
- Size the concurrency semaphore from the max binding limit and pass a
  per-binding request timeout into run_guarded.
- Add resolve_binding(index_id): defaults to the sole binding, raises
  invalid_request for omitted-on-multi and unknown ids.
- Compute effective_read_only as global read-only OR per-binding read_only.

Tools:
- Re-thread search/upsert onto a resolved BindingRuntime instead of
  single-binding server accessors.

Tests follow TDD: multi-binding config/startup coverage, resolve_binding
semantics, semaphore sizing, per-binding teardown, and backward-compatible
single-index behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jit-ci

jit-ci Bot commented Jun 16, 2026

Copy link
Copy Markdown

🛡️ Jit Security Scan Results

CRITICAL HIGH MEDIUM

✅ No security findings were detected in this PR


Security scan by Jit

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant