Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
remove McpClient
  • Loading branch information
christothes committed May 8, 2025
commit 869a455df36948b23e14a70e677bd928e4f6543a
4 changes: 4 additions & 0 deletions src/OpenAI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
<PublicKey>0024000004800000940000000602000000240000525341310004000001000100097ad52abbeaa2e1a1982747cc0106534f65cfea6707eaed696a3a63daea80de2512746801a7e47f88e7781e71af960d89ba2e25561f70b0e2dbc93319e0af1961a719ccf5a4d28709b2b57a5d29b7c09dc8d269a490ebe2651c4b6e6738c27c5fb2c02469fe9757f0a3479ac310d6588a50a28d7dd431b907fd325e18b9e8ed</PublicKey>
</InternalsVisibleTo>
<InternalsVisibleTo Include="Azure.AI.OpenAI" Condition="'$(Configuration)' == 'Unsigned'" />
<InternalsVisibleTo Include="OpenAI.Tests" Condition="'$(Configuration)' != 'Unsigned'">
<PublicKey>0024000004800000940000000602000000240000525341310004000001000100b197326f2e5bfe2e2a49eb2a05bee871c55cc894325b3775159732ad816c4f304916e7f154295486f8ccabefa3c19b059d51cd19987cc2d31a3195d6203ad0948662f51cc61cc3eb535fc852dfe5159318c734b163f7d1387f1112e1ffe10f83aae7b809c4e36cf2025da5d1aed6b67e1556883d8778eeb63131c029555166de</PublicKey>
</InternalsVisibleTo>
<InternalsVisibleTo Include="OpenAI.Tests" Condition="'$(Configuration)' == 'Unsigned'" />
</ItemGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
Expand Down
2 changes: 1 addition & 1 deletion src/Utility/ChatTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ internal void AddFunctionTool(MethodInfo function)
/// </summary>
/// <param name="client">The MCP client instance.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task AddMcpToolsAsync(McpClient client)
internal async Task AddMcpToolsAsync(McpClient client)
{
if (client == null) throw new ArgumentNullException(nameof(client));
_mcpClientsByEndpoint[client.Endpoint.AbsoluteUri] = client;
Expand Down
2 changes: 1 addition & 1 deletion src/Utility/MCP/McpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace OpenAI.Agents;
/// Client for interacting with a Model Context Protocol (MCP) server.
/// </summary>
//[Experimental("OPENAIMCP001")]
public class McpClient
internal class McpClient
{
private readonly McpSession _session;
private readonly ClientPipeline _pipeline;
Expand Down
5 changes: 3 additions & 2 deletions src/Utility/MCP/McpSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,9 @@ private async Task ProcessEventAsync(SseEvent sseEvent)
{
var serverUri = _serverEndpoint;
var endpoint = serverUri.GetLeftPart(UriPartial.Authority);
string trailingSlash = endpoint.EndsWith("/") ? "" : "/";
var messageEndpoint = $"{endpoint}{trailingSlash}{sseEvent.Data.Trim()}";
var path = sseEvent.Data.Trim();
string trailingSlash = endpoint.EndsWith("/") || path.StartsWith("/") ? "" : "/";
var messageEndpoint = $"{endpoint}{trailingSlash}{path}";
_endpointTcs.TrySetResult(messageEndpoint);
}
break;
Expand Down
2 changes: 1 addition & 1 deletion src/Utility/ResponseTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ internal void AddFunctionTool(MethodInfo function)
/// </summary>
/// <param name="client">The MCP client instance.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public async Task AddMcpToolsAsync(McpClient client)
internal async Task AddMcpToolsAsync(McpClient client)
{
if (client == null) throw new ArgumentNullException(nameof(client));
_mcpClientsByEndpoint[client.Endpoint.AbsoluteUri] = client;
Expand Down
11 changes: 7 additions & 4 deletions tests/OpenAI.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@

<LangVersion>latest</LangVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' != 'Unsigned'">
<!-- Sign the assembly with the specified key file. -->
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\src\OpenAI.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\src\OpenAI.csproj" />
</ItemGroup>
Expand All @@ -22,8 +29,4 @@
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\src\Utility\Telemetry\*.cs" LinkBase="Telemetry\Shared" />
<Compile Include="..\src\Utility\AppContextSwitchHelper.cs" LinkBase="Telemetry\Shared" />
</ItemGroup>
</Project>
122 changes: 33 additions & 89 deletions tests/Utility/ChatToolsTests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System;
using System.ClientModel;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Moq;
Expand All @@ -17,57 +14,8 @@ namespace OpenAI.Tests.Utility;

[TestFixture]
[Category("Utility")]
public class ChatToolsTests
public class ChatToolsTests : ToolsTestsBase
{
private class TestTools
{
public static string Echo(string message) => message;
public static int Add(int a, int b) => a + b;
public static double Multiply(double x, double y) => x * y;
public static bool IsGreaterThan(long value1, long value2) => value1 > value2;
public static float Divide(float numerator, float denominator) => numerator / denominator;
public static string ConcatWithBool(string text, bool flag) => $"{text}:{flag}";
}

private class TestToolsAsync
{
public static async Task<string> EchoAsync(string message)
{
await Task.Delay(1); // Simulate async work
return message;
}

public static async Task<int> AddAsync(int a, int b)
{
await Task.Delay(1); // Simulate async work
return a + b;
}

public static async Task<double> MultiplyAsync(double x, double y)
{
await Task.Delay(1); // Simulate async work
return x * y;
}

public static async Task<bool> IsGreaterThanAsync(long value1, long value2)
{
await Task.Delay(1); // Simulate async work
return value1 > value2;
}

public static async Task<float> DivideAsync(float numerator, float denominator)
{
await Task.Delay(1); // Simulate async work
return numerator / denominator;
}

public static async Task<string> ConcatWithBoolAsync(string text, bool flag)
{
await Task.Delay(1); // Simulate async work
return $"{text}:{flag}";
}
}

private Mock<EmbeddingClient> mockEmbeddingClient;

[SetUp]
Expand Down Expand Up @@ -234,9 +182,6 @@ public async Task AddMcpToolsAsync_AddsToolsCorrectly()
{
// Arrange
var mcpEndpoint = new Uri("http://localhost:1234");
var mockMcpClient = new Mock<McpClient>(mcpEndpoint);
var tools = new ChatTools();

var mockToolsResponse = BinaryData.FromString(@"
{
""tools"": [
Expand Down Expand Up @@ -279,42 +224,48 @@ public async Task AddMcpToolsAsync_AddsToolsCorrectly()
]
}");

mockMcpClient.Setup(c => c.StartAsync())
.Returns(Task.CompletedTask);
mockMcpClient.Setup(c => c.ListToolsAsync())
.ReturnsAsync(mockToolsResponse);
mockMcpClient.Setup(c => c.CallToolAsync(It.IsAny<string>(), It.IsAny<BinaryData>()))
.ReturnsAsync(BinaryData.FromString("\"test result\""));
mockMcpClient.SetupGet(c => c.Endpoint)
.Returns(mcpEndpoint);
var responsesByTool = new Dictionary<string, string>
{
["mcp-tool-1"] = "\"tool1 result\"",
["mcp-tool-2"] = "\"tool2 result\""
};

var testClient = new TestMcpClient(
mcpEndpoint,
mockToolsResponse,
toolName => BinaryData.FromString(responsesByTool[toolName.Split('_').Last()]));
var tools = new ChatTools();

// Act
await tools.AddMcpToolsAsync(mockMcpClient.Object);
await tools.AddMcpToolsAsync(testClient);

// Assert
Assert.That(tools.Tools, Has.Count.EqualTo(2));
var toolNames = tools.Tools.Select(t => t.FunctionName).ToList();
Assert.That(toolNames, Contains.Item("localhost1234_-_mcp-tool-1"));
Assert.That(toolNames, Contains.Item("localhost1234_-_mcp-tool-2"));

// Verify we can call the tools
var toolCall = ChatToolCall.CreateFunctionToolCall("call1", "localhost1234_-_mcp-tool-1", BinaryData.FromString(@"{""param1"": ""test""}"));
var result = await tools.CallAsync(new[] { toolCall });
var resultsList = result.ToList();
// Verify we can call the tools with different responses
var toolCalls = new[]
{
ChatToolCall.CreateFunctionToolCall("call1", "localhost1234_-_mcp-tool-1", BinaryData.FromString(@"{""param1"": ""test""}")),
ChatToolCall.CreateFunctionToolCall("call2", "localhost1234_-_mcp-tool-2", BinaryData.FromString(@"{""param2"": ""test""}"))
};
var results = await tools.CallAsync(toolCalls);
var resultsList = results.ToList();

Assert.That(resultsList, Has.Count.EqualTo(1));
Assert.That(resultsList, Has.Count.EqualTo(2));
Assert.That(resultsList[0].ToolCallId, Is.EqualTo("call1"));
Assert.That(resultsList[0].Content[0].Text, Is.EqualTo("\"test result\""));
Assert.That(resultsList[0].Content[0].Text, Is.EqualTo("\"tool1 result\""));
Assert.That(resultsList[1].ToolCallId, Is.EqualTo("call2"));
Assert.That(resultsList[1].Content[0].Text, Is.EqualTo("\"tool2 result\""));
}

[Test]
public async Task CreateCompletionOptions_WithMaxToolsParameter_FiltersTools()
{
// Arrange
var mcpEndpoint = new Uri("http://localhost:1234");
var mockMcpClient = new Mock<McpClient>(mcpEndpoint);
var tools = new ChatTools(mockEmbeddingClient.Object);

var mockToolsResponse = BinaryData.FromString(@"
{
""tools"": [
Expand Down Expand Up @@ -364,7 +315,7 @@ public async Task CreateCompletionOptions_WithMaxToolsParameter_FiltersTools()
]
}");

// Setup mock responses
// Setup mock embedding responses
var embeddings = new[]
{
OpenAIEmbeddingsModelFactory.OpenAIEmbedding(vector: new[] { 0.8f, 0.5f }),
Expand All @@ -377,15 +328,6 @@ public async Task CreateCompletionOptions_WithMaxToolsParameter_FiltersTools()
usage: OpenAIEmbeddingsModelFactory.EmbeddingTokenUsage(30, 30));
var mockResponse = new MockPipelineResponse(200);

mockMcpClient.Setup(c => c.StartAsync())
.Returns(Task.CompletedTask);
mockMcpClient.Setup(c => c.ListToolsAsync())
.ReturnsAsync(mockToolsResponse);
mockMcpClient.Setup(c => c.CallToolAsync("math-tool", It.IsAny<BinaryData>()))
.ReturnsAsync(BinaryData.FromString("\"math-tool result\""));
mockMcpClient.SetupGet(c => c.Endpoint)
.Returns(mcpEndpoint);

mockEmbeddingClient
.Setup(c => c.GenerateEmbeddingAsync(
It.IsAny<string>(),
Expand All @@ -400,8 +342,14 @@ public async Task CreateCompletionOptions_WithMaxToolsParameter_FiltersTools()
It.IsAny<CancellationToken>()))
.ReturnsAsync(ClientResult.FromValue(embeddingCollection, mockResponse));

var testClient = new TestMcpClient(
mcpEndpoint,
mockToolsResponse,
toolName => BinaryData.FromString($"\"{toolName} result\""));
var tools = new ChatTools(mockEmbeddingClient.Object);

// Add the tools
await tools.AddMcpToolsAsync(mockMcpClient.Object);
await tools.AddMcpToolsAsync(testClient);

// Act & Assert
// Test with maxTools = 1
Expand All @@ -420,9 +368,5 @@ public async Task CreateCompletionOptions_WithMaxToolsParameter_FiltersTools()
var result = await tools.CallAsync(new[] { toolCall });
Assert.That(result.First().ToolCallId, Is.EqualTo("call1"));
Assert.That(result.First().Content[0].Text, Is.EqualTo("\"math-tool result\""));

// Verify expected interactions
mockMcpClient.Verify(c => c.StartAsync(), Times.Once);
mockMcpClient.Verify(c => c.ListToolsAsync(), Times.Once);
}
}
Loading