mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-11 21:28:52 +00:00
mcp(coverage): add 34 tools across 7 domains to close 2026-05-05 parity audit P1 findings
Closes findings P1-1..P1-35 from the 2026-05-05 CLI/API/MCP↔GUI parity
audit (cowork/cli-gui-parity-audit-2026-05-05/RESULTS.md). Before this
bundle, 35 operator-facing API endpoints had GUI surfaces but no MCP
counterpart — operators using AI assistants for cert lifecycle work in
regulated environments had to drop to curl for approve/reject, health-check
acknowledgement, renewal-policy CRUD, network-scan triggering, discovery
triage, intermediate-CA management, and job verification.
Tool count: 87→121 in tools.go (+34), 6 unchanged in tools_est.go.
Re-derive via grep -cE 'gomcp\\.AddTool\\(' internal/mcp/tools.go
internal/mcp/tools_est.go.
The 7 phases (matching the bundle prompt at
cowork/mcp-coverage-expansion-prompt.md):
Phase A — Approvals (P1-28..P1-31, 4 tools)
list_approvals, get_approval, approve_request, reject_request.
Two-person-integrity contract (ErrApproveBySameActor → HTTP 403)
is preserved automatically: the decided_by actor is derived
server-side from middleware.UserKey, NOT from request body, so
the MCP server's authenticated API-key identity becomes the
audit-trail actor. The MCP input schema deliberately omits any
actor_id field to prevent client-side spoofing.
Phase B — Health Checks (P1-20..P1-27, 8 tools)
list, summary, get, create, update, delete, history, acknowledge.
Mirrors the existing target-resource shape; acknowledge takes
optional 'actor' string captured in the audit row (handler defaults
to 'unknown' if absent).
Phase C — Renewal Policies (P1-1..P1-5, 5 tools)
Standard CRUD against /api/v1/renewal-policies. Distinct from the
legacy 'policy' tools that point at the same path — these expose
the renewal-policy domain explicitly with full alert_channels +
alert_severity_map field shape.
Phase D — Network Scan Targets (P1-14..P1-19, 6 tools)
CRUD + trigger_scan. trigger_network_scan returns the discovery-
scan body so the AI can chain into list_discovered_certificates
filtered by agent_id.
Phase E — Discovery read-side (P1-10..P1-13, 4 tools)
list_discovered_certificates, get_discovered_certificate,
list_discovery_scans, discovery_summary. Complements the
pre-existing claim/dismiss tools (registered alongside Health
historically per the I-2 closure).
Phase F — Intermediate CAs (P1-6..P1-9, 4 tools)
list, create (root + child via discriminator on body shape), get,
retire. The handler is admin-gated via middleware.IsAdmin; the
least-privilege boundary is enforced at the API layer (HTTP 403
for non-admin Bearer callers) — not by transport carve-out.
Phase G — Verification + deployments (P1-32, P1-34, P1-35, 3 tools)
list_certificate_deployments, verify_job, get_job_verification.
P1-33 (POST /api/v1/agents/{id}/discoveries) is intentionally
excluded — machine-to-machine push channel for agents reporting
filesystem-scan results, not an operator-driven flow. Documented
inline in the RegisterTools dispatch.
Implementation:
- 14 new input types in internal/mcp/types.go with jsonschema struct
tags driving LLM tool discovery.
- 7 register* functions in internal/mcp/tools.go each handling one
phase, wired into RegisterTools dispatch in declaration order.
- 34 new entries in tools_per_tool_test.go::allHappyPathCases —
the existing in-process MCP harness (TestMCP_AllTools_HappyPath +
TestMCP_AllTools_ErrorPath + TestMCP_RegisterTools_DispatchableToolCount)
auto-extends coverage to cover every new tool: happy-path round-
trip with fence-shape assertion, 5xx error-path with MCP_ERROR fence
propagation, and 'every registered tool is dispatchable' guard.
- docs/reference/mcp.md 'Available Tools' table expanded from 16 to
22 resource domains with current per-domain tool counts.
Acceptance gate (verified):
- go build ./cmd/server/... ./cmd/agent/... ./cmd/cli/... ./cmd/mcp-server/...
clean across all four production binaries.
- go vet ./... clean.
- go test -short -count=1 ./internal/mcp/... pass (TestMCP_AllTools_*
expanded to 127 tool round-trips).
- go test -short -count=1 ./... pass repo-wide.
- bash scripts/ci-guards/openapi-handler-parity.sh clean (router 178,
OpenAPI 144, exceptions 36 — unchanged; we add MCP wrappers, not
routes).
- gofmt -l clean across the four touched files.
This commit is contained in:
@@ -367,6 +367,56 @@ var allHappyPathCases = []toolCase{
|
||||
{"est_get_csrattrs", map[string]any{"profile": "corp"}, http.MethodGet, "/.well-known/est/corp/csrattrs"},
|
||||
{"est_enroll", map[string]any{"profile": "corp", "csr": "-----BEGIN CERTIFICATE REQUEST-----\nXXX\n-----END CERTIFICATE REQUEST-----"}, http.MethodPost, "/.well-known/est/corp/simpleenroll"},
|
||||
{"est_reenroll", map[string]any{"profile": "corp", "csr": "-----BEGIN CERTIFICATE REQUEST-----\nXXX\n-----END CERTIFICATE REQUEST-----"}, http.MethodPost, "/.well-known/est/corp/simplereenroll"},
|
||||
|
||||
// 2026-05-05 CLI/API/MCP↔GUI parity audit closure — 34 new tools across 7 phases.
|
||||
|
||||
// Phase A — Approvals (P1-28..P1-31)
|
||||
{"certctl_list_approvals", map[string]any{}, http.MethodGet, "/api/v1/approvals"},
|
||||
{"certctl_get_approval", map[string]any{"id": "ar-1"}, http.MethodGet, "/api/v1/approvals/ar-1"},
|
||||
{"certctl_approve_request", map[string]any{"id": "ar-1"}, http.MethodPost, "/api/v1/approvals/ar-1/approve"},
|
||||
{"certctl_reject_request", map[string]any{"id": "ar-1"}, http.MethodPost, "/api/v1/approvals/ar-1/reject"},
|
||||
|
||||
// Phase B — Health Checks (P1-20..P1-27)
|
||||
{"certctl_list_health_checks", map[string]any{}, http.MethodGet, "/api/v1/health-checks"},
|
||||
{"certctl_health_check_summary", map[string]any{}, http.MethodGet, "/api/v1/health-checks/summary"},
|
||||
{"certctl_get_health_check", map[string]any{"id": "hc-1"}, http.MethodGet, "/api/v1/health-checks/hc-1"},
|
||||
{"certctl_create_health_check", map[string]any{"endpoint": "api.example.com:443"}, http.MethodPost, "/api/v1/health-checks"},
|
||||
{"certctl_update_health_check", map[string]any{"id": "hc-1", "endpoint": "api.example.com:443"}, http.MethodPut, "/api/v1/health-checks/hc-1"},
|
||||
{"certctl_delete_health_check", map[string]any{"id": "hc-1"}, http.MethodDelete, "/api/v1/health-checks/hc-1"},
|
||||
{"certctl_health_check_history", map[string]any{"id": "hc-1"}, http.MethodGet, "/api/v1/health-checks/hc-1/history"},
|
||||
{"certctl_acknowledge_health_check", map[string]any{"id": "hc-1"}, http.MethodPost, "/api/v1/health-checks/hc-1/acknowledge"},
|
||||
|
||||
// Phase C — Renewal Policies (P1-1..P1-5)
|
||||
{"certctl_list_renewal_policies", map[string]any{}, http.MethodGet, "/api/v1/renewal-policies"},
|
||||
{"certctl_get_renewal_policy", map[string]any{"id": "rp-1"}, http.MethodGet, "/api/v1/renewal-policies/rp-1"},
|
||||
{"certctl_create_renewal_policy", map[string]any{"name": "weekly-rotate"}, http.MethodPost, "/api/v1/renewal-policies"},
|
||||
{"certctl_update_renewal_policy", map[string]any{"id": "rp-1", "name": "renamed"}, http.MethodPut, "/api/v1/renewal-policies/rp-1"},
|
||||
{"certctl_delete_renewal_policy", map[string]any{"id": "rp-1"}, http.MethodDelete, "/api/v1/renewal-policies/rp-1"},
|
||||
|
||||
// Phase D — Network Scan Targets (P1-14..P1-19)
|
||||
{"certctl_list_network_scan_targets", map[string]any{}, http.MethodGet, "/api/v1/network-scan-targets"},
|
||||
{"certctl_get_network_scan_target", map[string]any{"id": "ns-1"}, http.MethodGet, "/api/v1/network-scan-targets/ns-1"},
|
||||
{"certctl_create_network_scan_target", map[string]any{"name": "dc1-web", "cidrs": []string{"10.0.0.0/24"}, "ports": []int{443}}, http.MethodPost, "/api/v1/network-scan-targets"},
|
||||
{"certctl_update_network_scan_target", map[string]any{"id": "ns-1", "name": "renamed"}, http.MethodPut, "/api/v1/network-scan-targets/ns-1"},
|
||||
{"certctl_delete_network_scan_target", map[string]any{"id": "ns-1"}, http.MethodDelete, "/api/v1/network-scan-targets/ns-1"},
|
||||
{"certctl_trigger_network_scan", map[string]any{"id": "ns-1"}, http.MethodPost, "/api/v1/network-scan-targets/ns-1/scan"},
|
||||
|
||||
// Phase E — Discovery read-side (P1-10..P1-13)
|
||||
{"certctl_list_discovered_certificates", map[string]any{}, http.MethodGet, "/api/v1/discovered-certificates"},
|
||||
{"certctl_get_discovered_certificate", map[string]any{"id": "dc-1"}, http.MethodGet, "/api/v1/discovered-certificates/dc-1"},
|
||||
{"certctl_list_discovery_scans", map[string]any{}, http.MethodGet, "/api/v1/discovery-scans"},
|
||||
{"certctl_discovery_summary", map[string]any{}, http.MethodGet, "/api/v1/discovery-summary"},
|
||||
|
||||
// Phase F — Intermediate CAs (P1-6..P1-9)
|
||||
{"certctl_list_intermediate_cas", map[string]any{"issuer_id": "iss-1"}, http.MethodGet, "/api/v1/issuers/iss-1/intermediates"},
|
||||
{"certctl_create_intermediate_ca", map[string]any{"issuer_id": "iss-1", "name": "subca-1", "parent_ca_id": "ica-root"}, http.MethodPost, "/api/v1/issuers/iss-1/intermediates"},
|
||||
{"certctl_get_intermediate_ca", map[string]any{"id": "ica-1"}, http.MethodGet, "/api/v1/intermediates/ica-1"},
|
||||
{"certctl_retire_intermediate_ca", map[string]any{"id": "ica-1"}, http.MethodPost, "/api/v1/intermediates/ica-1/retire"},
|
||||
|
||||
// Phase G — Verification + deployments (P1-32, P1-34, P1-35)
|
||||
{"certctl_list_certificate_deployments", map[string]any{"id": "mc-1"}, http.MethodGet, "/api/v1/certificates/mc-1/deployments"},
|
||||
{"certctl_verify_job", map[string]any{"id": "j-1", "target_id": "t-1", "expected_fingerprint": "AA:BB", "actual_fingerprint": "AA:BB", "verified": true}, http.MethodPost, "/api/v1/jobs/j-1/verify"},
|
||||
{"certctl_get_job_verification", map[string]any{"id": "j-1"}, http.MethodGet, "/api/v1/jobs/j-1/verification"},
|
||||
}
|
||||
|
||||
// TestMCP_AllTools_HappyPath dispatches every tool against the mock API in
|
||||
|
||||
Reference in New Issue
Block a user