Skip to content

Extract the MCP client, registry, and audit log into this workspace - #1

Merged
senamakel merged 252 commits into
mainfrom
mcp-extraction
Aug 22, 2026
Merged

senamakel merged 252 commits into
mainfrom
mcp-extraction

Conversation

@senamakel

Copy link
Copy Markdown
Member

Extracts OpenHuman's MCP client, server registry, and write-audit log into this
workspace as a reusable TinyBus module, and hardens it on the way over.

What changed

crates/tinymcp-bus holds the wire contract — payload types, member names, and
the sanitizer — with no transport, no runtime, and no behavior. crates/tinymcp
holds everything that answers a frame: both MCP transports (Streamable HTTP with
OAuth discovery and SSE, and stdio JSON-RPC), the server registry over SQLite,
the Smithery and official catalogs, the connection supervisor, the browser
sign-in flow, the setup secret vault, and the write-audit log. It re-exports the
whole contract, so tinymcp::McpTool and tinymcp_bus::McpTool are the same
type rather than structural twins.

The 28-member TinyBus interface is behind a default-on module feature, so a
host consuming this by path dependency does not pull in a second copy of
TinyBus.

Behavior changes

Nothing that crosses the RPC surface. Within this crate, three error variants
now say something more useful than what they replaced — see the defects below.

State that used to live in process-wide OnceLocks is owned instead: the
connection map, the failure map, the pending-authorization map, the cursor
cache, the secret vault, and the registry credentials are all fields now. Two
hosts in one process no longer share connections, and the suite is no longer
order-dependent.

Defects found and fixed

Each has a regression test.

  • Remote-triggerable panic and corruption in strip_instruction_fences.
    The scan took an offset from a lowercased copy and spliced it into the
    original, so any non-ASCII character whose lowercase form has a different
    UTF-8 length shifted every subsequent offset. "İİİ<system>payload" silently
    produced "İİİ<syload"; "İ<system>é" panicked on a non-character-boundary
    split. Tool descriptions come from user-installed servers, so this was
    reachable by a third party. Now an ASCII-case-insensitive scan over the
    original bytes.
  • Credential leak into error messages. reqwest::Error's Display renders
    the full request URL including any ?api_key=…, which defeated the endpoint
    redaction the error type was applying. Every reqwest cause is now stored
    .without_url().
  • Process abort on a malformed proxy URL. The client builder ended in
    .expect("reqwest client must build"). Building is fallible now, and an
    unusable individual proxy URL is logged and skipped rather than taking the
    process down.
  • Orphaned subprocesses. The stdio child was spawned without
    kill_on_drop(true).
  • stdio never validated the negotiated protocol version, though HTTP always
    did.
  • A lock released mid-way in the secret vault's resolve-and-drop. The
    original's own comment described a recheck that was never written.
  • SecretRef::parse stripped the scheme before trimming, so a ref with
    surrounding whitespace was rejected.
  • Error::Unauthorized had dropped (HTTP 401) from its message, which is
    what OpenHuman's error classifier anchors on to demote a re-authentication
    prompt out of error reporting.
  • A disabled server was refused with MalformedResponse, which means the
    peer misbehaved. It has Error::ServerDisabled now.
  • A server installed but not connected reported UnknownServer, sending a
    user to reinstall something they already had. Error::NotConnected is
    distinct because the two ask different things of a caller.

On-disk compatibility

The registry file is not migrated on the way over: an installed server is state
a user set up, and losing it means re-authorizing every integration by hand.
crates/tinymcp/tests/opens_an_existing_openhuman_database.rs builds a database
from OpenHuman's original schema — copied verbatim from its history rather
than derived from this crate's, which is the point — including a row predating
the transport, deployment_url, and enabled columns, and asserts the same
servers and credentials read back.

The audit log moves to its own file, mcp_audit/mcp_audit.db. Rows written into
OpenHuman's memory-tree database before the move stay where they are; an audit
log is history rather than operational state, and nothing reads the old table.

Validation

All run from the repository root:

  • cargo fmt --all -- --check — clean
  • cargo clippy --all-targets --all-features -- -D warnings — clean
  • cargo build --all-targets --all-features — clean
  • cargo test --all-features — 622 passed, 0 failed
  • cargo run -p tinymcp --example verify_module -- target/debug/libtinymcp.so —
    verified, 28 members on ai.tinyhumans.tinymcp.Mcp

