diff --git a/ModelContextProtocol.slnx b/ModelContextProtocol.slnx
index a70e3e310..bf59ac982 100644
--- a/ModelContextProtocol.slnx
+++ b/ModelContextProtocol.slnx
@@ -42,7 +42,9 @@
-
+
+
+
diff --git a/samples/EverythingServer.Core/EverythingServer.Core.csproj b/samples/EverythingServer.Core/EverythingServer.Core.csproj
new file mode 100644
index 000000000..cddc8d0b9
--- /dev/null
+++ b/samples/EverythingServer.Core/EverythingServer.Core.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net9.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/samples/EverythingServer.Core/EverythingServerExtensions.cs b/samples/EverythingServer.Core/EverythingServerExtensions.cs
new file mode 100644
index 000000000..730c24db9
--- /dev/null
+++ b/samples/EverythingServer.Core/EverythingServerExtensions.cs
@@ -0,0 +1,149 @@
+using EverythingServer.Core.Prompts;
+using EverythingServer.Core.Resources;
+using EverythingServer.Core.Tools;
+using Microsoft.Extensions.AI;
+using Microsoft.Extensions.DependencyInjection;
+using ModelContextProtocol;
+using ModelContextProtocol.Protocol;
+using ModelContextProtocol.Server;
+using System.Collections.Concurrent;
+
+namespace EverythingServer.Core;
+
+///
+/// Extension methods for configuring the EverythingServer MCP handlers.
+///
+public static class EverythingServerExtensions
+{
+ ///
+ /// Adds all the tools, prompts, resources, and handlers for the EverythingServer.
+ ///
+ /// The MCP server builder.
+ /// The subscriptions dictionary to use for managing resource subscriptions.
+ /// The MCP server builder for chaining.
+ public static IMcpServerBuilder AddEverythingMcpHandlers(
+ this IMcpServerBuilder builder,
+ ConcurrentDictionary> subscriptions)
+ {
+ return builder
+ .WithTools()
+ .WithTools()
+ .WithTools()
+ .WithTools()
+ .WithTools()
+ .WithTools()
+ .WithTools()
+ .WithPrompts()
+ .WithPrompts()
+ .WithResources()
+ .WithSubscribeToResourcesHandler(async (ctx, ct) =>
+ {
+ var sessionId = ctx.Server.SessionId ?? "stdio";
+
+ if (!subscriptions.TryGetValue(sessionId, out var sessionSubscriptions))
+ {
+ sessionSubscriptions = new ConcurrentDictionary();
+ subscriptions[sessionId] = sessionSubscriptions;
+ }
+
+ if (ctx.Params?.Uri is { } uri)
+ {
+ sessionSubscriptions.TryAdd(uri, 0);
+
+ await ctx.Server.SampleAsync([
+ new ChatMessage(ChatRole.System, "You are a helpful test server"),
+ new ChatMessage(ChatRole.User, $"Resource {uri}, context: A new subscription was started"),
+ ],
+ options: new ChatOptions
+ {
+ MaxOutputTokens = 100,
+ Temperature = 0.7f,
+ },
+ cancellationToken: ct);
+ }
+
+ return new EmptyResult();
+ })
+ .WithUnsubscribeFromResourcesHandler(async (ctx, ct) =>
+ {
+ var sessionId = ctx.Server.SessionId ?? "stdio";
+
+ if (subscriptions.TryGetValue(sessionId, out var sessionSubscriptions))
+ {
+ if (ctx.Params?.Uri is { } uri)
+ {
+ sessionSubscriptions.TryRemove(uri, out _);
+ }
+ }
+ return new EmptyResult();
+ })
+ .WithCompleteHandler(async (ctx, ct) =>
+ {
+ var exampleCompletions = new Dictionary>
+ {
+ { "style", ["casual", "formal", "technical", "friendly"] },
+ { "temperature", ["0", "0.5", "0.7", "1.0"] },
+ { "resourceId", ["1", "2", "3", "4", "5"] }
+ };
+
+ if (ctx.Params is not { } @params)
+ {
+ throw new NotSupportedException($"Params are required.");
+ }
+
+ var @ref = @params.Ref;
+ var argument = @params.Argument;
+
+ if (@ref is ResourceTemplateReference rtr)
+ {
+ var resourceId = rtr.Uri?.Split("/").Last();
+
+ if (resourceId is null)
+ {
+ return new CompleteResult();
+ }
+
+ var values = exampleCompletions["resourceId"].Where(id => id.StartsWith(argument.Value));
+
+ return new CompleteResult
+ {
+ Completion = new Completion { Values = [.. values], HasMore = false, Total = values.Count() }
+ };
+ }
+
+ if (@ref is PromptReference pr)
+ {
+ if (!exampleCompletions.TryGetValue(argument.Name, out IEnumerable? value))
+ {
+ throw new NotSupportedException($"Unknown argument name: {argument.Name}");
+ }
+
+ var values = value.Where(value => value.StartsWith(argument.Value));
+ return new CompleteResult
+ {
+ Completion = new Completion { Values = [.. values], HasMore = false, Total = values.Count() }
+ };
+ }
+
+ throw new NotSupportedException($"Unknown reference type: {@ref.Type}");
+ })
+ .WithSetLoggingLevelHandler(async (ctx, ct) =>
+ {
+ if (ctx.Params?.Level is null)
+ {
+ throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
+ }
+
+ // The SDK updates the LoggingLevel field of the IMcpServer
+
+ await ctx.Server.SendNotificationAsync("notifications/message", new
+ {
+ Level = "debug",
+ Logger = "test-server",
+ Data = $"Logging level set to {ctx.Params.Level}",
+ }, cancellationToken: ct);
+
+ return new EmptyResult();
+ });
+ }
+}
diff --git a/samples/EverythingServer/LoggingUpdateMessageSender.cs b/samples/EverythingServer.Core/LoggingUpdateMessageSender.cs
similarity index 97%
rename from samples/EverythingServer/LoggingUpdateMessageSender.cs
rename to samples/EverythingServer.Core/LoggingUpdateMessageSender.cs
index f8c959e71..422166046 100644
--- a/samples/EverythingServer/LoggingUpdateMessageSender.cs
+++ b/samples/EverythingServer.Core/LoggingUpdateMessageSender.cs
@@ -3,7 +3,7 @@
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
-namespace EverythingServer;
+namespace EverythingServer.Core;
public class LoggingUpdateMessageSender(McpServer server) : BackgroundService
{
diff --git a/samples/EverythingServer/Prompts/ComplexPromptType.cs b/samples/EverythingServer.Core/Prompts/ComplexPromptType.cs
similarity index 91%
rename from samples/EverythingServer/Prompts/ComplexPromptType.cs
rename to samples/EverythingServer.Core/Prompts/ComplexPromptType.cs
index 8b47a07e6..d17bfa68d 100644
--- a/samples/EverythingServer/Prompts/ComplexPromptType.cs
+++ b/samples/EverythingServer.Core/Prompts/ComplexPromptType.cs
@@ -1,9 +1,9 @@
-using EverythingServer.Tools;
+using EverythingServer.Core.Tools;
using Microsoft.Extensions.AI;
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Prompts;
+namespace EverythingServer.Core.Prompts;
[McpServerPromptType]
public class ComplexPromptType
diff --git a/samples/EverythingServer/Prompts/SimplePromptType.cs b/samples/EverythingServer.Core/Prompts/SimplePromptType.cs
similarity index 88%
rename from samples/EverythingServer/Prompts/SimplePromptType.cs
rename to samples/EverythingServer.Core/Prompts/SimplePromptType.cs
index d6ba51a33..e849cabfb 100644
--- a/samples/EverythingServer/Prompts/SimplePromptType.cs
+++ b/samples/EverythingServer.Core/Prompts/SimplePromptType.cs
@@ -1,7 +1,7 @@
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Prompts;
+namespace EverythingServer.Core.Prompts;
[McpServerPromptType]
public class SimplePromptType
diff --git a/samples/EverythingServer.Core/README.md b/samples/EverythingServer.Core/README.md
new file mode 100644
index 000000000..b8f7e3d98
--- /dev/null
+++ b/samples/EverythingServer.Core/README.md
@@ -0,0 +1,42 @@
+# EverythingServer.Core
+
+This is the core library containing all the shared MCP server handlers (tools, prompts, resources) used by both the HTTP and stdio implementations of the EverythingServer sample.
+
+## What's Inside
+
+This library contains:
+
+- **Tools**: Various example tools demonstrating different MCP capabilities
+ - `AddTool`: Simple addition operation
+ - `AnnotatedMessageTool`: Returns annotated messages
+ - `EchoTool`: Echoes input back to the client
+ - `LongRunningTool`: Demonstrates long-running operations with progress reporting
+ - `PrintEnvTool`: Prints environment variables
+ - `SampleLlmTool`: Example LLM sampling integration
+ - `TinyImageTool`: Returns image content
+
+- **Prompts**: Example prompts showing argument handling
+ - `SimplePromptType`: Basic prompt example
+ - `ComplexPromptType`: Prompt with multiple arguments
+
+- **Resources**: Example resources with subscriptions
+ - `SimpleResourceType`: Dynamic resource with URI template matching
+
+- **Background Services**: For managing subscriptions and logging
+ - `SubscriptionMessageSender`: Sends periodic updates to subscribed resources
+ - `LoggingUpdateMessageSender`: Sends periodic logging messages at different levels
+
+- **Extension Method**: `AddEverythingMcpHandlers` to configure all handlers in one call
+
+## Usage
+
+Reference this project from your MCP server implementation (HTTP or stdio) and call the extension method:
+
+```csharp
+builder.Services
+ .AddMcpServer()
+ .WithHttpTransport() // or WithStdioServerTransport()
+ .AddEverythingMcpHandlers(subscriptions);
+```
+
+See the `EverythingServer.Http` and `EverythingServer.Stdio` projects for complete examples.
diff --git a/samples/EverythingServer/ResourceGenerator.cs b/samples/EverythingServer.Core/ResourceGenerator.cs
similarity index 97%
rename from samples/EverythingServer/ResourceGenerator.cs
rename to samples/EverythingServer.Core/ResourceGenerator.cs
index 524f35063..03fd69453 100644
--- a/samples/EverythingServer/ResourceGenerator.cs
+++ b/samples/EverythingServer.Core/ResourceGenerator.cs
@@ -1,6 +1,6 @@
using ModelContextProtocol.Protocol;
-namespace EverythingServer;
+namespace EverythingServer.Core;
static class ResourceGenerator
{
diff --git a/samples/EverythingServer/Resources/SimpleResourceType.cs b/samples/EverythingServer.Core/Resources/SimpleResourceType.cs
similarity index 97%
rename from samples/EverythingServer/Resources/SimpleResourceType.cs
rename to samples/EverythingServer.Core/Resources/SimpleResourceType.cs
index da185425f..7bf9c753a 100644
--- a/samples/EverythingServer/Resources/SimpleResourceType.cs
+++ b/samples/EverythingServer.Core/Resources/SimpleResourceType.cs
@@ -2,7 +2,7 @@
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Resources;
+namespace EverythingServer.Core.Resources;
[McpServerResourceType]
public class SimpleResourceType
diff --git a/samples/EverythingServer/SubscriptionMessageSender.cs b/samples/EverythingServer.Core/SubscriptionMessageSender.cs
similarity index 76%
rename from samples/EverythingServer/SubscriptionMessageSender.cs
rename to samples/EverythingServer.Core/SubscriptionMessageSender.cs
index 6cb895905..9f047ad51 100644
--- a/samples/EverythingServer/SubscriptionMessageSender.cs
+++ b/samples/EverythingServer.Core/SubscriptionMessageSender.cs
@@ -1,8 +1,11 @@
using System.Collections.Concurrent;
+using Microsoft.Extensions.Hosting;
using ModelContextProtocol;
using ModelContextProtocol.Server;
-internal class SubscriptionMessageSender(McpServer server, ConcurrentDictionary subscriptions) : BackgroundService
+namespace EverythingServer.Core;
+
+public class SubscriptionMessageSender(McpServer server, ConcurrentDictionary subscriptions) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
diff --git a/samples/EverythingServer/Tools/AddTool.cs b/samples/EverythingServer.Core/Tools/AddTool.cs
similarity index 87%
rename from samples/EverythingServer/Tools/AddTool.cs
rename to samples/EverythingServer.Core/Tools/AddTool.cs
index ccaa306d6..22b06e9ce 100644
--- a/samples/EverythingServer/Tools/AddTool.cs
+++ b/samples/EverythingServer.Core/Tools/AddTool.cs
@@ -1,7 +1,7 @@
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Tools;
+namespace EverythingServer.Core.Tools;
[McpServerToolType]
public class AddTool
diff --git a/samples/EverythingServer/Tools/AnnotatedMessageTool.cs b/samples/EverythingServer.Core/Tools/AnnotatedMessageTool.cs
similarity index 97%
rename from samples/EverythingServer/Tools/AnnotatedMessageTool.cs
rename to samples/EverythingServer.Core/Tools/AnnotatedMessageTool.cs
index 7f92d0ae1..6ea97aa87 100644
--- a/samples/EverythingServer/Tools/AnnotatedMessageTool.cs
+++ b/samples/EverythingServer.Core/Tools/AnnotatedMessageTool.cs
@@ -2,7 +2,7 @@
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Tools;
+namespace EverythingServer.Core.Tools;
[McpServerToolType]
public class AnnotatedMessageTool
diff --git a/samples/EverythingServer/Tools/EchoTool.cs b/samples/EverythingServer.Core/Tools/EchoTool.cs
similarity index 87%
rename from samples/EverythingServer/Tools/EchoTool.cs
rename to samples/EverythingServer.Core/Tools/EchoTool.cs
index 6abd6d363..425bd560f 100644
--- a/samples/EverythingServer/Tools/EchoTool.cs
+++ b/samples/EverythingServer.Core/Tools/EchoTool.cs
@@ -1,7 +1,7 @@
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Tools;
+namespace EverythingServer.Core.Tools;
[McpServerToolType]
public class EchoTool
diff --git a/samples/EverythingServer/Tools/LongRunningTool.cs b/samples/EverythingServer.Core/Tools/LongRunningTool.cs
similarity index 96%
rename from samples/EverythingServer/Tools/LongRunningTool.cs
rename to samples/EverythingServer.Core/Tools/LongRunningTool.cs
index 405b5e823..e24b8d529 100644
--- a/samples/EverythingServer/Tools/LongRunningTool.cs
+++ b/samples/EverythingServer.Core/Tools/LongRunningTool.cs
@@ -3,7 +3,7 @@
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Tools;
+namespace EverythingServer.Core.Tools;
[McpServerToolType]
public class LongRunningTool
diff --git a/samples/EverythingServer/Tools/PrintEnvTool.cs b/samples/EverythingServer.Core/Tools/PrintEnvTool.cs
similarity index 92%
rename from samples/EverythingServer/Tools/PrintEnvTool.cs
rename to samples/EverythingServer.Core/Tools/PrintEnvTool.cs
index ca289b5f3..657135bf1 100644
--- a/samples/EverythingServer/Tools/PrintEnvTool.cs
+++ b/samples/EverythingServer.Core/Tools/PrintEnvTool.cs
@@ -2,7 +2,7 @@
using System.ComponentModel;
using System.Text.Json;
-namespace EverythingServer.Tools;
+namespace EverythingServer.Core.Tools;
[McpServerToolType]
public class PrintEnvTool
diff --git a/samples/EverythingServer/Tools/SampleLlmTool.cs b/samples/EverythingServer.Core/Tools/SampleLlmTool.cs
similarity index 97%
rename from samples/EverythingServer/Tools/SampleLlmTool.cs
rename to samples/EverythingServer.Core/Tools/SampleLlmTool.cs
index 6bbe6e51d..d4a39d3cc 100644
--- a/samples/EverythingServer/Tools/SampleLlmTool.cs
+++ b/samples/EverythingServer.Core/Tools/SampleLlmTool.cs
@@ -2,7 +2,7 @@
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Tools;
+namespace EverythingServer.Core.Tools;
[McpServerToolType]
public class SampleLlmTool
diff --git a/samples/EverythingServer/Tools/TinyImageTool.cs b/samples/EverythingServer.Core/Tools/TinyImageTool.cs
similarity index 99%
rename from samples/EverythingServer/Tools/TinyImageTool.cs
rename to samples/EverythingServer.Core/Tools/TinyImageTool.cs
index bd88ce989..390242240 100644
--- a/samples/EverythingServer/Tools/TinyImageTool.cs
+++ b/samples/EverythingServer.Core/Tools/TinyImageTool.cs
@@ -2,7 +2,7 @@
using ModelContextProtocol.Server;
using System.ComponentModel;
-namespace EverythingServer.Tools;
+namespace EverythingServer.Core.Tools;
[McpServerToolType]
public class TinyImageTool
diff --git a/samples/EverythingServer/EverythingServer.csproj b/samples/EverythingServer.Http/EverythingServer.Http.csproj
similarity index 87%
rename from samples/EverythingServer/EverythingServer.csproj
rename to samples/EverythingServer.Http/EverythingServer.Http.csproj
index eadf720ca..75abfb542 100644
--- a/samples/EverythingServer/EverythingServer.csproj
+++ b/samples/EverythingServer.Http/EverythingServer.Http.csproj
@@ -15,6 +15,7 @@
+
diff --git a/samples/EverythingServer/EverythingServer.http b/samples/EverythingServer.Http/EverythingServer.http
similarity index 100%
rename from samples/EverythingServer/EverythingServer.http
rename to samples/EverythingServer.Http/EverythingServer.http
diff --git a/samples/EverythingServer.Http/Program.cs b/samples/EverythingServer.Http/Program.cs
new file mode 100644
index 000000000..3c7ddd120
--- /dev/null
+++ b/samples/EverythingServer.Http/Program.cs
@@ -0,0 +1,61 @@
+using EverythingServer.Core;
+using ModelContextProtocol.Server;
+using OpenTelemetry;
+using OpenTelemetry.Logs;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+using System.Collections.Concurrent;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Dictionary of session IDs to a set of resource URIs they are subscribed to
+// The value is a ConcurrentDictionary used as a thread-safe HashSet
+// because .NET does not have a built-in concurrent HashSet
+ConcurrentDictionary> subscriptions = new();
+
+builder.Services
+ .AddMcpServer()
+ .WithHttpTransport(options =>
+ {
+ // Add a RunSessionHandler to remove all subscriptions for the session when it ends
+ options.RunSessionHandler = async (httpContext, mcpServer, token) =>
+ {
+ if (mcpServer.SessionId == null)
+ {
+ // There is no sessionId if the serverOptions.Stateless is true
+ await mcpServer.RunAsync(token);
+ return;
+ }
+ try
+ {
+ subscriptions[mcpServer.SessionId] = new ConcurrentDictionary();
+ // Start an instance of SubscriptionMessageSender for this session
+ using var subscriptionSender = new SubscriptionMessageSender(mcpServer, subscriptions[mcpServer.SessionId]);
+ await subscriptionSender.StartAsync(token);
+ // Start an instance of LoggingUpdateMessageSender for this session
+ using var loggingSender = new LoggingUpdateMessageSender(mcpServer);
+ await loggingSender.StartAsync(token);
+ await mcpServer.RunAsync(token);
+ }
+ finally
+ {
+ // This code runs when the session ends
+ subscriptions.TryRemove(mcpServer.SessionId, out _);
+ }
+ };
+ })
+ .AddEverythingMcpHandlers(subscriptions);
+
+ResourceBuilder resource = ResourceBuilder.CreateDefault().AddService("everything-server");
+builder.Services.AddOpenTelemetry()
+ .WithTracing(b => b.AddSource("*").AddHttpClientInstrumentation().SetResourceBuilder(resource))
+ .WithMetrics(b => b.AddMeter("*").AddHttpClientInstrumentation().SetResourceBuilder(resource))
+ .WithLogging(b => b.SetResourceBuilder(resource))
+ .UseOtlpExporter();
+
+var app = builder.Build();
+
+app.MapMcp();
+
+app.Run();
diff --git a/samples/EverythingServer/Properties/launchSettings.json b/samples/EverythingServer.Http/Properties/launchSettings.json
similarity index 100%
rename from samples/EverythingServer/Properties/launchSettings.json
rename to samples/EverythingServer.Http/Properties/launchSettings.json
diff --git a/samples/EverythingServer.Http/README.md b/samples/EverythingServer.Http/README.md
new file mode 100644
index 000000000..0f8f6d17d
--- /dev/null
+++ b/samples/EverythingServer.Http/README.md
@@ -0,0 +1,47 @@
+# EverythingServer.Http
+
+HTTP-based MCP server demonstrating all MCP capabilities using ASP.NET Core.
+
+## Overview
+
+This is the HTTP transport implementation of the EverythingServer sample. It demonstrates how to build an MCP server that communicates over HTTP with support for:
+
+- Multiple concurrent sessions
+- Resource subscriptions with per-session tracking
+- Long-running operations with progress reporting
+- Sampling (LLM integration)
+- Logging level management
+- OpenTelemetry integration
+
+## Running the Server
+
+```bash
+dotnet run --project samples/EverythingServer.Http/EverythingServer.csproj
+```
+
+By default, the server runs on `http://localhost:5000` and `https://localhost:5001`.
+
+You can configure the port and other settings in `appsettings.json` or via command-line arguments:
+
+```bash
+dotnet run --project samples/EverythingServer.Http/EverythingServer.csproj --urls "http://localhost:3000"
+```
+
+## Testing the Server
+
+Use the included `EverythingServer.http` file with VS Code's REST Client extension or similar tools to test the endpoints.
+
+## Architecture
+
+This project uses:
+- `EverythingServer.Core` for shared MCP handlers
+- ASP.NET Core for HTTP hosting
+- `WithHttpTransport()` with `RunSessionHandler` for session management
+- Background services started per session for subscriptions and logging updates
+
+## Key Features
+
+- **Session Management**: Each HTTP session maintains its own subscription list
+- **Resource Subscriptions**: Clients can subscribe to resources and receive periodic updates
+- **Logging Messages**: Periodic logging messages at various levels based on server's current logging level
+- **Complete MCP Implementation**: All MCP capabilities demonstrated in one server
diff --git a/samples/EverythingServer/appsettings.Development.json b/samples/EverythingServer.Http/appsettings.Development.json
similarity index 100%
rename from samples/EverythingServer/appsettings.Development.json
rename to samples/EverythingServer.Http/appsettings.Development.json
diff --git a/samples/EverythingServer/appsettings.json b/samples/EverythingServer.Http/appsettings.json
similarity index 100%
rename from samples/EverythingServer/appsettings.json
rename to samples/EverythingServer.Http/appsettings.json
diff --git a/samples/EverythingServer.Stdio/EverythingServer.Stdio.csproj b/samples/EverythingServer.Stdio/EverythingServer.Stdio.csproj
new file mode 100644
index 000000000..30ac140e5
--- /dev/null
+++ b/samples/EverythingServer.Stdio/EverythingServer.Stdio.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net9.0
+ enable
+ enable
+ Exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/EverythingServer.Stdio/Program.cs b/samples/EverythingServer.Stdio/Program.cs
new file mode 100644
index 000000000..844bf781b
--- /dev/null
+++ b/samples/EverythingServer.Stdio/Program.cs
@@ -0,0 +1,50 @@
+using EverythingServer.Core;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using ModelContextProtocol.Server;
+using OpenTelemetry;
+using OpenTelemetry.Logs;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+using System.Collections.Concurrent;
+
+var builder = Host.CreateApplicationBuilder(args);
+
+// Dictionary of session IDs to a set of resource URIs they are subscribed to
+// The value is a ConcurrentDictionary used as a thread-safe HashSet
+// because .NET does not have a built-in concurrent HashSet
+// For stdio mode, we use a single "stdio" key since there's only one session
+ConcurrentDictionary> subscriptions = new();
+var stdioSubscriptions = new ConcurrentDictionary();
+subscriptions["stdio"] = stdioSubscriptions;
+
+builder.Services
+ .AddMcpServer()
+ .WithStdioServerTransport()
+ .AddEverythingMcpHandlers(subscriptions);
+
+// Register background services for stdio mode
+builder.Services.AddHostedService(sp => new SubscriptionMessageSender(
+ sp.GetRequiredService(),
+ stdioSubscriptions));
+builder.Services.AddHostedService(sp => new LoggingUpdateMessageSender(
+ sp.GetRequiredService()));
+
+// Configure logging to write to stderr to avoid interfering with MCP protocol on stdout
+builder.Logging.AddConsole(options =>
+{
+ options.LogToStandardErrorThreshold = LogLevel.Trace;
+});
+
+ResourceBuilder resource = ResourceBuilder.CreateDefault().AddService("everything-server");
+builder.Services.AddOpenTelemetry()
+ .WithTracing(b => b.AddSource("*").AddHttpClientInstrumentation().SetResourceBuilder(resource))
+ .WithMetrics(b => b.AddMeter("*").AddHttpClientInstrumentation().SetResourceBuilder(resource))
+ .WithLogging(b => b.SetResourceBuilder(resource))
+ .UseOtlpExporter();
+
+var app = builder.Build();
+
+await app.RunAsync();
diff --git a/samples/EverythingServer.Stdio/README.md b/samples/EverythingServer.Stdio/README.md
new file mode 100644
index 000000000..6d6d3b8aa
--- /dev/null
+++ b/samples/EverythingServer.Stdio/README.md
@@ -0,0 +1,73 @@
+# EverythingServer.Stdio
+
+Stdio-based MCP server demonstrating all MCP capabilities using standard input/output communication.
+
+## Overview
+
+This is the stdio transport implementation of the EverythingServer sample. It demonstrates how to build an MCP server that communicates via standard input/output (stdio), which is the most common way to integrate MCP servers with desktop applications and AI assistants.
+
+## Features
+
+The stdio server includes all the same capabilities as the HTTP version:
+
+- Tools (add, echo, long-running operations, etc.)
+- Prompts (simple and complex with arguments)
+- Resources with subscriptions
+- Sampling (LLM integration)
+- Logging level management
+- Progress reporting
+- OpenTelemetry integration
+
+## Running the Server
+
+```bash
+dotnet run --project samples/EverythingServer.Stdio/EverythingServer.Stdio.csproj
+```
+
+The server will read JSON-RPC messages from stdin and write responses to stdout. All diagnostic logging is sent to stderr to avoid interfering with the MCP protocol.
+
+## Using with MCP Clients
+
+To use this server with an MCP client, configure it to launch the executable:
+
+```json
+{
+ "mcpServers": {
+ "everything-server": {
+ "command": "dotnet",
+ "args": [
+ "run",
+ "--project",
+ "path/to/samples/EverythingServer.Stdio/EverythingServer.Stdio.csproj"
+ ]
+ }
+ }
+}
+```
+
+Or if you've published the application:
+
+```json
+{
+ "mcpServers": {
+ "everything-server": {
+ "command": "path/to/EverythingServer.Stdio"
+ }
+ }
+}
+```
+
+## Architecture
+
+This project uses:
+- `EverythingServer.Core` for shared MCP handlers
+- Microsoft.Extensions.Hosting for the host builder
+- `WithStdioServerTransport()` for stdio communication
+- Hosted background services for subscriptions and logging updates
+- Console logging configured to write to stderr
+
+## Key Differences from HTTP Version
+
+- **Single Session**: Stdio servers typically handle one session per process
+- **Hosted Services**: Background services are registered as hosted services instead of being started per-session
+- **Logging**: All logs go to stderr to keep stdout clean for MCP protocol messages
diff --git a/samples/EverythingServer/Program.cs b/samples/EverythingServer/Program.cs
deleted file mode 100644
index acaa7a37c..000000000
--- a/samples/EverythingServer/Program.cs
+++ /dev/null
@@ -1,180 +0,0 @@
-using EverythingServer;
-using EverythingServer.Prompts;
-using EverythingServer.Resources;
-using EverythingServer.Tools;
-using Microsoft.Extensions.AI;
-using ModelContextProtocol;
-using ModelContextProtocol.Protocol;
-using ModelContextProtocol.Server;
-using OpenTelemetry;
-using OpenTelemetry.Logs;
-using OpenTelemetry.Metrics;
-using OpenTelemetry.Resources;
-using OpenTelemetry.Trace;
-using System.Collections.Concurrent;
-
-var builder = WebApplication.CreateBuilder(args);
-
-// Dictionary of session IDs to a set of resource URIs they are subscribed to
-// The value is a ConcurrentDictionary used as a thread-safe HashSet
-// because .NET does not have a built-in concurrent HashSet
-ConcurrentDictionary> subscriptions = new();
-
-builder.Services
- .AddMcpServer()
- .WithHttpTransport(options =>
- {
- // Add a RunSessionHandler to remove all subscriptions for the session when it ends
- options.RunSessionHandler = async (httpContext, mcpServer, token) =>
- {
- if (mcpServer.SessionId == null)
- {
- // There is no sessionId if the serverOptions.Stateless is true
- await mcpServer.RunAsync(token);
- return;
- }
- try
- {
- subscriptions[mcpServer.SessionId] = new ConcurrentDictionary();
- // Start an instance of SubscriptionMessageSender for this session
- using var subscriptionSender = new SubscriptionMessageSender(mcpServer, subscriptions[mcpServer.SessionId]);
- await subscriptionSender.StartAsync(token);
- // Start an instance of LoggingUpdateMessageSender for this session
- using var loggingSender = new LoggingUpdateMessageSender(mcpServer);
- await loggingSender.StartAsync(token);
- await mcpServer.RunAsync(token);
- }
- finally
- {
- // This code runs when the session ends
- subscriptions.TryRemove(mcpServer.SessionId, out _);
- }
- };
- })
- .WithTools()
- .WithTools()
- .WithTools()
- .WithTools()
- .WithTools()
- .WithTools()
- .WithTools()
- .WithPrompts()
- .WithPrompts()
- .WithResources()
- .WithSubscribeToResourcesHandler(async (ctx, ct) =>
- {
- if (ctx.Server.SessionId == null)
- {
- throw new McpException("Cannot add subscription for server with null SessionId");
- }
- if (ctx.Params?.Uri is { } uri)
- {
- subscriptions[ctx.Server.SessionId].TryAdd(uri, 0);
-
- await ctx.Server.SampleAsync([
- new ChatMessage(ChatRole.System, "You are a helpful test server"),
- new ChatMessage(ChatRole.User, $"Resource {uri}, context: A new subscription was started"),
- ],
- options: new ChatOptions
- {
- MaxOutputTokens = 100,
- Temperature = 0.7f,
- },
- cancellationToken: ct);
- }
-
- return new EmptyResult();
- })
- .WithUnsubscribeFromResourcesHandler(async (ctx, ct) =>
- {
- if (ctx.Server.SessionId == null)
- {
- throw new McpException("Cannot remove subscription for server with null SessionId");
- }
- if (ctx.Params?.Uri is { } uri)
- {
- subscriptions[ctx.Server.SessionId].TryRemove(uri, out _);
- }
- return new EmptyResult();
- })
- .WithCompleteHandler(async (ctx, ct) =>
- {
- var exampleCompletions = new Dictionary>
- {
- { "style", ["casual", "formal", "technical", "friendly"] },
- { "temperature", ["0", "0.5", "0.7", "1.0"] },
- { "resourceId", ["1", "2", "3", "4", "5"] }
- };
-
- if (ctx.Params is not { } @params)
- {
- throw new NotSupportedException($"Params are required.");
- }
-
- var @ref = @params.Ref;
- var argument = @params.Argument;
-
- if (@ref is ResourceTemplateReference rtr)
- {
- var resourceId = rtr.Uri?.Split("/").Last();
-
- if (resourceId is null)
- {
- return new CompleteResult();
- }
-
- var values = exampleCompletions["resourceId"].Where(id => id.StartsWith(argument.Value));
-
- return new CompleteResult
- {
- Completion = new Completion { Values = [.. values], HasMore = false, Total = values.Count() }
- };
- }
-
- if (@ref is PromptReference pr)
- {
- if (!exampleCompletions.TryGetValue(argument.Name, out IEnumerable? value))
- {
- throw new NotSupportedException($"Unknown argument name: {argument.Name}");
- }
-
- var values = value.Where(value => value.StartsWith(argument.Value));
- return new CompleteResult
- {
- Completion = new Completion { Values = [.. values], HasMore = false, Total = values.Count() }
- };
- }
-
- throw new NotSupportedException($"Unknown reference type: {@ref.Type}");
- })
- .WithSetLoggingLevelHandler(async (ctx, ct) =>
- {
- if (ctx.Params?.Level is null)
- {
- throw new McpProtocolException("Missing required argument 'level'", McpErrorCode.InvalidParams);
- }
-
- // The SDK updates the LoggingLevel field of the IMcpServer
-
- await ctx.Server.SendNotificationAsync("notifications/message", new
- {
- Level = "debug",
- Logger = "test-server",
- Data = $"Logging level set to {ctx.Params.Level}",
- }, cancellationToken: ct);
-
- return new EmptyResult();
- });
-
-ResourceBuilder resource = ResourceBuilder.CreateDefault().AddService("everything-server");
-builder.Services.AddOpenTelemetry()
- .WithTracing(b => b.AddSource("*").AddHttpClientInstrumentation().SetResourceBuilder(resource))
- .WithMetrics(b => b.AddMeter("*").AddHttpClientInstrumentation().SetResourceBuilder(resource))
- .WithLogging(b => b.SetResourceBuilder(resource))
- .UseOtlpExporter();
-
-var app = builder.Build();
-
-app.MapMcp();
-
-app.Run();