From 9a244f5770cc13c4afd676cb8c5a4bb4b6b1f9db Mon Sep 17 00:00:00 2001
From: Peder Holdgaard Pedersen <127606677+PederHP@users.noreply.github.com>
Date: Thu, 2 Jan 2025 19:02:24 +0100
Subject: [PATCH] Clean up and add more comments (#2)
---
src/mcpdotnet/Client/IMcpClient.cs | 38 +++-
src/mcpdotnet/Client/McpClient.cs | 38 ++--
src/mcpdotnet/Client/McpClientException.cs | 17 +-
src/mcpdotnet/Client/McpClientFactory.cs | 23 ++-
src/mcpdotnet/Client/McpClientOptions.cs | 9 +-
.../Configuration/McpServerConfig.cs | 3 +-
.../LlmIntegration/DefaultMcpToolProvider.cs | 188 ------------------
.../LlmIntegration/IMcpToolProvider.cs | 23 ---
src/mcpdotnet/Features/Tools/ToolMapper.cs | 115 -----------
.../Features/Tools/ToolsExtensions.cs | 61 ------
src/mcpdotnet/Features/Tools/ToolsTypes.cs | 74 -------
.../Protocol/Types/BlobResourceContents.cs | 21 ++
.../Protocol/Types/CallToolResponse.cs | 18 ++
src/mcpdotnet/Protocol/Types/Capabilities.cs | 36 ----
.../Protocol/Types/IResourceContents.cs | 10 +
.../Protocol/Types/Implementation.cs | 3 +-
src/mcpdotnet/Protocol/Types/JsonSchema.cs | 23 +++
.../Protocol/Types/JsonSchemaProperty.cs | 18 ++
.../Protocol/Types/ListToolsResponse.cs | 13 ++
src/mcpdotnet/Protocol/Types/Resources.cs | 45 -----
.../Protocol/Types/ServerCapabilities.cs | 39 ++++
.../Protocol/Types/TextResourceContents.cs | 21 ++
src/mcpdotnet/Protocol/Types/Tool.cs | 23 +++
src/mcpdotnet/Protocol/Types/ToolContent.cs | 58 ++++++
src/mcpdotnet/Protocol/Types/Tools.cs | 44 ----
.../Utils/Json/ContentJsonConverter.cs | 11 +-
.../Utils/Json/JsonRpcMessageConverter.cs | 11 +-
.../Json/JsonSerializerOptionsExtensions.cs | 5 +-
.../Json/ResourceContentsJsonConverter.cs | 10 +-
src/mcpdotnet/mcpdotnet.csproj | 4 -
30 files changed, 349 insertions(+), 653 deletions(-)
delete mode 100644 src/mcpdotnet/Features/LlmIntegration/DefaultMcpToolProvider.cs
delete mode 100644 src/mcpdotnet/Features/LlmIntegration/IMcpToolProvider.cs
delete mode 100644 src/mcpdotnet/Features/Tools/ToolMapper.cs
delete mode 100644 src/mcpdotnet/Features/Tools/ToolsExtensions.cs
delete mode 100644 src/mcpdotnet/Features/Tools/ToolsTypes.cs
create mode 100644 src/mcpdotnet/Protocol/Types/BlobResourceContents.cs
create mode 100644 src/mcpdotnet/Protocol/Types/CallToolResponse.cs
create mode 100644 src/mcpdotnet/Protocol/Types/IResourceContents.cs
create mode 100644 src/mcpdotnet/Protocol/Types/JsonSchema.cs
create mode 100644 src/mcpdotnet/Protocol/Types/JsonSchemaProperty.cs
create mode 100644 src/mcpdotnet/Protocol/Types/ListToolsResponse.cs
create mode 100644 src/mcpdotnet/Protocol/Types/ServerCapabilities.cs
create mode 100644 src/mcpdotnet/Protocol/Types/TextResourceContents.cs
create mode 100644 src/mcpdotnet/Protocol/Types/Tool.cs
create mode 100644 src/mcpdotnet/Protocol/Types/ToolContent.cs
delete mode 100644 src/mcpdotnet/Protocol/Types/Tools.cs
diff --git a/src/mcpdotnet/Client/IMcpClient.cs b/src/mcpdotnet/Client/IMcpClient.cs
index 6d2daa4..05bd6b1 100644
--- a/src/mcpdotnet/Client/IMcpClient.cs
+++ b/src/mcpdotnet/Client/IMcpClient.cs
@@ -5,6 +5,8 @@ using McpDotNet.Protocol.Messages;
///
/// Represents an instance of the MCP client connecting to a specific server.
+/// Use PingAsync, ListToolsAsync and CallToolAsync instead of SendRequestAsync for common operations.
+/// SendRequestAsync is provided for custom request and those not yet covered explicitly by the client.
///
public interface IMcpClient : IAsyncDisposable
{
@@ -14,44 +16,58 @@ public interface IMcpClient : IAsyncDisposable
bool IsInitialized { get; }
///
- /// Gets the capabilities that the server supports.
+ /// Gets the capabilities supported by the server.
///
ServerCapabilities? ServerCapabilities { get; }
///
- /// Gets the version information of the server.
+ /// Gets the version and implementation information of the server.
///
Implementation? ServerInfo { get; }
///
- /// Connects to the server.
+ /// Establishes a connection to the server.
///
+ /// A token to cancel the operation.
+ /// A task representing the asynchronous operation.
Task ConnectAsync(CancellationToken cancellationToken = default);
///
- /// A simple ping to the server to check if it is alive. Task completes when the ping is successful.
+ /// Sends a ping request to verify server connectivity.
///
+ /// A token to cancel the operation.
+ /// A task that completes when the ping is successful.
Task PingAsync(CancellationToken cancellationToken = default);
///
- /// Queries the server for a list of tools that are available (if any)
+ /// Retrieves a list of available tools from the server.
///
+ /// A token to cancel the operation.
+ /// A task containing the server's response with tool information.
Task ListToolsAsync(CancellationToken cancellationToken = default);
///
- /// Calls a tool on the server.
+ /// Invokes a tool on the server with optional arguments.
///
- /// The name of the tool as given when listing tool definitions.
- /// Arguments for the tool (if any)
+ /// The name of the tool to call.
+ /// Optional arguments for the tool.
+ /// A token to cancel the operation.
+ /// A task containing the tool's response.
Task CallToolAsync(string toolName, Dictionary? arguments = null, CancellationToken cancellationToken = default);
///
- /// A generic method to send a request to the server and get a response.
+ /// Sends a generic JSON-RPC request to the server.
///
+ /// The expected response type.
+ /// The JSON-RPC request to send.
+ /// A token to cancel the operation.
+ /// A task containing the server's response.
Task SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken) where T : class;
///
- /// Registers a handler for a specific notification method.
+ /// Registers a handler for server notifications of a specific method.
///
+ /// The notification method to handle.
+ /// The async handler function to process notifications.
void OnNotification(string method, Func handler);
-}
+}
\ No newline at end of file
diff --git a/src/mcpdotnet/Client/McpClient.cs b/src/mcpdotnet/Client/McpClient.cs
index f0b56fb..6a6dc98 100644
--- a/src/mcpdotnet/Client/McpClient.cs
+++ b/src/mcpdotnet/Client/McpClient.cs
@@ -1,5 +1,4 @@
-// Client/McpClientOptions.cs
-namespace McpDotNet.Client;
+namespace McpDotNet.Client;
using McpDotNet.Protocol.Types;
using McpDotNet.Protocol.Transport;
@@ -9,10 +8,8 @@ using System.Diagnostics;
using System.Text.Json;
using McpDotNet.Utils.Json;
-///
-/// Main MCP client implementation that handles protocol lifecycle and message routing.
-///
-public class McpClient : IMcpClient
+///
+internal class McpClient : IMcpClient
{
private readonly IMcpTransport _transport;
private readonly McpClientOptions _options;
@@ -24,21 +21,20 @@ public class McpClient : IMcpClient
private CancellationTokenSource? _cts;
private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions().ConfigureForMcp();
- ///
- /// Gets whether the client is connected and initialized.
- ///
+ ///
public bool IsInitialized => _isInitialized;
- ///
- /// Gets the server's capabilities after initialization.
- ///
+ ///
public ServerCapabilities? ServerCapabilities { get; private set; }
- ///
- /// Gets information about the server implementation after initialization.
- ///
+ ///
public Implementation? ServerInfo { get; private set; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An MCP transport implementation.
+ /// Options for the client, defining protocol version and capabilities.
public McpClient(IMcpTransport transport, McpClientOptions options)
{
_transport = transport;
@@ -48,9 +44,7 @@ public class McpClient : IMcpClient
_nextRequestId = 1;
}
- ///
- /// Connects to the server and performs the initialization sequence.
- ///
+ ///
public async Task ConnectAsync(CancellationToken cancellationToken = default)
{
if (_isInitialized)
@@ -128,6 +122,7 @@ public class McpClient : IMcpClient
}
}
+ ///
public async Task PingAsync(CancellationToken cancellationToken)
{
await SendRequestAsync(
@@ -139,6 +134,7 @@ public class McpClient : IMcpClient
).ConfigureAwait(false);
}
+ ///
public async Task ListToolsAsync(CancellationToken cancellationToken)
{
return await SendRequestAsync(
@@ -151,6 +147,7 @@ public class McpClient : IMcpClient
).ConfigureAwait(false);
}
+ ///
public async Task CallToolAsync(string toolName, Dictionary? arguments = null,
CancellationToken cancellationToken = default)
{
@@ -218,6 +215,7 @@ public class McpClient : IMcpClient
}
}
+ ///
public async Task SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken) where T : class
{
if (!_transport.IsConnected)
@@ -275,9 +273,7 @@ public class McpClient : IMcpClient
await _transport.SendMessageAsync(notification, cancellationToken).ConfigureAwait(false);
}
- ///
- /// Registers a handler for notifications of a specific method.
- ///
+ ///
public void OnNotification(string method, Func handler)
{
var handlers = _notificationHandlers.GetOrAdd(method, _ => new());
diff --git a/src/mcpdotnet/Client/McpClientException.cs b/src/mcpdotnet/Client/McpClientException.cs
index 900542f..f6692d5 100644
--- a/src/mcpdotnet/Client/McpClientException.cs
+++ b/src/mcpdotnet/Client/McpClientException.cs
@@ -1,5 +1,4 @@
-// Client/McpClientOptions.cs
-namespace McpDotNet.Client;
+namespace McpDotNet.Client;
///
/// Represents errors that occur in the MCP client.
@@ -11,15 +10,29 @@ public class McpClientException : Exception
///
public int? ErrorCode { get; }
+ ///
+ /// Initializes a new instance of the class with a specified error message.
+ ///
+ /// The message that describes the error.
public McpClientException(string message) : base(message)
{
}
+ ///
+ /// Initializes a new instance of the class with a specified error message and error code.
+ ///
+ /// The message that describes the error.
+ /// The error code associated with the JSON-RPC error response.
public McpClientException(string message, int errorCode) : base(message)
{
ErrorCode = errorCode;
}
+ ///
+ /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception.
+ ///
+ /// The message that describes the error.
+ /// The exception that is the cause of the current exception, or a null reference if no inner exception is specified.
public McpClientException(string message, Exception innerException) : base(message, innerException)
{
}
diff --git a/src/mcpdotnet/Client/McpClientFactory.cs b/src/mcpdotnet/Client/McpClientFactory.cs
index 2561907..7b9200f 100644
--- a/src/mcpdotnet/Client/McpClientFactory.cs
+++ b/src/mcpdotnet/Client/McpClientFactory.cs
@@ -1,11 +1,13 @@
-// Client/McpClientFactory.cs
-namespace McpDotNet.Client;
+namespace McpDotNet.Client;
using McpDotNet.Configuration;
using McpDotNet.Protocol.Transport;
///
-/// Factory for creating MCP clients based on configuration.
+/// Factory for creating MCP clients based on configuration. It caches clients for reuse, so it is safe to call GetClientAsync multiple times.
+/// Call GetClientAsync to get a client for a specific server (by ID), which will create a new client and connect if it doesn't already exist.
+/// All server configurations must be passed in the constructor.
+/// Capabilities (as defined in client options) are shared across all clients, as the client host can always decide not to use certain capabilities.
///
public class McpClientFactory
{
@@ -15,6 +17,15 @@ public class McpClientFactory
private readonly Func _transportFactoryMethod;
private readonly Func _clientFactoryMethod;
+ ///
+ /// Initializes a new instance of the class.
+ /// It is not necessary to pass factory methods for creating transports and clients, as default implementations are provided.
+ /// Custom factory methods can be provided for mocking or to use custom transport or client implementations.
+ ///
+ /// Configuration objects for each server the factory should support.
+ /// A configuration object which specifies client capabilities and protocol version.
+ /// An optional factory method which returns transport implementations based on a server configuration.
+ /// An optional factory method which creates a client based on client options and transport implementation.
public McpClientFactory(
IEnumerable serverConfigs,
McpClientOptions clientOptions,
@@ -28,8 +39,12 @@ public class McpClientFactory
}
///
- /// Gets or creates a client for the specified server.
+ /// Gets or creates a client for the specified server. The first time a server is requested, a new client is created and connected.
+ /// Note that this will often spawn the server process during connection, so in some cases you want to call this method only when needed.
+ /// In other cases, you may want to call it ahead of time to ensure the server is ready when needed, as it may take some time to start up.
///
+ /// The ID of the server to connect to. It must have been passed in the serverConfigs when constructing the factory.
+ /// A token to cancel the operation.
public async Task GetClientAsync(string serverId, CancellationToken cancellationToken = default)
{
if (!_serverConfigs.TryGetValue(serverId, out var config))
diff --git a/src/mcpdotnet/Client/McpClientOptions.cs b/src/mcpdotnet/Client/McpClientOptions.cs
index 00d6475..8bc7902 100644
--- a/src/mcpdotnet/Client/McpClientOptions.cs
+++ b/src/mcpdotnet/Client/McpClientOptions.cs
@@ -1,10 +1,11 @@
-// Client/McpClientOptions.cs
+using McpDotNet.Protocol.Types;
+
namespace McpDotNet.Client;
-using global::McpDotNet.Protocol.Types;
-
///
-/// Configuration options for the MCP client.
+/// Configuration options for the MCP client. This is passed to servers during the initialization sequence, letting them know about the client's capabilities and
+/// protocol version.
+/// See the protocol specification for details on capability negotiation
///
public record McpClientOptions
{
diff --git a/src/mcpdotnet/Configuration/McpServerConfig.cs b/src/mcpdotnet/Configuration/McpServerConfig.cs
index f245abd..3f9d735 100644
--- a/src/mcpdotnet/Configuration/McpServerConfig.cs
+++ b/src/mcpdotnet/Configuration/McpServerConfig.cs
@@ -1,5 +1,4 @@
-// Configuration/McpServerConfig.cs
-namespace McpDotNet.Configuration;
+namespace McpDotNet.Configuration;
///
/// Configuration for an MCP server connection.
diff --git a/src/mcpdotnet/Features/LlmIntegration/DefaultMcpToolProvider.cs b/src/mcpdotnet/Features/LlmIntegration/DefaultMcpToolProvider.cs
deleted file mode 100644
index ba92f9c..0000000
--- a/src/mcpdotnet/Features/LlmIntegration/DefaultMcpToolProvider.cs
+++ /dev/null
@@ -1,188 +0,0 @@
-// Features/LlmIntegration/DefaultMcpToolProvider.cs
-namespace McpDotNet.Features.LlmIntegration;
-
-using System.Text.Json;
-using mcpdotnet.Features.Tools;
-using McpDotNet.Client;
-using McpDotNet.Features.Tools;
-
-///
-/// Default implementation of IMcpToolProvider that handles conversion between MCP and LLM-friendly formats.
-///
-public class DefaultMcpToolProvider : IMcpToolProvider
-{
- private readonly McpClient _client;
- private readonly JsonSerializerOptions _jsonOptions;
-
- public DefaultMcpToolProvider(McpClient client, JsonSerializerOptions? jsonOptions = null)
- {
- _client = client;
- _jsonOptions = jsonOptions ?? new JsonSerializerOptions
- {
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase
- };
- }
-
- ///
- public async Task> GetToolsAsync(CancellationToken cancellationToken = default)
- {
- return await _client.ListToolDefsAsync(cancellationToken);
- }
-
- ///
- public async Task ExecuteToolCallAsync(
- string toolName,
- Dictionary arguments,
- CancellationToken cancellationToken = default)
- {
- try
- {
- // Execute tool call
- var result = await _client.CallToolAsync(toolName, arguments, cancellationToken);
- if (result != null)
- {
- return new ToolResult()
- {
- Succeeded = !result.IsError,
- Content = result.Content.Select(c => new ToolContent()
- {
- Type = c.Type,
- Value = (c.Data ?? c.Text) ?? "",
- MimeType = c.MimeType
- }).ToList()
- };
- }
- else
- {
- return new ToolResult
- {
- Succeeded = false,
- Content = [new ToolContent
- {
- Type = "error",
- Value = "Tool execution failed with no error message"
- }],
- };
- }
- }
- catch (Exception ex)
- {
- return new ToolResult
- {
- Succeeded = false,
- Content = [new ToolContent
- {
- Type = "error",
- Value = ex.Message
- }],
- };
- }
- }
-
-
-
- //private static (Dictionary Arguments, List Required) ParseInputSchema(ToolInputSchema schema)
- //{
- // var arguments = new Dictionary();
- // var required = new List();
-
- // if (schema.Properties == null)
- // {
- // return (arguments, required);
- // }
-
- // foreach (var (name, propObj) in schema.Properties)
- // {
- // // Convert property to JsonElement for easier parsing
- // var prop = (propObj as JsonElement?) ?? JsonSerializer.SerializeToElement(propObj);
-
- // if (prop.TryGetProperty("type", out var typeElement))
- // {
- // var type = typeElement.GetString() ?? "unknown";
- // var description = prop.TryGetProperty("description", out var descElement)
- // ? descElement.GetString()
- // : string.Empty;
-
- // var isRequired = prop.TryGetProperty("required", out var reqElement) && reqElement.GetBoolean();
- // if (isRequired)
- // {
- // required.Add(name);
- // }
-
- // string[]? enumValues = null;
- // if (type == "string" && prop.TryGetProperty("enum", out var enumElement))
- // {
- // enumValues = enumElement.EnumerateArray()
- // .Select(e => e.GetString())
- // .Where(s => s != null)
- // .Cast()
- // .ToArray();
- // }
-
- // arguments[name] = new ToolArgument
- // {
- // Name = name,
- // Type = type,
- // Description = description ?? string.Empty,
- // Required = isRequired,
- // EnumValues = enumValues
- // };
- // }
- // }
-
- // // Also check for required array at schema root
- // if (schema.Properties.TryGetValue("required", out var rootRequired))
- // {
- // var requiredArray = rootRequired as JsonElement?;
- // if (requiredArray?.ValueKind == JsonValueKind.Array)
- // {
- // foreach (var item in requiredArray.Value.EnumerateArray())
- // {
- // var name = item.GetString();
- // if (name != null && !required.Contains(name))
- // {
- // required.Add(name);
-
- // // Update the argument's required flag if it exists
- // if (arguments.TryGetValue(name, out var arg))
- // {
- // arguments[name] = arg with { Required = true };
- // }
- // }
- // }
- // }
- // }
-
- // return (arguments, required);
- //}
-
- //private static ToolResultContent ConvertContent(IContent content)
- //{
- // return content switch
- // {
- // TextContent text => new ToolResultContent
- // {
- // Type = "text",
- // Value = text.Text,
- // },
- // ImageContent image => new ToolResultContent
- // {
- // Type = "image",
- // Value = image.Data,
- // MimeType = image.MimeType
- // },
- // _ => new ToolResultContent
- // {
- // Type = "unknown",
- // Value = content.ToString() ?? string.Empty
- // }
- // };
- //}
-
- //private static string ExtractErrorMessage(IReadOnlyList content)
- //{
- // // Try to extract error message from content, defaulting to first text content if available
- // var textContent = content.OfType().FirstOrDefault();
- // return textContent?.Text ?? "Tool execution failed with no error message";
- //}
-}
\ No newline at end of file
diff --git a/src/mcpdotnet/Features/LlmIntegration/IMcpToolProvider.cs b/src/mcpdotnet/Features/LlmIntegration/IMcpToolProvider.cs
deleted file mode 100644
index bab0503..0000000
--- a/src/mcpdotnet/Features/LlmIntegration/IMcpToolProvider.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Features/LlmIntegration/IMcpToolProvider.cs
-using mcpdotnet.Features.Tools;
-
-namespace McpDotNet.Features.LlmIntegration;
-
-///
-/// Provides MCP tool information and execution capabilities to an LLM.
-///
-public interface IMcpToolProvider
-{
- ///
- /// Gets the available tools and their descriptions.
- ///
- Task> GetToolsAsync(CancellationToken cancellationToken = default);
-
- ///
- /// Executes a tool call.
- ///
- Task ExecuteToolCallAsync(
- string toolName,
- Dictionary arguments,
- CancellationToken cancellationToken = default);
-}
\ No newline at end of file
diff --git a/src/mcpdotnet/Features/Tools/ToolMapper.cs b/src/mcpdotnet/Features/Tools/ToolMapper.cs
deleted file mode 100644
index 421253c..0000000
--- a/src/mcpdotnet/Features/Tools/ToolMapper.cs
+++ /dev/null
@@ -1,115 +0,0 @@
-using System.Text.Json;
-using mcpdotnet.Features.Tools;
-using McpDotNet.Protocol.Types;
-
-namespace McpDotNet.Features.Tools;
-
-internal static class ToolMapper
-{
- public static ToolDefinition ToToolDefinition(Tool protocolTool)
- {
- ArgumentNullException.ThrowIfNull(protocolTool);
- ArgumentNullException.ThrowIfNull(protocolTool.InputSchema);
-
- var parameters = new Dictionary();
-
- if (protocolTool.InputSchema.Properties != null)
- {
- foreach (var property in protocolTool.InputSchema.Properties)
- {
- // Convert the dynamic property value to a JsonElement to access its properties
- var propertySchema = property.Value;
-
- parameters[property.Key] = new ToolParameter
- {
- Name = property.Key,
- Type = propertySchema.Type,
- Description = propertySchema.Description,
- Required = IsPropertyRequired(protocolTool.InputSchema, property.Key),
- AllowedValues = GetEnumValues(propertySchema)
- };
- }
- }
-
- return new ToolDefinition
- {
- Name = protocolTool.Name,
- Description = protocolTool.Description ?? string.Empty,
- Parameters = parameters
- };
- }
-
- public static ToolResult ToToolResult(CallToolResponse protocolResult)
- {
- ArgumentNullException.ThrowIfNull(protocolResult);
-
- return new ToolResult
- {
- Succeeded = protocolResult.IsError,
- Content = protocolResult.Content?.Select(ToToolContent).ToList(),
- ErrorMessage = protocolResult.IsError == true ?
- GetErrorMessage(protocolResult.Content) : null
- };
- }
-
- private static mcpdotnet.Features.Tools.ToolContent ToToolContent(Protocol.Types.ToolContent c)
- {
- return new mcpdotnet.Features.Tools.ToolContent()
- {
- Type = c.Type,
- Value = (c.Data ?? c.Text) ?? "",
- MimeType = c.MimeType
- };
- }
-
- private static string GetSchemaType(JsonElement schema)
- {
- if (!schema.TryGetProperty("type", out var typeElement))
- {
- return "object";
- }
-
- return typeElement.GetString() ?? "object";
- }
-
- private static string GetSchemaDescription(JsonElement schema)
- {
- if (!schema.TryGetProperty("description", out var descElement))
- {
- return string.Empty;
- }
-
- return descElement.GetString() ?? string.Empty;
- }
-
- private static bool IsPropertyRequired(JsonSchema? schema, string propertyName)
- {
- if (schema?.Required != null && schema.Required.Contains(propertyName))
- {
- return true;
- }
-
- return false;
- }
-
- private static string[]? GetEnumValues(JsonSchemaProperty? schema)
- {
- // TODO
- return null;
- }
-
- private static string GetErrorMessage(IReadOnlyList? contents)
- {
- if (contents == null || contents.Count == 0)
- {
- return "Unknown error";
- }
-
- // Concatenate all text content as the error message
- var errorMessages = contents
- .Where(c => !string.IsNullOrEmpty(c.Text))
- .Select(c => c.Text);
-
- return string.Join(" ", errorMessages);
- }
-}
\ No newline at end of file
diff --git a/src/mcpdotnet/Features/Tools/ToolsExtensions.cs b/src/mcpdotnet/Features/Tools/ToolsExtensions.cs
deleted file mode 100644
index efb65fe..0000000
--- a/src/mcpdotnet/Features/Tools/ToolsExtensions.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-using mcpdotnet.Features.Tools;
-using McpDotNet.Client;
-using McpDotNet.Protocol.Messages;
-using McpDotNet.Protocol.Types;
-
-namespace McpDotNet.Features.Tools;
-
-///
-/// Extension methods for working with MCP tools.
-///
-public static class ToolsExtensions
-{
- ///
- /// Lists all available tools from the server.
- ///
- public static async Task> ListToolDefsAsync(
- this McpClient client,
- CancellationToken cancellationToken = default)
- {
- if (client.ServerCapabilities?.Tools == null)
- {
- throw new McpClientException("Server does not support tools");
- }
-
- var response = await client.ListToolsAsync(cancellationToken);
-
- return response.Tools
- .Select(ToolMapper.ToToolDefinition)
- .ToList();
- }
-
- ///
- /// Calls a tool with the specified arguments.
- ///
- public static async Task CallToolAsync(
- this McpClient client,
- string toolName,
- object? arguments = null,
- CancellationToken cancellationToken = default)
- {
- if (client.ServerCapabilities?.Tools == null)
- {
- throw new McpClientException("Server does not support tools");
- }
-
- var response = await client.SendRequestAsync(
- new JsonRpcRequest
- {
- Method = "tools/call",
- Params = new
- {
- name = toolName,
- arguments = arguments
- }
- },
- cancellationToken
- );
-
- return ToolMapper.ToToolResult(response);
- }
-}
\ No newline at end of file
diff --git a/src/mcpdotnet/Features/Tools/ToolsTypes.cs b/src/mcpdotnet/Features/Tools/ToolsTypes.cs
deleted file mode 100644
index 740ccc1..0000000
--- a/src/mcpdotnet/Features/Tools/ToolsTypes.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-// Features/Tools/ToolTypes.cs
-namespace mcpdotnet.Features.Tools;
-
-///
-/// Represents a tool available from an MCP server.
-///
-public record ToolDefinition
-{
- public required string Name { get; init; }
- public required string Description { get; init; }
- public required Dictionary Parameters { get; init; }
-}
-
-
-///
-/// Represents a tool parameter.
-///
-public record ToolParameter
-{
- public required string Name { get; init; }
- public required string Type { get; init; }
- public required string Description { get; init; }
- public bool Required { get; init; }
- public string[]? AllowedValues { get; init; }
-}
-
-///
-/// Result of a tool execution, from server to client
-public record McpToolCallResponse
-{
- // TODO
-}
-
-///
-/// Result of a tool execution, from client to host.
-///
-public record ToolResult
-{
- public required bool Succeeded { get; init; }
- public List? Content { get; init; }
- public string? ErrorMessage { get; init; }
-}
-
-///
-/// Content returned by a tool execution.
-///
-public record ToolContent
-{
- ///
- /// The type of content.
- ///
- public required string Type { get; init; }
-
- ///
- /// The content value.
- ///
- public required object Value { get; init; }
-
- ///
- /// Optional MIME type for binary content.
- ///
- public string? MimeType { get; init; }
-}
-
-///
-/// A list of tools available from an MCP server.
-///
-public record ToolDefinitionList
-{
- ///
- /// The list of tools.
- ///
- public required List Tools { get; init; }
-}
\ No newline at end of file
diff --git a/src/mcpdotnet/Protocol/Types/BlobResourceContents.cs b/src/mcpdotnet/Protocol/Types/BlobResourceContents.cs
new file mode 100644
index 0000000..325fbf9
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/BlobResourceContents.cs
@@ -0,0 +1,21 @@
+namespace McpDotNet.Protocol.Types;
+
+using System.Text.Json.Serialization;
+
+///
+/// Represents binary resource contents.
+///
+public record BlobResourceContents : IResourceContents
+{
+ [JsonPropertyName("uri")]
+ public required string Uri { get; init; }
+
+ [JsonPropertyName("mimeType")]
+ public string? MimeType { get; init; }
+
+ ///
+ /// The base64-encoded binary content of the resource.
+ ///
+ [JsonPropertyName("blob")]
+ public required string Blob { get; init; }
+}
\ No newline at end of file
diff --git a/src/mcpdotnet/Protocol/Types/CallToolResponse.cs b/src/mcpdotnet/Protocol/Types/CallToolResponse.cs
new file mode 100644
index 0000000..090a839
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/CallToolResponse.cs
@@ -0,0 +1,18 @@
+namespace McpDotNet.Protocol.Types;
+
+///
+/// A response to a request to call a tool on the server.
+/// See the schema for details
+///
+public class CallToolResponse
+{
+ ///
+ /// The server's response to a tools/call request from the client.
+ ///
+ public List Content { get; set; } = new();
+
+ ///
+ /// Whether the tool call was unsuccessful. If true, the call was unsuccessful.
+ ///
+ public bool IsError { get; set; }
+}
diff --git a/src/mcpdotnet/Protocol/Types/Capabilities.cs b/src/mcpdotnet/Protocol/Types/Capabilities.cs
index e6c7782..2c5273a 100644
--- a/src/mcpdotnet/Protocol/Types/Capabilities.cs
+++ b/src/mcpdotnet/Protocol/Types/Capabilities.cs
@@ -26,42 +26,6 @@ public record ClientCapabilities
public SamplingCapability? Sampling { get; init; }
}
-///
-/// Represents the capabilities that a server may support.
-///
-public record ServerCapabilities
-{
- ///
- /// Experimental, non-standard capabilities that the server supports.
- ///
- [JsonPropertyName("experimental")]
- public Dictionary? Experimental { get; init; }
-
- ///
- /// Present if the server supports sending log messages to the client.
- ///
- [JsonPropertyName("logging")]
- public LoggingCapability? Logging { get; init; }
-
- ///
- /// Present if the server offers any prompt templates.
- ///
- [JsonPropertyName("prompts")]
- public PromptsCapability? Prompts { get; init; }
-
- ///
- /// Present if the server offers any resources to read.
- ///
- [JsonPropertyName("resources")]
- public ResourcesCapability? Resources { get; init; }
-
- ///
- /// Present if the server offers any tools to call.
- ///
- [JsonPropertyName("tools")]
- public ToolsCapability? Tools { get; init; }
-}
-
///
/// Represents the roots capability configuration.
///
diff --git a/src/mcpdotnet/Protocol/Types/IResourceContents.cs b/src/mcpdotnet/Protocol/Types/IResourceContents.cs
new file mode 100644
index 0000000..a652966
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/IResourceContents.cs
@@ -0,0 +1,10 @@
+namespace McpDotNet.Protocol.Types;
+
+///
+/// Interface for resource contents.
+///
+public interface IResourceContents
+{
+ string Uri { get; }
+ string? MimeType { get; }
+}
diff --git a/src/mcpdotnet/Protocol/Types/Implementation.cs b/src/mcpdotnet/Protocol/Types/Implementation.cs
index d4e730d..d93cd12 100644
--- a/src/mcpdotnet/Protocol/Types/Implementation.cs
+++ b/src/mcpdotnet/Protocol/Types/Implementation.cs
@@ -1,5 +1,4 @@
-// Protocol/Types/Implementation.cs
-namespace McpDotNet.Protocol.Types;
+namespace McpDotNet.Protocol.Types;
using System.Text.Json.Serialization;
diff --git a/src/mcpdotnet/Protocol/Types/JsonSchema.cs b/src/mcpdotnet/Protocol/Types/JsonSchema.cs
new file mode 100644
index 0000000..d322f31
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/JsonSchema.cs
@@ -0,0 +1,23 @@
+namespace McpDotNet.Protocol.Types;
+
+///
+/// Represents a JSON schema for a tool's input arguments.
+/// See the schema for details
+///
+public class JsonSchema
+{
+ ///
+ /// The type of the schema, should be "object".
+ ///
+ public string Type { get; set; } = string.Empty;
+
+ ///
+ /// Map of property names to property definitions.
+ ///
+ public Dictionary? Properties { get; set; }
+
+ ///
+ /// List of required property names.
+ ///
+ public List? Required { get; set; }
+}
diff --git a/src/mcpdotnet/Protocol/Types/JsonSchemaProperty.cs b/src/mcpdotnet/Protocol/Types/JsonSchemaProperty.cs
new file mode 100644
index 0000000..35a5ede
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/JsonSchemaProperty.cs
@@ -0,0 +1,18 @@
+namespace McpDotNet.Protocol.Types;
+
+///
+/// Represents a property in a JSON schema.
+/// See the schema for details
+///
+public class JsonSchemaProperty
+{
+ ///
+ /// The type of the property. Should be a JSON Schema type and is required.
+ ///
+ public string Type { get; set; } = string.Empty;
+
+ ///
+ /// A human-readable description of the property.
+ ///
+ public string? Description { get; set; }
+}
diff --git a/src/mcpdotnet/Protocol/Types/ListToolsResponse.cs b/src/mcpdotnet/Protocol/Types/ListToolsResponse.cs
new file mode 100644
index 0000000..433c6c9
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/ListToolsResponse.cs
@@ -0,0 +1,13 @@
+namespace McpDotNet.Protocol.Types;
+
+///
+/// A response to a request to list the tools available on the server.
+/// See the schema for details
+///
+public class ListToolsResponse
+{
+ ///
+ /// The server's response to a tools/list request from the client.
+ ///
+ public List Tools { get; set; } = new();
+}
diff --git a/src/mcpdotnet/Protocol/Types/Resources.cs b/src/mcpdotnet/Protocol/Types/Resources.cs
index 9f8e8c9..a746ff8 100644
--- a/src/mcpdotnet/Protocol/Types/Resources.cs
+++ b/src/mcpdotnet/Protocol/Types/Resources.cs
@@ -37,48 +37,3 @@ public record Resource
[JsonPropertyName("annotations")]
public Annotations? Annotations { get; init; }
}
-
-///
-/// Interface for resource contents.
-///
-public interface IResourceContents
-{
- string Uri { get; }
- string? MimeType { get; }
-}
-
-///
-/// Represents text-based resource contents.
-///
-public record TextResourceContents : IResourceContents
-{
- [JsonPropertyName("uri")]
- public required string Uri { get; init; }
-
- [JsonPropertyName("mimeType")]
- public string? MimeType { get; init; }
-
- ///
- /// The text content of the resource.
- ///
- [JsonPropertyName("text")]
- public required string Text { get; init; }
-}
-
-///
-/// Represents binary resource contents.
-///
-public record BlobResourceContents : IResourceContents
-{
- [JsonPropertyName("uri")]
- public required string Uri { get; init; }
-
- [JsonPropertyName("mimeType")]
- public string? MimeType { get; init; }
-
- ///
- /// The base64-encoded binary content of the resource.
- ///
- [JsonPropertyName("blob")]
- public required string Blob { get; init; }
-}
\ No newline at end of file
diff --git a/src/mcpdotnet/Protocol/Types/ServerCapabilities.cs b/src/mcpdotnet/Protocol/Types/ServerCapabilities.cs
new file mode 100644
index 0000000..50256d5
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/ServerCapabilities.cs
@@ -0,0 +1,39 @@
+namespace McpDotNet.Protocol.Types;
+
+using System.Text.Json.Serialization;
+
+///
+/// Represents the capabilities that a server may support.
+///
+public record ServerCapabilities
+{
+ ///
+ /// Experimental, non-standard capabilities that the server supports.
+ ///
+ [JsonPropertyName("experimental")]
+ public Dictionary? Experimental { get; init; }
+
+ ///
+ /// Present if the server supports sending log messages to the client.
+ ///
+ [JsonPropertyName("logging")]
+ public LoggingCapability? Logging { get; init; }
+
+ ///
+ /// Present if the server offers any prompt templates.
+ ///
+ [JsonPropertyName("prompts")]
+ public PromptsCapability? Prompts { get; init; }
+
+ ///
+ /// Present if the server offers any resources to read.
+ ///
+ [JsonPropertyName("resources")]
+ public ResourcesCapability? Resources { get; init; }
+
+ ///
+ /// Present if the server offers any tools to call.
+ ///
+ [JsonPropertyName("tools")]
+ public ToolsCapability? Tools { get; init; }
+}
diff --git a/src/mcpdotnet/Protocol/Types/TextResourceContents.cs b/src/mcpdotnet/Protocol/Types/TextResourceContents.cs
new file mode 100644
index 0000000..b01a1db
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/TextResourceContents.cs
@@ -0,0 +1,21 @@
+namespace McpDotNet.Protocol.Types;
+
+using System.Text.Json.Serialization;
+
+///
+/// Represents text-based resource contents.
+///
+public record TextResourceContents : IResourceContents
+{
+ [JsonPropertyName("uri")]
+ public required string Uri { get; init; }
+
+ [JsonPropertyName("mimeType")]
+ public string? MimeType { get; init; }
+
+ ///
+ /// The text content of the resource.
+ ///
+ [JsonPropertyName("text")]
+ public required string Text { get; init; }
+}
diff --git a/src/mcpdotnet/Protocol/Types/Tool.cs b/src/mcpdotnet/Protocol/Types/Tool.cs
new file mode 100644
index 0000000..153cf17
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/Tool.cs
@@ -0,0 +1,23 @@
+namespace McpDotNet.Protocol.Types;
+
+///
+/// Represents a tool that the server is capable of calling. Part of the ListToolsResponse.
+/// See the schema for details
+///
+public class Tool
+{
+ ///
+ /// he name of the tool.
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// A human-readable description of the tool.
+ ///
+ public string? Description { get; set; }
+
+ ///
+ /// A JSON Schema object defining the expected parameters for the tool.
+ ///
+ public JsonSchema? InputSchema { get; set; }
+}
diff --git a/src/mcpdotnet/Protocol/Types/ToolContent.cs b/src/mcpdotnet/Protocol/Types/ToolContent.cs
new file mode 100644
index 0000000..a1efeb9
--- /dev/null
+++ b/src/mcpdotnet/Protocol/Types/ToolContent.cs
@@ -0,0 +1,58 @@
+namespace McpDotNet.Protocol.Types;
+
+///
+/// Represents the content of a tool response.
+/// See the schema for details
+/// There are multiple subtypes of content, depending on the "type" field, these are represented as separate classes.
+///
+public class ToolContent
+{
+ ///
+ /// The type of content. This determines the structure of the content object. Can be "image", "text", "resource".
+ ///
+ public string Type { get; set; } = string.Empty;
+
+ ///
+ /// The text content of the message.
+ ///
+ public string? Text { get; set; }
+
+ ///
+ /// The base64-encoded image data.
+ ///
+ public string? Data { get; set; }
+
+ ///
+ /// The MIME type of the image.
+ ///
+ public string? MimeType { get; set; }
+
+ ///
+ /// One of BlobResourceContents or TextResourceContents.
+ ///
+ public object? Resource { get; set; }
+
+ ///
+ /// Validates the content object.
+ ///
+ /// Whether the type of tool content has the correct data fields provided.
+ public bool Validate()
+ {
+ if (Type == "text")
+ {
+ return !string.IsNullOrEmpty(Text);
+ }
+ else if (Type == "image")
+ {
+ return !string.IsNullOrEmpty(Data) && !string.IsNullOrEmpty(MimeType);
+ }
+ else if (Type == "resource")
+ {
+ return Resource is BlobResourceContents || Resource is TextResourceContents;
+ }
+ else
+ {
+ return false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/mcpdotnet/Protocol/Types/Tools.cs b/src/mcpdotnet/Protocol/Types/Tools.cs
deleted file mode 100644
index 6527907..0000000
--- a/src/mcpdotnet/Protocol/Types/Tools.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-// Protocol/Types/Tools.cs
-namespace McpDotNet.Protocol.Types;
-
-using mcpdotnet.Features.Tools;
-using System.Text.Json.Serialization;
-
-public class ListToolsResponse
-{
- public List Tools { get; set; } = new();
-}
-
-public class Tool
-{
- public string Name { get; set; } = string.Empty;
- public string? Description { get; set; }
- public JsonSchema? InputSchema { get; set; }
-}
-
-public class JsonSchema
-{
- public string Type { get; set; } = string.Empty;
- public Dictionary? Properties { get; set; }
- public List? Required { get; set; }
-}
-
-public class JsonSchemaProperty
-{
- public string Type { get; set; } = string.Empty;
- public string? Description { get; set; }
-}
-
-public class CallToolResponse
-{
- public List Content { get; set; } = new();
- public bool IsError { get; set; }
-}
-
-public class ToolContent
-{
- public string Type { get; set; } = string.Empty;
- public string? Text { get; set; }
- public string? Data { get; set; }
- public string? MimeType { get; set; }
-}
\ No newline at end of file
diff --git a/src/mcpdotnet/Utils/Json/ContentJsonConverter.cs b/src/mcpdotnet/Utils/Json/ContentJsonConverter.cs
index 0c8ca3d..79859e8 100644
--- a/src/mcpdotnet/Utils/Json/ContentJsonConverter.cs
+++ b/src/mcpdotnet/Utils/Json/ContentJsonConverter.cs
@@ -1,15 +1,15 @@
-// Utils/Json/ContentJsonConverter.cs
-namespace McpDotNet.Utils.Json;
-
-using System.Text.Json;
+using System.Text.Json;
using System.Text.Json.Serialization;
-using global::McpDotNet.Protocol.Types;
+using McpDotNet.Protocol.Types;
+
+namespace McpDotNet.Utils.Json;
///
/// JSON converter for IContent that handles polymorphic deserialization based on the "type" property.
///
public class ContentJsonConverter : JsonConverter
{
+ ///
public override IContent? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
@@ -34,6 +34,7 @@ public class ContentJsonConverter : JsonConverter
};
}
+ ///
public override void Write(Utf8JsonWriter writer, IContent value, JsonSerializerOptions options)
{
switch (value)
diff --git a/src/mcpdotnet/Utils/Json/JsonRpcMessageConverter.cs b/src/mcpdotnet/Utils/Json/JsonRpcMessageConverter.cs
index 89931f4..f4712d7 100644
--- a/src/mcpdotnet/Utils/Json/JsonRpcMessageConverter.cs
+++ b/src/mcpdotnet/Utils/Json/JsonRpcMessageConverter.cs
@@ -1,15 +1,15 @@
-// Utils/Json/JsonRpcMessageConverter.cs (continued)
-namespace McpDotNet.Utils.Json;
-
-using System.Text.Json;
+using System.Text.Json;
using System.Text.Json.Serialization;
-using global::McpDotNet.Protocol.Messages;
+using McpDotNet.Protocol.Messages;
+
+namespace McpDotNet.Utils.Json;
///
/// JSON converter for IJsonRpcMessage that handles polymorphic deserialization of different message types.
///
public class JsonRpcMessageConverter : JsonConverter
{
+ ///
public override IJsonRpcMessage? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
@@ -63,6 +63,7 @@ public class JsonRpcMessageConverter : JsonConverter
throw new JsonException("Invalid JSON-RPC message format");
}
+ ///
public override void Write(Utf8JsonWriter writer, IJsonRpcMessage value, JsonSerializerOptions options)
{
switch (value)
diff --git a/src/mcpdotnet/Utils/Json/JsonSerializerOptionsExtensions.cs b/src/mcpdotnet/Utils/Json/JsonSerializerOptionsExtensions.cs
index 51fce6a..0659b4d 100644
--- a/src/mcpdotnet/Utils/Json/JsonSerializerOptionsExtensions.cs
+++ b/src/mcpdotnet/Utils/Json/JsonSerializerOptionsExtensions.cs
@@ -1,8 +1,7 @@
-namespace McpDotNet.Utils.Json;
-
-using System.Text.Json;
+using System.Text.Json;
using System.Text.Json.Serialization;
+namespace McpDotNet.Utils.Json;
///
/// Extensions for configuring System.Text.Json serialization options for MCP.
///
diff --git a/src/mcpdotnet/Utils/Json/ResourceContentsJsonConverter.cs b/src/mcpdotnet/Utils/Json/ResourceContentsJsonConverter.cs
index c472d7a..d323506 100644
--- a/src/mcpdotnet/Utils/Json/ResourceContentsJsonConverter.cs
+++ b/src/mcpdotnet/Utils/Json/ResourceContentsJsonConverter.cs
@@ -1,14 +1,15 @@
-namespace McpDotNet.Utils.Json;
-
-using System.Text.Json;
+using System.Text.Json;
using System.Text.Json.Serialization;
-using global::McpDotNet.Protocol.Types;
+using McpDotNet.Protocol.Types;
+
+namespace McpDotNet.Utils.Json;
///
/// JSON converter for IResourceContents that handles polymorphic deserialization based on content properties.
///
public class ResourceContentsJsonConverter : JsonConverter
{
+ ///
public override IResourceContents? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
@@ -32,6 +33,7 @@ public class ResourceContentsJsonConverter : JsonConverter
throw new JsonException("Resource contents must have either 'text' or 'blob' property");
}
+ ///
public override void Write(Utf8JsonWriter writer, IResourceContents value, JsonSerializerOptions options)
{
switch (value)
diff --git a/src/mcpdotnet/mcpdotnet.csproj b/src/mcpdotnet/mcpdotnet.csproj
index c46b6f2..fa71b7a 100644
--- a/src/mcpdotnet/mcpdotnet.csproj
+++ b/src/mcpdotnet/mcpdotnet.csproj
@@ -6,8 +6,4 @@
enable
-
-
-
-