Skip to content

Commit bc00585

Browse files
committed
Respect DI while creating target object
1 parent e1a2564 commit bc00585

File tree

7 files changed

+85
-9
lines changed

7 files changed

+85
-9
lines changed

Directory.Packages.props

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
<MicrosoftExtensionsAIVersion>9.7.1</MicrosoftExtensionsAIVersion>
77
</PropertyGroup>
88

9+
<!-- Product dependencies .NET Framework 4.7.2 -->
10+
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
11+
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.1" />
12+
</ItemGroup>
13+
914
<!-- Product dependencies netstandard -->
1015
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
1116
<PackageVersion Include="Microsoft.Bcl.Memory" Version="$(System9Version)" />
@@ -20,6 +25,7 @@
2025
<!-- Product dependencies LTS -->
2126
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
2227
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.15" />
28+
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.1" />
2329
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
2430
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
2531
<PackageVersion Include="System.IO.Pipelines" Version="8.0.0" />
@@ -29,6 +35,7 @@
2935
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
3036
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(System9Version)" />
3137
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.9.0" />
38+
<PackageVersion Include="Microsoft.Extensions.Http" Version="$(System9Version)" />
3239
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(System9Version)" />
3340
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="$(System9Version)" />
3441
<PackageVersion Include="System.IO.Pipelines" Version="$(System9Version)" />

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerPrompt.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
7676
ConfigureParameterBinding = pi =>
7777
{
7878
if (RequestServiceProvider<GetPromptRequestParams>.IsAugmentedWith(pi.ParameterType) ||
79-
(options?.Services?.GetService<IServiceProviderIsService>() is { } ispis &&
80-
ispis.IsService(pi.ParameterType)))
79+
(options?.Services?.GetService<IServiceProviderIsService>() is { } serviceProviderIsService &&
80+
serviceProviderIsService.IsService(pi.ParameterType)))
8181
{
8282
return new()
8383
{

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerResource.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
8383
ConfigureParameterBinding = pi =>
8484
{
8585
if (RequestServiceProvider<ReadResourceRequestParams>.IsAugmentedWith(pi.ParameterType) ||
86-
(options?.Services?.GetService<IServiceProviderIsService>() is { } ispis &&
87-
ispis.IsService(pi.ParameterType)))
86+
(options?.Services?.GetService<IServiceProviderIsService>() is { } serviceProviderIsService &&
87+
serviceProviderIsService.IsService(pi.ParameterType)))
8888
{
8989
return new()
9090
{

src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ private static AIFunctionFactoryOptions CreateAIFunctionFactoryOptions(
8383
ConfigureParameterBinding = pi =>
8484
{
8585
if (RequestServiceProvider<CallToolRequestParams>.IsAugmentedWith(pi.ParameterType) ||
86-
(options?.Services?.GetService<IServiceProviderIsService>() is { } ispis &&
87-
ispis.IsService(pi.ParameterType)))
86+
(options?.Services?.GetService<IServiceProviderIsService>() is { } serviceProviderIsService &&
87+
serviceProviderIsService.IsService(pi.ParameterType)))
8888
{
8989
return new()
9090
{

src/ModelContextProtocol/McpServerBuilderExtensions.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -783,8 +783,18 @@ private static void AddSingleSessionServerDependencies(IServiceCollection servic
783783
/// <summary>Creates an instance of the target object.</summary>
784784
private static object CreateTarget(
785785
IServiceProvider? services,
786-
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type) =>
787-
services is not null ? ActivatorUtilities.CreateInstance(services, type) :
788-
Activator.CreateInstance(type)!;
786+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
787+
{
788+
if (services is null)
789+
{
790+
return Activator.CreateInstance(type)!;
791+
}
792+
793+
var isService = services.GetService<IServiceProviderIsService>()?.IsService(type) == true;
794+
795+
return isService
796+
? services.GetRequiredService(type)
797+
: ActivatorUtilities.CreateInstance(services, type);
798+
}
789799
#endregion
790800
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using ModelContextProtocol.Client;
3+
using ModelContextProtocol.Protocol;
4+
using ModelContextProtocol.Server;
5+
using System.ComponentModel;
6+
7+
namespace ModelContextProtocol.Tests.Configuration;
8+
9+
public class McpServerBuilderExtensionsCreateTargetHelperTests(ITestOutputHelper testOutputHelper)
10+
: ClientServerTestBase(testOutputHelper)
11+
{
12+
private const string ToolName = "Pets Service";
13+
private const string BaseAddress = "https://localhost:7387/pets";
14+
15+
protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder)
16+
{
17+
mcpServerBuilder.WithTools<PetsService>();
18+
19+
services.AddHttpClient<PetsService>(client =>
20+
{
21+
client.BaseAddress = new Uri(BaseAddress);
22+
});
23+
}
24+
25+
/// <summary>
26+
/// Verifies that a typed HttpClient registered in DI is correctly injected into a server tool
27+
/// and used by the tool implementation. This test covers the scenario described in
28+
/// https://github.com/modelcontextprotocol/csharp-sdk/issues/685.
29+
/// </summary>
30+
[Fact]
31+
public async Task Typed_HttpClient_Is_Used_By_Tool()
32+
{
33+
// Arrange
34+
await using var client = await CreateMcpClientForServer();
35+
36+
// Act
37+
var result = await client.CallToolAsync(ToolName, cancellationToken: TestContext.Current.CancellationToken);
38+
39+
// Assert
40+
Assert.NotNull(result.Content);
41+
Assert.NotEmpty(result.Content);
42+
43+
var text = (result.Content[0] as TextContentBlock)?.Text;
44+
Assert.Equal(BaseAddress, text);
45+
}
46+
47+
[McpServerToolType]
48+
private sealed class PetsService(HttpClient httpClient)
49+
{
50+
[McpServerTool(Name = ToolName)]
51+
[Description("List all pets")]
52+
public string GetBaseAddress()
53+
{
54+
// Returning HttpClient.BaseAddress for verification
55+
return httpClient.BaseAddress?.ToString() ?? string.Empty;
56+
}
57+
}
58+
}

tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
</PackageReference>
4444
<PackageReference Include="Microsoft.Extensions.AI" />
4545
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
46+
<PackageReference Include="Microsoft.Extensions.Http" />
4647
<PackageReference Include="Microsoft.Extensions.Logging" />
4748
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
4849
<PackageReference Include="Microsoft.NET.Test.Sdk" />

0 commit comments

Comments
 (0)