mirror of
https://github.com/openai/codex.git
synced 2026-09-15 12:08:01 +00:00
rm schema-only structs; use RawMcpServerConfig instead
This commit is contained in:
@@ -690,7 +690,6 @@ pub fn set_default_oss_provider(codex_home: &Path, provider: &str) -> std::io::R
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
pub struct ConfigToml {
|
||||
pub test: Option<String>,
|
||||
/// Optional override of model selection.
|
||||
pub model: Option<String>,
|
||||
/// Review model override used by the `/review` feature.
|
||||
@@ -748,6 +747,7 @@ pub struct ConfigToml {
|
||||
|
||||
/// Definition for MCP servers that Codex can reach out to for tool calls.
|
||||
#[serde(default)]
|
||||
// Uses the raw MCP input shape (custom deserialization) rather than `McpServerConfig`.
|
||||
#[schemars(schema_with = "crate::config::schema::mcp_servers_schema")]
|
||||
pub mcp_servers: HashMap<String, McpServerConfig>,
|
||||
|
||||
@@ -816,6 +816,7 @@ pub struct ConfigToml {
|
||||
|
||||
/// Centralized feature flags (new). Prefer this over individual toggles.
|
||||
#[serde(default)]
|
||||
// Injects known feature keys into the schema and forbids unknown keys.
|
||||
#[schemars(schema_with = "crate::config::schema::features_schema")]
|
||||
pub features: Option<FeaturesToml>,
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ pub struct ConfigProfile {
|
||||
pub analytics: Option<crate::config::types::AnalyticsConfigToml>,
|
||||
/// Optional feature toggles scoped to this profile.
|
||||
#[serde(default)]
|
||||
// Injects known feature keys into the schema and forbids unknown keys.
|
||||
#[schemars(schema_with = "crate::config::schema::features_schema")]
|
||||
pub features: Option<crate::features::FeaturesToml>,
|
||||
pub oss_provider: Option<String>,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
use crate::config::ConfigToml;
|
||||
use crate::config::types::RawMcpServerConfig;
|
||||
use crate::features::FEATURES;
|
||||
use schemars::JsonSchema;
|
||||
use schemars::r#gen::SchemaGenerator;
|
||||
#[cfg(test)]
|
||||
use schemars::r#gen::SchemaSettings;
|
||||
@@ -11,13 +11,10 @@ use schemars::schema::ObjectValidation;
|
||||
use schemars::schema::RootSchema;
|
||||
use schemars::schema::Schema;
|
||||
use schemars::schema::SchemaObject;
|
||||
use schemars::schema::SubschemaValidation;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(test)]
|
||||
use std::path::Path;
|
||||
|
||||
/// Build the config schema used by the fixture test.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn config_schema() -> RootSchema {
|
||||
SchemaSettings::draft07()
|
||||
@@ -28,6 +25,7 @@ pub(crate) fn config_schema() -> RootSchema {
|
||||
.into_root_schema_for::<ConfigToml>()
|
||||
}
|
||||
|
||||
/// Write the config schema fixture to disk.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn write_config_schema(out_path: &Path) -> anyhow::Result<()> {
|
||||
let schema = config_schema();
|
||||
@@ -36,6 +34,7 @@ pub(crate) fn write_config_schema(out_path: &Path) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Schema for the `[features]` map with known keys only.
|
||||
pub(crate) fn features_schema(schema_gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut object = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
@@ -54,6 +53,7 @@ pub(crate) fn features_schema(schema_gen: &mut SchemaGenerator) -> Schema {
|
||||
Schema::Object(object)
|
||||
}
|
||||
|
||||
/// Schema for the `[mcp_servers]` map using the raw input shape.
|
||||
pub(crate) fn mcp_servers_schema(schema_gen: &mut SchemaGenerator) -> Schema {
|
||||
let mut object = SchemaObject {
|
||||
instance_type: Some(InstanceType::Object.into()),
|
||||
@@ -61,7 +61,7 @@ pub(crate) fn mcp_servers_schema(schema_gen: &mut SchemaGenerator) -> Schema {
|
||||
};
|
||||
|
||||
let validation = ObjectValidation {
|
||||
additional_properties: Some(Box::new(mcp_server_schema(schema_gen))),
|
||||
additional_properties: Some(Box::new(schema_gen.subschema_for::<RawMcpServerConfig>())),
|
||||
..Default::default()
|
||||
};
|
||||
object.object = Some(Box::new(validation));
|
||||
@@ -69,70 +69,6 @@ pub(crate) fn mcp_servers_schema(schema_gen: &mut SchemaGenerator) -> Schema {
|
||||
Schema::Object(object)
|
||||
}
|
||||
|
||||
fn mcp_server_schema(schema_gen: &mut SchemaGenerator) -> Schema {
|
||||
let server = SchemaObject {
|
||||
subschemas: Some(Box::new(SubschemaValidation {
|
||||
one_of: Some(vec![
|
||||
schema_gen.subschema_for::<McpServerStdioSchema>(),
|
||||
schema_gen.subschema_for::<McpServerStreamableHttpSchema>(),
|
||||
]),
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
};
|
||||
Schema::Object(server)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
struct McpServerStdioSchema {
|
||||
command: String,
|
||||
#[serde(default)]
|
||||
args: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
env: Option<HashMap<String, String>>,
|
||||
#[serde(default)]
|
||||
env_vars: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
cwd: Option<String>,
|
||||
#[serde(default)]
|
||||
enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
startup_timeout_sec: Option<f64>,
|
||||
#[serde(default)]
|
||||
startup_timeout_ms: Option<u64>,
|
||||
#[serde(default)]
|
||||
tool_timeout_sec: Option<f64>,
|
||||
#[serde(default)]
|
||||
enabled_tools: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
disabled_tools: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
struct McpServerStreamableHttpSchema {
|
||||
url: String,
|
||||
#[serde(default)]
|
||||
bearer_token_env_var: Option<String>,
|
||||
#[serde(default)]
|
||||
http_headers: Option<HashMap<String, String>>,
|
||||
#[serde(default)]
|
||||
env_http_headers: Option<HashMap<String, String>>,
|
||||
#[serde(default)]
|
||||
enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
startup_timeout_sec: Option<f64>,
|
||||
#[serde(default)]
|
||||
startup_timeout_ms: Option<u64>,
|
||||
#[serde(default)]
|
||||
tool_timeout_sec: Option<f64>,
|
||||
#[serde(default)]
|
||||
enabled_tools: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
disabled_tools: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -49,47 +49,51 @@ pub struct McpServerConfig {
|
||||
pub disabled_tools: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
// Raw MCP config shape used for deserialization and JSON Schema generation.
|
||||
// Keep this in sync with the validation logic in `McpServerConfig`.
|
||||
#[derive(Deserialize, Clone, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
pub(crate) struct RawMcpServerConfig {
|
||||
// stdio
|
||||
pub command: Option<String>,
|
||||
#[serde(default)]
|
||||
pub args: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub env: Option<HashMap<String, String>>,
|
||||
#[serde(default)]
|
||||
pub env_vars: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub cwd: Option<PathBuf>,
|
||||
pub http_headers: Option<HashMap<String, String>>,
|
||||
#[serde(default)]
|
||||
pub env_http_headers: Option<HashMap<String, String>>,
|
||||
|
||||
// streamable_http
|
||||
pub url: Option<String>,
|
||||
pub bearer_token: Option<String>,
|
||||
pub bearer_token_env_var: Option<String>,
|
||||
|
||||
// shared
|
||||
#[serde(default)]
|
||||
pub startup_timeout_sec: Option<f64>,
|
||||
#[serde(default)]
|
||||
pub startup_timeout_ms: Option<u64>,
|
||||
#[serde(default, with = "option_duration_secs")]
|
||||
#[schemars(with = "Option<f64>")]
|
||||
pub tool_timeout_sec: Option<Duration>,
|
||||
#[serde(default)]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub enabled_tools: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub disabled_tools: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for McpServerConfig {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize, Clone)]
|
||||
struct RawMcpServerConfig {
|
||||
// stdio
|
||||
command: Option<String>,
|
||||
#[serde(default)]
|
||||
args: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
env: Option<HashMap<String, String>>,
|
||||
#[serde(default)]
|
||||
env_vars: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
cwd: Option<PathBuf>,
|
||||
http_headers: Option<HashMap<String, String>>,
|
||||
#[serde(default)]
|
||||
env_http_headers: Option<HashMap<String, String>>,
|
||||
|
||||
// streamable_http
|
||||
url: Option<String>,
|
||||
bearer_token: Option<String>,
|
||||
bearer_token_env_var: Option<String>,
|
||||
|
||||
// shared
|
||||
#[serde(default)]
|
||||
startup_timeout_sec: Option<f64>,
|
||||
#[serde(default)]
|
||||
startup_timeout_ms: Option<u64>,
|
||||
#[serde(default, with = "option_duration_secs")]
|
||||
tool_timeout_sec: Option<Duration>,
|
||||
#[serde(default)]
|
||||
enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
enabled_tools: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
disabled_tools: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
let mut raw = RawMcpServerConfig::deserialize(deserializer)?;
|
||||
|
||||
let startup_timeout_sec = match (raw.startup_timeout_sec, raw.startup_timeout_ms) {
|
||||
|
||||
@@ -182,14 +182,7 @@
|
||||
"default": {},
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/McpServerStdioSchema"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/McpServerStreamableHttpSchema"
|
||||
}
|
||||
]
|
||||
"$ref": "#/definitions/RawMcpServerConfig"
|
||||
}
|
||||
},
|
||||
"model": {
|
||||
@@ -626,142 +619,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"McpServerStdioSchema": {
|
||||
"description": "Schema-only representations of MCP server configs.\n\nKeep these in sync with the custom MCP config parsing in `crate::config::types::McpServerConfig`.",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"command"
|
||||
],
|
||||
"properties": {
|
||||
"args": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"cwd": {
|
||||
"default": null,
|
||||
"type": "string"
|
||||
},
|
||||
"disabled_tools": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"enabled": {
|
||||
"default": null,
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled_tools": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"default": null,
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"env_vars": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"startup_timeout_ms": {
|
||||
"default": null,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"startup_timeout_sec": {
|
||||
"default": null,
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"tool_timeout_sec": {
|
||||
"default": null,
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"McpServerStreamableHttpSchema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"url"
|
||||
],
|
||||
"properties": {
|
||||
"bearer_token_env_var": {
|
||||
"default": null,
|
||||
"type": "string"
|
||||
},
|
||||
"disabled_tools": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"enabled": {
|
||||
"default": null,
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled_tools": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"env_http_headers": {
|
||||
"default": null,
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"http_headers": {
|
||||
"default": null,
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"startup_timeout_ms": {
|
||||
"default": null,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"startup_timeout_sec": {
|
||||
"default": null,
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"tool_timeout_sec": {
|
||||
"default": null,
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ModelProviderInfo": {
|
||||
"description": "Serializable representation of a provider definition.",
|
||||
"type": "object",
|
||||
@@ -1080,6 +937,96 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"RawMcpServerConfig": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"args": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"bearer_token": {
|
||||
"type": "string"
|
||||
},
|
||||
"bearer_token_env_var": {
|
||||
"type": "string"
|
||||
},
|
||||
"command": {
|
||||
"type": "string"
|
||||
},
|
||||
"cwd": {
|
||||
"default": null,
|
||||
"type": "string"
|
||||
},
|
||||
"disabled_tools": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"enabled": {
|
||||
"default": null,
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled_tools": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"env": {
|
||||
"default": null,
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"env_http_headers": {
|
||||
"default": null,
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"env_vars": {
|
||||
"default": null,
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"http_headers": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"startup_timeout_ms": {
|
||||
"default": null,
|
||||
"type": "integer",
|
||||
"format": "uint64",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"startup_timeout_sec": {
|
||||
"default": null,
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"tool_timeout_sec": {
|
||||
"default": null,
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
"url": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"ReasoningEffort": {
|
||||
"description": "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning",
|
||||
"type": "string",
|
||||
|
||||
Reference in New Issue
Block a user