Consumed end-to-end from OpenHuman on a matching branch: its --lib mcp suite
(192) and all four MCP integration binaries (24) pass against this crate.

Deliberately untested

The live catalog paths against Smithery and the official registry, which need
network and credentials.

senamakel and others added 30 commits August 21, 2026 19:14
…data

Renames the `template` crate to `tinymcp` and updates its description, keywords, and categories to reflect the actual Model Context Protocol functionality. Also corrects the `tinymcp-bus` description and keywords to reference the proper module name instead of the placeholder "template".

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a new specification document for the MCP extraction process, defining the protocol and data format requirements for extracting metadata from MCP-compliant sources. This provides a formal reference for implementation and ensures consistent behavior across tools.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new document outlining the plan for extracting the Model Context Protocol (MCP) implementation into a standalone library, providing a clear reference for the upcoming refactoring work.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Added a guard clause to prevent a panic when the sanitizer receives an empty or malformed input that previously bypassed the initial length check. This ensures the function returns a safe default instead of crashing, improving robustness against unexpected data from external sources.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a new test module to verify the sanitization behavior in the tinymcp-bus crate, ensuring input is properly cleaned before processing.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Make the sanitize module publicly accessible from the bus crate so that consumers can use its sanitization utilities directly.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds a test module to the sanitize module in tinymcp-bus, providing initial test coverage for the sanitization logic.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reordered imports across several files to follow a consistent convention of grouping external crate imports before internal ones, and added public re-exports of the sanitize module's constants and functions from the tinymcp-bus crate root so that callers can access them without descending into the module hierarchy.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…t explanation

Backtick-quote `OpenHuman` in the module-level doc comment to match the code-style convention used elsewhere. Reword the test comment to explain that a lowercased-copy offset is shifted in the original, rather than describing the consequence as landing mid-codepoint, making the reason for scanning original bytes clearer.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Removed the `ConfigValue` enum and its associated `Display` implementation from the configuration types module, as they were no longer referenced anywhere in the codebase. This cleanup reduces dead code and simplifies the module.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Removed the unused `std::io::Error` import from the config module to clean up the code and eliminate a compiler warning.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When the configuration file does not exist, the parser now returns a default configuration instead of failing with an error. This allows the application to start with sensible defaults when no user configuration is present.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…erivation

Add the `schemars` crate as an optional dependency behind a new `schemars` feature flag, enabling hosts to generate JSON Schema from the bus's configuration payloads for building settings user interfaces. The feature is off by default to avoid paying the compile cost when only making calls.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the schemars dependency to version 1.2.2, which includes breaking changes requiring a new ref-cast dependency and an upgrade to syn 3.0.3 for its derive macros. This change keeps the dependency current and ensures compatibility with the latest crate ecosystem.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add `#![allow(...)]` attributes to two test modules so that clippy does not flag the deliberate use of `unwrap`, `expect`, and `panic` in test code, keeping the test output clean without changing any test logic.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transport layer now correctly processes messages with zero payload length instead of treating them as invalid. This change ensures compatibility with clients that send empty messages for keep-alive or protocol negotiation purposes.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Remove the `serde(rename_all = "camelCase")` attribute from `McpRemoteTool` to let field names serialize as-is, and replace the convoluted `is_error` expression in `McpToolResult::success` with a direct `false` value for clarity.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transport layer now correctly processes messages with zero payload length, which previously caused an error when attempting to read from an empty buffer. This fix ensures that zero-length messages are accepted as valid inputs rather than being rejected.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test for connection timeout was using an incorrect assertion that would pass even when the timeout did not occur. The assertion has been updated to properly verify that the connection attempt times out as expected.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The transport module and its public types are now re-exported from the crate root, making them available to downstream consumers. Test assertions in the transport test file are reformatted for improved readability without changing any behavior.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…rtion in test

Backtick-quote `OpenID` in the doc comment for `AuthorizationServerMetadata` to follow Rust documentation conventions for protocol names. Replace a runtime assertion with a `const` assertion in the display title test to catch violations at compile time rather than at test time.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Mark the sanitize module and contract crate config/transport types as done, and add a note about the proxy field replacement rationale.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Spec and plan for moving OpenHuman's MCP client, registry and audit log
into this repository, plus the first two phases of the port:

- sanitize/ moves into the contract crate, where both the transports and
  a host can name one copy of the stripping rule.
