Skip to main content
Glama
pkinative-mcp

pkinative-mcp

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
PKINATIVE_MCP_ATNoPinned instant (ISO 8601, or epoch milliseconds) every omitted `at` / `notBefore` / `signingTime` falls back to, for reproducible verdicts and artefacts. Unset: the wall clock.
PKINATIVE_MCP_PORTNoWhen set to a valid port (1–65535), serves Streamable HTTP on http://127.0.0.1:<port>/mcp instead of stdio; any other value refuses to start.
PKINATIVE_MCP_PASSWORDNo(secret) The operator-side password of the encrypted PKCS#8 key or PKCS#12 file the tools open. It counts as the password of every password-accepting tool.
PKINATIVE_MCP_CACHE_DIRNoOpt-in content-addressed response cache (SHA-256 keyed, 1 h TTL, 256 MiB LRU, plaintext at rest).
PKINATIVE_MCP_HTTP_TOKENNo(secret) Opt-in bearer token for the HTTP transport (≥ 16 characters, no whitespace — a weaker value aborts startup).
PKINATIVE_MCP_OUTPUT_DIRNoAbsolute path of the one sandbox directory: every `path` input (PKI objects, content, keys) is read from it and `outputMode: 'file'` outputs are written under it (exclusive create — never overwritten). Unset ⇒ every tool works on inline PEM / base64 only.
PKINATIVE_MCP_MAX_INPUT_BYTESNoOperator ceiling for `limits.maxInputBytes` and the decoded size of one PKI object (the engine default). Integer ≥ 1024.67108864
PKINATIVE_MCP_MAX_KDF_ITERATIONSNoOperator ceiling for `limits.maxKdfIterations` and `limits.maxPkcs12KdfIterations` (the engine default): the CPU bound of a hostile PBES2 key or PKCS#12 file. Integer ≥ 1.10000000
PKINATIVE_MCP_MAX_PATHS_EXPLOREDNoOperator ceiling for `limits.maxPathsExplored` (the engine default): the RFC 5280 §6 path-building work bound. Integer ≥ 1.1000

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{}
prompts
{}
resources
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
convert_pemA

The RFC 7468 layer. mode:'decode' lists every block of a PEM text (index, label, DER size, offset, RFC 1421 headers under pemMode:'lax', SHA-256 of the DER) — label keeps one kind, extractIndex also delivers that block as an artefact (encoding 'der' by default, 'pem' or 'hex'; inline as an embedded resource, or as a sandbox file with outputMode:'file' + outputPath, never overwritten). mode:'encode' wraps DER bytes (derBase64 or a DER file path) into one strict PEM block under label (required) and delivers it as the artefact (64-character lines, LF). A private-key block (PRIVATE KEY, ENCRYPTED PRIVATE KEY, RSA/EC/DSA PRIVATE KEY) is listed by label and size but never extracted, and never produced: SECURITY_VIOLATION (use inspect_private_key) — DER that parses as a PKCS#8 key or a PKCS#12 file is refused under ANY label. Refuses: derBase64 in decode, pem in encode, an encoding other than 'pem' in encode, a file output without extractIndex (VALIDATION_ERROR); an index past the last block (NOT_FOUND); a label absent from the text (PKI_PEM_UNEXPECTED_LABEL); a malformed text (PKI_PEM_* — pemMode:'lax' tolerates whitespace, line length and headers; a missing base64 padding is refused in both modes). Token-frugal: verbosity:'summary' (count + labels), fields:[…].

resolve_oidA

Object identifiers in one call. mode:'name' → the registered name of each dotted OID (null when unregistered; ≤ 1000 per call); 'validate' → X.660 validity of each (at least two arcs, first arc 0–2, second ≤ 39 under 0 and 1, no leading zeros — reported, never an error); 'list' → the engine's registry (oid, name, standard), filtered by a case-insensitive substring of the name or the OID, paged with offset / limit (default 200, ≤ 2000); 'encode' → the X.690 bytes of each OID as hex, form 'content' (base-128 content octets, default) or 'tlv' (tag + length + content), relative:true for a RELATIVE-OID (tlv only); 'decode' → the dotted OID of hex octets (content, or a whole TLV with form:'tlv') with its name. Refuses: a member outside its mode, relative without form:'tlv' (VALIDATION_ERROR); an invalid OID in encode (PKI_OID_INVALID — validate reports it instead); malformed octets in decode (PKI_OID_INVALID, PKI_ASN1_*). Token-frugal: verbosity:'summary', fields:[…].

compute_fingerprintA

Read-only digest of ONE object. mode:'fingerprint' (default) hashes the exact DER of the input (SHA-256 by default; SHA-1 / SHA-384 / SHA-512; webcrypto:true uses Web Crypto, same result) — what openssl x509 -fingerprint prints; mode:'key-id' computes the RFC 5280 §4.2.1.2 key identifier (SHA-1 by default, or SHA-256) of the public key found in a certificate, a CSR or a PUBLIC KEY, reporting where it came from; mode:'shake256' squeezes outputLength bytes (default 32, ≤ 1048576) of SHAKE256 — the digest of an Ed448 CMS signer. hex is lowercase without separators, formatted follows separator (':' default) and letterCase (upper default). Any PEM label or DER is accepted (a private key may be digested: the digest reveals nothing and no input byte is returned); a bundle of several blocks is refused (extract one with convert_pem). Refuses: an algorithm outside its mode, SHA-384/512 for a key identifier, outputLength or webcrypto outside their mode (VALIDATION_ERROR); key-id on an object that holds no public key (PKI_X509_* / PKI_ASN1_*). Token-frugal: verbosity:'summary', fields:[…].

