Skip to content

Commit fbb1817

Browse files
committed
Simplify tool configuration
1 parent 7903c75 commit fbb1817

1 file changed

Lines changed: 22 additions & 68 deletions

File tree

Lines changed: 22 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
using Microsoft.AspNetCore.Authentication;
22
using Microsoft.Extensions.Options;
33
using ModelContextProtocol.AspNetCore.Auth;
4-
using ModelContextProtocol.Protocol.Types;
4+
using ProtectedMCPServer.Tools;
5+
using System.Net.Http.Headers;
56
using System.Security.Claims;
67
using System.Text.Encodings.Web;
78

89
var builder = WebApplication.CreateBuilder(args);
910

1011
// Configure authentication to use MCP for challenges
11-
builder.Services.AddAuthentication(options =>
12+
builder.Services.AddAuthentication(options =>
1213
{
1314
options.DefaultScheme = "Bearer";
1415
options.DefaultChallengeScheme = McpAuthenticationDefaults.AuthenticationScheme; // Use MCP for challenges
1516
})
1617
.AddScheme<AuthenticationSchemeOptions, SimpleAuthHandler>("Bearer", options => { })
17-
.AddMcp(options => {
18+
.AddMcp(options =>
19+
{
1820
options.ResourceMetadata.AuthorizationServers.Add(new Uri("https://login.microsoftonline.com/a2213e1c-e51e-4304-9a0d-effe57f31655/v2.0"));
1921
options.ResourceMetadata.BearerMethodsSupported.Add("header");
2022
options.ResourceMetadata.ScopesSupported.AddRange(["weather.read", "weather.write"]);
@@ -34,65 +36,17 @@
3436
builder.Services.AddSingleton<ResourceMetadataService>();
3537

3638
// Configure MCP Server
37-
builder.Services.AddMcpServer(options =>
38-
{
39-
options.ServerInstructions = "This is an MCP server with OAuth authorization enabled.";
40-
41-
// Configure regular server capabilities like tools, prompts, resources
42-
options.Capabilities = new()
43-
{
44-
Tools = new()
45-
{
46-
// Simple Echo tool
47-
CallToolHandler = (request, cancellationToken) =>
48-
{
49-
if (request.Params?.Name == "echo")
50-
{
51-
if (request.Params.Arguments?.TryGetValue("message", out var message) is not true)
52-
{
53-
throw new Exception("It happens.");
54-
}
55-
56-
return new ValueTask<CallToolResponse>(new CallToolResponse()
57-
{
58-
Content = [new Content() { Text = $"Echo: {message}", Type = "text" }]
59-
});
60-
}
61-
62-
// Protected tool that requires authorization
63-
if (request.Params?.Name == "protected-data")
64-
{
65-
// This tool will only be accessible to authenticated clients
66-
return new ValueTask<CallToolResponse>(new CallToolResponse()
67-
{
68-
Content = [new Content() { Text = "This is protected data that only authorized clients can access" }]
69-
});
70-
}
71-
72-
throw new Exception("It happens.");
73-
},
74-
75-
ListToolsHandler = async (_, _) => new()
76-
{
77-
Tools =
78-
[
79-
new()
80-
{
81-
Name = "echo",
82-
Description = "Echoes back the message you send"
83-
},
84-
new()
85-
{
86-
Name = "protected-data",
87-
Description = "Returns protected data that requires authorization"
88-
}
89-
]
90-
}
91-
}
92-
};
93-
})
39+
builder.Services.AddMcpServer()
40+
.WithTools<WeatherTools>()
9441
.WithHttpTransport();
9542

43+
builder.Services.AddSingleton(_ =>
44+
{
45+
var client = new HttpClient() { BaseAddress = new Uri("https://api.weather.gov") };
46+
client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("weather-tool", "1.0"));
47+
return client;
48+
});
49+
9650
var app = builder.Build();
9751

9852
app.UseAuthentication();
@@ -122,7 +76,7 @@ class SimpleAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
12276
public SimpleAuthHandler(
12377
IOptionsMonitor<AuthenticationSchemeOptions> options,
12478
ILoggerFactory logger,
125-
UrlEncoder encoder)
79+
UrlEncoder encoder)
12680
: base(options, logger, encoder)
12781
{
12882
}
@@ -134,37 +88,37 @@ protected override Task<AuthenticateResult> HandleAuthenticateAsync()
13488
{
13589
return Task.FromResult(AuthenticateResult.Fail("Authorization header missing"));
13690
}
137-
91+
13892
// Parse the token
13993
var headerValue = authHeader.ToString();
14094
if (!headerValue.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
14195
{
14296
return Task.FromResult(AuthenticateResult.Fail("Bearer token missing"));
14397
}
144-
98+
14599
var token = headerValue["Bearer ".Length..].Trim();
146-
100+
147101
// Accept any non-empty token for testing purposes
148102
if (string.IsNullOrEmpty(token))
149103
{
150104
return Task.FromResult(AuthenticateResult.Fail("Token cannot be empty"));
151105
}
152-
106+
153107
// Log the received token for debugging
154108
Console.WriteLine($"Received and accepted token: {token}");
155-
109+
156110
// Create a claims identity with required claims
157111
var claims = new[]
158112
{
159113
new Claim(ClaimTypes.Name, "demo_user"),
160114
new Claim(ClaimTypes.NameIdentifier, "user123"),
161115
new Claim("scope", "weather.read")
162116
};
163-
117+
164118
var identity = new ClaimsIdentity(claims, "Bearer");
165119
var principal = new ClaimsPrincipal(identity);
166120
var ticket = new AuthenticationTicket(principal, "Bearer");
167-
121+
168122
return Task.FromResult(AuthenticateResult.Success(ticket));
169123
}
170124
}

0 commit comments

Comments
 (0)