Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 3 additions & 3 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
<PackageVersion Include="Microsoft.Azure.Kusto.Ingest" Version="14.0.3" />
<PackageVersion Include="Microsoft.CognitiveServices.Speech" Version="1.46.0" />
<PackageVersion Include="Microsoft.Identity.Client.Broker" Version="4.72.1" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.3.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Abstractions" Version="10.7.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Evaluation" Version="10.2.0" />
<PackageVersion Include="Microsoft.Extensions.AI.Evaluation.Quality" Version="10.2.0" />
<PackageVersion Include="Microsoft.Extensions.AI.OpenAI" Version="10.2.0-preview.1.26063.2" />
Expand All @@ -87,8 +87,8 @@
<PackageVersion Include="Microsoft.DataFactory.MCP.Core" Version="0.21.0-beta" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
<PackageVersion Include="ModelContextProtocol" Version="1.1.0" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="1.1.0" />
<PackageVersion Include="ModelContextProtocol" Version="1.4.0" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="1.4.0" />
<PackageVersion Include="MySqlConnector" Version="2.4.0" />
<PackageVersion Include="Npgsql" Version="10.0.1" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,15 @@ private static IOptions<ServiceStartOptions> CreateOptions(ServiceStartOptions?
private static ITelemetryService CreateMockTelemetryService() => Substitute.For<ITelemetryService>();

private static RequestContext<ListToolsRequestParams> CreateListToolsRequest() =>
new(CreateMockServer(), new() { Method = RequestMethods.ToolsList })
{
Params = new()
};
new(CreateMockServer(), new() { Method = RequestMethods.ToolsList }, new());

private static RequestContext<CallToolRequestParams> CreateCallToolRequest(
string toolName = "test-tool",
IDictionary<string, JsonElement>? arguments = null) => new(CreateMockServer(), new() { Method = RequestMethods.ToolsCall })
IDictionary<string, JsonElement>? arguments = null) => new(CreateMockServer(), new() { Method = RequestMethods.ToolsCall }, new()
{
Params = new()
{
Name = toolName,
Arguments = arguments ?? new Dictionary<string, JsonElement>()
}
};
Name = toolName,
Arguments = arguments ?? new Dictionary<string, JsonElement>()
});

private static object GetAndAssertTagKeyValue(Activity activity, string tagName)
{
Expand Down Expand Up @@ -171,7 +165,7 @@ public async Task ListToolsHandler_DelegatesToToolLoader()

var mockTelemetry = CreateMockTelemetryService();
var activity = new Activity("test-activity");
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>())
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>(), Arg.Any<RequestParams?>())
.Returns(activity);

var options = CreateOptions();
Expand All @@ -196,7 +190,7 @@ public async Task ListToolsHandler_DelegatesToToolLoader()
Assert.Equal(expectedResult, result);
await mockToolLoader.Received(1).ListToolsHandler(request, Arg.Any<CancellationToken>());

mockTelemetry.Received(1).StartActivity(ActivityName.ListToolsHandler, Arg.Any<Implementation?>());
mockTelemetry.Received(1).StartActivity(ActivityName.ListToolsHandler, Arg.Any<Implementation?>(), Arg.Any<RequestParams?>());
Assert.Equal(ActivityStatusCode.Ok, activity.Status);
}

Expand All @@ -210,7 +204,7 @@ public async Task CallToolHandler_DelegatesToToolLoader()

var mockTelemetry = CreateMockTelemetryService();
var activity = new Activity("test-activity");
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>())
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>(), Arg.Any<RequestParams?>())
.Returns(activity);

var options = CreateOptions();
Expand Down Expand Up @@ -240,7 +234,7 @@ public async Task CallToolHandler_DelegatesToToolLoader()
Assert.Equal(expectedResult, result);
await mockToolLoader.Received(1).CallToolHandler(request, Arg.Any<CancellationToken>());

mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any<Implementation?>());
mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any<Implementation?>(), Arg.Any<RequestParams?>());
Assert.Equal(ActivityStatusCode.Ok, activity.Status);

var actualToolName = GetAndAssertTagKeyValue(activity, TagName.ToolName);
Expand Down Expand Up @@ -316,7 +310,7 @@ public async Task ListToolsHandler_WhenToolLoaderThrows_PropagatesException()

