feat(mcp): compliance_view MCP App (KLA-405)#31
Conversation
Audit-friendly compliance snapshot. Four cards: MFA adoption, device
encryption (FDE) with per-OS breakdown, password-age histogram, admin
inventory. Same pattern as KLA-403/404 — no-args tool, slotted into
appSpecs alongside dashboard_view.
Backend (internal/mcp/apps_compliance.go):
- Three parallel V1 calls: /systemusers (drives MFA + password age in
a single scan), /systems (drives FDE), /users (admin inventory).
- MFA scope intentionally excludes suspended and locked accounts so
the percentage isn't polluted by inactive seats.
- Password buckets: <30d / 30-60d / 60-90d / >90d, with a no_data
count for users missing password_date so the histogram math stays
honest. Both RFC3339 and date-only ("2026-01-01") parse paths
supported — JumpCloud emits both shapes depending on tenant.
- FDE segmented by OS, sorted by total desc then alphabetical so the
busiest platforms surface first.
- Offender lists (without_mfa, unencrypted) capped at 200 entries
with a *_total field so the UI can show "showing N of M".
- Per-fetch failures land in Warnings; only the all-failed case
returns a tool error.
UI (apps_html/compliance.html):
- 2x2 card grid with a single big number + color-coded coverage bar
per metric (green >=95 %, yellow >=80 %, red below).
- Truncation notes when the per-card list is capped.
- Refresh button reuses jcApp.callTool with no args.
Tests:
- aggregateUserCompliance: 7-user fixture covering active/suspended/
locked exclusion, MFA via both totp_enabled and mfa.configured,
every password bucket including no_data.
- aggregateDeviceCompliance: per-OS sort, unencrypted drill-down,
unknown-OS fallback.
- aggregateAdmins: alphabetical sort, lastLogin pass-through.
- End-to-end: full httptest server covering /systemusers + /systems +
/users.
- Registration + resource-serving assertions mirror device_view.
- TestMCP_ListTools_AllRegistered count bumped 198 → 199.
Out of scope (deliberate, follow-up):
- Per-method MFA breakdown (totp / webauthn / push). Needs the V2
per-user MFA detail endpoint; the basic /systemusers response
only exposes totp_enabled and mfa.configured booleans.
- Password-expiration-upcoming windows (7 / 30 days). Needs the
org's password policy max-age value to compute reliably.
- CSV / JSON export button. Currently the operator can already pipe
the tool's JSON response into jq; a dedicated export tool is a
separate small ticket.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| if (children) children.forEach(function(c){ | ||
| if (c == null) return; | ||
| if (typeof c === "string") n.appendChild(document.createTextNode(c)); | ||
| else if (c instanceof Node) n.appendChild(c); |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Reviewed by Cursor Bugbot for commit 45a1285. Configure here.
…Bugbot KLA-405)
Two real findings from the post-45a1285 review pass.
1. Future password_date inflated the healthy bucket. `now.Sub(t)`
produces a negative duration, which floors to a negative `days`,
which satisfies `days < 30` and lands in `bucketCounts[0]` — the
<30d "fresh" bucket. The complianceAgeLT* comment promised future
dates would "defensively land in >90d," but the implementation
didn't. Add an explicit `days < 0` route to bucket 3.
Causes: clock skew, tenants that record expiration date instead
of set date, API timestamp oddities. Auditors should see those
anomalies in the suspicious bucket, not as healthy entries.
New TestAggregateUserCompliance_FutureDateRoutesToOver90d pins
the contract.
2. Warning + error banners never rendered. The CSS has
`#warnings { display: none }` and `#error { display: none }`;
setting `style.display = ""` only removes the inline override,
leaving the stylesheet rule in effect. Must set
`style.display = "block"` to actually show the elements (which
is what dashboard.html does in the same spot). Partial-failure
warnings and load-failure errors were silently swallowed.
`#content`'s display:none is inline only (no stylesheet rule),
so its `""` clear still works — noted in the code comment.
HTML-only + small Go change. Go tests pass; the HTML change is
visual-only and falls under the existing manual-verify acceptance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
On the CodeQL alert ( The flagged line is
The same helper ships unchanged in Refactoring a safe-by-construction helper to silence a false positive would make the code harder to read for everyone with no security benefit; dismissal is the right disposition. |

Closes KLA-405. Audit-friendly compliance snapshot — sibling of KLA-404 device_view, same registerApp pattern.
Summary
compliance_view+ ui:// resourceui://jc/compliance. Slotted intoappSpecsalongsidedashboard_view.Backend (
internal/mcp/apps_compliance.go)Three parallel V1 calls:
/systemusers— drives MFA + password-age aggregation in a single scan. MFA scope deliberately excludes suspended and locked accounts so the percentage isn't polluted by inactive seats. Password buckets<30d / 30-60d / 60-90d / >90dwith a separateno_datacount for users missingpassword_dateso the histogram math stays honest. Both RFC3339 and date-only (2026-01-01) parse paths supported./systems— FDE coverage, segmented by OS, sorted by total desc → alphabetical. Returns anunencrypteddrill-down list./users— admin inventory (distinct V1 endpoint from/systemusers). One snapshot pulls every admin; orgs typically have single-digit admins.Offender lists (
without_mfa,unencrypted) capped at 200 with a*_totalfield so the UI can show "showing N of M." Per-fetch failures land inWarnings; only the all-failed case errors the tool.UI (
apps_html/compliance.html)2x2 card grid; each metric leads with a big-number percentage + color-coded coverage bar (green ≥95 %, yellow ≥80 %, red below). Per-OS breakdown table inside the FDE card. Password card shows 4-column histogram. Admin card shows per-admin role, MFA badge, last login. Refresh button reuses
jcApp.callToolwith empty args.Tests
aggregateUserCompliance— 7-user fixture covering active/suspended/locked exclusion, MFA via bothtotp_enabledandmfa.configured, every password bucket includingno_dataaggregateDeviceCompliance— per-OS sort, unencrypted drill-down, unknown-OS fallbackaggregateAdmins— alphabetical sort, lastLogin pass-through/systemusers+/systems+/users)TestMCP_ListTools_AllRegisteredcount bumped 198 → 199Out of scope (deliberate, follow-up)
/systemusersonly exposestotp_enabledandmfa.configuredbooleans.jq; a dedicated export tool is a separate small ticket.Test plan
go test ./...cleango vet ./...cleangofmt -lclean on touched filesmake build && ./jc mcp tools | grep compliance_viewfinds the toolcompliance_viewfrom Claude Desktop on a real org and sanity-check the percentages against the existingcompliance-reportrecipe output.🤖 Generated with Claude Code
Note
Medium Risk
Adds a new MCP App tool that fans out to multiple JumpCloud V1 endpoints and aggregates org-wide compliance metrics; main risk is increased API load and correctness/edge cases in compliance calculations (MFA scope, password-date parsing/bucketing, FDE OS segmentation). Changes are isolated to the MCP app layer and covered by new unit/end-to-end tests, reducing regression risk.
Overview
Adds a new MCP App,
compliance_view, with aui://jc/complianceresource that renders an audit-focused 4-card compliance snapshot (MFA adoption, device encryption/FDE with per-OS breakdown and unencrypted drill-down, password-age histogram, and admin inventory).Implements backend aggregation in
fetchComplianceDatavia three parallel V1 list calls (/systemusers,/systems,/users), including bounded offender lists, warning-on-partial-failure behavior, and defensive password-date parsing/bucketing (including future dates routed to the >90d bucket).Introduces the embedded
apps_html/compliance.htmlUI and adds comprehensive tests for the aggregators, tool/resource registration, and an end-to-end httptest server; updates the tool registry test to expect one additional tool (198→199).Reviewed by Cursor Bugbot for commit 29e644e. Bugbot is set up for automated code reviews on this repo. Configure here.