Skip to main content

codex/cli/
mcp.rs

1use crate::CliOverridesPatch;
2use serde_json::Value;
3use std::{ffi::OsString, process::ExitStatus};
4
5/// Request for `codex mcp` (overview/help).
6#[derive(Clone, Debug, Eq, PartialEq)]
7pub struct McpOverviewRequest {
8    pub overrides: CliOverridesPatch,
9}
10
11impl McpOverviewRequest {
12    pub fn new() -> Self {
13        Self {
14            overrides: CliOverridesPatch::default(),
15        }
16    }
17
18    pub fn with_overrides(mut self, overrides: CliOverridesPatch) -> Self {
19        self.overrides = overrides;
20        self
21    }
22}
23
24impl Default for McpOverviewRequest {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30/// Request for `codex mcp list`.
31#[derive(Clone, Debug, Eq, PartialEq)]
32pub struct McpListRequest {
33    pub json: bool,
34    pub overrides: CliOverridesPatch,
35}
36
37impl McpListRequest {
38    pub fn new() -> Self {
39        Self {
40            json: false,
41            overrides: CliOverridesPatch::default(),
42        }
43    }
44
45    pub fn json(mut self, enable: bool) -> Self {
46        self.json = enable;
47        self
48    }
49
50    pub fn with_overrides(mut self, overrides: CliOverridesPatch) -> Self {
51        self.overrides = overrides;
52        self
53    }
54}
55
56impl Default for McpListRequest {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62/// Output from `codex mcp list`.
63#[derive(Clone, Debug, PartialEq)]
64pub struct McpListOutput {
65    pub status: ExitStatus,
66    pub stdout: String,
67    pub stderr: String,
68    pub json: Option<Value>,
69}
70
71/// Request for `codex mcp get <NAME>`.
72#[derive(Clone, Debug, Eq, PartialEq)]
73pub struct McpGetRequest {
74    pub name: String,
75    pub json: bool,
76    pub overrides: CliOverridesPatch,
77}
78
79impl McpGetRequest {
80    pub fn new(name: impl Into<String>) -> Self {
81        Self {
82            name: name.into(),
83            json: false,
84            overrides: CliOverridesPatch::default(),
85        }
86    }
87
88    pub fn json(mut self, enable: bool) -> Self {
89        self.json = enable;
90        self
91    }
92
93    pub fn with_overrides(mut self, overrides: CliOverridesPatch) -> Self {
94        self.overrides = overrides;
95        self
96    }
97}
98
99/// Transport for `codex mcp add`.
100#[derive(Clone, Debug, Eq, PartialEq)]
101pub enum McpAddTransport {
102    Stdio {
103        env: Vec<(String, String)>,
104        command: Vec<OsString>,
105    },
106    StreamableHttp {
107        url: String,
108        bearer_token_env_var: Option<String>,
109    },
110}
111
112/// Request for `codex mcp add`.
113#[derive(Clone, Debug, Eq, PartialEq)]
114pub struct McpAddRequest {
115    pub name: String,
116    pub transport: McpAddTransport,
117    pub overrides: CliOverridesPatch,
118}
119
120impl McpAddRequest {
121    pub fn stdio(name: impl Into<String>, command: Vec<OsString>) -> Self {
122        Self {
123            name: name.into(),
124            transport: McpAddTransport::Stdio {
125                env: Vec::new(),
126                command,
127            },
128            overrides: CliOverridesPatch::default(),
129        }
130    }
131
132    pub fn streamable_http(name: impl Into<String>, url: impl Into<String>) -> Self {
133        Self {
134            name: name.into(),
135            transport: McpAddTransport::StreamableHttp {
136                url: url.into(),
137                bearer_token_env_var: None,
138            },
139            overrides: CliOverridesPatch::default(),
140        }
141    }
142
143    pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
144        if let McpAddTransport::Stdio { env, .. } = &mut self.transport {
145            env.push((key.into(), value.into()));
146        }
147        self
148    }
149
150    pub fn bearer_token_env_var(mut self, env_var: impl Into<String>) -> Self {
151        if let McpAddTransport::StreamableHttp {
152            bearer_token_env_var,
153            ..
154        } = &mut self.transport
155        {
156            let env_var = env_var.into();
157            *bearer_token_env_var = (!env_var.trim().is_empty()).then_some(env_var);
158        }
159        self
160    }
161
162    pub fn with_overrides(mut self, overrides: CliOverridesPatch) -> Self {
163        self.overrides = overrides;
164        self
165    }
166}
167
168/// Request for `codex mcp remove <NAME>`.
169#[derive(Clone, Debug, Eq, PartialEq)]
170pub struct McpRemoveRequest {
171    pub name: String,
172    pub overrides: CliOverridesPatch,
173}
174
175impl McpRemoveRequest {
176    pub fn new(name: impl Into<String>) -> Self {
177        Self {
178            name: name.into(),
179            overrides: CliOverridesPatch::default(),
180        }
181    }
182
183    pub fn with_overrides(mut self, overrides: CliOverridesPatch) -> Self {
184        self.overrides = overrides;
185        self
186    }
187}
188
189/// Request for `codex mcp logout <NAME>`.
190#[derive(Clone, Debug, Eq, PartialEq)]
191pub struct McpLogoutRequest {
192    pub name: String,
193    pub overrides: CliOverridesPatch,
194}
195
196impl McpLogoutRequest {
197    pub fn new(name: impl Into<String>) -> Self {
198        Self {
199            name: name.into(),
200            overrides: CliOverridesPatch::default(),
201        }
202    }
203
204    pub fn with_overrides(mut self, overrides: CliOverridesPatch) -> Self {
205        self.overrides = overrides;
206        self
207    }
208}
209
210/// Request for `codex mcp login <NAME>` (OAuth).
211#[derive(Clone, Debug, Eq, PartialEq)]
212pub struct McpOauthLoginRequest {
213    pub name: String,
214    pub scopes: Vec<String>,
215    pub overrides: CliOverridesPatch,
216}
217
218impl McpOauthLoginRequest {
219    pub fn new(name: impl Into<String>) -> Self {
220        Self {
221            name: name.into(),
222            scopes: Vec::new(),
223            overrides: CliOverridesPatch::default(),
224        }
225    }
226
227    pub fn scopes<I, S>(mut self, scopes: I) -> Self
228    where
229        I: IntoIterator<Item = S>,
230        S: Into<String>,
231    {
232        self.scopes.extend(
233            scopes
234                .into_iter()
235                .map(|s| s.into())
236                .filter(|s| !s.trim().is_empty()),
237        );
238        self
239    }
240
241    pub fn with_overrides(mut self, overrides: CliOverridesPatch) -> Self {
242        self.overrides = overrides;
243        self
244    }
245}