Skip to content

Commit db4244c

Browse files
jozkeejeffhandley
authored andcommitted
Add back Uri ctor to HostedMcpServerTool (dotnet#6926)
1 parent 233e508 commit db4244c

File tree

6 files changed

+43
-13
lines changed

6 files changed

+43
-13
lines changed

src/Libraries/.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4533,7 +4533,7 @@ dotnet_diagnostic.S3994.severity = none
45334533
# Title : URI return values should not be strings
45344534
# Category : Major Code Smell
45354535
# Help Link: https://rules.sonarsource.com/csharp/RSPEC-3995
4536-
dotnet_diagnostic.S3995.severity = warning
4536+
dotnet_diagnostic.S3995.severity = suggestion
45374537

45384538
# Title : URI properties should not be strings
45394539
# Category : Major Code Smell

src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/UriContent.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class UriContent : AIContent
3030
/// <exception cref="ArgumentNullException"><paramref name="uri"/> is <see langword="null"/>.</exception>
3131
/// <exception cref="ArgumentNullException"><paramref name="mediaType"/> is <see langword="null"/>.</exception>
3232
/// <exception cref="ArgumentException"><paramref name="mediaType"/> is an invalid media type.</exception>
33-
/// <exception cref="UriFormat"><paramref name="uri"/> is an invalid URL.</exception>
33+
/// <exception cref="UriFormatException"><paramref name="uri"/> is an invalid URL.</exception>
3434
/// <remarks>
3535
/// A media type must be specified, so that consumers know what to do with the content.
3636
/// If an exact media type is not known, but the category (e.g. image) is known, a wildcard

src/Libraries/Microsoft.Extensions.AI.Abstractions/Tools/HostedMcpServerTool.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,31 @@ public HostedMcpServerTool(string serverName, string serverAddress)
2727
ServerAddress = Throw.IfNullOrWhitespace(serverAddress);
2828
}
2929

30+
/// <summary>
31+
/// Initializes a new instance of the <see cref="HostedMcpServerTool"/> class.
32+
/// </summary>
33+
/// <param name="serverName">The name of the remote MCP server.</param>
34+
/// <param name="serverUrl">The URL of the remote MCP server.</param>
35+
/// <exception cref="ArgumentNullException"><paramref name="serverName"/> or <paramref name="serverUrl"/> is <see langword="null"/>.</exception>
36+
/// <exception cref="ArgumentException"><paramref name="serverName"/> is empty or composed entirely of whitespace.</exception>
37+
/// <exception cref="ArgumentException"><paramref name="serverUrl"/> is not an absolute URL.</exception>
38+
public HostedMcpServerTool(string serverName, Uri serverUrl)
39+
: this(serverName, ValidateUrl(serverUrl))
40+
{
41+
}
42+
43+
private static string ValidateUrl(Uri serverUrl)
44+
{
45+
_ = Throw.IfNull(serverUrl);
46+
47+
if (!serverUrl.IsAbsoluteUri)
48+
{
49+
Throw.ArgumentException(nameof(serverUrl), "The provided URL is not absolute.");
50+
}
51+
52+
return serverUrl.AbsoluteUri;
53+
}
54+
3055
/// <inheritdoc />
3156
public override string Name => "mcp";
3257

