-
Notifications
You must be signed in to change notification settings - Fork 350
Resources-enhancements #257
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Tyler-R-Kendrick
wants to merge
10
commits into
modelcontextprotocol:main
from
Tyler-R-Kendrick:resources-enhancements
+324
−41
Closed
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
a918739
added mcp server resource to mimic tool registration.
Tyler-R-Kendrick 4beb291
simplified event unsubscription in primitive registration
Tyler-R-Kendrick 144413b
Added tests
Tyler-R-Kendrick 0179cfe
Merge branch 'main' of https://github.com/Tyler-R-Kendrick/mcp-csharp…
Tyler-R-Kendrick ada39ad
fixed tests for resource list changes.
Tyler-R-Kendrick c6989de
Merge branch 'main' of https://github.com/Tyler-R-Kendrick/mcp-csharp…
Tyler-R-Kendrick 9816b55
Refactor McpServer and McpServerResource classes; remove unused resou…
Tyler-R-Kendrick a4a61fd
Remove Disposable utility class as it is no longer needed
Tyler-R-Kendrick d914fe5
Merge branch 'main' of https://github.com/Tyler-R-Kendrick/mcp-csharp…
Tyler-R-Kendrick 2e2ad7f
changed task to value task
Tyler-R-Kendrick File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/ModelContextProtocol/Protocol/Types/IListCapability.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
using ModelContextProtocol.Server; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace ModelContextProtocol.Protocol.Types; | ||
|
||
/// <summary> | ||
/// Represents the tools capability configuration. | ||
/// </summary> | ||
/// <typeparam name="TPrimitive">The type of the primitive.</typeparam> | ||
internal interface IListCapability<TPrimitive> | ||
where TPrimitive : IMcpServerPrimitive | ||
{ | ||
/// <summary> | ||
/// Gets or sets whether this server supports notifications for changes to the tool list. | ||
/// </summary> | ||
[JsonPropertyName("listChanged")] | ||
public bool? ListChanged { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the handler for list tools requests. | ||
/// </summary> | ||
[JsonIgnore] | ||
public McpServerPrimitiveCollection<TPrimitive>? Collection { get; set; } | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,9 +23,6 @@ internal sealed class McpServer : McpEndpoint, IMcpServer | |
private readonly ITransport _sessionTransport; | ||
private readonly bool _servicesScopePerRequest; | ||
|
||
private readonly EventHandler? _toolsChangedDelegate; | ||
private readonly EventHandler? _promptsChangedDelegate; | ||
|
||
private string _endpointName; | ||
private int _started; | ||
|
||
|
@@ -35,6 +32,7 @@ internal sealed class McpServer : McpEndpoint, IMcpServer | |
/// rather than a nullable to be able to manipulate it atomically. | ||
/// </remarks> | ||
private StrongBox<LoggingLevel>? _loggingLevel; | ||
private readonly List<Action> _disposables = []; | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="McpServer"/>. | ||
|
@@ -68,32 +66,17 @@ public McpServer(ITransport transport, McpServerOptions options, ILoggerFactory? | |
SetCompletionHandler(options); | ||
SetPingHandler(); | ||
|
||
var capabilities = options.Capabilities; | ||
Tyler-R-Kendrick marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// Register any notification handlers that were provided. | ||
if (options.Capabilities?.NotificationHandlers is { } notificationHandlers) | ||
if (capabilities?.NotificationHandlers is { } notificationHandlers) | ||
{ | ||
NotificationHandlers.RegisterRange(notificationHandlers); | ||
} | ||
|
||
// Now that everything has been configured, subscribe to any necessary notifications. | ||
if (ServerOptions.Capabilities?.Tools?.ToolCollection is { } tools) | ||
{ | ||
_toolsChangedDelegate = delegate | ||
{ | ||
_ = SendMessageAsync(new JsonRpcNotification() { Method = NotificationMethods.ToolListChangedNotification }); | ||
}; | ||
|
||
tools.Changed += _toolsChangedDelegate; | ||
} | ||
|
||
if (ServerOptions.Capabilities?.Prompts?.PromptCollection is { } prompts) | ||
{ | ||
_promptsChangedDelegate = delegate | ||
{ | ||
_ = SendMessageAsync(new JsonRpcNotification() { Method = NotificationMethods.PromptListChangedNotification }); | ||
}; | ||
|
||
prompts.Changed += _promptsChangedDelegate; | ||
} | ||
|
||
RegisterListChange(capabilities?.Tools, NotificationMethods.ToolListChangedNotification); | ||
RegisterListChange(capabilities?.Prompts, NotificationMethods.PromptListChangedNotification); | ||
RegisterListChange(capabilities?.Resources, NotificationMethods.ResourceListChangedNotification); | ||
|
||
// And initialize the session. | ||
InitializeSession(transport); | ||
|
@@ -140,18 +123,11 @@ public async Task RunAsync(CancellationToken cancellationToken = default) | |
|
||
public override async ValueTask DisposeUnsynchronizedAsync() | ||
{ | ||
if (_toolsChangedDelegate is not null && | ||
ServerOptions.Capabilities?.Tools?.ToolCollection is { } tools) | ||
{ | ||
tools.Changed -= _toolsChangedDelegate; | ||
} | ||
|
||
if (_promptsChangedDelegate is not null && | ||
ServerOptions.Capabilities?.Prompts?.PromptCollection is { } prompts) | ||
foreach (var disposable in _disposables) | ||
{ | ||
prompts.Changed -= _promptsChangedDelegate; | ||
disposable(); | ||
} | ||
|
||
_disposables.Clear(); | ||
await base.DisposeUnsynchronizedAsync().ConfigureAwait(false); | ||
} | ||
|
||
|
@@ -216,9 +192,26 @@ private void SetResourcesHandler(McpServerOptions options) | |
|
||
var listResourcesHandler = resourcesCapability.ListResourcesHandler; | ||
var listResourceTemplatesHandler = resourcesCapability.ListResourceTemplatesHandler; | ||
var readResourceHandler = resourcesCapability.ReadResourceHandler; | ||
var resourceCollection = resourcesCapability.ResourceCollection; | ||
|
||
if ((listResourcesHandler is not { } && listResourceTemplatesHandler is not { }) || | ||
resourcesCapability.ReadResourceHandler is not { } readResourceHandler) | ||
var originalListResourcesHandler = listResourcesHandler; | ||
listResourcesHandler = async (request, cancellationToken) => | ||
{ | ||
ListResourcesResult result = originalListResourcesHandler is not null ? | ||
await originalListResourcesHandler(request, cancellationToken).ConfigureAwait(false) : | ||
new(); | ||
|
||
if (request.Params?.Cursor is null && resourceCollection is not null) | ||
{ | ||
result.Resources.AddRange(resourceCollection.Select(t => t.ProtocolResource)); | ||
} | ||
|
||
return result; | ||
}; | ||
|
||
var isMissingListResourceHandlers = originalListResourcesHandler is null && listResourceTemplatesHandler is null; | ||
if (resourceCollection is not { IsEmpty: false } && (isMissingListResourceHandlers || readResourceHandler is not { })) | ||
{ | ||
throw new InvalidOperationException( | ||
$"{nameof(ServerCapabilities)}.{nameof(ServerCapabilities.Resources)} was enabled, " + | ||
|
@@ -233,6 +226,7 @@ private void SetResourcesHandler(McpServerOptions options) | |
McpJsonUtilities.JsonContext.Default.ListResourcesRequestParams, | ||
McpJsonUtilities.JsonContext.Default.ListResourcesResult); | ||
|
||
readResourceHandler ??= static async (_, _) => new(); | ||
SetHandler( | ||
RequestMethods.ResourcesRead, | ||
readResourceHandler, | ||
|
@@ -551,6 +545,21 @@ private void SetHandler<TRequest, TResponse>( | |
requestTypeInfo, responseTypeInfo); | ||
} | ||
|
||
private void RegisterListChange<T>(IListCapability<T>? capability, string methodName) | ||
where T : IMcpServerPrimitive | ||
{ | ||
// https://modelcontextprotocol.io/specification/2024-11-05/server/tools#capabilities | ||
// Look to spec for guidance on ListChanged over collection existance. | ||
Comment on lines
+551
to
+552
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? |
||
if (capability?.Collection is { } collection) | ||
//&& capability.ListChanged is true) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? |
||
{ | ||
void ChangedDelegate(object? sender, EventArgs e) | ||
=> _ = this.SendNotificationAsync(methodName); | ||
collection.Changed += ChangedDelegate; | ||
_disposables.Add(() => collection.Changed -= ChangedDelegate); | ||
} | ||
} | ||
|
||
/// <summary>Maps a <see cref="LogLevel"/> to a <see cref="LoggingLevel"/>.</summary> | ||
internal static LoggingLevel ToLoggingLevel(LogLevel level) => | ||
level switch | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using Microsoft.Extensions.FileProviders; | ||
using ModelContextProtocol.Protocol.Types; | ||
|
||
namespace ModelContextProtocol.Server; | ||
|
||
/// <summary> | ||
/// Represents a resource that the server supports. | ||
/// </summary> | ||
public abstract class McpServerResource : IMcpServerPrimitive | ||
{ | ||
/// <summary> | ||
/// The resource instance. | ||
/// </summary> | ||
public abstract required Resource ProtocolResource { get; init; } | ||
|
||
/// <inheritdoc /> | ||
public string Name => ProtocolResource.Name; | ||
|
||
/// <summary> | ||
/// Gets the resource URI. | ||
/// </summary> | ||
/// <param name="request">The request context.</param> | ||
/// <param name="cancellationToken">The cancellation token.</param> | ||
/// <returns>The file info of the resource.</returns> | ||
public abstract Task<IFileInfo> GetFileInfoAsync( | ||
RequestContext<ReadResourceRequestParams> request, | ||
CancellationToken cancellationToken = default); | ||
} | ||
Tyler-R-Kendrick marked this conversation as resolved.
Show resolved
Hide resolved
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.