diff --git a/src/ModelContextProtocol/Client/McpClientTool.cs b/src/ModelContextProtocol/Client/McpClientTool.cs
index 1fdc3a56..58cb02d5 100644
--- a/src/ModelContextProtocol/Client/McpClientTool.cs
+++ b/src/ModelContextProtocol/Client/McpClientTool.cs
@@ -1,5 +1,6 @@
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Utils.Json;
+using ModelContextProtocol.Utils;
using Microsoft.Extensions.AI;
using System.Text.Json;
@@ -9,22 +10,51 @@ namespace ModelContextProtocol.Client;
public sealed class McpClientTool : AIFunction
{
private readonly IMcpClient _client;
+ private readonly string _name;
+ private readonly string _description;
- internal McpClientTool(IMcpClient client, Tool tool, JsonSerializerOptions serializerOptions)
+ 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;
+ }
+
+ ///
+ /// Creates a new instance of the tool with the specified name.
+ /// This is useful for optimizing the tool name for specific models or for prefixing the tool name with a (usually server-derived) namespace to avoid conflicts.
+ /// The server will still be called with the original tool name, so no mapping is required.
+ ///
+ /// The model-facing name to give the tool.
+ /// Copy of this McpClientTool with the provided name
+ public McpClientTool WithName(string name)
+ {
+ return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, name, _description);
+ }
+
+ ///
+ /// Creates a new instance of the tool with the specified description.
+ /// This can be used to provide modified or additional (e.g. examples) context to the model about the tool.
+ /// This will in general require a hard-coded mapping in the client.
+ /// It is not recommended to use this without running evaluations to ensure the model actually benefits from the custom description.
+ ///
+ /// The description to give the tool.
+ /// Copy of this McpClientTool with the provided description
+ public McpClientTool WithDescription(string description)
+ {
+ return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, description);
}
/// Gets the protocol type for this instance.
public Tool ProtocolTool { get; }
///
- public override string Name => ProtocolTool.Name;
+ public override string Name => _name;
///
- public override string Description => ProtocolTool.Description ?? string.Empty;
+ public override string Description => _description;
///
public override JsonElement JsonSchema => ProtocolTool.InputSchema;
diff --git a/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs b/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs
index 7c3e92f9..02bd50d1 100644
--- a/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs
+++ b/tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs
@@ -171,4 +171,32 @@ public async Task GetPromptsAsync_HonorsJsonSerializerOptions()
await Assert.ThrowsAsync(() => client.GetPromptAsync("Prompt", new Dictionary { ["i"] = 42 }, emptyOptions, cancellationToken: TestContext.Current.CancellationToken));
}
+
+ [Fact]
+ public async Task WithName_ChangesToolName()
+ {
+ JsonSerializerOptions options = new(JsonSerializerOptions.Default);
+ IMcpClient client = await CreateMcpClientForServer();
+
+ var tool = (await client.ListToolsAsync(options, TestContext.Current.CancellationToken)).First();
+ var originalName = tool.Name;
+ var renamedTool = tool.WithName("RenamedTool");
+
+ Assert.NotNull(renamedTool);
+ Assert.Equal("RenamedTool", renamedTool.Name);
+ Assert.Equal(originalName, tool?.Name);
+ }
+
+ [Fact]
+ public async Task WithDescription_ChangesToolDescription()
+ {
+ JsonSerializerOptions options = new(JsonSerializerOptions.Default);
+ IMcpClient client = await CreateMcpClientForServer();
+ var tool = (await client.ListToolsAsync(options, TestContext.Current.CancellationToken)).FirstOrDefault();
+ var originalDescription = tool?.Description;
+ var redescribedTool = tool?.WithDescription("ToolWithNewDescription");
+ Assert.NotNull(redescribedTool);
+ Assert.Equal("ToolWithNewDescription", redescribedTool.Description);
+ Assert.Equal(originalDescription, tool?.Description);
+ }
}
\ No newline at end of file