Repository navigation
Release Notes
perf: run SemanticCache.check() as KNN with a client-side threshold (FDE-85) (#754)
SemanticCache.check() and acheck() run as a KNN query with the distance threshold applied client-side. Lookups with a selective filter expression take up to about a third less time, and unfiltered lookups take 2–3% longer.
feat(mcp): serve async Python functions as MCP tools (#758, #759, #760, #761)
The MCP server can now serve your own async Python functions as tools, called code tools. There are two ways to register one. If you run the server from your own process, decorate the function with server.code_tool(...). If you run rvl mcp, mark the function with @code_tool from redisvl.mcp, list its module under server.code_tool_modules and set server.allow_code_tools: true. That flag defaults to false because importing a module runs its code with the server's privileges, and each module must be importable by the server process.
A function qualifies only if it takes a ToolContext as its first positional parameter, annotates its other parameters and has a description, either its docstring or description=. The ToolContext is the tool's only route to Redis. Its index handle stays inside the bound index, applies the binding's concurrency cap and request timeout to every call, and offers writes, addressed by document id, only to tools declared with writes=True. Every call is checked against the read scope, the write scope for a writing tool and any per-tool scope= (when authentication is configured), and errors are mapped to the MCP error contract.
feat(router): add async aroute and aroute_many to SemanticRouter (#784)
SemanticRouter gains aroute(), aroute_many(), adisconnect() and an aindex property. The async index is created on first use. Routers given an SSL, unix-socket, Sentinel or RedisCluster client can't convert it to async; pass redis_url to the router for async routing in those setups. Vectorizers without a native aembed (e.g. the default HFTextVectorizer) still embed synchronously; the Redis call is non-blocking either way. The async client is bound to the event loop that made the first call; call adisconnect() before switching loops.
🚀 Enhancement
- perf: run SemanticCache.check() as KNN with a client-side threshold (FDE-85) #754 (@vishal-bala)
- feat(mcp): serve async Python functions as MCP tools, in four stacked PRs (@vishal-bala)
- feat(router): add async aroute and aroute_many to SemanticRouter #784 (@Yarmoluk)
📝 Documentation
- docs: tighten PR template guidance and make Notes opt-in #803 (@vishal-bala)
🧪 Tests
- test(query): compare KNN results by id in test_search_and_query #804 (@vishal-bala)
CI/CD Changes
- fix(ci): route Dependabot PRs through the maintainer-dispatched service tests #802 (@vishal-bala)
- ci(release): Ditch
conventional-commitsplugin from auto releases #798 (@vishal-bala)
Authors: 2
- Daniel Yarmoluk (@Yarmoluk)
- Vishal Bala (@vishal-bala)