Skip to content

Commit a253cd0

Browse files
committed
Use top-level statements in ProtectedMCPClient Program.cs
1 parent cbfe8c7 commit a253cd0

File tree

1 file changed

+141
-148
lines changed

1 file changed

+141
-148
lines changed

samples/ProtectedMCPClient/Program.cs

Lines changed: 141 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -5,180 +5,173 @@
55
using System.Net;
66
using System.Text;
77
using System.Web;
8+
using static System.Runtime.InteropServices.JavaScript.JSType;
89

9-
namespace ProtectedMCPClient;
10+
Console.WriteLine("Protected MCP Weather Server");
11+
Console.WriteLine();
1012

11-
class Program
13+
var serverUrl = "http://localhost:7071/sse";
14+
var clientId = Environment.GetEnvironmentVariable("CLIENT_ID") ?? throw new Exception("The CLIENT_ID environment variable is not set.");
15+
16+
// We can customize a shared HttpClient with a custom handler if desired
17+
var sharedHandler = new SocketsHttpHandler
18+
{
19+
PooledConnectionLifetime = TimeSpan.FromMinutes(2),
20+
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1)
21+
};
22+
23+
var httpClient = new HttpClient(sharedHandler);
24+
// Create the token provider with our custom HttpClient and authorization URL handler
25+
var tokenProvider = new GenericOAuthProvider(
26+
new Uri(serverUrl),
27+
httpClient,
28+
null, // AuthorizationHelpers will be created automatically
29+
clientId: clientId,
30+
clientSecret: "", // No secret needed for this client
31+
redirectUri: new Uri("http://localhost:1179/callback"),
32+
scopes: [$"api://{clientId}/weather.read"],
33+
logger: null,
34+
authorizationUrlHandler: HandleAuthorizationUrlAsync
35+
);
36+
37+
Console.WriteLine();
38+
Console.WriteLine($"Connecting to weather server at {serverUrl}...");
39+
40+
try
1241
{
13-
static async Task Main(string[] args)
42+
var transportOptions = new SseClientTransportOptions
1443
{
15-
Console.WriteLine("Protected MCP Weather Server");
16-
Console.WriteLine();
44+
Endpoint = new Uri(serverUrl),
45+
Name = "Secure Weather Client"
46+
};
1747

18-
var serverUrl = "http://localhost:7071/sse";
19-
var clientId = Environment.GetEnvironmentVariable("CLIENT_ID") ?? throw new Exception("The CLIENT_ID environment variable is not set.");
48+
// Create a transport with authentication support using the correct constructor parameters
49+
var transport = new SseClientTransport(
50+
transportOptions,
51+
tokenProvider
52+
);
2053

21-
// We can customize a shared HttpClient with a custom handler if desired
22-
var sharedHandler = new SocketsHttpHandler
23-
{
24-
PooledConnectionLifetime = TimeSpan.FromMinutes(2),
25-
PooledConnectionIdleTimeout = TimeSpan.FromMinutes(1)
26-
};
54+
var client = await McpClientFactory.CreateAsync(transport);
55+
56+
var tools = await client.ListToolsAsync();
57+
if (tools.Count == 0)
58+
{
59+
Console.WriteLine("No tools available on the server.");
60+
return;
61+
}
62+
63+
Console.WriteLine($"Found {tools.Count} tools on the server.");
64+
Console.WriteLine();
65+
66+
if (tools.Any(t => t.Name == "GetAlerts"))
67+
{
68+
Console.WriteLine("Calling GetAlerts tool...");
2769

28-
var httpClient = new HttpClient(sharedHandler);
29-
// Create the token provider with our custom HttpClient and authorization URL handler
30-
var tokenProvider = new GenericOAuthProvider(
31-
new Uri(serverUrl),
32-
httpClient,
33-
null, // AuthorizationHelpers will be created automatically
34-
clientId: clientId,
35-
clientSecret: "", // No secret needed for this client
36-
redirectUri: new Uri("http://localhost:1179/callback"),
37-
scopes: [$"api://{clientId}/weather.read"],
38-
logger: null,
39-
authorizationUrlHandler: HandleAuthorizationUrlAsync
70+
var result = await client.CallToolAsync(
71+
"GetAlerts",
72+
new Dictionary<string, object?> { { "state", "WA" } }
4073
);
4174

75+
Console.WriteLine("Result: " + ((TextContentBlock)result.Content[0]).Text);
4276
Console.WriteLine();
43-
Console.WriteLine($"Connecting to weather server at {serverUrl}...");
44-
45-
try
46-
{
47-
var transportOptions = new SseClientTransportOptions
48-
{
49-
Endpoint = new Uri(serverUrl),
50-
Name = "Secure Weather Client"
51-
};
52-
53-
// Create a transport with authentication support using the correct constructor parameters
54-
var transport = new SseClientTransport(
55-
transportOptions,
56-
tokenProvider
57-
);
58-
59-
var client = await McpClientFactory.CreateAsync(transport);
60-
61-
var tools = await client.ListToolsAsync();
62-
if (tools.Count == 0)
63-
{
64-
Console.WriteLine("No tools available on the server.");
65-
return;
66-
}
67-
68-
Console.WriteLine($"Found {tools.Count} tools on the server.");
69-
Console.WriteLine();
70-
71-
if (tools.Any(t => t.Name == "GetAlerts"))
72-
{
73-
Console.WriteLine("Calling GetAlerts tool...");
74-
75-
var result = await client.CallToolAsync(
76-
"GetAlerts",
77-
new Dictionary<string, object?> { { "state", "WA" } }
78-
);
79-
80-
Console.WriteLine("Result: " + ((TextContentBlock)result.Content[0]).Text);
81-
Console.WriteLine();
82-
}
83-
}
84-
catch (Exception ex)
85-
{
86-
Console.WriteLine($"Error: {ex.Message}");
87-
if (ex.InnerException != null)
88-
{
89-
Console.WriteLine($"Inner error: {ex.InnerException.Message}");
90-
}
77+
}
78+
}
79+
catch (Exception ex)
80+
{
81+
Console.WriteLine($"Error: {ex.Message}");
82+
if (ex.InnerException != null)
83+
{
84+
Console.WriteLine($"Inner error: {ex.InnerException.Message}");
85+
}
9186

9287
#if DEBUG
93-
Console.WriteLine($"Stack trace: {ex.StackTrace}");
88+
Console.WriteLine($"Stack trace: {ex.StackTrace}");
9489
#endif
95-
}
96-
Console.WriteLine("Press any key to exit...");
97-
Console.ReadKey();
98-
}
90+
}
91+
Console.WriteLine("Press any key to exit...");
92+
Console.ReadKey();
93+
94+
/// <summary>
95+
/// Handles the OAuth authorization URL by starting a local HTTP server and opening a browser.
96+
/// This implementation demonstrates how SDK consumers can provide their own authorization flow.
97+
/// </summary>
98+
/// <param name="authorizationUrl">The authorization URL to open in the browser.</param>
99+
/// <param name="redirectUri">The redirect URI where the authorization code will be sent.</param>
100+
/// <param name="cancellationToken">The cancellation token.</param>
101+
/// <returns>The authorization code extracted from the callback, or null if the operation failed.</returns>
102+
static async Task<string?> HandleAuthorizationUrlAsync(Uri authorizationUrl, Uri redirectUri, CancellationToken cancellationToken)
103+
{
104+
Console.WriteLine("Starting OAuth authorization flow...");
105+
Console.WriteLine($"Opening browser to: {authorizationUrl}");
106+
107+
var listenerPrefix = redirectUri.GetLeftPart(UriPartial.Authority);
108+
if (!listenerPrefix.EndsWith("/")) listenerPrefix += "/";
109+
110+
using var listener = new HttpListener();
111+
listener.Prefixes.Add(listenerPrefix);
99112

100-
/// <summary>
101-
/// Handles the OAuth authorization URL by starting a local HTTP server and opening a browser.
102-
/// This implementation demonstrates how SDK consumers can provide their own authorization flow.
103-
/// </summary>
104-
/// <param name="authorizationUrl">The authorization URL to open in the browser.</param>
105-
/// <param name="redirectUri">The redirect URI where the authorization code will be sent.</param>
106-
/// <param name="cancellationToken">The cancellation token.</param>
107-
/// <returns>The authorization code extracted from the callback, or null if the operation failed.</returns>
108-
private static async Task<string?> HandleAuthorizationUrlAsync(Uri authorizationUrl, Uri redirectUri, CancellationToken cancellationToken)
113+
try
109114
{
110-
Console.WriteLine("Starting OAuth authorization flow...");
111-
Console.WriteLine($"Opening browser to: {authorizationUrl}");
115+
listener.Start();
116+
Console.WriteLine($"Listening for OAuth callback on: {listenerPrefix}");
112117

113-
var listenerPrefix = redirectUri.GetLeftPart(UriPartial.Authority);
114-
if (!listenerPrefix.EndsWith("/")) listenerPrefix += "/";
118+
OpenBrowser(authorizationUrl);
115119

116-
using var listener = new HttpListener();
117-
listener.Prefixes.Add(listenerPrefix);
120+
var context = await listener.GetContextAsync();
121+
var query = HttpUtility.ParseQueryString(context.Request.Url?.Query ?? string.Empty);
122+
var code = query["code"];
123+
var error = query["error"];
118124

119-
try
120-
{
121-
listener.Start();
122-
Console.WriteLine($"Listening for OAuth callback on: {listenerPrefix}");
123-
124-
OpenBrowser(authorizationUrl);
125-
126-
var context = await listener.GetContextAsync();
127-
var query = HttpUtility.ParseQueryString(context.Request.Url?.Query ?? string.Empty);
128-
var code = query["code"];
129-
var error = query["error"];
130-
131-
string responseHtml = "<html><body><h1>Authentication complete</h1><p>You can close this window now.</p></body></html>";
132-
byte[] buffer = Encoding.UTF8.GetBytes(responseHtml);
133-
context.Response.ContentLength64 = buffer.Length;
134-
context.Response.ContentType = "text/html";
135-
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
136-
context.Response.Close();
137-
138-
if (!string.IsNullOrEmpty(error))
139-
{
140-
Console.WriteLine($"Auth error: {error}");
141-
return null;
142-
}
143-
144-
if (string.IsNullOrEmpty(code))
145-
{
146-
Console.WriteLine("No authorization code received");
147-
return null;
148-
}
149-
150-
Console.WriteLine("Authorization code received successfully.");
151-
return code;
152-
}
153-
catch (Exception ex)
125+
string responseHtml = "<html><body><h1>Authentication complete</h1><p>You can close this window now.</p></body></html>";
126+
byte[] buffer = Encoding.UTF8.GetBytes(responseHtml);
127+
context.Response.ContentLength64 = buffer.Length;
128+
context.Response.ContentType = "text/html";
129+
context.Response.OutputStream.Write(buffer, 0, buffer.Length);
130+
context.Response.Close();
131+
132+
if (!string.IsNullOrEmpty(error))
154133
{
155-
Console.WriteLine($"Error getting auth code: {ex.Message}");
134+
Console.WriteLine($"Auth error: {error}");
156135
return null;
157136
}
158-
finally
137+
138+
if (string.IsNullOrEmpty(code))
159139
{
160-
if (listener.IsListening) listener.Stop();
140+
Console.WriteLine("No authorization code received");
141+
return null;
161142
}
143+
144+
Console.WriteLine("Authorization code received successfully.");
145+
return code;
146+
}
147+
catch (Exception ex)
148+
{
149+
Console.WriteLine($"Error getting auth code: {ex.Message}");
150+
return null;
151+
}
152+
finally
153+
{
154+
if (listener.IsListening) listener.Stop();
162155
}
156+
}
163157

164-
/// <summary>
165-
/// Opens the specified URL in the default browser.
166-
/// </summary>
167-
/// <param name="url">The URL to open.</param>
168-
private static void OpenBrowser(Uri url)
158+
/// <summary>
159+
/// Opens the specified URL in the default browser.
160+
/// </summary>
161+
/// <param name="url">The URL to open.</param>
162+
static void OpenBrowser(Uri url)
163+
{
164+
try
169165
{
170-
try
171-
{
172-
var psi = new ProcessStartInfo
173-
{
174-
FileName = url.ToString(),
175-
UseShellExecute = true
176-
};
177-
Process.Start(psi);
178-
}
179-
catch (Exception ex)
166+
var psi = new ProcessStartInfo
180167
{
181-
Console.WriteLine($"Error opening browser. {ex.Message}");
182-
}
168+
FileName = url.ToString(),
169+
UseShellExecute = true
170+
};
171+
Process.Start(psi);
172+
}
173+
catch (Exception ex)
174+
{
175+
Console.WriteLine($"Error opening browser. {ex.Message}");
183176
}
184177
}

0 commit comments

Comments
 (0)