Skip to content

Commit 1bbc1a7

Browse files
Copilot0xrinegade
andcommitted
Add comprehensive MCP server search functionality
- Add 'search' subcommand to MCP CLI with multiple filter options - Implement search_servers method in MCP service with pattern matching - Support searching by name, URL, GitHub URL, and authentication type - Add transport type filtering (http, stdio, websocket, any) - Add enabled-only filtering option - Support both human-readable and JSON output formats - Comprehensive search across all server metadata and configuration - Enhanced display with status icons, transport icons, and auth badges - Integrated into existing MCP command structure with proper error handling Co-authored-by: 0xrinegade <[email protected]>
1 parent 75af989 commit 1bbc1a7

File tree

3 files changed

+145
-0
lines changed

3 files changed

+145
-0
lines changed

src/clparse.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,6 +1048,36 @@ pub fn parse_command_line() -> clap::ArgMatches {
10481048
.help("Automatically enable the server after setup")
10491049
)
10501050
)
1051+
.subcommand(
1052+
Command::new("search")
1053+
.about("Search for MCP servers by name, description, or features")
1054+
.arg(
1055+
Arg::new("query")
1056+
.help("Search query (searches name, description, and features)")
1057+
.required(true)
1058+
.index(1)
1059+
)
1060+
.arg(
1061+
Arg::new("transport")
1062+
.long("transport")
1063+
.value_name("TYPE")
1064+
.value_parser(clap::builder::PossibleValuesParser::new(["http", "stdio", "any"]))
1065+
.default_value("any")
1066+
.help("Filter by transport type")
1067+
)
1068+
.arg(
1069+
Arg::new("enabled_only")
1070+
.long("enabled-only")
1071+
.action(ArgAction::SetTrue)
1072+
.help("Only show enabled servers")
1073+
)
1074+
.arg(
1075+
Arg::new("json")
1076+
.long("json")
1077+
.action(ArgAction::SetTrue)
1078+
.help("Output results in JSON format")
1079+
)
1080+
)
10511081
)
10521082
.subcommand(
10531083
Command::new("audit")

src/main.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,73 @@ async fn handle_mcp_command(
662662
}
663663
}
664664

665+
"search" => {
666+
let query = mcp_sub_matches.get_one::<String>("query")
667+
.expect("query is required by clap");
668+
let transport = mcp_sub_matches.get_one::<String>("transport");
669+
let enabled_only = mcp_sub_matches.get_flag("enabled_only");
670+
let json_output = mcp_sub_matches.get_flag("json");
671+
672+
let transport_filter = transport.map(|t| if t == "any" { None } else { Some(t.as_str()) }).flatten();
673+
let results = mcp_service.search_servers(query, transport_filter, enabled_only);
674+
675+
if results.is_empty() {
676+
println!("🔍 No MCP servers found matching query: '{}'", query);
677+
if enabled_only {
678+
println!("💡 Try removing the --enabled-only filter to see all servers");
679+
}
680+
if transport_filter.is_some() {
681+
println!("💡 Try changing the transport filter or use --transport=any");
682+
}
683+
} else {
684+
if json_output {
685+
let json_results: Vec<serde_json::Value> = results.iter().map(|(id, config)| {
686+
serde_json::json!({
687+
"id": id,
688+
"name": config.name,
689+
"url": config.url,
690+
"transport": match config.transport_type {
691+
McpTransportType::Http => "http",
692+
McpTransportType::Websocket => "websocket",
693+
McpTransportType::Stdio => "stdio"
694+
},
695+
"enabled": config.enabled,
696+
"has_auth": config.auth.is_some(),
697+
"github_url": config.github_url
698+
})
699+
}).collect();
700+
println!("{}", serde_json::to_string_pretty(&json_results)?);
701+
} else {
702+
println!("🔍 Found {} MCP server(s) matching '{}:'", results.len(), query);
703+
println!();
704+
705+
for (id, config) in results {
706+
let status_icon = if config.enabled { "🟢" } else { "🔴" };
707+
let transport_icon = match config.transport_type {
708+
McpTransportType::Http => "🌐",
709+
McpTransportType::Websocket => "🔗",
710+
McpTransportType::Stdio => "⚡",
711+
};
712+
let auth_badge = if config.auth.is_some() { " 🔐" } else { "" };
713+
714+
println!(" {} {} {} {}{}", status_icon, transport_icon, id, config.name, auth_badge);
715+
println!(" URL: {}", config.url);
716+
717+
if let Some(github_url) = &config.github_url {
718+
println!(" GitHub: {}", github_url);
719+
}
720+
721+
if let Some(local_path) = &config.local_path {
722+
println!(" Local: {}", local_path);
723+
}
724+
725+
println!(" Transport: {:?}", config.transport_type);
726+
println!();
727+
}
728+
}
729+
}
730+
}
731+
665732
_ => {
666733
eprintln!("Unknown MCP subcommand: {}", mcp_sub_command);
667734
std::process::exit(1);

src/services/mcp_service.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,54 @@ impl McpService {
10921092

10931093
Ok(())
10941094
}
1095+
1096+
/// Search for MCP servers by name, description, or features
1097+
pub fn search_servers(
1098+
&self,
1099+
query: &str,
1100+
transport_filter: Option<&str>,
1101+
enabled_only: bool,
1102+
) -> Vec<(&String, &McpServerConfig)> {
1103+
let query_lower = query.to_lowercase();
1104+
1105+
self.servers
1106+
.iter()
1107+
.filter(|(_, config)| {
1108+
// Filter by enabled status if requested
1109+
if enabled_only && !config.enabled {
1110+
return false;
1111+
}
1112+
1113+
// Filter by transport type if specified
1114+
if let Some(transport) = transport_filter {
1115+
if transport != "any" {
1116+
let config_transport = match config.transport_type {
1117+
McpTransportType::Http => "http",
1118+
McpTransportType::Websocket => "websocket",
1119+
McpTransportType::Stdio => "stdio",
1120+
};
1121+
if config_transport != transport {
1122+
return false;
1123+
}
1124+
}
1125+
}
1126+
1127+
// Search in name, URL, and GitHub URL
1128+
let name_matches = config.name.to_lowercase().contains(&query_lower);
1129+
let url_matches = config.url.to_lowercase().contains(&query_lower);
1130+
let github_matches = config.github_url.as_ref()
1131+
.map(|url| url.to_lowercase().contains(&query_lower))
1132+
.unwrap_or(false);
1133+
1134+
// Also search in auth type if configured
1135+
let auth_matches = config.auth.as_ref()
1136+
.map(|auth| auth.auth_type.to_lowercase().contains(&query_lower))
1137+
.unwrap_or(false);
1138+
1139+
name_matches || url_matches || github_matches || auth_matches
1140+
})
1141+
.collect()
1142+
}
10951143
}
10961144

10971145
impl Default for McpService {

0 commit comments

Comments
 (0)