1use std::{collections::BTreeMap, ffi::OsString, path::PathBuf, time::Duration};
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use tokio::sync::{mpsc, oneshot};
6
7pub const METHOD_INITIALIZE: &str = "initialize";
9pub const METHOD_SHUTDOWN: &str = "shutdown";
11pub const METHOD_EXIT: &str = "exit";
13pub const METHOD_CANCEL: &str = "$/cancelRequest";
15
16pub const METHOD_CODEX: &str = "tools/call";
18pub const METHOD_CODEX_REPLY: &str = "tools/call";
20pub const METHOD_CODEX_EVENT: &str = "codex/event";
22pub const METHOD_CODEX_APPROVAL: &str = "codex/approval";
24
25pub const METHOD_THREAD_START: &str = "thread/start";
27pub const METHOD_THREAD_RESUME: &str = "thread/resume";
29pub const METHOD_THREAD_LIST: &str = "thread/list";
31pub const METHOD_THREAD_FORK: &str = "thread/fork";
33pub const METHOD_TURN_START: &str = "turn/start";
35pub const METHOD_TURN_INTERRUPT: &str = "turn/interrupt";
37
38pub type RequestId = u64;
40
41pub type EventStream<T> = mpsc::UnboundedReceiver<T>;
43
44#[derive(Clone, Debug)]
52pub struct StdioServerConfig {
53 pub binary: PathBuf,
54 pub code_home: Option<PathBuf>,
55 pub current_dir: Option<PathBuf>,
56 pub env: Vec<(OsString, OsString)>,
57 pub app_server_analytics_default_enabled: bool,
59 pub mirror_stdio: bool,
60 pub startup_timeout: Duration,
61}
62
63#[derive(Clone, Debug, Serialize, Deserialize)]
65pub struct ClientInfo {
66 pub name: String,
67 pub version: String,
68}
69
70#[derive(Clone, Debug, Serialize, Deserialize)]
72pub struct InitializeParams {
73 #[serde(rename = "clientInfo")]
74 pub client: ClientInfo,
75 #[serde(rename = "protocolVersion")]
76 pub protocol_version: String,
77 #[serde(default)]
78 pub capabilities: Value,
79}
80
81#[derive(Clone, Debug, Serialize, Deserialize)]
83#[serde(rename_all = "kebab-case")]
84pub struct CodexCallParams {
85 pub prompt: String,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub model: Option<String>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub cwd: Option<PathBuf>,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub sandbox: Option<String>,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub approval_policy: Option<String>,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub profile: Option<String>,
96 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
97 pub config: BTreeMap<String, Value>,
98}
99
100#[derive(Clone, Debug, Serialize, Deserialize)]
102pub struct CodexReplyParams {
103 #[serde(rename = "conversationId")]
104 pub conversation_id: String,
105 pub prompt: String,
106}
107
108#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
110pub enum ApprovalKind {
111 Exec,
112 Apply,
113 Unknown(String),
114}
115
116#[derive(Clone, Debug, Serialize, Deserialize)]
118pub struct ApprovalRequest {
119 pub approval_id: String,
120 pub kind: ApprovalKind,
121 pub payload: Value,
123}
124
125#[derive(Clone, Debug, Serialize, Deserialize)]
127pub enum ApprovalDecision {
128 Approve {
129 approval_id: String,
130 },
131 Reject {
132 approval_id: String,
133 reason: Option<String>,
134 },
135}
136
137#[derive(Clone, Debug, Serialize, Deserialize)]
139#[serde(tag = "type", rename_all = "snake_case")]
140pub enum CodexEvent {
141 TaskComplete {
142 conversation_id: String,
143 result: Value,
144 },
145 ApprovalRequired(ApprovalRequest),
146 Cancelled {
147 conversation_id: Option<String>,
148 reason: Option<String>,
149 },
150 Error {
151 message: String,
152 data: Option<Value>,
153 },
154 Raw {
155 method: String,
156 params: Value,
157 },
158}
159
160#[derive(Clone, Debug, Serialize, Deserialize)]
162pub struct CodexCallResult {
163 #[serde(default, rename = "conversationId", alias = "conversation_id")]
164 pub conversation_id: Option<String>,
165 #[serde(default, rename = "content", alias = "output")]
166 pub output: Value,
167}
168
169pub struct CodexCallHandle {
171 pub request_id: RequestId,
172 pub events: EventStream<CodexEvent>,
173 pub response: oneshot::Receiver<Result<CodexCallResult, super::McpError>>,
174}
175
176#[derive(Clone, Debug, Serialize, Deserialize)]
178pub struct ThreadStartParams {
179 pub thread_id: Option<String>,
180 #[serde(default)]
181 pub metadata: Value,
182}
183
184#[derive(Clone, Debug, Serialize, Deserialize)]
186#[serde(rename_all = "camelCase")]
187pub struct ThreadResumeParams {
188 pub thread_id: String,
189}
190
191#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
193#[serde(rename_all = "snake_case")]
194pub enum ThreadListSortKey {
195 CreatedAt,
196 UpdatedAt,
197}
198
199#[derive(Clone, Debug, Serialize, Deserialize)]
201#[serde(rename_all = "camelCase")]
202pub struct ThreadListParams {
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub cwd: Option<PathBuf>,
205 pub cursor: Option<String>,
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub limit: Option<u32>,
209 #[serde(skip_serializing_if = "Option::is_none")]
210 pub sort_key: Option<ThreadListSortKey>,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub archived: Option<bool>,
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub model_providers: Option<Vec<String>>,
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub source_kinds: Option<Vec<String>>,
217}
218
219#[derive(Clone, Debug, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct ThreadListResponse {
223 pub data: Vec<ThreadSummary>,
224 pub next_cursor: Option<String>,
225}
226
227#[derive(Clone, Debug, Serialize, Deserialize)]
229#[serde(rename_all = "camelCase")]
230pub struct ThreadSummary {
231 pub id: String,
232 pub created_at: i64,
233 pub updated_at: i64,
234 #[serde(default, skip_serializing_if = "Option::is_none")]
235 pub cwd: Option<PathBuf>,
236 #[serde(default, skip_serializing_if = "BTreeMap::is_empty", flatten)]
237 pub extra: BTreeMap<String, Value>,
238}
239
240#[derive(Clone, Debug, Serialize, Deserialize)]
242#[serde(rename_all = "camelCase")]
243pub struct ThreadForkParams {
244 pub thread_id: String,
245 #[serde(skip_serializing_if = "Option::is_none")]
246 pub cwd: Option<PathBuf>,
247 #[serde(skip_serializing_if = "Option::is_none")]
248 pub approval_policy: Option<String>,
249 #[serde(skip_serializing_if = "Option::is_none")]
250 pub sandbox: Option<String>,
251 #[serde(skip_serializing_if = "Option::is_none")]
252 pub persist_extended_history: Option<bool>,
253}
254
255#[derive(Clone, Debug, Serialize, Deserialize)]
257pub struct ThreadForkResponse {
258 pub thread: ForkedThread,
259}
260
261#[derive(Clone, Debug, Serialize, Deserialize)]
263pub struct ForkedThread {
264 pub id: String,
265}
266
267#[derive(Clone, Debug, Serialize, Deserialize)]
269#[serde(rename_all = "camelCase")]
270pub struct TurnStartParams {
271 pub thread_id: String,
272 #[serde(rename = "input", alias = "prompt")]
273 pub input: Vec<TurnInput>,
274 pub model: Option<String>,
275 #[serde(default)]
276 pub config: BTreeMap<String, Value>,
277}
278
279#[derive(Clone, Debug, Serialize, Deserialize)]
280pub struct TurnInput {
281 #[serde(rename = "type")]
282 pub kind: String,
283 #[serde(skip_serializing_if = "Option::is_none")]
284 pub text: Option<String>,
285}
286
287#[derive(Clone, Debug, Serialize, Deserialize)]
289#[serde(rename_all = "camelCase")]
290pub struct TurnStartParamsV2 {
291 pub thread_id: String,
292 pub input: Vec<UserInputV2>,
293 #[serde(skip_serializing_if = "Option::is_none")]
294 pub approval_policy: Option<String>,
295 #[serde(skip_serializing_if = "Option::is_none")]
296 pub cwd: Option<PathBuf>,
297}
298
299#[derive(Clone, Debug, Serialize, Deserialize)]
301#[serde(tag = "type", rename_all = "snake_case")]
302pub enum UserInputV2 {
303 Text {
304 text: String,
305 #[serde(default)]
306 text_elements: Vec<Value>,
307 },
308}
309
310impl UserInputV2 {
311 pub fn text(text: impl Into<String>) -> Self {
312 Self::Text {
313 text: text.into(),
314 text_elements: Vec::new(),
315 }
316 }
317}
318
319#[derive(Clone, Debug, Serialize, Deserialize)]
321#[serde(rename_all = "camelCase")]
322pub struct TurnInterruptParams {
323 pub thread_id: Option<String>,
324 pub turn_id: String,
325}
326
327#[derive(Clone, Debug, Serialize, Deserialize)]
329#[serde(tag = "type", rename_all = "snake_case")]
330pub enum AppNotification {
331 TaskComplete {
332 thread_id: String,
333 turn_id: Option<String>,
334 result: Value,
335 },
336 Item {
337 thread_id: String,
338 turn_id: Option<String>,
339 item: Value,
340 },
341 Error {
342 message: String,
343 data: Option<Value>,
344 },
345 Raw {
346 method: String,
347 params: Value,
348 },
349}
350
351pub struct AppCallHandle {
353 pub request_id: RequestId,
354 pub events: EventStream<AppNotification>,
355 pub response: oneshot::Receiver<Result<Value, super::McpError>>,
356}