diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 2746f0762..0ee6e9c65 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -3,7 +3,7 @@
 	"image": "mcr.microsoft.com/devcontainers/dotnet:1-8.0-jammy",
 	"features": {
 		"ghcr.io/devcontainers/features/dotnet:2": {
-			"version": "9.0"
+			"version": "10.0"
 		},
 		"ghcr.io/devcontainers/features/node:1": {}
 	},
diff --git a/.github/workflows/ci-build-test.yml b/.github/workflows/ci-build-test.yml
index b29bccd53..b6e7deefc 100644
--- a/.github/workflows/ci-build-test.yml
+++ b/.github/workflows/ci-build-test.yml
@@ -43,8 +43,8 @@ jobs:
       uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
       with:
         dotnet-version: |
+          10.0.x
           9.0.x
-          8.0.x
 
     # NetFX testing on non-Windows requires mono
     - name: Setup Mono
@@ -78,7 +78,6 @@ jobs:
         --filter '(Execution!=Manual)'
         --no-build
         --configuration ${{ matrix.configuration }}
-        --logger "console;verbosity=normal"
         --logger "trx"
         --logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true"
         --blame
diff --git a/.github/workflows/ci-code-coverage.yml b/.github/workflows/ci-code-coverage.yml
index dc86e65b9..55acbfa96 100644
--- a/.github/workflows/ci-code-coverage.yml
+++ b/.github/workflows/ci-code-coverage.yml
@@ -15,8 +15,8 @@ jobs:
         uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
         with:
           dotnet-version: |
+            10.0.x
             9.0.x
-            8.0.x
 
       - name: Download test results
         uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 866b5774d..b7e466115 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -32,7 +32,9 @@ jobs:
     - name: .NET Setup
       uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
       with:
-        dotnet-version: 9.x
+        dotnet-version: |
+          10.0.x
+          9.0.x
 
     - name: Generate documentation
       run: make generate-docs
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9ae26f8a0..f4e9301e2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -52,7 +52,9 @@ jobs:
     - name: Set up .NET
       uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
       with:
-        dotnet-version: 9.0.x
+        dotnet-version: |
+          10.0.x
+          9.0.x
 
     - name: Build
       run: dotnet build --configuration ${{ matrix.configuration }}
@@ -77,8 +79,8 @@ jobs:
         uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
         with:
           dotnet-version: |
+            10.0.x
             9.0.x
-            8.0.x
 
       - name: Pack
         run: dotnet pack
@@ -106,7 +108,7 @@ jobs:
       - name: Setup .NET
         uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
         with:
-          dotnet-version: 9.0.x
+          dotnet-version: 10.0.x
 
       - name: Download build artifacts
         uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
@@ -138,11 +140,6 @@ jobs:
     steps:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
 
-      - name: Setup .NET
-        uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
-        with:
-          dotnet-version: 9.0.x
-
       - name: Download build artifacts
         uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
 
@@ -166,7 +163,7 @@ jobs:
       - name: Setup .NET
         uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4.3.1
         with:
-          dotnet-version: 9.0.x
+          dotnet-version: 10.0.x
 
       - name: Download build artifacts
         uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 1ae45da31..2d8f454e8 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -34,6 +34,15 @@
     
   
 
+  
+  
+    
+    
+    
+    
+    
+  
+
   
   
     
diff --git a/global.json b/global.json
index 903111e2b..58c475f9f 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,7 @@
 {
   "sdk": {
-    "version": "9.0.204",
-    "rollForward": "minor"
+    "version": "10.0.100-rc.1",
+    "rollForward": "minor",
+    "allowPrerelease": true
   }
 }
