Skip to content

Improved samples: added weather tool in AspNetCoreSseServer and option to connect via http to AspNetCoreSseServer. #469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions samples/AspNetCoreSseServer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
builder.Services.AddMcpServer()
.WithHttpTransport()
.WithTools<EchoTool>()
.WithTools<SampleLlmTool>();
.WithTools<SampleLlmTool>()
.WithTools<WeatherTools>();

builder.Services.AddOpenTelemetry()
.WithTracing(b => b.AddSource("*")
Expand All @@ -18,7 +19,7 @@
.AddHttpClientInstrumentation())
.WithLogging()
.UseOtlpExporter();

builder.Services.AddHttpClient();
var app = builder.Build();

app.MapMcp();
Expand Down
55 changes: 55 additions & 0 deletions samples/AspNetCoreSseServer/Tools/WeatherTools.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using ModelContextProtocol.Server;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net.Http;
using System.Text.Json.Nodes;

namespace TestServerWithHosting.Tools;

[McpServerToolType]
public sealed class WeatherTools
{
private readonly IHttpClientFactory _httpClientFactory;

public WeatherTools(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
[McpServerTool(Name = "get_current_weather"), Description("returns the current weather given a town or region name")]
public async Task<string> Get_Weather(IMcpServer mcpServer, [Description("The location (town or region) name")] string location)
{
try
{
var client = _httpClientFactory.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, $"https://nominatim.openstreetmap.org/search?format=json&q={location}");
request.Headers.Add("User-Agent", "Test-MCP-Server");
var ret = await client.SendAsync(request);
if (!ret.IsSuccessStatusCode)
{
return $"there was an error getting coordinates from location StatusCode: {ret.StatusCode} message: {await ret.Content.ReadAsStringAsync()}";
}
var response = ret.Content.ReadAsStreamAsync();
var locationInfo = JsonNode.Parse(await response) as JsonArray ?? new JsonArray();
if (locationInfo == null || locationInfo.Count == 0)
{
return $"could not parse no result {response} into an json array or no results were found for location {location}";
}
request = new HttpRequestMessage(HttpMethod.Get, $"https://api.open-meteo.com/v1/forecast?latitude={locationInfo.First()?["lat"]}&longitude={locationInfo.First()?["lon"]}9&current_weather=true");
request.Headers.Add("User-Agent", "Test-MCP-Server");
ret = await client.SendAsync(request);
if (!ret.IsSuccessStatusCode)
{
return $"error getting coordinates from location StatusCode: {ret.StatusCode} message: {await ret.Content.ReadAsStringAsync()}";
}
return await ret.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
return $"general error: {ex.ToString()}";
}
}
}
40 changes: 28 additions & 12 deletions samples/QuickstartClient/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Intrinsics.X86;
using System.Text;

var builder = Host.CreateApplicationBuilder(args);

Expand All @@ -11,15 +14,22 @@
.AddUserSecrets<Program>();

var (command, arguments) = GetCommandAndArguments(args);

var clientTransport = new StdioClientTransport(new()
IClientTransport? clientTransport = null;
if (command == "http")
{
Name = "Demo Server",
Command = command,
Arguments = arguments,
});

await using var mcpClient = await McpClientFactory.CreateAsync(clientTransport);
// make sure AspNetCoreSseServer is running
clientTransport = new SseClientTransport(new SseClientTransportOptions { Endpoint = new Uri("https://localhost:7133"), UseStreamableHttp = true });
}
else
{
clientTransport = new StdioClientTransport(new()
{
Name = "Demo Server",
Command = command,
Arguments = arguments,
});
}
await using var mcpClient = await McpClientFactory.CreateAsync(clientTransport!);

var tools = await mcpClient.ListToolsAsync();
foreach (var tool in tools)
Expand All @@ -43,7 +53,7 @@
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("MCP Client Started!");
Console.ResetColor();

var messages = new List<ChatMessage>();
PromptForInput();
while(Console.ReadLine() is string query && !"exit".Equals(query, StringComparison.OrdinalIgnoreCase))
{
Expand All @@ -52,14 +62,18 @@
PromptForInput();
continue;
}

await foreach (var message in anthropicClient.GetStreamingResponseAsync(query, options))
messages.Add(new ChatMessage(ChatRole.User, query));
var sb = new StringBuilder();
await foreach (var message in anthropicClient.GetStreamingResponseAsync(messages, options))
{
Console.Write(message);
sb.AppendLine(message.ToString());
}
messages.Add(new ChatMessage(ChatRole.Assistant, sb.ToString()));
Console.WriteLine();

PromptForInput();

}

static void PromptForInput()
Expand Down Expand Up @@ -89,6 +103,8 @@ static void PromptForInput()
[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, "--no-build"]),
[var script] when script.Equals("http", StringComparison.OrdinalIgnoreCase) => ("http", args),
_ => ("dotnet", ["run", "--project", "../../../../QuickstartWeatherServer", "--no-build"])
};
}
}

8 changes: 8 additions & 0 deletions samples/QuickstartClient/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"profiles": {
"QuickstartClient": {
"commandName": "Project",
"commandLineArgs": "http"
}
}
}