Files
openhuman/gitbooks/developing/architecture/mcp-registry.md
T

116 lines
9.2 KiB
Markdown

---
description: >-
The dynamic, user-facing side of MCP-client support: discover servers on
Smithery and the official MCP registry, persist installs to SQLite, supervise
local-spawn subprocess lifecycle, surface their tools to agents via the
unified tool registry.
icon: plug
---
# MCP Registry (`src/openhuman/mcp_registry/`)
`src/openhuman/mcp_registry/` is the **dynamic, user-facing** half of OpenHuman's Model Context Protocol client support. It lets a user browse the supported upstream registries (Smithery and the official modelcontextprotocol registry), install a chosen server, persist that choice to SQLite, and (for servers launched as local subprocesses or HTTP-remote endpoints) supervise the connection lifecycle. Installed servers' tools are surfaced to agents via the unified tool registry (`crate::openhuman::tool_registry`).
> **Naming note**: the Rust module path is `mcp_registry`, but the RPC namespace and on-disk SQLite filename are still `mcp_clients` for backward compatibility with existing frontend code and stored user state. Grep both names when chasing call sites.
This module is paired with `src/openhuman/mcp_client/`: the **transport library** (HTTP + stdio primitives) plus the _static, config-declared_ server set read from `[[mcp_client.servers]]` in `config.toml`. Agents reach that static set through generic bridge tools. The static set is intentionally separate from this dynamic registry; both kinds will eventually share the transport primitives from `mcp_client`.
```text
┌───────────────────────────────────────────────┐
Registries ───► registries/ + registry.rs (10-min SQLite cache)│
└────────────────────┬──────────────────────────┘
│ browse / install
┌──────────────────────┐
Frontend (Skills UI) ─►│ ops.rs / schemas.rs │ RPC controllers
└──────────┬───────────┘
┌──────────────────────┐
│ store.rs │ mcp_clients.db (SQLite)
│ InstalledServer rows│
└──────────┬───────────┘
│ at boot
┌──────────────────────┐
│ boot.rs │ spawn_installed_servers
└──────────┬───────────┘
│ for each local-spawn
┌──────────────────────┐
│ connections.rs │ wraps mcp_client::
│ (global registry) │ McpStdioClient
└──────────┬───────────┘
│ surfaces tools to
tool_registry (agents)
```
## Server transport model
An `InstalledServer` carries a `transport: Transport` discriminator (`types.rs`) with two variants:
- **`Stdio`**: a local subprocess launched by `npx`, `uvx`, or a direct binary (see `types::CommandKind`), speaking **stdio JSON-RPC**.
- **`HttpRemote { url }`**: a hosted server (the majority of what Smithery lists), dialled over streamable HTTP by `mcp_client::McpHttpClient`.
`connections.rs` dispatches on the transport. Both the manual install dialog (`mcp_clients_install`) and the setup-agent path (`mcp_setup_install_and_connect`) pick the best connection via `setup_ops::pick_connection` (published stdio → any stdio → published http_remote → any http_remote) and build the transport with `setup_ops::build_install_transport`, so the two paths behave identically.
## Boot-time spawn
`boot::spawn_installed_servers` is called from `bootstrap_core_runtime` so every installed server is connected as soon as the core comes up. Errors are logged per-server and **never block boot**; a broken MCP install should not gate the desktop app starting. The lifecycle log subscriber (`bus::init`) is registered alongside the other domain subscribers in `register_domain_subscribers` so those connect events are observed.
## Layout
| Path | Role |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `types.rs` | Data structures: `InstalledServer`, `McpTool`, `ConnStatus`, Smithery DTOs, etc. |
| `store.rs` | SQLite persistence: `mcp_clients.db`, CRUD over `InstalledServer` rows. |
| `registry.rs` | Smithery HTTP client with a 10-minute SQLite cache so re-browsing doesn't hammer the upstream registry. |
| `registries/` | Adapters for the upstream registries this code can browse: Smithery (`smithery.rs`) + the official modelcontextprotocol registry (`mcp_official.rs`). Each reads optional auth config-first with an env-var fallback (`mcp_client.registry_auth`). |
| `connections.rs` | Global in-process connection registry. Wraps `crate::openhuman::mcp_client::McpStdioClient` (there is no separate stdio client implementation here). |
| `boot.rs` | Boot-time spawn (`spawn_installed_servers`) called from `bootstrap_core_runtime`. |
| `setup.rs` / `setup_ops.rs` | "Setup agent" support: the small agent that walks a user through configuring a freshly installed server (env vars, secrets, first connect). |
| `ops.rs` | RPC handler implementations (install, uninstall, list, browse, enable / disable, etc.). |
| `schemas.rs` | Controller schemas + handler dispatch. Re-exported from `mod.rs` as `all_mcp_registry_controller_schemas` / `all_mcp_registry_registered_controllers`. |
| `bus.rs` | `DomainEvent` subscriber for lifecycle logging. |
## Public surface
The exports from `mod.rs` are intentionally narrow:
```rust
pub use schemas::{
all_controller_schemas as all_mcp_registry_controller_schemas,
all_registered_controllers as all_mcp_registry_registered_controllers,
schemas as mcp_registry_schemas,
};
pub use types::{ConnStatus, InstalledServer, McpTool};
```
Everything else (`boot`, `bus`, `connections`, `store`, `setup`, `setup_ops`) is `pub mod` for in-crate callers but not re-exported.
## Calls into
- `crate::openhuman::mcp_client::McpStdioClient`: the actual stdio transport.
- `crate::openhuman::tool_registry`: installed servers' tools land here so agents see them alongside native tools.
- `memory_store` / workspace SQLite, for `mcp_clients.db` persistence.
- Smithery.ai HTTP, for registry browsing.
## Called by
- `bootstrap_core_runtime` (via `boot::spawn_installed_servers`).
- Frontend Skills UI: the **MCP** tab at `/skills?tab=mcp` (`McpServersTab`) dispatches through `ops.rs` over the `openhuman.mcp_clients_*` RPC namespace: browse, install (auto-connects), connect/disconnect, status, tool_call, `update_env` (reconfigure + reconnect), and `registry_settings_get` / `registry_settings_set` (Smithery / official-registry credentials; secret values are write-only). The agent-native flow uses `openhuman.mcp_setup_*` via the `mcp_setup` sub-agent (orchestrator delegate `setup_mcp_server`).
- The setup agent in `setup_ops.rs`, for first-connect onboarding.
## Tests
Unit tests are co-located inline under `#[cfg(test)]` blocks in `store.rs`, `connections.rs`, and `setup.rs`. There is no dedicated `*_tests.rs` sibling per file (the convention in this domain is inline).
## Related
- [`mcp_registry/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp_registry/mod.rs): the authoritative rustdoc this page mirrors.
- `src/openhuman/mcp_client/`: the transport library + static config-declared server set.
- [Agent Harness](agent-harness.md): how the agent ends up calling MCP tools through `tool_registry`.
- [Architecture overview](../architecture.md): where this fits in the wider system.