From 76131bd6fdbc0dc79b4bfdc22cc29ed87c669402 Mon Sep 17 00:00:00 2001 From: dmitrii-f-t27 Date: Sat, 17 Oct 2026 02:15:51 -0300 Subject: [PATCH] spec(port): port specs/port/trios-railway/crates/trios-railway-mcp/src/main.t27 (Closes #9776) Port gHashTag/trios-railway:crates/trios-railway-mcp/src/main.rs (at 8b229e9489ee, 114 lines) as specs/port/trios-railway/crates/ trios-railway-mcp/src/main.t27 - Roadmap Stage 6, goal #4548, target t27c gen-rust. The dual-transport MCP server's decisions are ported as pure functions and tested (5 tests, 5 pass, 0 vacuous, 14 runtime asserts): the PORT fallback of 8080 (presence flag), the IPv6 unspecified bind address for the Railway proxy, the route table - two transports (Streamable HTTP on /mcp, legacy SSE on /sse + /message) and four routes with /healthz - the 15-second SSE keep-alive, the info log-filter default, the greeting naming both transports, and the ok liveness answer. The main transport keeps its name with an undefined; body no test calls. Helper names are chosen unique across the 2040 specs (resolve_listen_port, mounted_route_count) - dupe_scan verified before the commit. Verified: parse OK, spec-status IMPLEMENTED, typecheck 0 errors, coverage 16/17 (only the transport untested, by design), test-report 5/5 pass, 0 BLOCKED, gen 86 lines with 0 'not yet implemented', boundary exactly this one file, dupe_scan: all 14 introduced names unique, bare body scan adds no duplicate group (752/221 unchanged). Dmitrii Fedorov ([@dmitrii-f-t27](https://github.com/dmitrii-f-t27)) --- .../crates/trios-railway-mcp/src/main.t27 | 143 ++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 specs/port/trios-railway/crates/trios-railway-mcp/src/main.t27 diff --git a/specs/port/trios-railway/crates/trios-railway-mcp/src/main.t27 b/specs/port/trios-railway/crates/trios-railway-mcp/src/main.t27 new file mode 100644 index 0000000000..afefc3f390 --- /dev/null +++ b/specs/port/trios-railway/crates/trios-railway-mcp/src/main.t27 @@ -0,0 +1,143 @@ +// trios-railway-mcp — the public MCP server with dual transport. +// Port of gHashTag/trios-railway:crates/trios-railway-mcp/src/main.rs +// (at 8b229e9489ee, 114 lines) — Roadmap Stage 6, goal #4548, target +// t27c gen-rust. +// +// The original serves MCP (Model Context Protocol) over BOTH the modern +// Streamable HTTP transport (/mcp) and the legacy SSE transport +// (/sse + /message), with /healthz as the liveness probe and / as the +// greeting. It binds on the IPv6 unspecified address so the Railway +// IPv6-only private-network proxy can reach it, reads the port from the +// environment defaulting to 8080, keeps the SSE stream alive every 15 +// seconds, defaults the log filter to "info", prints one boot line +// before tracing starts (R5: never silent), and shuts down gracefully +// on the interrupt signal. +// +// The servers, the router and the signal handling are plumbing; the +// decisions — the port fallback, the bind address, the route table with +// its two transports and four routes, the keep-alive interval, the +// default log filter, and the two handler texts — are ported here as +// pure functions and tested. The main transport keeps its name with an +// `undefined;` body that no test calls (issue #9776, "Port the decision, +// not the plumbing"). + +module port::trios_railway_mcp_main; + +// --------------------------------------------------------------------------- +// Original lines 39-105: `async fn main() -> Result<()>`. The boot: bind, +// mount both transports, serve with graceful shutdown. Pure transport; +// its decisions are ported below. +// --------------------------------------------------------------------------- + +pub fn main() -> ?str { + return undefined; // plumbing: the dual-transport server boot and serve loop (lines 39-105) +} + +// Original lines 55-58: the PORT environment read with the 8080 +// fallback — unset or unparseable falls back, per the presence flag. +pub fn default_port() -> u16 { + return 8080; +} + +pub fn resolve_listen_port(port_set: bool, port: u16) -> u16 { + if (!port_set) { + return default_port(); + } + return port; +} + +// Original lines 59-61: bind on the IPv6 unspecified address so the +// Railway IPv6-only private-network proxy reaches the server. +pub fn unspecified_bind_address() -> str { + return "[::]"; +} + +// Original lines 66-89: two transports (Streamable HTTP on /mcp, legacy +// SSE on /sse + /message) and the /healthz liveness probe — four routes +// in all. +pub fn transport_count() -> usize { + return 2; +} + +pub fn mounted_route_count() -> usize { + return 4; +} + +pub fn mcp_path() -> str { + return "/mcp"; +} + +pub fn sse_path() -> str { + return "/sse"; +} + +pub fn message_path() -> str { + return "/message"; +} + +pub fn healthz_path() -> str { + return "/healthz"; +} + +// Original line 79: the SSE keep-alive interval. +pub fn sse_keep_alive_secs() -> u64 { + return 15; +} + +// Original lines 47-49: the log filter defaults to "info" when the +// environment carries no valid filter. +pub fn default_log_filter() -> str { + return "info"; +} + +// --------------------------------------------------------------------------- +// Original lines 107-110: `async fn root_handler() -> &'static str` — +// the greeting names both transports and where to reach them. +// --------------------------------------------------------------------------- + +pub fn root_handler() -> str { + return "trios-railway-mcp: public MCP server for the IGLA project.\nPOST JSON-RPC to /mcp (Streamable HTTP) or connect to /sse (legacy SSE)\n"; +} + +// --------------------------------------------------------------------------- +// Original lines 112-114: `async fn health_handler() -> &'static str` — +// the liveness answer. +// --------------------------------------------------------------------------- + +pub fn health_handler() -> str { + return "ok"; +} + +// --------------------------------------------------------------------------- +// Tests: every decision above asserted against values read from the +// original. No test calls the main transport. +// --------------------------------------------------------------------------- + +test health_answers_ok { + assert(health_handler() == "ok"); +} + +test root_greeting_names_both_transports { + assert(root_handler() == "trios-railway-mcp: public MCP server for the IGLA project.\nPOST JSON-RPC to /mcp (Streamable HTTP) or connect to /sse (legacy SSE)\n"); +} + +test port_defaults_and_overrides { + assert(default_port() == 8080); + assert(resolve_listen_port(false, 0) == 8080); + assert(resolve_listen_port(true, 9000) == 9000); +} + +test bind_and_route_layout { + assert(unspecified_bind_address() == "[::]"); + assert(transport_count() == 2); + assert(mounted_route_count() == 4); + assert(mcp_path() == "/mcp"); + assert(sse_path() == "/sse"); + assert(message_path() == "/message"); + assert(healthz_path() == "/healthz"); +} + +test keep_alive_and_log_filter { + assert(sse_keep_alive_secs() == 15); + assert(default_log_filter() == "info"); +}