Skip to content

Commit 960a9e6

Browse files
committed
makes type field in mcp config optional
1 parent 26fd1aa commit 960a9e6

File tree

1 file changed

+66
-5
lines changed

1 file changed

+66
-5
lines changed

crates/agent/src/agent/agent_config/definitions.rs

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ pub struct McpServers {
215215
pub mcp_servers: HashMap<String, McpServerConfig>,
216216
}
217217

218-
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
218+
#[derive(Debug, Clone, Serialize, JsonSchema)]
219219
#[serde(tag = "type")]
220220
pub enum McpServerConfig {
221221
#[serde(rename = "stdio")]
@@ -224,6 +224,50 @@ pub enum McpServerConfig {
224224
Remote(RemoteMcpServerConfig),
225225
}
226226

227+
impl<'de> Deserialize<'de> for McpServerConfig {
228+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
229+
where
230+
D: serde::Deserializer<'de>,
231+
{
232+
use serde::de::Error;
233+
234+
// Helper enum with derived Deserialize to avoid infinite recursion
235+
#[derive(Deserialize)]
236+
#[serde(tag = "type")]
237+
enum McpServerConfigHelper {
238+
#[serde(rename = "stdio")]
239+
Local(LocalMcpServerConfig),
240+
#[serde(rename = "http")]
241+
Remote(RemoteMcpServerConfig),
242+
}
243+
244+
let value = serde_json::Value::deserialize(deserializer)?;
245+
246+
// Check if "type" field exists
247+
if let Some(obj) = value.as_object() {
248+
if !obj.contains_key("type") {
249+
// If "type" is missing, default to "stdio" by adding it
250+
let mut obj = obj.clone();
251+
obj.insert("type".to_string(), serde_json::Value::String("stdio".to_string()));
252+
let value_with_type = serde_json::Value::Object(obj);
253+
let helper: McpServerConfigHelper =
254+
serde_json::from_value(value_with_type).map_err(D::Error::custom)?;
255+
return Ok(match helper {
256+
McpServerConfigHelper::Local(config) => McpServerConfig::Local(config),
257+
McpServerConfigHelper::Remote(config) => McpServerConfig::Remote(config),
258+
});
259+
}
260+
}
261+
262+
// Normal deserialization with type field present
263+
let helper: McpServerConfigHelper = serde_json::from_value(value).map_err(D::Error::custom)?;
264+
Ok(match helper {
265+
McpServerConfigHelper::Local(config) => McpServerConfig::Local(config),
266+
McpServerConfigHelper::Remote(config) => McpServerConfig::Remote(config),
267+
})
268+
}
269+
}
270+
227271
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
228272
#[serde(rename_all = "camelCase")]
229273
pub struct LocalMcpServerConfig {
@@ -420,7 +464,7 @@ mod tests {
420464
assert_eq!(remote.url, "https://mcp.api.coingecko.com/sse");
421465
assert!(remote.oauth_scopes.is_empty());
422466
},
423-
_ => panic!("Expected Remote variant"),
467+
McpServerConfig::Local(_) => panic!("Expected Remote variant"),
424468
}
425469

426470
// Test HTTP server with oauth scopes
@@ -435,7 +479,7 @@ mod tests {
435479
assert_eq!(remote.url, "https://mcp.datadoghq.com/api/unstable/mcp-server/mcp");
436480
assert_eq!(remote.oauth_scopes, vec!["mcp", "profile", "email"]);
437481
},
438-
_ => panic!("Expected Remote variant"),
482+
McpServerConfig::Local(_) => panic!("Expected Remote variant"),
439483
}
440484

441485
// Test HTTP server with empty oauth scopes
@@ -450,7 +494,7 @@ mod tests {
450494
assert_eq!(remote.url, "https://example-server.modelcontextprotocol.io/mcp");
451495
assert!(remote.oauth_scopes.is_empty());
452496
},
453-
_ => panic!("Expected Remote variant"),
497+
McpServerConfig::Local(_) => panic!("Expected Remote variant"),
454498
}
455499
}
456500

@@ -467,7 +511,24 @@ mod tests {
467511
assert_eq!(local.command, "node");
468512
assert_eq!(local.args, vec!["server.js"]);
469513
},
470-
_ => panic!("Expected Local variant"),
514+
McpServerConfig::Remote(_) => panic!("Expected Local variant"),
515+
}
516+
}
517+
518+
#[test]
519+
fn test_mcp_server_config_defaults_to_stdio() {
520+
// Test that when "type" field is missing, it defaults to "stdio" (Local variant)
521+
let config = serde_json::json!({
522+
"command": "node",
523+
"args": ["server.js"]
524+
});
525+
let result: McpServerConfig = serde_json::from_value(config).unwrap();
526+
match result {
527+
McpServerConfig::Local(local) => {
528+
assert_eq!(local.command, "node");
529+
assert_eq!(local.args, vec!["server.js"]);
530+
},
531+
McpServerConfig::Remote(_) => panic!("Expected Local variant when type field is missing"),
471532
}
472533
}
473534

0 commit comments

Comments
 (0)