Wrap each request in a service scope (#276)
* Wrap each request in a service scope * Change request/notification delegates to return ValueTask * Address feedback
This commit is contained in:
parent
f1cc2d81de
commit
f4c87f5e2f
1 changed files with 477 additions and 306 deletions
|
|
@ -0,0 +1,75 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ModelContextProtocol.Client;
|
||||
using ModelContextProtocol.Server;
|
||||
using ModelContextProtocol.Utils.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ModelContextProtocol.Tests.Configuration;
|
||||
|
||||
public partial class McpServerScopedTests : ClientServerTestBase
|
||||
{
|
||||
public McpServerScopedTests(ITestOutputHelper testOutputHelper)
|
||||
: base(testOutputHelper)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder)
|
||||
{
|
||||
mcpServerBuilder.WithTools<EchoTool>(serializerOptions: McpServerScopedTestsJsonContext.Default.Options);
|
||||
services.AddScoped(_ => new ComplexObject() { Name = "Scoped" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task InjectScopedServiceAsArgument()
|
||||
{
|
||||
IMcpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var tools = await client.ListToolsAsync(McpServerScopedTestsJsonContext.Default.Options, TestContext.Current.CancellationToken);
|
||||
var tool = tools.First(t => t.Name == nameof(EchoTool.EchoComplex));
|
||||
Assert.DoesNotContain("\"complex\"", JsonSerializer.Serialize(tool.JsonSchema, McpJsonUtilities.DefaultOptions));
|
||||
|
||||
int startingConstructed = ComplexObject.Constructed;
|
||||
int startingDisposed = ComplexObject.Disposed;
|
||||
|
||||
for (int i = 1; i <= 10; i++)
|
||||
{
|
||||
Assert.Contains("\"Scoped\"", JsonSerializer.Serialize(await tool.InvokeAsync(cancellationToken: TestContext.Current.CancellationToken), McpJsonUtilities.DefaultOptions));
|
||||
|
||||
Assert.Equal(startingConstructed + i, ComplexObject.Constructed);
|
||||
Assert.Equal(startingDisposed + i, ComplexObject.Disposed);
|
||||
}
|
||||
}
|
||||
|
||||
[McpServerToolType]
|
||||
public sealed class EchoTool()
|
||||
{
|
||||
[McpServerTool]
|
||||
public static string EchoComplex(ComplexObject complex) => complex.Name!;
|
||||
}
|
||||
|
||||
public class ComplexObject : IAsyncDisposable
|
||||
{
|
||||
public static int Constructed;
|
||||
public static int Disposed;
|
||||
|
||||
public ComplexObject()
|
||||
{
|
||||
Interlocked.Increment(ref Constructed);
|
||||
}
|
||||
|
||||
public ValueTask DisposeAsync()
|
||||
{
|
||||
Interlocked.Increment(ref Disposed);
|
||||
return default;
|
||||
}
|
||||
|
||||
public string? Name { get; set; }
|
||||
public int Age { get; set; }
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(string))]
|
||||
[JsonSerializable(typeof(ComplexObject))]
|
||||
[JsonSerializable(typeof(JsonElement))]
|
||||
partial class McpServerScopedTestsJsonContext : JsonSerializerContext;
|
||||
}
|
||||
Loading…
Reference in a new issue