Improve tool handling (#28)
* Improve tool handling * Rework stuff with new hosting options * Update readme * Fix build * Try run client tests * Fix build * Fix issues on linux * Try to run testserver on linux * Fix test issue * Fix code smells
This commit is contained in:
parent
7d69499ad5
commit
ec9bdfcf5d
10 changed files with 1036 additions and 235 deletions
|
|
@ -17,7 +17,7 @@ public class IntegrationTests
|
|||
{
|
||||
Id = "everything",
|
||||
Name = "Everything",
|
||||
TransportType = McpDotNet.Protocol.Transport.TransportTypes.StdIo,
|
||||
TransportType = "stdio",
|
||||
TransportOptions = new Dictionary<string, string>
|
||||
{
|
||||
["command"] = "npx",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\McpDotNet.Extensions.AI\McpDotNet.Extensions.AI.csproj" />
|
||||
<ProjectReference Include="..\..\src\mcpdotnet\mcpdotnet.csproj" />
|
||||
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.3.0-preview.1.25114.11" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ using McpDotNet.Server;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
|
||||
internal class Program
|
||||
internal static class Program
|
||||
{
|
||||
private static ILoggerFactory CreateLoggerFactory()
|
||||
{
|
||||
|
|
@ -17,7 +17,6 @@ internal class Program
|
|||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
|
||||
.CreateLogger();
|
||||
|
||||
var logsPath = Path.Combine(AppContext.BaseDirectory, "testserver.log");
|
||||
return LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddSerilog();
|
||||
|
|
@ -26,7 +25,7 @@ internal class Program
|
|||
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("Starting server...");
|
||||
Log.Logger.Information("Starting server...");
|
||||
|
||||
McpServerOptions options = new McpServerOptions()
|
||||
{
|
||||
|
|
@ -40,11 +39,12 @@ internal class Program
|
|||
ProtocolVersion = "2024-11-05",
|
||||
ServerInstructions = "This is a test server with only stub functionality",
|
||||
};
|
||||
|
||||
var loggerFactory = CreateLoggerFactory();
|
||||
McpServerFactory factory = new McpServerFactory(new StdioServerTransport("TestServer", loggerFactory), options, loggerFactory);
|
||||
IMcpServer server = factory.CreateServer();
|
||||
|
||||
Console.WriteLine("Server object created, registering handlers.");
|
||||
Log.Logger.Information("Server object created, registering handlers.");
|
||||
|
||||
#region Helped method
|
||||
static CreateMessageRequestParams CreateRequestSamplingParams(string context, string uri, int maxTokens = 100)
|
||||
|
|
@ -69,6 +69,7 @@ internal class Program
|
|||
#endregion
|
||||
|
||||
#region Tools
|
||||
|
||||
server.ListToolsHandler = (request, cancellationToken) =>
|
||||
{
|
||||
return Task.FromResult(new ListToolsResult()
|
||||
|
|
@ -108,22 +109,22 @@ internal class Program
|
|||
|
||||
server.CallToolHandler = async (request, cancellationToken) =>
|
||||
{
|
||||
if (request?.Name == "echo")
|
||||
if (request.Params?.Name == "echo")
|
||||
{
|
||||
if (request.Arguments is null || !request.Arguments.TryGetValue("message", out var message))
|
||||
if (request.Params?.Arguments is null || !request.Params.Arguments.TryGetValue("message", out var message))
|
||||
{
|
||||
throw new McpServerException("Missing required argument 'message'");
|
||||
}
|
||||
return new CallToolResponse()
|
||||
{
|
||||
Content = [new Content() { Text = "Echo: " + message.ToString(), Type = "text" }]
|
||||
Content = [new Content() { Text = "Echo: " + message?.ToString(), Type = "text" }]
|
||||
};
|
||||
}
|
||||
else if (request?.Name == "sampleLLM")
|
||||
else if (request.Params?.Name == "sampleLLM")
|
||||
{
|
||||
if (request.Arguments is null ||
|
||||
!request.Arguments.TryGetValue("prompt", out var prompt) ||
|
||||
!request.Arguments.TryGetValue("maxTokens", out var maxTokens))
|
||||
if (request.Params?.Arguments is null ||
|
||||
!request.Params.Arguments.TryGetValue("prompt", out var prompt) ||
|
||||
!request.Params.Arguments.TryGetValue("maxTokens", out var maxTokens))
|
||||
{
|
||||
throw new McpServerException("Missing required arguments 'prompt' and 'maxTokens'");
|
||||
}
|
||||
|
|
@ -137,7 +138,7 @@ internal class Program
|
|||
}
|
||||
else
|
||||
{
|
||||
throw new McpServerException($"Unknown tool: {request?.Name}");
|
||||
throw new McpServerException($"Unknown tool: {request.Params?.Name}");
|
||||
}
|
||||
};
|
||||
#endregion
|
||||
|
|
@ -186,12 +187,12 @@ internal class Program
|
|||
server.ListResourcesHandler = (request, cancellationToken) =>
|
||||
{
|
||||
int startIndex = 0;
|
||||
if (request is null) request = new();
|
||||
if (request.Cursor is not null)
|
||||
request ??= new(server, new());
|
||||
if (request.Params?.Cursor is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var startIndexAsString = Encoding.UTF8.GetString(Convert.FromBase64String(request.Cursor));
|
||||
var startIndexAsString = Encoding.UTF8.GetString(Convert.FromBase64String(request.Params.Cursor));
|
||||
startIndex = Convert.ToInt32(startIndexAsString);
|
||||
}
|
||||
catch
|
||||
|
|
@ -216,12 +217,11 @@ internal class Program
|
|||
|
||||
server.ReadResourceHandler = (request, cancellationToken) =>
|
||||
{
|
||||
if (request?.Uri is null)
|
||||
if (request.Params?.Uri is null)
|
||||
{
|
||||
throw new McpServerException("Missing required argument 'uri'");
|
||||
}
|
||||
|
||||
ResourceContents contents = resourceContents.FirstOrDefault(r => r.Uri == request.Uri)
|
||||
ResourceContents contents = resourceContents.FirstOrDefault(r => r.Uri == request.Params.Uri)
|
||||
?? throw new McpServerException("Resource not found");
|
||||
|
||||
return Task.FromResult(new ReadResourceResult()
|
||||
|
|
@ -268,8 +268,8 @@ internal class Program
|
|||
|
||||
server.GetPromptHandler = (request, cancellationToken) =>
|
||||
{
|
||||
List<PromptMessage> messages = [];
|
||||
if (request?.Name == "simple_prompt")
|
||||
List<PromptMessage> messages = new();
|
||||
if (request.Params?.Name == "simple_prompt")
|
||||
{
|
||||
messages.Add(new PromptMessage()
|
||||
{
|
||||
|
|
@ -281,10 +281,10 @@ internal class Program
|
|||
}
|
||||
});
|
||||
}
|
||||
else if (request?.Name == "complex_prompt")
|
||||
else if (request.Params?.Name == "complex_prompt")
|
||||
{
|
||||
string temperature = request.Arguments?["temperature"]?.ToString() ?? "unknown";
|
||||
string style = request.Arguments?["style"]?.ToString() ?? "unknown";
|
||||
string temperature = request.Params.Arguments?["temperature"]?.ToString() ?? "unknown";
|
||||
string style = request.Params.Arguments?["style"]?.ToString() ?? "unknown";
|
||||
messages.Add(new PromptMessage()
|
||||
{
|
||||
Role = Role.User,
|
||||
|
|
@ -316,7 +316,7 @@ internal class Program
|
|||
}
|
||||
else
|
||||
{
|
||||
throw new McpServerException($"Unknown prompt: {request?.Name}");
|
||||
throw new McpServerException($"Unknown prompt: {request.Params?.Name}");
|
||||
}
|
||||
|
||||
return Task.FromResult(new GetPromptResult()
|
||||
|
|
@ -329,11 +329,11 @@ internal class Program
|
|||
#region Sampling
|
||||
#endregion
|
||||
|
||||
Console.WriteLine("Server initialized.");
|
||||
Log.Logger.Information("Server initialized.");
|
||||
|
||||
await server.StartAsync();
|
||||
|
||||
Console.WriteLine("Server started.");
|
||||
Log.Logger.Information("Server started.");
|
||||
|
||||
// Run until process is stopped by the client (parent process)
|
||||
while (true)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using McpDotNet.Protocol.Types;
|
|||
|
||||
namespace McpDotNet.Tests;
|
||||
|
||||
[Trait("Execution", "Manual")]
|
||||
public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture>
|
||||
{
|
||||
private readonly ClientIntegrationTestFixture _fixture;
|
||||
|
|
|
|||
138
tests/mcpdotnet.Tests/McpServerBuilderExtensionsHandlerTests.cs
Normal file
138
tests/mcpdotnet.Tests/McpServerBuilderExtensionsHandlerTests.cs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
using McpDotNet.Configuration;
|
||||
using McpDotNet.Protocol.Types;
|
||||
using McpDotNet.Server;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
|
||||
namespace McpDotNet.Tests;
|
||||
|
||||
public class McpServerBuilderExtensionsHandlerTests
|
||||
{
|
||||
private readonly Mock<IMcpServerBuilder> _builder;
|
||||
private readonly ServiceCollection _services;
|
||||
|
||||
public McpServerBuilderExtensionsHandlerTests()
|
||||
{
|
||||
_services = new ServiceCollection();
|
||||
_builder = new Mock<IMcpServerBuilder>();
|
||||
_builder.SetupGet(b => b.Services).Returns(_services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithListToolsHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<ListToolsRequestParams>, CancellationToken, Task<ListToolsResult>> handler = (context, token) => Task.FromResult(new ListToolsResult());
|
||||
|
||||
_builder.Object.WithListToolsHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.ListToolsHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithCallToolHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<CallToolRequestParams>, CancellationToken, Task<CallToolResponse>> handler = (context, token) => Task.FromResult(new CallToolResponse());
|
||||
|
||||
_builder.Object.WithCallToolHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.CallToolHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithListPromptsHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<ListPromptsRequestParams>, CancellationToken, Task<ListPromptsResult>> handler = (context, token) => Task.FromResult(new ListPromptsResult());
|
||||
|
||||
_builder.Object.WithListPromptsHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.ListPromptsHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithGetPromptHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<GetPromptRequestParams>, CancellationToken, Task<GetPromptResult>> handler = (context, token) => Task.FromResult(new GetPromptResult());
|
||||
|
||||
_builder.Object.WithGetPromptHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.GetPromptHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithListResourcesHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<ListResourcesRequestParams>, CancellationToken, Task<ListResourcesResult>> handler = (context, token) => Task.FromResult(new ListResourcesResult());
|
||||
|
||||
_builder.Object.WithListResourcesHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.ListResourcesHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithReadResourceHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<ReadResourceRequestParams>, CancellationToken, Task<ReadResourceResult>> handler = (context, token) => Task.FromResult(new ReadResourceResult());
|
||||
|
||||
_builder.Object.WithReadResourceHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.ReadResourceHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithGetCompletionHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<CompleteRequestParams>, CancellationToken, Task<CompleteResult>> handler = (context, token) => Task.FromResult(new CompleteResult());
|
||||
|
||||
_builder.Object.WithGetCompletionHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.GetCompletionHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithSubscribeToResourcesHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<string>, CancellationToken, Task> handler = (context, token) => Task.CompletedTask;
|
||||
|
||||
_builder.Object.WithSubscribeToResourcesHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.SubscribeToResourcesHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithUnsubscribeFromResourcesHandler_Sets_Handler()
|
||||
{
|
||||
Func<RequestContext<string>, CancellationToken, Task> handler = (context, token) => Task.CompletedTask;
|
||||
|
||||
_builder.Object.WithUnsubscribeFromResourcesHandler(handler);
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.Equal(handler, options.UnsubscribeFromResourcesHandler);
|
||||
}
|
||||
}
|
||||
163
tests/mcpdotnet.Tests/McpServerBuilderExtensionsToolsTests.cs
Normal file
163
tests/mcpdotnet.Tests/McpServerBuilderExtensionsToolsTests.cs
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
using McpDotNet.Configuration;
|
||||
using McpDotNet.Server;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
|
||||
namespace McpDotNet.Tests;
|
||||
|
||||
public class McpServerBuilderExtensionsToolsTests
|
||||
{
|
||||
private readonly Mock<IMcpServerBuilder> _builder;
|
||||
private readonly ServiceCollection _services;
|
||||
|
||||
public McpServerBuilderExtensionsToolsTests()
|
||||
{
|
||||
_services = new ServiceCollection();
|
||||
_builder = new Mock<IMcpServerBuilder>();
|
||||
_builder.SetupGet(b => b.Services).Returns(_services);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Adds_Tools_To_Server()
|
||||
{
|
||||
McpServerBuilderExtensions.WithTools(_builder.Object, typeof(EchoTool));
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
Assert.NotNull(options.ListToolsHandler);
|
||||
Assert.NotNull(options.CallToolHandler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Can_List_Registered_Tool()
|
||||
{
|
||||
McpServerBuilderExtensions.WithTools(_builder.Object, typeof(EchoTool));
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
var result = await options.ListToolsHandler!(new(Mock.Of<IMcpServer>(), new()), CancellationToken.None);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotEmpty(result.Tools);
|
||||
|
||||
var tool = result.Tools[0];
|
||||
Assert.Equal("Echo", tool.Name);
|
||||
Assert.Equal("Echoes the input back to the client.", tool.Description);
|
||||
Assert.NotNull(tool.InputSchema);
|
||||
Assert.Equal("object", tool.InputSchema.Type);
|
||||
Assert.NotNull(tool.InputSchema.Properties);
|
||||
Assert.NotEmpty(tool.InputSchema.Properties);
|
||||
Assert.Contains("message", tool.InputSchema.Properties);
|
||||
Assert.Equal("string", tool.InputSchema.Properties["message"].Type);
|
||||
Assert.Equal("the echoes message", tool.InputSchema.Properties["message"].Description);
|
||||
Assert.NotNull(tool.InputSchema.Required);
|
||||
Assert.NotEmpty(tool.InputSchema.Required);
|
||||
Assert.Contains("message", tool.InputSchema.Required);
|
||||
|
||||
tool = result.Tools[1];
|
||||
Assert.Equal("double_echo", tool.Name);
|
||||
Assert.Equal("Echoes the input back to the client.", tool.Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Can_Call_Registered_Tool()
|
||||
{
|
||||
McpServerBuilderExtensions.WithTools(_builder.Object, typeof(EchoTool));
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
var result = await options.CallToolHandler!(new(Mock.Of<IMcpServer>(), new() { Name = "Echo", Arguments = new() { { "message", "Peter" } } }), CancellationToken.None);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(result.Content);
|
||||
Assert.NotEmpty(result.Content);
|
||||
|
||||
Assert.Equal("hello Peter", result.Content[0].Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Can_Call_Registered_Tool_With_Dependency_Injection()
|
||||
{
|
||||
bool dependencyInjectionCalled = false;
|
||||
_services.AddTransient<IDependendService>(sp =>
|
||||
{
|
||||
dependencyInjectionCalled = true;
|
||||
return Mock.Of<IDependendService>();
|
||||
});
|
||||
|
||||
McpServerBuilderExtensions.WithTools(_builder.Object, typeof(EchoToolWithDi));
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
var mcpServer = new Mock<IMcpServer>();
|
||||
mcpServer.SetupGet(s => s.ServiceProvider).Returns(serviceProvider);
|
||||
|
||||
var result = await options.CallToolHandler!(new(mcpServer.Object, new() { Name = "Echo", Arguments = new() { { "message", "Peter" } } }), CancellationToken.None);
|
||||
Assert.NotNull(result);
|
||||
Assert.NotNull(result.Content);
|
||||
Assert.NotEmpty(result.Content);
|
||||
|
||||
Assert.Equal("hello Peter", result.Content[0].Text);
|
||||
|
||||
Assert.True(dependencyInjectionCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Throws_Exception_On_Unknown_Tool()
|
||||
{
|
||||
McpServerBuilderExtensions.WithTools(_builder.Object, typeof(EchoTool));
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
var exception = await Assert.ThrowsAsync<McpServerException>(async () => await options.CallToolHandler!(new(Mock.Of<IMcpServer>(), new() { Name = "NotRegisteredTool" }), CancellationToken.None));
|
||||
Assert.Equal("Unknown tool: NotRegisteredTool", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Throws_Exception_Missing_Parameter()
|
||||
{
|
||||
McpServerBuilderExtensions.WithTools(_builder.Object, typeof(EchoTool));
|
||||
|
||||
var serviceProvider = _services.BuildServiceProvider();
|
||||
var options = serviceProvider.GetRequiredService<IOptions<McpServerDelegates>>().Value;
|
||||
|
||||
var exception = await Assert.ThrowsAsync<McpServerException>(async () => await options.CallToolHandler!(new(Mock.Of<IMcpServer>(), new() { Name = "Echo" }), CancellationToken.None));
|
||||
Assert.Equal("Missing required argument 'message'.", exception.Message);
|
||||
}
|
||||
|
||||
private static class EchoTool
|
||||
{
|
||||
[McpTool(Description = "Echoes the input back to the client.")]
|
||||
public static string Echo([McpParameter(true, Description = "the echoes message")] string message)
|
||||
{
|
||||
return "hello " + message;
|
||||
}
|
||||
|
||||
[McpTool(Name = "double_echo", Description = "Echoes the input back to the client.")]
|
||||
public static string Echo2([McpParameter(true)] string message)
|
||||
{
|
||||
return "hello hello" + message;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IDependendService
|
||||
{
|
||||
}
|
||||
|
||||
private class EchoToolWithDi
|
||||
{
|
||||
public EchoToolWithDi(IDependendService service)
|
||||
{
|
||||
}
|
||||
|
||||
[McpTool(Description = "Echoes the input back to the client.")]
|
||||
public Task<string> Echo([McpParameter(true)] string message)
|
||||
{
|
||||
return Task.FromResult("hello " + message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
using McpDotNet.Configuration;
|
||||
using McpDotNet.Protocol.Transport;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
|
||||
namespace McpDotNet.Tests;
|
||||
|
||||
public class McpServerBuilderExtensionsTransportsTests
|
||||
{
|
||||
[Fact]
|
||||
public void WithStdioServerTransport_Sets_Transport()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
var builder = new Mock<IMcpServerBuilder>();
|
||||
builder.SetupGet(b => b.Services).Returns(services);
|
||||
|
||||
builder.Object.WithStdioServerTransport();
|
||||
|
||||
var transportType = services.FirstOrDefault(s => s.ServiceType == typeof(IServerTransport));
|
||||
Assert.NotNull(transportType);
|
||||
Assert.Equal(typeof(StdioServerTransport), transportType.ImplementationType);
|
||||
}
|
||||
}
|
||||
|
|
@ -31,11 +31,14 @@ public class ServerIntegrationTestFixture : IDisposable
|
|||
TransportType = TransportTypes.StdIo,
|
||||
TransportOptions = new Dictionary<string, string>
|
||||
{
|
||||
["command"] = "TestServer.exe",
|
||||
["command"] = OperatingSystem.IsWindows() ? "TestServer.exe" : "dotnet",
|
||||
// Change to ["arguments"] = "mcp-server-everything" if you want to run the server locally after creating a symlink
|
||||
}
|
||||
};
|
||||
|
||||
if (!OperatingSystem.IsWindows())
|
||||
DefaultConfig.TransportOptions["arguments"] = "TestServer.dll";
|
||||
|
||||
// Inject the mock transport into the factory
|
||||
Factory = new McpClientFactory(
|
||||
[DefaultConfig],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
using McpDotNet.Client;
|
||||
using McpDotNet.Configuration;
|
||||
using McpDotNet.Protocol.Messages;
|
||||
using McpDotNet.Protocol.Types;
|
||||
|
||||
namespace McpDotNet.Tests;
|
||||
|
|
@ -18,7 +16,7 @@ public class ServerIntegrationTests : IClassFixture<ServerIntegrationTestFixture
|
|||
public async Task ConnectAndPing_Stdio_TestServer()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
|
||||
// Act
|
||||
var client = await _fixture.Factory.GetClientAsync("test_server");
|
||||
await client.PingAsync(CancellationToken.None);
|
||||
|
|
@ -45,7 +43,7 @@ public class ServerIntegrationTests : IClassFixture<ServerIntegrationTestFixture
|
|||
public async Task ListTools_Stdio_TestServer()
|
||||
{
|
||||
// arrange
|
||||
|
||||
|
||||
// act
|
||||
var client = await _fixture.Factory.GetClientAsync("test_server");
|
||||
var tools = await client.ListToolsAsync();
|
||||
|
|
@ -59,7 +57,7 @@ public class ServerIntegrationTests : IClassFixture<ServerIntegrationTestFixture
|
|||
public async Task CallTool_Stdio_EchoServer()
|
||||
{
|
||||
// arrange
|
||||
|
||||
|
||||
// act
|
||||
var client = await _fixture.Factory.GetClientAsync("test_server");
|
||||
var result = await client.CallToolAsync(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
<RootNamespace>McpDotNet.Tests</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -31,7 +32,10 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\mcpdotnet.TestServer\bin\$(Configuration)\net8.0\TestServer.exe">
|
||||
<Content Condition="$([MSBuild]::IsOSPlatform('Windows'))" Include="..\mcpdotnet.TestServer\bin\$(Configuration)\net8.0\TestServer.exe">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Condition="$([MSBuild]::IsOSPlatform('Linux'))" Include="..\mcpdotnet.TestServer\bin\$(Configuration)\net8.0\TestServer.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in a new issue