Compare commits

..

1 Commits

Author SHA1 Message Date
Sharang Parnerkar eb88656c43 feat(dashboard): UI for managing MCP tokens
CI / Check (pull_request) Failing after 4m33s
CI / Detect Changes (pull_request) Has been skipped
CI / Deploy Agent (pull_request) Has been skipped
CI / Deploy Dashboard (pull_request) Has been skipped
CI / Deploy Docs (pull_request) Has been skipped
CI / Deploy MCP (pull_request) Has been skipped
Adds /mcp-tokens page that lets a logged-in user mint, list, and
revoke bearer tokens for the MCP server. Stacks on #92 (which added
the agent endpoints + middleware) — once both land, the loop is
closed: a user can copy a token from the dashboard straight into
their Claude Desktop / Cursor / ChatGPT MCP config.

UX
- "Create Token" button → inline form with name input.
- On submit, server function calls `POST /api/v1/mcp-tokens`. The
  raw token is shown ONCE in a prominent yellow banner with a copy
  button and a "won't be shown again" warning, then the user
  dismisses it manually.
- List view: card per token with name, prefix `mcpt_xxxx…`, created
  date, last_used (or "never"). Revoked tokens render dimmed with a
  "revoked" pill. Active tokens have a trash button → confirm
  modal → soft delete.
- Toast feedback on create/revoke success/failure.

Files
- infrastructure/mcp_tokens.rs (new) — three #[server] functions:
  fetch_mcp_tokens, create_mcp_token, revoke_mcp_token. All go
  through agent_client so the Keycloak Bearer is auto-attached;
  the agent then enforces tenant scoping on every endpoint.
- pages/mcp_tokens.rs (new) — the page component itself.
- app.rs — adds Route::McpTokensPage at /mcp-tokens.
- pages/mod.rs, infrastructure/mod.rs — module + re-export wiring.

Timestamp format
- The agent serializes BSON DateTime as extended JSON
  `{"$date":{"$numberLong":"..."}}`. Page has a small helper that
  accepts that shape, plain ISO strings, or anything else
  (best-effort). Same approach used elsewhere in the dashboard so
  there's no new dependency.

Test plan
- cargo fmt --all clean
- cargo clippy -p compliance-dashboard --features server
  -- -D warnings clean
- cargo clippy -p compliance-dashboard --features web
  --no-default-features -- -D warnings clean
- cargo check on both feature sets clean

Followup
- No sidebar entry yet (matches mcp_servers — settings-style
  pages are reached via direct URL today). Worth adding a
  Settings sub-menu in a separate UX pass.
- Token expiry + per-tool scope when those land on the agent side
  will need a small UI for the create modal (extra fields).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-30 17:27:36 +02:00
2 changed files with 2 additions and 15 deletions
-13
View File
@@ -7,17 +7,4 @@ ignore = [
# not a realistic attack surface here. Revisit when mongodb bumps hickory. # not a realistic attack surface here. Revisit when mongodb bumps hickory.
"RUSTSEC-2026-0118", # NSEC3 loop, no fix available upstream "RUSTSEC-2026-0118", # NSEC3 loop, no fix available upstream
"RUSTSEC-2026-0119", # O(n²) name compression, fixed in hickory-proto >=0.26.1 "RUSTSEC-2026-0119", # O(n²) name compression, fixed in hickory-proto >=0.26.1
# rmcp 0.16.0 — DNS rebinding in Streamable HTTP server transport (missing
# Host header validation). Patched in rmcp >= 1.4.0, which is a major API
# version jump from our pin; rmcp shipped 0.x → 1.x → 2.x in three months
# and the migration touches every tool handler + the auth middleware we
# just landed in #92. Threat model in our deployment: the MCP server is
# exposed at a public hostname (comp-mcp-dev.meghsakha.com) behind orca's
# TLS-terminating ingress with per-tenant bearer auth — the attack model
# (browser DNS-rebinding into localhost MCP server) doesn't directly apply.
# Defense-in-depth Host-header check is still a worthwhile follow-up.
# FOLLOW-UP: bump rmcp to 2.x in a dedicated PR (M7.3 follow-up, sized
# multi-hour due to API surface change).
"RUSTSEC-2026-0189",
] ]
Generated
+2 -2
View File
@@ -4282,9 +4282,9 @@ dependencies = [
[[package]] [[package]]
name = "quinn-proto" name = "quinn-proto"
version = "0.11.15" version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
dependencies = [ dependencies = [
"bytes", "bytes",
"getrandom 0.3.4", "getrandom 0.3.4",