diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 22d53c3de..f650d4d1f 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -1141,6 +1141,9 @@ pub enum ThreadItem { arguments: JsonValue, result: Option, error: Option, + /// The duration of the MCP tool call in milliseconds. + #[ts(type = "number | null")] + duration_ms: Option, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] diff --git a/codex-rs/app-server/src/bespoke_event_handling.rs b/codex-rs/app-server/src/bespoke_event_handling.rs index 8e39580fa..547daf08b 100644 --- a/codex-rs/app-server/src/bespoke_event_handling.rs +++ b/codex-rs/app-server/src/bespoke_event_handling.rs @@ -1178,6 +1178,7 @@ async fn construct_mcp_tool_call_notification( arguments: begin_event.invocation.arguments.unwrap_or(JsonValue::Null), result: None, error: None, + duration_ms: None, }; ItemStartedNotification { thread_id, @@ -1197,6 +1198,7 @@ async fn construct_mcp_tool_call_end_notification( } else { McpToolCallStatus::Failed }; + let duration_ms = i64::try_from(end_event.duration.as_millis()).ok(); let (result, error) = match &end_event.result { Ok(value) => ( @@ -1222,6 +1224,7 @@ async fn construct_mcp_tool_call_end_notification( arguments: end_event.invocation.arguments.unwrap_or(JsonValue::Null), result, error, + duration_ms, }; ItemCompletedNotification { thread_id, @@ -1598,6 +1601,7 @@ mod tests { arguments: serde_json::json!({"server": ""}), result: None, error: None, + duration_ms: None, }, }; @@ -1751,6 +1755,7 @@ mod tests { arguments: JsonValue::Null, result: None, error: None, + duration_ms: None, }, }; @@ -1804,6 +1809,7 @@ mod tests { structured_content: None, }), error: None, + duration_ms: Some(0), }, }; @@ -1845,6 +1851,7 @@ mod tests { error: Some(McpToolCallError { message: "boom".to_string(), }), + duration_ms: Some(1), }, };