symbi_runtime/integrations/mcp/
types.rs1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct McpTool {
15 pub name: String,
17 pub description: String,
19 pub schema: serde_json::Value,
21 pub provider: ToolProvider,
23 pub verification_status: VerificationStatus,
25 pub metadata: Option<HashMap<String, serde_json::Value>>,
27 #[serde(default)]
29 pub sensitive_params: Vec<String>,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ToolProvider {
35 pub identifier: String,
37 pub name: String,
39 pub public_key_url: String,
41 pub version: Option<String>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
47pub enum VerificationStatus {
48 Verified {
50 result: Box<VerificationResult>,
52 verified_at: String,
54 },
55 Failed {
57 reason: String,
59 failed_at: String,
61 },
62 Pending,
64 Skipped {
66 reason: String,
68 },
69}
70
71impl VerificationStatus {
72 pub fn is_verified(&self) -> bool {
74 matches!(self, VerificationStatus::Verified { .. })
75 }
76
77 pub fn is_failed(&self) -> bool {
79 matches!(self, VerificationStatus::Failed { .. })
80 }
81
82 pub fn is_pending(&self) -> bool {
84 matches!(self, VerificationStatus::Pending)
85 }
86
87 pub fn verification_result(&self) -> Option<&VerificationResult> {
89 match self {
90 VerificationStatus::Verified { result, .. } => Some(result),
91 _ => None,
92 }
93 }
94}
95
96#[derive(Debug, Clone)]
98pub struct McpClientConfig {
99 pub enforce_verification: bool,
101 pub allow_unverified_in_dev: bool,
103 pub verification_timeout_seconds: u64,
105 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#[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#[derive(Debug, Clone)]
168pub struct ToolDiscoveryEvent {
169 pub tool: McpTool,
171 pub source: String,
173 pub discovered_at: String,
175}
176
177#[derive(Debug, Clone)]
179pub struct ToolVerificationRequest {
180 pub tool: McpTool,
182 pub force_reverify: bool,
184}
185
186#[derive(Debug, Clone)]
188pub struct ToolVerificationResponse {
189 pub tool_name: String,
191 pub status: VerificationStatus,
193 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}