use subtle::ConstantTimeEq; use thiserror::Error; #[inline] pub fn ct_eq(a: &[u8], b: &[u8]) -> bool { a.ct_eq(b).into() } #[derive(Debug, Error)] pub enum McpError { #[error("mcp disabled by configuration")] Disabled, #[error("invalid mcp config: {0}")] InvalidConfig(String), #[error("mcp db error: {0}")] Db(#[from] db::conn::DbError), #[error("mcp auth: {0}")] Auth(AuthError), #[error("mcp tool `{tool}` failed: {message}")] Tool { tool: String, message: String }, #[error("internal MCP track not configured (mcp.internal_secret_key unset)")] InternalNotConfigured, #[error("mcp authentication context missing from request")] AuthContextMissing, } pub const JSONRPC_PARSE_ERROR: i64 = -32700; pub const JSONRPC_INVALID_REQUEST: i64 = -32600; pub const JSONRPC_METHOD_NOT_FOUND: i64 = -32601; pub const JSONRPC_INVALID_PARAMS: i64 = -32602; pub const JSONRPC_INTERNAL_ERROR: i64 = -32603; pub const MCP_TOOL_ERROR: i64 = -32000; pub const MCP_UNAUTHORIZED: i64 = -32001; pub const MCP_IP_NOT_ALLOWED: i64 = -32002; #[derive(Debug, Error)] pub enum AuthError { #[error("invalid credentials")] InvalidCredentials, #[error("insufficient access key scope: {0:?}")] InsufficientScope(db::user::user_access_keys::AccessKeyScope), #[error("internal MCP track not configured")] InternalNotConfigured, #[error("peer IP is not in the platform whitelist")] IpNotAllowed, } /// can pattern-match the variant. The `#[from] AuthError` on impl From for McpError { fn from(e: AuthError) -> Self { match e { AuthError::InternalNotConfigured => McpError::InternalNotConfigured, other => McpError::Auth(other), } } } #[cfg(test)] mod tests { use super::*; #[test] fn ct_eq_basic() { assert!(ct_eq(b"hello", b"hello")); assert!(!ct_eq(b"hello", b"world")); assert!(!ct_eq(b"hello", b"hell")); assert!(!ct_eq(b"hell", b"hello")); assert!(ct_eq(b"", b"")); } #[test] fn auth_error_maps_to_mcp_error() { let a = AuthError::InternalNotConfigured; let m: McpError = a.into(); assert!(matches!(m, McpError::InternalNotConfigured)); let a = AuthError::InvalidCredentials; let m: McpError = a.into(); assert!(matches!(m, McpError::Auth(AuthError::InvalidCredentials))); } }