Skip to content

Commit 94c39bc

Browse files
committed
No direct inclusion of HTTP client factory
1 parent e8f7012 commit 94c39bc

4 files changed

Lines changed: 25 additions & 49 deletions

File tree

Directory.Packages.props

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
1010
<PackageVersion Include="Microsoft.Bcl.Memory" Version="9.0.4" />
1111
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
12-
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
1312
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
1413
<PackageVersion Include="System.IO.Pipelines" Version="8.0.0" />
1514
<PackageVersion Include="System.Text.Json" Version="8.0.5" />
@@ -19,15 +18,13 @@
1918
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
2019
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.15" />
2120
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
22-
<PackageVersion Include="Microsoft.Extensions.Http" Version="8.0.0" />
2321
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.3" />
2422
<PackageVersion Include="System.IO.Pipelines" Version="8.0.0" />
2523
</ItemGroup>
2624
<!-- Product dependencies .NET 9 -->
2725
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
2826
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.4" />
2927
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
30-
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.4" />
3128
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.4" />
3229
<PackageVersion Include="Microsoft.IdentityModel.Tokens" Version="8.9.0" />
3330
<PackageVersion Include="System.IO.Pipelines" Version="9.0.4" />

samples/ProtectedMCPClient/BasicOAuthAuthorizationProvider.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ public class BasicOAuthAuthorizationProvider : ITokenProvider
2424
private readonly HttpClient _httpClient;
2525
private readonly AuthorizationHelpers _authorizationHelpers;
2626

27-
// Client name for IHttpClientFactory used by the BasicOAuthAuthorizationProvider
28-
public const string HttpClientName = "ProtectedMCPClient.OAuth";
27+
// Lazy-initialized shared HttpClient for when no client is provided
28+
private static readonly Lazy<HttpClient> _defaultHttpClient = new(() => new HttpClient());
2929

3030
private TokenContainer? _token;
3131
private AuthorizationServerMetadata? _authServerMetadata;
@@ -34,27 +34,24 @@ public class BasicOAuthAuthorizationProvider : ITokenProvider
3434
/// Initializes a new instance of the <see cref="BasicOAuthAuthorizationProvider"/> class.
3535
/// </summary>
3636
/// <param name="serverUrl">The MCP server URL.</param>
37-
/// <param name="httpClientFactory">The HTTP client factory to use for creating HTTP clients.</param>
37+
/// <param name="httpClient">The HTTP client to use for OAuth requests. If null, a default HttpClient will be used.</param>
3838
/// <param name="authorizationHelpers">The authorization helpers.</param>
3939
/// <param name="clientId">OAuth client ID.</param>
4040
/// <param name="clientSecret">OAuth client secret.</param>
4141
/// <param name="redirectUri">OAuth redirect URI.</param>
4242
/// <param name="scopes">OAuth scopes.</param>
4343
public BasicOAuthAuthorizationProvider(
4444
Uri serverUrl,
45-
IHttpClientFactory httpClientFactory,
46-
AuthorizationHelpers authorizationHelpers,
45+
HttpClient? httpClient,
46+
AuthorizationHelpers? authorizationHelpers,
4747
string clientId = "demo-client",
4848
string clientSecret = "",
4949
Uri? redirectUri = null,
5050
IEnumerable<string>? scopes = null)
5151
{
5252
_serverUrl = serverUrl ?? throw new ArgumentNullException(nameof(serverUrl));
53-
if (httpClientFactory == null) throw new ArgumentNullException(nameof(httpClientFactory));
54-
_authorizationHelpers = authorizationHelpers ?? throw new ArgumentNullException(nameof(authorizationHelpers));
55-
56-
// Get the HttpClient once during construction instead of for each request
57-
_httpClient = httpClientFactory.CreateClient(HttpClientName);
53+
_httpClient = httpClient ?? _defaultHttpClient.Value;
54+
_authorizationHelpers = authorizationHelpers ?? new AuthorizationHelpers(_httpClient);
5855

5956
_redirectUri = redirectUri ?? new Uri("http://localhost:8080/callback");
6057
_scopes = scopes?.ToList() ?? [];

samples/ProtectedMCPClient/Program.cs

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,21 @@ static async Task Main(string[] args)
1414

1515
var serverUrl = "http://localhost:7071/sse";
1616

17-
var services = new ServiceCollection();
18-
services.AddHttpClient();
19-
17+
// We can customize a shared HttpClient with a custom handler if desired
2018
var sharedHandler = new SocketsHttpHandler
2119
{
2220
PooledConnectionLifetime = TimeSpan.FromMinutes(2),
2321
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1)
2422
};
2523

