use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
use crate::integrations::schemapin::types::KeyStoreError;
use crate::integrations::schemapin::{SchemaPinError, VerificationResult};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpTool {
pub name: String,
pub description: String,
pub schema: serde_json::Value,
pub provider: ToolProvider,
pub verification_status: VerificationStatus,
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolProvider {
pub identifier: String,
pub name: String,
pub public_key_url: String,
pub version: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum VerificationStatus {
Verified {
result: Box<VerificationResult>,
verified_at: String,
},
Failed {
reason: String,
failed_at: String,
},
Pending,
Skipped {
reason: String,
},
}
impl VerificationStatus {
pub fn is_verified(&self) -> bool {
matches!(self, VerificationStatus::Verified { .. })
}
pub fn is_failed(&self) -> bool {
matches!(self, VerificationStatus::Failed { .. })
}
pub fn is_pending(&self) -> bool {
matches!(self, VerificationStatus::Pending)
}
pub fn verification_result(&self) -> Option<&VerificationResult> {
match self {
VerificationStatus::Verified { result, .. } => Some(result),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct McpClientConfig {
pub enforce_verification: bool,
pub allow_unverified_in_dev: bool,
pub verification_timeout_seconds: u64,
pub max_concurrent_verifications: usize,
}
impl Default for McpClientConfig {
fn default() -> Self {
Self {
enforce_verification: true,
allow_unverified_in_dev: false,
verification_timeout_seconds: 30,
max_concurrent_verifications: 5,
}
}
}
#[derive(Error, Debug)]
pub enum McpClientError {
#[error("Schema verification failed: {reason}")]
VerificationFailed { reason: String },
#[error("Tool not found: {name}")]
ToolNotFound { name: String },
#[error("Tool verification required but not verified: {name}")]
ToolNotVerified { name: String },
#[error("Invalid tool schema: {reason}")]
InvalidSchema { reason: String },
#[error("Provider key retrieval failed: {reason}")]
KeyRetrievalFailed { reason: String },
#[error("Communication error: {reason}")]
CommunicationError { reason: String },
#[error("Configuration error: {reason}")]
ConfigurationError { reason: String },
#[error("SchemaPin error: {source}")]
SchemaPinError {
#[from]
source: SchemaPinError,
},
#[error("Key store error: {source}")]
KeyStoreError {
#[from]
source: KeyStoreError,
},
#[error("Serialization error: {reason}")]
SerializationError { reason: String },
#[error("Timeout occurred during operation")]
Timeout,
}
#[derive(Debug, Clone)]
pub struct ToolDiscoveryEvent {
pub tool: McpTool,
pub source: String,
pub discovered_at: String,
}
#[derive(Debug, Clone)]
pub struct ToolVerificationRequest {
pub tool: McpTool,
pub force_reverify: bool,
}
#[derive(Debug, Clone)]
pub struct ToolVerificationResponse {
pub tool_name: String,
pub status: VerificationStatus,
pub warnings: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::integrations::schemapin::VerificationResult;
#[test]
fn test_verification_status_is_verified() {
let verified_status = VerificationStatus::Verified {
result: Box::new(VerificationResult {
success: true,
message: "Test verification".to_string(),
schema_hash: Some("hash123".to_string()),
public_key_url: Some("https://example.com/key".to_string()),
signature: None,
metadata: None,
timestamp: Some("2024-01-01T00:00:00Z".to_string()),
}),
verified_at: "2024-01-01T00:00:00Z".to_string(),
};
assert!(verified_status.is_verified());
assert!(!verified_status.is_failed());
assert!(!verified_status.is_pending());
}
#[test]
fn test_verification_status_is_failed() {
let failed_status = VerificationStatus::Failed {
reason: "Invalid signature".to_string(),
failed_at: "2024-01-01T00:00:00Z".to_string(),
};
assert!(!failed_status.is_verified());
assert!(failed_status.is_failed());
assert!(!failed_status.is_pending());
}
#[test]
fn test_verification_status_is_pending() {
let pending_status = VerificationStatus::Pending;
assert!(!pending_status.is_verified());
assert!(!pending_status.is_failed());
assert!(pending_status.is_pending());
}
#[test]
fn test_verification_result_extraction() {
let result = VerificationResult {
success: true,
message: "Test verification".to_string(),
schema_hash: Some("hash123".to_string()),
public_key_url: Some("https://example.com/key".to_string()),
signature: None,
metadata: None,
timestamp: Some("2024-01-01T00:00:00Z".to_string()),
};
let verified_status = VerificationStatus::Verified {
result: Box::new(result.clone()),
verified_at: "2024-01-01T00:00:00Z".to_string(),
};
let extracted_result = verified_status.verification_result().unwrap();
assert_eq!(extracted_result.success, result.success);
assert_eq!(extracted_result.message, result.message);
}
#[test]
fn test_default_config() {
let config = McpClientConfig::default();
assert!(config.enforce_verification);
assert!(!config.allow_unverified_in_dev);
assert_eq!(config.verification_timeout_seconds, 30);
assert_eq!(config.max_concurrent_verifications, 5);
}
}