Skip to content

Bug: resources/list endpoint incorrectly exposes resource templates #319

@arcaputo3

Description

@arcaputo3

Bug description
The resources/list endpoint currently returns all registered resources, including those with templated URIs (e.g., user://{userId}). This behavior is inconsistent with the protocol's design, which separates static resources from templates.

According to convention, resources/list should only expose static, directly addressable resources. Templated resources should be discoverable exclusively through the resources/templates/list endpoint. This incorrect exposure can lead to confusion and improper handling on the client side.

Additionally, a related cleanup task is to fix a typo in a utility class name: DeafaultMcpUriTemplateManagerFactory should be DefaultMcpUriTemplateManagerFactory.

Environment

  • Library: io.modelcontextprotocol.sdk:mcp
  • Version: 0.11.0-SNAPSHOT and prior.
  • Java version: N/A (Affects all supported Java versions).
  • Vector store: N/A.

Steps to reproduce

  1. Create an McpAsyncServer instance.
  2. Register a mix of static and templated resources using the .resources() builder method. For example:
    McpServer.async(transportProvider)
        .resources(
            new McpServerFeatures.AsyncResourceSpecification(
                new McpSchema.Resource("file:///static.txt", "Static File", "text/plain", null),
                (exchange, req) -> Mono.empty()
            ),
            new McpServerFeatures.AsyncResourceSpecification(
                new McpSchema.Resource("user://{userId}", "User Profile", "application/json", null),
                (exchange, req) -> Mono.empty()
            )
        )
        .build();
  3. Start the server and connect a client.
  4. The client sends a resources/list request to the server.
  5. Observe the ListResourcesResult: the response contains both "file:///static.txt" and "user://{userId}".

Expected behavior
The response from the resources/list request should only contain the static resource. The templated resource should be filtered out.

  • resources/list response: Should contain only file:///static.txt.
  • resources/templates/list response: Should contain user://{userId} (as it is correctly inferred from the URI pattern).

Minimal Complete Reproducible example
The issue can be demonstrated and verified with a unit test that simulates the server's filtering logic. The current implementation in McpAsyncServer.java lacks the necessary filter.

Failing Test (Illustrates the Bug)
A test case that directly checks the current logic would look like this:

// In a test class like ResourceTemplateListingTest.java

@Test
void testResourceListingWithMixedResources() {
    // Given a list of mixed resource types
    List<McpSchema.Resource> allResources = List.of(
            new McpSchema.Resource("file:///doc1.txt", "Doc 1", "text/plain", null, null),
            new McpSchema.Resource("user://{userId}", "User Profile", "text/plain", null, null)
    );

    // When applying the server's filtering logic (currently none)
    // This simulates the logic in McpAsyncServer.resourcesListRequestHandler()
    List<McpSchema.Resource> filteredResources = allResources.stream()
        // .filter(resource -> !resource.uri().contains("{")) // This line is missing
        .collect(Collectors.toList());

    // Then the assertion fails because the template is included
    // Expected size: 1, Actual size: 2
    assertThat(filteredResources).hasSize(1);
    assertThat(filteredResources).extracting(McpSchema.Resource::uri)
        .containsExactly("file:///doc1.txt");
}

Proposed Fix
The fix involves adding a filter to the resourcesListRequestHandler in McpAsyncServer.java:

// File: mcp/src/main/java/io/modelcontextprotocol/server/McpAsyncServer.java

private McpServerSession.RequestHandler<McpSchema.ListResourcesResult> resourcesListRequestHandler() {
    return (exchange, params) -> {
        var resourceList = this.resources.values()
            .stream()
            .map(McpServerFeatures.AsyncResourceSpecification::resource)
            // Add this filter to exclude templates
            .filter(resource -> !resource.uri().contains("{"))
            .toList();
        return Mono.just(new McpSchema.ListResourcesResult(resourceList, null));
    };
}

With this change, the reproducible example test case will pass, and the server will behave as expected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions