diff --git a/src/ModelContextProtocol/Client/McpClientExtensions.cs b/src/ModelContextProtocol/Client/McpClientExtensions.cs
index 9c2ca38b..56b77f69 100644
--- a/src/ModelContextProtocol/Client/McpClientExtensions.cs
+++ b/src/ModelContextProtocol/Client/McpClientExtensions.cs
@@ -139,6 +139,44 @@ public static Task<GetPromptResult> GetPromptAsync(this IMcpClient client, strin
             cancellationToken);
     }
 
+    /// <summary>
+    /// Retrieves a sequence of available resource templates from the server.
+    /// </summary>
+    /// <param name="client">The client.</param>
+    /// <param name="cancellationToken">A token to cancel the operation.</param>
+    /// <returns>An asynchronous sequence of resource template information.</returns>
+    public static async IAsyncEnumerable<ResourceTemplate> ListResourceTemplatesAsync(
+        this IMcpClient client, [EnumeratorCancellation] CancellationToken cancellationToken = default)
+    {
+        string? cursor = null;
+        do
+        {
+            var resources = await ListResourceTemplatesAsync(client, cursor, cancellationToken).ConfigureAwait(false);
+            foreach (var resource in resources.ResourceTemplates)
+            {
+                yield return resource;
+            }
+
+            cursor = resources.NextCursor;
+        }
+        while (cursor is not null);
+    }
+
+    /// <summary>
+    /// Retrieves a list of available resources from the server.
+    /// </summary>
+    /// <param name="client">The client.</param>
+    /// <param name="cursor">A  cursor to paginate the results.</param>
+    /// <param name="cancellationToken">A token to cancel the operation.</param>
+    public static Task<ListResourceTemplatesResult> ListResourceTemplatesAsync(this IMcpClient client, string? cursor, CancellationToken cancellationToken = default)
+    {
+        Throw.IfNull(client);
+
+        return client.SendRequestAsync<ListResourceTemplatesResult>(
+            CreateRequest("resources/templates/list", CreateCursorDictionary(cursor)),
+            cancellationToken);
+    }
+
     /// <summary>
     /// Retrieves a sequence of available resources from the server.
     /// </summary>
