fix: restore mcp flexibility and improve cli tooling
This commit is contained in:
@@ -4,10 +4,11 @@
|
||||
/// Supports switching between local (in-process) and remote (STDIO) execution modes.
|
||||
use super::client::McpClient;
|
||||
use super::{remote_client::RemoteMcpClient, LocalMcpClient};
|
||||
use crate::config::Config;
|
||||
use crate::config::{Config, McpMode};
|
||||
use crate::tools::registry::ToolRegistry;
|
||||
use crate::validation::SchemaValidator;
|
||||
use crate::Result;
|
||||
use crate::{Error, Result};
|
||||
use log::{info, warn};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Factory for creating MCP clients based on configuration
|
||||
@@ -30,30 +31,72 @@ impl McpClientFactory {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create an MCP client based on the current configuration
|
||||
///
|
||||
/// In v1.0+, MCP architecture is always enabled. If MCP servers are configured,
|
||||
/// uses the first server; otherwise falls back to local in-process client.
|
||||
/// Create an MCP client based on the current configuration.
|
||||
pub fn create(&self) -> Result<Box<dyn McpClient>> {
|
||||
// Use the first configured MCP server, if any.
|
||||
if let Some(server_cfg) = self.config.mcp_servers.first() {
|
||||
match RemoteMcpClient::new_with_config(server_cfg) {
|
||||
Ok(client) => Ok(Box::new(client)),
|
||||
Err(e) => {
|
||||
eprintln!("Warning: Failed to start remote MCP client '{}': {}. Falling back to local mode.", server_cfg.name, e);
|
||||
match self.config.mcp.mode {
|
||||
McpMode::Disabled => Err(Error::Config(
|
||||
"MCP mode is set to 'disabled'; tooling cannot function in this configuration."
|
||||
.to_string(),
|
||||
)),
|
||||
McpMode::LocalOnly | McpMode::Legacy => {
|
||||
if matches!(self.config.mcp.mode, McpMode::Legacy) {
|
||||
warn!("Using deprecated MCP legacy mode; consider switching to 'local_only'.");
|
||||
}
|
||||
Ok(Box::new(LocalMcpClient::new(
|
||||
self.registry.clone(),
|
||||
self.validator.clone(),
|
||||
)))
|
||||
}
|
||||
McpMode::RemoteOnly => {
|
||||
let server_cfg = self.config.mcp_servers.first().ok_or_else(|| {
|
||||
Error::Config(
|
||||
"MCP mode 'remote_only' requires at least one entry in [[mcp_servers]]"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
RemoteMcpClient::new_with_config(server_cfg)
|
||||
.map(|client| Box::new(client) as Box<dyn McpClient>)
|
||||
.map_err(|e| {
|
||||
Error::Config(format!(
|
||||
"Failed to start remote MCP client '{}': {e}",
|
||||
server_cfg.name
|
||||
))
|
||||
})
|
||||
}
|
||||
McpMode::RemotePreferred => {
|
||||
if let Some(server_cfg) = self.config.mcp_servers.first() {
|
||||
match RemoteMcpClient::new_with_config(server_cfg) {
|
||||
Ok(client) => {
|
||||
info!(
|
||||
"Connected to remote MCP server '{}' via {} transport.",
|
||||
server_cfg.name, server_cfg.transport
|
||||
);
|
||||
Ok(Box::new(client) as Box<dyn McpClient>)
|
||||
}
|
||||
Err(e) if self.config.mcp.allow_fallback => {
|
||||
warn!(
|
||||
"Failed to start remote MCP client '{}': {}. Falling back to local tooling.",
|
||||
server_cfg.name, e
|
||||
);
|
||||
Ok(Box::new(LocalMcpClient::new(
|
||||
self.registry.clone(),
|
||||
self.validator.clone(),
|
||||
)))
|
||||
}
|
||||
Err(e) => Err(Error::Config(format!(
|
||||
"Failed to start remote MCP client '{}': {e}. To allow fallback, set [mcp].allow_fallback = true.",
|
||||
server_cfg.name
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
warn!("No MCP servers configured; using local MCP tooling.");
|
||||
Ok(Box::new(LocalMcpClient::new(
|
||||
self.registry.clone(),
|
||||
self.validator.clone(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No servers configured – fall back to local client.
|
||||
eprintln!("Warning: No MCP servers defined in config. Using local client.");
|
||||
Ok(Box::new(LocalMcpClient::new(
|
||||
self.registry.clone(),
|
||||
self.validator.clone(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,11 +109,10 @@ impl McpClientFactory {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::config::McpServerConfig;
|
||||
use crate::Error;
|
||||
|
||||
#[test]
|
||||
fn test_factory_creates_local_client_when_no_servers_configured() {
|
||||
let config = Config::default();
|
||||
|
||||
fn build_factory(config: Config) -> McpClientFactory {
|
||||
let ui = Arc::new(crate::ui::NoOpUiController);
|
||||
let registry = Arc::new(ToolRegistry::new(
|
||||
Arc::new(tokio::sync::Mutex::new(config.clone())),
|
||||
@@ -78,10 +120,58 @@ mod tests {
|
||||
));
|
||||
let validator = Arc::new(SchemaValidator::new());
|
||||
|
||||
let factory = McpClientFactory::new(Arc::new(config), registry, validator);
|
||||
McpClientFactory::new(Arc::new(config), registry, validator)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_factory_creates_local_client_when_no_servers_configured() {
|
||||
let config = Config::default();
|
||||
|
||||
let factory = build_factory(config);
|
||||
|
||||
// Should create without error and fall back to local client
|
||||
let result = factory.create();
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remote_only_without_servers_errors() {
|
||||
let mut config = Config::default();
|
||||
config.mcp.mode = McpMode::RemoteOnly;
|
||||
config.mcp_servers.clear();
|
||||
|
||||
let factory = build_factory(config);
|
||||
let result = factory.create();
|
||||
assert!(matches!(result, Err(Error::Config(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remote_preferred_without_fallback_propagates_remote_error() {
|
||||
let mut config = Config::default();
|
||||
config.mcp.mode = McpMode::RemotePreferred;
|
||||
config.mcp.allow_fallback = false;
|
||||
config.mcp_servers = vec![McpServerConfig {
|
||||
name: "invalid".to_string(),
|
||||
command: "nonexistent-mcp-server-binary".to_string(),
|
||||
args: Vec::new(),
|
||||
transport: "stdio".to_string(),
|
||||
env: std::collections::HashMap::new(),
|
||||
}];
|
||||
|
||||
let factory = build_factory(config);
|
||||
let result = factory.create();
|
||||
assert!(
|
||||
matches!(result, Err(Error::Config(message)) if message.contains("Failed to start remote MCP client"))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_legacy_mode_uses_local_client() {
|
||||
let mut config = Config::default();
|
||||
config.mcp.mode = McpMode::Legacy;
|
||||
|
||||
let factory = build_factory(config);
|
||||
let result = factory.create();
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user