Skip to content

Commit 25fb8fb

Browse files
committed
fix: critical resource delegation bug in mcp_server macro
CRITICAL: This fixes the main issue in v0.9.0 where resources were not working. Changes: - Fix mcp_server macro to properly delegate to McpResourcesProvider trait - Ensure McpResourcesProvider trait is always implemented (like McpToolsProvider) - Generate empty implementation when no resources are defined - Update helper methods to work with always-available trait - Version bump to 0.9.1 for emergency patch release Resources now work properly: - resources/list shows actual resources instead of empty array - resources/read works for non-parameterized resources - No compilation errors for servers without resources - All examples compile and work correctly Resolves the broken resource functionality shipped in v0.9.0.
1 parent 4cdf31d commit 25fb8fb

File tree

5 files changed

+63
-34
lines changed

5 files changed

+63
-34
lines changed

.claude/settings.local.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@
3636
"mcp__puppeteer__puppeteer_evaluate",
3737
"WebFetch(domain:lib.rs)",
3838
"WebFetch(domain:raw.githubusercontent.com)",
39-
"Bash(npx @modelcontextprotocol/inspector:*)"
39+
"Bash(npx @modelcontextprotocol/inspector:*)",
40+
"Bash(./target/release/timedate-mcp-server:*)"
4041
],
4142
"deny": []
4243
}
43-
}
44+
}

Cargo.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ members = [
2727
resolver = "2"
2828

2929
[workspace.package]
30-
version = "0.9.0"
30+
version = "0.9.1"
3131
rust-version = "1.88"
3232
edition = "2024"
3333
license = "MIT OR Apache-2.0"
@@ -101,17 +101,17 @@ assert_matches = "1.5"
101101
serde_yaml = "0.9"
102102

103103
# Framework internal dependencies (published versions)
104-
pulseengine-mcp-protocol = { version = "0.9.0", path = "mcp-protocol" }
105-
pulseengine-mcp-logging = { version = "0.9.0", path = "mcp-logging" }
106-
pulseengine-mcp-auth = { version = "0.9.0", path = "mcp-auth" }
107-
pulseengine-mcp-security = { version = "0.9.0", path = "mcp-security" }
108-
pulseengine-mcp-monitoring = { version = "0.9.0", path = "mcp-monitoring" }
109-
pulseengine-mcp-transport = { version = "0.9.0", path = "mcp-transport" }
110-
pulseengine-mcp-cli = { version = "0.9.0", path = "mcp-cli" }
111-
pulseengine-mcp-cli-derive = { version = "0.9.0", path = "mcp-cli-derive" }
112-
pulseengine-mcp-server = { version = "0.9.0", path = "mcp-server" }
113-
pulseengine-mcp-macros = { version = "0.9.0", path = "mcp-macros" }
114-
pulseengine-mcp-external-validation = { version = "0.9.0", path = "mcp-external-validation" }
104+
pulseengine-mcp-protocol = { version = "0.9.1", path = "mcp-protocol" }
105+
pulseengine-mcp-logging = { version = "0.9.1", path = "mcp-logging" }
106+
pulseengine-mcp-auth = { version = "0.9.1", path = "mcp-auth" }
107+
pulseengine-mcp-security = { version = "0.9.1", path = "mcp-security" }
108+
pulseengine-mcp-monitoring = { version = "0.9.1", path = "mcp-monitoring" }
109+
pulseengine-mcp-transport = { version = "0.9.1", path = "mcp-transport" }
110+
pulseengine-mcp-cli = { version = "0.9.1", path = "mcp-cli" }
111+
pulseengine-mcp-cli-derive = { version = "0.9.1", path = "mcp-cli-derive" }
112+
pulseengine-mcp-server = { version = "0.9.1", path = "mcp-server" }
113+
pulseengine-mcp-macros = { version = "0.9.1", path = "mcp-macros" }
114+
pulseengine-mcp-external-validation = { version = "0.9.1", path = "mcp-external-validation" }
115115

116116
[profile.release]
117117
opt-level = "s"

mcp-macros/src/mcp_server.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,14 +218,18 @@ fn generate_server_implementation(
218218
}
219219
}
220220

