use super::{ToolFilter, ToolMatcher};
use rmcp::model::Tool;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
#[serde(from = "ToolExposureConfig", into = "ToolExposureConfig")]
#[schemars(with = "ToolExposureConfig")]
pub enum ToolExposure {
#[default]
ModelVisible,
Deferred(ToolFilter),
}
impl ToolExposure {
pub fn deferred_all() -> Self {
Self::Deferred(ToolFilter::default())
}
pub fn defers_tools(&self) -> bool {
matches!(self, Self::Deferred(_))
}
pub fn is_deferred(&self, tool: &Tool) -> bool {
matches!(self, Self::Deferred(filter) if filter.allows(&tool.name, tool))
}
}
#[derive(Deserialize, Serialize, JsonSchema)]
#[serde(untagged, deny_unknown_fields)]
enum ToolExposureConfig {
Enabled(bool),
Rules {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
include: Vec<ToolMatcher>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
exclude: Vec<ToolMatcher>,
},
}
impl From<ToolExposureConfig> for ToolExposure {
fn from(repr: ToolExposureConfig) -> Self {
match repr {
ToolExposureConfig::Enabled(false) => Self::ModelVisible,
ToolExposureConfig::Enabled(true) => Self::deferred_all(),
ToolExposureConfig::Rules { include, exclude } => {
Self::Deferred(ToolFilter { allow: include, deny: exclude })
}
}
}
}
impl From<ToolExposure> for ToolExposureConfig {
fn from(exposure: ToolExposure) -> Self {
match exposure {
ToolExposure::ModelVisible => Self::Enabled(false),
ToolExposure::Deferred(filter) if filter.is_empty() => Self::Enabled(true),
ToolExposure::Deferred(ToolFilter { allow, deny }) => Self::Rules { include: allow, exclude: deny },
}
}
}