mirror of
https://github.com/openai/codex.git
synced 2026-05-03 04:42:20 +03:00
feat: Support more plugin MCP file shapes. (#18780)
Update core-plugins MCP loading to accept either an mcpServers object or a top-level server map in .mcp.json
This commit is contained in:
@@ -67,11 +67,26 @@ pub fn log_plugin_load_errors(outcome: &PluginLoadOutcome<McpServerConfig>) {
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct PluginMcpFile {
|
||||
#[serde(default)]
|
||||
struct PluginMcpServersFile {
|
||||
mcp_servers: HashMap<String, JsonValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum PluginMcpFile {
|
||||
McpServersObject(PluginMcpServersFile),
|
||||
ServerMap(HashMap<String, JsonValue>),
|
||||
}
|
||||
|
||||
impl PluginMcpFile {
|
||||
fn into_mcp_servers(self) -> HashMap<String, JsonValue> {
|
||||
match self {
|
||||
Self::McpServersObject(file) => file.mcp_servers,
|
||||
Self::ServerMap(mcp_servers) => mcp_servers,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct PluginAppFile {
|
||||
@@ -775,7 +790,7 @@ async fn load_mcp_servers_from_file(
|
||||
};
|
||||
normalize_plugin_mcp_servers(
|
||||
plugin_root,
|
||||
parsed.mcp_servers,
|
||||
parsed.into_mcp_servers(),
|
||||
mcp_config_path.to_string_lossy().as_ref(),
|
||||
)
|
||||
}
|
||||
@@ -988,6 +1003,82 @@ mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn plugin_mcp_file_supports_mcp_servers_object_format() {
|
||||
let parsed = serde_json::from_str::<PluginMcpFile>(
|
||||
r#"{
|
||||
"mcpServers": {
|
||||
"sample": {
|
||||
"command": "sample-mcp"
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.expect("parse wrapped plugin mcp config")
|
||||
.into_mcp_servers();
|
||||
|
||||
assert_eq!(
|
||||
parsed,
|
||||
HashMap::from([(
|
||||
"sample".to_string(),
|
||||
serde_json::json!({
|
||||
"command": "sample-mcp"
|
||||
}),
|
||||
)])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_mcp_file_supports_mcp_servers_object_format_with_metadata() {
|
||||
let parsed = serde_json::from_str::<PluginMcpFile>(
|
||||
r#"{
|
||||
"$schema": "https://example.com/plugin-mcp.schema.json",
|
||||
"mcpServers": {
|
||||
"sample": {
|
||||
"command": "sample-mcp"
|
||||
}
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.expect("parse plugin mcp config with metadata")
|
||||
.into_mcp_servers();
|
||||
|
||||
assert_eq!(
|
||||
parsed,
|
||||
HashMap::from([(
|
||||
"sample".to_string(),
|
||||
serde_json::json!({
|
||||
"command": "sample-mcp"
|
||||
}),
|
||||
)])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_mcp_file_supports_top_level_server_map_format() {
|
||||
let parsed = serde_json::from_str::<PluginMcpFile>(
|
||||
r#"{
|
||||
"linear": {
|
||||
"type": "http",
|
||||
"url": "https://mcp.linear.app/mcp"
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.expect("parse flat plugin mcp config")
|
||||
.into_mcp_servers();
|
||||
|
||||
assert_eq!(
|
||||
parsed,
|
||||
HashMap::from([(
|
||||
"linear".to_string(),
|
||||
serde_json::json!({
|
||||
"type": "http",
|
||||
"url": "https://mcp.linear.app/mcp"
|
||||
}),
|
||||
)])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn materialize_git_subdir_uses_sparse_checkout() {
|
||||
let codex_home = tempfile::tempdir().expect("create codex home");
|
||||
|
||||
Reference in New Issue
Block a user