From 468faf41dc9c155d522aefa576a62acfb3ee09bf Mon Sep 17 00:00:00 2001 From: Casey Chow Date: Mon, 18 May 2026 21:49:50 -0400 Subject: [PATCH] feat(core): add reload_plugins tool --- .../core/src/tools/handlers/reload_plugins.rs | 73 +++++++++++++++++++ .../src/tools/handlers/reload_plugins_spec.rs | 33 +++++++++ 2 files changed, 106 insertions(+) create mode 100644 codex-rs/core/src/tools/handlers/reload_plugins.rs create mode 100644 codex-rs/core/src/tools/handlers/reload_plugins_spec.rs diff --git a/codex-rs/core/src/tools/handlers/reload_plugins.rs b/codex-rs/core/src/tools/handlers/reload_plugins.rs new file mode 100644 index 0000000000..7176a991f5 --- /dev/null +++ b/codex-rs/core/src/tools/handlers/reload_plugins.rs @@ -0,0 +1,73 @@ +use crate::function_tool::FunctionCallError; +use crate::tools::context::FunctionToolOutput; +use crate::tools::context::ToolInvocation; +use crate::tools::context::ToolOutput; +use crate::tools::context::ToolPayload; +use crate::tools::context::boxed_tool_output; +use crate::tools::handlers::reload_plugins_spec::RELOAD_PLUGINS_TOOL_NAME; +use crate::tools::handlers::reload_plugins_spec::create_reload_plugins_tool; +use crate::tools::registry::CoreToolRuntime; +use crate::tools::registry::ToolExecutor; +use codex_tools::ToolName; +use codex_tools::ToolSpec; + +pub struct ReloadPluginsHandler; + +#[async_trait::async_trait] +impl ToolExecutor for ReloadPluginsHandler { + fn tool_name(&self) -> ToolName { + ToolName::plain(RELOAD_PLUGINS_TOOL_NAME) + } + + fn spec(&self) -> Option { + Some(create_reload_plugins_tool()) + } + + async fn handle( + &self, + invocation: ToolInvocation, + ) -> Result, FunctionCallError> { + let ToolInvocation { + payload, + session, + turn, + .. + } = invocation; + + match payload { + ToolPayload::Function { arguments } if arguments.trim() == "{}" => {} + ToolPayload::Function { arguments } if arguments.trim().is_empty() => {} + ToolPayload::Function { .. } => { + return Err(FunctionCallError::RespondToModel( + "reload_plugins does not accept arguments".to_string(), + )); + } + _ => { + return Err(FunctionCallError::Fatal(format!( + "{RELOAD_PLUGINS_TOOL_NAME} handler received unsupported payload" + ))); + } + } + + session.reload_user_config_layer().await; + let config = session.get_config().await; + let mcp_config = config + .to_mcp_config(session.plugins_manager().as_ref()) + .await; + session + .refresh_mcp_servers_now( + turn.as_ref(), + mcp_config.configured_mcp_servers.clone(), + mcp_config.mcp_oauth_credentials_store_mode, + Some(session.mcp_elicitation_reviewer()), + ) + .await; + + Ok(boxed_tool_output(FunctionToolOutput::from_text( + "{\"reloaded\":true}".to_string(), + Some(true), + ))) + } +} + +impl CoreToolRuntime for ReloadPluginsHandler {} diff --git a/codex-rs/core/src/tools/handlers/reload_plugins_spec.rs b/codex-rs/core/src/tools/handlers/reload_plugins_spec.rs new file mode 100644 index 0000000000..e42e6a5835 --- /dev/null +++ b/codex-rs/core/src/tools/handlers/reload_plugins_spec.rs @@ -0,0 +1,33 @@ +use codex_tools::JsonSchema; +use codex_tools::ResponsesApiTool; +use codex_tools::ToolSpec; +use std::collections::BTreeMap; + +pub const RELOAD_PLUGINS_TOOL_NAME: &str = "reload_plugins"; + +pub fn create_reload_plugins_tool() -> ToolSpec { + ToolSpec::Function(ResponsesApiTool { + name: RELOAD_PLUGINS_TOOL_NAME.to_string(), + description: "Reload local plugin configuration, clear plugin and skill caches, and rebuild MCP tools from the refreshed plugin state for the current turn. Plugin instructions or skills already injected into an older thread remain unchanged; use a new thread to pick up newly loaded plugin context.".to_string(), + strict: false, + defer_loading: None, + parameters: JsonSchema::object(BTreeMap::new(), Some(Vec::new()), Some(false.into())), + output_schema: None, + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn reload_plugins_tool_has_no_arguments() { + let ToolSpec::Function(tool) = create_reload_plugins_tool() else { + panic!("reload_plugins should be a function tool"); + }; + + assert_eq!(tool.name, RELOAD_PLUGINS_TOOL_NAME); + assert_eq!(tool.parameters.required, Some(Vec::new())); + assert_eq!(tool.parameters.properties, Some(BTreeMap::new())); + } +}