-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
epic:config-parsingConfig file discovery, parsing, and data modelsConfig file discovery, parsing, and data modelsepic:rust-migrationRust + Iced migration infrastructureRust + Iced migration infrastructurephase:1Phase 1 — Read-only explorerPhase 1 — Read-only explorerpriority:highMust-have for MVPMust-have for MVPtype:migrationDirect port of existing Swift functionalityDirect port of existing Swift functionality
Description
Context
MCP servers have two transport types: stdio (command + args + env) and HTTP (type + url + headers). MCPConfig wraps a map of named servers, representing .mcp.json.
Ported from: Fig/Sources/Models/MCPServer.swift, Fig/Sources/Models/MCPConfig.swift
What to implement
File paths
fig-core/src/models/mcp_server.rsfig-core/src/models/mcp_config.rs
Rust struct definitions
// mcp_server.rs
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MCPServer {
// Stdio properties
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub args: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub env: Option<HashMap<String, String>>,
// HTTP properties
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "type")]
pub server_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<HashMap<String, String>>,
#[serde(flatten)]
pub additional_properties: HashMap<String, Value>,
}
impl MCPServer {
pub fn is_stdio(&self) -> bool {
self.command.is_some() && self.server_type.as_deref() != Some("http")
}
pub fn is_http(&self) -> bool {
self.server_type.as_deref() == Some("http") && self.url.is_some()
}
pub fn stdio(command: String, args: Option<Vec<String>>, env: Option<HashMap<String, String>>) -> Self {
Self { command: Some(command), args, env, ..Default::default() }
}
pub fn http(url: String, headers: Option<HashMap<String, String>>) -> Self {
Self { server_type: Some("http".to_string()), url: Some(url), headers, ..Default::default() }
}
/// Returns true if server has environment variables with potentially sensitive values
pub fn has_sensitive_env(&self) -> bool {
self.env.as_ref().map(|e| e.keys().any(|k| {
let lower = k.to_lowercase();
lower.contains("token") || lower.contains("key") || lower.contains("secret") || lower.contains("password")
})).unwrap_or(false)
}
}
// mcp_config.rs
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct MCPConfig {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "mcpServers")]
pub mcp_servers: Option<HashMap<String, MCPServer>>,
#[serde(flatten)]
pub additional_properties: HashMap<String, Value>,
}
impl MCPConfig {
pub fn server_names(&self) -> Vec<String> {
self.mcp_servers.as_ref()
.map(|s| s.keys().cloned().collect())
.unwrap_or_default()
}
pub fn server(&self, name: &str) -> Option<&MCPServer> {
self.mcp_servers.as_ref()?.get(name)
}
pub fn server_count(&self) -> usize {
self.mcp_servers.as_ref().map(|s| s.len()).unwrap_or(0)
}
}Acceptance criteria
- Both stdio and HTTP server configs parse correctly
- Factory methods
MCPServer::stdio()andMCPServer::http()work - Round-trip tests pass with unknown fields preserved
-
is_stdio()andis_http()detect transport type correctly
Test requirements
test_stdio_server_round_trip— with command, args, envtest_http_server_round_trip— with type, url, headerstest_mcp_config_multiple_serverstest_mcp_server_type_detection—is_stdio(),is_http()test_mcp_server_factory_methodstest_has_sensitive_env
Dependencies
Requires: #92
Blocks
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
epic:config-parsingConfig file discovery, parsing, and data modelsConfig file discovery, parsing, and data modelsepic:rust-migrationRust + Iced migration infrastructureRust + Iced migration infrastructurephase:1Phase 1 — Read-only explorerPhase 1 — Read-only explorerpriority:highMust-have for MVPMust-have for MVPtype:migrationDirect port of existing Swift functionalityDirect port of existing Swift functionality