diff --git a/src/ModelContextProtocol/Configuration/McpServerBuilderExtensions.Handler.cs b/src/ModelContextProtocol/Configuration/McpServerBuilderExtensions.Handler.cs
index 6e88992a..3612b925 100644
--- a/src/ModelContextProtocol/Configuration/McpServerBuilderExtensions.Handler.cs
+++ b/src/ModelContextProtocol/Configuration/McpServerBuilderExtensions.Handler.cs
@@ -11,6 +11,19 @@ namespace ModelContextProtocol;
 /// </summary>
 public static partial class McpServerBuilderExtensions
 {
+    /// <summary>
+    /// Sets the handler for list resource templates requests.
+    /// </summary>
+    /// <param name="builder">The builder instance.</param>
+    /// <param name="handler">The handler.</param>
+    public static IMcpServerBuilder WithListResourceTemplatesHandler(this IMcpServerBuilder builder, Func<RequestContext<ListResourceTemplatesRequestParams>, CancellationToken, Task<ListResourceTemplatesResult>> handler)
+    {
+        Throw.IfNull(builder);
+
+        builder.Services.Configure<McpServerHandlers>(s => s.ListResourceTemplatesHandler = handler);
+        return builder;
+    }
+
     /// <summary>
     /// Sets the handler for list tools requests.
     /// </summary>
diff --git a/src/ModelContextProtocol/Protocol/Types/Capabilities.cs b/src/ModelContextProtocol/Protocol/Types/Capabilities.cs
index 600e20f2..3392f57c 100644
--- a/src/ModelContextProtocol/Protocol/Types/Capabilities.cs
+++ b/src/ModelContextProtocol/Protocol/Types/Capabilities.cs
@@ -110,6 +110,12 @@ public record ResourcesCapability
     [JsonPropertyName("listChanged")]
     public bool? ListChanged { get; init; }
 
+    /// <summary>
+    /// Gets or sets the handler for list resource templates requests.
+    /// </summary>
+    [JsonIgnore]
+    public Func<RequestContext<ListResourceTemplatesRequestParams>, CancellationToken, Task<ListResourceTemplatesResult>>? ListResourceTemplatesHandler { get; init; }
+
     /// <summary>
     /// Gets or sets the handler for list resources requests.
     /// </summary>
diff --git a/src/ModelContextProtocol/Protocol/Types/ListResourceTemplatesRequestParams.cs b/src/ModelContextProtocol/Protocol/Types/ListResourceTemplatesRequestParams.cs
new file mode 100644
index 00000000..4b4eecf9
--- /dev/null
+++ b/src/ModelContextProtocol/Protocol/Types/ListResourceTemplatesRequestParams.cs
@@ -0,0 +1,15 @@
+namespace ModelContextProtocol.Protocol.Types;
+
+/// <summary>
+/// Sent from the client to request a list of resource templates the server has.
+/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
+/// </summary>
+public class ListResourceTemplatesRequestParams
+{
+    /// <summary>
+    /// An opaque token representing the current pagination position.
+    /// If provided, the server should return results starting after this cursor.
+    /// </summary>
+    [System.Text.Json.Serialization.JsonPropertyName("cursor")]
+    public string? Cursor { get; init; }
+}
\ No newline at end of file
diff --git a/src/ModelContextProtocol/Protocol/Types/ListResourceTemplatesResult.cs b/src/ModelContextProtocol/Protocol/Types/ListResourceTemplatesResult.cs
new file mode 100644
index 00000000..d2337923
--- /dev/null
+++ b/src/ModelContextProtocol/Protocol/Types/ListResourceTemplatesResult.cs
@@ -0,0 +1,16 @@
+using ModelContextProtocol.Protocol.Messages;
+
+namespace ModelContextProtocol.Protocol.Types;
+
+/// <summary>
+/// The server's response to a resources/templates/list request from the client.
+/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
+/// </summary>
+public class ListResourceTemplatesResult : PaginatedResult
+{
+    /// <summary>
+    /// A list of resource templates that the server offers.
+    /// </summary>
+    [System.Text.Json.Serialization.JsonPropertyName("resourceTemplates")]
+    public List<ResourceTemplate> ResourceTemplates { get; set; } = [];
+}
\ No newline at end of file
diff --git a/src/ModelContextProtocol/Protocol/Types/ResourceTemplate.cs b/src/ModelContextProtocol/Protocol/Types/ResourceTemplate.cs
new file mode 100644
index 00000000..b32822a2
--- /dev/null
+++ b/src/ModelContextProtocol/Protocol/Types/ResourceTemplate.cs
@@ -0,0 +1,42 @@
+using ModelContextProtocol.Protocol.Types;
+
+using System.Text.Json.Serialization;
+
+namespace ModelContextProtocol.Protocol.Types;
+
+/// <summary>
+/// Represents a known resource template that the server is capable of reading.
+/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
+/// </summary>
+public record ResourceTemplate
+{
+    /// <summary>
+    /// The URI template (according to RFC 6570) that can be used to construct resource URIs.
+    /// </summary>
+    [JsonPropertyName("uriTemplate")]
+    public required string UriTemplate { get; init; }
+
+    /// <summary>
+    /// A human-readable name for this resource template.
+    /// </summary>
+    [JsonPropertyName("name")]
+    public required string Name { get; init; }
+
+    /// <summary>
+    /// A description of what this resource template represents.
+    /// </summary>
+    [JsonPropertyName("description")]
+    public string? Description { get; init; }
+
+    /// <summary>
+    /// The MIME type of this resource template, if known.
+    /// </summary>
+    [JsonPropertyName("mimeType")]
+    public string? MimeType { get; init; }
+
+    /// <summary>
+    /// Optional annotations for the resource template.
+    /// </summary>
+    [JsonPropertyName("annotations")]
+    public Annotations? Annotations { get; init; }
+}
\ No newline at end of file
diff --git a/src/ModelContextProtocol/Server/McpServer.cs b/src/ModelContextProtocol/Server/McpServer.cs
index 5e813fef..6ad1defe 100644
--- a/src/ModelContextProtocol/Server/McpServer.cs
+++ b/src/ModelContextProtocol/Server/McpServer.cs
@@ -155,6 +155,12 @@ private void SetResourcesHandler(McpServerOptions options)
         SetRequestHandler<ListResourcesRequestParams, ListResourcesResult>("resources/list", (request, ct) => listResourcesHandler(new(this, request), ct));
         SetRequestHandler<ReadResourceRequestParams, ReadResourceResult>("resources/read", (request, ct) => readResourceHandler(new(this, request), ct));
 
+        // Set the list resource templates handler, or use the default if not specified
+        var listResourceTemplatesHandler = resourcesCapability.ListResourceTemplatesHandler
+            ?? (static (_, _) => Task.FromResult(new ListResourceTemplatesResult()));
+
+        SetRequestHandler<ListResourceTemplatesRequestParams, ListResourceTemplatesResult>("resources/templates/list", (request, ct) => listResourceTemplatesHandler(new(this, request), ct));
+
         if (resourcesCapability.Subscribe is not true)
         {
             return;
diff --git a/src/ModelContextProtocol/Server/McpServerHandlers.cs b/src/ModelContextProtocol/Server/McpServerHandlers.cs
index 56327339..c41c601e 100644
--- a/src/ModelContextProtocol/Server/McpServerHandlers.cs
+++ b/src/ModelContextProtocol/Server/McpServerHandlers.cs
@@ -27,6 +27,11 @@ public sealed class McpServerHandlers
     /// </summary>
     public Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>>? GetPromptHandler { get; set; }
 
+    /// <summary>
+    /// Gets or sets the handler for list resource templates requests.
+    /// </summary>
+    public Func<RequestContext<ListResourceTemplatesRequestParams>, CancellationToken, Task<ListResourceTemplatesResult>>? ListResourceTemplatesHandler { get; set; }
+
     /// <summary>
     /// Gets or sets the handler for list resources requests.
     /// </summary>
@@ -82,11 +87,13 @@ promptsCapability with
             resourcesCapability = resourcesCapability is null ?
                 new()
                 {
+                    ListResourceTemplatesHandler = ListResourceTemplatesHandler,
                     ListResourcesHandler = ListResourcesHandler,
                     ReadResourceHandler = ReadResourceHandler
                 } :
                 resourcesCapability with
                 {
+                    ListResourceTemplatesHandler = ListResourceTemplatesHandler ?? resourcesCapability.ListResourceTemplatesHandler,
                     ListResourcesHandler = ListResourcesHandler ?? resourcesCapability.ListResourcesHandler,
                     ReadResourceHandler = ReadResourceHandler ?? resourcesCapability.ReadResourceHandler
                 };
diff --git a/tests/ModelContextProtocol.TestServer/Program.cs b/tests/ModelContextProtocol.TestServer/Program.cs
index a59e1ec7..8574a73a 100644
--- a/tests/ModelContextProtocol.TestServer/Program.cs
+++ b/tests/ModelContextProtocol.TestServer/Program.cs
@@ -313,6 +313,20 @@ private static ResourcesCapability ConfigureResources()
 
         return new()
         {
+            ListResourceTemplatesHandler = (request, cancellationToken) =>
+            {
+                return Task.FromResult(new ListResourceTemplatesResult()
+                {
+                    ResourceTemplates = [
+                        new ResourceTemplate()
+                        {
+                            UriTemplate = "test://dynamic/resource/{id}",
+                            Name = "Dynamic Resource",
+                        }
+                    ]
+                });
+            },
+
             ListResourcesHandler = (request, cancellationToken) =>
             {
                 int startIndex = 0;
@@ -349,6 +363,27 @@ private static ResourcesCapability ConfigureResources()
                 {
                     throw new McpServerException("Missing required argument 'uri'");
                 }
+
+                if (request.Params.Uri.StartsWith("test://dynamic/resource/"))
+                {
+                    var id = request.Params.Uri.Split('/').LastOrDefault();
+                    if (string.IsNullOrEmpty(id))
+                    {
+                        throw new McpServerException("Invalid resource URI");
+                    }
+                    return Task.FromResult(new ReadResourceResult()
+                    {
+                        Contents = [
+                            new ResourceContents()
+                            {
+                                Uri = request.Params.Uri,
+                                MimeType = "text/plain",
+                                Text = $"Dynamic resource {id}: This is a plaintext resource"
+                            }
+                        ]
+                    });
+                }
+
                 ResourceContents contents = resourceContents.FirstOrDefault(r => r.Uri == request.Params.Uri)
                     ?? throw new McpServerException("Resource not found");
 
@@ -364,7 +399,8 @@ private static ResourcesCapability ConfigureResources()
                 {
                     throw new McpServerException("Missing required argument 'uri'");
                 }
-                if (!request.Params.Uri.StartsWith("test://static/resource/"))
+                if (!request.Params.Uri.StartsWith("test://static/resource/")
+                    && !request.Params.Uri.StartsWith("test://dynamic/resource/"))
                 {
                     throw new McpServerException("Invalid resource URI");
                 }
@@ -383,7 +419,8 @@ private static ResourcesCapability ConfigureResources()
                 {
                     throw new McpServerException("Missing required argument 'uri'");
                 }
-                if (!request.Params.Uri.StartsWith("test://static/resource/"))
+                if (!request.Params.Uri.StartsWith("test://static/resource/")
+                    && !request.Params.Uri.StartsWith("test://dynamic/resource/"))
                 {
                     throw new McpServerException("Invalid resource URI");
                 }
diff --git a/tests/ModelContextProtocol.TestSseServer/Program.cs b/tests/ModelContextProtocol.TestSseServer/Program.cs
index 7e9f5e44..712f845f 100644
--- a/tests/ModelContextProtocol.TestSseServer/Program.cs
+++ b/tests/ModelContextProtocol.TestSseServer/Program.cs
@@ -200,6 +200,21 @@ static CreateMessageRequestParams CreateRequestSamplingParams(string context, st
             },
             Resources = new()
             {
+                ListResourceTemplatesHandler = (request, cancellationToken) =>
+                {
+
+                    return Task.FromResult(new ListResourceTemplatesResult()
+                    {
+                        ResourceTemplates = [
+                            new ResourceTemplate()
+                            {
+                                UriTemplate = "test://dynamic/resource/{id}",
+                                Name = "Dynamic Resource",
+                            }
+                        ]
+                    });
+                },
+
                 ListResourcesHandler = (request, cancellationToken) =>
                 {
                     int startIndex = 0;
@@ -236,7 +251,27 @@ static CreateMessageRequestParams CreateRequestSamplingParams(string context, st
                     {
                         throw new McpServerException("Missing required argument 'uri'");
                     }
-                    
+
+                    if (request.Params.Uri.StartsWith("test://dynamic/resource/"))
+                    {
+                        var id = request.Params.Uri.Split('/').LastOrDefault();
+                        if (string.IsNullOrEmpty(id))
+                        {
+                            throw new McpServerException("Invalid resource URI");
+                        }
+                        return Task.FromResult(new ReadResourceResult()
+                        {
+                            Contents = [
+                                new ResourceContents()
+                                {
+                                    Uri = request.Params.Uri,
+                                    MimeType = "text/plain",
+                                    Text = $"Dynamic resource {id}: This is a plaintext resource"
+                                }
+                            ]
+                        });
+                    }
+
                     ResourceContents? contents = resourceContents.FirstOrDefault(r => r.Uri == request.Params.Uri) ?? 
                         throw new McpServerException("Resource not found");
                     
diff --git a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
index af13cc79..121c4eac 100644
--- a/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
+++ b/tests/ModelContextProtocol.Tests/ClientIntegrationTests.cs
@@ -176,6 +176,21 @@ await Assert.ThrowsAsync<McpClientException>(() =>
             client.GetPromptAsync("non_existent_prompt", null, CancellationToken.None));
     }
 
+    [Theory]
+    [MemberData(nameof(GetClients))]
+    public async Task ListResourceTemplates_Stdio(string clientId)
+    {
+        // arrange
+
+        // act
+        await using var client = await _fixture.CreateClientAsync(clientId);
+
+        List<ResourceTemplate> allResourceTemplates = await client.ListResourceTemplatesAsync(TestContext.Current.CancellationToken).ToListAsync(TestContext.Current.CancellationToken);
+
+        // The server provides a single test resource template
+        Assert.Single(allResourceTemplates);
+    }
+
     [Theory]
     [MemberData(nameof(GetClients))]
     public async Task ListResources_Stdio(string clientId)
diff --git a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsHandlerTests.cs b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsHandlerTests.cs
index 7cd5c8e7..9a0fe72b 100644
--- a/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsHandlerTests.cs
+++ b/tests/ModelContextProtocol.Tests/Configuration/McpServerBuilderExtensionsHandlerTests.cs
@@ -71,6 +71,19 @@ public void WithGetPromptHandler_Sets_Handler()
         Assert.Equal(handler, options.GetPromptHandler);
     }
 
+    [Fact]
+    public void WithListResourceTemplatesHandler_Sets_Handler()
+    {
+        Func<RequestContext<ListResourceTemplatesRequestParams>, CancellationToken, Task<ListResourceTemplatesResult>> handler = (context, token) => Task.FromResult(new ListResourceTemplatesResult());
+
+        _builder.Object.WithListResourceTemplatesHandler(handler);
+
+        var serviceProvider = _services.BuildServiceProvider();
+        var options = serviceProvider.GetRequiredService<IOptions<McpServerHandlers>>().Value;
+
+        Assert.Equal(handler, options.ListResourceTemplatesHandler);
+    }
+
     [Fact]
     public void WithListResourcesHandler_Sets_Handler()
     {
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerDelegatesTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerDelegatesTests.cs
index 9ed75765..3f7d5f7c 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerDelegatesTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerDelegatesTests.cs
@@ -14,6 +14,7 @@ public void AllPropertiesAreSettable()
         Assert.Null(handlers.CallToolHandler);
         Assert.Null(handlers.ListPromptsHandler);
         Assert.Null(handlers.GetPromptHandler);
+        Assert.Null(handlers.ListResourceTemplatesHandler);
         Assert.Null(handlers.ListResourcesHandler);
         Assert.Null(handlers.ReadResourceHandler);
         Assert.Null(handlers.GetCompletionHandler);
@@ -24,6 +25,7 @@ public void AllPropertiesAreSettable()
         handlers.CallToolHandler = (p, c) => Task.FromResult(new CallToolResponse());
         handlers.ListPromptsHandler = (p, c) => Task.FromResult(new ListPromptsResult());
         handlers.GetPromptHandler = (p, c) => Task.FromResult(new GetPromptResult());
+        handlers.ListResourceTemplatesHandler = (p, c) => Task.FromResult(new ListResourceTemplatesResult());
         handlers.ListResourcesHandler = (p, c) => Task.FromResult(new ListResourcesResult());
         handlers.ReadResourceHandler = (p, c) => Task.FromResult(new ReadResourceResult());
         handlers.GetCompletionHandler = (p, c) => Task.FromResult(new CompleteResult());
@@ -34,6 +36,7 @@ public void AllPropertiesAreSettable()
         Assert.NotNull(handlers.CallToolHandler);
         Assert.NotNull(handlers.ListPromptsHandler);
         Assert.NotNull(handlers.GetPromptHandler);
+        Assert.NotNull(handlers.ListResourceTemplatesHandler);
         Assert.NotNull(handlers.ListResourcesHandler);
         Assert.NotNull(handlers.ReadResourceHandler);
         Assert.NotNull(handlers.GetCompletionHandler);
diff --git a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
index e8ca56bc..560611b8 100644
--- a/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
+++ b/tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
@@ -304,6 +304,44 @@ await Can_Handle_Requests(
             });
     }
 
+    [Fact]
+    public async Task Can_Handle_ResourceTemplates_List_Requests()
+    {
+        await Can_Handle_Requests(
+            new ServerCapabilities
+            {
+                Resources = new()
+                {
+                    ListResourceTemplatesHandler = (request, ct) =>
+                    {
+                        return Task.FromResult(new ListResourceTemplatesResult
+                        {
+                            ResourceTemplates = [new() { UriTemplate = "test", Name = "Test Resource" }]
+                        });
+                    },
+                    ListResourcesHandler = (request, ct) =>
+                    {
+                        return Task.FromResult(new ListResourcesResult
+                        {
+                            Resources = [new() { Uri = "test", Name = "Test Resource" }]
+                        });
+                    },
+                    ReadResourceHandler = (request, ct) => throw new NotImplementedException(),
+                }
+            },
+            "resources/templates/list",
+            configureOptions: null,
+            assertResult: response =>
+            {
+                Assert.IsType<ListResourceTemplatesResult>(response);
+
+                var result = (ListResourceTemplatesResult)response;
+                Assert.NotNull(result.ResourceTemplates);
+                Assert.NotEmpty(result.ResourceTemplates);
+                Assert.Equal("test", result.ResourceTemplates[0].UriTemplate);
+            });
+    }
+
     [Fact]
     public async Task Can_Handle_Resources_List_Requests()
     {