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:
felixxia-oai
2026-08-26 21:30:23 +00:00
committed by copyberry
parent daa3eaf10f
commit 21ff2e802c
10 changed files with 264 additions and 3 deletions
@@ -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.
+3
View File
@@ -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;