From 2021981b872ce3ab6d420b97e106349657a5763f Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Tue, 15 Sep 2026 09:25:50 +0800 Subject: [PATCH 1/3] fix(oni-mcp): preserve legacy routing over future metadata --- mods/OniMcp/Server/McpHttpServerModernProtocol.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/mods/OniMcp/Server/McpHttpServerModernProtocol.cs b/mods/OniMcp/Server/McpHttpServerModernProtocol.cs index 6d4cb1ac..a60f794c 100644 --- a/mods/OniMcp/Server/McpHttpServerModernProtocol.cs +++ b/mods/OniMcp/Server/McpHttpServerModernProtocol.cs @@ -31,7 +31,15 @@ private bool TryHandleModernPost(HttpListenerRequest httpRequest, HttpListenerRe ? (string)meta["io.modelcontextprotocol/protocolVersion"] : null; - bool modernSignal = string.Equals(protocolVersion, ModernProtocolVersion, StringComparison.Ordinal) + bool explicitModern = string.Equals(protocolVersion, ModernProtocolVersion, StringComparison.Ordinal); + if (IsSupportedProtocolVersion(protocolVersion)) + return false; + + string sessionId = httpRequest.Headers["Mcp-Session-Id"]; + if (!explicitModern && IsSessionActive(sessionId)) + return false; + + bool modernSignal = explicitModern || string.Equals(metaVersion, ModernProtocolVersion, StringComparison.Ordinal); if (!modernSignal) return false; From 2dc41c3c3b95912c8fd82719145d1cd1a614e2ea Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Tue, 15 Sep 2026 09:26:16 +0800 Subject: [PATCH 2/3] test(oni-mcp): cover mixed-era routing precedence --- tests/OniMcp.ModernProtocol.Tests/Program.cs | 75 ++++++++++++++++++-- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/tests/OniMcp.ModernProtocol.Tests/Program.cs b/tests/OniMcp.ModernProtocol.Tests/Program.cs index fc0ee0c5..36321b6a 100644 --- a/tests/OniMcp.ModernProtocol.Tests/Program.cs +++ b/tests/OniMcp.ModernProtocol.Tests/Program.cs @@ -38,7 +38,7 @@ private static void Main() const string missingUri = "oni://missing-resource"; const string modernMeta = "\"_meta\":{\"io.modelcontextprotocol/protocolVersion\":\"2026-07-28\",\"io.modelcontextprotocol/clientCapabilities\":{},\"io.modelcontextprotocol/clientInfo\":{\"name\":\"sep-2164-regression\",\"version\":\"1.0\"}}"; string body = "{\"jsonrpc\":\"2.0\",\"method\":\"resources/read\",\"id\":2164,\"params\":{\"uri\":\"" + missingUri + "\"," + modernMeta + "}}"; - using (var response = PostModern(client, body, missingUri)) + using (var response = Post(client, body, null, "2026-07-28", "resources/read", missingUri)) { Assert(response.StatusCode == HttpStatusCode.OK, "Resource application error changed HTTP status"); JObject json = JObject.Parse(response.Content.ReadAsStringAsync().GetAwaiter().GetResult()); @@ -52,6 +52,63 @@ private static void Main() } Assert(server.GetSessionSummaries().Count == 0, "Modern resource error allocated legacy session state"); + + const string initialize = "{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"id\":1,\"params\":{\"protocolVersion\":\"2025-11-25\"}}"; + string sessionId; + using (var response = Post(client, initialize, null, "2025-11-25")) + { + Assert(response.StatusCode == HttpStatusCode.OK, "Legacy initialize failed"); + Assert(JObject.Parse(response.Content.ReadAsStringAsync().GetAwaiter().GetResult())["result"] != null, + "Legacy initialize returned no result"); + sessionId = response.Headers.GetValues("Mcp-Session-Id").Single(); + } + + const string futureMeta = "\"_meta\":{\"io.modelcontextprotocol/protocolVersion\":\"2026-07-28\",\"io.modelcontextprotocol/clientCapabilities\":{\"sampling\":{}},\"io.modelcontextprotocol/clientInfo\":{\"name\":\"mixed-era-regression\",\"version\":\"1.0\"}}"; + string legacyToolsList = "{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":2,\"params\":{" + futureMeta + "}}"; + + using (var response = Post(client, legacyToolsList, sessionId, "2025-11-25")) + { + Assert(response.StatusCode == HttpStatusCode.OK, + "Explicit legacy transport was diverted by future metadata"); + Assert(JObject.Parse(response.Content.ReadAsStringAsync().GetAwaiter().GetResult())["result"] != null, + "Explicit legacy transport did not reach legacy tools/list"); + Assert(response.Headers.GetValues("Mcp-Session-Id").Single() == sessionId, + "Explicit legacy transport lost its session identity"); + } + + using (var response = Post(client, legacyToolsList, sessionId)) + { + Assert(response.StatusCode == HttpStatusCode.OK, + "Established legacy session was diverted by future metadata"); + Assert(JObject.Parse(response.Content.ReadAsStringAsync().GetAwaiter().GetResult())["result"] != null, + "Established legacy session did not reach legacy tools/list"); + Assert(response.Headers.GetValues("Mcp-Protocol-Version").Single() == "2025-11-25", + "Established legacy session changed protocol era"); + } + + using (var response = Post(client, legacyToolsList, sessionId, "2026-07-28", "tools/list")) + { + Assert(response.StatusCode == HttpStatusCode.NotFound, + "Explicit modern transport was downgraded by a legacy session id"); + JObject json = JObject.Parse(response.Content.ReadAsStringAsync().GetAwaiter().GetResult()); + Assert((int)json["error"]["code"] == McpErrorCode.MethodNotFound, + "Explicit modern transport did not stay on the modern path"); + Assert(!response.Headers.Contains("Mcp-Session-Id"), + "Modern response reused a legacy session header"); + } + + string discover = "{\"jsonrpc\":\"2.0\",\"method\":\"server/discover\",\"id\":3,\"params\":{" + modernMeta + "}}"; + using (var response = Post(client, discover, null, null, "server/discover")) + { + Assert(response.StatusCode == HttpStatusCode.BadRequest, + "Headerless modern metadata did not reach modern validation"); + JObject json = JObject.Parse(response.Content.ReadAsStringAsync().GetAwaiter().GetResult()); + Assert((int)json["error"]["code"] == -32020, + "Headerless modern metadata fell back to legacy session validation"); + } + + Assert(server.GetSessionSummaries().Count == 1, + "Mixed-era regression changed legacy session ownership"); } } finally @@ -59,17 +116,23 @@ private static void Main() server.StopServer(); Invoke(_bridge, "OnDestroy"); } - Console.WriteLine("PASS SEP-2164 modern resource-not-found wire regression"); + Console.WriteLine("PASS modern resource and mixed-era routing wire regressions"); } - private static HttpResponseMessage PostModern(HttpClient client, string json, string name) + private static HttpResponseMessage Post(HttpClient client, string json, string sessionId = null, + string protocolVersion = null, string method = null, string name = null) { using (var request = new HttpRequestMessage(HttpMethod.Post, "")) { request.Content = new StringContent(json, Encoding.UTF8, "application/json"); - request.Headers.Add("Mcp-Protocol-Version", "2026-07-28"); - request.Headers.Add("Mcp-Method", "resources/read"); - request.Headers.Add("Mcp-Name", name); + if (sessionId != null) + request.Headers.Add("Mcp-Session-Id", sessionId); + if (protocolVersion != null) + request.Headers.Add("Mcp-Protocol-Version", protocolVersion); + if (method != null) + request.Headers.Add("Mcp-Method", method); + if (name != null) + request.Headers.Add("Mcp-Name", name); Task work = client.SendAsync(request); PumpUntil(work); return work.GetAwaiter().GetResult(); From bddc39e72fab7c3b0f5a6e91c41ba6ee75477227 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Tue, 15 Sep 2026 09:27:00 +0800 Subject: [PATCH 3/3] test(oni-mcp): add LINQ import for header assertions --- tests/OniMcp.ModernProtocol.Tests/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/OniMcp.ModernProtocol.Tests/Program.cs b/tests/OniMcp.ModernProtocol.Tests/Program.cs index 36321b6a..65e69b70 100644 --- a/tests/OniMcp.ModernProtocol.Tests/Program.cs +++ b/tests/OniMcp.ModernProtocol.Tests/Program.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Sockets;