Use this file to discover all available pages before exploring further.
Resources expose data and content that MCP clients can read. The C# SDK supports both direct resources (static URIs) and resource templates (parameterized URIs).
Use the [McpServerResource] attribute to mark methods as MCP resources:
SimpleResourceType.cs
using ModelContextProtocol.Protocol;using ModelContextProtocol.Server;using System.ComponentModel;[McpServerResourceType]public class SimpleResourceType{ [McpServerResource( UriTemplate = "test://direct/text/resource", Name = "Direct Text Resource", MimeType = "text/plain", IconSource = "https://example.com/memo.svg" )] [Description("A direct text resource")] public static string DirectTextResource() => "This is a direct resource";}
[McpServerResource( UriTemplate = "test://template/resource/{id}", Name = "Template Resource")][Description("A template resource with a numeric ID")]public static ResourceContents TemplateResource( RequestContext<ReadResourceRequestParams> requestContext, int id){ if (id < 1 || id > 100) { throw new NotSupportedException($"Unknown resource: {requestContext.Params?.Uri}"); } return new TextResourceContents { Text = $"Resource content for ID {id}", MimeType = "text/plain", Uri = requestContext.Params!.Uri };}
Resource templates are listed via resources/templates/list requests and support URI parameter extraction.
public static TextResourceContents GetTextResource(){ return new TextResourceContents { Text = "Content goes here", MimeType = "text/plain", Uri = "app://resource" };}
Clients can subscribe to resources for change notifications:
Program.cs
var subscriptions = new ConcurrentDictionary<string, ConcurrentDictionary<string, byte>>();builder.Services.AddMcpServer() .WithHttpTransport() .WithResources<MyResources>() .WithSubscribeToResourcesHandler(async (ctx, ct) => { if (ctx.Server.SessionId == null || ctx.Params?.Uri == null) { return new EmptyResult(); } // Track subscription if (!subscriptions.ContainsKey(ctx.Server.SessionId)) { subscriptions[ctx.Server.SessionId] = new(); } subscriptions[ctx.Server.SessionId].TryAdd(ctx.Params.Uri, 0); return new EmptyResult(); }) .WithUnsubscribeFromResourcesHandler(async (ctx, ct) => { if (ctx.Server.SessionId == null || ctx.Params?.Uri == null) { return new EmptyResult(); } // Remove subscription if (subscriptions.TryGetValue(ctx.Server.SessionId, out var uris)) { uris.TryRemove(ctx.Params.Uri, out _); } return new EmptyResult(); });
Notify subscribers of changes:
public async Task NotifyResourceChanged( McpServer server, string uri){ await server.SendNotificationAsync( "notifications/resources/updated", new { Uri = uri });}
// Register by type.WithResources<FileResources>()// Register from assembly.WithResourcesFromAssembly()// Register from types collection.WithResources([typeof(Resource1), typeof(Resource2)])