mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 22:21:30 +00:00
9d183478de
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.
173 lines
9.0 KiB
Markdown
173 lines
9.0 KiB
Markdown
# MCP Server Guide
|
|
|
|
> Last reviewed: 2026-05-05
|
|
|
|
certctl ships with an MCP (Model Context Protocol) server that lets AI assistants manage your certificate infrastructure through natural language. Ask your MCP-compatible AI client to "show me all expiring certificates," "revoke the VPN cert," or "what agents are offline?" and the MCP server translates that into API calls against your certctl instance.
|
|
|
|
This guide covers setup, configuration, and usage with any MCP-compatible AI client.
|
|
|
|
## What Is MCP?
|
|
|
|
MCP is an open protocol that connects AI assistants to external tools and data sources. Instead of copying and pasting API responses into a chat window, MCP lets the AI call your tools directly. The certctl MCP server exposes the certctl API as MCP tools (re-derive count via `grep -cE 'mcp\.AddTool\(' internal/mcp/tools.go`) — the AI sees typed schemas describing what each tool does, what parameters it accepts, and what it returns.
|
|
|
|
The MCP server is a separate binary (`cmd/mcp-server/`) that communicates via stdio transport. It's a stateless HTTP proxy: every MCP tool call becomes an HTTP request to the certctl REST API. No new state, no new database tables, no new attack surface beyond what the API already exposes.
|
|
|
|
## Prerequisites
|
|
|
|
You need:
|
|
|
|
1. A running certctl server (see [Quick Start](../getting-started/quickstart.md))
|
|
2. The MCP server binary — either built from source or from a Docker image
|
|
3. An MCP-compatible AI client
|
|
|
|
## Building the MCP Server
|
|
|
|
```bash
|
|
cd certctl
|
|
go build -o certctl-mcp ./cmd/mcp-server/
|
|
```
|
|
|
|
The binary has zero runtime dependencies beyond the certctl server it connects to.
|
|
|
|
## Configuration
|
|
|
|
The MCP server reads three environment variables:
|
|
|
|
| Variable | Required | Default | Description |
|
|
|----------|----------|---------|-------------|
|
|
| `CERTCTL_SERVER_URL` | No | `https://localhost:8443` | URL of the certctl REST API (HTTPS-only as of v2.2) |
|
|
| `CERTCTL_API_KEY` | No | (empty) | API key for authentication (passed as `Bearer` token) |
|
|
| `CERTCTL_SERVER_CA_BUNDLE_PATH` | Yes (for self-signed / internal CA) | (empty) | Path to PEM CA bundle that signed the server cert. Required when the server cert isn't rooted in the system trust store (the default compose stack ships a self-signed cert at `deploy/test/certs/ca.crt`). |
|
|
|
|
If your certctl server has auth enabled (the default), you must provide the API key. The MCP server passes it through to every HTTP request.
|
|
|
|
Since v2.2 the certctl control plane is HTTPS-only. If the server cert is self-signed or chained to an internal CA, set `CERTCTL_SERVER_CA_BUNDLE_PATH` so the MCP server can verify the TLS handshake. Never set `CERTCTL_SERVER_TLS_INSECURE_SKIP_VERIFY=true` outside local development — it disables all certificate validation.
|
|
|
|
## Configuring Your MCP Client
|
|
|
|
Most MCP clients accept a JSON config block of this shape. Consult your client's documentation for the exact config-file location.
|
|
|
|
```json
|
|
{
|
|
"mcpServers": {
|
|
"certctl": {
|
|
"command": "/path/to/certctl-mcp",
|
|
"env": {
|
|
"CERTCTL_SERVER_URL": "https://localhost:8443",
|
|
"CERTCTL_SERVER_CA_BUNDLE_PATH": "/path/to/certctl/deploy/test/certs/ca.crt",
|
|
"CERTCTL_API_KEY": "your-api-key-here"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
After saving, restart your MCP client. You should see "certctl" appear in its tool list (the available-tools count varies by certctl version; the exact set is enumerated in `internal/mcp/tools.go`).
|
|
|
|
## Available Tools
|
|
|
|
The MCP server exposes the full REST API organized across 22 resource domains. Re-derive the live count via `grep -cE 'gomcp\.AddTool\(' internal/mcp/tools.go internal/mcp/tools_est.go` (the per-domain numbers below decay between releases — treat them as approximate at point of writing):
|
|
|
|
| Domain | Tools | Examples |
|
|
|--------|-------|---------|
|
|
| Certificates | 14 | List, get, create, update, archive, versions, renew, deploy, revoke, bulk-revoke / -renew / -reassign, claim/dismiss discovered |
|
|
| CRL & OCSP | 2 | Get DER CRL by issuer, check OCSP status |
|
|
| Issuers | 6 | List, get, create, update, delete, test connection |
|
|
| Targets | 5 | List, get, create, update, delete |
|
|
| Agents | 9 | List, list retired, get, register, retire, heartbeat, get work, submit CSR, report job status |
|
|
| Jobs | 5 | List, get, approve, reject, cancel |
|
|
| Policies | 6 | List, get, create, update, delete, list violations |
|
|
| Profiles | 5 | List, get, create, update, delete |
|
|
| Teams | 5 | List, get, create, update, delete |
|
|
| Owners | 5 | List, get, create, update, delete |
|
|
| Agent Groups | 6 | List, get, create, update, delete, list members |
|
|
| Audit | 2 | List events (with filters), get event by ID |
|
|
| Notifications | 4 | List, get, mark as read, requeue dead-letter |
|
|
| Stats | 5 | Summary, certs by status, expiration timeline, job trends, issuance rate |
|
|
| Metrics | 1 | System metrics (gauges, counters, uptime) |
|
|
| Digest | 2 | Preview digest, send digest |
|
|
| Health | 4 | Health check, readiness probe, auth info, auth check |
|
|
| Approvals | 4 | List, get, approve, reject (issuance approval workflow) |
|
|
| Health Checks | 8 | List, summary, get, create, update, delete, history, acknowledge |
|
|
| Renewal Policies | 5 | List, get, create, update, delete |
|
|
| Network Scan Targets | 6 | List, get, create, update, delete, trigger scan |
|
|
| Discovery | 4 | List discovered certs, get, list scans, summary |
|
|
| Intermediate CAs | 4 | List, create, get, retire (admin-gated) |
|
|
| Verification | 3 | List cert deployments, verify job, get job verification |
|
|
| EST | 6 | List/admin profiles, get cacerts, csrattrs, simpleenroll, simplereenroll |
|
|
|
|
Every tool has typed input parameters with `jsonschema` descriptions, so the AI knows exactly what arguments to provide and what each field means.
|
|
|
|
## Example Conversations
|
|
|
|
Once configured, you can interact with certctl through natural language:
|
|
|
|
**"Show me all certificates expiring in the next 14 days"**
|
|
The AI calls `certctl_list_certificates` with `status=Expiring` and interprets the results.
|
|
|
|
**"Renew the API production certificate"**
|
|
The AI calls `certctl_trigger_renewal` with `id=mc-api-prod`.
|
|
|
|
**"Who owns the payments gateway cert?"**
|
|
The AI calls `certctl_get_certificate` with `id=mc-payments-prod` and reads the `owner_id` and `team_id` fields.
|
|
|
|
**"Are any agents offline?"**
|
|
The AI calls `certctl_list_agents` and checks the heartbeat timestamps.
|
|
|
|
**"Revoke the old VPN cert — the key was compromised"**
|
|
The AI calls `certctl_revoke_certificate` with `id=mc-vpn-old` and `reason=keyCompromise`.
|
|
|
|
**"Give me a summary of the certificate fleet"**
|
|
The AI calls `certctl_dashboard_summary` for aggregate stats, then optionally `certctl_certificates_by_status` for the breakdown.
|
|
|
|
**"Create a new cert for staging.api.example.com owned by the platform team"**
|
|
The AI calls `certctl_create_certificate` with the common name, team ID, and owner ID.
|
|
|
|
## Architecture
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
AI["AI Assistant\n(any MCP client)"]
|
|
MCP["certctl MCP\ncmd/mcp-server/"]
|
|
SERVER["certctl Server\n:8443"]
|
|
|
|
AI <-->|"stdio"| MCP
|
|
MCP -->|"HTTP + Bearer token"| SERVER
|
|
|
|
MCP ~~~ TOOLS["REST API via MCP · 22 domains\nTyped input structs"]
|
|
```
|
|
|
|
The MCP server is intentionally thin:
|
|
|
|
- **No state** — every request is a pass-through HTTP call. Restart it anytime.
|
|
- **No new auth** — uses the same API key as the REST API.
|
|
- **No new dependencies** — just the official MCP Go SDK (`modelcontextprotocol/go-sdk`).
|
|
- **No new attack surface** — the AI can only do what the API key allows.
|
|
|
|
## Security Considerations
|
|
|
|
The MCP server inherits the security properties of the REST API:
|
|
|
|
- **API key scoping**: The MCP server uses whatever API key you configure. If certctl gets API key scoping in a future release (per-resource or per-action permissions), the MCP server will automatically respect those restrictions.
|
|
- **Audit trail**: Every tool call results in an HTTP request that's logged in the API audit middleware — actor, method, path, status, and latency are all recorded.
|
|
- **Read-only usage**: For read-only AI access, you could configure a restricted API key (when key scoping ships). Until then, be aware that the AI can call write endpoints (create, update, delete, revoke) if the API key permits it.
|
|
- **No private key exposure**: The MCP server never sees or transmits private keys — the same architectural guarantee as the REST API.
|
|
|
|
## Troubleshooting
|
|
|
|
**"MCP server not connecting"**
|
|
Check that `CERTCTL_SERVER_URL` is reachable from where the MCP binary runs. Try `curl $CERTCTL_SERVER_URL/health` to verify.
|
|
|
|
**"401 Unauthorized on every tool call"**
|
|
Your `CERTCTL_API_KEY` is missing or wrong. Check the key matches what the certctl server expects.
|
|
|
|
**"Tool calls return empty results"**
|
|
The certctl server might have no data. Run the demo seed (`docker compose up`) to populate demo data, or check that your database has records.
|
|
|
|
## What's Next
|
|
|
|
- [Quick Start](quickstart.md) — Get certctl running locally
|
|
- [OpenAPI Spec](openapi.md) — Full API reference and SDK generation
|
|
- [Architecture](architecture.md) — System design deep dive
|
|
- [Concepts](concepts.md) — Certificate lifecycle fundamentals
|