How OpenHuman Handles the Static versus Dynamic Server Split for MCP Servers

OpenHuman splits MCP servers into a static set defined in config.toml and a dynamic set discovered from Smithery modules at runtime, combining both in the host layer to provide a unified server list.

The OpenHuman project implements a dual-layer architecture for managing Model Context Protocol (MCP) servers, separating user-declared static configurations from runtime-discovered dynamic modules. This static versus dynamic server split for MCP servers ensures that core integrations remain stable while allowing flexible extension through the Smithery ecosystem.

Understanding the Two-Layer MCP Architecture

OpenHuman's MCP implementation organizes server management into two distinct layers that operate in parallel during the initialization phase.

The Static Server Set

The static layer provides a read-only collection of MCP servers declared explicitly by the user. Located in src/openhuman/mcp/config_servers/registry.rs, the McpServerRegistry reads [[mcp_client.servers]] entries from config.toml at startup, validates each definition, and creates a McpTransportClient (either HTTP or stdio) that remains cached for the entire process.

This module is leaf-gated on the mcp feature flag and lives under the openhuman::mcp::config_servers namespace. Its primary responsibilities include parsing TOML configuration through McpClientConfig, McpServerConfig, and McpAuthConfig structures, then building McpServerDefinition objects stored in the registry.

The Dynamic Server Registry

The dynamic layer handles Smithery-installed MCP servers discovered at runtime. Implemented in src/openhuman/mcp/registry/ops.rs, the McpRegistry scans the Smithery directory, loads each module's manifest, and constructs an McpStdioClient for communication with dynamically-added servers.

Unlike the static set, these servers require no configuration file changes. When OpenHuman boots, openhuman::mcp::registry::ops::load_registry() automatically detects new modules placed in the Smithery directory and adds them to the active registry.

Configuring Static MCP Servers

Static servers require explicit declaration in the user configuration file. The system parses these entries during the initialization sequence in src/openhuman/mcp/host.rs.

To configure a static server, add an entry to your config.toml:

[[mcp_client.servers]]
name = "my-mcp"
url = "https://mcp.example.com"
auth = { token = "secret-token" }

The McpServerConfig parser validates this entry and instantiates an McpStdioClient that spawns a subprocess for the declared server. This approach ensures that critical integrations remain available regardless of runtime module scanning results.

Discovering Dynamic MCP Servers

Dynamic servers leverage the Smithery module system for zero-configuration additions. The registry implementation reuses the same McpStdioClient transport used by static servers, but discovers servers from the Smithery "modules" directory rather than TOML entries.

To register a dynamic server without modifying config.toml:


# Place a compiled Smithery module under the user's Smithery directory

cp my_dynamic_mcp.so $HOME/.openhuman/smithery/

When the host initializes, load_registry() scans this directory, loads the module manifest, and constructs the appropriate client transport. This means adding a new Smithery server instantly makes it available to MCP clients without service restarts or configuration edits.

Host Layer Aggregation and Fallback

The openhuman::mcp::host module orchestrates the combination of both server sets during core initialization. The host attempts to construct the static registry first; if this fails, the error is logged and the host continues, allowing the dynamic registry to supply servers that were not statically declared.

When an MCP client queries the combined server list through the JSON-RPC interface, the initialize method returns both static definitions from config.toml and dynamically discovered Smithery servers:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": { 
    "protocolVersion": "2025-11-25", 
    "clientInfo": { "name": "my-client" } 
  }
}

The response aggregates entries from both McpServerRegistry (static) and McpRegistry (dynamic), presenting a unified server list to the client.

Key Implementation Files

The static versus dynamic server split for MCP servers is implemented across several critical source files:

Summary

  • Static servers are declared in config.toml and parsed by McpServerRegistry at startup, providing stable, configuration-driven integrations
  • Dynamic servers are discovered from Smithery modules at runtime via McpRegistry, enabling flexible extension without configuration changes
  • Both layers use the McpStdioClient transport implementation for consistent communication
  • The host layer in host.rs attempts static initialization first, falling back to dynamic-only operation if static configuration fails
  • Client requests receive a unified server list combining both static and dynamic sources

Frequently Asked Questions

What is the difference between static and dynamic MCP servers in OpenHuman?

Static MCP servers are explicitly defined in the user's config.toml file and loaded at startup by McpServerRegistry, while dynamic servers are discovered at runtime from Smithery modules placed in the user's .openhuman/smithery/ directory. Static servers provide guaranteed availability for critical integrations, whereas dynamic servers offer flexibility for experimental or third-party extensions.

How do I add a static MCP server to OpenHuman?

Add a [[mcp_client.servers]] entry to your config.toml file with the server name, URL, and authentication details. The McpServerConfig parser in src/openhuman/mcp/config_servers/registry.rs validates the entry and creates a persistent McpTransportClient for the duration of the process.

Can dynamic MCP servers replace static ones if the configuration fails?

Yes. According to the implementation in src/openhuman/mcp/host.rs, if the static registry fails to initialize, the error is logged and the host continues startup, allowing the dynamic registry discovered through openhuman::mcp::registry::ops::load_registry() to supply available servers.

What transport protocols does OpenHuman use for MCP server communication?

OpenHuman supports both HTTP and stdio transports through the McpTransportClient trait. Static servers may use either transport based on their URL scheme, while dynamic Smithery servers currently utilize McpStdioClient which spawns a subprocess for communication.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →