Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions specs/port/trios-railway/crates/trios-railway-mcp/src/main.t27
Original file line number Diff line number Diff line change
@@ -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");
}
Loading