Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Oct 17, 2025

Problem

When multiple templated resources were defined in a server, attempting to access a resource that matched the second or later template would fail with an "Unknown resource URI" error. For example:

[McpServerResourceType]
public class KnowledgeBase
{
    [McpServerResource(UriTemplate = "KBInfo://summary"), Description("Get KB summary")]
    public static string KBSummary() => "Summary content";

    [McpServerResource(UriTemplate = "KBInfo://{path}"), Description("Get knowledge detail")]
    public static string GetKnowledgeDetail(string path) => $"Content for {path}";
}

Accessing KBInfo://KB/docs2/topic-auth.md would fail with:

McpException: Unknown resource URI: 'KBInfo://KB/docs2/topic-auth.md'

Root Cause

PR #733 introduced a regression in the resource matching logic. The code was setting MatchedPrimitive to the first templated resource without checking if the URI pattern actually matched. When ReadAsync returned null (indicating no match), the handler didn't fall through to try other templated resources, resulting in the error.

Solution

This fix restores the correct behavior while maintaining filter support:

  1. Filter logic: Continues to set MatchedPrimitive to the first templated resource as a "hint" - this is necessary for filters to access MatchedPrimitive

  2. Handler logic: Enhanced to try all templated resources:

    • First attempts the MatchedPrimitive if set
    • If it doesn't match (returns null), iterates through all other templated resources
    • Returns the first matching resource

The fix ensures all templated resources are checked in sequence until a match is found, resolving the issue while maintaining backward compatibility with existing filter behavior.

Testing

Added regression test MultipleTemplatedResources_MatchesCorrectResource that verifies:

  • Multiple templated resources can coexist
  • Each resource correctly matches its URI pattern
  • Non-matching URIs are properly handled

Fixes #[issue-number]

Original prompt

This section details on the original issue you should resolve

<issue_title>McpServerResource not working.</issue_title>
<issue_description>Describe the bug
I just defined two resources to test,

[McpServerResource(MimeType = "text/json",UriTemplate ="KBInfo://summary"), Description("Get this mcp's Knowledge summary info")]
public static string KBSummary()
{
    var allReadme = Directory.GetFiles("./KB", "x.md", SearchOption.AllDirectories);
    var res = new List<KBItemResponse>();
    foreach (var item in allReadme)
    {
        res.Add(new()
        {
            FilePath = $"{Path.GetRelativePath(".", item)}",
            SummaryInfo = File.ReadAllText(item)
        });
    }
    return System.Text.Json.JsonSerializer.Serialize(res);
}

[McpServerResource(MimeType = "text/markdown", UriTemplate = "KBInfo://{path}"), Description("Get some knowledge's detailinfo")]
public static string GetKnowledgeDetailInfo(string path)
{

    return string.Empty;
}

To Reproduce
Use AI VS code like the Roo Code etc.
Ask some question wants to use this resource.

Expected behavior
Error accessing MCP resource:
MCP error -32602: Unknown resource URI: 'KBInfo://KB/docs2/topic-auth.md'

Logs

ModelContextProtocol.McpException: Unknown resource URI: 'KBInfo://KB/docs2/topic-auth.md'
         at ModelContextProtocol.Server.McpServerImpl.<>c.<<ConfigureResources>b__48_2>d.MoveNext()
      --- End of stack trace from previous location ---
         at ModelContextProtocol.Server.McpServerImpl.<>c__DisplayClass48_1.<<ConfigureResources>b__8>d.MoveNext()
      --- End of stack trace from previous location ---
         at ModelContextProtocol.AspNetCore.AuthorizationFilterSetup.<>c__DisplayClass14_0.<<CheckReadResourceFilter>b__1>d.MoveNext()
      --- End of stack trace from previous location ---
         at ModelContextProtocol.Server.McpServerImpl.<>c__DisplayClass48_2.<<ConfigureResources>b__9>d.MoveNext()
      --- End of stack trace from previous location ---
         at ModelContextProtocol.Server.McpServerImpl.<InvokeHandlerAsync>g__InvokeScopedAsync|52_0[TParams,TResult](McpRequestHandler`2 handler, TParams args, JsonRpcRequest jsonRpcRequest, CancellationToken cancellationToken)
         at ModelContextProtocol.Server.McpServerImpl.<InvokeHandlerAsync>g__InvokeScopedAsync|52_0[TParams,TResult](McpRequestHandler`2 handler, TParams args, JsonRpcRequest jsonRpcRequest, CancellationToken cancellationToken)
         at ModelContextProtocol.RequestHandlers.<>c__DisplayClass0_0`2.<<Set>b__0>d.MoveNext()
      --- End of stack trace from previous location ---
         at ModelContextProtocol.McpSessionHandler.HandleRequest(JsonRpcRequest request, CancellationToken cancellationToken)
         at ModelContextProtocol.McpSessionHandler.HandleMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken)
         at ModelContextProtocol.McpSessionHandler.<>c__DisplayClass26_1.<<ProcessMessagesCoreAsync>g__ProcessMessageAsync|1>d.MoveNext()

Additional context
None
</issue_description>

Comments on the Issue (you are @copilot in this section)

@stephentoub Can you share a full repro? How are you registering these resources with your server? @stephentoub > The MCP spec isn't clear on what level of templates should be supported, but I think most SDKs only support level 1.

It should support through level 4...at least that was the intent. </comment_new>
<comment_new>@stephentoub
@halter73, I think this broke in #733. This loop:

                    // Fall back to an O(N) lookup, trying to match against each URI template.
                    // The number of templates is controlled by the server developer, and the number is expected to be
                    // not terribly large. If that changes, this can be tweaked to enable a more efficient lookup.
                    foreach (var resourceTemplate in resources)
                    {
                        if (await resourceTemplate.ReadAsync(request, cancellationToken).ConfigureAwait(false) is { } result)
                        {
                            return result;
                        }
                    }

got changed to this:

                        // Fall back to an O(N) lookup, trying to match against each URI template.
                        // The number of templates is controlled by the server developer, and the number is expected to be
                        // not terribly large. If that changes, this can be tweaked to enable a more efficient lookup.
                        foreach (var resourceTemplate in resources)
                        {
                            // Check if this template would handle the request by testing if ReadAsync would succeed
                         ...

</details>

Fixes modelcontextprotocol/csharp-sdk#821

<!-- START COPILOT CODING AGENT TIPS -->
---

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.

@Copilot Copilot AI changed the title [WIP] Fix McpServerResource not working issue Fix McpServerResource URI template matching for multiple templated resources Oct 17, 2025
@Copilot Copilot AI requested a review from stephentoub October 17, 2025 13:36
Copilot finished work on behalf of stephentoub October 17, 2025 13:36
if (resourceTemplate.IsTemplated)
{
// This is a simplified check - a more robust implementation would match the URI pattern
// For now, we'll let the actual handler attempt the match
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@halter73, the concept of MatchedPrimitive appears to be unsupportable with how we've defined resource template handling, where the actual primitive gets to decide if it can successfully handle the request. Are you imagining then that support would need to be moved out of the resource template and into this matching code?

@stephentoub stephentoub deleted the copilot/fix-mcpserverresource-issue branch October 17, 2025 18:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants