Experimental database library pilot¶
The opt-in backend proves composition with redis-mcp without replacing
redisctl's production database backend. Legacy remains the build/runtime default.
This is development work for #1172, not a production migration or release promise.
The fixed-target contract was accepted for this opt-in pilot in #1175. This does not approve source access, publication or a default backend change.
Try it¶
The redis-developer/redis-database-mcp-rs repository is public. The manifest pins revision
8eff8240679d38157ee08b9d5e5eaeb656ce7550; it does not require a crates.io
publication or GitHub release to develop or review the pilot. Anonymous Git
checkout and uncached Cargo resolution have been verified for both default
features and database-mcp-pilot; no dependency-access credentials are required.
cargo build -p redisctl-mcp --features database-mcp-pilot
./target/debug/redisctl-mcp --database-backend redis-mcp --tools database,app \
--database-url redis://127.0.0.1:6379
# Alternatively: explicitly select exactly one database profile.
./target/debug/redisctl-mcp --database-backend redis-mcp --tools database,app \
--profile local-db
--database-url overrides REDIS_URL; explicit --profile selection overrides
REDISCTL_PROFILE. A URL and selected database profile conflict. Repeating the
same profile is harmless, but multiple distinct database profiles, an unknown
selected profile, or no explicit database target fail startup. Config defaults,
the first configured profile, and localhost are never fallback targets. Cloud
and Enterprise profiles may coexist and retain their existing routing.
URLs are validated offline by the library; connections are lazy and reused.
--cluster selects Cluster at startup and rejects nonzero database indexes.
TLS verification is preserved; unsupported URL options are rejected rather
than silently ignored. The embedding builder accepts explicit inputs only—it
does not read CLI environment variables as hidden target sources.
Only bare database selection is supported for the pilot. Legacy database
submodule selectors are rejected. The library owns the read-only Keyspace and
Strings catalog; no legacy database handlers are mixed into it. Host policy,
tool visibility, audit middleware, non-database tools, and transport remain
redisctl-owned. Global/per-toolset allow rules cannot add writes or raw commands.
Known limitations and release gates¶
- Cluster keyspace-wide calls are guarded. The underlying transport's SCAN
is node-local/default-primary; it cannot guarantee full-cluster enumeration.
The pilot supplies the known deployment to the library, which rejects SCAN,
DBSIZE, and RANDOMKEY on Cluster with
DEPLOYMENT_UNAVAILABLE. These tools remain listed with stable errors; no single-node result claims full-cluster completeness. Cluster-wide keyspace semantics are a default-cutover gate. - Reconnect/Cluster-safe
CLIENT SETNAMEis not implemented by this pilot. An explicit warning is emitted; production users needing naming stay on legacy. Upstream replay support in redis-tower #736 is complete; adoption through the library's setup PR #128 remains outside this immutable pilot pin and is a default-cutover gate. - Deterministic library shutdown/lifecycle remains a cutover gate tracked in redis-database-mcp-rs #125. The pilot does not introduce session families.
- The immutable Git dependency is intentional for coordinated development. Before publishing redisctl crates to crates.io, switch to a real registry dependency and rerun clean-room packaging. Library publication is not required to review or build this pilot. Binary release packaging and supported distribution remain separate gates; this branch is not a release approval.
- Embedded skills/resources and prompts remain available. The
redisctl://skillsindex reports the tools actually exposed by this backend after host policy and visibility filtering. Read it before using a workflow: many workflows reference tools absent from the pilot. Setup guidance must not enable writes or silently switch backends to satisfy those workflows.
Rollback is --database-backend legacy (or omit the selector); disabling the
Cargo feature removes the experimental runtime backend, not Cargo's need to
resolve its optional Git dependency. Selecting an uncompiled pilot fails
explicitly rather than silently falling back.
Public source resolution and release gates¶
The repository owner made the library public on October 6, 2026. The former internal-source access blocker is resolved: the exact pin can be fetched with an empty Cargo home, credential helpers disabled, no global/system Git config, and no inherited authentication environment. Public CI must still pass its current-head compilation and test gates; public visibility is not test evidence.
Source resolution matters in all affected entry points:
| Workflow | Cargo-resolving entry points |
|---|---|
ci.yml |
Clippy/core checks, package unit tests, integration/live tests, platform builds/tests, coverage |
docs.yml |
Default embedding API rustdoc and all-feature workspace rustdoc |
cargo-deny.yml |
Both advisory and bans/licenses/sources dependency-graph checks |
release.yml |
dist plan metadata, local/global builds and any host step resolving workspace metadata |
release-plz.yml |
Workspace/version resolution and eventual package/publish validation |
security.yml runs cargo audit against the lockfile; that is distinct from
the Cargo-resolving graph checks above. A cache hit is not proof of clean fetch
access. Default-feature Cargo resolution still fetches the optional Git source
even though it does not activate the pilot backend. Anonymous resolution and
current-head CI are distinct evidence; neither proves registry packaging or
authorizes a release or default cutover. Publishing and release workflow host
steps remain separately gated and are not run merely to validate this pilot.
Catalog and result differences¶
The legacy baseline mcp-catalog-v1.json is untouched. The separate
mcp-pilot-catalog-v1.json freezes all 16 pilot tool names, input/output schemas,
and safety annotations. All pilot tools omit per-call url/profile, declare
read-only/non-destructive annotations, and return JSON structuredContent
plus its text representation instead of legacy human-readable text.
The table records every pilot tool. Input fields not mentioned retain their
names; the full snapshots capture validation, defaults, and required fields.
key_encoding selects UTF-8 (default) or base64 where supported.
| Tool | Additional input difference | Structured result fields |
|---|---|---|
redis_dbsize |
None | key_count |
redis_digest |
New tool: key, key_encoding |
exists, digest, algorithm |
redis_dump |
Adds key_encoding, bounded max_bytes |
key, key_encoding, exists, payload_base64, payload_bytes |
redis_exists |
Bounded keys |
requested, existing, all_exist |
redis_get |
Adds key_encoding |
key, key_encoding, exists, value, encoding |
redis_getrange |
Adds key_encoding |
key, key_encoding, start, end, bytes, value, encoding |
redis_memory_usage |
None | key, exists, bytes |
redis_mget |
Bounded keys |
values, count |
redis_object_inspect |
New tool: key, key_encoding, operation |
key, key_encoding, operation, exists, encoding, value |
redis_ping |
None | response, latency_ms |
redis_randomkey |
None | key, encoding |
redis_scan |
Removes whole-scan limit; adds page cursor, bounded count |
cursor, keys, count, page |
redis_sort |
New read-only tool: key, key_encoding, by, get, offset, count, order, alpha |
key, key_encoding, source_exists, offset, requested_count, get_pattern_count, returned, values |
redis_strlen |
None | key, length_bytes |
redis_ttl |
Adds key_encoding |
key, key_encoding, ttl_seconds, exists, persistent |
redis_type |
Adds key_encoding |
key, key_encoding, key_type |
GET preserves binary values with base64 and missing values with exists: false
and null fields. SCAN returns one bounded page rather than aggregating an
entire scan. Output budgets may reject a response; they do not return an
apparently complete truncated result. Tools outside this table are absent,
including all writes, destructive operations, and raw commands.
Unknown input fields are rejected: per-call targets cannot silently rebind the process. Callers must use the declared JSON types; legacy numeric-string coercion is not a compatibility promise for library inputs. The public builder can set pilot deadlines and encoded-output/entry budgets; their defaults remain library-owned (30 seconds, 256 KiB, and 1,000 collection entries).
Validation and ownership¶
tests/database_pilot.rs covers catalog isolation, default rollback, host
policy/visibility, disabled toolsets, missing target/invalid limits, bounded
first connection failures, and credential-safe errors. Resolver unit tests
cover explicit profiles, deduplication, unknown/ambiguous/conflicting sources,
management-profile coexistence, offline URL validation, and Cluster DB rules.
Live tests own disposable standalone and three-primary Cluster processes: legacy GET comparison, binary/missing values, TYPE/TTL/SCAN, authentication, client reuse, output budgets, deadlines/cancellation recovery, Cluster slot routing and the keyspace deployment guard, untrusted/trusted TLS, and clean stdio JSON. The TLS child gets only its temporary CA; no user trust store is changed.
# Offline contracts
cargo test -p redisctl-mcp --all-features --lib --test database_pilot
# Live fixture tests: requires redis-server, redis-cli, and openssl, not Docker
cargo test -p redisctl-mcp --all-features --test database_pilot \
-- --ignored --test-threads=1
CI runs both contracts and live fixtures. Snapshot updates require explicit
review using UPDATE_MCP_PILOT_CATALOG=1; never rewrite the legacy snapshot to
make an experimental schema change appear backward compatible.