Skip to content

Commit 8d60496

Browse files
committed
makes type field in mcp config optional
1 parent 26fd1aa commit 8d60496

File tree

1 file changed

+61
-1
lines changed

1 file changed

+61
-1
lines changed

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

Lines changed: 61 additions & 1 deletion
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,49 @@ 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 = serde_json::from_value(value_with_type).map_err(D::Error::custom)?;
254+
return Ok(match helper {
255+
McpServerConfigHelper::Local(config) => McpServerConfig::Local(config),
256+
McpServerConfigHelper::Remote(config) => McpServerConfig::Remote(config),
257+
});
258+
}
259+
}
260+
261+
// Normal deserialization with type field present
262+
let helper: McpServerConfigHelper = serde_json::from_value(value).map_err(D::Error::custom)?;
263+
Ok(match helper {
264+
McpServerConfigHelper::Local(config) => McpServerConfig::Local(config),
265+
McpServerConfigHelper::Remote(config) => McpServerConfig::Remote(config),
266+
})
267+
}
268+
}
269+
227270
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
228271
#[serde(rename_all = "camelCase")]
229272
pub struct LocalMcpServerConfig {
@@ -471,6 +514,23 @@ mod tests {
471514
}
472515
}
473516

517+
#[test]
518+
fn test_mcp_server_config_defaults_to_stdio() {
519+
// Test that when "type" field is missing, it defaults to "stdio" (Local variant)
520+
let config = serde_json::json!({
521+
"command": "node",
522+
"args": ["server.js"]
523+
});
524+
let result: McpServerConfig = serde_json::from_value(config).unwrap();
525+
match result {
526+
McpServerConfig::Local(local) => {
527+
assert_eq!(local.command, "node");
528+
assert_eq!(local.args, vec!["server.js"]);
529+
},
530+
_ => panic!("Expected Local variant when type field is missing"),
531+
}
532+
}
533+
474534
#[test]
475535
fn test_mcp_servers_map_deser() {
476536
let servers = serde_json::json!({

0 commit comments

Comments
 (0)