# Integrating MCP with Static and Dynamic Servers in OpenHuman

> Integrate MCP with static and dynamic servers in OpenHuman. Discover and invoke tools seamlessly across stdio and HTTP transports with a unified registry API.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-08-29

---

**OpenHuman's MCP subsystem unifies static TOML-declared servers and dynamic SQLite-backed Smithery installations under a single registry API, enabling seamless tool discovery and invocation across both stdio and HTTP transports.**

OpenHuman provides a robust Modular Compute Provider (MCP) implementation that bridges local command-line tools and remote HTTP services through a unified interface. Whether you are configuring persistent servers in [`config.toml`](https://github.com/tinyhumansai/openhuman/blob/main/config.toml) or installing ephemeral tools via the Smithery UI, the `openhuman::mcp` family of crates offers consistent APIs for tool discovery, authentication, and execution. This guide walks through the architecture, transport implementations, and integration patterns found in the `tinyhumansai/openhuman` repository.

## MCP Architecture Overview

The `openhuman::mcp` family consists of five specialized members governed by feature gates. The root module at [`src/openhuman/mcp/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/mod.rs) is always compiled and re-exports functionality from its submodules:

- **`http_client`** – Ungated HTTP transport and MCP protocol implementation
- **`config_servers`** – Leaf-gated (`mcp` feature) static server registry for stdio transport
- **`registry`** – Leaf-gated (`mcp` feature) dynamic server registry backed by SQLite
- **`audit`** – Leaf-gated (`mcp` feature) write-only activity logging
- **`server`** – Leaf-gated (`mcp` feature) built-in stdio/HTTP server for debugging

Each gated member contains its own [`stub.rs`](https://github.com/tinyhumansai/openhuman/blob/main/stub.rs) file, ensuring that builds without the `mcp` feature still compile successfully.

## Static Server Integration

Static servers are defined declaratively in the user's [`config.toml`](https://github.com/tinyhumansai/openhuman/blob/main/config.toml) under the `[[mcp_client.servers]]` array. These definitions are parsed into `McpServerDefinition` structs and exposed through the `McpServerRegistry`.

### Loading Static Configurations

The registry builder resides in [`src/openhuman/mcp/config_servers/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/config_servers/registry.rs). It constructs a static registry directly from the loaded configuration:

```rust
use openhuman::mcp::config_servers::registry::McpServerRegistry;
use openhuman::config::Config;

let cfg = Config::load_or_init()?;
let registry = McpServerRegistry::from_config(&cfg)?;

```

### Static Server Definition Schema

Each server entry in [`config.toml`](https://github.com/tinyhumansai/openhuman/blob/main/config.toml) supports the following fields, which map to the `McpServerConfig` struct:

```toml
[[mcp_client.servers]]
name = "my-local-mcp"
command = "npx"
args = ["mcp-server"]
env = { NODE_ENV = "production" }
enabled = true
allowed_tools = ["gitbooks", "my_tool"]
auth = { bearer = "my-token" }

```

When the `command` field is non-empty, the registry instantiates an `McpStdioClient` (stdio transport). If `command` is empty, the entry uses the `McpHttpClient` (HTTP transport). Transport selection logic is implemented in [`src/openhuman/mcp/config_servers/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/config_servers/mod.rs).

### Environment Setup for Subprocesses

The stdio client uses [`src/openhuman/mcp/config_servers/spawn_env.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/config_servers/spawn_env.rs) to reconstruct the `PATH` environment variable. This ensures that version managers like `npx` and `uvx` remain discoverable even when the host GUI strips the user's PATH.

## Dynamic Server Integration

Dynamic servers are installed through the Smithery UI and persist their metadata in a SQLite database (`mcp_clients.db`). The `registry` member loads this data and exposes it through the same `McpServerRegistry` interface used for static servers.

### Loading Dynamic Registries

Located in [`src/openhuman/mcp/registry/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/registry/mod.rs), the dynamic registry builder queries the SQLite store:

```rust
use openhuman::mcp::registry::McpServerRegistry;
use openhuman::config::Config;

let cfg = Config::load_or_init()?;
let dynamic_registry = McpServerRegistry::from_dynamic_store(&cfg)?;

```

The helper functions for database operations reside in [`src/openhuman/mcp/registry/helpers.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/registry/helpers.rs).

### Unified API Surface

Both static and dynamic registries expose identical public methods: `list`, `get`, `list_tools`, and `call_tool`. This allows the rest of the OpenHuman core to treat them uniformly regardless of origin.

## Transport Implementations

### HTTP Transport

The `McpHttpClient` in [`src/openhuman/mcp/http_client/client.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/http_client/client.rs) implements the streamable HTTP transport. It performs an initialize handshake that negotiates the MCP protocol version (latest: `2025-11-25`), supports SSE-based event draining, and manages session lifecycle via the `Mcp-Session-Id` header.

For authentication, the client handles OAuth discovery by parsing `WWW-Authenticate` challenges on 401 responses and following the OIDC `.well-known` flow.

```rust
let http = McpHttpClient::new(endpoint, timeout_secs);
let init = http.initialize().await?;
let tools = http.list_tools().await?;
let result = http.call_tool("gitbooks", payload).await?;

```

### Stdio Transport

The `McpStdioClient` in [`src/openhuman/mcp/config_servers/stdio.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/config_servers/stdio.rs) spawns subprocesses (e.g., `npx mcp-server`) and communicates via JSON-RPC over stdin/stdout. It maintains a single long-lived session guarded by a `tokio::Mutex`.

```rust
let stdio = McpStdioClient::new(command, args, env, cwd, identity);
let init = stdio.initialize().await?;
let result = stdio.call_tool("my_tool", payload).await?;

```

## Agent Tool Integration

MCP-related agent tools (`mcp_list_servers`, `mcp_list_tools`, `mcp_call_tool`) are implemented in [`src/openhuman/tools/impl/network/mcp.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/tools/impl/network/mcp.rs). These tools retrieve the registry via a global accessor pattern:

```rust
use openhuman::mcp::registry::McpServerRegistry;

let registry = McpServerRegistry::global();
let tools = registry.list_tools(server_name).await?;

```

Before any network or subprocess I/O occurs, the tools validate calls against per-server `allowed_tools` and `disallowed_tools` lists. This check is fail-closed and enforced at line 106 of the implementation.

## Front-End React Integration

The TypeScript implementation resides under `app/src/lib/mcp/` and provides:

- **Transport abstraction** ([`transport.ts`](https://github.com/tinyhumansai/openhuman/blob/main/transport.ts)) – Mirrors the Rust client APIs for browser contexts
- **Validation utilities** ([`validation.ts`](https://github.com/tinyhumansai/openhuman/blob/main/validation.ts)) – Ensures JSON payloads conform to MCP schemas before transmission
- **Rate limiting** ([`rateLimiter.ts`](https://github.com/tinyhumansai/openhuman/blob/main/rateLimiter.ts)) – Prevents server overload

### Component Usage

```tsx
import { McpHttpClient } from '@/lib/mcp/transport';
import { validateMcpPayload } from '@/lib/mcp/validation';

const client = new McpHttpClient('https://my-mcp.example.com', 30);
const payload = { path: '/docs' };

if (validateMcpPayload(payload)) {
  const result = await client.callTool('gitbooks', payload);
}

```

The [`McpToolPlayground.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/McpToolPlayground.tsx) component in `app/src/components/channels/mcp/` provides an interactive UI for testing both static and dynamic servers. Server representations are rendered by [`McpServerCard.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/McpServerCard.tsx), while [`InstallDialog.tsx`](https://github.com/tinyhumansai/openhuman/blob/main/InstallDialog.tsx) handles Smithery installations.

## Security and Logging

All transport implementations use `tracing::debug` and `tracing::info` macros for structured logging. The `redact_endpoint` helper in the HTTP client (defined in [`src/openhuman/mcp/http_client/client.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/http_client/client.rs)) strips user-info from URLs before logging, replacing credentials with `<redacted>`. The stdio client applies the same redaction logic when logging spawned command lines.

## Summary

- OpenHuman's MCP architecture separates concerns into five gated members under `openhuman::mcp`, with `http_client` always available and the rest gated behind the `mcp` feature.
- **Static servers** are configured in [`config.toml`](https://github.com/tinyhumansai/openhuman/blob/main/config.toml) and managed by [`config_servers/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/config_servers/registry.rs), supporting both stdio and HTTP transports.
- **Dynamic servers** are stored in SQLite and loaded via [`registry/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/registry/mod.rs), exposing the same API as static registries.
- The **HTTP transport** handles protocol negotiation, OAuth discovery, and SSE streaming in [`http_client/client.rs`](https://github.com/tinyhumansai/openhuman/blob/main/http_client/client.rs).
- The **stdio transport** spawns subprocesses with reconstructed PATH environments via [`config_servers/stdio.rs`](https://github.com/tinyhumansai/openhuman/blob/main/config_servers/stdio.rs) and [`spawn_env.rs`](https://github.com/tinyhumansai/openhuman/blob/main/spawn_env.rs).
- Agent tools access the registry through a global singleton in [`tools/impl/network/mcp.rs`](https://github.com/tinyhumansai/openhuman/blob/main/tools/impl/network/mcp.rs), with fail-closed validation on tool allow-lists.
- The React front-end provides type-safe transport wrappers, validation, and UI components for interactive MCP tool usage.

## Frequently Asked Questions

### How does OpenHuman decide between stdio and HTTP transport for a static server?

OpenHuman inspects the `command` field in the `[[mcp_client.servers]]` configuration entry. If `command` is non-empty, it instantiates `McpStdioClient` from [`src/openhuman/mcp/config_servers/stdio.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/config_servers/stdio.rs). If `command` is empty, it uses `McpHttpClient` from [`src/openhuman/mcp/http_client/client.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/http_client/client.rs). This logic is centralized in [`src/openhuman/mcp/config_servers/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/config_servers/mod.rs).

### Can I use both static and dynamic servers simultaneously in the same session?

Yes. The `McpServerRegistry` supports merging static and dynamic sources. You can load static definitions via `McpServerRegistry::from_config` and dynamic definitions via `McpServerRegistry::from_dynamic_store`. Both expose identical APIs (`list_tools`, `call_tool`, etc.), allowing seamless switching between TOML-declared servers and Smithery-installed tools.

### How does OpenHuman handle authentication tokens for MCP servers?

Authentication is configured per-server in [`config.toml`](https://github.com/tinyhumansai/openhuman/blob/main/config.toml) using the `auth` field (e.g., `auth = { bearer = "token" }`). For HTTP servers, the `McpHttpClient` supports OAuth discovery by parsing `WWW-Authenticate` headers and following OIDC flows. All credentials are redacted from logs via the `redact_endpoint` helper in [`src/openhuman/mcp/http_client/client.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/http_client/client.rs) to prevent leakage in debug output.

### What happens if the PATH environment is missing when spawning a stdio server?

The `McpStdioClient` uses [`src/openhuman/mcp/config_servers/spawn_env.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/mcp/config_servers/spawn_env.rs) to reconstruct the `PATH` environment variable before spawning subprocesses. This ensures that binaries managed by version managers like `npx`, `uvx`, or `pnpm` remain discoverable even when the hosting environment (such as a GUI application) provides a stripped PATH.