221-
async fn list_resources(&self, _request: pulseengine_mcp_protocol::PaginatedRequestParam) -> std::result::Result<pulseengine_mcp_protocol::ListResourcesResult, Self::Error> {
222-
// Default implementation - no resources
223-
Ok(pulseengine_mcp_protocol::ListResourcesResult { resources: vec![], next_cursor: None })
221+
async fn list_resources(&self, request: pulseengine_mcp_protocol::PaginatedRequestParam) -> std::result::Result<pulseengine_mcp_protocol::ListResourcesResult, Self::Error> {
222+
// Use helper method that safely checks for trait implementation
223+
let resources = self.try_get_resources_default();
224+
Ok(pulseengine_mcp_protocol::ListResourcesResult { resources, next_cursor: request.cursor })
224225
}
225226

226227
async fn read_resource(&self, request: pulseengine_mcp_protocol::ReadResourceRequestParam) -> std::result::Result<pulseengine_mcp_protocol::ReadResourceResult, Self::Error> {
227-
// Default implementation - no resources
228-
Err(#error_type_name::InvalidParams(format!("Unknown resource: {}", request.uri)))
228+
// Use helper method that calls resource implementation
229+
match self.try_read_resource_default(request.clone()).await {
230+
Ok(result) => Ok(result),
231+
Err(e) => Err(#error_type_name::InvalidParams(e.to_string()))
232+
}
229233
}
230234

231235
async fn list_prompts(&self, _request: pulseengine_mcp_protocol::PaginatedRequestParam) -> std::result::Result<pulseengine_mcp_protocol::ListPromptsResult, Self::Error> {

mcp-macros/src/mcp_tool.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ pub fn mcp_tools_impl(_attr: TokenStream, item: TokenStream) -> syn::Result<Toke
318318
}
319319
}
320320

321-
// Generate resource provider implementation if there are resources
321+
// Always generate resource provider implementation (like tools)
322322
let resource_provider_impl = if !resource_definitions.is_empty() {
323323
quote! {
324324
impl #impl_generics pulseengine_mcp_server::McpResourcesProvider for #struct_name #ty_generics #where_clause {
@@ -345,7 +345,25 @@ pub fn mcp_tools_impl(_attr: TokenStream, item: TokenStream) -> syn::Result<Toke
345345
}
346346
}
347347
} else {
348-
quote! {}
348+
// Generate empty implementation when no resources are defined
349+
quote! {
350+
impl #impl_generics pulseengine_mcp_server::McpResourcesProvider for #struct_name #ty_generics #where_clause {
351+
fn get_available_resources(&self) -> Vec<pulseengine_mcp_protocol::Resource> {
352+
vec![]
353+
}
354+
355+
fn read_resource_impl(
356+
&self,
357+
request: pulseengine_mcp_protocol::ReadResourceRequestParam,
358+
) -> impl std::future::Future<Output = std::result::Result<pulseengine_mcp_protocol::ReadResourceResult, pulseengine_mcp_protocol::Error>> + Send {
359+
async move {
360+
Err(pulseengine_mcp_protocol::Error::invalid_params(
361+
format!("Unknown resource: {}", request.uri)
362+
))
363+
}
364+
}
365+
}
366+
}
349367
};
350368

351369
// Resource backend override temporarily disabled to avoid trait conflicts
@@ -780,7 +798,13 @@ fn generate_helper_methods(
780798
/// Helper method to check if resources are available (used in tests)
781799
#[allow(dead_code)]
782800
pub fn try_get_resources_default(&self) -> Vec<pulseengine_mcp_protocol::Resource> {
783-
vec![] // Return empty for now to avoid trait bound issues
801+
<Self as pulseengine_mcp_server::McpResourcesProvider>::get_available_resources(self)
802+
}
803+
804+
/// Helper method to read resources (used by mcp_server macro)
805+
#[allow(dead_code)]
806+
pub async fn try_read_resource_default(&self, request: pulseengine_mcp_protocol::ReadResourceRequestParam) -> std::result::Result<pulseengine_mcp_protocol::ReadResourceResult, pulseengine_mcp_protocol::Error> {
807+
<Self as pulseengine_mcp_server::McpResourcesProvider>::read_resource_impl(self, request).await
784808
}
785809
}
786810
}

0 commit comments

Comments
 (0)