Skip to content

feat(fast-time): rmcp /mcp transport + legacy SSE shim#5299

Merged
Lang-Akshay merged 15 commits into
mainfrom
user/luca/rust-fast-time-sse
Jun 29, 2026
Merged

feat(fast-time): rmcp /mcp transport + legacy SSE shim#5299
Lang-Akshay merged 15 commits into
mainfrom
user/luca/rust-fast-time-sse

Conversation

@lucarlig

@lucarlig lucarlig commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

✨ 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 for ghcr.io/ibm/fast-time-server.


📏 Reviewability

  • This PR has one clear purpose
  • The linked issue is not labeled triage (no linked issue)
  • Unrelated bugs or improvements are tracked in separate issues/PRs
  • Tests are included with the code they validate

🧪 Checks

Rust fast-time server

  • cargo fmt -p fast-time-server --check
  • cargo test -p fast-time-server
  • cargo clippy -p fast-time-server --all-targets -- -D warnings
  • cargo build -p fast-time-server
  • Runtime host allowlist smoke: allowed host returns 200; unlisted host returns 403
  • Built-container dual-mode smoke: /health, /version, REST timezone lookup, Streamable HTTP initialize, and legacy SSE response
  • Built-container SSE-only smoke with generated performance-compose arguments

Workflows and repo hygiene

  • actionlint .github/workflows/fast-time-server-image.yml
  • actionlint .github/workflows/wrapper.yml .github/workflows/sql-sanitizer.yml .github/workflows/fast-time-server-image.yml
  • make detect-secrets-scan
  • make pre-commit
  • git diff --check
  • Fast-time reference sweep: active runtime, CI, and docs entrypoints now point to Rust; remaining Go references are legacy Go source/docs

Compose and deployment surfaces

  • SQL sanitizer startup smoke: Rust fast-time starts on :9080; /health and /version return 200
  • Small local make testing-up compose smoke with constrained gateway resources; verified gateway, fast-time, auth, and registration paths; make testing-down completed
  • Performance compose generator smoke: generated /tmp/docker-compose.perf.yml from tests/performance/config.yaml; docker compose config --quiet passed
  • docker compose -f docker-compose.yml config --quiet
  • docker compose -f docker-compose-verbose-logging.yml config --quiet
  • docker compose -f docker-compose-debug.yml config --quiet
  • docker compose -f docker-compose-performance.yml config --quiet
  • helm template mcp-stack charts/mcp-stack
  • Compose service builds for fast_time_server, slow_time_server, and benchmark_server in main, debug, verbose-logging, and performance compose files

Rust server container images

  • All changed Rust server image definitions build on native linux/arm64
  • All changed Rust server image definitions cross-build for linux/amd64
  • Built images run as non-root mcpuser and include ca-certificates plus curl
  • A2A echo container smoke: /health, agent card, and JSON-RPC SendMessage
  • Benchmark container smoke for both Dockerfile and Containerfile: /health and tools/list
  • Fast-time container smoke for both Dockerfile and Containerfile: /health, /version, REST timezone lookup, and Streamable HTTP initialize
  • Slow-time container smoke: /health and tools/list
  • Makefile container helpers build successfully for benchmark, slow-time, and fast-time

Full-suite status

  • make lint (not run; scoped lint/checks above ran instead)
  • make test (not run; scoped tests above ran instead)
  • CHANGELOG updated (not user-facing)

📓 Notes (optional)

  • The legacy /sse, /messages, and /message paths stay as a hand-rolled compatibility shim because rmcp Streamable HTTP does not reproduce the existing ContextForge SSE endpoint semantics.
  • fast_time_server in compose defaults to the published GHCR image and still supports local overrides with FAST_TIME_IMAGE.
  • Streamable HTTP Host validation is configurable through MCP_ALLOWED_HOSTS / -allowed-hosts, with compose defaults and -public-url host support.
  • sql-sanitizer E2E now starts the Rust fast-time server on :9080, matching the workflow's existing health probes and gateway registration URL.
  • Wrapper E2E and generated performance compose now build/start the Rust fast-time server instead of the Go implementation.
  • Verbose/debug/performance compose variants and Helm values now describe/use the Rust fast-time server consistently with the main compose path.

@lucarlig lucarlig self-assigned this Jun 18, 2026
@lucarlig lucarlig force-pushed the user/luca/rust-fast-time-sse branch 4 times, most recently from fa8d028 to 1d5babc Compare June 25, 2026 10:58
@lucarlig lucarlig force-pushed the user/luca/rust-fast-time-sse branch from 1d5babc to 5b32e6a Compare June 26, 2026 09:02
@lucarlig lucarlig changed the title feat: add Rust fast-time SSE transport feat(fast-time): rmcp /mcp transport + legacy SSE shim Jun 26, 2026
@jonpspri jonpspri added the MUST P1: Non-negotiable, critical requirements without which the product is non-functional or unsafe label Jun 26, 2026
ja8zyjits
ja8zyjits previously approved these changes Jun 26, 2026

@ja8zyjits ja8zyjits left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM
only changes to an mcp-server low impact on core CF.

@ja8zyjits ja8zyjits self-assigned this Jun 26, 2026
@ja8zyjits ja8zyjits dismissed their stale review June 26, 2026 12:56

waiting for confirmation

@ja8zyjits ja8zyjits added the do-not-merge Do NOT merge. This requires manual effort, with multiple checks! label Jun 26, 2026
@lucarlig lucarlig force-pushed the user/luca/rust-fast-time-sse branch 4 times, most recently from d3ff2cf to 0760b2f Compare June 26, 2026 16:24
@lucarlig lucarlig removed the do-not-merge Do NOT merge. This requires manual effort, with multiple checks! label Jun 29, 2026
lucarlig added 7 commits June 29, 2026 10:23
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>
lucarlig added 7 commits June 29, 2026 10:23
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>
@lucarlig lucarlig force-pushed the user/luca/rust-fast-time-sse branch from 17c9920 to 9499be9 Compare June 29, 2026 10:04

@ja8zyjits ja8zyjits left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this and the sql-sanitizer.yaml changes.
Iam not sure if that is needed here?

Comment thread docker-compose.yml
ja8zyjits
ja8zyjits previously approved these changes Jun 29, 2026

@ja8zyjits ja8zyjits left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Signed-off-by: lucarlig <luca.carlig@ibm.com>

@Lang-Akshay Lang-Akshay left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@Lang-Akshay Lang-Akshay added this pull request to the merge queue Jun 29, 2026
Merged via the queue into main with commit 1b98aeb Jun 29, 2026
76 of 77 checks passed
@Lang-Akshay Lang-Akshay deleted the user/luca/rust-fast-time-sse branch June 29, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

MUST P1: Non-negotiable, critical requirements without which the product is non-functional or unsafe

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants