MCP Integration
How Aleph integrates with the Model Context Protocol for extensible tool servers
What is MCP?
The Model Context Protocol (MCP) is an open standard that defines how AI applications communicate with external tool servers. It provides a structured way for LLMs to discover and invoke tools, read resources, and retrieve prompts from external processes.
Aleph acts as an MCP client, connecting to one or more MCP servers that provide tools, resources, and prompts. This architecture lets you extend Aleph's capabilities without modifying its core — simply run an MCP server alongside Aleph.
Architecture
┌──────────────────────────────┐
│ Aleph Core │
│ │
│ ┌────────────────────────┐ │
│ │ McpManager (Actor) │ │ ┌────────────────────────┐
│ │ │──┼──────│ MCP Server: Filesystem │
│ │ ┌──────────────────┐ │ │ └────────────────────────┘
│ │ │ McpClient │ │ │
│ │ │ │──┼──┼──────┌────────────────────────┐
│ │ │ tool_location │ │ │ │ MCP Server: GitHub │
│ │ │ external_servers│ │ │ └────────────────────────┘
│ │ └──────────────────┘ │ │
│ │ │──┼──────┌────────────────────────┐
│ │ Health monitoring │ │ │ MCP Server: Database │
│ │ Circuit breaker │ │ └────────────────────────┘
│ │ Auto-restart │ │
│ └────────────────────────┘ │
│ │
│ ┌────────────────────────┐ │
│ │ AlephToolServer │ │
│ │ (unified registry) │ │
│ └────────────────────────┘ │
└──────────────────────────────┘The integration has three layers:
- McpClient (
src/mcp/client.rs) — Manages connections to external MCP servers, tool discovery, and tool invocation - McpManager (
src/mcp/manager/) — An actor that orchestrates server lifecycles with health monitoring and circuit breaker patterns - McpToolWrapper (
src/builtin_tools/mcp_wrapper.rs) — Wraps MCP tools asAlephToolDyninstances for registration in theAlephToolServer
Transport Types
Aleph supports three MCP transport mechanisms:
| Transport | Use Case | Connection |
|---|---|---|
| Stdio | Local processes | Spawns a child process, communicates via stdin/stdout |
| HTTP | Remote servers | Standard HTTP request/response |
| SSE | Remote servers with streaming | Server-Sent Events for real-time updates |
pub enum McpTransportType {
Stdio, // Subprocess communication
Http, // HTTP transport
Sse, // Server-Sent Events
}Server Configuration
Persistent Configuration
MCP server configurations are stored in ~/.aleph/mcp_config.json:
{
"version": 1,
"servers": {
"filesystem": {
"id": "filesystem",
"name": "Filesystem Server",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/projects"],
"env": {},
"requires_runtime": "node",
"auto_start": true,
"timeout_seconds": 300
},
"github": {
"id": "github",
"name": "GitHub Server",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
},
"requires_runtime": "node",
"auto_start": true
}
}
}Configuration Options
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique server identifier |
name | string | Yes | Human-readable display name |
transport | string | Yes | stdio, http, or sse |
command | string | Stdio only | Command to execute |
args | array | No | Command arguments |
url | string | HTTP/SSE only | Server endpoint URL |
env | object | No | Environment variables |
requires_runtime | string | No | Required runtime (node, python, bun) |
auto_start | boolean | No | Start automatically (default: true) |
timeout_seconds | integer | No | Request timeout in seconds |
Environment Variable Expansion
Configuration values support ${VAR} syntax for environment variable expansion:
{
"env": {
"API_KEY": "${MY_API_KEY}",
"HOME": "${HOME}"
},
"command": "${HOME}/.local/bin/my-server"
}Unknown variables are left as-is, so you can safely reference variables that may not be set in all environments.
McpManager Actor
The McpManager uses the actor pattern for safe concurrent server management:
// Create the actor and handle
let (actor, handle) = McpManagerActor::new(None).await?;
// Spawn the actor task
tokio::spawn(actor.run());
// Use the handle for all operations
handle.add_server(config).await?;
handle.list_servers().await?;
handle.shutdown().await?;Server Lifecycle Commands
The manager supports the full server lifecycle via commands:
| Command | Description |
|---|---|
AddServer | Register a new server configuration |
RemoveServer | Remove a server by ID |
StartServer | Start a stopped server |
StopServer | Stop a running server |
RestartServer | Restart a specific server |
ListServers | List all servers with status |
GetStatus | Get detailed status for a server |
AggregateTools | Get tools from all servers |
AggregateResources | Get resources from all servers |
AggregatePrompts | Get prompts from all servers |
ReloadConfig | Reload configuration from disk |
Shutdown | Graceful shutdown of all servers |
Event System
The manager broadcasts events to subscribers for real-time monitoring:
let mut events = handle.subscribe();
while let Ok(event) = events.recv().await {
match event {
McpManagerEvent::ServerStarted { server_id, tool_count, .. } => {
println!("{} started with {} tools", server_id, tool_count);
}
McpManagerEvent::ServerCrashed { server_id, error, .. } => {
eprintln!("{} crashed: {}", server_id, error);
}
_ => {}
}
}Health Monitoring
The McpManager implements a circuit breaker pattern for server health:
Healthy -> Degraded (2+ failures) -> Unhealthy (5+ failures) -> Restarting -> Healthy
-> Dead (max restarts exceeded)| Status | Description |
|---|---|
Healthy | Server is operating normally |
Degraded | Some failures detected (2-4 consecutive) |
Unhealthy | Circuit open (5+ consecutive failures) |
Restarting | Server is being restarted |
Dead | Maximum restarts exceeded |
Stopped | Server intentionally stopped |
Health tracking includes:
- Consecutive failure counting
- Restart window management (prevents restart storms)
- Last error message retention
- Elapsed time since last health check
Tool Discovery
When an MCP server starts, Aleph automatically discovers its tools:
// Server starts and lists its tools
let tools = connection.list_tools().await;
// Tools are registered in the tool location map
for tool in &tools {
tool_location_map.insert(tool.name.clone(), ToolLocation::External(server_name));
}Discovered tools are available to the agent alongside built-in tools. The AlephToolServer treats them identically — the agent does not need to know whether a tool is built-in or provided by an MCP server.
Tool Invocation
When the agent calls an MCP tool:
- The
AlephToolServerresolves the tool name to its MCP server - The
McpClientsends a JSON-RPCtools/callrequest to the server - The server executes the tool and returns a result
- The result is forwarded to the LLM as a
tool_result
pub async fn call_tool(&self, name: &str, args: Value) -> Result<McpToolResult> {
// Try server-prefixed tool name (e.g., "github:create_issue")
if let Some((server_name, _)) = name.split_once(':') {
if let Some(connection) = servers.get(server_name) {
return connection.call_tool(name, args).await;
}
}
// Try all servers
for connection in servers.values() {
if connection.has_tool(name).await {
return connection.call_tool(name, args).await;
}
}
Err(AlephError::McpToolNotFound(name.to_string()))
}Resources and Prompts
Beyond tools, Aleph also supports MCP resources and prompts:
Resources
Read structured data from MCP servers:
// List available resources
let resources = client.list_resources().await;
// Read a resource by URI
let content = client.read_resource("filesystem:file:///path/to/file").await?;The mcp_read_resource built-in tool exposes this to the agent.
Prompts
Retrieve prompt templates from MCP servers:
// List available prompts
let prompts = client.list_prompts().await;
// Get a prompt with arguments
let result = client.get_prompt("github:review_pr", Some(args)).await?;The mcp_get_prompt built-in tool exposes this to the agent.
Sampling Support
Aleph supports MCP sampling requests, where the server asks the client (Aleph) to generate text using its LLM. This enables advanced server-side orchestration:
// Set up sampling handler
client.set_sampling_callback(|request| async {
// Forward to Aleph's LLM provider
let response = provider.complete(request.messages).await?;
Ok(SamplingResponse { content: response })
}).await;Runtime Checks
Before starting a server, Aleph verifies that the required runtime is available:
ExternalServerConfig {
name: "my-server".to_string(),
command: "npx".to_string(),
args: vec!["-y", "@my/mcp-server"],
requires_runtime: Some("node".to_string()),
// ...
}Supported runtimes: node, python, bun. If the runtime is not found, the server is skipped with a warning rather than failing the entire startup.
Startup Report
The start_external_servers method returns a report detailing which servers started successfully and which failed:
let report = client.start_external_servers(configs).await;
println!("Started: {:?}", report.succeeded); // ["filesystem", "github"]
println!("Failed: {:?}", report.failed); // [("database", "connection refused")]
if !report.all_succeeded() {
eprintln!("Some MCP servers failed to start");
}This allows Aleph to continue operating even when some MCP servers are unavailable.
Remote Servers
For MCP servers running on remote machines, use HTTP or SSE transport:
{
"id": "remote-api",
"name": "Remote API Server",
"transport": "http",
"url": "https://mcp.example.com/api",
"headers": {
"Authorization": "Bearer ${API_TOKEN}"
},
"timeout_seconds": 60
}{
"id": "streaming-server",
"name": "Streaming Server",
"transport": "sse",
"url": "https://mcp.example.com/sse",
"timeout_seconds": 300
}When transport is set to auto, Aleph defaults to HTTP. Future versions may add automatic transport detection via capability negotiation.
Security Considerations
- Process isolation: Stdio MCP servers run as separate processes with their own permissions
- Environment variables: Sensitive values like API keys should use
${VAR}expansion rather than being stored in plaintext - Tool confirmation: MCP tools can be placed in the
requireConfirmationlist in tool filtering configuration - Network boundaries: Remote MCP servers should use HTTPS with proper authentication headers
- Runtime validation: Required runtimes are checked before server launch to prevent arbitrary command execution
Best Practices
- Use
auto_start: truefor essential servers that should always be available - Set reasonable timeouts to prevent hanging operations
- Specify
requires_runtimeto get clear error messages when dependencies are missing - Use environment variables for secrets — never hardcode API keys in configuration
- Monitor events via the manager's event system for operational visibility
- Group related tools into a single MCP server to reduce connection overhead