diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 87d59b21..9123bd63 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -2643,6 +2643,7 @@ version = "0.0.0" dependencies = [ "anyhow", "assert_cmd", + "codex-core", "codex-mcp-server", "mcp-types", "pretty_assertions", @@ -2650,6 +2651,7 @@ dependencies = [ "shlex", "tempfile", "tokio", + "uuid", "wiremock", ] diff --git a/codex-rs/mcp-server/src/lib.rs b/codex-rs/mcp-server/src/lib.rs index 0912fed1..ebef8ca9 100644 --- a/codex-rs/mcp-server/src/lib.rs +++ b/codex-rs/mcp-server/src/lib.rs @@ -19,7 +19,7 @@ mod codex_tool_config; mod codex_tool_runner; mod exec_approval; mod json_to_toml; -mod mcp_protocol; +pub mod mcp_protocol; mod message_processor; mod outgoing_message; mod patch_approval; diff --git a/codex-rs/mcp-server/src/message_processor.rs b/codex-rs/mcp-server/src/message_processor.rs index 7ba827d6..a4013cc3 100644 --- a/codex-rs/mcp-server/src/message_processor.rs +++ b/codex-rs/mcp-server/src/message_processor.rs @@ -6,6 +6,7 @@ use crate::codex_tool_config::CodexToolCallParam; use crate::codex_tool_config::CodexToolCallReplyParam; use crate::codex_tool_config::create_tool_for_codex_tool_call_param; use crate::codex_tool_config::create_tool_for_codex_tool_call_reply_param; +use crate::mcp_protocol::ToolCallRequestParams; use crate::outgoing_message::OutgoingMessageSender; use codex_core::Codex; @@ -300,6 +301,14 @@ impl MessageProcessor { params: ::Params, ) { tracing::info!("tools/call -> params: {:?}", params); + // Serialize params into JSON and try to parse as new type + if let Ok(new_params) = + serde_json::to_value(¶ms).and_then(serde_json::from_value::) + { + // New tool call matched → forward + self.handle_new_tool_calls(id, new_params).await; + return; + } let CallToolRequestParams { name, arguments } = params; match name.as_str() { @@ -323,6 +332,20 @@ impl MessageProcessor { } } } + async fn handle_new_tool_calls(&self, request_id: RequestId, _params: ToolCallRequestParams) { + // TODO: implement the new tool calls + let result = CallToolResult { + content: vec![ContentBlock::TextContent(TextContent { + r#type: "text".to_string(), + text: "Unknown tool".to_string(), + annotations: None, + })], + is_error: Some(true), + structured_content: None, + }; + self.send_response::(request_id, result) + .await; + } async fn handle_tool_call_codex(&self, id: RequestId, arguments: Option) { let (initial_prompt, config): (String, CodexConfig) = match arguments { diff --git a/codex-rs/mcp-server/tests/common/Cargo.toml b/codex-rs/mcp-server/tests/common/Cargo.toml index 3aa246f1..420b9781 100644 --- a/codex-rs/mcp-server/tests/common/Cargo.toml +++ b/codex-rs/mcp-server/tests/common/Cargo.toml @@ -10,6 +10,7 @@ path = "lib.rs" anyhow = "1" assert_cmd = "2" codex-mcp-server = { path = "../.." } +codex-core = { path = "../../../core" } mcp-types = { path = "../../../mcp-types" } pretty_assertions = "1.4.1" serde_json = "1" @@ -22,3 +23,4 @@ tokio = { version = "1", features = [ "rt-multi-thread", ] } wiremock = "0.6" +uuid = { version = "1", features = ["serde", "v4"] } \ No newline at end of file diff --git a/codex-rs/mcp-server/tests/common/mcp_process.rs b/codex-rs/mcp-server/tests/common/mcp_process.rs index 8138749c..d7144af4 100644 --- a/codex-rs/mcp-server/tests/common/mcp_process.rs +++ b/codex-rs/mcp-server/tests/common/mcp_process.rs @@ -11,8 +11,13 @@ use tokio::process::ChildStdout; use anyhow::Context; use assert_cmd::prelude::*; +use codex_core::protocol::InputItem; use codex_mcp_server::CodexToolCallParam; use codex_mcp_server::CodexToolCallReplyParam; +use codex_mcp_server::mcp_protocol::ConversationId; +use codex_mcp_server::mcp_protocol::ConversationSendMessageArgs; +use codex_mcp_server::mcp_protocol::ToolCallRequestParams; + use mcp_types::CallToolRequestParams; use mcp_types::ClientCapabilities; use mcp_types::Implementation; @@ -29,6 +34,7 @@ use pretty_assertions::assert_eq; use serde_json::json; use std::process::Command as StdCommand; use tokio::process::Command; +use uuid::Uuid; pub struct McpProcess { next_request_id: AtomicI64, @@ -174,6 +180,26 @@ impl McpProcess { .await } + pub async fn send_user_message_tool_call( + &mut self, + message: &str, + session_id: &str, + ) -> anyhow::Result { + let params = ToolCallRequestParams::ConversationSendMessage(ConversationSendMessageArgs { + conversation_id: ConversationId(Uuid::parse_str(session_id)?), + content: vec![InputItem::Text { + text: message.to_string(), + }], + parent_message_id: None, + conversation_overrides: None, + }); + self.send_request( + mcp_types::CallToolRequest::METHOD, + Some(serde_json::to_value(params)?), + ) + .await + } + async fn send_request( &mut self, method: &str,