mod activity;
mod discovery;
mod http;
mod proxy;
mod transport;
pub use activity::Activity;
pub use discovery::Discovery;
pub use http::{HttpRequest, HttpResponse, parse_request};
pub use proxy::{is_alive, proxy_streams};
pub use transport::{HttpTransport, handle, post};
pub const MCP_PATH: &str = "/mcp";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Token(String);
impl Token {
pub fn generate() -> Self {
let mut bytes = [0u8; 32];
getrandom::fill(&mut bytes).expect("the OS random source must be available");
let hex: String = bytes.iter().map(|byte| format!("{byte:02x}")).collect();
Self(hex)
}
pub fn from_string(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn expose(&self) -> &str {
&self.0
}
pub fn matches(&self, candidate: &str) -> bool {
let expected = self.0.as_bytes();
let candidate = candidate.as_bytes();
if expected.len() != candidate.len() {
return false;
}
let mut diff = 0u8;
for (a, b) in expected.iter().zip(candidate.iter()) {
diff |= a ^ b;
}
diff == 0
}
}
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Token(***)")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_generated_token_is_long_and_unpredictable() {
let first = Token::generate();
let second = Token::generate();
assert_eq!(first.expose().len(), 64);
assert_ne!(first, second);
}
#[test]
fn token_equality_is_exact() {
let token = Token::from_string("secret-value");
assert!(token.matches("secret-value"));
assert!(!token.matches("secret-valuX"));
assert!(!token.matches("secret"));
assert!(!token.matches(""));
}
#[test]
fn the_display_does_not_leak_the_token() {
let token = Token::from_string("super-secret");
assert_eq!(token.to_string(), "Token(***)");
}
}