Resources API Reference
Complete reference for MCP Gateway Resources API.
Overview
Resources represent data that can be read by AI assistants. Common use cases:
- File contents
- Database records
- API responses
- Live metrics
- Configuration data
Quick Reference
| Method | Description |
|---|---|
resources/list |
List all available resources |
resources/read |
Read a specific resource |
resources/subscribe |
Subscribe to resource updates (v1.8.0) |
resources/unsubscribe |
Unsubscribe from updates (v1.8.0) |
resources/list
List all resources available on the server.
Request
{
"jsonrpc": "2.0",
"method": "resources/list",
"params": {
"cursor": "optional-cursor"
},
"id": 1
}
Response
{
"jsonrpc": "2.0",
"result": {
"resources": [
{
"uri": "file://data/users.json",
"name": "User Data",
"description": "User records in JSON format",
"mimeType": "application/json",
"icons": [{"src": "icon.png", "mimeType": "image/png", "theme": "light"}]
}
],
"nextCursor": "optional-cursor"
},
"id": 1
}
resources/read
Read a specific resource by URI.
Request
{
"jsonrpc": "2.0",
"method": "resources/read",
"params": {
"uri": "file://data/users.json"
},
"id": 2
}
Response
{
"jsonrpc": "2.0",
"result": {
"contents": [
{
"uri": "file://data/users.json",
"mimeType": "application/json",
"text": "[{\"id\":1,\"name\":\"Alice\"}]"
}
]
},
"id": 2
}
resources/subscribe (v1.8.0)
Subscribe to notifications when a resource changes.
Request
{
"jsonrpc": "2.0",
"method": "resources/subscribe",
"params": {
"uri": "file://data/users.json"
},
"id": 3
}
Response
{
"jsonrpc": "2.0",
"result": {
"subscribed": true,
"uri": "file://data/users.json"
},
"id": 3
}
Requirements:
- Session management must be enabled
- Resource must exist (validated before subscription)
- Exact URI matching only (v1.8.0)
resources/unsubscribe (v1.8.0)
Unsubscribe from resource update notifications.
Request
{
"jsonrpc": "2.0",
"method": "resources/unsubscribe",
"params": {
"uri": "file://data/users.json"
},
"id": 4
}
Response
{
"jsonrpc": "2.0",
"result": {
"unsubscribed": true,
"uri": "file://data/users.json"
},
"id": 4
}
Note: Idempotent - safe to call multiple times.
Defining Resources
File Resource
using Mcp.Gateway.Tools;
public class FileResources
{
[McpResource("file://data/users.json",
Name = "User Data",
Description = "User records in JSON format",
MimeType = "application/json")]
[McpIcon("icon.png")]
[McpIcon("icon2.png", "image/png", Sizes = new[] { "16x16", "32x32", "48x48", "any" })]
[McpIcon("icon-light.png", "image/png", McpIconTheme.Light)]
[McpIcon("icon-dark.png", "image/png", McpIconTheme.Dark)]
public JsonRpcMessage GetUsers(JsonRpcMessage request)
{
var data = File.ReadAllText("data/users.json");
var content = new ResourceContent(
Uri: "file://data/users.json",
MimeType: "application/json",
Text: data);
return ToolResponse.Success(
request.Id,
new ReadResourceResult
{
Contents = [content]
});
}
}
Database Resource
[McpResource("db://users",
Name = "User Database",
Description = "All users from database",
MimeType = "application/json")]
public async Task<JsonRpcMessage> GetUsersFromDb(JsonRpcMessage request)
{
var users = await _dbContext.Users.ToListAsync();
var json = JsonSerializer.Serialize(users);
var content = new ResourceContent(
Uri: "db://users",
MimeType: "application/json",
Text: json);
return ToolResponse.Success(
request.Id,
new ReadResourceResult
{
Contents = [content]
});
}
Live Metrics Resource
[McpResource("system://metrics",
Name = "System Metrics",
Description = "Live system metrics",
MimeType: "application/json")]
public JsonRpcMessage GetMetrics(JsonRpcMessage request)
{
var metrics = new
{
cpu = GetCpuUsage(),
memory = GetMemoryUsage(),
timestamp = DateTime.UtcNow
};
var content = new ResourceContent(
Uri: "system://metrics",
MimeType: "application/json",
Text: JsonSerializer.Serialize(metrics));
return ToolResponse.Success(
request.Id,
new ReadResourceResult
{
Contents = [content]
});
}
Resource Attributes
[McpResource]
Marks a method as an MCP resource.
[McpResource(
string uri, // Required: Resource URI
string? Name = null, // Optional: Display name
string? Description = null, // Optional: Description
string? MimeType = null)] // Optional: Content type
Sending Notifications
When a resource changes, notify subscribed clients using dependency injection:
Option 1: Constructor Injection
Requires class registration in DI:
using Mcp.Gateway.Tools.Notifications;
public class FileResources
{
private readonly INotificationSender _notificationSender;
public FileResources(INotificationSender notificationSender)
{
_notificationSender = notificationSender;
}
[McpTool("update_users")]
public async Task<JsonRpcMessage> UpdateUsers(
TypedJsonRpc<UpdateUsersArgs> request)
{
var args = request.GetParams()!;
// Update the file
await File.WriteAllTextAsync("data/users.json", args.Data);
// Notify subscribed sessions (v1.8.0)
await _notificationSender.SendNotificationAsync(
NotificationMessage.ResourcesUpdated("file://data/users.json"));
return ToolResponse.Success(request.Id, new { updated = true });
}
}
// Register in DI:
builder.Services.AddScoped<FileResources>();
Option 2: Method Parameter Injection
No class registration needed - parameters resolved from DI:
using Mcp.Gateway.Tools.Notifications;
public class FileResources
{
[McpTool("update_users")]
public async Task<JsonRpcMessage> UpdateUsers(
TypedJsonRpc<UpdateUsersArgs> request,
INotificationSender notificationSender) // ← Automatically injected!
{
var args = request.GetParams()!;
// Update the file
await File.WriteAllTextAsync("data/users.json", args.Data);
// Notify subscribed sessions (v1.8.0)
await notificationSender.SendNotificationAsync(
NotificationMessage.ResourcesUpdated("file://data/users.json"));
return ToolResponse.Success(request.Id, new { updated = true });
}
}
// No registration needed - class auto-discovered!
Parameter resolution order:
JsonRpcMessageorTypedJsonRpc<T>- The request (must be first parameter)- Additional parameters - Resolved from DI container (in order)
Benefits of method parameter injection:
- ✅ No class registration needed
- ✅ Simpler for resources with few dependencies
- ✅ Clear what each method needs
- ✅ Easier testing (mock parameters directly)
URI Schemes
Common URI schemes:
| Scheme | Example | Use Case |
|---|---|---|
file:// |
file://data/config.json |
File system |
db:// |
db://users |
Database records |
http:// |
http://api.example.com/data |
HTTP endpoints |
system:// |
system://metrics |
System information |
custom:// |
custom://my-resource |
Custom schemes |
MIME Types
Common MIME types:
| Type | Description |
|---|---|
application/json |
JSON data |
text/plain |
Plain text |
text/markdown |
Markdown |
text/html |
HTML |
application/xml |
XML |
Best Practices
1. Use Descriptive URIs
// ✅ GOOD
[McpResource("file://logs/app-2025-12-20.log")]
// ❌ BAD
[McpResource("file://log1")]
2. Specify MIME Type
[McpResource("file://data/users.json",
MimeType = "application/json")]
3. Handle Missing Resources
[McpResource("file://data/users.json")]
public JsonRpcMessage GetUsers(JsonRpcMessage request)
{
if (!File.Exists("data/users.json"))
{
throw new ToolInvalidParamsException(
"Resource not found: users.json");
}
var data = File.ReadAllText("data/users.json");
return ToolResponse.Success(...);
}
4. Cache When Appropriate
private readonly MemoryCache _cache = new MemoryCache(new MemoryCacheOptions());
[McpResource("db://users")]
public async Task<JsonRpcMessage> GetUsers(JsonRpcMessage request)
{
var cacheKey = "users";
if (!_cache.TryGetValue(cacheKey, out string? data))
{
var users = await _dbContext.Users.ToListAsync();
data = JsonSerializer.Serialize(users);
_cache.Set(cacheKey, data, TimeSpan.FromMinutes(5));
}
return ToolResponse.Success(...);
}
Resource Subscriptions (v1.8.0)
Server-Side
Resources support subscriptions automatically when:
- Session management is enabled
- Resource is registered via
[McpResource] - Server sends notifications on changes
// Resource definition
[McpResource("file://data/users.json")]
public JsonRpcMessage GetUsers(JsonRpcMessage request) { ... }
// When resource changes
await _notificationSender.SendNotificationAsync(
NotificationMessage.ResourcesUpdated("file://data/users.json"));
// Only subscribed sessions receive notification!
Client-Side
// 1. Subscribe
await fetch('/mcp', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'MCP-Session-Id': sessionId
},
body: JSON.stringify({
jsonrpc: '2.0',
method: 'resources/subscribe',
params: { uri: 'file://data/users.json' },
id: 1
})
});
// 2. Open SSE stream
const eventSource = new EventSource('/mcp', {
headers: { 'MCP-Session-Id': sessionId }
});
eventSource.addEventListener('message', (event) => {
const notification = JSON.parse(event.data);
if (notification.method === 'notifications/resources/updated') {
// Re-fetch the resource
fetchResource(notification.params.uri);
}
});
// 3. Unsubscribe
await fetch('/mcp', {
method: 'POST',
body: JSON.stringify({
jsonrpc: '2.0',
method: 'resources/unsubscribe',
params: { uri: 'file://data/users.json' },
id: 2
})
});
Dynamic Resources (v1.8.5)
Support for registering and unregistering resources at runtime without using attributes. This is useful for scenarios where resources are not known at compile time, such as dynamic file lists or user-specific data.
Registering a Resource
Use the RegisterResource method on ToolService (available via dependency injection).
[McpTool("add-dynamic-resource")]
public async Task<JsonRpcMessage> AddResource(
JsonRpcMessage request,
ToolService toolService)
{
// Define the resource handler
Func<JsonRpcMessage, Task<JsonRpcMessage>> handler = async (req) =>
{
var content = new ResourceContent(
Uri: "dynamic://data/1",
MimeType: "application/json",
Text: "{\"value\": 42}"
);
var result = new ReadResourceResult
{
Contents = [content]
}
return ToolResponse.Success(req.Id, result);
};
// Register dynamically
toolService.RegisterResource(
uri: "dynamic://data/1",
handler: handler,
name: "Dynamic Data",
description: "A resource created at runtime",
mimeType: "application/json"
);
return ToolResponse.Success(request.Id, new { added = true });
}
Unregistering a Resource
Use the UnregisterResource method to remove a resource at runtime.
[McpTool("remove-dynamic-resource")]
public JsonRpcMessage RemoveResource(
JsonRpcMessage request,
ToolService toolService)
{
// Remove the resource
toolService.UnregisterResource("dynamic://data/1");
return ToolResponse.Success(request.Id, new { removed = true });
}
Key Features
- Runtime Metadata: Define Name, Description, and MimeType programmatically without
[McpResource]. - Seamless Integration: Dynamically registered resources appear in
resources/listand work withresources/readjust like static ones. - Thread Safety: Registration and unregistration are thread-safe and can be called concurrently.
Testing
Unit Test
[Fact]
public void GetUsers_FileExists_ReturnsContent()
{
// Arrange
var resources = new FileResources();
var request = JsonRpcMessage.CreateRequest("resources/read", "1");
// Act
var response = resources.GetUsers(request);
// Assert
Assert.NotNull(response.Result);
}
Integration Test
[Fact]
public async Task ResourcesRead_ValidUri_ReturnsContent()
{
// Arrange
using var server = new McpGatewayFixture();
var client = server.CreateClient();
var request = new
{
jsonrpc = "2.0",
method = "resources/read",
@params = new { uri = "file://data/users.json" },
id = 1
};
// Act
var response = await client.PostAsJsonAsync("/mcp", request);
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
// Assert
Assert.True(result.RootElement.TryGetProperty("result", out var resultProp));
Assert.True(resultProp.TryGetProperty("contents", out _));
}
See Also
- Resource Subscriptions - Complete subscription guide
- Tools API - Tool invocation reference
- Resource Example - Complete resource server example