feat(mcp): support multiple active index bindings (RAAE-1604)#629
Draft
vishal-bala wants to merge 1 commit into
Draft
feat(mcp): support multiple active index bindings (RAAE-1604)#629vishal-bala wants to merge 1 commit into
vishal-bala wants to merge 1 commit into
Conversation
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 Security Scan Results✅ No security findings were detected in this PR
Security scan by Jit
|
This was referenced Jun 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 onsearch-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(inredisvl/mcp/runtime.py) that bundles everything a tool call needs for one logical index: the binding config, the connectedAsyncSearchIndex, its effective (inspected + overridden) schema, an optional vectorizer, the resolved native-hybrid-search capability, and the effective read-only flag. The server now holds adict[str, BindingRuntime]keyed by logical id instead of a single set of_index/_vectorizerfields. 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
MCPConfigontoMCPIndexBindingConfigwhere they naturally belong per binding. The single-binding convenience accessors onMCPConfigare removed. Each binding gains optionaldescriptionandread_onlyfields, and a binding's effective write availability is computed as global--read-onlyOR the per-indexread_only. Tool resolution goes through a newserver.resolve_binding(index_id)helper that defaults to the sole binding when one is configured (preserving backward compatibility) and returns aninvalid_requesterror 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 resolvedBindingRuntimerather than reaching into single-binding server accessors.Additional notes:
BindingRuntime, replacing the previous lazy single-index cache.max_concurrencyacross bindings; the request timeout is sourced per-binding and passed explicitly intorun_guarded.get_index()/get_vectorizer()are retained as thin convenience wrappers overresolve_binding(None).description/read_onlydefaults,resolve_bindingrouting 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
mypyclean across all source files;black/isortformatted.🤖 Generated with Claude Code