using ModelContextProtocol.Protocol.Types; using ModelContextProtocol.Utils.Json; using Microsoft.Extensions.AI; using System.Text.Json; using System.Collections.ObjectModel; namespace ModelContextProtocol.Client; /// /// Provides an that calls a tool via an . /// /// /// /// The class encapsulates an along with a description of /// a tool available via that client, allowing it to be invoked as an . This enables integration /// with AI models that support function calling capabilities. /// /// /// Tools retrieved from an MCP server can be customized for model presentation using methods like /// and without changing the underlying tool functionality. /// /// /// Typically, you would get instances of this class by calling the /// or extension methods on an instance. /// /// public sealed class McpClientTool : AIFunction { /// Additional properties exposed from tools. private static readonly ReadOnlyDictionary s_additionalProperties = new(new Dictionary() { ["Strict"] = false, // some MCP schemas may not meet "strict" requirements }); private readonly IMcpClient _client; private readonly string _name; private readonly string _description; internal McpClientTool(IMcpClient client, Tool tool, JsonSerializerOptions serializerOptions, string? name = null, string? description = null) { _client = client; ProtocolTool = tool; JsonSerializerOptions = serializerOptions; _name = name ?? tool.Name; _description = description ?? tool.Description ?? string.Empty; } /// /// Gets the protocol type for this instance. /// /// /// This property provides direct access to the underlying protocol representation of the tool, /// which can be useful for advanced scenarios or when implementing custom MCP client extensions. /// It contains the original metadata about the tool as provided by the server, including its /// name, description, and schema information before any customizations applied through methods /// like or . /// public Tool ProtocolTool { get; } /// public override string Name => _name; /// public override string Description => _description; /// public override JsonElement JsonSchema => ProtocolTool.InputSchema; /// public override JsonSerializerOptions JsonSerializerOptions { get; } /// public override IReadOnlyDictionary AdditionalProperties => s_additionalProperties; /// protected async override ValueTask InvokeCoreAsync( AIFunctionArguments arguments, CancellationToken cancellationToken) { CallToolResponse result = await _client.CallToolAsync(ProtocolTool.Name, arguments, JsonSerializerOptions, cancellationToken: cancellationToken).ConfigureAwait(false); return JsonSerializer.SerializeToElement(result, McpJsonUtilities.JsonContext.Default.CallToolResponse); } /// /// Creates a new instance of the tool but modified to return the specified name from its property. /// /// The model-facing name to give the tool. /// A new instance of with the provided name. /// /// /// This is useful for optimizing the tool name for specific models or for prefixing the tool name /// with a namespace to avoid conflicts. /// /// /// Changing the name can help with: /// /// /// Making the tool name more intuitive for the model /// Preventing name collisions when using tools from multiple sources /// Creating specialized versions of a general tool for specific contexts /// /// /// When invoking , the MCP server will still be called with /// the original tool name, so no mapping is required on the server side. This new name only affects /// the value returned from this instance's . /// /// public McpClientTool WithName(string name) { return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, name, _description); } /// /// Creates a new instance of the tool but modified to return the specified description from its property. /// /// The description to give the tool. /// /// /// Changing the description can help the model better understand the tool's purpose or provide more /// context about how the tool should be used. This is particularly useful when: /// /// /// The original description is too technical or lacks clarity for the model /// You want to add example usage scenarios to improve the model's understanding /// You need to tailor the tool's description for specific model requirements /// /// /// When invoking , the MCP server will still be called with /// the original tool description, so no mapping is required on the server side. This new description only affects /// the value returned from this instance's . /// /// /// A new instance of with the provided description. public McpClientTool WithDescription(string description) { return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, description); } }