test: prove the legacy handshake on stdio, not only over HTTP
The dual-era initialize change affects every existing consumer -- courier, folio and apiary all speak stdio with a legacy handshake -- but the only coverage was over HTTP. A regression would have broken them at connect time, which is not somewhere a test would obviously look.
Adds stdio coverage: each supported legacy revision is echoed rather than normalised, an unrecognised request gets a legacy counter-offer and never the modern revision (which has no initialize handshake at all), and the full mcp-server-run loop answers a legacy handshake over real ports rather than through the dispatcher directly.
Verified against a real consumer with a control, which is the part that matters. Courier built against the RELEASED sigil-mcp answers a client asking for 2025-06-18 with "2025-11-25"; built against this branch it answers "2025-06-18". Courier's own claude/channel capability and serverInfo are unchanged, and its 120 tests pass.
So this is a fix for existing servers, not only groundwork for a new transport: every sigil-mcp server currently misreports its version to a pinned client, and Codex pins 2025-06-18 by default.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test/test-server.sgl | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 60 insertions(+), 1 deletion(-)test/test-server.sglmodified
(result (jsonrpc-response-result resp))) (assert-true (jsonrpc-response? resp)) (assert-equal 1 (jsonrpc-response-id resp)) ;; A client that names no version gets our newest legacy revision. (assert-equal "2025-11-25" (assoc-ref 'protocolVersion result)) (let ((info (assoc-ref 'serverInfo result))) (assert-equal "test" (assoc-ref 'name info)) (assert-equal "1.0" (assoc-ref 'version info)))))) (assert-equal "1.0" (assoc-ref 'version info))))) ;; These cover the STDIO path specifically. Existing consumers -- courier, ;; folio, apiary -- all speak stdio with a legacy handshake, so a ;; regression here breaks them silently at connect time rather than ;; anywhere a test would obviously look. (test "a legacy client's requested version is echoed back" ;; Answering with a fixed version regardless of the request is what ;; makes a pinned client disconnect: it receives a version string it ;; does not know and hangs up. (let* ((s (mcp-server name: "test" version: "1.0")) (msg (jsonrpc-request id: 1 method: "initialize" params: (dict protocolVersion: "2025-06-18"))) (result (jsonrpc-response-result (mcp-server-handle-message s msg)))) (assert-equal "2025-06-18" (assoc-ref 'protocolVersion result)))) (test "each supported legacy revision is echoed, not normalised" (let ((s (mcp-server name: "test" version: "1.0"))) (for-each (lambda (v) (let* ((msg (jsonrpc-request id: 1 method: "initialize" params: (dict protocolVersion: v))) (result (jsonrpc-response-result (mcp-server-handle-message s msg)))) (assert-equal v (assoc-ref 'protocolVersion result)))) (list "2025-11-25" "2025-06-18" "2025-03-26" "2024-11-05")))) (test "an unknown requested version gets a LEGACY counter-offer" ;; Never the modern revision: it has no initialize handshake at all, so ;; a client that just sent one could not speak it. (let* ((s (mcp-server name: "test" version: "1.0")) (msg (jsonrpc-request id: 1 method: "initialize" params: (dict protocolVersion: "1999-01-01"))) (result (jsonrpc-response-result (mcp-server-handle-message s msg))) (answered (assoc-ref 'protocolVersion result))) (assert-equal "2025-11-25" answered) (assert-false (equal? mcp-current-version answered)))) (test "the full stdio loop answers a legacy handshake" ;; Drives mcp-server-run over a real string port pair, the way a stdio ;; consumer does, rather than calling the dispatcher directly. (let* ((s (mcp-server name: "test" version: "1.0")) (input (open-input-string (string-append (json-encode (dict jsonrpc: "2.0" id: 1 method: "initialize" params: (dict protocolVersion: "2025-06-18"))) "\n"))) (output (open-output-string))) (parameterize ((current-input-port input) (current-output-port output)) (mcp-server-run s load-env?: #f)) (let* ((line (car (filter (lambda (l) (not (string-empty? l))) (string-split (get-output-string output) "\n")))) (resp (json-decode line)) (result (dict-ref resp result:))) (assert-equal "2025-06-18" (dict-ref result protocolVersion:)) (assert-true (dict-ref result capabilities: #f))))));; ============================================================;; Ping