Skip to content

Commit 1496b41

Browse files
committed
Update with better DevEx
1 parent d511312 commit 1496b41

File tree

6 files changed

+50
-19
lines changed

6 files changed

+50
-19
lines changed

samples/ProtectedMCPClient/BasicOAuthAuthorizationProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public class BasicOAuthAuthorizationProvider(
3535
// Store auth server metadata separately so token only stores token data
3636
private AuthorizationServerMetadata? _authServerMetadata;
3737

38+
public string AuthorizationScheme => "Bearer";
39+
3840
/// <inheritdoc />
3941
public async Task<string?> GetCredentialAsync(Uri resourceUri, CancellationToken cancellationToken = default)
4042
{

samples/ProtectedMCPClient/Program.cs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,7 @@ static async Task Main(string[] args)
1818
clientId: "6ad97b5f-7a7b-413f-8603-7a3517d4adb8",
1919
redirectUri: new Uri("http://localhost:1179/callback"),
2020
scopes: new List<string> { "api://167b4284-3f92-4436-92ed-38b38f83ae08/weather.read" }
21-
);
22-
23-
var authHandler = new AuthorizationDelegatingHandler(tokenProvider, "Bearer")
24-
{
25-
InnerHandler = new HttpClientHandler()
26-
};
27-
28-
var httpClient = new HttpClient(authHandler);
21+
);
2922

3023
Console.WriteLine();
3124
Console.WriteLine($"Connecting to weather server at {serverUrl}...");
@@ -38,7 +31,7 @@ static async Task Main(string[] args)
3831
Name = "Secure Weather Client"
3932
};
4033

41-
var transport = new SseClientTransport(transportOptions, httpClient);
34+
var transport = new SseClientTransport(transportOptions, tokenProvider);
4235

4336
var client = await McpClientFactory.CreateAsync(transport);
4437

src/ModelContextProtocol/Authentication/AuthorizationDelegatingHandler.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,15 @@ namespace ModelContextProtocol.Authentication;
77
/// </summary>
88
public class AuthorizationDelegatingHandler : DelegatingHandler
99
{
10-
private readonly IMcpAuthorizationProvider _tokenProvider;
11-
private readonly string _scheme;
10+
private readonly IMcpAuthorizationProvider _authorizationProvider;
1211

1312
/// <summary>
1413
/// Initializes a new instance of the <see cref="AuthorizationDelegatingHandler"/> class.
1514
/// </summary>
16-
/// <param name="tokenProvider">The provider that supplies authentication tokens.</param>
17-
/// <param name="scheme">The authentication scheme to use, e.g., "Bearer".</param>
18-
public AuthorizationDelegatingHandler(IMcpAuthorizationProvider tokenProvider, string scheme)
15+
/// <param name="authorizationProvider">The provider that supplies authentication tokens.</param>
16+
public AuthorizationDelegatingHandler(IMcpAuthorizationProvider authorizationProvider)
1917
{
20-
_tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider));
21-
_scheme = scheme ?? throw new ArgumentNullException(nameof(scheme));
18+
_authorizationProvider = authorizationProvider ?? throw new ArgumentNullException(nameof(authorizationProvider));
2219
}
2320

2421
/// <summary>
@@ -39,7 +36,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
3936
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
4037
{
4138
// Try to handle the unauthorized response
42-
var handled = await _tokenProvider.HandleUnauthorizedResponseAsync(
39+
var handled = await _authorizationProvider.HandleUnauthorizedResponseAsync(
4340
response,
4441
cancellationToken);
4542

@@ -66,10 +63,10 @@ private async Task AddAuthorizationHeaderAsync(HttpRequestMessage request, Cance
6663
{
6764
if (request.RequestUri != null)
6865
{
69-
var token = await _tokenProvider.GetCredentialAsync(request.RequestUri, cancellationToken);
66+
var token = await _authorizationProvider.GetCredentialAsync(request.RequestUri, cancellationToken);
7067
if (!string.IsNullOrEmpty(token))
7168
{
72-
request.Headers.Authorization = new AuthenticationHeaderValue(_scheme, token);
69+
request.Headers.Authorization = new AuthenticationHeaderValue(_authorizationProvider.AuthorizationScheme, token);
7370
}
7471
}
7572
}

src/ModelContextProtocol/Authentication/ITokenProvider.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ namespace ModelContextProtocol.Authentication;
66
/// </summary>
77
public interface IMcpAuthorizationProvider
88
{
9+
/// <summary>
10+
/// Gets the authentication scheme to use with credentials from this provider.
11+
/// </summary>
12+
/// <remarks>
13+
/// <para>
14+
/// Common values include "Bearer" for JWT tokens and "Basic" for username/password authentication.
15+
/// </para>
16+
/// </remarks>
17+
string AuthorizationScheme { get; }
18+
919
/// <summary>
1020
/// Gets an authentication token or credential for authenticating requests to a resource.
1121
/// </summary>

src/ModelContextProtocol/Protocol/Transport/SseClientTransport.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Microsoft.Extensions.Logging;
2+
using ModelContextProtocol.Authentication;
23
using ModelContextProtocol.Utils;
34

45
namespace ModelContextProtocol.Protocol.Transport;
@@ -51,6 +52,32 @@ public SseClientTransport(SseClientTransportOptions transportOptions, HttpClient
5152
Name = transportOptions.Name ?? transportOptions.Endpoint.ToString();
5253
}
5354

55+
/// <summary>
56+
/// Initializes a new instance of the <see cref="SseClientTransport"/> class with authentication support.
57+
/// </summary>
58+
/// <param name="transportOptions">Configuration options for the transport.</param>
59+
/// <param name="authorizationProvider">The authorization provider to use for authentication.</param>
60+
/// <param name="loggerFactory">Logger factory for creating loggers used for diagnostic output during transport operations.</param>
61+
public SseClientTransport(SseClientTransportOptions transportOptions, IMcpAuthorizationProvider authorizationProvider, ILoggerFactory? loggerFactory = null)
62+
{
63+
Throw.IfNull(transportOptions);
64+
Throw.IfNull(authorizationProvider);
65+
66+
_options = transportOptions;
67+
_loggerFactory = loggerFactory;
68+
Name = transportOptions.Name ?? transportOptions.Endpoint.ToString();
69+
70+
// Create an auth handler with the authorization provider
71+
var authHandler = new AuthorizationDelegatingHandler(authorizationProvider)
72+
{
73+
InnerHandler = new HttpClientHandler()
74+
};
75+
76+
// Create an HttpClient with the auth handler
77+
_httpClient = new HttpClient(authHandler);
78+
_ownsHttpClient = true;
79+
}
80+
5481
/// <inheritdoc />
5582
public string Name { get; }
5683

src/ModelContextProtocol/Protocol/Transport/SseClientTransportOptions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
namespace ModelContextProtocol.Protocol.Transport;
22

3+
using ModelContextProtocol.Authentication;
4+
35
/// <summary>
46
/// Provides options for configuring <see cref="SseClientTransport"/> instances.
57
/// </summary>

0 commit comments

Comments
 (0)