Skip to main content

distri_types/
integration.rs

1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::sync::Arc;
5
6use crate::{AuthMetadata, Tool};
7
8/// OAuth provider configuration for dynamic registration
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct OAuthProviderConfig {
11    pub provider: String,
12    pub client_id: Option<String>,
13    pub client_secret: Option<String>,
14    pub authorization_url: String,
15    pub token_url: String,
16    pub refresh_url: Option<String>,
17    pub scopes: Vec<String>,
18    pub redirect_uri: Option<String>,
19}
20
21/// Secret provider configuration
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct SecretProviderConfig {
24    pub provider: String,
25    pub key_name: String,
26    pub location: Option<String>, // header, query, body
27    pub description: Option<String>,
28}
29
30/// Discriminated union for auth provider configuration
31#[derive(Debug, Clone, Serialize, Deserialize)]
32#[serde(tag = "type")]
33pub enum AuthProviderConfig {
34    #[serde(rename = "oauth")]
35    OAuth(OAuthProviderConfig),
36    #[serde(rename = "secret")]
37    Secret(SecretProviderConfig),
38}
39
40/// Integration represents a group of tools with shared authentication,
41/// notifications, and other common functionality (similar to MCP servers)
42#[async_trait]
43pub trait Integration: Send + Sync + std::fmt::Debug {
44    /// Get the integration name/identifier
45    fn get_name(&self) -> String;
46
47    /// Get description of what this integration provides
48    fn get_description(&self) -> String;
49
50    /// Get the version of this integration
51    fn get_version(&self) -> String {
52        "1.0.0".to_string()
53    }
54
55    /// Get authentication metadata for this integration
56    fn get_auth_metadata(&self) -> Option<Box<dyn AuthMetadata>> {
57        None
58    }
59
60    /// Get all tools provided by this integration
61    fn get_tools(&self) -> Vec<Arc<dyn Tool>>;
62
63    /// Get callback schema (JSON schema for callbacks)
64    fn get_callbacks(&self) -> HashMap<String, serde_json::Value> {
65        HashMap::new() // Default: no callbacks
66    }
67
68    /// Get notifications/events this integration can send (future feature)
69    fn get_notifications(&self) -> Vec<String> {
70        vec![] // Default to no notifications
71    }
72
73    /// Get additional metadata about this integration
74    fn get_metadata(&self) -> HashMap<String, serde_json::Value> {
75        HashMap::new()
76    }
77
78    /// Initialize the integration (connect, validate auth, etc.)
79    async fn initialize(&self) -> Result<(), anyhow::Error> {
80        Ok(()) // Default: no initialization needed
81    }
82
83    /// Shutdown/cleanup the integration
84    async fn shutdown(&self) -> Result<(), anyhow::Error> {
85        Ok(()) // Default: no cleanup needed
86    }
87}
88
89/// Wrapper for tools that come from integrations
90/// This allows us to identify which integration a tool belongs to
91#[derive(Debug)]
92pub struct IntegrationTool {
93    /// The underlying tool implementation
94    tool: Arc<dyn Tool>,
95    /// The integration this tool belongs to
96    integration_name: String,
97}
98
99impl IntegrationTool {
100    /// Create a new IntegrationTool
101    pub fn new(tool: Arc<dyn Tool>, integration_name: String) -> Self {
102        Self {
103            tool,
104            integration_name,
105        }
106    }
107
108    /// Get the integration name this tool belongs to
109    pub fn get_integration_name(&self) -> &str {
110        &self.integration_name
111    }
112
113    /// Get the underlying tool
114    pub fn get_tool(&self) -> &Arc<dyn Tool> {
115        &self.tool
116    }
117}
118
119#[async_trait]
120impl Tool for IntegrationTool {
121    fn get_name(&self) -> String {
122        self.tool.get_name()
123    }
124
125    fn get_parameters(&self) -> serde_json::Value {
126        self.tool.get_parameters()
127    }
128
129    fn get_description(&self) -> String {
130        self.tool.get_description()
131    }
132
133    fn is_external(&self) -> bool {
134        self.tool.is_external()
135    }
136
137    fn is_mcp(&self) -> bool {
138        self.tool.is_mcp()
139    }
140
141    fn is_sync(&self) -> bool {
142        self.tool.is_sync()
143    }
144
145    fn is_final(&self) -> bool {
146        self.tool.is_final()
147    }
148
149    fn needs_executor_context(&self) -> bool {
150        self.tool.needs_executor_context()
151    }
152
153    /// Override to return tool's auth metadata - integration auth is handled separately
154    fn get_auth_metadata(&self) -> Option<Box<dyn AuthMetadata>> {
155        // Return tool's own auth metadata
156        // Integration-level auth is handled by the integration system
157        self.tool.get_auth_metadata()
158    }
159
160    /// Return the plugin name this tool belongs to
161    fn get_plugin_name(&self) -> Option<String> {
162        Some(self.integration_name.clone())
163    }
164
165    async fn execute(
166        &self,
167        tool_call: crate::ToolCall,
168        context: Arc<crate::ToolContext>,
169    ) -> Result<Vec<crate::Part>, anyhow::Error> {
170        self.tool.execute(tool_call, context).await
171    }
172
173    fn execute_sync(
174        &self,
175        tool_call: crate::ToolCall,
176        context: Arc<crate::ToolContext>,
177    ) -> Result<Vec<crate::Part>, anyhow::Error> {
178        self.tool.execute_sync(tool_call, context)
179    }
180}
181
182/// Information about an integration (for discovery/listing)
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct IntegrationInfo {
185    pub name: String,
186    pub description: String,
187    pub version: String,
188    pub tools: Vec<String>, // Tool names
189    pub callbacks: HashMap<String, serde_json::Value>,
190    pub notifications: Vec<String>,
191    pub requires_auth: bool,
192    pub auth_entity: Option<String>,
193    pub metadata: HashMap<String, serde_json::Value>,
194}
195
196/// Plugin data structure matching TypeScript format - uses existing types
197#[derive(Debug, Clone, Serialize, Deserialize)]
198pub struct PluginData {
199    #[serde(default)]
200    pub package_name: String,
201    pub integrations: Vec<IntegrationData>,
202}
203
204/// Tool definition extended with auth and integration info
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct IntegrationToolDefinition {
207    pub name: String,
208    pub description: String,
209    pub version: Option<String>,
210    #[serde(default)]
211    pub parameters: serde_json::Value,
212    #[serde(default, skip_serializing_if = "Option::is_none", rename = "auth")]
213    pub auth: Option<crate::AuthRequirement>,
214}
215
216/// Integration data structure matching TypeScript format
217#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct IntegrationData {
219    #[serde(default)]
220    pub name: String,
221
222    pub description: String,
223    pub version: String,
224    #[serde(default)]
225    pub tools: Vec<IntegrationToolDefinition>,
226    #[serde(default)]
227    pub callbacks: HashMap<String, serde_json::Value>,
228    #[serde(default, skip_serializing_if = "Option::is_none", rename = "auth")]
229    pub auth: Option<crate::AuthRequirement>,
230    #[serde(default)]
231    pub notifications: Vec<String>,
232    #[serde(default)]
233    pub metadata: HashMap<String, serde_json::Value>,
234}
235
236impl IntegrationInfo {
237    pub fn from_integration(integration: &dyn Integration) -> Self {
238        let auth_metadata = integration.get_auth_metadata();
239        let (requires_auth, auth_entity) = if let Some(auth) = auth_metadata.as_ref() {
240            (auth.requires_auth(), Some(auth.get_auth_entity()))
241        } else {
242            (false, None)
243        };
244
245        Self {
246            name: integration.get_name(),
247            description: integration.get_description(),
248            version: integration.get_version(),
249            tools: integration
250                .get_tools()
251                .iter()
252                .map(|t| t.get_name())
253                .collect(),
254            callbacks: integration.get_callbacks(),
255            notifications: integration.get_notifications(),
256            requires_auth,
257            auth_entity,
258            metadata: integration.get_metadata(),
259        }
260    }
261}