use std::sync::Arc;
use fastmcp_core::CanonicalHttpUrl;
use fastmcp_protocol::http_headers::ParameterHeaderBinding;
use super::catalog::{ManagedToolCatalogError, ManagedToolCatalogSnapshot};
use super::{ManagedToolClient, ManagedToolError, ToolContract};
use crate::http_executor::parameter_headers::{ReviewedToolHeaders, ToolHeaderDispatchError};
pub mod repair;
impl ManagedToolClient {
pub fn review_headers(
self,
review: impl FnMut(&ParameterHeaderBinding) -> bool,
) -> Result<Self, ManagedToolError> {
self.require_unconfigured_headers()?;
let reviewed = self
.contract
.review_headers(self.session.resource(), review)?;
self.with_reviewed_headers(reviewed)
}
pub fn with_reviewed_headers(
mut self,
reviewed: Arc<ReviewedToolHeaders>,
) -> Result<Self, ManagedToolError> {
self.require_unconfigured_headers()?;
self.contract
.admit_headers(self.session.resource(), &reviewed)?;
self.header_review = Some(reviewed);
Ok(self)
}
fn require_unconfigured_headers(&self) -> Result<(), ManagedToolError> {
self.contract.check()?;
if self.header_review.is_some() {
return Err(ManagedToolError::Headers(
ToolHeaderDispatchError::AlreadyProjected,
));
}
Ok(())
}
}
impl ManagedToolCatalogSnapshot {
pub fn tool_with_reviewed_headers(
&self,
name: &str,
review: impl FnMut(&ParameterHeaderBinding) -> bool,
) -> Result<Option<ManagedToolClient>, ManagedToolCatalogError> {
self.tool(name)?
.map(|tool| {
tool.review_headers(review)
.map_err(ManagedToolCatalogError::Tool)
})
.transpose()
}
}
impl ToolContract {
fn admit_headers(
&self,
resource: &CanonicalHttpUrl,
reviewed: &ReviewedToolHeaders,
) -> Result<(), ManagedToolError> {
self.check()?;
if reviewed.resource() != resource
|| reviewed.tool_name() != self.name.as_str()
|| reviewed.schema() != self.input.schema()
{
return Err(ManagedToolError::HeaderBindingMismatch);
}
self.check()
}
fn review_headers(
&self,
resource: &CanonicalHttpUrl,
mut review: impl FnMut(&ParameterHeaderBinding) -> bool,
) -> Result<Arc<ReviewedToolHeaders>, ManagedToolError> {
self.check()?;
let reviewed = ReviewedToolHeaders::new(
resource.clone(),
self.name.clone(),
self.input.schema().clone(),
|binding| {
if self.is_invalidated() {
return false;
}
let approved = review(binding);
approved && !self.is_invalidated()
},
);
self.check()?;
let reviewed = reviewed.map_err(ManagedToolError::Headers)?;
self.admit_headers(resource, &reviewed)?;
Ok(Arc::new(reviewed))
}
}
#[cfg(test)]
mod tests;