Skip to main content

symbi_runtime/integrations/mcp/
types.rs

1//! MCP Client Types
2//!
3//! Defines types for the Model Context Protocol client with schema verification
4
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use thiserror::Error;
8
9use crate::integrations::schemapin::types::KeyStoreError;
10use crate::integrations::schemapin::{SchemaPinError, VerificationResult};
11
12/// MCP tool definition with verification status
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct McpTool {
15    /// Tool name
16    pub name: String,
17    /// Tool description
18    pub description: String,
19    /// Tool schema (JSON Schema)
20    pub schema: serde_json::Value,
21    /// Provider information
22    pub provider: ToolProvider,
23    /// Verification status
24    pub verification_status: VerificationStatus,
25    /// Additional metadata
26    pub metadata: Option<HashMap<String, serde_json::Value>>,
27    /// Parameter names that contain sensitive data and should be redacted in logs
28    #[serde(default)]
29    pub sensitive_params: Vec<String>,
30}
31
32/// Tool provider information
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ToolProvider {
35    /// Provider identifier (e.g., domain name)
36    pub identifier: String,
37    /// Provider name
38    pub name: String,
39    /// Public key URL for verification
40    pub public_key_url: String,
41    /// Provider version
42    pub version: Option<String>,
43}
44
45/// Verification status of a tool
46#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
47pub enum VerificationStatus {
48    /// Tool has been verified successfully
49    Verified {
50        /// Verification result details
51        result: Box<VerificationResult>,
52        /// Timestamp of verification
53        verified_at: String,
54    },
55    /// Tool verification failed
56    Failed {
57        /// Reason for failure
58        reason: String,
59        /// Timestamp of failed verification
60        failed_at: String,
61    },
62    /// Tool has not been verified yet
63    Pending,
64    /// Tool verification was skipped
65    Skipped {
66        /// Reason for skipping
67        reason: String,
68    },
69}
70
71impl VerificationStatus {
72    /// Check if the tool is verified
73    pub fn is_verified(&self) -> bool {
74        matches!(self, VerificationStatus::Verified { .. })
75    }
76
77    /// Check if verification failed
78    pub fn is_failed(&self) -> bool {
79        matches!(self, VerificationStatus::Failed { .. })
80    }
81
82    /// Check if verification is pending
83    pub fn is_pending(&self) -> bool {
84        matches!(self, VerificationStatus::Pending)
85    }
86
87    /// Get the verification result if available
88    pub fn verification_result(&self) -> Option<&VerificationResult> {
89        match self {
90            VerificationStatus::Verified { result, .. } => Some(result),
91            _ => None,
92        }
93    }
94}
95
96/// MCP client configuration
97#[derive(Debug, Clone)]
98pub struct McpClientConfig {
99    /// Whether to enforce schema verification
100    pub enforce_verification: bool,
101    /// Whether to allow unverified tools in development mode
102    pub allow_unverified_in_dev: bool,
103    /// Timeout for verification operations in seconds
104    pub verification_timeout_seconds: u64,
105    /// Maximum number of concurrent verifications
106    pub max_concurrent_verifications: usize,
107}
108
109impl Default for McpClientConfig {
110    fn default() -> Self {
111        Self {
112            enforce_verification: true,
113            allow_unverified_in_dev: false,
114            verification_timeout_seconds: 30,
115            max_concurrent_verifications: 5,
116        }
117    }
118}
119
120/// MCP client errors
121#[derive(Error, Debug)]
122pub enum McpClientError {
123    #[error("Schema verification failed: {reason}")]
124    VerificationFailed { reason: String },
125
126    #[error("Tool not found: {name}")]
127    ToolNotFound { name: String },
128
129    #[error("Tool verification required but not verified: {name}")]
130    ToolNotVerified { name: String },
131
132    #[error("Invalid tool schema: {reason}")]
133    InvalidSchema { reason: String },
134
135    #[error("Provider key retrieval failed: {reason}")]
136    KeyRetrievalFailed { reason: String },
137
138    #[error("Communication error: {reason}")]
139    CommunicationError { reason: String },
140
141    #[error("Configuration error: {reason}")]
142    ConfigurationError { reason: String },
143
144    #[error("SchemaPin error: {source}")]
145    SchemaPinError {
146        #[from]
147        source: SchemaPinError,
148    },
149
150    #[error("Key store error: {source}")]
151    KeyStoreError {
152        #[from]
153        source: KeyStoreError,
154    },
155
156    #[error("Serialization error: {reason}")]
157    SerializationError { reason: String },
158
159    #[error("Key fetch failed for provider '{provider}': {reason}")]
160    KeyFetchFailed { provider: String, reason: String },
161
162    #[error("Timeout occurred during operation")]
163    Timeout,
164}
165
166/// Tool discovery event
167#[derive(Debug, Clone)]
168pub struct ToolDiscoveryEvent {
169    /// The discovered tool
170    pub tool: McpTool,
171    /// Source of the discovery
172    pub source: String,
173    /// Timestamp of discovery
174    pub discovered_at: String,
175}
176
177/// Tool verification request
178#[derive(Debug, Clone)]
179pub struct ToolVerificationRequest {
180    /// Tool to verify
181    pub tool: McpTool,
182    /// Whether to force re-verification
183    pub force_reverify: bool,
184}
185
186/// Tool verification response
187#[derive(Debug, Clone)]
188pub struct ToolVerificationResponse {
189    /// Tool name
190    pub tool_name: String,
191    /// Verification status
192    pub status: VerificationStatus,
193    /// Any warnings during verification
194    pub warnings: Vec<String>,
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200    use crate::integrations::schemapin::VerificationResult;
201
202    #[test]
203    fn test_verification_status_is_verified() {
204        let verified_status = VerificationStatus::Verified {
205            result: Box::new(VerificationResult {
206                success: true,
207                message: "Test verification".to_string(),
208                schema_hash: Some("hash123".to_string()),
209                public_key_url: Some("https://example.com/key".to_string()),
210                signature: None,
211                metadata: None,
212                timestamp: Some("2024-01-01T00:00:00Z".to_string()),
213            }),
214            verified_at: "2024-01-01T00:00:00Z".to_string(),
215        };
216
217        assert!(verified_status.is_verified());
218        assert!(!verified_status.is_failed());
219        assert!(!verified_status.is_pending());
220    }
221
222    #[test]
223    fn test_verification_status_is_failed() {
224        let failed_status = VerificationStatus::Failed {
225            reason: "Invalid signature".to_string(),
226            failed_at: "2024-01-01T00:00:00Z".to_string(),
227        };
228
229        assert!(!failed_status.is_verified());
230        assert!(failed_status.is_failed());
231        assert!(!failed_status.is_pending());
232    }
233
234    #[test]
235    fn test_verification_status_is_pending() {
236        let pending_status = VerificationStatus::Pending;
237
238        assert!(!pending_status.is_verified());
239        assert!(!pending_status.is_failed());
240        assert!(pending_status.is_pending());
241    }
242
243    #[test]
244    fn test_verification_result_extraction() {
245        let result = VerificationResult {
246            success: true,
247            message: "Test verification".to_string(),
248            schema_hash: Some("hash123".to_string()),
249            public_key_url: Some("https://example.com/key".to_string()),
250            signature: None,
251            metadata: None,
252            timestamp: Some("2024-01-01T00:00:00Z".to_string()),
253        };
254
255        let verified_status = VerificationStatus::Verified {
256            result: Box::new(result.clone()),
257            verified_at: "2024-01-01T00:00:00Z".to_string(),
258        };
259
260        let extracted_result = verified_status.verification_result().unwrap();
261        assert_eq!(extracted_result.success, result.success);
262        assert_eq!(extracted_result.message, result.message);
263    }
264
265    #[test]
266    fn test_default_config() {
267        let config = McpClientConfig::default();
268        assert!(config.enforce_verification);
269        assert!(!config.allow_unverified_in_dev);
270        assert_eq!(config.verification_timeout_seconds, 30);
271        assert_eq!(config.max_concurrent_verifications, 5);
272    }
273}