test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Tools/HostedMcpServerToolTests.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class HostedMcpServerToolTests
1212
[Fact]
1313
public void Constructor_PropsDefault()
1414
{
15-
HostedMcpServerTool tool = new("serverName", "https://localhost/");
15+
HostedMcpServerTool tool = new("serverName", new Uri("https://localhost/"));
1616

1717
Assert.Empty(tool.AdditionalProperties);
1818

@@ -70,9 +70,14 @@ public void Constructor_Roundtrips()
7070
[Fact]
7171
public void Constructor_Throws()
7272
{
73-
Assert.Throws<ArgumentException>(() => new HostedMcpServerTool(string.Empty, "https://localhost/"));
74-
Assert.Throws<ArgumentNullException>(() => new HostedMcpServerTool(null!, "https://localhost/"));
75-
Assert.Throws<ArgumentException>(() => new HostedMcpServerTool("name", string.Empty));
76-
Assert.Throws<ArgumentNullException>(() => new HostedMcpServerTool("name", null!));
73+
Assert.Throws<ArgumentException>("serverName", () => new HostedMcpServerTool(string.Empty, "https://localhost/"));
74+
Assert.Throws<ArgumentException>("serverName", () => new HostedMcpServerTool(string.Empty, new Uri("https://localhost/")));
75+
Assert.Throws<ArgumentNullException>("serverName", () => new HostedMcpServerTool(null!, "https://localhost/"));
76+
Assert.Throws<ArgumentNullException>("serverName", () => new HostedMcpServerTool(null!, new Uri("https://localhost/")));
77+
78+
Assert.Throws<ArgumentException>("serverAddress", () => new HostedMcpServerTool("name", string.Empty));
79+
Assert.Throws<ArgumentException>("serverUrl", () => new HostedMcpServerTool("name", new Uri("/api/mcp", UriKind.Relative)));
80+
Assert.Throws<ArgumentNullException>("serverAddress", () => new HostedMcpServerTool("name", (string)null!));
81+
Assert.Throws<ArgumentNullException>("serverUrl", () => new HostedMcpServerTool("name", (Uri)null!));
7782
}
7883
}

test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientIntegrationTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public async Task RemoteMCP_ListTools()
7474

7575
ChatOptions chatOptions = new()
7676
{
77-
Tools = [new HostedMcpServerTool("deepwiki", "https://mcp.deepwiki.com/mcp") { ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire }],
77+
Tools = [new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp")) { ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire }],
7878
};
7979

8080
ChatResponse response = await CreateChatClient()!.GetResponseAsync("Which tools are available on the wiki_tools MCP server?", chatOptions);
@@ -96,7 +96,7 @@ async Task RunAsync(bool streaming, bool requireSpecific)
9696
{
9797
ChatOptions chatOptions = new()
9898
{
99-
Tools = [new HostedMcpServerTool("deepwiki", "https://mcp.deepwiki.com/mcp")
99+
Tools = [new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp"))
100100
{
101101
ApprovalMode = requireSpecific ?
102102
HostedMcpServerToolApprovalMode.RequireSpecific(null, ["read_wiki_structure", "ask_question"]) :
@@ -136,7 +136,7 @@ async Task RunAsync(bool streaming, bool requireSpecific, bool useConversationId
136136
{
137137
ChatOptions chatOptions = new()
138138
{
139-
Tools = [new HostedMcpServerTool("deepwiki", "https://mcp.deepwiki.com/mcp")
139+
Tools = [new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp"))
140140
{
141141
ApprovalMode = requireSpecific ?
142142
HostedMcpServerToolApprovalMode.RequireSpecific(["read_wiki_structure", "ask_question"], null) :

test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,7 @@ public async Task McpToolCall_ApprovalRequired_NonStreaming(string role)
929929

930930
var chatOptions = new ChatOptions
931931
{
932-
Tools = [new HostedMcpServerTool("deepwiki", "https://mcp.deepwiki.com/mcp")]
932+
Tools = [new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp"))]
933933
};
934934
McpServerToolApprovalRequestContent approvalRequest;
935935

@@ -1307,7 +1307,7 @@ public async Task McpToolCall_ApprovalNotRequired_NonStreaming(bool rawTool)
13071307

13081308
AITool mcpTool = rawTool ?
13091309
ResponseTool.CreateMcpTool("deepwiki", serverUri: new("https://mcp.deepwiki.com/mcp"), toolCallApprovalPolicy: new McpToolCallApprovalPolicy(GlobalMcpToolCallApprovalPolicy.NeverRequireApproval)).AsAITool() :
1310-
new HostedMcpServerTool("deepwiki", "https://mcp.deepwiki.com/mcp")
1310+
new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp"))
13111311
{
13121312
ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire,
13131313
};
@@ -1723,7 +1723,7 @@ public async Task McpToolCall_ApprovalNotRequired_Streaming()
17231723

17241724
ChatOptions chatOptions = new()
17251725
{
1726-
Tools = [new HostedMcpServerTool("deepwiki", "https://mcp.deepwiki.com/mcp")
1726+
Tools = [new HostedMcpServerTool("deepwiki", new Uri("https://mcp.deepwiki.com/mcp"))
17271727
{
17281728
ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire,
17291729
}

0 commit comments

Comments
 (0)