decode_asn1A

Read-only X.690 view of any DER object (BER with encodingRules:'ber'): the node tree — offset, header and content lengths, tag class / number / name, constructed flag, child count, a hex preview of each primitive (maxPreviewBytes, default 32) and the dotted value + registered name of every OBJECT IDENTIFIER — depth-first, capped at 20000 nodes (truncated:true). nodePath ('0.2.1' = child indices from the root; each node's path is what to pass) narrows to a subtree; read applies a typed reader to the selected node (boolean, integer, small-integer, enumerated, null, bit-string, octet-string, oid, relative-oid, string with stringType for an IMPLICIT tag, time with timeType) and returns value in wire form (bigint as decimal string, bytes as hex, time as epoch ms + ISO); reencode:true re-encodes the selected node through the engine and delivers it as an artefact (der / hex / pem for a labelled whole input; inline or a sandbox file) with reencode.identical; sequence:true decodes several top-level values back to back; allowTrailingData:true tolerates bytes after a single value. Refuses a private key or a PKCS#12 file — by PEM label, or when the DER parses as PKCS#8 / PKCS#12 — with SECURITY_VIOLATION (inspect_private_key / inspect_pkcs12 report them safely); a missing nodePath step is NOT_FOUND naming the range; a wrong reader tag is PKI_ASN1_UNEXPECTED_TAG; an IMPLICIT string / time without stringType / timeType is PKI_API_MISUSE; X.690 violations are PKI_ASN1_* (BER forms: encodingRules:'ber'). Token-frugal: verbosity:'summary' (node count, root tag, value), fields:['nodes.path','nodes.tagName'].

encode_asn1A

Encode a JSON node spec into DER through the engine's own encoders: spec is one node { type, … } — boolean (value), integer / enumerated (value: integer, decimal or 0x-hex string, negative allowed), null, bit-string (hex, unusedBits), named-bits (bits[]), octet-string (hex), oid / relative-oid (value), string (stringType utf8|numeric|printable|teletex|ia5|visible|universal|bmp, value), time (value: ISO instant or epoch ms; timeType UTCTime|GeneralizedTime|rfc5280 — the RFC 5280 rule by default), sequence / set / set-of (children[]; set-of is sorted as DER requires), explicit / implicit (tag, class universal|application|context|private, inner), tlv (class, tag, constructed, hex) and der (hex of one pre-encoded value, checked). Nesting is bounded by limits.maxDepth (PKI_LIMIT_EXCEEDED). The bytes are decoded back for the summary (root tag, node count). Output DER (default), hex (inline, ≤ 64 KiB) or PEM under label, inline as an embedded resource or as a sandbox file (never overwritten). Refuses a malformed node naming its JSON path ($.children[1].value …), a PEM encoding without label, a private-key label, a value that parses as a PKCS#8 key or a PKCS#12 file (SECURITY_VIOLATION — nothing here produces key material); a value the engine cannot encode is PKI_ASN1_* / PKI_OID_INVALID. Deterministic: the same spec gives the same bytes. The inverse of decode_asn1 (its tree uses the same vocabulary).

inspect_certificateA

Read-only RFC 5280 report of ONE certificate in one call: version, serial, signature algorithm, issuer and subject (RFC 4514 + RDNs), validity (epoch ms + ISO), public key (kind, bits, curve / modulus / exponent), every extension decoded (the 19 kinds pkinative knows; rawExtensions:true keeps them undecoded), self-issued flag, fingerprints (SHA-256 by default), and every engine diagnostic. extension:'subjectAltName' returns that one extension. issuer or checkSelfSignature:true adds a signature check — ONE signature against ONE key, which is NOT trust: verify_certificate_chain is the RFC 5280 §6 verdict (anchors, validity at an instant, names, policies, revocation). Token-frugal: verbosity:'summary', fields:[…]. Input: pem / derBase64 / path.

create_certificateA

Issue an X.509 v3 certificate from spec (subject, validity, extensions — basicConstraints, keyUsage, extendedKeyUsage, subjectAltName, issuerAltName, SKI / AKI, raw), signed through signer (a PKCS#8 key or a PKCS#12 file, imported as a NON-EXTRACTABLE Web Crypto key — nothing generates or exports a key here). Self-signed when no issuerCertificate is given (the subject key is derived from a plain signer.key, or taken from subjectPublicKey); issued when issuerCertificate is given (subjectPublicKey = a certificate, a CSR or a PUBLIC KEY is then required; the AKI is computed from the issuer). Serial 'random' by default (node:crypto, reported as summary.randomSerial), notBefore defaults to PKINATIVE_MCP_AT or now (whole seconds) — pin both for reproducible bytes. The new certificate is verified against the issuer (or itself) BEFORE it is returned (KEY_MISMATCH otherwise). RSA keys need signer.rsaScheme; SHA-1 needs allowSha1:true. Output PEM (default), DER or hex, inline or as a sandbox file (never overwritten).

encode_x509_structureA

Encode ONE X.509 building block to DER through pkinative's structural encoders, deterministically (the same spec gives the same bytes): structure is name, name-attribute, validity, spki, algorithm-identifier, attribute, extension, extensions, basic-constraints, key-usage, extended-key-usage, subject-alt-name, subject-key-identifier, authority-key-identifier or signature-algorithm; spec holds that structure's members (closed objects — an unknown or missing member is refused with VALIDATION_ERROR naming spec <path>; validity takes explicit notBefore AND notAfter, never an implicit now; a key identifier comes as hex or is computed from a certificate / CSR / PUBLIC KEY input). signature-algorithm takes signer instead of a spec (the key is imported NON-EXTRACTABLE and only its algorithm is read: ECDSA, Ed25519/Ed448, RSA with the REQUIRED rsaScheme — KEY_TYPE_REQUIRED for an encrypted key without keyType, SECRET_REQUIRED without a password). Output hex by default (structuredContent.hex, ≤ 64 KiB), or DER / PEM (PEM needs label), inline or as a sandbox file (never overwritten). The bytes are a fragment, not a signed object: nothing here signs. Use encode_asn1 for arbitrary ASN.1 and create_certificate / create_csr for whole objects.

decode_extensionA

Read-only decode of ONE X.509 extension value by its OID — the bytes INSIDE extnValue's OCTET STRING, as valueHex or value (DER by derBase64 / sandbox path) — exactly as inspect_certificate decodes a certificate's extensions: the 19 known kinds (basicConstraints, keyUsage, extendedKeyUsage, subjectAltName, nameConstraints, certificatePolicies, crlDistributionPoints, authorityInfoAccess, SCT list, …) come back with their members, any other OID as kind 'unknown' with its size; critical (default false) is reported and checked against the kind's RFC 5280 rule (a diagnostic, not a refusal). A value that is not well-formed for that OID is refused with PKI_X509_EXTENSION_MALFORMED (remedy: decode_asn1 for the plain tree). Token-frugal: verbosity:'summary', fields:[…]. The input is not a whole Extension SEQUENCE: pass the extnValue content, as inspect_certificate rawExtensions:true reports it in valueDer.

check_certificate_nameA

The RFC 6125 identity verdict, two forms. Certificate form: certificate + exactly one of host (DNS name as it appeared in the URL) / ip — is the certificate valid for that identity (subjectAltName dNSName / iPAddress; wildcards under allowWildcards, default true; allowCommonNameFallback, default false, reads a CN-only certificate's commonName and then needs chain — the issuing CAs, leaf's issuer first — so the CN is held to their dNSName name constraints as a SAN would be). Match form: presented (the certificate's dNSName, possibly a wildcard) + reference — one RFC 6125 §6.4 name comparison, no certificate. A negative verdict is valid:false + reasons[] (PKI_REASON_NAME_MISMATCH, …) in structuredContent — never isError. The name only: it is not trust (verify_certificate_chain with serverName is the full verdict), not validity, not revocation. Token-frugal: verbosity:'summary'.

check_certificate_purposeA

The RFC 5280 §4.2.1.12 purpose verdict: may certificate be used for purpose (serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, ocspSigning, any, or a dotted KeyPurposeId OID)? The path judged is [certificate, ...chain] (issuers, leaf's issuer first): under restrictIssuers (default true, what every Web PKI validator does) a CA's own extKeyUsage restricts what it issued; requireExplicitPurpose (default false) refuses a certificate with no extKeyUsage instead of reading its absence as unrestricted. A negative verdict is valid:false + reasons[] (PKI_REASON_PURPOSE_NOT_PERMITTED with the path[i] element) in structuredContent — never isError; an unknown purpose name is VALIDATION_ERROR. The purpose only: not trust, validity, names or revocation (verify_certificate_chain with purposes is the full verdict). Token-frugal: verbosity:'summary'.

inspect_csrA

Read-only RFC 2986 report of ONE PKCS#10 certification request: subject (RFC 4514 + RDNs), public key (kind, bits, curve / modulus), every attribute, the extensions requested through extensionRequest decoded as a certificate's would be (rawExtensions:true keeps them undecoded), whether a challengePassword is present (never its text), the signature algorithm, and — by default (verify, one Web Crypto verification) — the proof of possession: signature.valid tells whether the request was signed by the key it carries, with reasons[] (PKI_REASON_SIGNATURE_NOT_CHECKED for SHA-1 without allowSha1:true). That is NOT trust: no CA signed a CSR, and a CA is free to ignore every extension it asks for. Pair with create_certificate (subjectPublicKey = the request) to issue from it. Token-frugal: verbosity:'summary' (subject, key kind, extension kinds, signatureValid), fields:[…]. Input: pem / derBase64 / path (.csr .p10 .der .pem).

create_csrA

Build a PKCS#10 certification request (RFC 2986) from spec (subject; the extensions to request — subjectAltName, keyUsage, extendedKeyUsage, basicConstraints, subjectKeyIdentifier, raw — carried in extensionRequest; issuerAltName / authorityKeyIdentifier are refused as the issuer's), signed through signer (a PKCS#8 key or a PKCS#12 file imported as a NON-EXTRACTABLE Web Crypto key; nothing generates or exports a key here). The public key certified is the signing key's own: derived from a plain signer.key, taken from the PKCS#12 certificate, or given as subjectPublicKey (a certificate, a CSR or a PUBLIC KEY — required for an encrypted PKCS#8 key, whose type then comes from it). The proof of possession is verified BEFORE the request is returned: KEY_MISMATCH when the key and the public key differ. RSA keys need signer.rsaScheme; an encrypted key needs signer.password (or PKINATIVE_MCP_PASSWORD); SHA-1 needs allowSha1:true. ECDSA / RSA-PSS signatures are randomised (PRODUCES). Output PEM (default), DER or hex, inline or as a sandbox file (never overwritten). inspect_csr reads it back; create_certificate issues from it (subjectPublicKey = the request).

verify_certificate_chainA

The one-call trust verdict for a certificate: builds a path from leaf through candidates to one of YOUR trustAnchors, verifies every signature, validates the path at at (RFC 5280 §6: validity, basic constraints, key usage, name constraints, policies — policy inputs), checks serverName (RFC 6125 host or IP) and purposes (extended key usage along the path), and judges revocation from the CRL / OCSP bytes YOU supply (crls, ocspResponses; requireRevocation:true demands evidence for every certificate below the anchor; this server never fetches). A negative verdict is valid:false + reasons[] (PKI_REASON_* codes) in structuredContent — never isError; isError is reserved for inputs the engine refuses. Pin at (ISO 8601) for a reproducible, cacheable verdict; evaluatedAt echoes the instant used. Token-frugal: verbosity:'summary' (valid, reasonCodes, pathLength). Prefer it over inspect_certificate.signature for any trust question; build_certificate_path / validate_certificate_path explain a failing path.

build_certificate_pathA

Path discovery only (RFC 5280 §6 path building): walks from leaf through the untrusted candidates to one of YOUR trustAnchors under the structural rules evaluated at at (validity, basic constraints, key usage, name constraints, the policy inputs; purposes steer discovery but the leaf's extended key usage is judged by verify_certificate_chain, not here) and reports the path it found, leaf first up to the anchor. NO signature is verified: the engine marks every link PKI_REASON_SIGNATURE_NOT_CHECKED, so valid is always false and the verdict of this tool is found (a path reached an anchor, structuralReasons empty); the leaf alone with PKI_REASON_NO_TRUST_ANCHOR when none did. Use it to learn which intermediates a chain needs, then validate_certificate_path (ordered path, signatures) or verify_certificate_chain (the full verdict with revocation). Never isError for a negative result. Pin at for a reproducible, cacheable result. Token-frugal: verbosity:'summary' (found, structuralReasonCodes, pathLength).

validate_certificate_pathA

RFC 5280 §6.1 validation of a path YOU ordered (path, leaf first, then each issuer) against YOUR trustAnchors at at: validity, basic constraints, key usage, name constraints and the policy inputs. By default every link signature is verified first — certificate i against i+1, the last against the anchor whose subject matches its issuer — and reported per link in signatures[] (PKI_REASON_SIGNATURE_INVALID fails the verdict). checkSignatures:false gives a structural verdict: valid stays false (PKI_REASON_SIGNATURE_NOT_CHECKED on every link) and failingReasons carries what actually fails; signaturesChecked says which one you got. No revocation here (verify_certificate_chain judges CRL / OCSP evidence; check_crl_revocation / check_ocsp_status judge one certificate). A negative verdict is valid:false + reasons[], never isError. Pin at for a reproducible, cacheable verdict. SHA-1 signatures need allowSha1:true. Token-frugal: verbosity:'summary'.

inspect_crlA

Read-only RFC 5280 §5 report of ONE certificate revocation list: version, issuer (RFC 4514 + RDNs), signature algorithm, thisUpdate / nextUpdate, CRL number, delta indicator and base CRL number, issuing distribution point, entry count (the entries are counted by walking, never all decoded — bounded by limits.maxRevokedCertificates), every extension decoded, size, diagnostics. find looks ONE serial up (serialHex, or a certificate whose serial and issuer are used) and returns entry (revoked, revocationDate, reason, invalidityDate, entry extensions). issuer adds a signature check — ONE signature against ONE key, not a verdict: check_crl_revocation judges a certificate against the list (freshness, scope, delta), verify_certificate_chain judges the whole chain. Token-frugal: verbosity:'summary' (issuerString, thisUpdateIso, nextUpdateIso, crlNumber, entryCount, isDelta, revoked, signatureValid). Input: pem / derBase64 / path.

check_crl_revocationA

The RFC 5280 §6.3 revocation verdict of ONE certificate against ONE base crl you fetched (this server never fetches), evaluated at at: the list must be in scope for the certificate (issuer, issuing distribution point, reasons), fresh (nextUpdate, or staleTolerance ms past it), signature-verified, and the serial must not be listed (PKI_REASON_REVOKED with the reason otherwise). delta applies a delta CRL over the base (it must carry a deltaCRLIndicator — VALIDATION_ERROR otherwise — and name the base's number). issuer is the CRL issuer certificate: the base (and delta) signatures are verified against it and reported as signatureVerified / deltaSignatureVerified; without it the engine refuses the unverified list, so pass it for a real verdict. valid:false + reasons[] is a verdict, never isError. Pin at for a reproducible, cacheable verdict. SHA-1 signatures need allowSha1:true. Token-frugal: verbosity:'summary'. inspect_crl shows the list's facts; verify_certificate_chain with crls:[…] is the whole-chain form.

create_ocsp_requestA

Build the DER OCSPRequest (RFC 6960 §4.1) asking a responder for the status of certificate, identified by the CertID computed from issuer (the CA that issued it — its name and key hashes) and the serial. hashAlgorithm 'SHA-1' (default, the engine default and what every responder answers) or 'SHA-256' (recommended where the responder supports it). nonce adds the RFC 8954 nonce extension: 'random' (16 bytes from node:crypto, summary.randomNonce:true) or your own hex (1–32 bytes); keep summary.nonceHex to demand it back with check_ocsp_status. certIdOnly:true returns the bare CertID (der / hex). This server never sends anything: POST the bytes yourself (Content-Type application/ocsp-request) to the URL in the certificate's authorityInfoAccess (inspect_certificate), then judge the answer with check_ocsp_status { response, certificate, issuer, nonce }. summary.certIdHex is the CertID to match in the response. Output DER (default, .orq), PEM or hex, inline or as a sandbox file (never overwritten).

inspect_ocsp_responseA

Read-only RFC 6960 §4.2 report of ONE OCSP response (the DER a responder returned): the response status (only successful carries an answer), the BasicOCSPResponse — responder id (by name or by key hash), producedAt, every SingleResponse (CertID: hash algorithm, issuer name / key hashes, serial; status good / revoked with time and reason / unknown; thisUpdate, nextUpdate, extensions), response extensions, the embedded certificates, the signature algorithm — whether an RFC 8954 nonce is echoed, and diagnostics (bounded by limits.maxOcspSingleResponses). responder adds a signature check against that certificate; omitted, the embedded certificates are tried and the one that verifies is named. ONE signature against ONE key is NOT a status verdict: check_ocsp_status matches the CertID, judges the responder's authorisation (RFC 6960 §4.2.2.2), freshness and nonce. Token-frugal: verbosity:'summary' (status, responses[serialNumber, status], noncePresent, signatureValid). Input: derBase64 / path (or PEM).

check_ocsp_statusA

The RFC 6960 verdict of ONE OCSP response you fetched (this server never queries a responder) for ONE certificate: the CertID is recomputed from issuer (with the hash the response used, or hashAlgorithm) and must be answered; the signature is verified with responder, else the certificates embedded in the response, else the issuer itself; the signer must be authorised (RFC 6960 §4.2.2.2: the CA, a delegate the CA issued carrying id-kp-OCSPSigning, or responderTrusted:true for a trust you hold out of band); then the engine judges the status (good; PKI_REASON_REVOKED / unknown otherwise), freshness at at (thisUpdate / nextUpdate / producedAt, staleTolerance and futureTolerance ms), and the nonce (nonce must be echoed; requireNonce:true refuses a response without one). A non-successful response status is a negative verdict with status null. valid:false + reasons[] is a verdict, never isError. Pin at for a reproducible, cacheable verdict. Token-frugal: verbosity:'summary' (valid, reasonCodes, status, signatureVerified, responderAuthorized, responder). create_ocsp_request builds the request you POST; inspect_ocsp_response shows the facts without a verdict.

sign_cmsA

Produce an RFC 5652 SignedData over content (attached, or detached:true) or over a precomputed contentDigest (detached only — the PDF /ByteRange case), with ONE signer: signer (a PKCS#8 key or a PKCS#12 file, imported as a NON-EXTRACTABLE Web Crypto key — nothing generates or exports a key here) and certificate (required with signer.key; a PKCS#12 bag's certificate serves otherwise), chain and crls parsed (never embedded unread) then embedded. Signed attributes are always written: contentType, messageDigest, signingTime (default PKINATIVE_MCP_AT or now, whole seconds; 'none' omits it — pin it for reproducible bytes), SigningCertificateV2 and CMSAlgorithmProtection (on by default; CAdES / PAdES need them), plus your signedAttributes / unsignedAttributes (DER Attributes as hex). sid defaults to issuerAndSerialNumber (keep it unless the reader is known). The new signature is verified against the signer certificate BEFORE it is returned (KEY_MISMATCH otherwise). RSA keys need signer.rsaScheme; SHA-1 needs allowSha1:true. Output DER (default; .p7s), PEM (label CMS) or hex, inline or as a sandbox file (never overwritten). Add a counter time-stamp afterwards with amend_cms.

verify_cmsA

The one-call verdict on a CMS SignedData (a .p7s, an S/MIME body, a PDF /Contents): for EVERY signer, the signed attributes (contentType, messageDigest, ESS signing-certificate, CMSAlgorithmProtection), the content digest (attached, or content / contentDigest for a detached signature — a detached signature without its content is NOT valid), the signature, the certificate's chain through candidates and the embedded certificates to one of YOUR trustAnchors at at (RFC 5280 §6, purposes along the path), every counter time-stamp (RFC 3161, verified like verify_timestamp), and revocation from the CRL / OCSP bytes YOU supply plus those the message carries (requireRevocation:true demands evidence; this server never fetches). atTimeStamp:true is long-term validation: a signer's chain is judged at its verified time-stamp instead of at. requireSigningCertificate / requireAlgorithmProtection enforce the CAdES / RFC 8933 profile. A negative verdict is valid:false + reasons[] (PKI_REASON_* codes, per signer too; intact tells unaltered from untrusted) — never isError; isError is for inputs the engine refuses (content given for an attached message, both content and contentDigest). Pin at for a reproducible, cacheable verdict. Token-frugal: verbosity:'summary' (valid, reasonCodes, per-signer codes).

inspect_cmsA

Read-only RFC 5652 report of ONE CMS SignedData (.p7s / .p7m / .p7b, CMS or PKCS7 PEM, or a PDF /Contents): version, content type, attached content length (null = detached), digest algorithms, the embedded certificates (subject / issuer / serial), CRL and OCSP counts, and every signer — identifier (issuer + serial or key id), algorithms, signing time as CLAIMED, ESS signing-certificate, CMSAlgorithmProtection, signed and unsigned attributes (oid, name, value count), counter time-stamp count and the signature value as hex (what a counter time-stamp stamps: fields:['signerInfos.signature'], hash it, create_timestamp_request digestHex). signerCertificate (+ signerIndex, and content for a signer without signed attributes) adds signerSignature.valid — ONE signer against ONE key, a key check and NOT the verdict: verify_cms judges trust, digests, time-stamps and revocation. Trailing bytes are refused unless allowTrailingData:true; BER producers need encodingRules:'ber'. Refuses: NOT_FOUND for a signerIndex past the last signer, PKI_* for an unreadable structure. Token-frugal: verbosity:'summary', fields:[…].

amend_cmsA

The one edit a signed message legitimately receives afterwards, every signed octet unchanged: operation 'add-unsigned-attribute' appends attribute (one DER Attribute as hex) to signer signerIndex's unsigned attributes; 'add-timestamp' appends token — a TimeStampToken, or the whole TimeStampResp a TSA returned (unwrapped; a declined response is refused) — as id-aa-signatureTimeStampToken (CAdES-T / PAdES B-T). A counter time-stamp stamps the signer's SIGNATURE VALUE, not the document: the token's imprint is checked against that value BEFORE anything is written and a token over anything else is refused with VALIDATION_ERROR (data.detail PKI_REASON_TSP_IMPRINT_MISMATCH, with the remedy: inspect_cms fields:['signerInfos.signature'] → hash → create_timestamp_request digestHex → POST to your TSA yourself → amend_cms). Trust is not judged here (verify_cms is). Deterministic for the same inputs. Refuses: NOT_FOUND for a signerIndex past the last signer, PKI_API_MISUSE for a signed-only attribute type, PKI_* for unreadable bytes. Output DER (default), PEM (CMS) or hex, inline or as a sandbox file (never overwritten).

create_timestamp_requestA

Build the RFC 3161 TimeStampReq you POST to a TSA yourself (Content-Type application/timestamp-query; this server never talks to a network): the message imprint is hashAlgorithm (SHA-256 default, SHA-384, SHA-512) over data, or digestHex already computed with it (wrong length → VALIDATION_ERROR). nonce:'random' draws 63 bits from node:crypto (summary.randomNonce:true; never cached) — without a nonce nothing ties the response to THIS request; a decimal or 0x-hex value pins it. policy names the TSA policy, certReq (default true) asks for the TSA certificate in the token. For a counter time-stamp over a CMS signature, the data is the signer's SIGNATURE VALUE (inspect_cms fields:['signerInfos.signature']), never the document. Keep the request bytes (or summary.imprintHex and nonce): verify_timestamp checks the response against them. Output DER (default; .tsq), PEM or hex, inline or as a sandbox file (never overwritten).

inspect_timestampA

Read-only RFC 3161 report of a TimeStampResp (.tsr: status, statusStrings, failInfo, and the token when granted), a TimeStampToken (.tst) or a bare TSTInfo — auto-detected in that order unless as names one. The TSTInfo view: policy, message imprint (algorithm + hex), serial, genTime (epoch ms + ISO), accuracy (null = undeclared), ordering, nonce (decimal), the TSA name as STATED (a hint, not an identity), extensions; the token view adds its CMS envelope (signer count, embedded certificates). Parsing proves nothing — verify_timestamp is the verdict (imprint, TSA signature, chain, revocation). Refuses with the PKI_* cause when the bytes are none of the three (as:'…' shows one reading's exact refusal); strict:true escalates the first engine warning (OpenSSL tokens carry PKI_DIAG_CMS_SET_NOT_SORTED). Token-frugal: verbosity:'summary' (status, genTime, serial, imprint).

verify_timestampA

The one-call verdict on an RFC 3161 time-stamp: exactly one of token (a TimeStampToken, .tst) or response (the whole TimeStampResp, .tsr — a declined response is PKI_REASON_TSP_NOT_GRANTED), judged against what YOU say was stamped — request (the .tsq you sent: imprint, nonce and policy are all checked, the only way to catch a replay), data (hashed with the token's own algorithm) or digestHex; at least one is required, because a token verified without what it stamps proves nothing (VALIDATION_ERROR otherwise). The TSA's signature is verified, its certificate must chain to one of YOUR trustAnchors (through candidates and the certificates the token carries) at at with a critical timeStamping EKU (allowNonCriticalTimeStampingEku:true for deployed TSAs that are not), and revocation is judged from the CRL / OCSP bytes YOU supply (requireRevocation:true demands evidence). A negative verdict is valid:false + reasons[] (PKI_REASON_* codes) — never isError. genTime / earliest / latest (accuracy applied) are established only when valid. Pin at for a reproducible, cacheable verdict. Token-frugal: verbosity:'summary'.

inspect_private_keyA

Describe a PKCS#8 private key WITHOUT its password: format ('pkcs8' or 'encrypted-pkcs8'), version, kind (ec / rsa / rsa-pss / ed25519 / ed448 / x25519 / x448 / ml-dsa / unknown), curve, algorithm OID and name, attributes, whether a public key is embedded; for an encrypted key the PBES2 parameters (PBKDF2 iterations, PRF, AES key size, salt length, ciphertext size) or the name of a scheme pkinative refuses (PKCS#12 / PKCS#5 v1.5 ciphers). tryImport:true additionally imports the key into a NON-EXTRACTABLE Web Crypto key and reports the handle (type, algorithm, usages) — an encrypted key then needs password (or PKINATIVE_MCP_PASSWORD; SECRET_REQUIRED) and keyType (KEY_TYPE_REQUIRED), an RSA key needs rsaScheme (RSA_SCHEME_REQUIRED, no default); a wrong password is PKI_CRYPTO_DECRYPTION_FAILED and a legacy cipher PKI_KEY_ENCRYPTION_UNSUPPORTED with the openssl pkcs8 remedy. No result ever carries a key byte, a scalar or the password: the report is an allow-list view and the key never leaves the engine (nothing exports or writes a key here). A PUBLIC KEY or a non-PKCS#8 DER is refused with the PKI_* cause. A call carrying a password is never cached. Token-frugal: verbosity:'summary' (format, kind, curve, encrypted, scheme, imported). Input: pem / derBase64 / path. inspect_pkcs12 is its PKCS#12 counterpart; use it before sign_cms / create_csr to learn what a signer needs (keyType, rsaScheme).

inspect_pkcs12A

Describe a PKCS#12 (.p12 / .pfx) WITHOUT its password: version, the MAC construction (kind 'pbmac1' RFC 9579 with its PBKDF2 / HMAC parameters, or 'pkcs12-kdf' — the RFC 7292 Appendix B MAC pkinative never verifies; null when the file has none), every SafeContents with its encryption scheme (PBES2 parameters, or the name of a legacy RC2 / 3DES scheme pkinative refuses), and the bags of the unencrypted SafeContents. With password (or PKINATIVE_MCP_PASSWORD): macVerified (true / false — a wrong password or an altered file is a REPORT, not an error; null without a MAC) and opened: every SafeContents opened, each bag as an allow-list view — a certBag by subject / issuer / serial, a keyBag or pkcs8ShroudedKeyBag by its algorithm, curve and encryption parameters, a crlBag by size. No result ever carries a key byte, a bag value or the password, not even from an unencrypted keyBag. Refusals: a wrong password on an ENCRYPTED SafeContents is PKI_CRYPTO_DECRYPTION_FAILED; a legacy MAC PKI_KEY_MAC_UNSUPPORTED and a legacy cipher PKI_KEY_ENCRYPTION_UNSUPPORTED (only when a password asks for them), each with the OpenSSL 3.4 conversion remedy; a public-key integrity-mode file PKI_KEY_MAC_UNSUPPORTED at parse. Nothing is imported here: open_pkcs12 is the one-call verdict with non-extractable key import. limits.maxPkcs12Bags and maxPkcs12KdfIterations bound the file's declared cost. Token-frugal: verbosity:'summary' (macKind, macVerified, contentCount, bagCount, bagKinds).

open_pkcs12A

Open a PKCS#12 (.p12 / .pfx) in one call with password (or PKINATIVE_MCP_PASSWORD; SECRET_REQUIRED without one): the RFC 9579 PBMAC1 MAC is verified, every PBES2 SafeContents decrypted, every key unwrapped by the host straight into a NON-EXTRACTABLE Web Crypto key — described (algorithm, handle type, usages) and never exported — every certificate parsed (compact refs) and every CRL counted. The verdict is valid + integrity ('verified' | 'unverified' | 'mismatch') + reasons[]; a negative verdict is a REPORT, never isError: a wrong password or altered file → integrity 'mismatch' + PKI_REASON_PKCS12_MAC_MISMATCH (nothing decrypted); a legacy RC2 / 3DES file → PKI_REASON_PKCS12_ENCRYPTION_UNSUPPORTED (convert it once with openssl pkcs12 -legacy then -export -pbmac1_pbkdf2); no MAC or a legacy MAC → PKI_REASON_PKCS12_INTEGRITY_UNVERIFIED unless allowUnverifiedIntegrity:true (trusted origin only); an RSA key without rsaScheme ('pkcs1' | 'pss', hash default SHA-256; no default scheme) → PKI_REASON_PKCS12_RSA_SCHEME_UNSPECIFIED with that key's signingKey null. exportCertificates:true attaches a PEM bundle of the certificates then the CRLs (PEM only; inline embedded resource or a sandbox .pem file, never overwritten) — only from a file that opened; nothing is exported otherwise, and never a key. No result carries a key byte, a bag value or the password; a call with a password is never cached. Token-frugal: verbosity:'summary' (valid, integrity, reasonCodes, counts). Prefer inspect_pkcs12 first: it describes the file (MAC kind, bags) without any password.

describe_engineA

Offline preflight (no input needed): server / engine / tool-API versions, whether this host's Web Crypto can verify, sign and decrypt (canVerify / canSign / canDecrypt) and satisfies the engine's Node floor, where randomness comes from, the engine's 22 default limits, the operator ceilings (PKINATIVE_MCP_MAX_INPUT_BYTES, PKINATIVE_MCP_MAX_KDF_ITERATIONS, PKINATIVE_MCP_MAX_PATHS_EXPLORED), every server cap, the sandbox / cache / PKINATIVE_MCP_AT state and the extension allow-lists, the 57 PKI_* error codes (with class), 43 PKI_REASON_* and 97 PKI_DIAG_* codes, the wrapper codes, the purposes and key-usage names, the doctrine refusals (what this server will NOT do and the remedy) and the engine surface deliberately not exposed as inputs. network is always 'none'. Call it first in a session, or whenever a cap / ceiling refusal needs interpreting.

explain_codeA

Explain any code this server can emit, from the registries vendored at the pkinative v1.0.0 tag: the 57 PKI_* engine error codes (coarse class, PkiError subclass, when raised, CWE, the engine's remedy and the MCP input that lifts it), the 43 PKI_REASON_* codes a verdict returns, the 97 PKI_DIAG_* diagnostics (with severity) and the wrapper's own codes (VALIDATION_ERROR, SECURITY_VIOLATION, LIMIT_CEILING_EXCEEDED, SECRET_REQUIRED, …). list:true enumerates (kind filters one vocabulary). Branch on codes, never on message text.

draft_governance_issueA

Produce a LOCAL GitHub issue draft + compliance report for pkinative / pkinative-cli / pkinative-mcp and NEVER submit it — the agent drafts, a human reviews and files it under their own identity (Human-In-The-Loop contract, .github/AGENT_RULES.md). No GitHub write path and no network path exist in this server. The draft is checked against the zero-dependency + reproduction + no-secret policy; a violation (new runtime dependency, an embedded private key or password, missing reproduction, duplicateSearchPerformed:false) throws GOVERNANCE_VIOLATION. targetRepo 'pkinative' for engine behaviour (parsing, verification, limits, refusals), 'pkinative-cli' for the CLI, 'pkinative-mcp' for wrapper behaviour (schemas, sandbox, transport). Returns markdown inline (outputMode:'file' also writes a .md inside the sandbox). Use it only after reproducing a defect with TEST material. Present the draft AND the report to the user, then STOP.

Prompts

Interactive templates invoked by user choice

NameDescription
governance_contractThe non-negotiable AI-governance / Human-In-The-Loop contract for pkinative-mcp: the agent is a draftsman, the human is the only gate, zero runtime dependencies, no autonomous GitHub writes, no network, no secret in a draft.
draft_issue_workflowStep-by-step workflow for drafting a GitHub issue with the draft_governance_issue tool and handing it to a human for review and submission.
verify_before_trustThe minimal sequence (inspect_certificate → verify_certificate_chain with a pinned instant → offline revocation evidence) to run on any certificate before relying on it, and why a verified signature is not trust.
issue_certificateHow create_csr / create_certificate chain together (self-signed CA, request, issued leaf), what is derived, what is verified before the bytes are returned, and the key / scheme / instant rules.
sign_and_timestampsign_cms, the counter time-stamp over the signature value (create_timestamp_request → your TSA → amend_cms), and verify_cms as the one verdict.
offline_revocationWhere to fetch CRL / OCSP evidence yourself, how create_ocsp_request builds the request you send, and how check_crl_revocation / check_ocsp_status / verify_certificate_chain judge the bytes you bring.
open_keys_safelyinspect_private_key / inspect_pkcs12 / open_pkcs12: describe before opening, PBES2 / PBMAC1 doctrine, the legacy-file remedy, the two password channels, and why no report ever holds a key byte.

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A4.3/5.0

Scored across 34 tools

Disambiguation4/5

The tools split into clear families — inspect (facts), verify/check (verdicts), create/sign, and encode/decode — and descriptions repeatedly point to the correct alternative. However, many adjacent inspect/verify/check pairs across CRL, OCSP, CMS, timestamp, and PKCS#12 create realistic routing ambiguity for less expert agents.

Naming Consistency5/5

Every tool is snake_case and starts with a verb, with resource nouns reused consistently (certificate, crl, ocsp, cms, timestamp, pkcs12). Minor variation such as open_pkcs12 vs inspect_pkcs12 still fits the overall pattern.

Tool Count2/5

34 tools is a very large surface; even for a broad PKI domain it exceeds the comfortable range and splits capabilities across many narrowly scoped calls. The scope justifies most tools individually, but the set is heavy for an agent to navigate reliably.

Completeness4/5

Coverage is unusually deep across certificates, CSRs, chain building/validation, CRL/OCSP, CMS, timestamping, keys, PKCS#12, ASN.1/PEM, and error introspection. Gaps are mostly intentional doctrine — no key generation/export, no network fetching — plus no enveloped CMS encryption/decryption, so the lifecycle is not fully closed.

Maintenance

ActivityMaintained
ResponsivenessNo recent community issues