Skip to content

Commit 4ecb6bc

Browse files
authored
Allow override of name and description of McpClientTool (#165)
1 parent 9302466 commit 4ecb6bc

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

src/ModelContextProtocol/Client/McpClientTool.cs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using ModelContextProtocol.Protocol.Types;
22
using ModelContextProtocol.Utils.Json;
3+
using ModelContextProtocol.Utils;
34
using Microsoft.Extensions.AI;
45
using System.Text.Json;
56

@@ -9,22 +10,51 @@ namespace ModelContextProtocol.Client;
910
public sealed class McpClientTool : AIFunction
1011
{
1112
private readonly IMcpClient _client;
13+
private readonly string _name;
14+
private readonly string _description;
1215

13-
internal McpClientTool(IMcpClient client, Tool tool, JsonSerializerOptions serializerOptions)
16+
internal McpClientTool(IMcpClient client, Tool tool, JsonSerializerOptions serializerOptions, string? name = null, string? description = null)
1417
{
1518
_client = client;
1619
ProtocolTool = tool;
1720
JsonSerializerOptions = serializerOptions;
21+
_name = name ?? tool.Name;
22+
_description = description ?? tool.Description ?? string.Empty;
23+
}
24+
25+
/// <summary>
26+
/// Creates a new instance of the tool with the specified name.
27+
/// 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.
28+
/// The server will still be called with the original tool name, so no mapping is required.
29+
/// </summary>
30+
/// <param name="name">The model-facing name to give the tool.</param>
31+
/// <returns>Copy of this McpClientTool with the provided name</returns>
32+
public McpClientTool WithName(string name)
33+
{
34+
return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, name, _description);
35+
}
36+
37+
/// <summary>
38+
/// Creates a new instance of the tool with the specified description.
39+
/// This can be used to provide modified or additional (e.g. examples) context to the model about the tool.
40+
/// This will in general require a hard-coded mapping in the client.
41+
/// It is not recommended to use this without running evaluations to ensure the model actually benefits from the custom description.
42+
/// </summary>
43+
/// <param name="description">The description to give the tool.</param>
44+
/// <returns>Copy of this McpClientTool with the provided description</returns>
45+
public McpClientTool WithDescription(string description)
46+
{
47+
return new McpClientTool(_client, ProtocolTool, JsonSerializerOptions, _name, description);
1848
}
1949

2050
/// <summary>Gets the protocol <see cref="Tool"/> type for this instance.</summary>
2151
public Tool ProtocolTool { get; }
2252

2353
/// <inheritdoc/>
24-
public override string Name => ProtocolTool.Name;
54+
public override string Name => _name;
2555

2656
/// <inheritdoc/>
27-
public override string Description => ProtocolTool.Description ?? string.Empty;
57+
public override string Description => _description;
2858

2959
/// <inheritdoc/>
3060
public override JsonElement JsonSchema => ProtocolTool.InputSchema;

tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,32 @@ public async Task GetPromptsAsync_HonorsJsonSerializerOptions()
171171

172172
await Assert.ThrowsAsync<NotSupportedException>(() => client.GetPromptAsync("Prompt", new Dictionary<string, object?> { ["i"] = 42 }, emptyOptions, cancellationToken: TestContext.Current.CancellationToken));
173173
}
174+
175+
[Fact]
176+
public async Task WithName_ChangesToolName()
177+
{
178+
JsonSerializerOptions options = new(JsonSerializerOptions.Default);
179+
IMcpClient client = await CreateMcpClientForServer();
180+
181+
var tool = (await client.ListToolsAsync(options, TestContext.Current.CancellationToken)).First();
182+
var originalName = tool.Name;
183+
var renamedTool = tool.WithName("RenamedTool");
184+
185+
Assert.NotNull(renamedTool);
186+
Assert.Equal("RenamedTool", renamedTool.Name);
187+
Assert.Equal(originalName, tool?.Name);
188+
}
189+
190+
[Fact]
191+
public async Task WithDescription_ChangesToolDescription()
192+
{
193+
JsonSerializerOptions options = new(JsonSerializerOptions.Default);
194+
IMcpClient client = await CreateMcpClientForServer();
195+
var tool = (await client.ListToolsAsync(options, TestContext.Current.CancellationToken)).FirstOrDefault();
196+
var originalDescription = tool?.Description;
197+
var redescribedTool = tool?.WithDescription("ToolWithNewDescription");
198+
Assert.NotNull(redescribedTool);
199+
Assert.Equal("ToolWithNewDescription", redescribedTool.Description);
200+
Assert.Equal(originalDescription, tool?.Description);
201+
}
174202
}

0 commit comments

Comments
 (0)