#![forbid(unsafe_code)]
pub mod blocking;
mod filesystem;
#[cfg(feature = "apps")]
mod mcp_apps;
mod schema_tool;
#[cfg(feature = "proxy")]
pub mod managed_oauth;
#[cfg(feature = "proxy")]
pub use managed_oauth::dynamic::ClientCredentialsProvider;
pub use blocking::{
BlockingCompletion, BlockingHandlerLane, BlockingPrompt, BlockingResource, BlockingTool,
};
pub use fastmcp_protocol::{SchemaRegistryError, SchemaRegistryLimits, SchemaResourceRegistry};
pub use filesystem::{FilesystemProvider, FilesystemProviderError, FilesystemResourceHandler};
#[cfg(feature = "apps")]
pub use mcp_apps::{McpAppsUiResource, McpAppsUiResourceError};
pub use schema_tool::{RegisteredSchemaTool, RegisteredSchemaToolError};
#[cfg(test)]
mod host_cancelled_pairing {
const HOST_CANCELLED_CONVERSION_SITES: usize = 2;
fn collect_rust_sources(dir: &std::path::Path, out: &mut Vec<(String, String)>) {
let entries = std::fs::read_dir(dir)
.unwrap_or_else(|error| panic!("cannot read {}: {error}", dir.display()));
for entry in entries {
let path = entry.expect("directory entry").path();
if path.is_dir() {
collect_rust_sources(&path, out);
} else if path.extension().is_some_and(|extension| extension == "rs") {
let contents = std::fs::read_to_string(&path)
.unwrap_or_else(|error| panic!("cannot read {}: {error}", path.display()));
out.push((path.display().to_string(), contents));
}
}
}
fn declared_fn_names(contents: &str) -> Vec<&str> {
let mut names = Vec::new();
for line in contents.lines() {
if line.trim_start().starts_with("//") {
continue;
}
let mut offset = 0;
while let Some(at) = line[offset..].find("fn ") {
let start = offset + at;
offset = start + 3;
let word_boundary = start == 0
|| !matches!(line.as_bytes()[start - 1],
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_');
if !word_boundary {
continue;
}
let name = line[offset..]
.split(|character: char| !character.is_alphanumeric() && character != '_')
.next()
.unwrap_or_default();
if !name.is_empty() {
names.push(name);
}
}
}
names
}
fn host_cancelled_conversion_sites(sources: &[(String, String)]) -> Vec<String> {
sources
.iter()
.filter(|(_, contents)| declared_fn_names(contents).contains(&"host_cancelled"))
.map(|(path, _)| path.clone())
.collect()
}
fn host_cancelled_conversions_missing_their_pair(sources: &[(String, String)]) -> Vec<String> {
let mut missing = Vec::new();
for (path, contents) in sources {
let names = declared_fn_names(contents);
if !names.contains(&"host_cancelled") {
continue;
}
let has = |suffix: &str| {
names
.iter()
.any(|name| name.starts_with("host_cancelled_") && name.ends_with(suffix))
};
if !(has("_by_variant") && has("_planted_negative")) {
missing.push(path.clone());
}
}
missing
}
#[test]
fn no_host_cancelled_conversion_lacks_its_test_pair() {
let root =
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src/providers/managed_oauth");
let mut sources = Vec::new();
collect_rust_sources(&root, &mut sources);
assert!(
!sources.is_empty(),
"the walk found no sources under {}",
root.display()
);
let sites = host_cancelled_conversion_sites(&sources);
let found = sites.len();
let frozen = HOST_CANCELLED_CONVERSION_SITES;
assert_eq!(
found, frozen,
"this tree froze at {frozen} `host_cancelled` conversions; \
the walk found {found}: {sites:?}. If one was added, give it a \
`host_cancelled_*_by_variant` + `host_cancelled_*_planted_negative` pair in its own \
file and raise the constant. If one was renamed or removed, this guard is now blind \
to it -- repair the scan, do not lower the constant to match it.",
);
let unpaired = host_cancelled_conversions_missing_their_pair(&sources);
assert!(
unpaired.is_empty(),
"these files declare a `host_cancelled` conversion with no \
`host_cancelled_*_by_variant` + `host_cancelled_*_planted_negative` pair \
beside it: {unpaired:?}",
);
}
#[test]
fn a_host_cancelled_conversion_arriving_without_its_pair_is_named() {
let paired = |kind: &str| {
format!(
"fn host_cancelled() -> {kind} {{ todo!() }}\n\
#[test]\n\
fn host_cancelled_{kind}_by_variant() {{}}\n\
#[test]\n\
fn host_cancelled_{kind}_planted_negative() {{}}\n",
)
};
let planted = vec![
("managed_oauth/interaction.rs".to_owned(), paired("managed")),
(
"managed_oauth/dynamic/machine/interaction.rs".to_owned(),
paired("machine"),
),
(
"managed_oauth/dynamic/device/interaction.rs".to_owned(),
"fn host_cancelled() -> Device { todo!() }\n\
// TODO: fn host_cancelled_device_by_variant() and\n\
// fn host_cancelled_device_planted_negative() are still unwritten.\n"
.to_owned(),
),
];
assert_eq!(host_cancelled_conversion_sites(&planted).len(), 3);
assert_eq!(
host_cancelled_conversions_missing_their_pair(&planted),
vec!["managed_oauth/dynamic/device/interaction.rs".to_owned()],
);
}
}