Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/modelcontextprotocol/csharp-sdk/llms.txt

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).

Basic Resource Implementation

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";
}

Registering Resources

Register resource types with the MCP server builder:
Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddMcpServer()
    .WithHttpTransport()
    .WithResources<SimpleResourceType>();

var app = builder.Build();
app.MapMcp();
app.Run();

Direct Resources vs Resource Templates

Direct Resources

Direct resources have fixed URIs without parameters:
[McpServerResource(
    UriTemplate = "app://config/settings",
    Name = "Application Settings",
    MimeType = "application/json"
)]
[Description("Current application configuration")]
public static string GetSettings()
{
    return JsonSerializer.Serialize(new
    {
        Version = "1.0.0",
        Environment = "production"
    });
}
Direct resources are listed via resources/list requests.

Resource Templates

Resource templates accept parameters in the URI:
[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.

Resource Attribute Properties

UriTemplate
string
required
The URI template for the resource. Can include {parameter} placeholders for templates.
Name
string
The resource’s name. If null, the method name is used.
Title
string
A human-readable title for the resource.
MimeType
string
The MIME type of the resource content (e.g., “text/plain”, “application/json”).
IconSource
string
URI for the resource’s icon (HTTP/HTTPS URL or data URI).

Parameter Binding

URI Template Parameters

Parameters in the URI template are automatically extracted and bound:
[McpServerResource(UriTemplate = "files://{category}/{filename}")]
public static string GetFile(string category, string filename)
{
    return $"Category: {category}, File: {filename}";
}

Special Parameter Types

Like tools, resources support special parameter types:
1

RequestContext<ReadResourceRequestParams>

Access the full request context:
[McpServerResource(UriTemplate = "app://resource/{id}")]
public static ResourceContents GetResource(
    RequestContext<ReadResourceRequestParams> requestContext,
    int id)
{
    var uri = requestContext.Params?.Uri;
    // ... implementation
}
2

CancellationToken

Support cancellation for long operations:
[McpServerResource(UriTemplate = "data://large/{id}")]
public static async Task<string> GetLargeData(
    int id,
    CancellationToken cancellationToken)
{
    await Task.Delay(1000, cancellationToken);
    return "Large data content";
}
3

McpServer

Access the server instance:
[McpServerResource(UriTemplate = "app://status")]
public static string GetServerStatus(McpServer server)
{
    return $"Session: {server.SessionId}";
}
4

IServiceProvider

Resolve services from dependency injection:
[McpServerResource(UriTemplate = "data://external/{id}")]
public static async Task<string> GetExternalData(
    int id,
    IServiceProvider services)
{
    var httpClient = services.GetRequiredService<HttpClient>();
    return await httpClient.GetStringAsync($"https://api.example.com/{id}");
}

Return Types

The SDK converts various return types to ReadResourceResult:
[McpServerResource(UriTemplate = "app://text")]
public static string GetText() => "Plain text content";
// Converted to TextResourceContents

Text vs Blob Resources

Text Resources

For text-based content:
public static TextResourceContents GetTextResource()
{
    return new TextResourceContents
    {
        Text = "Content goes here",
        MimeType = "text/plain",
        Uri = "app://resource"
    };
}

Blob Resources

For binary content:
public static BlobResourceContents GetBinaryResource()
{
    byte[] imageData = Convert.FromBase64String("iVBORw0KG...");
    
    return new BlobResourceContents
    {
        Blob = imageData,
        MimeType = "image/png",
        Uri = "app://resource"
    };
}

Instance Methods with Dependencies

Use instance methods for resources that need services:
[McpServerResourceType]
public class FileResources
{
    private readonly IConfiguration _configuration;
    private readonly ILogger<FileResources> _logger;

    public FileResources(
        IConfiguration configuration,
        ILogger<FileResources> logger)
    {
        _configuration = configuration;
        _logger = logger;
    }

    [McpServerResource(UriTemplate = "file://{path}")]
    [Description("Read a file from the configured directory")]
    public async Task<ResourceContents> ReadFile(
        string path,
        CancellationToken cancellationToken)
    {
        var baseDirectory = _configuration["FilesDirectory"];
        var fullPath = Path.Combine(baseDirectory, path);
        
        _logger.LogInformation("Reading file: {Path}", fullPath);
        
        var content = await File.ReadAllTextAsync(fullPath, cancellationToken);
        
        return new TextResourceContents
        {
            Text = content,
            MimeType = "text/plain",
            Uri = $"file://{path}"
        };
    }
}

Resource Subscriptions

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 });
}

Resource Metadata

Add custom metadata to resources:
[McpServerResource(UriTemplate = "data://products/{id}")]
[McpMeta("source", "database")]
[McpMeta("cacheEnabled", true)]
[McpMeta("refreshInterval", 60.0)]
[Description("Product information from database")]
public static ResourceContents GetProduct(int id)
{
    // ... implementation
}

Advanced Configuration

Programmatic Resource Creation

builder.Services.AddMcpServer()
    .WithResources([
        McpServerResource.Create(
            typeof(MyResources).GetMethod(nameof(MyResources.GetData))!,
            options: new McpServerResourceCreateOptions
            {
                Icons = [
                    new Icon
                    {
                        Source = "https://example.com/icon.svg",
                        MimeType = "image/svg+xml"
                    }
                ]
            })
    ]);

Multiple Registration Methods

// Register by type
.WithResources<FileResources>()

// Register from assembly
.WithResourcesFromAssembly()

// Register from types collection
.WithResources([typeof(Resource1), typeof(Resource2)])

Best Practices

1

Choose Appropriate URIs

Use descriptive URI schemes that clearly indicate the resource type (e.g., file://, db://, api://).
2

Set MIME Types

Always specify the correct MIME type to help clients handle content properly.
3

Validate Parameters

Validate URI template parameters to prevent access to unauthorized resources.
4

Handle Errors

Throw McpException or NotSupportedException for invalid resource URIs.
5

Support Cancellation

Accept CancellationToken for resources that perform I/O operations.

Next Steps

  • Learn about Tools for executable functions
  • Explore Prompts for prompt templates
  • Add Completions for argument suggestions