Skip to content

feat(mcp): key-value scratchpad tools + database-targeted KV API#185

Merged
StefanSteiner merged 7 commits into
tableau:mainfrom
StefanSteiner:feat/kv-mcp-tools
Jul 10, 2026
Merged

feat(mcp): key-value scratchpad tools + database-targeted KV API#185
StefanSteiner merged 7 commits into
tableau:mainfrom
StefanSteiner:feat/kv-mcp-tools

Conversation

@StefanSteiner

Copy link
Copy Markdown
Contributor

Summary

Adds a key-value scratchpad to the HyperDB MCP so an LLM can stash
variables, state, summaries, or JSON strings under a store/key
namespace without creating a table — and promotes the underlying
hyperdb-api KV API to a public, database-targetable surface to back it.

This is Milestone 2 of the KV-store work; Milestone 1 (the
hyperdb-api KvStore itself) shipped in #182. Both land together as one
feat: release.

What's in it

hyperdb-api (public API)

  • KvStore::kv_store_in(database, name) and
    Connection::kv_list_stores_in(alias) — open / enumerate a KV store in a
    specific database. kv_store_in takes an unescaped database name
    and quotes it internally, so the API is safe by construction (the caller
    can't forget to escape). The async client gets the matching
    AsyncConnection::kv_store_in.

hyperdb-mcp (8 new tools)

Tool Purpose
kv_set Save a value under store + key (upsert)
kv_get Read a value by store + key ({found, value})
kv_delete Delete a key ({deleted})
kv_list List keys in a store (sorted)
kv_list_stores List store namespaces in a database
kv_size Count keys in a store
kv_pop Atomic read-and-remove of the lowest key (work-queue)
kv_clear Delete all keys in a store
  • Ephemeral by default. Omit database and the store lives in the
    ephemeral primary DB (lost on restart). Pass database:"persistent" (or
    persist:true) to persist across restarts, or any writable attached
    alias to target that database. kv_set's description carries a loud
    durability warning. Each database keeps its own isolated set of stores.
  • Read-only aware. The four mutators (kv_set, kv_delete, kv_pop,
    kv_clear) are blocked with a read-only violation when the server runs
    --read-only; the four readers keep working.
  • hyper://schema/kv resource documents the _hyperdb_kv_store
    backing table, its indexless shape, the ephemeral-vs-persistent
    durability rule, per-database isolation, and a LEFT JOIN template for
    enriching analytical tables with KV metadata.

Testing

  • New: hyperdb-mcp/tests/kv_tools_tests.rs — 11 end-to-end tests
    driving the tools through the real rmcp dispatch path: CRUD lifecycle,
    key sorting, delete/pop/clear semantics, database routing + isolation
    (persistent vs ephemeral vs attached alias), the read-only guard
    (mutators blocked, readers allowed), the ephemeral-only guard
    (database:"persistent" errors, not panics), and persistence across a
    server restart
    .
  • New: hyperdb-api/tests/kv_store_in_tests.rs — covers the
    database-targeted API.
  • readme_tests.rs extended so all 8 tool names are enforced present in
    the LLM-facing README.
  • Full workspace test suite green; cargo clippy --workspace --all-targets --all-features -- -D warnings clean; make doc introduces no new
    warnings; real-tooling stdio smoke test confirms the 8 tools appear in
    tools/list, hyper://schema/kv is listed and readable, and a
    kv_setkv_getkv_list round-trip works against the release binary.

Changelog

  • hyperdb-api/CHANGELOG.md — the kv_store_in / kv_list_stores_in
    public API under ### Added.
  • hyperdb-mcp/CHANGELOG.md — the 8 kv_* tools and the
    hyper://schema/kv resource under ### Added.

@StefanSteiner StefanSteiner merged commit c24e291 into tableau:main Jul 10, 2026
11 checks passed
StefanSteiner added a commit that referenced this pull request Jul 10, 2026
…follow-up to #185) (#188)

* fix(mcp): map caller-fixable identifier errors to INVALID_ARGUMENT

An invalid KV `store`/`key` (a disallowed byte or over the 512-byte
limit) surfaced from the hyperdb-api layer as `Error::InvalidName` and
fell through the catch-all arm to `INTERNAL_ERROR` — telling an LLM
caller "server bug, give up" for something it supplied and can fix.
Map `InvalidName` and `InvalidTableDefinition` to `INVALID_ARGUMENT`,
which carries a self-correction suggestion; the human-readable message
naming the offending byte or length is unchanged.

`InvalidOperation` is deliberately left out: it is hyperdb-api
caller-API misuse where the caller is this MCP's own Rust code, not the
LLM, so it correctly stays `INTERNAL_ERROR`. A regression test locks in
that distinction.

Caught during the KV MCP smoke run.

* docs(mcp): add on-demand KV smoke-test guide

Capture the manual, tool-driven smoke tests that exercise the eight
`kv_*` tools against a live MCP server: CRUD/upsert, listing and
discovery, value fidelity, destructive semantics, input validation
(now INVALID_ARGUMENT), read-only mode, database routing/isolation,
the LEFT JOIN enrichment pattern, the hidden backing table, and
single-process atomicity. Includes a safety section: the persistent
database may hold real data, so scratch names are `smoke_`-prefixed
and every run ends by verifying the persistent DB is untouched.

The existence-check safety rule is scoped to the target database —
a bare `describe` inspects only the ephemeral primary and `status`
never lists table names, so neither alone protects a persistent write.

Cross-linked from DEVELOPMENT.md's Test section.

* docs(mcp): surface the KV store across all doc surfaces

The key-value scratchpad tools shipped without documentation in the
user-facing MCP README, the LLM-facing get_readme, or the workspace
READMEs. Add coverage in the right places so an LLM told "remember X"
considers kv_set instead of always reaching for CREATE TABLE:

- hyperdb-mcp/README.md: new "### Key-Value Store" tool section; a
  "Table or key-value store?" decision rule in Queryable Memory for AI;
  the hyper://schema/kv Resources-table row; KV tools in the Features
  bullet; and the KV mutator/reader split across all three read-only
  enumerations (flag table, Allowed, Blocked).
- src/readme.rs (get_readme): "KV store vs. a custom table" decision
  subsection; kv_pop lexicographic-order clarification; a describe
  persistent-DB signpost (status reports counts, not names); a
  back-link completing the bidirectional cross-link.
- src/server.rs: kv_pop and kv_list_stores #[tool(description)] strings
  clarify lexicographic pop order and the no-store-registry behavior.
- README.md (top-level): KvStore / AsyncKvStore Key Features bullet.

Also fix a pre-existing false claim on the --read-only flag-table row
(carried forward on a line this change edited): Hyper-format export is
NOT disabled in read-only mode (export has no writable gate; it's a
read-only file copy), matching the Read-Only "Allowed" list and the
HyperMcpServer::new doc comment.
@StefanSteiner

Copy link
Copy Markdown
Contributor Author

Follow-up: filed #192 with 5 improvements to these KV tools, surfaced by dogfooding them in a real Claude Code session (storing + querying a 70 KB ~/.claude.json blob) — value_path for file-backed kv_set, an insert-vs-overwrite signal, value-size reporting, batch ops (the API-layer set_batch already exists, just unsurfaced), and docs for the value::json + JSON_EACH query pattern. Each item has a proposed fix and a code citation.

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