turul_mcp_builders/traits/resource_traits.rs
1//! Framework traits for MCP resource construction
2//!
3//! **IMPORTANT**: These are framework features, NOT part of the MCP specification.
4//! The MCP specification defines concrete types only.
5
6use serde_json::Value;
7use std::collections::HashMap;
8
9// Import protocol types (spec-defined)
10use turul_mcp_protocol::Resource;
11use turul_mcp_protocol::meta::Annotations;
12
13pub trait HasResourceMetadata {
14 /// Programmatic identifier (fallback display name)
15 fn name(&self) -> &str;
16
17 /// Human-readable display name (UI contexts)
18 fn title(&self) -> Option<&str> {
19 None
20 }
21}
22
23/// Resource description trait
24pub trait HasResourceDescription {
25 fn description(&self) -> Option<&str> {
26 None
27 }
28}
29
30/// Resource URI trait
31pub trait HasResourceUri {
32 fn uri(&self) -> &str;
33}
34
35/// Resource MIME type trait
36pub trait HasResourceMimeType {
37 fn mime_type(&self) -> Option<&str> {
38 None
39 }
40}
41
42/// Resource size trait
43pub trait HasResourceSize {
44 fn size(&self) -> Option<u64> {
45 None
46 }
47}
48
49/// Resource annotations trait
50pub trait HasResourceAnnotations {
51 fn annotations(&self) -> Option<&Annotations> {
52 None
53 }
54}
55
56/// Resource-specific meta trait (separate from RPC _meta)
57pub trait HasResourceMeta {
58 fn resource_meta(&self) -> Option<&HashMap<String, Value>> {
59 None
60 }
61}
62
63/// **Complete MCP Resource Creation** - Build readable resources that clients can access.
64///
65/// This trait represents a **complete, working MCP resource** that can be registered with a server
66/// and accessed by clients. When you implement the required metadata traits, you automatically
67/// get `ResourceDefinition` for free via blanket implementation.
68///
69/// ## What This Enables
70///
71/// Resources implementing `ResourceDefinition` become **full MCP citizens** that are:
72/// - 🔍 **Discoverable** via `resources/list` requests
73/// - 📖 **Readable** via `resources/read` requests
74/// - 🎯 **Template-capable** with URI variable substitution
75/// - 📡 **Protocol-ready** for JSON-RPC communication
76///
77/// ## Complete Working Example
78///
79/// ```rust
80/// use turul_mcp_protocol::Resource;
81/// use turul_mcp_protocol::meta::Annotations;
82/// use turul_mcp_builders::prelude::*; // Import framework traits
83/// use std::collections::HashMap;
84///
85/// // This struct will automatically implement ResourceDefinition!
86/// struct ApiDataFeed {
87/// uri: String,
88/// }
89///
90/// impl ApiDataFeed {
91/// fn new() -> Self {
92/// Self {
93/// uri: "https://api.example.com/data/{dataset}".to_string(),
94/// }
95/// }
96/// }
97///
98/// impl HasResourceMetadata for ApiDataFeed {
99/// fn name(&self) -> &str { "api_data" }
100/// fn title(&self) -> Option<&str> { Some("Live API Data Feed") }
101/// }
102///
103/// impl HasResourceDescription for ApiDataFeed {
104/// fn description(&self) -> Option<&str> {
105/// Some("Real-time data feed from external API")
106/// }
107/// }
108///
109/// impl HasResourceUri for ApiDataFeed {
110/// fn uri(&self) -> &str { &self.uri }
111/// }
112///
113/// impl HasResourceMimeType for ApiDataFeed {
114/// fn mime_type(&self) -> Option<&str> { Some("application/json") }
115/// }
116///
117/// impl HasResourceSize for ApiDataFeed {
118/// fn size(&self) -> Option<u64> { None }
119/// }
120///
121/// impl HasResourceAnnotations for ApiDataFeed {
122/// fn annotations(&self) -> Option<&Annotations> { None }
123/// }
124///
125/// impl HasResourceMeta for ApiDataFeed {
126/// fn resource_meta(&self) -> Option<&HashMap<String, serde_json::Value>> { None }
127/// }
128///
129/// // 🎉 ApiDataFeed now automatically implements ResourceDefinition!
130/// let resource = ApiDataFeed::new();
131/// assert_eq!(resource.name(), "api_data");
132/// assert_eq!(resource.mime_type(), Some("application/json"));
133/// ```
134///
135/// ## Usage Patterns
136///
137/// ### Easy: Use Derive Macros (see turul-mcp-derive crate)
138/// ```rust
139/// // Example of manual implementation without macros
140/// use turul_mcp_protocol::Resource;
141/// use turul_mcp_protocol::meta::Annotations;
142/// use turul_mcp_builders::prelude::*; // Import framework traits
143/// use std::collections::HashMap;
144///
145/// struct LogFiles;
146///
147/// impl HasResourceMetadata for LogFiles {
148/// fn name(&self) -> &str { "logs" }
149/// fn title(&self) -> Option<&str> { None }
150/// }
151///
152/// impl HasResourceDescription for LogFiles {
153/// fn description(&self) -> Option<&str> { None }
154/// }
155///
156/// impl HasResourceUri for LogFiles {
157/// fn uri(&self) -> &str { "file:///var/log/{service}.log" }
158/// }
159///
160/// impl HasResourceMimeType for LogFiles {
161/// fn mime_type(&self) -> Option<&str> { None }
162/// }
163///
164/// impl HasResourceSize for LogFiles {
165/// fn size(&self) -> Option<u64> { None }
166/// }
167///
168/// impl HasResourceAnnotations for LogFiles {
169/// fn annotations(&self) -> Option<&Annotations> { None }
170/// }
171///
172/// impl HasResourceMeta for LogFiles {
173/// fn resource_meta(&self) -> Option<&HashMap<String, serde_json::Value>> { None }
174/// }
175///
176/// let resource = LogFiles;
177/// assert_eq!(resource.name(), "logs");
178/// ```
179///
180/// ### Advanced: Manual Implementation (shown above)
181/// Perfect when you need dynamic URIs, custom metadata, or complex content handling.
182///
183/// ## Real-World Resource Ideas
184///
185/// - **File Resources**: Config files, logs, documentation, data exports
186/// - **API Resources**: REST endpoints, GraphQL schemas, webhook data
187/// - **Data Resources**: Database views, CSV exports, JSON feeds, report data
188/// - **System Resources**: Process info, system stats, environment configs
189/// - **Template Resources**: Dynamic content with `{variable}` substitution
190///
191/// ## URI Template Power
192///
193/// Resources support powerful URI templating:
194/// ```text
195/// Static: "file:///config.json" → Single resource
196/// Template: "file:///logs/{service}.log" → Multiple resources
197/// Complex: "api://data/{type}/{id}?fmt={fmt}" → Full parameterization
198/// ```
199///
200/// ## How It Works in MCP
201///
202/// 1. **Registration**: Server registers your resource during startup
203/// 2. **Discovery**: Client calls `resources/list` → sees available resources
204/// 3. **Template Resolution**: Client expands URI templates with parameters
205/// 4. **Reading**: Client calls `resources/read` with resolved URI
206/// 5. **Content Delivery**: Your resource returns actual content
207/// 6. **Response**: Framework serializes content back to client
208///
209/// The framework handles URI template parsing, parameter validation, and protocol serialization!
210pub trait ResourceDefinition:
211 HasResourceMetadata + // name, title (from BaseMetadata)
212 HasResourceDescription + // description
213 HasResourceUri + // uri
214 HasResourceMimeType + // mimeType
215 HasResourceSize + // size
216 HasResourceAnnotations + // annotations
217 HasResourceMeta + // _meta (resource-specific)
218 super::icon_traits::HasIcons + // icons (MCP 2025-11-25)
219 Send +
220 Sync
221{
222 /// Display name precedence: title > name (matches TypeScript spec)
223 fn display_name(&self) -> &str {
224 self.title().unwrap_or_else(|| self.name())
225 }
226
227 /// Convert to concrete Resource struct for protocol serialization
228 fn to_resource(&self) -> Resource {
229 Resource {
230 uri: self.uri().to_string(),
231 name: self.name().to_string(),
232 title: self.title().map(String::from),
233 description: self.description().map(String::from),
234 mime_type: self.mime_type().map(String::from),
235 size: self.size(),
236 annotations: self.annotations().cloned(),
237 icons: self.icons().cloned(),
238 meta: self.resource_meta().cloned(),
239 }
240 }
241}
242impl<T> ResourceDefinition for T where
243 T: HasResourceMetadata
244 + HasResourceDescription
245 + HasResourceUri
246 + HasResourceMimeType
247 + HasResourceSize
248 + HasResourceAnnotations
249 + HasResourceMeta
250 + super::icon_traits::HasIcons
251 + Send
252 + Sync
253{
254}