Fix URI template derivation and lookup (#530)

* Use lowercase host in resource URI template derivation.

* Address feedback

* Add casing testing for non-templated resource lookups.

* Fix uri primitive lookup

* Use a hardcoded mcp hostname in derived resource template URIs and ensure built-in resources work with the VS Code client.

* Address feedback

* Fix equality comparison

* Use dedicated type for resource collections.
This commit is contained in:
Eirik Tsarpalis 2025-06-23 19:49:38 +03:00 • committed by GitHub
commit 0b26b7bf22
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 174 additions and 75 deletions

View file

@ -264,7 +264,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
Name = name,
Title = options?.Title,
Description = options?.Description,
MimeType = options?.MimeType,
MimeType = options?.MimeType ?? "application/octet-stream",
};
return new AIFunctionMcpServerResource(function, resource);
@ -295,7 +295,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
{
StringBuilder template = new();
template.Append("resource://").Append(Uri.EscapeDataString(name));
template.Append("resource://mcp/").Append(Uri.EscapeDataString(name));
if (function.JsonSchema.TryGetProperty("properties", out JsonElement properties))
{
@ -359,7 +359,7 @@ internal sealed class AIFunctionMcpServerResource : McpServerResource
return null;
}
}
else if (request.Params.Uri != ProtocolResource!.Uri)
else if (!UriTemplate.UriTemplateComparer.Instance.Equals(request.Params.Uri, ProtocolResource!.Uri))
{
return null;
}

View file

@ -228,7 +228,7 @@ internal sealed class McpServer : McpEndpoint, IMcpServer
var unsubscribeHandler = resourcesCapability.UnsubscribeFromResourcesHandler ?? (static async (_, __) => new EmptyResult());
var resources = resourcesCapability.ResourceCollection;
var listChanged = resourcesCapability.ListChanged;
var subcribe = resourcesCapability.Subscribe;
var subscribe = resourcesCapability.Subscribe;
// Handle resources provided via DI.
if (resources is { IsEmpty: false })
@ -309,7 +309,7 @@ internal sealed class McpServer : McpEndpoint, IMcpServer
listChanged = true;
// TODO: Implement subscribe/unsubscribe logic for resource and resource template collections.
// subcribe = true;
// subscribe = true;
}
ServerCapabilities.Resources.ListResourcesHandler = listResourcesHandler;
@ -319,7 +319,7 @@ internal sealed class McpServer : McpEndpoint, IMcpServer
ServerCapabilities.Resources.SubscribeToResourcesHandler = subscribeHandler;
ServerCapabilities.Resources.UnsubscribeFromResourcesHandler = unsubscribeHandler;
ServerCapabilities.Resources.ListChanged = listChanged;
ServerCapabilities.Resources.Subscribe = subcribe;
ServerCapabilities.Resources.Subscribe = subscribe;
SetHandler(
RequestMethods.ResourcesList,

View file

@ -10,13 +10,14 @@ public class McpServerPrimitiveCollection<T> : ICollection<T>, IReadOnlyCollecti
where T : IMcpServerPrimitive
{
/// <summary>Concurrent dictionary of primitives, indexed by their names.</summary>
private readonly ConcurrentDictionary<string, T> _primitives = [];
private readonly ConcurrentDictionary<string, T> _primitives;
/// <summary>
/// Initializes a new instance of the <see cref="McpServerPrimitiveCollection{T}"/> class.
/// </summary>
public McpServerPrimitiveCollection()
public McpServerPrimitiveCollection(IEqualityComparer<string>? keyComparer = null)
{
_primitives = new(keyComparer);
}
/// <summary>Occurs when the collection is changed.</summary>

View file

@ -0,0 +1,5 @@
namespace ModelContextProtocol.Server;
/// <summary>Provides a thread-safe collection of <see cref="McpServerResource"/> instances, indexed by their URI templates.</summary>
public sealed class McpServerResourceCollection()
: McpServerPrimitiveCollection<McpServerResource>(UriTemplate.UriTemplateComparer.Instance);