mirror of
https://github.com/openai/codex.git
synced 2026-11-06 20:20:34 +00:00
codex impl
This commit is contained in:
@@ -78,6 +78,9 @@ macro_rules! for_each_schema_type {
|
||||
$macro!(crate::LoginChatGptResponse);
|
||||
$macro!(crate::LogoutChatGptParams);
|
||||
$macro!(crate::LogoutChatGptResponse);
|
||||
$macro!(crate::McpOAuthCredentialsStoreMode);
|
||||
$macro!(crate::McpServerConfig);
|
||||
$macro!(crate::McpServerTransportConfig);
|
||||
$macro!(crate::NewConversationParams);
|
||||
$macro!(crate::NewConversationResponse);
|
||||
$macro!(crate::Profile);
|
||||
|
||||
@@ -134,13 +134,15 @@ client_request_definitions! {
|
||||
#[ts(rename = "config/read")]
|
||||
GetConfig {
|
||||
params: #[ts(type = "undefined")] #[serde(skip_serializing_if = "Option::is_none")] Option<()>,
|
||||
response: Config,
|
||||
response: UserSavedConfig,
|
||||
},
|
||||
|
||||
#[serde(rename = "config/update")]
|
||||
#[ts(rename = "config/update")]
|
||||
UpdateConfig {
|
||||
params: UpdateConfigParams,
|
||||
// TODO(owen): or should we return UserSavedConfig directly?
|
||||
// First, figure out how we want to represent errors.
|
||||
response: UpdateConfigResponse,
|
||||
},
|
||||
|
||||
@@ -426,13 +428,13 @@ pub struct LogoutAccountResponse {}
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateConfigParams {
|
||||
pub config: Config,
|
||||
pub config: UserSavedConfig,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateConfigResponse {
|
||||
pub config: Config,
|
||||
pub config: UserSavedConfig,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
@@ -652,6 +654,12 @@ pub struct UserSavedConfig {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tools: Option<Tools>,
|
||||
|
||||
/// MCP servers
|
||||
#[serde(default)]
|
||||
pub mcp_servers: HashMap<String, McpServerConfig>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub mcp_oauth_credentials_store: Option<McpOAuthCredentialsStoreMode>,
|
||||
|
||||
/// Profiles
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub profile: Option<String>,
|
||||
@@ -673,6 +681,58 @@ pub struct Profile {
|
||||
pub model_verbosity: Option<Verbosity>,
|
||||
pub chatgpt_base_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct McpServerConfig {
|
||||
pub transport: McpServerTransportConfig,
|
||||
#[serde(default)]
|
||||
pub enabled: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub startup_timeout_sec: Option<f64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tool_timeout_sec: Option<f64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub enabled_tools: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub disabled_tools: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||
pub enum McpServerTransportConfig {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
Stdio {
|
||||
command: String,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
args: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
env: Option<HashMap<String, String>>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
env_vars: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
cwd: Option<PathBuf>,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
StreamableHttp {
|
||||
url: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
bearer_token_env_var: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
http_headers: Option<HashMap<String, String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
env_http_headers: Option<HashMap<String, String>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS, Default)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum McpOAuthCredentialsStoreMode {
|
||||
#[default]
|
||||
Auto,
|
||||
File,
|
||||
Keyring,
|
||||
}
|
||||
/// MCP representation of a [`codex_core::config::ToolsToml`].
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Serialize, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
||||
@@ -5,6 +5,9 @@ use app_test_support::McpProcess;
|
||||
use app_test_support::to_response;
|
||||
use codex_app_server_protocol::GetUserSavedConfigResponse;
|
||||
use codex_app_server_protocol::JSONRPCResponse;
|
||||
use codex_app_server_protocol::McpOAuthCredentialsStoreMode;
|
||||
use codex_app_server_protocol::McpServerConfig as ProtocolMcpServerConfig;
|
||||
use codex_app_server_protocol::McpServerTransportConfig as ProtocolMcpServerTransportConfig;
|
||||
use codex_app_server_protocol::Profile;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_app_server_protocol::SandboxSettings;
|
||||
@@ -36,6 +39,7 @@ model_verbosity = "medium"
|
||||
profile = "test"
|
||||
forced_chatgpt_workspace_id = "12345678-0000-0000-0000-000000000000"
|
||||
forced_login_method = "chatgpt"
|
||||
mcp_oauth_credentials_store = "keyring"
|
||||
|
||||
[sandbox_workspace_write]
|
||||
writable_roots = ["/tmp"]
|
||||
@@ -55,6 +59,32 @@ model_reasoning_summary = "detailed"
|
||||
model_verbosity = "medium"
|
||||
model_provider = "openai"
|
||||
chatgpt_base_url = "https://api.chatgpt.com"
|
||||
|
||||
[mcp_servers.docs]
|
||||
command = "codex-docs"
|
||||
args = ["serve"]
|
||||
env_vars = ["DOCS_TOKEN"]
|
||||
cwd = "/tmp/docs"
|
||||
startup_timeout_sec = 12.5
|
||||
tool_timeout_sec = 42.0
|
||||
enabled = false
|
||||
enabled_tools = ["read_docs"]
|
||||
disabled_tools = ["delete_docs"]
|
||||
|
||||
[mcp_servers.docs.env]
|
||||
PLAN = "gold"
|
||||
|
||||
[mcp_servers.issues]
|
||||
url = "https://example.com/mcp"
|
||||
bearer_token_env_var = "MCP_TOKEN"
|
||||
startup_timeout_sec = 30.0
|
||||
tool_timeout_sec = 15.0
|
||||
|
||||
[mcp_servers.issues.http_headers]
|
||||
"X-Test" = "42"
|
||||
|
||||
[mcp_servers.issues.env_http_headers]
|
||||
"X-Token" = "TOKEN_ENV"
|
||||
"#,
|
||||
)
|
||||
}
|
||||
@@ -105,6 +135,45 @@ async fn get_config_toml_parses_all_fields() {
|
||||
web_search: Some(false),
|
||||
view_image: Some(true),
|
||||
}),
|
||||
mcp_servers: HashMap::from([
|
||||
(
|
||||
"docs".into(),
|
||||
ProtocolMcpServerConfig {
|
||||
transport: ProtocolMcpServerTransportConfig::Stdio {
|
||||
command: "codex-docs".into(),
|
||||
args: vec!["serve".into()],
|
||||
env: Some(HashMap::from([("PLAN".into(), "gold".into())])),
|
||||
env_vars: vec!["DOCS_TOKEN".into()],
|
||||
cwd: Some("/tmp/docs".into()),
|
||||
},
|
||||
enabled: false,
|
||||
startup_timeout_sec: Some(12.5),
|
||||
tool_timeout_sec: Some(42.0),
|
||||
enabled_tools: Some(vec!["read_docs".into()]),
|
||||
disabled_tools: Some(vec!["delete_docs".into()]),
|
||||
},
|
||||
),
|
||||
(
|
||||
"issues".into(),
|
||||
ProtocolMcpServerConfig {
|
||||
transport: ProtocolMcpServerTransportConfig::StreamableHttp {
|
||||
url: "https://example.com/mcp".into(),
|
||||
bearer_token_env_var: Some("MCP_TOKEN".into()),
|
||||
http_headers: Some(HashMap::from([("X-Test".into(), "42".into())])),
|
||||
env_http_headers: Some(HashMap::from([(
|
||||
"X-Token".into(),
|
||||
"TOKEN_ENV".into(),
|
||||
)])),
|
||||
},
|
||||
enabled: true,
|
||||
startup_timeout_sec: Some(30.0),
|
||||
tool_timeout_sec: Some(15.0),
|
||||
enabled_tools: None,
|
||||
disabled_tools: None,
|
||||
},
|
||||
),
|
||||
]),
|
||||
mcp_oauth_credentials_store: Some(McpOAuthCredentialsStoreMode::Keyring),
|
||||
profile: Some("test".to_string()),
|
||||
profiles: HashMap::from([(
|
||||
"test".into(),
|
||||
@@ -161,6 +230,8 @@ async fn get_config_toml_empty() {
|
||||
model_reasoning_summary: None,
|
||||
model_verbosity: None,
|
||||
tools: None,
|
||||
mcp_servers: HashMap::new(),
|
||||
mcp_oauth_credentials_store: None,
|
||||
profile: None,
|
||||
profiles: HashMap::new(),
|
||||
},
|
||||
|
||||
@@ -967,6 +967,11 @@ impl From<ConfigToml> for UserSavedConfig {
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect();
|
||||
let mcp_servers = config_toml
|
||||
.mcp_servers
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.into()))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
approval_policy: config_toml.approval_policy,
|
||||
@@ -979,12 +984,24 @@ impl From<ConfigToml> for UserSavedConfig {
|
||||
model_reasoning_summary: config_toml.model_reasoning_summary,
|
||||
model_verbosity: config_toml.model_verbosity,
|
||||
tools: config_toml.tools.map(From::from),
|
||||
mcp_servers,
|
||||
mcp_oauth_credentials_store: config_toml.mcp_oauth_credentials_store.map(From::from),
|
||||
profile: config_toml.profile,
|
||||
profiles,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OAuthCredentialsStoreMode> for codex_app_server_protocol::McpOAuthCredentialsStoreMode {
|
||||
fn from(mode: OAuthCredentialsStoreMode) -> Self {
|
||||
match mode {
|
||||
OAuthCredentialsStoreMode::Auto => Self::Auto,
|
||||
OAuthCredentialsStoreMode::File => Self::File,
|
||||
OAuthCredentialsStoreMode::Keyring => Self::Keyring,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ProjectConfig {
|
||||
pub trust_level: Option<String>,
|
||||
|
||||
@@ -388,6 +388,52 @@ impl From<SandboxWorkspaceWrite> for codex_app_server_protocol::SandboxSettings
|
||||
}
|
||||
}
|
||||
|
||||
impl From<McpServerConfig> for codex_app_server_protocol::McpServerConfig {
|
||||
fn from(cfg: McpServerConfig) -> Self {
|
||||
Self {
|
||||
transport: cfg.transport.into(),
|
||||
enabled: cfg.enabled,
|
||||
startup_timeout_sec: cfg
|
||||
.startup_timeout_sec
|
||||
.map(|duration| duration.as_secs_f64()),
|
||||
tool_timeout_sec: cfg.tool_timeout_sec.map(|duration| duration.as_secs_f64()),
|
||||
enabled_tools: cfg.enabled_tools,
|
||||
disabled_tools: cfg.disabled_tools,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<McpServerTransportConfig> for codex_app_server_protocol::McpServerTransportConfig {
|
||||
fn from(cfg: McpServerTransportConfig) -> Self {
|
||||
match cfg {
|
||||
McpServerTransportConfig::Stdio {
|
||||
command,
|
||||
args,
|
||||
env,
|
||||
env_vars,
|
||||
cwd,
|
||||
} => Self::Stdio {
|
||||
command,
|
||||
args,
|
||||
env,
|
||||
env_vars,
|
||||
cwd,
|
||||
},
|
||||
McpServerTransportConfig::StreamableHttp {
|
||||
url,
|
||||
bearer_token_env_var,
|
||||
http_headers,
|
||||
env_http_headers,
|
||||
} => Self::StreamableHttp {
|
||||
url,
|
||||
bearer_token_env_var,
|
||||
http_headers,
|
||||
env_http_headers,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, PartialEq, Default)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum ShellEnvironmentPolicyInherit {
|
||||
|
||||
Reference in New Issue
Block a user