diff --git a/samples/ProtectedMcpClient/Program.cs b/samples/ProtectedMcpClient/Program.cs
index 9dc2410ea..042d47713 100644
--- a/samples/ProtectedMcpClient/Program.cs
+++ b/samples/ProtectedMcpClient/Program.cs
@@ -134,6 +134,13 @@
 /// The URL to open.
 static void OpenBrowser(Uri url)
 {
+    // Validate the URI scheme - only allow safe protocols
+    if (url.Scheme != Uri.UriSchemeHttp && url.Scheme != Uri.UriSchemeHttps)
+    {
+        Console.WriteLine($"Error: Only HTTP and HTTPS URLs are allowed.");
+        return;
+    }
+
     try
     {
         var psi = new ProcessStartInfo
@@ -145,7 +152,7 @@ static void OpenBrowser(Uri url)
     }
     catch (Exception ex)
     {
-        Console.WriteLine($"Error opening browser. {ex.Message}");
+        Console.WriteLine($"Error opening browser: {ex.Message}");
         Console.WriteLine($"Please manually open this URL: {url}");
     }
 }
\ No newline at end of file
diff --git a/samples/TestServerWithHosting/TestServerWithHosting.csproj b/samples/TestServerWithHosting/TestServerWithHosting.csproj
index 2466a2811..7967395c0 100644
--- a/samples/TestServerWithHosting/TestServerWithHosting.csproj
+++ b/samples/TestServerWithHosting/TestServerWithHosting.csproj
@@ -2,7 +2,7 @@
 
   
     Exe
-    net9.0;net8.0;net472
+    net10.0;net9.0;net8.0;net472
     enable
     enable
     true
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 77e400e36..c8a04ae52 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -6,7 +6,7 @@
     https://github.com/modelcontextprotocol/csharp-sdk
     git
     0.4.0
-    preview.1
+    preview.2
     ModelContextProtocolOfficial
     © Anthropic and Contributors.
     ModelContextProtocol;mcp;ai;llm
