Merge branch 'fix/i2-mcp-discovered-cert-completeness' (I-2 closure, last P1)

This commit is contained in:
shankar0123
2026-04-25 16:33:56 +00:00
2 changed files with 55 additions and 0 deletions
+32
View File
@@ -1238,4 +1238,36 @@ func registerHealthTools(s *gomcp.Server, c *Client) {
}
return textResult(data)
})
// I-2 closure (cat-i-b0924b6675f8): pre-I-2 the README claimed "all
// API endpoints are exposed via MCP" but the discovered-certificate
// lifecycle (claim + dismiss) was never wrapped — operators using
// MCP clients (Claude, Cursor, etc.) had no path to bring an
// out-of-band cert under management or to mark a benign discovery
// as not-of-interest without dropping to the REST API directly.
// These two tools wrap the existing HTTP handlers
// (DiscoveryHandler.ClaimDiscovered + DismissDiscovered).
gomcp.AddTool(s, &gomcp.Tool{
Name: "certctl_claim_discovered_certificate",
Description: "Link a discovered certificate (dc-*) to an existing managed certificate (mc-*) via POST /api/v1/discovered-certificates/{id}/claim. Use this to bring an out-of-band cert (e.g. one found by an agent filesystem scan or a network scan) under certctl management without re-issuing — the discovered row is marked Managed and its managed_certificate_id is set so subsequent renewals/revocations on the managed cert update both rows.",
}, func(ctx context.Context, req *gomcp.CallToolRequest, input ClaimDiscoveredCertificateInput) (*gomcp.CallToolResult, any, error) {
body := map[string]string{"managed_certificate_id": input.ManagedCertificateID}
data, err := c.Post("/api/v1/discovered-certificates/"+input.ID+"/claim", body)
if err != nil {
return errorResult(err)
}
return textResult(data)
})
gomcp.AddTool(s, &gomcp.Tool{
Name: "certctl_dismiss_discovered_certificate",
Description: "Dismiss a discovered certificate (POST /api/v1/discovered-certificates/{id}/dismiss). Use this to mark a discovery as not-of-interest (e.g. expired self-signed test certs found by a network scan) — the row stops appearing in the unmanaged-list view but is preserved in the DB for audit history.",
}, func(ctx context.Context, req *gomcp.CallToolRequest, input DismissDiscoveredCertificateInput) (*gomcp.CallToolResult, any, error) {
data, err := c.Post("/api/v1/discovered-certificates/"+input.ID+"/dismiss", nil)
if err != nil {
return errorResult(err)
}
return textResult(data)
})
}
+23
View File
@@ -323,6 +323,29 @@ type TimelineInput struct {
Days int `json:"days,omitempty" jsonschema:"Number of days to look back (default 30, max 365)"`
}
// ── Discovered Certificates (I-2 closure) ──────────────────────────
// ClaimDiscoveredCertificateInput is the MCP tool input for claiming a
// discovered certificate (POST /api/v1/discovered-certificates/{id}/claim).
// I-2 closure (cat-i-b0924b6675f8). The HTTP handler at
// internal/api/handler/discovery.go::ClaimDiscovered links the discovered
// row (DC-*) to a managed certificate (mc-*); operators use this to
// bring an out-of-band cert under management without re-issuing.
type ClaimDiscoveredCertificateInput struct {
ID string `json:"id" jsonschema:"Discovered certificate ID (dc-*)"`
ManagedCertificateID string `json:"managed_certificate_id" jsonschema:"Existing managed certificate ID (mc-*) to link to"`
}
// DismissDiscoveredCertificateInput is the MCP tool input for dismissing
// a discovered certificate (POST /api/v1/discovered-certificates/{id}/dismiss).
// I-2 closure (cat-i-b0924b6675f8). Marks the row as not-of-interest
// (e.g. expired self-signed test certs found by a network scan); the row
// stops appearing in the unmanaged-list view but is preserved in the DB
// for audit history.
type DismissDiscoveredCertificateInput struct {
ID string `json:"id" jsonschema:"Discovered certificate ID (dc-*)"`
}
// ── Empty ───────────────────────────────────────────────────────────
type EmptyInput struct{}