#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
mod definition;
mod manifest;
mod origin_trial;
mod schema;
pub use definition::{DefinitionError, Endpoint, Registry, Tool, ToolDef};
pub use manifest::{Csrf, Manifest, ScriptTagOptions, Transport};
pub use origin_trial::*;
pub type Error = DefinitionError;
pub const RUNTIME_JS: &str = include_str!("../runtime/webmcp-runtime.js");
pub const RUNTIME_SHA256: &str = "287200d4ef89e16ccd0f0bbb035d9b484c3a761503f3df40f8f1cf5f3baae452";
pub(crate) fn escape_html(value: &str) -> String {
value
.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
pub fn form_attrs(tool: &str, description: &str, autosubmit: bool) -> Result<String, Error> {
definition::validate_name(tool)?;
if description.is_empty() {
return Err(DefinitionError::new(
"form description must be a nonempty string",
));
}
Ok(format!(
"toolname=\"{}\" tooldescription=\"{}\"{}",
escape_html(tool),
escape_html(description),
if autosubmit { " toolautosubmit" } else { "" }
))
}
pub fn param_attr(description: &str) -> String {
format!("toolparamdescription=\"{}\"", escape_html(description))
}
pub fn runtime_script_tag(src: &str, nonce: Option<&str>) -> String {
format!(
"<script type=\"module\" src=\"{}\"{}></script>",
escape_html(src),
nonce_attr(nonce)
)
}
pub(crate) fn nonce_attr(nonce: Option<&str>) -> String {
nonce
.map(|value| format!(" nonce=\"{}\"", escape_html(value)))
.unwrap_or_default()
}