refactor(mcp): scope semver to the MCP tool surface and hide library modules - #301
Merged
StefanSteiner merged 1 commit intoSep 7, 2026
Conversation
…modules `hyperdb-mcp/src/lib.rs` carried a lint `reason` saying the library target "is not a documented API surface", while `hyperdb-mcp/README.md` promised, without scope, that "the public API is stable and follows semantic versioning". Against a crate where all 21 modules are `pub`, that sentence promised semver stability on every module and every item inside them, which is why two incidental internals had to be written up as API events: `DaemonState` gaining a private field (tableau#289) and `state_perms` becoming new public surface (tableau#295). Scope the promise to the MCP tool surface — tool names, parameters, and behavior as reached over the MCP protocol — and state that the Rust library target is excluded. The root README says the same in its crate table, next to the equivalent note that already existed for `hyperdb-api-core`. Mark all 21 modules `#[doc(hidden)]`. None can become `pub(crate)`: Cargo compiles the `[[bin]]`, each file under `tests/`, and `examples/demo.rs` as separate crates that reach library items only through the external `hyperdb_mcp::` path, and every module is used by at least one of them (`paths` by the binary alone; `stats`, `subscriptions` and `watcher` by one test file each). So `pub` is load-bearing for compilation, not an API commitment. Nothing is privatised and no code that compiled before stops compiling, so this is not source-breaking. The crate-level architecture bullets now name modules in plain code spans instead of intra-doc links, because rustdoc declines to resolve a link to a hidden item and renders it as literal `[engine]` brackets. `readme_tests.rs` locates the `engine` bullet by that syntax, so its delimiters move with it; the terminology it guards is unchanged and still fails when removed.
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.
Problem
hyperdb-mcpmade two contradictory statements about what it guarantees.hyperdb-mcp/src/lib.rsexported 21 public modules while carrying a lintreasonstating the library target "is not a documented API surface". Buthyperdb-mcp/README.mdpromised, without scope, that "As of 1.0.0 the public API is stable and follows semantic versioning, so breaking changes require a major release."Against a crate where every module is
pub, that sentence promised semver stability on all 21 modules and every item inside them. It has already cost twice: #289 had to recordDaemonStategaining a private field as a source-breaking change, and #295 had to documentstate_permsbecoming new public surface — both incidental internals that only counted as API because of the blanket promise.Removing or hiding public surface is free before
1.0.0and needs a major version afterwards, so this lands now. The library is demonstrably unconsumed:hyperdb-mcphas zero reverse dependencies on crates.io (reverse_dependenciesreturns"total": 0), the newest published version is1.0.0-rc.2(max_stableis0.7.3, so1.0.0is not out), and no workspace crate depends on it.What changed
1. The README promise is scoped — the load-bearing change. The stable, semver-governed surface is now the MCP tool surface: tool names, parameters, and behavior as reached over the MCP protocol. The Rust library target is explicitly excluded. This is what actually stops incidental internals from counting as breaking changes. The AI-assisted note and its tone are preserved; the paragraph is wrapped only to satisfy MD013.
2. All 21 modules are
#[doc(hidden)]. Applied uniformly rather than todaemonalone — one hidden module beside twenty visible ones would leave the contradiction in place and imply the other twenty are supported.3. Nothing was privatised, and nothing could be.
pub(crate)is unavailable for every one of the 21 modules. Cargo compiles the[[bin]], each file undertests/, andexamples/demo.rsas separate crates that reach library items only through the externalhyperdb_mcp::path:pathsdaemon,diagnostics,engine,serverenginechart,ingestattach,error,export,ingest_arrow,inspect,lakehouse,readme,saved_queries,schema,stats,subscriptions,table_catalog,version,watcherSo
pubis load-bearing for compilation, not an API commitment.What
#[doc(hidden)]does and does not achieve: it removes the modules from published rustdoc and signals intent, but the items remain technically reachable. The README scoping in (1) is what actually defines the compatibility promise.4.
version,errorandreadmeare hidden too — no exceptions. Each was considered and rejected:error— the stable contract is the serializedSCREAMING_SNAKE_CASEcode (HYPERD_NOT_FOUND) an LLM pattern-matches, which the tool surface already covers.ErrorCodeis not#[non_exhaustive], so leaving it visible would make every new error code a source-breaking change — exactly the trap being removed.version— its strings reach users through thestatustool and thehyper://workspaceresource. The tool output is the promise; the Rust helpers are how it is built.readme— exists to serveget_readme. What is stable is what that tool returns, which hiding the const does not affect.With zero reverse dependencies there is no consumer whose needs justify a carve-out, and any carve-out would re-create the contradiction in narrower form.
5. Root
README.mdaligned. Its Project Status note claimed a repo-wide "the public API is stable" that could be read as covering this crate's library. It now says the definition is per-crate and points at the crate table, whosehyperdb-mcprow gained a scoping clause — mirroring the note already present forhyperdb-api-core.6. Architecture bullets use code spans, not intra-doc links. Verified against real rustdoc output: a link to a
#[doc(hidden)]item is not resolved and renders as literal[<code>engine</code>]brackets, while`error`renders cleanly.readme_tests.rs:425locates theenginebullet by those delimiters, so they moved with it. The terminology it guards is unchanged, and red-before-green confirmed it still fails when the wording is removed.Why no
!/**BREAKING:**markerNothing is privatised, and
#[doc(hidden)]has no effect on name resolution — no code that compiled before stops compiling. Per this changelog's own convention the marker denotes source-breaking changes; using it here would misuse it and wrongly imply a major bump on a pre-1.0.0line. The changelog entry says so explicitly and notes that narrowing a module topub(crate)later would carry it.Verification
Isolated
CARGO_TARGET_DIR;HYPERD_PATH="$PWD/.hyperd/current"(the pinned0.0.26479engine,main.0.0.26479.r96880f6a);HYPERDB_STATE_DIRpinned to a temp dir.cargo build -p hyperdb-mcp --binscargo test -p hyperdb-mcp --no-runcargo build -p hyperdb-mcp --examplescargo fmt --all -- --checkcargo clippy --workspace --all-targets --all-features -- -D warningscargo test -p hyperdb-mcpRUSTDOCFLAGS="-D warnings" cargo doc --no-depsnpx markdownlint-cli2Hidden modules confirmed absent from rustdoc, by regenerating docs from the pristine
lib.rsand from this branch in the same target dir:id="modules"sectionswindow.SIDEBAR_ITEMS = {};doc/hyperdb_mcp/now contains onlyindex.html,all.html, andsidebar-items.js.