var mockTelemetry = CreateMockTelemetryService();
var activity = new Activity("test-activity");
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>())
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>(), Arg.Any<RequestParams?>())
.Returns(activity);

var options = CreateOptions();
Expand All @@ -334,7 +328,7 @@ public async Task ListToolsHandler_WhenToolLoaderThrows_PropagatesException()

Assert.Equal(expectedException.Message, actualException.Message);

mockTelemetry.Received(1).StartActivity(ActivityName.ListToolsHandler, Arg.Any<Implementation?>());
mockTelemetry.Received(1).StartActivity(ActivityName.ListToolsHandler, Arg.Any<Implementation?>(), Arg.Any<RequestParams?>());
Assert.Equal(ActivityStatusCode.Error, activity.Status);

GetAndAssertTagKeyValue(activity, TagName.ExceptionType);
Expand All @@ -350,7 +344,7 @@ public async Task CallToolHandler_WhenToolLoaderThrows_PropagatesException()

var mockTelemetry = CreateMockTelemetryService();
var activity = new Activity("test-activity");
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>())
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>(), Arg.Any<RequestParams?>())
.Returns(activity);

var options = CreateOptions();
Expand All @@ -370,7 +364,7 @@ public async Task CallToolHandler_WhenToolLoaderThrows_PropagatesException()
runtime.CallToolHandler(request, TestContext.Current.CancellationToken).AsTask());
Assert.Equal(expectedException.Message, actualException.Message);

mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any<Implementation?>());
mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any<Implementation?>(), Arg.Any<RequestParams?>());
Assert.Equal(ActivityStatusCode.Error, activity.Status);

var actualToolName = GetAndAssertTagKeyValue(activity, TagName.ToolName);
Expand Down Expand Up @@ -450,7 +444,7 @@ public async Task ListToolsHandler_WithNullParameters_DelegatesToToolLoader()
var mockToolLoader = Substitute.For<IToolLoader>();
var options = CreateOptions();
var runtime = new McpRuntime(mockToolLoader, options, CreateMockTelemetryService(), logger);
var request = new RequestContext<ListToolsRequestParams>(CreateMockServer(), new() { Method = RequestMethods.ToolsList });
var request = new RequestContext<ListToolsRequestParams>(CreateMockServer(), new() { Method = RequestMethods.ToolsList }, null!);

var expectedResult = new ListToolsResult { Tools = [] };
mockToolLoader.ListToolsHandler(request, Arg.Any<CancellationToken>())
Expand All @@ -475,11 +469,11 @@ public async Task CallToolHandler_WithNullParameters_ReturnsError()

var mockTelemetry = CreateMockTelemetryService();
var activity = new Activity("test-activity");
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>())
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>(), Arg.Any<RequestParams?>())
.Returns(activity);

var runtime = new McpRuntime(mockToolLoader, options, mockTelemetry, logger);
var request = new RequestContext<CallToolRequestParams>(CreateMockServer(), new() { Method = RequestMethods.ToolsCall });
var request = new RequestContext<CallToolRequestParams>(CreateMockServer(), new() { Method = RequestMethods.ToolsCall }, null!);

// Act
var result = await runtime.CallToolHandler(request, TestContext.Current.CancellationToken);
Expand All @@ -497,7 +491,7 @@ public async Task CallToolHandler_WithNullParameters_ReturnsError()
// Verify that the tool loader was NOT called since the null request is handled at the runtime level
await mockToolLoader.DidNotReceive().CallToolHandler(Arg.Any<RequestContext<CallToolRequestParams>>(), Arg.Any<CancellationToken>());

mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any<Implementation?>());
mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any<Implementation?>(), Arg.Any<RequestParams?>());
Assert.Equal(ActivityStatusCode.Error, activity.Status);
GetAndAssertTagKeyValue(activity, TagName.ExceptionType);
}
Expand Down Expand Up @@ -644,7 +638,7 @@ public async Task CallToolHandler_SetsActivityTags()

var activity = new Activity("test");
var mockTelemetry = CreateMockTelemetryService();
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>()).Returns(activity);
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>(), Arg.Any<RequestParams?>()).Returns(activity);

