Skip to content

feat(server): rfc 0027 green transport — /mcp behind querier.mcp.enabled - #413

Merged
jensholdgaard merged 3 commits into
mainfrom
rfc0027-green-transport
Jul 6, 2026
Merged

feat(server): rfc 0027 green transport — /mcp behind querier.mcp.enabled#413
jensholdgaard merged 3 commits into
mainfrom
rfc0027-green-transport

Conversation

@jensholdgaard

@jensholdgaard jensholdgaard commented Jul 6, 2026

Copy link
Copy Markdown
Owner

What

RFC 0027 green slice 1 (transport) — Scenario RFC0027.1:

  • rmcp 2.1.0 (the official MCP Rust SDK — modelcontextprotocol/rust-sdk), server-side + streamable-HTTP features only, in ourios-server alone (§3.1: no new crate). cargo deny clean (the run also surfaced pre-existing RUSTSEC-2026-0204 on crossbeam-epoch; patch-bumped here).
  • src/mcp.rs: the /mcp sub-router — StreamableHttpService over a LocalSessionManager, wrapped in the RFC 0026 bearer gate as an axum layer (one undifferentiated 401 before any MCP dispatch; open mode passes through — the JSON API's exact contract). The handler announces the server with the §3.3 treat-log-bodies-as-data instruction; the §3.2 tools and the grammar resource attach in the next slices.
  • Gating: querier.mcp.enabled in the RFC 0020 file schema (with ${env:…} substitution) and OURIOS_QUERIER_MCP_ENABLED on the env path, default off; router/router_with_auth are unchanged (= off), router_with_mcp is the optioned constructor tests drive.

Scenario mapping

rfc0027_1_gating_and_placement (stub → green): flag off ⇒ /mcp 404 with the existing constructors untouched (the RFC 0016/0026 suites in this harness are the JSON-API-unchanged assertion); flag on ⇒ the MCP initialize handshake answers with serverInfo on the same router; auth enabled ⇒ no bearer 401 before dispatch, valid bearer served. The .2 tenant-denial arm needs a tool to probe and lands with the tools slice (its stub notes the transport label — the authn arm is covered here).

Test note: rmcp validates the Host header (DNS-rebinding protection); the in-process requests carry one explicitly, as any real client does.

Invariants / hazards

  • Hazard §4.6: nothing SQL-shaped is exposed; the surface is the handshake only until the tools slice.
  • RFC 0026 composition: the gate is the same authenticate_bearer the JSON API and OTLP listeners use — one bearer implementation across every surface.

Checks run locally

cargo fmt --all --check, cargo clippy -p ourios-server --all-targets --all-features -- -D warnings, cargo test -p ourios-server --all-features, cargo deny check — all green.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an optional MCP endpoint for the querier service, available at /mcp when enabled.
    • MCP can now be turned on via environment or file-based configuration.
  • Bug Fixes

    • Requests to the MCP endpoint now require valid authentication when access control is enabled.
    • Disabled MCP configurations correctly return 404 instead of exposing the endpoint.

The rmcp streamable-HTTP service (official MCP Rust SDK, server-side
features only) nests at /mcp on the querier listener when
querier.mcp.enabled / OURIOS_QUERIER_MCP_ENABLED is set (default off),
behind the RFC 0026 bearer gate as an axum layer — 401 before any MCP
dispatch, open mode passes through. The handler announces the server
and the treat-log-bodies-as-data instruction; tools and the grammar
resource land in the next slices. RFC0027.1 goes green. Also bumps
crossbeam-epoch past RUSTSEC-2026-0204 (pre-existing advisory caught
by the deny run).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@jensholdgaard, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b02d12d1-84d2-40c5-a4b6-8a9352a4854e

📥 Commits

Reviewing files that changed from the base of the PR and between 6495890 and 2858648.

📒 Files selected for processing (2)
  • crates/ourios-server/src/main.rs
  • crates/ourios-server/src/mcp.rs
📝 Walkthrough

Walkthrough

This PR adds an opt-in RFC 0027 MCP (Model Context Protocol) transport surface served at /mcp for the querier role. It introduces config parsing (querier.mcp.enabled), env/file threading of mcp_enabled, an OuriosMcp server handler with bearer-auth gating middleware, router mounting, and integration tests.

Changes

MCP Surface Implementation

Layer / File(s) Summary
Dependency and module registration
crates/ourios-server/Cargo.toml, crates/ourios-server/src/lib.rs
Adds rmcp dependency with server/transport-streamable-http-server features and registers the new mcp module.
Config parsing and substitution
crates/ourios-server/src/config/file.rs
Adds McpSection { enabled: Option<String> } under QuerierSection.mcp and extends ${env:…} substitution to cover querier.mcp.enabled.
Env/file/CLI wiring for mcp_enabled
crates/ourios-server/src/main.rs
Adds mcp_enabled to QuerierParams, reads OURIOS_QUERIER_MCP_ENABLED and file config, extends build_querier_config to derive the boolean, wires it into QuerierConfig, and updates existing tests for the new function signature.
MCP server handler and bearer auth
crates/ourios-server/src/mcp.rs
Implements OuriosMcp with get_info, adds require_bearer middleware returning 401 on failed auth, and mcp_router building the streamable-HTTP service with the bearer layer.
Querier router mounting
crates/ourios-server/src/querier.rs
Adds mcp_enabled to QuerierConfig, introduces router_with_mcp, conditionally nests the MCP router under /mcp in router_from_querier, and threads the flag through serve.
Integration tests
crates/ourios-server/tests/it/rfc0027_mcp.rs
Replaces the ignored stub with an active async test verifying 404 when disabled, 200 with serverInfo/protocolVersion when enabled, and 401/200 bearer gating.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Router as router_from_querier
  participant Middleware as require_bearer
  participant McpHandler as OuriosMcp

  Client->>Router: POST /mcp (initialize)
  alt mcp_enabled = false
    Router-->>Client: 404 NOT_FOUND
  else mcp_enabled = true
    Router->>Middleware: forward request
    alt auth configured, missing/invalid bearer
      Middleware-->>Client: 401 UNAUTHORIZED
    else authenticated or no auth required
      Middleware->>McpHandler: dispatch initialize
      McpHandler-->>Client: 200 OK (serverInfo, protocolVersion)
    end
  end
Loading

Possibly related PRs

  • jensholdgaard/ourios#283: Both PRs modify QuerierConfig and router construction in crates/ourios-server/src/querier.rs, one adding /v1/query and this one extending it to conditionally mount /mcp.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it does not follow the required template headings and omits the checklist and explicit related links. Reformat it into ## Summary, ## Related, and ## Checklist sections, and add the required RFC/link and checkbox items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: enabling RFC 0027 MCP transport at /mcp behind querier.mcp.enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rfc0027-green-transport

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds the RFC 0027 “green transport” slice for an MCP endpoint in the querier, gated behind a new querier.mcp.enabled config/env flag so existing routers remain unchanged unless explicitly opted in.

Changes:

  • Introduces a new /mcp sub-router using rmcp streamable HTTP, wrapped with the RFC 0026 bearer authentication gate.
  • Adds config plumbing for querier.mcp.enabled (file schema + env var) and wires it into querier router construction and serve.
  • Converts scenario RFC0027.1 from an ignored stub into an async integration test covering 404 (off), initialize handshake (on), and bearer gating behavior.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
crates/ourios-server/tests/it/rfc0027_mcp.rs Implements RFC0027.1 integration test for MCP gating/placement and bearer behavior.
crates/ourios-server/src/querier.rs Adds mcp_enabled to config and nests /mcp when enabled; introduces router_with_mcp.
crates/ourios-server/src/mcp.rs New MCP transport module: streamable-HTTP service + bearer middleware layer.
crates/ourios-server/src/main.rs Adds env/file parsing for OURIOS_QUERIER_MCP_ENABLED / querier.mcp.enabled and passes it into querier config.
crates/ourios-server/src/lib.rs Registers the new mcp module within the crate.
crates/ourios-server/src/config/file.rs Extends the file schema with querier.mcp.enabled and includes it in env-substitution.
crates/ourios-server/Cargo.toml Adds the rmcp dependency with server + streamable-http-server features only.
Cargo.lock Locks rmcp and transitive deps; bumps crossbeam-epoch to 0.9.20.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/ourios-server/src/mcp.rs
Comment thread crates/ourios-server/src/main.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
crates/ourios-server/src/main.rs (1)

364-398: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a unit test for the mcp_enabled_raw truthy branch.

Every updated build_querier_config test call passes None for the new mcp_enabled_raw argument; none assert QuerierParams.mcp_enabled == true for Some("1")/Some("true")/Some("yes"). The only positive-path coverage lives in the separate integration test, which exercises router_with_mcp directly and bypasses this parsing function.

✅ Suggested test addition
+    #[test]
+    fn build_querier_config_derives_mcp_enabled_from_truthy_values() {
+        for raw in [Some("1"), Some("true"), Some("yes")] {
+            let params = build_querier_config(Some("1"), None, None, raw)
+                .expect("ok")
+                .expect("enabled");
+            assert!(params.mcp_enabled, "mcp enabled for mcp_enabled_raw = {raw:?}");
+        }
+        for raw in [None, Some("0"), Some("false"), Some("nope")] {
+            let params = build_querier_config(Some("1"), None, None, raw)
+                .expect("ok")
+                .expect("enabled");
+            assert!(!params.mcp_enabled, "mcp disabled for mcp_enabled_raw = {raw:?}");
+        }
+    }

As per coding guidelines, "Unit tests must be next to the code and are mandatory for anything non-trivial."

Also applies to: 1345-1406

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ourios-server/src/main.rs` around lines 364 - 398, Add a unit test for
build_querier_config that exercises the mcp_enabled_raw truthy path. Update the
existing build_querier_config test cases to pass the new argument explicitly,
then add assertions that QuerierParams.mcp_enabled is true when mcp_enabled_raw
is Some("1"), Some("true"), and Some("yes"). Keep the test next to
build_querier_config so the parsing behavior is covered directly rather than
only through router_with_mcp integration tests.

Source: Coding guidelines

crates/ourios-server/src/mcp.rs (1)

52-81: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No unit tests colocated with require_bearer/mcp_router.

This file has no #[cfg(test)] module; the bearer-gating branch logic is only exercised indirectly via the integration test in tests/it/rfc0027_mcp.rs. As per coding guidelines, "Unit tests must be next to the code and are mandatory for anything non-trivial," so a focused unit test on require_bearer (open mode passes through, missing/invalid bearer → 401, valid bearer → passthrough) would satisfy the guideline directly rather than relying solely on full-router integration coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ourios-server/src/mcp.rs` around lines 52 - 81, Add a local
#[cfg(test)] module next to require_bearer and mcp_router with focused unit
coverage for the bearer gate instead of relying only on tests/it/rfc0027_mcp.rs.
Exercise require_bearer directly for the open/pass-through case, missing or
invalid Authorization returning 401, and a valid bearer flowing through to
next.run, using the existing authenticate_bearer behavior and mcp_router setup
only as needed to locate the logic.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/ourios-server/src/mcp.rs`:
- Around line 70-81: The MCP router is using
StreamableHttpServerConfig::default(), which keeps the host allowlist limited to
loopback and causes /mcp to be rejected on non-loopback deployments. Update
mcp_router to accept an allowed_hosts configuration (or equivalent trusted-proxy
toggle) and pass it into StreamableHttpService::new instead of relying on the
default config; keep the require_bearer middleware unchanged and make sure the
new config is threaded from the caller that builds the router.
- Around line 35-45: Use struct initialization instead of creating ServerInfo
with ServerInfo::default() and then reassigning fields in get_info. Update the
get_info method in mcp.rs to construct ServerInfo directly with the desired
capabilities and instructions, likely using struct update syntax, so it no
longer triggers clippy::field_reassign_with_default.
- Around line 1-81: The /mcp route in mcp_router currently has no metrics or
tracing instrumentation, so add request-level observability around the
StreamableHttpService and bearer middleware. Update mcp_router and/or
require_bearer to emit a request span and Prometheus counter/histogram for MCP
requests, including success/failure outcomes and latency. If this is
intentionally deferred to a later slice, add an explicit note in the module docs
or the mcp_router comment stating that MCP metrics and tracing are pending and
where they will be implemented.

In `@crates/ourios-server/src/querier.rs`:
- Around line 254-264: The
`Router::layer(DefaultBodyLimit::max(MAX_BODY_BYTES))` in `querier.rs` is
applied before `router.nest("/mcp", crate::mcp::mcp_router(auth))`, so the
nested MCP routes bypass the body-size limit. Move the `DefaultBodyLimit`
application to after all routes are added, or apply the same limit within
`crate::mcp::mcp_router` so both `/v1/query` and `/mcp` are covered. Use the
`router` construction and `mcp_router(auth)` call as the reference points when
updating the layering order.

In `@crates/ourios-server/tests/it/rfc0027_mcp.rs`:
- Around line 45-109: The MCP router currently uses
StreamableHttpServerConfig::default(), which only permits loopback hosts and can
cause a 403 before auth or MCP handling. Update querier::router_with_mcp to
accept and pass through allowed_hosts from config into the MCP server config, or
explicitly constrain the test to loopback-only behavior. Use the router_with_mcp
setup in rfc0027_1_gating_and_placement to verify the intended host policy.

---

Nitpick comments:
In `@crates/ourios-server/src/main.rs`:
- Around line 364-398: Add a unit test for build_querier_config that exercises
the mcp_enabled_raw truthy path. Update the existing build_querier_config test
cases to pass the new argument explicitly, then add assertions that
QuerierParams.mcp_enabled is true when mcp_enabled_raw is Some("1"),
Some("true"), and Some("yes"). Keep the test next to build_querier_config so the
parsing behavior is covered directly rather than only through router_with_mcp
integration tests.

In `@crates/ourios-server/src/mcp.rs`:
- Around line 52-81: Add a local #[cfg(test)] module next to require_bearer and
mcp_router with focused unit coverage for the bearer gate instead of relying
only on tests/it/rfc0027_mcp.rs. Exercise require_bearer directly for the
open/pass-through case, missing or invalid Authorization returning 401, and a
valid bearer flowing through to next.run, using the existing authenticate_bearer
behavior and mcp_router setup only as needed to locate the logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c5a28866-4800-452d-87d4-8056fca960e2

📥 Commits

Reviewing files that changed from the base of the PR and between 48bc4d8 and 6495890.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • crates/ourios-server/Cargo.toml
  • crates/ourios-server/src/config/file.rs
  • crates/ourios-server/src/lib.rs
  • crates/ourios-server/src/main.rs
  • crates/ourios-server/src/mcp.rs
  • crates/ourios-server/src/querier.rs
  • crates/ourios-server/tests/it/rfc0027_mcp.rs

Comment thread crates/ourios-server/src/mcp.rs
Comment thread crates/ourios-server/src/mcp.rs
Comment thread crates/ourios-server/src/mcp.rs
Comment thread crates/ourios-server/src/querier.rs
Comment thread crates/ourios-server/tests/it/rfc0027_mcp.rs
…abilities

The nested router now carries the same DefaultBodyLimit (Router::layer
does not reach routes nested after it); allowed_hosts opens (rmcp's
loopback-only default would 403 real deployments pre-auth; the bearer
layer is the gate, matching the JSON API's Host posture); capabilities
stay empty until the tools slice attaches them; the mcp flag parsing
gains its unit test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 2 comments.

Comment thread crates/ourios-server/src/mcp.rs Outdated
Comment thread crates/ourios-server/src/mcp.rs Outdated
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated no new comments.

@jensholdgaard
jensholdgaard merged commit 94743ff into main Jul 6, 2026
22 checks passed
@jensholdgaard
jensholdgaard deleted the rfc0027-green-transport branch July 6, 2026 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants