feat(fast-time): rmcp /mcp transport + legacy SSE shim#5299
Merged
Conversation
fa8d028 to
1d5babc
Compare
1d5babc to
5b32e6a
Compare
ja8zyjits
previously approved these changes
Jun 26, 2026
ja8zyjits
left a comment
Collaborator
There was a problem hiding this comment.
LGTM
only changes to an mcp-server low impact on core CF.
d3ff2cf to
0760b2f
Compare
12 tasks
Signed-off-by: lucarlig <luca.carlig@ibm.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
- Echo the client's requested protocolVersion when supported, else the latest version this server speaks (both SSE and streamable HTTP). - Track last-seen time per streamable-HTTP session and evict idle ones past SESSION_IDLE_TTL instead of leaking until the cap is reached. - get_stats now counts its own invocation like every other tool, and the unreachable session-id header fallback is dropped. Signed-off-by: lucarlig <luca.carlig@ibm.com>
…ools Replace the hand-rolled MCP layer with the official Rust SDK (rmcp): - /mcp Streamable HTTP transport is now served by rmcp's StreamableHttpService + LocalSessionManager, removing the bespoke session map and JSON-RPC dispatch (streamable_http.rs). A lightweight gate caps concurrent sessions at MAX_ACTIVE_SESSIONS (rmcp's session manager has no built-in cap); existing sessions are cleaned up by rmcp on disconnect/DELETE. - Tools are declared once via #[tool] macros with schemas derived from their parameter types, removing the hand-written tool schemas and dispatch (mcp.rs). The schema_error/schema_success fixtures retain their declared outputSchema. - The legacy HTTP+SSE transport remains a hand-rolled shim (documented exception) because rmcp does not reproduce the bespoke ContextForge legacy-SSE semantics (endpoint event, sessionId/session_id aliases, 202/404/410 codes). It delegates tools/list and tools/call back to the rmcp server so schemas and logic never drift. - serverInfo is built explicitly so /mcp reports the server identity rather than rmcp's own crate name. Behavior parity (DST conversion, echo delay limit, protocol-version echo, output schemas) is covered by unit tests; both transports verified end-to-end. Signed-off-by: lucarlig <luca.carlig@ibm.com>
Port the Go fast-time-server's resource and prompt surface to the Rust server so it advertises the same MCP capabilities: - 4 resources: timezone://info, time://current/world, time://formats, time://business-hours. - 3 prompts: compare_timezones, schedule_meeting, convert_time_detailed. - Advertise resources + prompts capabilities; add readOnly / idempotent / title annotations to get_system_time and convert_time. Resources and prompts are shared by both the /mcp transport (rmcp ServerHandler) and the legacy SSE shim (resources/list, resources/read, prompts/list, prompts/get). Signed-off-by: lucarlig <luca.carlig@ibm.com>
Bring the Rust server up to the Go transport/CLI surface: - clap CLI: --transport (stdio|sse|http|dual|rest), --addr/--listen/--port, --public-url, --auth-token, --log-level. With no arguments it still serves every HTTP route on BIND_ADDRESS (default 0.0.0.0:9080), preserving the container/Makefile behavior; the HTTP modes all serve the full router. - stdio transport via rmcp serve(), for `--transport stdio`. - Bearer-token auth middleware for the HTTP transports, exempting /health and /version, mirroring the Go authMiddleware (401 + WWW-Authenticate). - Send tracing logs to stderr so the stdio transport keeps stdout as a pure JSON-RPC stream. Signed-off-by: lucarlig <luca.carlig@ibm.com>
Port the Go fast-time-server's REST API to the Rust server, alongside the
existing high-throughput /api/echo and /api/time benchmark endpoints:
- /api/v1/time (+ /{tz}), /api/v1/convert, /api/v1/convert/batch
- /api/v1/timezones (+ /{tz}), /api/v1/resources (+ /{slug}),
/api/v1/prompts (+ /{name}/execute)
- /api/v1/test/echo, /api/v1/test/validate, /api/v1/test/performance
- permissive CORS with a 204 preflight, applied as the outermost layer
The REST resources/prompts mirror the Go REST handlers' simpler payloads,
which differ from the richer MCP resource/prompt content.
Signed-off-by: lucarlig <luca.carlig@ibm.com>
Serve /api/v1/openapi.json (an OpenAPI 3.0 document describing the REST surface) and /api/v1/docs (a Swagger UI page), matching the Go server. Signed-off-by: lucarlig <luca.carlig@ibm.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
Signed-off-by: lucarlig <luca.carlig@ibm.com>
17c9920 to
9499be9
Compare
ja8zyjits
requested changes
Jun 29, 2026
ja8zyjits
left a comment
Collaborator
There was a problem hiding this comment.
this and the sql-sanitizer.yaml changes.
Iam not sure if that is needed here?
Signed-off-by: lucarlig <luca.carlig@ibm.com>
9911180 to
74cc7bd
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
✨ Feature / Enhancement PR
🔗 Epic / Issue
Not linked.
🚀 Summary (1-2 sentences)
Move the Rust fast-time server to the
rmcp-backed Streamable HTTP implementation while preserving the legacy SSE and Go-compatible REST surfaces. Wire compose to the Rust server and add a GHCR publisher forghcr.io/ibm/fast-time-server.📏 Reviewability
triage(no linked issue)🧪 Checks
Rust fast-time server
cargo fmt -p fast-time-server --checkcargo test -p fast-time-servercargo clippy -p fast-time-server --all-targets -- -D warningscargo build -p fast-time-server200; unlisted host returns403/health,/version, REST timezone lookup, Streamable HTTP initialize, and legacy SSE responseWorkflows and repo hygiene
actionlint .github/workflows/fast-time-server-image.ymlactionlint .github/workflows/wrapper.yml .github/workflows/sql-sanitizer.yml .github/workflows/fast-time-server-image.ymlmake detect-secrets-scanmake pre-commitgit diff --checkCompose and deployment surfaces
:9080;/healthand/versionreturn200make testing-upcompose smoke with constrained gateway resources; verified gateway, fast-time, auth, and registration paths;make testing-downcompleted/tmp/docker-compose.perf.ymlfromtests/performance/config.yaml;docker compose config --quietpasseddocker compose -f docker-compose.yml config --quietdocker compose -f docker-compose-verbose-logging.yml config --quietdocker compose -f docker-compose-debug.yml config --quietdocker compose -f docker-compose-performance.yml config --quiethelm template mcp-stack charts/mcp-stackfast_time_server,slow_time_server, andbenchmark_serverin main, debug, verbose-logging, and performance compose filesRust server container images
linux/arm64linux/amd64mcpuserand includeca-certificatespluscurl/health, agent card, and JSON-RPCSendMessage/healthandtools/list/health,/version, REST timezone lookup, and Streamable HTTP initialize/healthandtools/listFull-suite status
make lint(not run; scoped lint/checks above ran instead)make test(not run; scoped tests above ran instead)📓 Notes (optional)
/sse,/messages, and/messagepaths stay as a hand-rolled compatibility shim becausermcpStreamable HTTP does not reproduce the existing ContextForge SSE endpoint semantics.fast_time_serverin compose defaults to the published GHCR image and still supports local overrides withFAST_TIME_IMAGE.MCP_ALLOWED_HOSTS/-allowed-hosts, with compose defaults and-public-urlhost support.sql-sanitizer E2Enow starts the Rust fast-time server on:9080, matching the workflow's existing health probes and gateway registration URL.