- config/ and transport/ carry the payload types, each with a unit test
  pinning its wire form.
- The protocol-version constants are hoisted out of the two transports
  that duplicated them.

The fence scan now runs over the original bytes instead of a lowercased
copy. Lowercasing is not length-preserving in Unicode, so the old offset
could land mid-codepoint: a tool description containing U+0130 before a
fence token either corrupted the string or panicked outright. Tool
descriptions come from whatever server the user installed, so that abort
was reachable from outside. Both cases are regression-tested.

Co-authored-by: Medulla <medulla@tinyhumans.ai>
When a service attempts to register with an ID that already exists in the registry, the system now returns an error instead of silently overwriting the existing entry. This prevents accidental replacement of active services and makes the registration contract explicit.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
When a client attempts to register a capability that already exists in the registry, the system now returns the existing entry instead of failing with an error. This makes registration idempotent and avoids unnecessary disruptions when clients reconnect or re-register the same capability.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Changed the test assertion to expect an empty vector instead of a single-element vector when no topics match the filter, ensuring the test correctly validates the registry's filtering behavior.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Make the registry module and its types publicly available from the crate root by adding the module declaration and re-exporting all public types. This allows consumers to use registry-related types without importing the internal module path. Additionally, reformat several test assertions to improve readability by breaking long lines and adjusting indentation.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…cp-bus

Remove the `#[non_exhaustive]` attribute from every public struct across the config, registry, and transport type modules. This change allows downstream consumers to construct these structs directly with struct literal syntax, which is necessary for ergonomic testing and for hosts that need to build these types without going through a builder or constructor. The attribute was originally added to reserve the right to add fields in future versions, but the current development velocity and the fact that these types are already versioned through the crate's own semver make the restriction more costly than the flexibility it protects.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
senamakel and others added 28 commits August 22, 2026 02:52
Replace the existing test that treated a banner line as a non-JSON reply with a test that verifies banner lines are skipped during handshake. Add two new tests: one for a line that looks like JSON but is not a valid reply, and one for a reply that lacks both a result and an error field. These changes improve error reporting and make the transport more resilient to common server misconfigurations.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…l bus

Add three integration tests that exercise the module's setup function against a real in-memory bus, covering the nominal case where the module comes up and serves its interface, the end-to-end call path through the bus, and the failure case where a corrupted store prevents the module from starting. Also re-export the setup function under a test-only alias so the test module can call it without making it part of the public API.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The `setup` function visibility is changed from private to `pub(super)` so that tests in the sibling test module can call it directly, removing the need for a separate `setup_for_test` re-export. The test calls are updated to use `super::setup` instead of the removed alias, and the call arguments are now properly converted with `try_into` to match the expected types.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The serving connection was being dropped before the call, releasing both the bus name and the served object. The test now holds a clone of the connection for its duration to prevent premature cleanup.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…n behaviour

Add three new test cases covering the supervisor's ability to handle transient store read failures, detect and reconnect dropped transports, and apply exponential backoff when a server cannot be reached. These scenarios ensure the supervisor loop remains robust under real-world conditions rather than panicking or retrying endlessly.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds two tests to the audit store test suite. The first verifies that a limit value exceeding the column's storage capacity is rejected, complementing the existing offset bound test. The second ensures that reading a row with an unreadable argument summary fails, as the column contains JSON and an undecodable value represents corruption that should not silently produce an empty summary.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The limit parameter in the audit log query is now capped at the maximum page size instead of being refused when it exceeds the column's capacity. This ensures that a caller requesting an unreasonably large limit receives the largest possible page rather than an error, making the API more resilient to clumsy requests.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…onnection

Adds thirteen integration tests covering the remaining public API paths through the registry facade, including credential merging on reinstall, source-routed name stripping, disconnected credential updates, OAuth completion failure handling, and subprocess connection testing. These tests ensure the facade correctly handles edge cases that were previously untested.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test for the facade handing out pieces now exercises the vault's request and submit flow before asserting on pending counts, ensuring the vault interaction path is covered alongside the existing oauth and overview assertions.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test for a non-existent command now checks that the error message contains "npx" and explicitly excludes transport or HTTP errors, making the assertion more precise about which execution arm produced the failure.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat test assertions and function calls across multiple test files to follow a consistent style, wrapping long lines and breaking chained method calls onto separate lines where they exceed the project's line-length limit. Also reorder import statements in `config_servers/test.rs`, `oauth/mod.rs`, and `tinybus_module/test.rs` to match the project's convention of grouping standard library imports before external crate imports. These changes are purely cosmetic and do not alter any runtime behaviour.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
… write!

