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 authored and copyberry committed 2026-08-26 21:30:23 +00:00
1 parent daa3eaf10f
commit 21ff2e802c
10 files changed
+264 -3

No files matched your search

@@ -160,6 +160,7 @@ async fn installed_extension_reconnects_after_auth_refresh() -> Result<()> {
turn_id: "turn-1",
call_id,
tool_name: &tool_name,
mcp_tool: None,
payload: &payload,
conversation_history: Arc::new(TestConversationHistory(Vec::new())),
source: ToolCallSource::Direct,
@@ -360,6 +361,7 @@ async fn sandboxed_shell_classification_respects_review_scope() -> Result<()> {
turn_id: "turn-1",
call_id: "call-2",
tool_name: &tool_name,
mcp_tool: None,
payload: &payload,
conversation_history: Arc::new(TestConversationHistory(Vec::new())),
source: ToolCallSource::Direct,
@@ -437,6 +439,7 @@ async fn computer_use_only_scores_cannot_approve_other_actions() -> Result<()> {
turn_id: "turn-1",
call_id: "ordinary-call",
tool_name: &ordinary_tool,
mcp_tool: None,
payload: &payload,
conversation_history: Arc::new(TestConversationHistory(Vec::new())),
source: ToolCallSource::CodeMode {
@@ -812,6 +815,7 @@ async fn sample_configured_conversation_history_with_source(
turn_id: "turn-1",
call_id: "call-1",
tool_name: &tool_name,
mcp_tool: None,
payload: &tool_payload,
conversation_history: Arc::new(conversation_history),
source,
@@ -878,6 +882,7 @@ impl GuardianFailureFixture {
turn_id: "turn-1",
call_id: "call-1",
tool_name: &tool_name,
mcp_tool: None,
payload: &payload,
conversation_history: Arc::new(TestConversationHistory(Vec::new())),
source: ToolCallSource::Direct,
@@ -2015,6 +2020,7 @@ async fn contributor_skips_required_models_in_standard_scope() -> Result<()> {
turn_id: "turn-1",
call_id: "protected.md",
tool_name: &tool_name,
mcp_tool: None,
payload: &payload,
conversation_history: Arc::new(TestConversationHistory(vec![oversized_compaction])),
source: ToolCallSource::Direct,
@@ -2095,6 +2101,7 @@ async fn contributor_counts_failed_thread_lookups_toward_score_lag() -> Result<(
turn_id: "turn-1",
call_id: "missing.md",
tool_name: &tool_name,
mcp_tool: None,
payload: &payload,
conversation_history: Arc::new(TestConversationHistory(Vec::new())),
source: ToolCallSource::Direct,
@@ -2469,6 +2476,7 @@ async fn contributor_reuses_the_latest_compatible_parent_compaction() -> Result<
turn_id: "turn-1",
call_id: "call-1",
tool_name: &tool_name,
mcp_tool: None,
payload: &tool_payload,
conversation_history: Arc::new(conversation_history),
source: ToolCallSource::Direct,
@@ -2538,6 +2546,7 @@ async fn contributor_reuses_the_latest_compatible_parent_compaction() -> Result<
turn_id: "turn-1",
call_id: "call-2",
tool_name: &tool_name,
mcp_tool: None,
payload: &tool_payload,
conversation_history: Arc::new(TestConversationHistory(vec![
latest_compaction,