mirror of
https://github.com/hanzoai/codex.git
synced 2026-10-30 14:59:26 +00:00
## What changed - Pass the resolved app `link_id` into MCP tool approval policy evaluation. - Apply link-specific `default_tools_approval_mode` and `approvals_reviewer` settings before app and global defaults, while preserving per-tool and managed policy precedence. ## Testing - Cover link-specific approval prompts, selected account links, reviewer routing, fallback behavior, and managed-policy overrides. GitOrigin-RevId: caa07ec3e73c87aad9522bd69808cd1c0b1686f5
104 lines
3.3 KiB
Rust
104 lines
3.3 KiB
Rust
//! Connector-owned projection of raw runtime tools into installed app state.
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
use codex_config::ConfigLayerStack;
|
|
|
|
use crate::AppToolPolicyEvaluator;
|
|
use crate::AppToolPolicyInput;
|
|
|
|
/// Connector-relevant fields from one runtime tool.
|
|
///
|
|
/// MCP owns the raw tool type and computes generic visibility/filter decisions. Connector
|
|
/// consumers adapt those fields into this view so connector policy stays out of MCP modules.
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct ConnectorRuntimeTool<'a> {
|
|
pub connector_id: Option<&'a str>,
|
|
pub connector_name: Option<&'a str>,
|
|
pub tool_name: &'a str,
|
|
pub tool_title: Option<&'a str>,
|
|
pub destructive_hint: Option<bool>,
|
|
pub open_world_hint: Option<bool>,
|
|
pub synthetic: bool,
|
|
pub model_visible: bool,
|
|
}
|
|
|
|
/// Installed state derived from one committed connector runtime snapshot.
|
|
///
|
|
/// `enabled` and `callable` include local and managed app/tool configuration. Global feature and
|
|
/// workspace policy remain host concerns and are applied by the caller.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct InstalledConnectorRuntime {
|
|
pub id: String,
|
|
pub runtime_name: Option<String>,
|
|
pub enabled: bool,
|
|
pub callable: bool,
|
|
}
|
|
|
|
/// Projects raw runtime tools into one row per installed connector.
|
|
pub fn installed_connector_runtime<'a>(
|
|
config_layer_stack: &ConfigLayerStack,
|
|
tools: impl IntoIterator<Item = ConnectorRuntimeTool<'a>>,
|
|
) -> Vec<InstalledConnectorRuntime> {
|
|
let policy = AppToolPolicyEvaluator::new(config_layer_stack);
|
|
let mut apps = BTreeMap::<String, (Option<String>, bool)>::new();
|
|
|
|
for tool in tools {
|
|
if tool.synthetic {
|
|
continue;
|
|
}
|
|
let Some(connector_id) = tool.connector_id.map(str::trim) else {
|
|
continue;
|
|
};
|
|
if connector_id.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
let runtime_name = tool
|
|
.connector_name
|
|
.map(str::trim)
|
|
.filter(|name| !name.is_empty())
|
|
.map(str::to_string);
|
|
let entry = apps
|
|
.entry(connector_id.to_string())
|
|
.or_insert((None, false));
|
|
if entry.0.is_none() {
|
|
entry.0 = runtime_name;
|
|
}
|
|
|
|
let policy_allows_tool = policy
|
|
.policy(AppToolPolicyInput {
|
|
connector_id: Some(connector_id),
|
|
link_id: None,
|
|
tool_name: tool.tool_name,
|
|
tool_title: tool.tool_title,
|
|
destructive_hint: tool.destructive_hint,
|
|
open_world_hint: tool.open_world_hint,
|
|
})
|
|
.enabled;
|
|
entry.1 |= tool.model_visible && policy_allows_tool;
|
|
}
|
|
|
|
apps.into_iter()
|
|
.map(|(id, (runtime_name, callable))| InstalledConnectorRuntime {
|
|
enabled: policy.app_enabled(&id),
|
|
id,
|
|
runtime_name,
|
|
callable,
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
/// Returns whether connector metadata marks a runtime tool as a synthetic link helper.
|
|
pub fn connector_tool_is_synthetic(connector_meta: Option<&serde_json::Value>) -> bool {
|
|
connector_meta
|
|
.and_then(serde_json::Value::as_object)
|
|
.and_then(|meta| meta.get("synthetic_link"))
|
|
.and_then(serde_json::Value::as_bool)
|
|
== Some(true)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[path = "runtime_projection_tests.rs"]
|
|
mod tests;
|