MCP server
The Model Context Protocol lets an assistant call tools on your behalf. For a certificate platform the appeal is obvious: “which certificates expire this month”, “why did that renewal fail”, “open a renewal for the shop certificate” are all questions people already ask, phrased the way they already ask them.
Design
Section titled “Design”A layer over the public API, not a second data path. The MCP server calls the same API you can call, so it inherits token scopes, rate limits and tenant isolation rather than reimplementing them. A tool cannot reach data your credentials cannot.
Read-first. Inventory, expiry and renewal status need certs:read. Opening a
renewal needs certs:renew, explicitly. Over OAuth only certs:read
is available at all, so an assistant connected by a person can read and nothing else.
Approval stays human. No MCP tool approves a renewal, because no token can. An assistant can prepare work; a person authorises spending.
Seven, shaped around questions rather than around endpoints.
| Tool | Answers |
|---|---|
nextpki_whoami |
Which tenant and scopes am I working with |
nextpki_list_certificates |
What is in the inventory |
nextpki_expiring_certificates |
What expires in the next N days |
nextpki_get_certificate |
Everything about one certificate |
nextpki_list_renewals |
What renewals exist, in which state |
nextpki_get_renewal |
Why one renewal is where it is |
nextpki_request_renewal |
Open a renewal request, needs certs:renew |
Two transports, two ways to authenticate
Section titled “Two transports, two ways to authenticate”stdio is one process per client, started by the client itself: Claude Code, Codex, editors. It runs on your machine and authenticates with a static API token from the environment. Simple, and appropriate, because the process and the token are both yours.
Streamable HTTP is one server for many clients. It holds no credentials of its own for user data: every request carries its own bearer token, and the server builds a per-request session bound to that token. A shared server with one token would be a tenant leak, so a server-side token is refused outright in this mode.
With an authorization server configured, the HTTP transport accepts only OAuth tokens. That is deliberate: leaving the static-token path open alongside it would leave a weaker route in place, and a weaker route that exists is the one an attacker uses.
What OAuth changes for you
Section titled “What OAuth changes for you”Nothing you have to implement, but two behaviours are worth knowing.
An access token issued for the MCP server is rejected by the API with 401. That is
the audience binding working: the token names the MCP server, not the API. The server
exchanges it internally for a short-lived API token and records the exchange in the
audit log. Details in OAuth for assistants.
Reusing a rotated refresh token revokes the whole grant, so a client that retries
with an old token loses its access rather than getting a fresh one. Treat
invalid_grant on refresh as “authorize again”.
Certificate data is attacker-controlled
Section titled “Certificate data is attacker-controlled”A subject or SAN is whatever the requester put in it. Someone can obtain a
certificate whose Common Name reads ignore previous instructions and …, and that
string lands in an assistant’s context looking like it came from a trusted source.
This is not hypothetical for this product specifically: CT-discovered certificates are issued by third parties for domains you own, so the content is genuinely outside your control.
What the server does about it:
- Every tool response that carries certificate-derived strings is prefixed with an explicit notice that the values are data, not instructions, and that they may contain text crafted to look like instructions.
- Tool output alone never authorises a write. Writes need an explicit scope, and
certs:renewis not available over OAuth at all. - Approval remains a human action in the console.
If you build your own MCP server against the NextPKI API, do the same: treat every string from the inventory as hostile input rather than instructions. Marking the boundary between your instructions and fetched data is the part that is easy to forget and hard to retrofit.
Wanted input
Section titled “Wanted input”The order of further tools depends on what people actually want to ask. If you have a use case, a Slack workflow, an internal assistant, a CI agent, describing it affects what gets built next.