Resource capability (#5)

Prompt capability
Pagination support
This commit is contained in:
Peder Holdgaard Pedersen 2025-01-04 19:39:22 +01:00 • committed by GitHub
commit c9ace236a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 864 additions and 274 deletions

View file

@ -42,9 +42,10 @@ public interface IMcpClient : IAsyncDisposable
/// <summary>
/// Retrieves a list of available tools from the server.
/// </summary>
/// <param name="cursor">An optional cursor to paginate the results.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A task containing the server's response with tool information.</returns>
Task<ListToolsResponse> ListToolsAsync(CancellationToken cancellationToken = default);
Task<ListToolsResult> ListToolsAsync(string? cursor = null, CancellationToken cancellationToken = default);
/// <summary>
/// Invokes a tool on the server with optional arguments.
@ -55,6 +56,55 @@ public interface IMcpClient : IAsyncDisposable
/// <returns>A task containing the tool's response.</returns>
Task<CallToolResponse> CallToolAsync(string toolName, Dictionary<string, object>? arguments = null, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a list of available prompts from the server.
/// </summary>
/// <param name="cursor">An optional cursor to paginate the results.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A task containing the server's response with prompt information.</returns>
Task<ListPromptsResult> ListPromptsAsync(string? cursor = null, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a specific prompt with optional arguments.
/// </summary>
/// <param name="name">The name of the prompt to retrieve</param>
/// <param name="arguments">Optional arguments for the prompt</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>A task containing the prompt's content and messages.</returns>
Task<GetPromptResult> GetPromptAsync(string name, Dictionary<string, object>? arguments = null, CancellationToken cancellationToken = default);
/// <summary>
/// Retrieves a list of available resources from the server.
/// </summary>
/// <param name="cursor">An optional cursor to paginate the results.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns></returns>
Task<ListResourcesResult> ListResourcesAsync(string? cursor = null, CancellationToken cancellationToken = default);
/// <summary>
/// Reads a resource from the server.
/// </summary>
/// <param name="uri">The uri of the resource.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns></returns>
Task<ReadResourceResult> ReadResourceAsync(string uri, CancellationToken cancellationToken = default);
/// <summary>
/// Subscribes to a resource on the server.
/// </summary>
/// <param name="uri">The uri of the resource.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns></returns>
Task SubscribeToResourceAsync(string uri, CancellationToken cancellationToken = default);
/// <summary>
/// Unsubscribes from a resource on the server.
/// </summary>
/// <param name="uri">The uri of the resource.</param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns></returns>
Task UnsubscribeFromResourceAsync(string uri, CancellationToken cancellationToken = default);
/// <summary>
/// Sends a generic JSON-RPC request to the server.
/// </summary>
@ -66,6 +116,8 @@ public interface IMcpClient : IAsyncDisposable
/// <summary>
/// Registers a handler for server notifications of a specific method.
///
/// <see cref="NotificationMethods">Constants for common notification methods</see>
/// </summary>
/// <param name="method">The notification method to handle.</param>
/// <param name="handler">The async handler function to process notifications.</param>

View file

@ -7,6 +7,7 @@ using McpDotNet.Protocol.Messages;
using System.Diagnostics;
using System.Text.Json;
using McpDotNet.Utils.Json;
using System.Xml.Linq;
/// <inheritdoc/>
internal class McpClient : IMcpClient
@ -135,13 +136,102 @@ internal class McpClient : IMcpClient
}
/// <inheritdoc/>
public async Task<ListToolsResponse> ListToolsAsync(CancellationToken cancellationToken)
public async Task<ListToolsResult> ListToolsAsync(string? cursor = null, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<ListToolsResponse>(
return await SendRequestAsync<ListToolsResult>(
new JsonRpcRequest
{
Method = "tools/list",
// No parameters needed for tools/list
Params = cursor != null ? new Dictionary<string, object?> { ["cursor"] = cursor } : null
},
cancellationToken
).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<ListPromptsResult> ListPromptsAsync(string? cursor = null, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<ListPromptsResult>(
new JsonRpcRequest
{
Method = "prompts/list",
Params = cursor != null ? new Dictionary<string, object?> { ["cursor"] = cursor } : null
},
cancellationToken
).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<GetPromptResult> GetPromptAsync(string name, Dictionary<string, object>? arguments = null, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<GetPromptResult>(
new JsonRpcRequest
{
Method = "prompts/get",
Params = CreateParametersDictionary(name, arguments)
},
cancellationToken
).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<ListResourcesResult> ListResourcesAsync(string? cursor = null, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<ListResourcesResult>(
new JsonRpcRequest
{
Method = "resources/list",
Params = cursor != null ? new Dictionary<string, object?> { ["cursor"] = cursor } : null
},
cancellationToken
).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<ReadResourceResult> ReadResourceAsync(string uri, CancellationToken cancellationToken = default)
{
return await SendRequestAsync<ReadResourceResult>(
new JsonRpcRequest
{
Method = "resources/read",
Params = new Dictionary<string, object?>
{
["uri"] = uri
}
},
cancellationToken
).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task SubscribeToResourceAsync(string uri, CancellationToken cancellationToken = default)
{
await SendRequestAsync<dynamic>(
new JsonRpcRequest
{
Method = "resources/subscribe",
Params = new Dictionary<string, object?>
{
["uri"] = uri
}
},
cancellationToken
).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task UnsubscribeFromResourceAsync(string uri, CancellationToken cancellationToken = default)
{
await SendRequestAsync<dynamic>(
new JsonRpcRequest
{
Method = "resources/unsubscribe",
Params = new Dictionary<string, object?>
{
["uri"] = uri
}
},
cancellationToken
).ConfigureAwait(false);
@ -155,16 +245,27 @@ internal class McpClient : IMcpClient
new JsonRpcRequest
{
Method = "tools/call",
Params = new Dictionary<string, object?>
{
["name"] = toolName,
["arguments"] = arguments
}
Params = CreateParametersDictionary(toolName, arguments)
},
cancellationToken
).ConfigureAwait(false);
}
private static Dictionary<string, object> CreateParametersDictionary(string nameParameter, Dictionary<string, object>? optionalArguments = null)
{
var parameters = new Dictionary<string, object?>
{
["name"] = nameParameter
};
if (optionalArguments != null)
{
parameters["arguments"] = optionalArguments;
}
return parameters;
}
private async Task ProcessMessagesAsync(CancellationToken cancellationToken)
{
try