Skip to main content

composio_sdk/models/
tools.rs

1//! Tools management
2//!
3//! This module provides functionality to manage and execute tools in Composio.
4//! Tools are individual actions that can be performed on external services.
5//!
6//! # Overview
7//!
8//! Tools represent specific actions like "create GitHub issue", "send email", etc.
9//! They can be:
10//! - Composio-managed tools (from the platform)
11//! - Custom tools (user-defined)
12//! - Meta tools (for tool discovery and management)
13//!
14//! # Type Organization
15//!
16//! This module re-exports types from other modules to maintain a clean API:
17//! - `ToolExecutionResponse` - from `models::response`
18//! - `ToolExecuteParams` - from `models::modifiers`
19//! - `CustomAuthParams` - from `models::modifiers`
20//! - `CustomConnectionData` - from `models::modifiers`
21//!
22//! This organization follows the Python SDK's `types.py` pattern where common
23//! types are re-exported from their canonical locations.
24
25use serde::{Deserialize, Serialize};
26use std::collections::HashMap;
27
28// Re-export types from other modules to maintain compatibility
29pub use crate::models::modifiers::{CustomAuthParams, CustomConnectionData, ToolExecuteParams};
30pub use crate::models::response::ToolExecutionResponse;
31
32/// Tool list parameters
33#[derive(Debug, Clone, Default, Serialize, Deserialize)]
34pub struct ToolListParams {
35    /// Filter by specific tool slugs
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub tool_slugs: Option<Vec<String>>,
38
39    /// Filter by toolkit slug
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub toolkit_slug: Option<String>,
42
43    /// Search query
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub search: Option<String>,
46
47    /// Filter by scopes
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub scopes: Option<Vec<String>>,
50
51    /// Filter by tags
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub tags: Option<Vec<String>>,
54
55    /// Filter by importance
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub importance: Option<String>,
58
59    /// Show deprecated tools
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub show_deprecated: Option<bool>,
62
63    /// Maximum number of results
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub limit: Option<u32>,
66
67    /// Pagination cursor
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub cursor: Option<String>,
70
71    /// Toolkit versions
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub toolkit_versions: Option<String>,
74}
75
76/// Tool information
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ToolInfo {
79    /// Tool slug
80    pub slug: String,
81
82    /// Tool name
83    pub name: String,
84
85    /// Tool description
86    pub description: String,
87
88    /// Toolkit information
89    pub toolkit: ToolkitRef,
90
91    /// Input parameters schema
92    pub input_parameters: serde_json::Value,
93
94    /// Output parameters schema
95    pub output_parameters: serde_json::Value,
96
97    /// Required OAuth scopes
98    #[serde(default)]
99    pub scopes: Vec<String>,
100
101    /// Tool tags
102    #[serde(default)]
103    pub tags: Vec<String>,
104
105    /// Tool version
106    pub version: String,
107
108    /// Available versions
109    #[serde(default)]
110    pub available_versions: Vec<String>,
111
112    /// Whether the tool is deprecated
113    #[serde(default)]
114    pub is_deprecated: bool,
115
116    /// Whether authentication is required
117    #[serde(default)]
118    pub no_auth: bool,
119}
120
121/// Toolkit reference in tool info
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct ToolkitRef {
124    /// Toolkit slug
125    pub slug: String,
126
127    /// Toolkit name
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub name: Option<String>,
130
131    /// Toolkit logo URL
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub logo: Option<String>,
134}
135
136/// Tool list response
137#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct ToolListResponse {
139    /// List of tools
140    pub items: Vec<ToolInfo>,
141
142    /// Next cursor for pagination
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub next_cursor: Option<String>,
145
146    /// Total number of pages
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub total_pages: Option<u32>,
149
150    /// Current page number
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub current_page: Option<u32>,
153
154    /// Total number of items
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub total_items: Option<u32>,
157}
158
159/// Tool enum response
160///
161/// Contains all available tool slug enumeration values.
162pub type ToolRetrieveEnumResponse = Vec<String>;
163
164/// Tool proxy parameters
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct ToolProxyParams {
167    /// API endpoint to call
168    pub endpoint: String,
169
170    /// HTTP method
171    pub method: HttpMethod,
172
173    /// Request body
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub body: Option<serde_json::Value>,
176
177    /// Connected account ID
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub connected_account_id: Option<String>,
180
181    /// Additional parameters (headers, query params)
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub parameters: Option<Vec<ProxyParameter>>,
184
185    /// Custom connection data
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub custom_connection_data: Option<CustomConnectionData>,
188}
189
190/// HTTP method enumeration
191#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
192#[serde(rename_all = "UPPERCASE")]
193pub enum HttpMethod {
194    Get,
195    Post,
196    Put,
197    Delete,
198    Patch,
199    Head,
200    Options,
201}
202
203/// Proxy parameter (header or query param)
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct ProxyParameter {
206    /// Parameter name
207    pub name: String,
208
209    /// Parameter value
210    pub value: String,
211
212    /// Where to include the parameter
213    #[serde(rename = "in")]
214    pub location: ParameterLocation,
215}
216
217/// Parameter location enumeration
218#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
219#[serde(rename_all = "lowercase")]
220pub enum ParameterLocation {
221    /// Include in headers
222    Header,
223
224    /// Include in query parameters
225    Query,
226
227    /// Include in path
228    Path,
229
230    /// Include in body
231    Body,
232}
233
234/// Tool proxy response
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ToolProxyResponse {
237    /// Response data
238    pub data: serde_json::Value,
239
240    /// HTTP status code
241    pub status: u16,
242
243    /// Response headers
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub headers: Option<HashMap<String, String>>,
246
247    /// Binary data (base64 encoded)
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub binary_data: Option<String>,
250
251    /// Whether the request was successful
252    pub successful: bool,
253
254    /// Error message if failed
255    #[serde(skip_serializing_if = "Option::is_none")]
256    pub error: Option<String>,
257}
258
259/// Tool input generation parameters
260#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ToolInputGenerationParams {
262    /// Tool slug
263    pub tool_slug: String,
264
265    /// Natural language description
266    pub text: String,
267
268    /// Custom tool description (optional)
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub custom_tool_description: Option<String>,
271
272    /// Custom system prompt (optional)
273    #[serde(skip_serializing_if = "Option::is_none")]
274    pub custom_system_prompt: Option<String>,
275}
276
277/// Tool input generation response
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct ToolInputGenerationResponse {
280    /// Generated arguments
281    #[serde(skip_serializing_if = "Option::is_none")]
282    pub arguments: Option<HashMap<String, serde_json::Value>>,
283
284    /// Error message if generation failed
285    #[serde(skip_serializing_if = "Option::is_none")]
286    pub error: Option<String>,
287
288    /// Whether generation was successful
289    pub successful: bool,
290}
291
292/// Custom tool definition
293#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct CustomToolDefinition {
295    /// Tool slug (unique identifier)
296    pub slug: String,
297
298    /// Tool name
299    pub name: String,
300
301    /// Tool description
302    pub description: String,
303
304    /// Input parameters schema
305    pub input_schema: serde_json::Value,
306
307    /// Output schema (optional)
308    #[serde(skip_serializing_if = "Option::is_none")]
309    pub output_schema: Option<serde_json::Value>,
310
311    /// Toolkit slug (if toolkit-based)
312    #[serde(skip_serializing_if = "Option::is_none")]
313    pub toolkit: Option<String>,
314
315    /// Whether authentication is required
316    #[serde(default)]
317    pub requires_auth: bool,
318}
319
320/// Custom tool execution request
321#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct CustomToolExecutionRequest {
323    /// Tool slug
324    pub slug: String,
325
326    /// Execution arguments
327    pub arguments: HashMap<String, serde_json::Value>,
328
329    /// User ID (optional)
330    #[serde(skip_serializing_if = "Option::is_none")]
331    pub user_id: Option<String>,
332
333    /// Connected account ID (optional)
334    #[serde(skip_serializing_if = "Option::is_none")]
335    pub connected_account_id: Option<String>,
336}
337
338// Note: The Tools resource implementation is pending full HTTP client integration.
339// The data structures above are ready for use once the client supports generic HTTP methods.
340
341#[cfg(test)]
342mod tests {
343    use super::*;
344
345    #[test]
346    fn test_tool_execution_response_reexport() {
347        // Test that re-exported type works
348        let response = ToolExecutionResponse {
349            data: serde_json::json!({"result": "success"}),
350            error: None,
351            successful: true,
352            log_id: Some("log_123".to_string()),
353            session_info: None,
354        };
355
356        let json = serde_json::to_string(&response).unwrap();
357        assert!(json.contains("success"));
358        assert!(json.contains("log_123"));
359    }
360
361    #[test]
362    fn test_http_method_serialization() {
363        let method = HttpMethod::Post;
364        let json = serde_json::to_string(&method).unwrap();
365        assert_eq!(json, "\"POST\"");
366    }
367
368    #[test]
369    fn test_parameter_location_serialization() {
370        let location = ParameterLocation::Header;
371        let json = serde_json::to_string(&location).unwrap();
372        assert_eq!(json, "\"header\"");
373    }
374
375    #[test]
376    fn test_tool_list_params_default() {
377        let params = ToolListParams::default();
378        assert!(params.tool_slugs.is_none());
379        assert!(params.toolkit_slug.is_none());
380        assert!(params.search.is_none());
381    }
382
383    #[test]
384    fn test_tool_execute_params_reexport() {
385        // Test that re-exported type works
386        let mut arguments = HashMap::new();
387        arguments.insert("title".to_string(), serde_json::json!("Test"));
388
389        let params = ToolExecuteParams {
390            allow_tracing: None,
391            arguments,
392            connected_account_id: Some("ca_123".to_string()),
393            custom_auth_params: None,
394            custom_connection_data: None,
395            entity_id: None,
396            text: None,
397            user_id: Some("user_456".to_string()),
398            version: Some("1.0.0".to_string()),
399            dangerously_skip_version_check: Some(false),
400        };
401
402        let json = serde_json::to_string(&params).unwrap();
403        assert!(json.contains("ca_123"));
404        assert!(json.contains("user_456"));
405    }
406
407    #[test]
408    fn test_tool_proxy_params() {
409        let params = ToolProxyParams {
410            endpoint: "/api/v1/users".to_string(),
411            method: HttpMethod::Get,
412            body: None,
413            connected_account_id: Some("ca_123".to_string()),
414            parameters: Some(vec![ProxyParameter {
415                name: "Authorization".to_string(),
416                value: "Bearer token".to_string(),
417                location: ParameterLocation::Header,
418            }]),
419            custom_connection_data: None,
420        };
421
422        assert_eq!(params.method, HttpMethod::Get);
423        assert_eq!(params.endpoint, "/api/v1/users");
424        assert!(params.parameters.is_some());
425    }
426
427    #[test]
428    fn test_custom_tool_definition() {
429        let tool = CustomToolDefinition {
430            slug: "my_custom_tool".to_string(),
431            name: "My Custom Tool".to_string(),
432            description: "A custom tool for testing".to_string(),
433            input_schema: serde_json::json!({
434                "type": "object",
435                "properties": {
436                    "input": {"type": "string"}
437                }
438            }),
439            output_schema: None,
440            toolkit: None,
441            requires_auth: false,
442        };
443
444        assert_eq!(tool.slug, "my_custom_tool");
445        assert!(!tool.requires_auth);
446    }
447
448    #[test]
449    fn test_tool_info_deserialization() {
450        let json = r#"{
451            "slug": "GITHUB_CREATE_ISSUE",
452            "name": "Create Issue",
453            "description": "Create a new issue",
454            "toolkit": {
455                "slug": "github",
456                "name": "GitHub"
457            },
458            "input_parameters": {},
459            "output_parameters": {},
460            "scopes": ["repo"],
461            "tags": ["write"],
462            "version": "1.0.0",
463            "available_versions": ["1.0.0"],
464            "is_deprecated": false,
465            "no_auth": false
466        }"#;
467
468        let tool: ToolInfo = serde_json::from_str(json).unwrap();
469        assert_eq!(tool.slug, "GITHUB_CREATE_ISSUE");
470        assert_eq!(tool.toolkit.slug, "github");
471        assert_eq!(tool.scopes.len(), 1);
472    }
473}