var options = CreateOptions();
var runtime = new McpRuntime(mockToolLoader, options, mockTelemetry, logger);
Expand Down Expand Up @@ -770,7 +764,7 @@ public async Task CallToolHandler_WithToolLoaderError_ShouldReturnErrorAndSetTel

var mockTelemetry = CreateMockTelemetryService();
var activity = new Activity("test-activity");
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>())
mockTelemetry.StartActivity(Arg.Any<string>(), Arg.Any<Implementation?>(), Arg.Any<RequestParams?>())
.Returns(activity);

var options = CreateOptions();
Expand Down Expand Up @@ -798,7 +792,7 @@ public async Task CallToolHandler_WithToolLoaderError_ShouldReturnErrorAndSetTel
// Act
var result = await runtime.CallToolHandler(request, TestContext.Current.CancellationToken);

mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any<Implementation?>());
mockTelemetry.Received(1).StartActivity(ActivityName.ToolExecuted, Arg.Any<Implementation?>(), Arg.Any<RequestParams?>());
Assert.Equal(ActivityStatusCode.Error, activity.Status);
GetAndAssertTagKeyValue(activity, TagName.ExceptionType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ public async Task HandleSecretElicitation_WhenElicitationDisabled_ProceedsWithou
Method = "tools/call",
Params = JsonSerializer.SerializeToNode(new CallToolRequestParams { Name = "test-tool" })
};
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest);
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest, null!);
var logger = Substitute.For<ILogger>();

// Act
Expand Down Expand Up @@ -342,7 +342,7 @@ public async Task HandleSecretElicitation_WhenClientDoesNotSupportElicitation_Re
Method = "tools/call",
Params = JsonSerializer.SerializeToNode(new CallToolRequestParams { Name = "test-tool" })
};
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest);
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest, null!);
var logger = Substitute.For<ILogger>();

// Act
Expand Down Expand Up @@ -374,7 +374,7 @@ public async Task HandleSecretElicitation_WhenUserAccepts_ProceedsWithOperation(
Method = "tools/call",
Params = JsonSerializer.SerializeToNode(new CallToolRequestParams { Name = "test-tool" })
};
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest);
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest, null!);
var logger = Substitute.For<ILogger>();

// Act
Expand Down Expand Up @@ -407,7 +407,7 @@ public async Task HandleSecretElicitation_WhenUserDeclines_RejectsOperation()
Method = "tools/call",
Params = JsonSerializer.SerializeToNode(new CallToolRequestParams { Name = "test-tool" })
};
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest);
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest, null!);
var logger = Substitute.For<ILogger>();

// Act
Expand Down Expand Up @@ -446,7 +446,7 @@ public async Task HandleSecretElicitation_UsesDecisionEnumSchema()
Method = "tools/call",
Params = JsonSerializer.SerializeToNode(new CallToolRequestParams { Name = "test-tool" })
};
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest);
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest, null!);
var logger = Substitute.For<ILogger>();

// Act
Expand Down Expand Up @@ -490,7 +490,7 @@ public async Task HandleSecretElicitation_WhenExceptionOccurs_ReturnsErrorResult
Method = "tools/call",
Params = JsonSerializer.SerializeToNode(new CallToolRequestParams { Name = "test-tool" })
};
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest);
var request = new RequestContext<CallToolRequestParams>(mockServer, jsonRpcRequest, null!);
var logger = Substitute.For<ILogger>();

// Act
Expand All @@ -503,17 +503,9 @@ public async Task HandleSecretElicitation_WhenExceptionOccurs_ReturnsErrorResult
Assert.Contains("Elicitation failed", ((TextContentBlock)result.Content[0]).Text);
}

internal sealed class TestableBaseToolLoader : BaseToolLoader
internal sealed class TestableBaseToolLoader(ILogger logger) : BaseToolLoader(logger)
{
public TestableBaseToolLoader(ILogger logger)
: base(logger)
{
}

public McpClientOptions CreateClientOptionsPublic(McpServer server)
{
return CreateClientOptions(server);
}
public McpClientOptions CreateClientOptionsPublic(McpServer server) => CreateClientOptions(server);

public static Task<CallToolResult?> HandleElicitationAsyncPublic(
RequestContext<CallToolRequestParams> request,
Expand Down
Loading