fix(mcp): cross-process catalog write safety via optimistic concurrency#38
Merged
StefanSteiner merged 5 commits intoMay 26, 2026
Merged
Conversation
Replace DELETE+INSERT-in-transaction upsert with UPDATE-then-conditional-INSERT. Each statement is individually atomic at the hyperd level, preventing duplicate _table_catalog rows when multiple MCP servers share one hyperd via the daemon.
Update both the LLM-facing readme (get_readme tool) and the user-facing README.md to position the persistent database as structured, queryable memory for AI — differentiating from flat-text memory systems by emphasizing JOIN/filter/aggregate capabilities across sessions.
The macOS CI runner under load can exceed 60s for hyperd startup when 34 other tests run in the same suite. 90s gives HyperProcess::new's internal 30s callback timeout ample room to surface real errors before the outer assertion fires.
…d_by) Capture which MCP client created and last modified each table catalog entry. The `initialize` handshake provides `client_info.name` and `version` which are stored as a combined label (e.g. "Claude Code 1.0.32"). - `created_by` is set once on INSERT, never overwritten - `last_modified_by` updates on every explicit ingest (last writer wins) - Reconciliation and watchers pass None to avoid clobbering provenance - Schema migration via ALTER TABLE ADD COLUMN for pre-existing catalogs
…timeout The internal `wait_for_callback` in HyperProcess::new has a 60s timeout. On slow macOS CI runners, spawn latency can push the total blocking time past the previous 90s outer limit, causing the test to panic with a generic message instead of surfacing the real error from the daemon thread. 150s ensures the daemon thread always has time to finish and report the actual failure cause.
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.
Title
fix(mcp): cross-process catalog safety, provenance tracking, and queryable-memory framing
Body
Summary
_table_catalogupsert with an optimistic UPDATE-then-conditional-INSERT pattern that is safe across multiple MCP server processes sharing the samehyperdvia the daemon.created_by/last_modified_by) to_table_catalogso users can see which MCP client created or last modified a table.TestDaemonstartup.Motivation
Optimistic concurrency: PR #26 introduced the single-instance daemon so multiple MCP clients share one
hyperd. The old DELETE+INSERT upsert could interleave across processes, producing duplicate catalog rows. The new pattern eliminates this race without adding infrastructure.Provenance: When multiple AI clients share a persistent database, users need to answer "who created this table?" or "who last updated it?" The
initializeMCP handshake providesclient_info.name+versionwhich we now capture and store.Queryable memory: Unlike flat-text memory, persistent data is queryable with SQL. LLMs can JOIN, filter, aggregate, and reason over stored context. The README changes make this capability front-and-center so LLMs naturally use it.
What changed
hyperdb-mcp/src/table_catalog.rscreated_by,last_modified_by). Schema migration via ALTER TABLE ADD COLUMN.use std::fmt::Write.hyperdb-mcp/src/server.rsinitializeoverride capturesclient_infointoclient_namefield.after_ingest_catalog_updatepasses client name through. Removed stale#[expect(clippy::unused_self)].hyperdb-mcp/src/watcher.rsNonefor client_name (watchers have no client context).hyperdb-mcp/src/readme.rshyperdb-mcp/README.mdhyperdb-mcp/CHANGELOG.mdhyperdb-mcp/tests/daemon_tests.rshyperdb-mcp/tests/per_tool_database_tests.rsupsert_stub_incall sites for new 8th argument.Test plan
cargo fmt --check && cargo clippy --workspace --tests -- -D warnings— cleancargo test -p hyperdb-mcp— all green (195 tests)cargo test -p hyperdb-mcp --test per_tool_database_tests— 30 passcargo test -p hyperdb-mcp --test end_to_end_mcp_tests— 10 passdescribeoutput via live MCP sessionINSERT ... WHERE NOT EXISTSreturnsaffected_rows: 0on duplicate (no error)