mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Expose MCP provenance to tool lifecycle extensions (#40976)
## What changed - Add optional `McpToolContext` metadata to `ToolStartInput`, exposing the model-visible MCP tool details and its source classification without exposing the executable client. - Classify MCP calls as connectors, configured servers, plugin servers, executor-selected plugins, or other registrations based on the prepared call. - Prepare each MCP call before notifying tool lifecycle contributors and reuse that same call for execution so the callback describes the call that runs. ## Testing - Cover host-owned connector calls and extension-owned Apps server calls, including their distinct provenance and executed tool names. GitOrigin-RevId: efd23f511b1045ffdd96349d621ad62365569b2b
This commit is contained in:
@@ -42,6 +42,8 @@ pub use thread_lifecycle::ThreadReadyInput;
|
||||
pub use thread_lifecycle::ThreadResumeInput;
|
||||
pub use thread_lifecycle::ThreadStartInput;
|
||||
pub use thread_lifecycle::ThreadStopInput;
|
||||
pub use tool_lifecycle::McpToolContext;
|
||||
pub use tool_lifecycle::McpToolSource;
|
||||
pub use tool_lifecycle::ToolCallOutcome;
|
||||
pub use tool_lifecycle::ToolFinishInput;
|
||||
pub use tool_lifecycle::ToolLifecycleFuture;
|
||||
|
||||
@@ -2,6 +2,9 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_config::McpServerConfig;
|
||||
use codex_mcp::McpServerSource;
|
||||
use codex_mcp::PreparedMcpCall;
|
||||
use codex_tools::ToolCallSource;
|
||||
use codex_tools::ToolName;
|
||||
use codex_tools::ToolPayload;
|
||||
@@ -33,6 +36,75 @@ pub enum ToolCallOutcome {
|
||||
Aborted,
|
||||
}
|
||||
|
||||
/// Provenance captured from the immutable MCP call selected for one tool invocation.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum McpToolSource {
|
||||
/// A connector routed through the host-owned Codex Apps MCP server.
|
||||
Connector,
|
||||
/// An MCP server whose frozen registration matches the active Codex configuration.
|
||||
Config,
|
||||
/// An MCP server registered by a locally loaded plugin.
|
||||
Plugin {
|
||||
/// Identifier of the plugin that owns this MCP server.
|
||||
id: String,
|
||||
},
|
||||
/// An executor-selected plugin whose root has not been attested by the host.
|
||||
SelectedPlugin,
|
||||
/// A compatibility or extension registration without user-owned provenance.
|
||||
Other,
|
||||
}
|
||||
|
||||
/// Read-only metadata and provenance captured from the MCP call that will execute.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct McpToolContext {
|
||||
tool: crate::McpToolInfo,
|
||||
source: McpToolSource,
|
||||
}
|
||||
|
||||
impl McpToolContext {
|
||||
/// Snapshots a prepared call without exposing its executable client to extensions.
|
||||
///
|
||||
/// Configured servers retain their provenance only when their captured connection
|
||||
/// still matches the host configuration for the current tool invocation.
|
||||
pub fn from_prepared_call(
|
||||
call: &PreparedMcpCall,
|
||||
configured_server: Option<&McpServerConfig>,
|
||||
) -> Self {
|
||||
let tool = call.tool_info().clone();
|
||||
let source = if tool.connector_id.is_some() && call.is_host_owned_apps() {
|
||||
McpToolSource::Connector
|
||||
} else if call.is_selected_plugin_server() {
|
||||
McpToolSource::SelectedPlugin
|
||||
} else if let Some(id) = call.plugin_id() {
|
||||
McpToolSource::Plugin { id: id.to_owned() }
|
||||
} else if call
|
||||
.config()
|
||||
.mcp_server_catalog
|
||||
.server(call.server_name())
|
||||
.is_some_and(|server| {
|
||||
matches!(server.source(), McpServerSource::Config)
|
||||
&& configured_server.is_some_and(|configured| server.config() == configured)
|
||||
})
|
||||
{
|
||||
McpToolSource::Config
|
||||
} else {
|
||||
McpToolSource::Other
|
||||
};
|
||||
|
||||
Self { tool, source }
|
||||
}
|
||||
|
||||
/// Returns frozen metadata for the exact model-visible MCP tool being executed.
|
||||
pub fn tool_info(&self) -> &crate::McpToolInfo {
|
||||
&self.tool
|
||||
}
|
||||
|
||||
/// Returns the registration source captured with the executable call.
|
||||
pub fn source(&self) -> &McpToolSource {
|
||||
&self.source
|
||||
}
|
||||
}
|
||||
|
||||
/// Input supplied when the host starts executing one tool call.
|
||||
pub struct ToolStartInput<'a> {
|
||||
/// Store scoped to the host session runtime.
|
||||
@@ -47,6 +119,8 @@ pub struct ToolStartInput<'a> {
|
||||
pub call_id: &'a str,
|
||||
/// Tool name as routed by the host.
|
||||
pub tool_name: &'a ToolName,
|
||||
/// Read-only metadata and provenance from the exact MCP call that will execute.
|
||||
pub mcp_tool: Option<&'a McpToolContext>,
|
||||
/// Finalized tool arguments, including any pre-tool-use hook rewrites.
|
||||
///
|
||||
/// Payloads can contain sensitive plaintext and must not be logged.
|
||||
|
||||
@@ -17,6 +17,7 @@ pub use capabilities::NoopResponseItemInjector;
|
||||
pub use capabilities::ResponseItemInjectionFuture;
|
||||
pub use capabilities::ResponseItemInjector;
|
||||
pub use codex_context_fragments::ContextualUserFragment;
|
||||
pub use codex_mcp::ToolInfo as McpToolInfo;
|
||||
pub use codex_protocol::models::ContentItemKind;
|
||||
pub use codex_protocol::models::ResponseItem;
|
||||
pub use codex_protocol::security_risk::SecurityRiskScore;
|
||||
@@ -49,6 +50,8 @@ pub use contributors::ExtensionFuture;
|
||||
pub use contributors::McpServerContribution;
|
||||
pub use contributors::McpServerContributionContext;
|
||||
pub use contributors::McpServerContributor;
|
||||
pub use contributors::McpToolContext;
|
||||
pub use contributors::McpToolSource;
|
||||
pub use contributors::PreviousWorldStateSection;
|
||||
pub use contributors::PromptFragment;
|
||||
pub use contributors::PromptSlot;
|
||||
|
||||
Reference in New Issue
Block a user