diff --git a/src/ModelContextProtocol.AspNetCore/HttpMcpServerBuilderExtensions.cs b/src/ModelContextProtocol.AspNetCore/HttpMcpServerBuilderExtensions.cs
index fbceab4b1..313cbfa99 100644
--- a/src/ModelContextProtocol.AspNetCore/HttpMcpServerBuilderExtensions.cs
+++ b/src/ModelContextProtocol.AspNetCore/HttpMcpServerBuilderExtensions.cs
@@ -29,7 +29,6 @@ public static IMcpServerBuilder WithHttpTransport(this IMcpServerBuilder builder
         builder.Services.TryAddSingleton();
         builder.Services.TryAddSingleton();
         builder.Services.AddHostedService();
-        builder.Services.AddDataProtection();
 
         builder.Services.TryAddEnumerable(ServiceDescriptor.Transient, AuthorizationFilterSetup>());
 
diff --git a/src/ModelContextProtocol.AspNetCore/IdleTrackingBackgroundService.cs b/src/ModelContextProtocol.AspNetCore/IdleTrackingBackgroundService.cs
index a4ae569ba..d68f83e5d 100644
--- a/src/ModelContextProtocol.AspNetCore/IdleTrackingBackgroundService.cs
+++ b/src/ModelContextProtocol.AspNetCore/IdleTrackingBackgroundService.cs
@@ -4,16 +4,18 @@
 
 namespace ModelContextProtocol.AspNetCore;
 
-internal sealed partial class IdleTrackingBackgroundService(
-    StatefulSessionManager sessions,
-    IOptions options,
-    IHostApplicationLifetime appLifetime,
-    ILogger logger) : BackgroundService
+internal sealed partial class IdleTrackingBackgroundService : BackgroundService
 {
-    // Workaround for https://github.com/dotnet/runtime/issues/91121. This is fixed in .NET 9 and later.
-    private readonly ILogger _logger = logger;
+    private readonly StatefulSessionManager _sessions;
+    private readonly IOptions _options;
+    private readonly IHostApplicationLifetime _appLifetime;
+    private readonly ILogger _logger;
 
-    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+    public IdleTrackingBackgroundService(
+        StatefulSessionManager sessions,
+        IOptions options,
+        IHostApplicationLifetime appLifetime,
+        ILogger logger)
     {
         // Still run loop given infinite IdleTimeout to enforce the MaxIdleSessionCount and assist graceful shutdown.
         if (options.Value.IdleTimeout != Timeout.InfiniteTimeSpan)
@@ -23,14 +25,22 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
 
         ArgumentOutOfRangeException.ThrowIfLessThan(options.Value.MaxIdleSessionCount, 0);
 
+        _sessions = sessions;
+        _options = options;
+        _appLifetime = appLifetime;
+        _logger = logger;
+    }
+
+    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+    {
         try
         {
-            var timeProvider = options.Value.TimeProvider;
+            var timeProvider = _options.Value.TimeProvider;
             using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5), timeProvider);
 
             while (!stoppingToken.IsCancellationRequested && await timer.WaitForNextTickAsync(stoppingToken))
             {
-                await sessions.PruneIdleSessionsAsync(stoppingToken);
+                await _sessions.PruneIdleSessionsAsync(stoppingToken);
             }
         }
         catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
@@ -40,7 +50,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
         {
             try
             {
-                await sessions.DisposeAllSessionsAsync();
+                await _sessions.DisposeAllSessionsAsync();
             }
             finally
             {
@@ -48,7 +58,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
                 {
                     // Something went terribly wrong. A very unexpected exception must be bubbling up, but let's ensure we also stop the application,
                     // so that it hopefully gets looked at and restarted. This shouldn't really be reachable.
-                    appLifetime.StopApplication();
+                    _appLifetime.StopApplication();
                     IdleTrackingBackgroundServiceStoppedUnexpectedly();
                 }
             }
diff --git a/src/ModelContextProtocol.AspNetCore/Stateless/StatelessSessionId.cs b/src/ModelContextProtocol.AspNetCore/Stateless/StatelessSessionId.cs
deleted file mode 100644
index 0257f6d95..000000000
--- a/src/ModelContextProtocol.AspNetCore/Stateless/StatelessSessionId.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using ModelContextProtocol.Protocol;
-using System.Text.Json.Serialization;
-
-namespace ModelContextProtocol.AspNetCore.Stateless;
-
-internal sealed class StatelessSessionId
-{
-    [JsonPropertyName("clientInfo")]
-    public Implementation? ClientInfo { get; init; }
-
-    [JsonPropertyName("userIdClaim")]
-    public UserIdClaim? UserIdClaim { get; init; }
-}
diff --git a/src/ModelContextProtocol.AspNetCore/Stateless/StatelessSessionIdJsonContext.cs b/src/ModelContextProtocol.AspNetCore/Stateless/StatelessSessionIdJsonContext.cs
deleted file mode 100644
index 6963ed609..000000000
--- a/src/ModelContextProtocol.AspNetCore/Stateless/StatelessSessionIdJsonContext.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-using System.Text.Json.Serialization;
-
-namespace ModelContextProtocol.AspNetCore.Stateless;
-
-[JsonSerializable(typeof(StatelessSessionId))]
-internal sealed partial class StatelessSessionIdJsonContext : JsonSerializerContext;
diff --git a/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs b/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs
index 14093facc..70976d44f 100644
--- a/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs
+++ b/src/ModelContextProtocol.AspNetCore/StreamableHttpHandler.cs
@@ -1,16 +1,13 @@
-using Microsoft.AspNetCore.DataProtection;
-using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Http.Features;
 using Microsoft.AspNetCore.WebUtilities;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Options;
 using Microsoft.Net.Http.Headers;
-using ModelContextProtocol.AspNetCore.Stateless;
 using ModelContextProtocol.Protocol;
 using ModelContextProtocol.Server;
 using System.Security.Claims;
 using System.Security.Cryptography;
-using System.Text.Json;
 using System.Text.Json.Serialization.Metadata;
 
 namespace ModelContextProtocol.AspNetCore;
@@ -20,7 +17,6 @@ internal sealed class StreamableHttpHandler(
     IOptionsFactory mcpServerOptionsFactory,
     IOptions httpServerTransportOptions,
     StatefulSessionManager sessionManager,
-    IDataProtectionProvider dataProtection,
     ILoggerFactory loggerFactory,
     IServiceProvider applicationServices)
 {
@@ -31,8 +27,6 @@ internal sealed class StreamableHttpHandler(
 
     public HttpServerTransportOptions HttpServerTransportOptions => httpServerTransportOptions.Value;
 
-    private IDataProtector Protector { get; } = dataProtection.CreateProtector("Microsoft.AspNetCore.StreamableHttpHandler.StatelessSessionId");
-
     public async Task HandlePostRequestAsync(HttpContext context)
     {
         // The Streamable HTTP spec mandates the client MUST accept both application/json and text/event-stream.
@@ -128,17 +122,6 @@ public async Task HandleDeleteRequestAsync(HttpContext context)
             await WriteJsonRpcErrorAsync(context, "Bad Request: Mcp-Session-Id header is required", StatusCodes.Status400BadRequest);
             return null;
         }
-        else if (HttpServerTransportOptions.Stateless)
-        {
-            var sessionJson = Protector.Unprotect(sessionId);
-            var statelessSessionId = JsonSerializer.Deserialize(sessionJson, StatelessSessionIdJsonContext.Default.StatelessSessionId);
-            var transport = new StreamableHttpServerTransport
-            {
-                Stateless = true,
-                SessionId = sessionId,
-            };
-            session = await CreateSessionAsync(context, transport, sessionId, statelessSessionId);
-        }
         else if (!sessionManager.TryGetValue(sessionId, out session))
         {
             // -32001 isn't part of the MCP standard, but this is what the typescript-sdk currently does.
@@ -170,6 +153,13 @@ await WriteJsonRpcErrorAsync(context,
         {
             return await StartNewSessionAsync(context);
         }
+        else if (HttpServerTransportOptions.Stateless)
+        {
+            // In stateless mode, we should not be getting existing sessions via sessionId
+            // This path should not be reached in stateless mode
+            await WriteJsonRpcErrorAsync(context, "Bad Request: The Mcp-Session-Id header is not supported in stateless mode", StatusCodes.Status400BadRequest);
+            return null;
+        }
         else
         {
             return await GetSessionAsync(context, sessionId);
@@ -193,14 +183,12 @@ private async ValueTask StartNewSessionAsync(HttpContext
         }
         else
         {
-            // "(uninitialized stateless id)" is not written anywhere. We delay writing the MCP-Session-Id
-            // until after we receive the initialize request with the client info we need to serialize.
-            sessionId = "(uninitialized stateless id)";
+            // In stateless mode, each request is independent. Don't set any session ID on the transport.
+            sessionId = "";
             transport = new()
             {
                 Stateless = true,
             };
-            ScheduleStatelessSessionIdWrite(context, transport);
         }
 
         return await CreateSessionAsync(context, transport, sessionId);
@@ -209,21 +197,19 @@ private async ValueTask StartNewSessionAsync(HttpContext
     private async ValueTask CreateSessionAsync(
         HttpContext context,
         StreamableHttpServerTransport transport,
-        string sessionId,
-        StatelessSessionId? statelessId = null)
+        string sessionId)
     {
         var mcpServerServices = applicationServices;
         var mcpServerOptions = mcpServerOptionsSnapshot.Value;
-        if (statelessId is not null || HttpServerTransportOptions.ConfigureSessionOptions is not null)
+        if (HttpServerTransportOptions.Stateless || HttpServerTransportOptions.ConfigureSessionOptions is not null)
         {
             mcpServerOptions = mcpServerOptionsFactory.Create(Options.DefaultName);
 
-            if (statelessId is not null)
+            if (HttpServerTransportOptions.Stateless)
             {
                 // The session does not outlive the request in stateless mode.
                 mcpServerServices = context.RequestServices;
                 mcpServerOptions.ScopeRequests = false;
-                mcpServerOptions.KnownClientInfo = statelessId.ClientInfo;
             }
 
             if (HttpServerTransportOptions.ConfigureSessionOptions is { } configureSessionOptions)
@@ -235,7 +221,7 @@ private async ValueTask CreateSessionAsync(
         var server = McpServer.Create(transport, mcpServerOptions, loggerFactory, mcpServerServices);
         context.Features.Set(server);
 
-        var userIdClaim = statelessId?.UserIdClaim ?? GetUserIdClaim(context.User);
+        var userIdClaim = GetUserIdClaim(context.User);
         var session = new StreamableHttpSession(sessionId, transport, server, userIdClaim, sessionManager);
 
         var runSessionAsync = HttpServerTransportOptions.RunSessionHandler ?? RunSessionAsync;
@@ -273,7 +259,6 @@ internal static string MakeNewSessionId()
         RandomNumberGenerator.Fill(buffer);
         return WebEncoders.Base64UrlEncode(buffer);
     }
-
     internal static async Task ReadJsonRpcMessageAsync(HttpContext context)
     {
         // Implementation for reading a JSON-RPC message from the request body
@@ -290,22 +275,6 @@ internal static string MakeNewSessionId()
         return message;
     }
 
-    private void ScheduleStatelessSessionIdWrite(HttpContext context, StreamableHttpServerTransport transport)
-    {
-        transport.OnInitRequestReceived = initRequestParams =>
-        {
-            var statelessId = new StatelessSessionId
-            {
-                ClientInfo = initRequestParams?.ClientInfo,
-                UserIdClaim = GetUserIdClaim(context.User),
-            };
-
-            var sessionJson = JsonSerializer.Serialize(statelessId, StatelessSessionIdJsonContext.Default.StatelessSessionId);
-            transport.SessionId = Protector.Protect(sessionJson);
-            context.Response.Headers[McpSessionIdHeaderName] = transport.SessionId;
-            return ValueTask.CompletedTask;
-        };
-    }
 
     internal static Task RunSessionAsync(HttpContext httpContext, McpServer session, CancellationToken requestAborted)
         => session.RunAsync(requestAborted);
diff --git a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs
index 00fc0a7cc..0bae663ba 100644
--- a/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServer.Methods.cs
@@ -416,7 +416,7 @@ private void ThrowIfSamplingUnsupported()
     {
         if (ClientCapabilities?.Sampling is null)
         {
-            if (ServerOptions.KnownClientInfo is not null)
+            if (ClientCapabilities is null)
             {
                 throw new InvalidOperationException("Sampling is not supported in stateless mode.");
             }
@@ -429,7 +429,7 @@ private void ThrowIfRootsUnsupported()
     {
         if (ClientCapabilities?.Roots is null)
         {
-            if (ServerOptions.KnownClientInfo is not null)
+            if (ClientCapabilities is null)
             {
                 throw new InvalidOperationException("Roots are not supported in stateless mode.");
             }
@@ -442,7 +442,7 @@ private void ThrowIfElicitationUnsupported()
     {
         if (ClientCapabilities?.Elicitation is null)
         {
-            if (ServerOptions.KnownClientInfo is not null)
+            if (ClientCapabilities is null)
             {
                 throw new InvalidOperationException("Elicitation is not supported in stateless mode.");
             }
diff --git a/src/ModelContextProtocol.Core/Server/McpServerExtensions.cs b/src/ModelContextProtocol.Core/Server/McpServerExtensions.cs
index cd8c368a1..b20865576 100644
--- a/src/ModelContextProtocol.Core/Server/McpServerExtensions.cs
+++ b/src/ModelContextProtocol.Core/Server/McpServerExtensions.cs
@@ -24,7 +24,7 @@ public static class McpServerExtensions
     /// The client does not support sampling.
     /// 
     /// This method requires the client to support sampling capabilities.
-    /// It allows detailed control over sampling parameters including messages, system prompt, temperature, 
+    /// It allows detailed control over sampling parameters including messages, system prompt, temperature,
     /// and token limits.
     /// 
     [Obsolete($"Use {nameof(McpServer)}.{nameof(McpServer.SampleAsync)} instead. This member will be removed in a subsequent release.")] // See: https://github.com/modelcontextprotocol/csharp-sdk/issues/774
diff --git a/src/ModelContextProtocol.Core/Server/SseResponseStreamTransport.cs b/src/ModelContextProtocol.Core/Server/SseResponseStreamTransport.cs
index 8941e4ed6..98155213e 100644
--- a/src/ModelContextProtocol.Core/Server/SseResponseStreamTransport.cs
+++ b/src/ModelContextProtocol.Core/Server/SseResponseStreamTransport.cs
@@ -66,6 +66,7 @@ public async ValueTask DisposeAsync()
     public async Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default)
     {
         Throw.IfNull(message);
+        // If the underlying writer has been disposed, just drop the message.
         await _sseWriter.SendMessageAsync(message, cancellationToken).ConfigureAwait(false);
     }
 
diff --git a/src/ModelContextProtocol.Core/Server/SseWriter.cs b/src/ModelContextProtocol.Core/Server/SseWriter.cs
index 4fb7feafe..a2314e623 100644
--- a/src/ModelContextProtocol.Core/Server/SseWriter.cs
+++ b/src/ModelContextProtocol.Core/Server/SseWriter.cs
@@ -47,7 +47,7 @@ public Task WriteAllAsync(Stream sseResponseStream, CancellationToken cancellati
         return _writeTask;
     }
 
-    public async Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default)
+    public async Task SendMessageAsync(JsonRpcMessage message, CancellationToken cancellationToken = default)
     {
         Throw.IfNull(message);
 
@@ -55,14 +55,14 @@ public async Task SendMessageAsync(JsonRpcMessage message, CancellationToken can
 
         if (_disposed)
         {
-            // Don't throw an ODE, because this is disposed internally when the transport disconnects due to an abort
-            // or sending all the responses for the a give given Streamable HTTP POST request, so the user might not be at fault.
-            // There's precedence for no-oping here similar to writing to the response body of an aborted request in ASP.NET Core.
-            return;
+            // Don't throw ObjectDisposedException here; just return false to indicate the message wasn't sent.
+            // The calling transport can determine what to do in this case (drop the message, or fall back to another transport).
+            return false;
         }
 
         // Emit redundant "event: message" lines for better compatibility with other SDKs.
         await _messages.Writer.WriteAsync(new SseItem(message, SseParser.EventTypeDefault), cancellationToken).ConfigureAwait(false);
+        return true;
     }
 
     public async ValueTask DisposeAsync()
diff --git a/src/ModelContextProtocol.Core/Server/StreamableHttpPostTransport.cs b/src/ModelContextProtocol.Core/Server/StreamableHttpPostTransport.cs
index 1992939de..1109c2b2b 100644
--- a/src/ModelContextProtocol.Core/Server/StreamableHttpPostTransport.cs
+++ b/src/ModelContextProtocol.Core/Server/StreamableHttpPostTransport.cs
@@ -72,7 +72,13 @@ public async Task SendMessageAsync(JsonRpcMessage message, CancellationToken can
             throw new InvalidOperationException("Server to client requests are not supported in stateless mode.");
         }
 
-        await _sseWriter.SendMessageAsync(message, cancellationToken).ConfigureAwait(false);
+        bool isAccepted = await _sseWriter.SendMessageAsync(message, cancellationToken).ConfigureAwait(false);
+        if (!isAccepted)
+        {
+            // The underlying writer didn't accept the message because the underlying request has completed.
+            // Rather than drop the message, fall back to sending it via the parent transport.
+            await parentTransport.SendMessageAsync(message, cancellationToken).ConfigureAwait(false);
+        }
     }
 
     public async ValueTask DisposeAsync()
diff --git a/src/ModelContextProtocol.Core/Server/StreamableHttpServerTransport.cs b/src/ModelContextProtocol.Core/Server/StreamableHttpServerTransport.cs
index 57283e9a2..4bbb49be9 100644
--- a/src/ModelContextProtocol.Core/Server/StreamableHttpServerTransport.cs
+++ b/src/ModelContextProtocol.Core/Server/StreamableHttpServerTransport.cs
@@ -131,6 +131,7 @@ public async Task SendMessageAsync(JsonRpcMessage message, CancellationToken can
             throw new InvalidOperationException("Unsolicited server to client messages are not supported in stateless mode.");
         }
 
+        // If the underlying writer has been disposed, just drop the message.
         await _sseWriter.SendMessageAsync(message, cancellationToken).ConfigureAwait(false);
     }
 
diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
index 1d27a219e..5e3a654f9 100644
--- a/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
+++ b/tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs
@@ -54,8 +54,10 @@ public async Task Connect_TestServer_ShouldProvideServerFields()
         Assert.NotNull(client.ServerInfo);
         Assert.NotNull(client.NegotiatedProtocolVersion);
 
-        if (ClientTransportOptions.Endpoint.AbsolutePath.EndsWith("/sse"))
+        if (ClientTransportOptions.Endpoint.AbsolutePath.EndsWith("/sse") ||
+            ClientTransportOptions.Endpoint.AbsolutePath.EndsWith("/stateless"))
         {
+            // In SSE and in Streamable HTTP's stateless mode, no protocol-defined session IDs are used.:w
             Assert.Null(client.SessionId);
         }
         else
diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpStreamableHttpTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpStreamableHttpTests.cs
index f8b61aa21..0e953e4d7 100644
--- a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpStreamableHttpTests.cs
+++ b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpStreamableHttpTests.cs
@@ -158,7 +158,7 @@ public async Task StreamableHttpClient_SendsMcpProtocolVersionHeader_AfterInitia
         {
             return async context =>
             {
-                if (!StringValues.IsNullOrEmpty(context.Request.Headers["mcp-session-id"]))
+                if (!StringValues.IsNullOrEmpty(context.Request.Headers["mcp-protocol-version"]))
                 {
                     protocolVersionHeaderValues.Add(context.Request.Headers["mcp-protocol-version"]);
                 }
@@ -179,8 +179,11 @@ public async Task StreamableHttpClient_SendsMcpProtocolVersionHeader_AfterInitia
         Assert.Equal("2025-03-26", mcpClient.NegotiatedProtocolVersion);
         await mcpClient.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
 
+        await mcpClient.DisposeAsync();
+
         // The header should be included in the GET request, the initialized notification, the tools/list call, and the delete request.
-        Assert.NotEmpty(protocolVersionHeaderValues);
+        // The DELETE request won't be sent for Stateless mode due to the lack of an Mcp-Session-Id.
+        Assert.Equal(Stateless ? 3 : 4, protocolVersionHeaderValues.Count);
         Assert.All(protocolVersionHeaderValues, v => Assert.Equal("2025-03-26", v));
     }
 }
diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs
index 341171c51..d7b9eaa01 100644
--- a/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs
+++ b/tests/ModelContextProtocol.AspNetCore.Tests/MapMcpTests.cs
@@ -85,6 +85,8 @@ public async Task Can_UseIHttpContextAccessor_InTool()
     [Fact]
     public async Task Messages_FromNewUser_AreRejected()
     {
+        Assert.SkipWhen(Stateless, "User validation across requests is not applicable in stateless mode.");
+
         Builder.Services.AddMcpServer().WithHttpTransport(ConfigureStateless).WithTools();
 
         // Add an authentication scheme that will send a 403 Forbidden response.
diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/ModelContextProtocol.AspNetCore.Tests.csproj b/tests/ModelContextProtocol.AspNetCore.Tests/ModelContextProtocol.AspNetCore.Tests.csproj
index 34801c736..7adeb03d3 100644
--- a/tests/ModelContextProtocol.AspNetCore.Tests/ModelContextProtocol.AspNetCore.Tests.csproj
+++ b/tests/ModelContextProtocol.AspNetCore.Tests/ModelContextProtocol.AspNetCore.Tests.csproj
@@ -1,7 +1,7 @@
 
 
   
-    net9.0;net8.0
+    net10.0;net9.0;net8.0
     enable
     enable
     false
diff --git a/tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs b/tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs
index 7b2be8f98..5cc7f74d8 100644
--- a/tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs
+++ b/tests/ModelContextProtocol.AspNetCore.Tests/StreamableHttpServerConformanceTests.cs
@@ -521,6 +521,41 @@ public async Task IdleSessionsPastMaxIdleSessionCount_ArePruned_LongestIdleFirst
         Assert.StartsWith("MaxIdleSessionCount of 2 exceeded. Closing idle session", idleLimitLogMessage.Message);
     }
 
+    [Fact]
+    public async Task McpServer_UsedOutOfScope_CanSendNotifications()
+    {
+        McpServer? capturedServer = null;
+        Builder.Services.AddMcpServer()
+            .WithHttpTransport()
+            .WithListResourcesHandler((_, _) => ValueTask.FromResult(new ListResourcesResult()))
+            .WithSubscribeToResourcesHandler((context, token) =>
+            {
+                capturedServer = context.Server;
+                return ValueTask.FromResult(new EmptyResult());
+            });
+
+        await StartAsync();
+
+        string sessionId = await CallInitializeAndValidateAsync();
+        SetSessionId(sessionId);
+
+        // Call the subscribe method to capture the McpServer instance.
+        using var response = await HttpClient.PostAsync("", JsonContent(Request("resources/subscribe")), TestContext.Current.CancellationToken);
+        var rpcResponse = await AssertSingleSseResponseAsync(response);
+        AssertType(rpcResponse.Result);
+        Assert.NotNull(capturedServer);
+
+        // Check the captured McpServer instance can send a notification.
+        await capturedServer.SendNotificationAsync(NotificationMethods.ResourceUpdatedNotification, TestContext.Current.CancellationToken);
+        using var getResponse = await HttpClient.GetAsync("", HttpCompletionOption.ResponseHeadersRead, TestContext.Current.CancellationToken);
+        JsonRpcMessage? firstSseMessage = await ReadSseAsync(getResponse.Content)
+            .Select(data => JsonSerializer.Deserialize(data, McpJsonUtilities.DefaultOptions))
+            .FirstOrDefaultAsync(TestContext.Current.CancellationToken);
+
+        var notification = Assert.IsType(firstSseMessage);
+        Assert.Equal(NotificationMethods.ResourceUpdatedNotification, notification.Method);
+    }
+
     private static StringContent JsonContent(string json) => new StringContent(json, Encoding.UTF8, "application/json");
     private static JsonTypeInfo GetJsonTypeInfo() => (JsonTypeInfo)McpJsonUtilities.DefaultOptions.GetTypeInfo(typeof(T));
 
diff --git a/tests/ModelContextProtocol.TestOAuthServer/ModelContextProtocol.TestOAuthServer.csproj b/tests/ModelContextProtocol.TestOAuthServer/ModelContextProtocol.TestOAuthServer.csproj
index 51092f564..9ea927474 100644
--- a/tests/ModelContextProtocol.TestOAuthServer/ModelContextProtocol.TestOAuthServer.csproj
+++ b/tests/ModelContextProtocol.TestOAuthServer/ModelContextProtocol.TestOAuthServer.csproj
@@ -1,7 +1,7 @@
 
 
   
-    net9.0;net8.0
+    net10.0;net9.0;net8.0
     enable
     enable
   
diff --git a/tests/ModelContextProtocol.TestServer/ModelContextProtocol.TestServer.csproj b/tests/ModelContextProtocol.TestServer/ModelContextProtocol.TestServer.csproj
index f38a35859..c4b39bb54 100644
--- a/tests/ModelContextProtocol.TestServer/ModelContextProtocol.TestServer.csproj
+++ b/tests/ModelContextProtocol.TestServer/ModelContextProtocol.TestServer.csproj
@@ -2,7 +2,7 @@
 
   
     Exe
-    net9.0;net8.0;net472
+    net10.0;net9.0;net8.0;net472
     enable
     enable
     TestServer
diff --git a/tests/ModelContextProtocol.TestSseServer/ModelContextProtocol.TestSseServer.csproj b/tests/ModelContextProtocol.TestSseServer/ModelContextProtocol.TestSseServer.csproj
index f56d0db9e..3296ff481 100644
--- a/tests/ModelContextProtocol.TestSseServer/ModelContextProtocol.TestSseServer.csproj
+++ b/tests/ModelContextProtocol.TestSseServer/ModelContextProtocol.TestSseServer.csproj
@@ -2,7 +2,7 @@
 
   
     Exe
-    net9.0;net8.0
+    net10.0;net9.0;net8.0
     enable
     enable
     TestSseServer
diff --git a/tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj b/tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj
index 993564bf0..3c7d631ad 100644
--- a/tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj
+++ b/tests/ModelContextProtocol.Tests/ModelContextProtocol.Tests.csproj
@@ -2,13 +2,15 @@
 
   
     Exe
-    net9.0;net8.0;net472
+    net10.0;net9.0;net8.0;net472
     enable
     enable
 
     false
     true
     ModelContextProtocol.Tests
+    
+    $(NoWarn);NU1903;NU1902