feat(mcp): arium-mcp resource-server crate + rmcp_example
#18 in tonybierman/arium — merged 2026-05-27
What
Adds OAuth 2.0 resource-server support for remote MCP servers, as a reusable crate plus a runnable example.
crates/arium-mcp (new)
An rmcp-version-agnostic axum/tower layer that turns any MCP HTTP endpoint into an OAuth 2.0 Resource Server:
- Per-request
Authorization: Bearer dxsk_…validation against arium'sapi_keystable. 401+WWW-Authenticate: Bearer resource_metadata="…"challenge on failure.- RFC 9728 Protected Resource Metadata at
/.well-known/oauth-protected-resource. - Access auditing via arium's existing
audit_eventslog —mcp.access.deniedalways,mcp.access.grantedopt-in (arium's anti-flood convention), with client IP + User-Agent. - Public API:
ResourceMetadata,AriumMcpState,metadata_router,protect. Full rustdoc + cargo-rdme README.
crates/arium (small change)
Exposes authenticate_token(pool, token) — extracted from the pub(crate) bearer_auth middleware so out-of-tree consumers (like arium-mcp) share one validation path. Re-exported from the crate root.
examples/rmcp_example (new)
Self-contained streamable-HTTP MCP server (rmcp 1.7) gated by arium-mcp: boots SQLite, runs arium::migrator(), seeds a demo user, mints + prints a dxsk_ token, and exposes trivial ping/echo tools behind the gate.
Scope / non-goals
Resource-server mode only — tokens are minted out-of-band. This is not an OAuth Authorization Server (no /authorize, /token, dynamic client registration, or PKCE auth-code flow). MCP clients that accept a manual bearer token/header (Inspector, Claude Code --header, IDEs, programmatic) work today; GUI "click Connect" connectors that require the interactive flow do not. The 401 + Protected Resource Metadata scaffolding is the clean, additive foundation for that follow-up.
Verification
cargo check/clippy -D warnings/fmt/cargo-macheteclean on both crates;arium-mcpdoctest passes;cargo rdme --check -w arium-mcpin sync (added to the CIreadmejob).- Unified-graph check (
arium-mcp+rmcp_example+arium-dioxuswitharium/mailon) compiles — sormcp_exampleneeds no CI--exclude. - Live run: discovery returns RFC 9728 JSON; no-token/bad-token → 401 +
WWW-Authenticate; valid token → 200 + SSEinitialize;audit_eventsshows the expectedmcp.access.denied/mcp.access.grantedrows (with IP captured).
🤖 Generated with Claude Code
Last updated 2026-05-28
Links to this note
Merged pull requests, newest first.