C# quickstarts (#216)

* Server quickstart docs for C#

* C# client quickstart
This commit is contained in:
Aaron Powell 2025-03-27 03:17:01 +11:00 • committed by GitHub
commit c768dcee44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 413 additions and 0 deletions

View file

@ -1365,6 +1365,184 @@ If you see:
</Tab>
<Tab title="C#">
[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" "<your key here>"
```
## 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<Program>();
```
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}");
}
```
<Note>
Be sure to add the `using` statements for the namespaces:
```csharp
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
```
</Note>
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<AITool>()]
};
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
```
<Note>
If you're continuing the weather tutorial from the server quickstart, your command might look something like this: `dotnet run -- path/to/QuickstartWeatherServer`.
</Note>
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:
<Frame>
<img src="/images/quickstart-dotnet-client.png" />
</Frame>
</Tab>
</Tabs>
## Next steps

View file

@ -1406,6 +1406,241 @@ This tells Claude for Desktop:
Save the file, and restart **Claude for Desktop**.
</Tab>
<Tab title='C#'>
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:
<CodeGroup>
```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
```
</CodeGroup>
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();
```
<Note>
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.
</Note>
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<string> 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<JsonElement>(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<string>();
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<string> 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<JsonElement>(json);
var periods = jsonElement.GetProperty("properties").GetProperty("periods").EnumerateArray();
// Process the forecast and return a formatted string
var forecastMessages = new List<string>();
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
<Note>
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.
</Note>
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:
<Tabs>
<Tab title="MacOS/Linux">
```bash
code ~/Library/Application\ Support/Claude/claude_desktop_config.json
```
</Tab>
<Tab title="Windows">
```powershell
code $env:AppData\Claude\claude_desktop_config.json
```
</Tab>
</Tabs>
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:
<Tabs>
<Tab title="MacOS/Linux">
```json
{
"mcpServers": {
"weather": {
"command": "dotnet",
"args": [
"run",
"--project",
"/ABSOLUTE/PATH/TO/PROJECT",
"--no-build"
]
}
}
}
```
</Tab>
<Tab title="Windows">
```json
{
"mcpServers": {
"weather": {
"command": "dotnet",
"args": [
"run",
"--project",
"C:\\ABSOLUTE\\PATH\\TO\\PROJECT",
"--no-build"
]
}
}
}
```
</Tab>
</Tabs>
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**.
</Tab>
</Tabs>