tinymcp-bus v0.3.1 publishes a complete names::METHODS (28 members), a
CONTRACT_VERSION and the payload types — enough that the survey started
optimistic. But OpenHuman cannot yet drop its tinymcp path dependency and reach
the module over the bus, because a set of things the host reaches for are not on
the wire. This issue enumerates them, verified against the code rather than
assumed.
Context: openhuman PR
#5689 lands the registry
entry pinning the v0.3.1 release, and documents this list next to the dependency
it cannot yet remove.
Members that do not exist
1. OAuthComplete(state, code) -> ConnectOutcome
The contract has OAuthBegin only. OpenHuman's loopback callback route calls
McpRegistry::oauth_complete, which does the token exchange and the reconnect,
and resolves state -> server_id through the module-private pending-authorization
map. It is not reconstructible from Connect.
2. The boot-connect pass and the reconnect supervisor
registry::connect_installed_servers(...) and Supervisor / SupervisorConfig /
.tick(store, connections, oauth, now) have no members — and the module does not
run either internally. tinybus_module/mod.rs's setup() builds the service,
serve_ats it, request_names and returns.
Its own lazy = false comment says "a host that loaded this module wants its
servers connected, and deferring the load would defer that until the first call —
by which point an agent has already been told it has no tools." Nothing implements
that today.
I think the right answer is that the module should own both — start the connect
pass and the supervisor task in setup() — rather than exposing them as members
for a host to drive. The supervisor's backoff state and its probe_alive liveness
check (a real round trip, distinct from map membership) are module-internal, so a
host driving this over Status polling would be reimplementing it worse. But that
is a design call, so flagging rather than assuming.
3. A member returning ConnectedServerOverview
The type is exported by tinymcp-bus, but nothing on the interface returns it —
an exported type with no wire path. OpenHuman reads it from the orchestrator prompt
builder and the agent tool registry (via all_connected_tools, itself
connected_overview().flat_map). Composing it host-side from Status +
InstalledList + N x ListTools is an N+1 on a path that runs on every tool-list
build.
Types the contract does not publish
Four of the 28 members return types a host cannot name:
ServerDetail — returned by RegistryGet, SetupGet and ConfigAssist,
but defined in crates/tinymcp/src/tinybus_module/service.rs, i.e. inside the
module crate.
AuthDetection / AuthKind — returned by DetectAuth, defined in
crates/tinymcp/src/registry/oauth/types.rs.
Both are decodable with hand-written mirror structs host-side, which is exactly the
"parallel set of payload types for hosts ... a conversion at every call site that
nothing checks" that tinymcp-bus's own lib.rs docs rule out. Moving them down
into the contract keeps the crate's stated invariant true.
Smaller, but each one real
4. registry::curation::{tag_official, float_official_first} are applied by
the caller after a search, and RegistrySearch deliberately does not apply them
("applied by crate::registry::curation on top of this, by a caller that wants
them", ops/types.rs). Neither is in the contract, and the official-list badging
is data-driven from curation's own list, so a host cannot reimplement it faithfully.
Either export them, or apply them inside RegistrySearch behind a flag.
5. render_tool_result and redact_endpoint live in tinymcp::transport, not
in the contract. OpenHuman's gitbooks documentation tool consumes
result.rendered and logs redact_endpoint(...); StaticCallTool returns
ToolCallOutcome { result: Value, is_error: bool } — the raw reply only. Either
export both, or add a rendered: McpToolResult field to ToolCallOutcome.
6. No error type is exported. OpenHuman's
classifies_mcp_connect_401_as_needs_auth is a live test that anchors the
McpServerNeedsAuth observability classifier on tinymcp::Error::Unauthorized's
Display output, precisely so the classifier cannot drift away from the wording it
matches. With no error type on the contract that coupling becomes an untethered
string literal. An error-name table (the shape tinymemory-bus uses) would keep it.
The architectural one
7. A per-data_dir object seam.
ModuleConfig carries a single data_dir, supplied once at load. OpenHuman's
mcp::host keys a HashMap<PathBuf, McpHost> by workspace, one Store +
AuditStore per workspace.
This is not a test-only concern. Login rewrites the active-user marker and reloads
config in-process, moving workspace_dir from users/local to users/<id>;
logout reverses it. An ordinary desktop session therefore opens at least two, and
repeated login/logout opens more. With one data_dir per loaded module, a
post-login RPC would be answered silently from the pre-login store — which is worse
than the direct call it replaced, because it is wrong rather than absent.
TinyMemory already solves exactly this: its root object takes a directory and
returns a new object path to talk to. An Open(data_dir) -> object_path member
here would follow the same shape.
Unrelated, but worth fixing in the same release
The v0.3.1 archives contain libtinymcp.so. OpenHuman's
modules::ops::platform_library_name derives libtinymcp_module.so from the
module id, matching every other first-party module. Downloads still work — tinybus
locates the library itself — but the warm-start installed_artifact fast path
never matches, so the module re-downloads on every process start. Renaming the
cdylib to tinymcp_module would fix it.
Not asked for
Prompt-injection detection over remote tool definitions, the RPC surface, the
agent-facing tools and proxy scoping all stay in OpenHuman — that split is right
and lib.rs already documents it. Nothing here asks the module to take on host
policy.
tinymcp-busv0.3.1 publishes a completenames::METHODS(28 members), aCONTRACT_VERSIONand the payload types — enough that the survey startedoptimistic. But OpenHuman cannot yet drop its
tinymcppath dependency and reachthe module over the bus, because a set of things the host reaches for are not on
the wire. This issue enumerates them, verified against the code rather than
assumed.
Context: openhuman PR
#5689 lands the registry
entry pinning the v0.3.1 release, and documents this list next to the dependency
it cannot yet remove.
Members that do not exist
1.
OAuthComplete(state, code) -> ConnectOutcomeThe contract has
OAuthBeginonly. OpenHuman's loopback callback route callsMcpRegistry::oauth_complete, which does the token exchange and the reconnect,and resolves
state -> server_idthrough the module-private pending-authorizationmap. It is not reconstructible from
Connect.2. The boot-connect pass and the reconnect supervisor
registry::connect_installed_servers(...)andSupervisor/SupervisorConfig/.tick(store, connections, oauth, now)have no members — and the module does notrun either internally.
tinybus_module/mod.rs'ssetup()builds the service,serve_ats it,request_names and returns.Its own
lazy = falsecomment says "a host that loaded this module wants itsservers connected, and deferring the load would defer that until the first call —
by which point an agent has already been told it has no tools." Nothing implements
that today.
I think the right answer is that the module should own both — start the connect
pass and the supervisor task in
setup()— rather than exposing them as membersfor a host to drive. The supervisor's backoff state and its
probe_alivelivenesscheck (a real round trip, distinct from map membership) are module-internal, so a
host driving this over
Statuspolling would be reimplementing it worse. But thatis a design call, so flagging rather than assuming.
3. A member returning
ConnectedServerOverviewThe type is exported by
tinymcp-bus, but nothing on the interface returns it —an exported type with no wire path. OpenHuman reads it from the orchestrator prompt
builder and the agent tool registry (via
all_connected_tools, itselfconnected_overview().flat_map). Composing it host-side fromStatus+InstalledList+ N xListToolsis an N+1 on a path that runs on every tool-listbuild.
Types the contract does not publish
Four of the 28 members return types a host cannot name:
ServerDetail— returned byRegistryGet,SetupGetandConfigAssist,but defined in
crates/tinymcp/src/tinybus_module/service.rs, i.e. inside themodule crate.
AuthDetection/AuthKind— returned byDetectAuth, defined incrates/tinymcp/src/registry/oauth/types.rs.Both are decodable with hand-written mirror structs host-side, which is exactly the
"parallel set of payload types for hosts ... a conversion at every call site that
nothing checks" that
tinymcp-bus's ownlib.rsdocs rule out. Moving them downinto the contract keeps the crate's stated invariant true.
Smaller, but each one real
4.
registry::curation::{tag_official, float_official_first}are applied bythe caller after a search, and
RegistrySearchdeliberately does not apply them("applied by
crate::registry::curationon top of this, by a caller that wantsthem",
ops/types.rs). Neither is in the contract, and the official-list badgingis data-driven from curation's own list, so a host cannot reimplement it faithfully.
Either export them, or apply them inside
RegistrySearchbehind a flag.5.
render_tool_resultandredact_endpointlive intinymcp::transport, notin the contract. OpenHuman's
gitbooksdocumentation tool consumesresult.renderedand logsredact_endpoint(...);StaticCallToolreturnsToolCallOutcome { result: Value, is_error: bool }— the raw reply only. Eitherexport both, or add a
rendered: McpToolResultfield toToolCallOutcome.6. No error type is exported. OpenHuman's
classifies_mcp_connect_401_as_needs_authis a live test that anchors theMcpServerNeedsAuthobservability classifier ontinymcp::Error::Unauthorized'sDisplayoutput, precisely so the classifier cannot drift away from the wording itmatches. With no error type on the contract that coupling becomes an untethered
string literal. An error-name table (the shape
tinymemory-bususes) would keep it.The architectural one
7. A per-
data_dirobject seam.ModuleConfigcarries a singledata_dir, supplied once at load. OpenHuman'smcp::hostkeys aHashMap<PathBuf, McpHost>by workspace, oneStore+AuditStoreper workspace.This is not a test-only concern. Login rewrites the active-user marker and reloads
config in-process, moving
workspace_dirfromusers/localtousers/<id>;logout reverses it. An ordinary desktop session therefore opens at least two, and
repeated login/logout opens more. With one
data_dirper loaded module, apost-login RPC would be answered silently from the pre-login store — which is worse
than the direct call it replaced, because it is wrong rather than absent.
TinyMemory already solves exactly this: its root object takes a directory and
returns a new object path to talk to. An
Open(data_dir) -> object_pathmemberhere would follow the same shape.
Unrelated, but worth fixing in the same release
The v0.3.1 archives contain
libtinymcp.so. OpenHuman'smodules::ops::platform_library_namederiveslibtinymcp_module.sofrom themodule id, matching every other first-party module. Downloads still work — tinybus
locates the library itself — but the warm-start
installed_artifactfast pathnever matches, so the module re-downloads on every process start. Renaming the
cdylib to
tinymcp_modulewould fix it.Not asked for
Prompt-injection detection over remote tool definitions, the RPC surface, the
agent-facing tools and proxy scoping all stay in OpenHuman — that split is right
and
lib.rsalready documents it. Nothing here asks the module to take on hostpolicy.