26-
services.AddHttpClient(BasicOAuthAuthorizationProvider.HttpClientName)
27-
.ConfigurePrimaryHttpMessageHandler(() => sharedHandler);
28-
29-
services.AddHttpClient(AuthorizationHelpers.HttpClientName)
30-
.ConfigurePrimaryHttpMessageHandler(() => sharedHandler);
31-
32-
services.AddTransient<AuthorizationHelpers>();
24+
var httpClient = new HttpClient(sharedHandler);
3325

34-
var serviceProvider = services.BuildServiceProvider();
35-
36-
var httpClientFactory = serviceProvider.GetRequiredService<IHttpClientFactory>();
37-
var authorizationHelpers = serviceProvider.GetRequiredService<AuthorizationHelpers>();
38-
39-
// Create the token provider with proper dependencies
26+
// Create the token provider with our custom HttpClient,
27+
// letting the AuthorizationHelpers be created automatically
4028
var tokenProvider = new BasicOAuthAuthorizationProvider(
4129
new Uri(serverUrl),
42-
httpClientFactory,
43-
authorizationHelpers,
30+
httpClient,
31+
null, // AuthorizationHelpers will be created automatically
4432
clientId: "6ad97b5f-7a7b-413f-8603-7a3517d4adb8",
4533
redirectUri: new Uri("http://localhost:1179/callback"),
4634
scopes: ["api://167b4284-3f92-4436-92ed-38b38f83ae08/weather.read"]
@@ -57,7 +45,11 @@ static async Task Main(string[] args)
5745
Name = "Secure Weather Client"
5846
};
5947

60-
var transport = new SseClientTransport(transportOptions, tokenProvider);
48+
// Create a transport with authentication support using the correct constructor parameters
49+
var transport = new SseClientTransport(
50+
transportOptions,
51+
tokenProvider
52+
);
6153
var client = await McpClientFactory.CreateAsync(transport);
6254

6355
var tools = await client.ListToolsAsync();

src/ModelContextProtocol/Authentication/AuthorizationHelpers.cs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,15 @@ namespace ModelContextProtocol.Authentication;
1010
public class AuthorizationHelpers
1111
{
1212
private readonly HttpClient _httpClient;
13-
14-
/// <summary>
15-
/// Client name for IHttpClientFactory used by the AuthorizationHelpers.
16-
/// </summary>
17-
public const string HttpClientName = "ModelContextProtocol.Authentication";
13+
private static readonly Lazy<HttpClient> _defaultHttpClient = new(() => new HttpClient());
1814

1915
/// <summary>
2016
/// Initializes a new instance of the <see cref="AuthorizationHelpers"/> class.
2117
/// </summary>
22-
/// <param name="httpClientFactory">The HTTP client factory to use for creating HTTP clients.</param>
23-
public AuthorizationHelpers(IHttpClientFactory httpClientFactory)
18+
/// <param name="httpClient">The HTTP client to use for requests. If null, a default HttpClient will be used.</param>
19+
public AuthorizationHelpers(HttpClient? httpClient = null)
2420
{
25-
Throw.IfNull(httpClientFactory);
26-
_httpClient = httpClientFactory.CreateClient(HttpClientName);
21+
_httpClient = httpClient ?? _defaultHttpClient.Value;
2722
}
2823

2924
/// <summary>
@@ -155,7 +150,7 @@ public async Task<ProtectedResourceMetadata> ExtractProtectedResourceMetadata(
155150
var parts = p.Split(['='], 2);
156151
if (parts.Length != 2)
157152
{
158-
return new KeyValuePair<string, string>(string.Empty, string.Empty);
153+
return new KeyValuePair<string, string?>(string.Empty, null);
159154
}
160155

161156
var key = parts[0].Trim();
@@ -167,16 +162,11 @@ public async Task<ProtectedResourceMetadata> ExtractProtectedResourceMetadata(
167162
value = value.Substring(1, value.Length - 2);
168163
}
169164

170-
return new KeyValuePair<string, string>(key, value);
165+
return new KeyValuePair<string, string?>(key, value);
171166
})
172167
.Where(kvp => !string.IsNullOrEmpty(kvp.Key))
173168
.ToDictionary();
174169

175-
if (paramDict.TryGetValue(parameterName, out var value))
176-
{
177-
return value;
178-
}
179-
180-
return null;
170+
return paramDict.TryGetValue(parameterName, out var value) ? value : null;
181171
}
182172
}

0 commit comments

Comments
 (0)