turbomcp_client/client/operations/
resources.rs

1//! Resource operations for MCP client
2//!
3//! This module provides resource-related functionality including listing resources,
4//! reading resource content, and managing resource templates.
5
6use std::sync::atomic::Ordering;
7
8use turbomcp_protocol::types::{
9    ListResourceTemplatesResult, ListResourcesResult, ReadResourceRequest, ReadResourceResult,
10    Resource,
11};
12use turbomcp_protocol::{Error, Result};
13
14impl<T: turbomcp_transport::Transport + 'static> super::super::core::Client<T> {
15    /// List available resources from the MCP server
16    ///
17    /// Returns a list of resources with their full metadata including URIs, names,
18    /// descriptions, MIME types, and other attributes provided by the server.
19    /// Resources represent data or content that can be accessed by the client.
20    ///
21    /// # Returns
22    ///
23    /// Returns a vector of `Resource` objects containing full metadata that can be
24    /// read using `read_resource()`.
25    ///
26    /// # Errors
27    ///
28    /// Returns an error if:
29    /// - The client is not initialized
30    /// - The server doesn't support resources
31    /// - The request fails
32    ///
33    /// # Examples
34    ///
35    /// ```rust,no_run
36    /// # use turbomcp_client::Client;
37    /// # use turbomcp_transport::stdio::StdioTransport;
38    /// # async fn example() -> turbomcp_protocol::Result<()> {
39    /// let mut client = Client::new(StdioTransport::new());
40    /// client.initialize().await?;
41    ///
42    /// let resources = client.list_resources().await?;
43    /// for resource in resources {
44    ///     println!("Resource: {} ({})", resource.name, resource.uri);
45    ///     if let Some(desc) = &resource.description {
46    ///         println!("  Description: {}", desc);
47    ///     }
48    /// }
49    /// # Ok(())
50    /// # }
51    /// ```
52    pub async fn list_resources(&self) -> Result<Vec<Resource>> {
53        if !self.inner.initialized.load(Ordering::Relaxed) {
54            return Err(Error::bad_request("Client not initialized"));
55        }
56
57        // Execute with plugin middleware
58        let response: ListResourcesResult =
59            self.execute_with_plugins("resources/list", None).await?;
60
61        Ok(response.resources)
62    }
63
64    /// Read the content of a specific resource by URI
65    ///
66    /// Retrieves the content of a resource identified by its URI.
67    /// Resources can contain text, binary data, or structured content.
68    ///
69    /// # Arguments
70    ///
71    /// * `uri` - The URI of the resource to read
72    ///
73    /// # Returns
74    ///
75    /// Returns `ReadResourceResult` containing the resource content and metadata.
76    ///
77    /// # Errors
78    ///
79    /// Returns an error if:
80    /// - The client is not initialized
81    /// - The URI is empty or invalid
82    /// - The resource doesn't exist
83    /// - Access to the resource is denied
84    ///
85    /// # Examples
86    ///
87    /// ```rust,no_run
88    /// # use turbomcp_client::Client;
89    /// # use turbomcp_transport::stdio::StdioTransport;
90    /// # async fn example() -> turbomcp_protocol::Result<()> {
91    /// let mut client = Client::new(StdioTransport::new());
92    /// client.initialize().await?;
93    ///
94    /// let result = client.read_resource("file:///path/to/document.txt").await?;
95    /// for content in result.contents {
96    ///     println!("Resource content: {:?}", content);
97    /// }
98    /// # Ok(())
99    /// # }
100    /// ```
101    pub async fn read_resource(&self, uri: &str) -> Result<ReadResourceResult> {
102        if !self.inner.initialized.load(Ordering::Relaxed) {
103            return Err(Error::bad_request("Client not initialized"));
104        }
105
106        if uri.is_empty() {
107            return Err(Error::bad_request("Resource URI cannot be empty"));
108        }
109
110        // Send read_resource request
111        let request = ReadResourceRequest {
112            uri: uri.to_string(),
113            _meta: None,
114        };
115
116        let response: ReadResourceResult = self
117            .execute_with_plugins("resources/read", Some(serde_json::to_value(request)?))
118            .await?;
119        Ok(response)
120    }
121
122    /// List available resource templates from the MCP server
123    ///
124    /// Returns a list of resource template URIs that define patterns for
125    /// generating resource URIs. Templates allow servers to describe
126    /// families of related resources without listing each individual resource.
127    ///
128    /// # Returns
129    ///
130    /// Returns a vector of resource template URI patterns.
131    ///
132    /// # Errors
133    ///
134    /// Returns an error if:
135    /// - The client is not initialized
136    /// - The server doesn't support resource templates
137    /// - The request fails
138    ///
139    /// # Examples
140    ///
141    /// ```rust,no_run
142    /// # use turbomcp_client::Client;
143    /// # use turbomcp_transport::stdio::StdioTransport;
144    /// # async fn example() -> turbomcp_protocol::Result<()> {
145    /// let mut client = Client::new(StdioTransport::new());
146    /// client.initialize().await?;
147    ///
148    /// let templates = client.list_resource_templates().await?;
149    /// for template in templates {
150    ///     println!("Resource template: {}", template);
151    /// }
152    /// # Ok(())
153    /// # }
154    /// ```
155    pub async fn list_resource_templates(&self) -> Result<Vec<String>> {
156        if !self.inner.initialized.load(Ordering::Relaxed) {
157            return Err(Error::bad_request("Client not initialized"));
158        }
159
160        // Send resources/templates request with plugin middleware
161        let response: ListResourceTemplatesResult = self
162            .execute_with_plugins("resources/templates", None)
163            .await?;
164        let template_uris = response
165            .resource_templates
166            .into_iter()
167            .map(|template| template.uri_template)
168            .collect();
169        Ok(template_uris)
170    }
171}