Reorder the `mcp_endpoint` and `handle` functions in the config servers test so that `handle` is defined before `mcp_endpoint`, which reads more naturally. In the registry connections test, replace `format!` with `write!` to avoid unnecessary string allocations when building shell script bodies.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replaced `write!` with `writeln!` in test helper functions to ensure each shell command ends with a proper newline, and corrected indentation in the `mcp_endpoint` helper to use consistent spacing. These changes fix subtle issues where missing newlines could cause the generated shell scripts to behave incorrectly during test execution.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The unix stdio test module was using `write!` without importing the `fmt::Write` trait, which caused a compilation error on unix platforms. The import is now added to ensure the tests compile correctly.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…er function

The inline route handlers for `/register` and `/token` in the test authority have been moved into a separate `endpoints()` function. This keeps the router builder readable by avoiding a single expression that spans the entire function length.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Moves the `handle` function from inside `mcp_server` to a standalone item, and changes `list_body` to accept a slice instead of an owned vector. This reduces nesting and avoids unnecessary allocations without altering test behaviour.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…rotocol version

The `envelope()` helper function was moved from inside the `catalog` test helper to module level so it can be reused by other tests. In the stdio transport test, the `LATEST_PROTOCOL_VERSION` constant is now interpolated directly into the format string instead of being passed as an argument, making the shell script template more readable.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…elper

Extract the nvm directory resolution into its own function so that the version comparison logic can be tested against a synthetic directory tree rather than depending on the developer's or CI runner's actual nvm installation. Add seven tests covering version ordering, non-version directories, missing bin directories, empty layouts, absent directories, and environment variable override.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add two tests that verify write failures are reported with actionable wording: a broken pipe is described as the server having closed its output, while other errors include what operation was being attempted.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
… ETXTBSY race

Replace the `script` helper that writes an executable shell script to disk with a `shell_client` function that passes the script body as an argument to `/bin/sh -c`. The old approach created a race condition: when tests run in parallel, one test's fork and exec can collide with another test's still-open file descriptor, causing the kernel to refuse execution with `ETXTBSY`. Using the interpreter directly eliminates the written file and therefore the race entirely.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformat the `shell_client` helper function signature and its call sites to improve readability by breaking long parameter lists across multiple lines. In the `a_reply_carrying_neither_a_result_nor_an_error_is_reported` test, replace the separate script path creation and `McpStdioClient::new` call with a direct `shell_client` invocation, simplifying the test setup while preserving the same behavior.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
… commands

The test helper `write_script` created an executable shell script on disk and passed its path as the server command. This caused `ETXTBSY` failures under parallel test execution because one test's still-open file descriptor prevented another test's `exec` from running the same script. The change removes `write_script` and instead passes the script body directly as an argument to `/bin/sh -c`, eliminating the race condition since the interpreter is never written by the test.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Several test functions in the stdio transport test module were creating a temporary directory via `tempfile::tempdir().unwrap()` but never using the resulting directory handle. These unused allocations have been removed to clean up the test code and eliminate unnecessary filesystem operations during test execution.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Changed the `shell_client` call in the test to pass `body` by value instead of by reference, since the function takes ownership of the string.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test helper that wrote executable shell scripts to disk and returned their paths has been removed. Instead, each test now passes the script body directly as an argument to `/bin/sh -c`. This eliminates a race condition where the kernel refused to exec a script file with `ETXTBSY` when another test still held the file descriptor open, since the suite runs test cases in parallel.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test helper `responder` no longer requires a temporary directory, so the
unnecessary `tempfile::tempdir()` calls and the conversion of its return value
to a string have been removed throughout the test module. This simplifies the
test setup and eliminates a source of potential test noise from leftover temp
directories.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The temporary directory variable was declared but never used in the test, causing a compiler warning. Removing it cleans up the test without affecting its behavior.

Auto-committed-on: dragonfly
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@senamakel
senamakel merged commit b4f3b2f into main Aug 22, 2026
15 checks passed
@senamakel
senamakel deleted the mcp-extraction branch August 22, 2026 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant