From 5644a9829075fd4dfa52364d228da2a432b43a14 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:52:28 +0000 Subject: [PATCH 1/4] Initial plan From fd5632165f947f17e6aea6592982d166c7f6a3f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:04:59 +0000 Subject: [PATCH 2/4] Convert InMemoryTransport and QuickstartWeatherServer to file-based projects, delete FileBasedMcpServer Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- ModelContextProtocol.slnx | 2 - samples/FileBasedMcpServer/Program.cs | 30 -------- samples/FileBasedMcpServer/README.md | 64 ---------------- .../InMemoryTransport.csproj | 15 ---- samples/InMemoryTransport/Program.cs | 5 +- samples/InMemoryTransport/README.md | 37 +++++++++ samples/QuickstartWeatherServer/Program.cs | 75 ++++++++++++++++++- .../QuickstartWeatherServer.csproj | 19 ----- samples/QuickstartWeatherServer/README.md | 61 +++++++++++++++ .../Tools/HttpClientExt.cs | 13 ---- .../Tools/WeatherTools.cs | 60 --------------- 11 files changed, 176 insertions(+), 205 deletions(-) delete mode 100755 samples/FileBasedMcpServer/Program.cs delete mode 100644 samples/FileBasedMcpServer/README.md delete mode 100644 samples/InMemoryTransport/InMemoryTransport.csproj create mode 100644 samples/InMemoryTransport/README.md delete mode 100644 samples/QuickstartWeatherServer/QuickstartWeatherServer.csproj create mode 100644 samples/QuickstartWeatherServer/README.md delete mode 100644 samples/QuickstartWeatherServer/Tools/HttpClientExt.cs delete mode 100644 samples/QuickstartWeatherServer/Tools/WeatherTools.cs diff --git a/ModelContextProtocol.slnx b/ModelContextProtocol.slnx index 1f6dce1ed..44ea426f1 100644 --- a/ModelContextProtocol.slnx +++ b/ModelContextProtocol.slnx @@ -43,11 +43,9 @@ - - diff --git a/samples/FileBasedMcpServer/Program.cs b/samples/FileBasedMcpServer/Program.cs deleted file mode 100755 index daf9464d1..000000000 --- a/samples/FileBasedMcpServer/Program.cs +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env -S dotnet run -- -#:package Microsoft.Extensions.Hosting -#:project ../../src/ModelContextProtocol/ModelContextProtocol.csproj - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using ModelContextProtocol.Server; -using System.ComponentModel; - -var builder = Host.CreateApplicationBuilder(args); - -builder.Services.AddMcpServer() - .WithStdioServerTransport() - .WithTools(); - -builder.Logging.AddConsole(options => -{ - options.LogToStandardErrorThreshold = LogLevel.Trace; -}); - -await builder.Build().RunAsync(); - -// File-scoped tool class -[McpServerToolType] -file class EchoTool -{ - [McpServerTool(Name = "echo"), Description("Echoes the message back to the client.")] - public static string Echo([Description("The message to echo back.")] string message) => $"Echo: {message}"; -} diff --git a/samples/FileBasedMcpServer/README.md b/samples/FileBasedMcpServer/README.md deleted file mode 100644 index b28c9298d..000000000 --- a/samples/FileBasedMcpServer/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# File-Based MCP Server Sample - -This sample demonstrates how to create a complete MCP (Model Context Protocol) server using .NET 10's file-based programs feature. Unlike traditional .NET projects that require a `.csproj` file, file-based programs allow you to write and run complete applications in a single `.cs` file. - -## Requirements - -- .NET 10 SDK (RC2 or later) -- No project file required! - -## Running the Sample - -Simply run the Program.cs file directly: - -```bash -dotnet run Program.cs -``` - -The server will start and listen for MCP messages on stdin/stdout (stdio transport). - -### Making it Executable (Unix/Linux/macOS) - -On Unix-like systems, you can make the file executable: - -```bash -chmod +x Program.cs -./Program.cs -``` - -Note: The shebang line uses `/usr/bin/env` to locate `dotnet`, so ensure it's in your PATH. - -## Testing the Server - -You can test the server by using `@modelcontextprotocol/inspector`, any stdio-compatible client, or sending JSON-RPC messages to stdin. Here's an example: - -### Initialize the server: -```bash -echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test-client","version":"1.0"}}}' | dotnet run Program.cs -``` - -### List available tools: -```bash -( - echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test-client","version":"1.0"}}}' - sleep 0.5 - echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' - sleep 1 -) | dotnet run Program.cs 2>/dev/null | grep '^{' | jq . -``` - -### Call the echo tool: -```bash -( - echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test-client","version":"1.0"}}}' - sleep 0.5 - echo '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"echo","arguments":{"message":"Hello, MCP!"}}}' - sleep 1 -) | dotnet run Program.cs 2>/dev/null | grep '^{' | jq . -``` - -## Reference - -- [File-Based Programs Tutorial](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/file-based-programs) -- [C# Preprocessor Directives for File-Based Apps](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/preprocessor-directives#file-based-apps) -- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/) diff --git a/samples/InMemoryTransport/InMemoryTransport.csproj b/samples/InMemoryTransport/InMemoryTransport.csproj deleted file mode 100644 index 7c1161ce9..000000000 --- a/samples/InMemoryTransport/InMemoryTransport.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net8.0 - enable - enable - true - - - - - - - diff --git a/samples/InMemoryTransport/Program.cs b/samples/InMemoryTransport/Program.cs index dbffaa34d..b965b5bb6 100644 --- a/samples/InMemoryTransport/Program.cs +++ b/samples/InMemoryTransport/Program.cs @@ -1,4 +1,7 @@ -using ModelContextProtocol.Client; +#!/usr/bin/env -S dotnet run -- +#:project ../../src/ModelContextProtocol/ModelContextProtocol.csproj + +using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; using System.IO.Pipelines; diff --git a/samples/InMemoryTransport/README.md b/samples/InMemoryTransport/README.md new file mode 100644 index 000000000..eac9f16d7 --- /dev/null +++ b/samples/InMemoryTransport/README.md @@ -0,0 +1,37 @@ +# InMemoryTransport Sample + +This sample demonstrates how to create an MCP client and server connected via an in-memory pipe, without using network sockets or stdio. This is useful for testing and embedding MCP servers directly in your application. + +## Requirements + +- .NET 8.0 SDK or later +- No project file required! + +## Running the Sample + +Simply run the Program.cs file directly: + +```bash +dotnet run Program.cs +``` + +Or on Unix-like systems, make the file executable: + +```bash +chmod +x Program.cs +./Program.cs +``` + +## What This Sample Shows + +- Creating a server with `StreamServerTransport` over an in-memory pipe +- Connecting a client using `StreamClientTransport` over the same pipe +- Listing available tools from the server +- Invoking tools on the server + +The sample creates a simple "Echo" tool that echoes back the input message. + +## Reference + +- [File-Based Programs Tutorial](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/file-based-programs) +- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/) diff --git a/samples/QuickstartWeatherServer/Program.cs b/samples/QuickstartWeatherServer/Program.cs index 9bc050b54..92ebdd02f 100644 --- a/samples/QuickstartWeatherServer/Program.cs +++ b/samples/QuickstartWeatherServer/Program.cs @@ -1,8 +1,16 @@ +#!/usr/bin/env -S dotnet run -- +#:package Microsoft.Extensions.Hosting +#:project ../../src/ModelContextProtocol/ModelContextProtocol.csproj + using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using QuickstartWeatherServer.Tools; +using ModelContextProtocol; +using ModelContextProtocol.Server; +using System.ComponentModel; +using System.Globalization; using System.Net.Http.Headers; +using System.Text.Json; var builder = Host.CreateApplicationBuilder(args); @@ -20,3 +28,68 @@ builder.Services.AddSingleton(httpClient); await builder.Build().RunAsync(); + +// Weather Tools +[McpServerToolType] +file sealed class WeatherTools +{ + [McpServerTool, Description("Get weather alerts for a US state.")] + public static async Task GetAlerts( + HttpClient client, + [Description("The US state to get alerts for. Use the 2 letter abbreviation for the state (e.g. NY).")] string state) + { + using var jsonDocument = await client.ReadJsonDocumentAsync($"/alerts/active/area/{state}"); + var jsonElement = jsonDocument.RootElement; + var alerts = jsonElement.GetProperty("features").EnumerateArray(); + + if (!alerts.Any()) + { + return "No active alerts for this state."; + } + + return string.Join("\n--\n", alerts.Select(alert => + { + JsonElement properties = alert.GetProperty("properties"); + return $""" + 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()} + """; + })); + } + + [McpServerTool, Description("Get weather forecast for a location.")] + public static async Task GetForecast( + HttpClient client, + [Description("Latitude of the location.")] double latitude, + [Description("Longitude of the location.")] double longitude) + { + var pointUrl = string.Create(CultureInfo.InvariantCulture, $"/points/{latitude},{longitude}"); + using var locationDocument = await client.ReadJsonDocumentAsync(pointUrl); + var forecastUrl = locationDocument.RootElement.GetProperty("properties").GetProperty("forecast").GetString() + ?? throw new McpException($"No forecast URL provided by {client.BaseAddress}points/{latitude},{longitude}"); + + using var forecastDocument = await client.ReadJsonDocumentAsync(forecastUrl); + var periods = forecastDocument.RootElement.GetProperty("properties").GetProperty("periods").EnumerateArray(); + + return string.Join("\n---\n", periods.Select(period => $""" + {period.GetProperty("name").GetString()} + Temperature: {period.GetProperty("temperature").GetInt32()}°F + Wind: {period.GetProperty("windSpeed").GetString()} {period.GetProperty("windDirection").GetString()} + Forecast: {period.GetProperty("detailedForecast").GetString()} + """)); + } +} + +// HttpClient Extension Methods +file static class HttpClientExt +{ + public static async Task ReadJsonDocumentAsync(this HttpClient client, string requestUri) + { + using var response = await client.GetAsync(requestUri); + response.EnsureSuccessStatusCode(); + return await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync()); + } +} diff --git a/samples/QuickstartWeatherServer/QuickstartWeatherServer.csproj b/samples/QuickstartWeatherServer/QuickstartWeatherServer.csproj deleted file mode 100644 index dc1108a8f..000000000 --- a/samples/QuickstartWeatherServer/QuickstartWeatherServer.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Exe - net8.0 - enable - enable - true - - - - - - - - - - - diff --git a/samples/QuickstartWeatherServer/README.md b/samples/QuickstartWeatherServer/README.md new file mode 100644 index 000000000..06abd2fbf --- /dev/null +++ b/samples/QuickstartWeatherServer/README.md @@ -0,0 +1,61 @@ +# QuickstartWeatherServer Sample + +This sample demonstrates how to create an MCP server that provides weather-related tools using the weather.gov API. This is a file-based program that runs without a traditional project file. + +## Requirements + +- .NET 8.0 SDK or later +- No project file required! + +## Running the Sample + +Simply run the Program.cs file directly: + +```bash +dotnet run Program.cs +``` + +Or on Unix-like systems, make the file executable: + +```bash +chmod +x Program.cs +./Program.cs +``` + +The server will start and listen for MCP messages on stdin/stdout (stdio transport). + +## Available Tools + +The server provides two weather tools: + +1. **GetAlerts** - Get weather alerts for a US state (use 2-letter abbreviation like "NY") +2. **GetForecast** - Get weather forecast for a location (requires latitude and longitude) + +## Testing the Server + +You can test the server using the QuickstartClient or any MCP-compatible client: + +```bash +# From the repository root +dotnet run --project samples/QuickstartClient samples/QuickstartWeatherServer +``` + +Or test with the MCP inspector: + +```bash +npx @modelcontextprotocol/inspector dotnet run samples/QuickstartWeatherServer/Program.cs +``` + +## What This Sample Shows + +- Creating an MCP server using `Host.CreateApplicationBuilder` +- Registering tools with `WithTools()` +- Using dependency injection to provide HttpClient to tools +- Configuring logging to stderr for MCP compatibility +- Using file-scoped classes for tool implementations + +## Reference + +- [File-Based Programs Tutorial](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/file-based-programs) +- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/) +- [Weather.gov API](https://www.weather.gov/documentation/services-web-api) diff --git a/samples/QuickstartWeatherServer/Tools/HttpClientExt.cs b/samples/QuickstartWeatherServer/Tools/HttpClientExt.cs deleted file mode 100644 index f7b2b5499..000000000 --- a/samples/QuickstartWeatherServer/Tools/HttpClientExt.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Text.Json; - -namespace ModelContextProtocol; - -internal static class HttpClientExt -{ - public static async Task ReadJsonDocumentAsync(this HttpClient client, string requestUri) - { - using var response = await client.GetAsync(requestUri); - response.EnsureSuccessStatusCode(); - return await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync()); - } -} \ No newline at end of file diff --git a/samples/QuickstartWeatherServer/Tools/WeatherTools.cs b/samples/QuickstartWeatherServer/Tools/WeatherTools.cs deleted file mode 100644 index 61dc0a0ee..000000000 --- a/samples/QuickstartWeatherServer/Tools/WeatherTools.cs +++ /dev/null @@ -1,60 +0,0 @@ -using ModelContextProtocol; -using ModelContextProtocol.Server; -using System.ComponentModel; -using System.Globalization; -using System.Text.Json; - -namespace QuickstartWeatherServer.Tools; - -[McpServerToolType] -public sealed class WeatherTools -{ - [McpServerTool, Description("Get weather alerts for a US state.")] - public static async Task GetAlerts( - HttpClient client, - [Description("The US state to get alerts for. Use the 2 letter abbreviation for the state (e.g. NY).")] string state) - { - using var jsonDocument = await client.ReadJsonDocumentAsync($"/alerts/active/area/{state}"); - var jsonElement = jsonDocument.RootElement; - var alerts = jsonElement.GetProperty("features").EnumerateArray(); - - if (!alerts.Any()) - { - return "No active alerts for this state."; - } - - return string.Join("\n--\n", alerts.Select(alert => - { - JsonElement properties = alert.GetProperty("properties"); - return $""" - 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()} - """; - })); - } - - [McpServerTool, Description("Get weather forecast for a location.")] - public static async Task GetForecast( - HttpClient client, - [Description("Latitude of the location.")] double latitude, - [Description("Longitude of the location.")] double longitude) - { - var pointUrl = string.Create(CultureInfo.InvariantCulture, $"/points/{latitude},{longitude}"); - using var locationDocument = await client.ReadJsonDocumentAsync(pointUrl); - var forecastUrl = locationDocument.RootElement.GetProperty("properties").GetProperty("forecast").GetString() - ?? throw new McpException($"No forecast URL provided by {client.BaseAddress}points/{latitude},{longitude}"); - - using var forecastDocument = await client.ReadJsonDocumentAsync(forecastUrl); - var periods = forecastDocument.RootElement.GetProperty("properties").GetProperty("periods").EnumerateArray(); - - return string.Join("\n---\n", periods.Select(period => $""" - {period.GetProperty("name").GetString()} - Temperature: {period.GetProperty("temperature").GetInt32()}°F - Wind: {period.GetProperty("windSpeed").GetString()} {period.GetProperty("windDirection").GetString()} - Forecast: {period.GetProperty("detailedForecast").GetString()} - """)); - } -} From 44a26540e0aa5c5f1c403886a95c3ab6672ee2cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:18:55 +0000 Subject: [PATCH 3/4] Revert InMemoryTransport to traditional project structure Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- ModelContextProtocol.slnx | 1 + .../InMemoryTransport.csproj | 15 ++++++++ samples/InMemoryTransport/Program.cs | 3 -- samples/InMemoryTransport/README.md | 37 ------------------- 4 files changed, 16 insertions(+), 40 deletions(-) create mode 100644 samples/InMemoryTransport/InMemoryTransport.csproj delete mode 100644 samples/InMemoryTransport/README.md diff --git a/ModelContextProtocol.slnx b/ModelContextProtocol.slnx index 44ea426f1..3ad349d47 100644 --- a/ModelContextProtocol.slnx +++ b/ModelContextProtocol.slnx @@ -43,6 +43,7 @@ + diff --git a/samples/InMemoryTransport/InMemoryTransport.csproj b/samples/InMemoryTransport/InMemoryTransport.csproj new file mode 100644 index 000000000..7c1161ce9 --- /dev/null +++ b/samples/InMemoryTransport/InMemoryTransport.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + enable + enable + true + + + + + + + diff --git a/samples/InMemoryTransport/Program.cs b/samples/InMemoryTransport/Program.cs index b965b5bb6..408ff44b4 100644 --- a/samples/InMemoryTransport/Program.cs +++ b/samples/InMemoryTransport/Program.cs @@ -1,6 +1,3 @@ -#!/usr/bin/env -S dotnet run -- -#:project ../../src/ModelContextProtocol/ModelContextProtocol.csproj - using ModelContextProtocol.Client; using ModelContextProtocol.Protocol; using ModelContextProtocol.Server; diff --git a/samples/InMemoryTransport/README.md b/samples/InMemoryTransport/README.md deleted file mode 100644 index eac9f16d7..000000000 --- a/samples/InMemoryTransport/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# InMemoryTransport Sample - -This sample demonstrates how to create an MCP client and server connected via an in-memory pipe, without using network sockets or stdio. This is useful for testing and embedding MCP servers directly in your application. - -## Requirements - -- .NET 8.0 SDK or later -- No project file required! - -## Running the Sample - -Simply run the Program.cs file directly: - -```bash -dotnet run Program.cs -``` - -Or on Unix-like systems, make the file executable: - -```bash -chmod +x Program.cs -./Program.cs -``` - -## What This Sample Shows - -- Creating a server with `StreamServerTransport` over an in-memory pipe -- Connecting a client using `StreamClientTransport` over the same pipe -- Listing available tools from the server -- Invoking tools on the server - -The sample creates a simple "Echo" tool that echoes back the input message. - -## Reference - -- [File-Based Programs Tutorial](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/file-based-programs) -- [Model Context Protocol Specification](https://modelcontextprotocol.io/specification/) From e54c0c9453848cae435c3331f396a06a1bf85536 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 09:23:17 +0000 Subject: [PATCH 4/4] Fix README command path for QuickstartWeatherServer Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com> --- samples/QuickstartWeatherServer/Program.cs | 0 samples/QuickstartWeatherServer/README.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 samples/QuickstartWeatherServer/Program.cs diff --git a/samples/QuickstartWeatherServer/Program.cs b/samples/QuickstartWeatherServer/Program.cs old mode 100644 new mode 100755 diff --git a/samples/QuickstartWeatherServer/README.md b/samples/QuickstartWeatherServer/README.md index 06abd2fbf..4ca494d6d 100644 --- a/samples/QuickstartWeatherServer/README.md +++ b/samples/QuickstartWeatherServer/README.md @@ -43,7 +43,7 @@ dotnet run --project samples/QuickstartClient samples/QuickstartWeatherServer Or test with the MCP inspector: ```bash -npx @modelcontextprotocol/inspector dotnet run samples/QuickstartWeatherServer/Program.cs +npx @modelcontextprotocol/inspector dotnet run Program.cs ``` ## What This Sample Shows