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}