-
Notifications
You must be signed in to change notification settings - Fork 680
Expand file tree
/
Copy pathProgram.cs
More file actions
124 lines (108 loc) · 3.89 KB
/
Program.cs
File metadata and controls
124 lines (108 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
using Anthropic;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddEnvironmentVariables()
.AddUserSecrets<Program>();
IClientTransport clientTransport;
var (command, arguments) = GetCommandAndArguments(args);
if (command == "http")
{
// make sure AspNetCoreMcpServer is running
clientTransport = new HttpClientTransport(new()
{
Endpoint = new Uri("http://localhost:3001")
});
}
else
{
clientTransport = new StdioClientTransport(new()
{
Name = "Demo Server",
Command = command,
Arguments = arguments,
});
}
await using var mcpClient = await McpClient.CreateAsync(clientTransport!);
var tools = await mcpClient.ListToolsAsync();
foreach (var tool in tools)
{
Console.WriteLine($"Connected to server with tools: {tool.Name}");
}
using var anthropicClient = new AnthropicClient(new() { ApiKey = builder.Configuration["ANTHROPIC_API_KEY"] })
.AsIChatClient("claude-haiku-4-5-20251001")
.AsBuilder()
.UseFunctionInvocation()
.Build();
var options = new ChatOptions
{
MaxOutputTokens = 1000,
ModelId = "claude-haiku-4-5-20251001",
Tools = [.. tools]
};
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("MCP Client Started!");
Console.ResetColor();
var messages = new List<ChatMessage>();
var sb = new StringBuilder();
PromptForInput();
while (Console.ReadLine() is string query && !"exit".Equals(query, StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrWhiteSpace(query))
{
PromptForInput();
continue;
}
messages.Add(new ChatMessage(ChatRole.User, query));
await foreach (var message in anthropicClient.GetStreamingResponseAsync(messages, options))
{
Console.Write(message);
sb.Append(message.ToString());
}
Console.WriteLine();
sb.AppendLine();
messages.Add(new ChatMessage(ChatRole.Assistant, sb.ToString()));
sb.Clear();
PromptForInput();
}
static void PromptForInput()
{
Console.WriteLine("Enter a command (or 'exit' to quit):");
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write("> ");
Console.ResetColor();
}
/// <summary>
/// Determines the command (executable) to run and the script/path to pass to it. This allows different
/// languages/runtime environments to be used as the MCP server.
/// </summary>
/// <remarks>
/// This method uses the file extension of the first argument to determine the command, if it's py, it'll run python,
/// if it's js, it'll run node, if it's a directory or a csproj file, it'll run dotnet.
///
/// If no arguments are provided, it defaults to running the QuickstartWeatherServer project from the current repo.
///
/// This method would only be required if you're creating a generic client, such as we use for the quickstart.
/// </remarks>
static (string command, string[] arguments) GetCommandAndArguments(string[] args)
{
return args switch
{
[var mode] when mode.Equals("http", StringComparison.OrdinalIgnoreCase) => ("http", args),
[var script] when script.EndsWith(".py") => ("python", args),
[var script] when script.EndsWith(".js") => ("node", args),
[var script] when Directory.Exists(script) || (File.Exists(script) && script.EndsWith(".csproj")) => ("dotnet", ["run", "--project", script]),
_ => ("dotnet", ["run", "--project", Path.Combine(GetCurrentSourceDirectory(), "../QuickstartWeatherServer")])
};
}
static string GetCurrentSourceDirectory([CallerFilePath] string? currentFile = null)
{
Debug.Assert(!string.IsNullOrWhiteSpace(currentFile));
return Path.GetDirectoryName(currentFile) ?? throw new InvalidOperationException("Unable to determine source directory.");
}