diff --git a/images/quickstart-dotnet-client.png b/images/quickstart-dotnet-client.png new file mode 100644 index 0000000..73b9f9f Binary files /dev/null and b/images/quickstart-dotnet-client.png differ diff --git a/quickstart/client.mdx b/quickstart/client.mdx index 281fe1e..8a1629b 100644 --- a/quickstart/client.mdx +++ b/quickstart/client.mdx @@ -1365,6 +1365,184 @@ If you see: + +[You can find the complete code for this tutorial here.](https://github.io/modelcontextprotocol/csharp-sdk/tree/main/samples/QuickstartClient) + +## System Requirements +Before starting, ensure your system meets these requirements: +- .NET 8.0 or higher +- Anthropic API key (Claude) +- Windows, Linux, or MacOS + +## Setting up your environment + +First, create a new .NET project: +```bash +dotnet new console -n QuickstartClient +cd QuickstartClient +``` + +Then, add the required dependencies to your project: +```bash +dotnet add package ModelContextProtocol --preview +dotnet add package Anthropic.SDK +dotnet add package Microsoft.Extensions.Hosting +``` + +## Setting up your API key +You'll need an Anthropic API key from the [Anthropic Console](https://console.anthropic.com/settings/keys). + +```bash +dotnet user-secrets init +dotnet user-secrets set "ANTHROPIC_API_KEY" "" +``` + +## Creating the Client +### Basic Client Structure +First, let's setup the basic client class: +```csharp +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +var builder = Host.CreateEmptyApplicationBuilder(settings: null); + +builder.Configuration + .AddUserSecrets(); +``` + +This creates the beginnings of a .NET console application that can read the API key from user secrets. + +Next, we'll setup the MCP Client: + +```csharp +var (command, arguments) = args switch +{ + [var script] when script.EndsWith(".py") => ("python", script), + [var script] when script.EndsWith(".js") => ("node", script), + [var script] when Directory.Exists(script) || (File.Exists(script) && script.EndsWith(".csproj")) => ("dotnet", $"run --project {script} --no-build"), + _ => throw new NotSupportedException("An unsupported server script was provided. Supported scripts are .py, .js, or .csproj") +}; + +var mcpClient = await McpClientFactory.CreateAsync(new() +{ + Id = "demo-client", + Name = "Demo Client", + TransportType = TransportTypes.StdIo, + TransportOptions = new() + { + ["command"] = command, + ["arguments"] = arguments, + } +}); + +var tools = await mcpClient.ListToolsAsync(); +foreach (var tool in tools) +{ + Console.WriteLine($"Connected to server with tools: {tool.Name}"); +} +``` + +Be sure to add the `using` statements for the namespaces: +```csharp +using ModelContextProtocol.Client; +using ModelContextProtocol.Protocol.Transport; +``` + + +This configures a MCP client that will connect to a server that is provided as a command line argument. It then lists the available tools from the connected server. + +### Query processing logic +Now let's add the core functionality for processing queries and handling tool calls: + +```csharp +IChatClient anthropicClient = new AnthropicClient(new APIAuthentication(builder.Configuration["ANTHROPIC_API_KEY"])) + .Messages + .AsBuilder() + .UseFunctionInvocation() + .Build(); + +var options = new ChatOptions +{ + MaxOutputTokens = 1000, + ModelId = "claude-3-5-sonnet-20241022", + Tools = [.. tools.Cast()] +}; + +while (true) +{ + Console.WriteLine("MCP Client Started!"); + Console.WriteLine("Type your queries or 'quit' to exit."); + + string? query = Console.ReadLine(); + + if (string.IsNullOrWhiteSpace(query)) + { + continue; + } + if (string.Equals(query, "quit", StringComparison.OrdinalIgnoreCase)) + { + break; + } + + var response = await anthropicClient.GetResponseAsync(query, options); + + foreach (var message in response.Messages) + { + Console.WriteLine(message.Text); + } +} + +anthropicClient.Dispose(); +await mcpClient.DisposeAsync(); +``` + +## Key Components Explained + +### 1. Client Initialization +* The client is initialized using `McpClientFactory.CreateAsync()`, which sets up the transport type and command to run the server. + +### 2. Server Connection +* Supports Python, Node.js, and .NET servers. +* The server is started using the command specified in the arguments. +* Configures to use stdio for communication with the server. +* Initializes the session and available tools. + +### 3. Query Processing +* Leverages [Microsoft.Extensions.AI](https://learn.microsoft.com/dotnet/ai/ai-extensions) for the chat client. +* Configures the `IChatClient` to use automatic tool (function) invocation. +* The client reads user input and sends it to the server. +* The server processes the query and returns a response. +* The response is displayed to the user. + +### Running the Client +To run your client with any MCP server: +```bash +dotnet run -- path/to/server.csproj # dotnet server +dotnet run -- path/to/server.py # python server +dotnet run -- path/to/server.js # node server +``` + +If you're continuing the weather tutorial from the server quickstart, your command might look something like this: `dotnet run -- path/to/QuickstartWeatherServer`. + + +The client will: + +1. Connect to the specified server +2. List available tools +3. Start an interactive chat session where you can: + - Enter queries + - See tool executions + - Get responses from Claude +4. Exit the session when done + +Here's an example of what it should look like it connected to a weather server quickstart: + + + + + + + ## Next steps diff --git a/quickstart/server.mdx b/quickstart/server.mdx index 94f86b9..a2658f3 100644 --- a/quickstart/server.mdx +++ b/quickstart/server.mdx @@ -1406,6 +1406,241 @@ This tells Claude for Desktop: Save the file, and restart **Claude for Desktop**. + + + +Let's get started with building our weather server! [You can find the complete code for what we'll be building here.](https://github.com/modelcontextprotocol/csharp-sdk/tree/main/samples/QuickstartWeatherServer) + +### Prerequisite knowledge +This quickstart assumes you have familiarity with: +- C# +- LLMs like Claude +- .NET 8 or higher + +### System requirements +- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) or higher installed. + +### Set up your environment +First, let's install `dotnet` if you haven't already. You can download `dotnet` from [official Microsoft .NET website](https://dotnet.microsoft.com/download/). Verify your `dotnet` installation: +```bash +dotnet --version +``` +Now, let's create and set up your project: + +```bash MacOS/Linux +# Create a new directory for our project +mkdir weather +cd weather +# Initialize a new C# project +dotnet new console +``` + +```powershell Windows +# Create a new directory for our project +mkdir weather +cd weather +# Initialize a new C# project +dotnet new console +``` + +After running `dotnet new console`, you will be presented with a new C# project. +You can open the project in your favorite IDE, such as [Visual Studio](https://visualstudio.microsoft.com/) or [Rider](https://www.jetbrains.com/rider/). +Alternatively, you can create a C# application using the [Visual Studio project wizard](https://learn.microsoft.com/en-us/visualstudio/get-started/csharp/tutorial-console?view=vs-2022). +After creating the project, add NuGet package for the Model Context Protocol SDK and hosting: + +```bash +# Add the Model Context Protocol SDK NuGet package +dotnet add package ModelContextProtocol --preview +# Add the .NET Hosting NuGet package +dotnet add package Microsoft.Extensions.Hosting +``` +Now let’s dive into building your server. + +## Building your server + +Open the `Program.cs` file in your project and replace its contents with the following code: + +```csharp +using Microsoft.Extensions.Hosting; +using ModelContextProtocol; + +var builder = Host.CreateEmptyApplicationBuilder(settings: null); +builder.Services.AddMcpServer() + .WithStdioServerTransport() + .WithToolsFromAssembly(); + +var app = builder.Build(); + +await app.RunAsync(); +``` + +When creating the `ApplicationHostBuilder`, ensure you use `CreateEmptyApplicationBuilder` instead of `CreateDefaultBuilder`. This ensures that the server does not write any additional messages to the console. This is only neccessary for servers using STDIO transport. + + +This code sets up a basic console application that uses the Model Context Protocol SDK to create an MCP server with standard I/O transport. + +### Weather API helper functions +Next, define a class with the tool execution handlers for querying and converting responses from the National Weather Service API: + +```csharp +using ModelContextProtocol.Server; +using System.ComponentModel; +using System.Net.Http.Headers; +using System.Text.Json; + +namespace QuickstartWeatherServer.Tools; + +[McpServerToolType] +public static class WeatherTools +{ + [McpServerTool, Description("Get weather alerts for a US state.")] + public static async Task GetAlerts( + [Description("The US state to get alerts for.")] string state) + { + using HttpClient client = GetWeatherClient(); + + var response = await client.GetAsync($"/alerts/active/area/{state}"); + + var json = await response.Content.ReadAsStringAsync(); + var jsonElement = JsonSerializer.Deserialize(json); + var alerts = jsonElement.GetProperty("features").EnumerateArray(); + + if (!alerts.Any()) + { + return "No active alerts for this state."; + } + + // Process the alerts and return a formatted string + var alertMessages = new List(); + foreach (var alert in alerts) + { + JsonElement properties = alert.GetProperty("properties"); + alertMessages.Add($""" + Event: {properties.GetProperty("event").GetString()} + Area: {properties.GetProperty("areaDesc").GetString()} + Severity: {properties.GetProperty("severity").GetString()} + Description: {properties.GetProperty("description").GetString()} + Instruction: {properties.GetProperty("instruction").GetString()} + """); + } + return string.Join("\n---\n", alertMessages); + } + + [McpServerTool, Description("Get weather forecast for a location.")] + public static async Task GetForecast( + [Description("Latitude of the location.")] double latitude, + [Description("Longitude of the location.")] double longitude) + { + using HttpClient client = GetWeatherClient(); + var response = await client.GetAsync($"/points/{latitude},{longitude}"); + if (!response.IsSuccessStatusCode) + { + return "Failed to retrieve forecast."; + } + + var json = await response.Content.ReadAsStringAsync(); + var jsonElement = JsonSerializer.Deserialize(json); + var periods = jsonElement.GetProperty("properties").GetProperty("periods").EnumerateArray(); + // Process the forecast and return a formatted string + var forecastMessages = new List(); + foreach (var period in periods) + { + forecastMessages.Add($""" + {period.GetProperty("name").GetString()} + Temperature: {period.GetProperty("temperature").GetInt32()}°F + Wind: {period.GetProperty("windSpeed").GetString()} {period.GetProperty("windDirection").GetString()} + Forecast: {period.GetProperty("detailedForecast").GetString()} + """); + } + return string.Join("\n---\n", forecastMessages); + } + + private static HttpClient GetWeatherClient() + { + var client = new HttpClient() { BaseAddress = new Uri("https://api.weather.gov") }; + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("weather-tool", "1.0")); + return client; + } +} +``` + +### Running the server +Finally, run the server using the following command: + +```bash +dotnet run +``` +This will start the server and listen for incoming requests on standard input/output. + +## Testing your server with Claude for Desktop + +Claude for Desktop is not yet available on Linux. Linux users can proceed to the [Building a client](/quickstart/client) tutorial to build an MCP client that connects to the server we just built. + + +First, make sure you have Claude for Desktop installed. [You can install the latest version +here.](https://claude.ai/download) If you already have Claude for Desktop, **make sure it's updated to the latest version.** +We'll need to configure Claude for Desktop for whichever MCP servers you want to use. To do this, open your Claude for Desktop App configuration at `~/Library/Application Support/Claude/claude_desktop_config.json` in a text editor. Make sure to create the file if it doesn't exist. +For example, if you have [VS Code](https://code.visualstudio.com/) installed: + + +```bash +code ~/Library/Application\ Support/Claude/claude_desktop_config.json +``` + + +```powershell +code $env:AppData\Claude\claude_desktop_config.json +``` + + + +You'll then add your servers in the `mcpServers` key. The MCP UI elements will only show up in Claude for Desktop if at least one server is properly configured. +In this case, we'll add our single weather server like so: + + + +```json +{ + "mcpServers": { + "weather": { + "command": "dotnet", + "args": [ + "run", + "--project", + "/ABSOLUTE/PATH/TO/PROJECT", + "--no-build" + ] + } + } +} +``` + + + + +```json +{ + "mcpServers": { + "weather": { + "command": "dotnet", + "args": [ + "run", + "--project", + "C:\\ABSOLUTE\\PATH\\TO\\PROJECT", + "--no-build" + ] + } + } +} +``` + + + +This tells Claude for Desktop: +1. There's an MCP server named "weather" +2. Launch it by running `dotnet run /ABSOLUTE/PATH/TO/PROJECT` +Save the file, and restart **Claude for Desktop**. +