gpg-mcp
Provides tools for GnuPG (GNU Privacy Guard) OpenPGP operations, allowing an AI agent to sign, verify, encrypt and decrypt messages, manage and inspect public/private keys, create keys, and handle revocation certificates. Private key material stays in gpg-agent and passphrases go only through pinentry, with allow-listing, approval gating, recipient preflight checks and audit logging.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@gpg-mcpverify the signature on release.tar.gz.asc"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
gpg-mcp
A security-focused Model Context Protocol server for GnuPG.
gpg-mcp lets an AI agent sign, verify, encrypt, decrypt and manage OpenPGP keys without ever
receiving private key material. Private keys stay in gpg-agent; passphrases go only into
pinentry; high-risk operations require an explicit human approval; destructive operations require
typing a fingerprint suffix.
Threat model in one sentence: the MCP server is a policy boundary, not an OS sandbox. It prevents the agent from receiving private keys and from silently using them. For protection against an agent that already has shell access as the same OS user, run the signer as a separate user or use a hardware token (see Stronger isolation).
Why this exists
Most “GPG MCP” wrappers are thin subprocess shells: any tool with a command: str parameter, or
an agent that can ask for --export-secret-keys, defeats the point. gpg-mcp instead exposes a
fixed, typed surface with deliberate omissions:
No raw command execution. There is no
run_gpg(args)tool.No secret-key export or backup.
--export-secret-keys,--export-secret-subkeysand--export-secret-ssh-keyare unreachable.No passphrase parameters. No
--passphrase,--passphrase-fd,--passphrase-file, no loopback pinentry, no passphrase in environment variables or stdin.No
--homediroverride. The keyring is fixed by configuration.No short key IDs. Operations require full 40- or 64-hex fingerprints.
No automatic key retrieval. Verification does not silently fetch keys from the network.
No hidden recipients. Decryption preflights the ciphertext and refuses messages not addressed to an allowed key.
A response guard. If gpg ever emits something that looks like private key material, the call fails and the output is dropped.
Related MCP server: Agent File Guardian
Security model
What the server guarantees
Private keys never cross the MCP boundary. Listing secret keys returns metadata only. Signing and decryption are performed by
gpg-agent.The server never sees a passphrase.
--pinentry-mode askis forced; pinentry talks to the human directly. The server cannot read the passphrase from argv, env or stdin.Signing and decryption use allow-listed keys. With a shared
~/.gnupg, an empty allow-list denies both. With a dedicated keyring, the keyring itself is the allow-list boundary.Decryption is recipient-checked. Before decrypting,
gpg --list-packetsis used to confirm the ciphertext is addressed to an allowed key. Hidden recipients are refused by default, even when the same message also has a visible allowed recipient.Invalid embedded signatures abort decryption. If
verify=true(default) and the decrypted message carries a bad, expired or revoked signature, the plaintext is not returned.validis the policy result; the cryptographic result is reported separately.Authorization is a fail-closed registry. Every high-risk tool names a registered operation. Unknown operations require approval. Composite operations (
sign_and_encrypt, andencryptwithsign_with) inherit the approval requirement of every constituent, so a policy that gates only signing or only encryption cannot be bypassed.Revocation certificates are validated before import. The certificate must contain a revocation signature, must reject secret-key packets, must target exactly the requested fingerprint, and must demonstrably revoke that key in an ephemeral keyring.
apply=falseis a true dry run and never touches the real keyring.Destructive operations require typed confirmation. Deleting, revoking, or applying a revocation certificate requires the operator to type the last 8 characters of the fingerprint.
Every high-risk operation is audited. The ledger records operation names, fingerprints, payload SHA-256, size and the decision — never plaintext, payload previews or passphrases.
No shell. Commands are
execve-style argv arrays. No shell interpolation, no option injection from user strings.Minimal environment.
LD_*,GPG_AGENT_INFO,GPG_TTY,SSH_AUTH_SOCKand other variables are not passed to gpg. Only the display/session variables pinentry needs are kept.Sanitised binaries and paths.
gpg,gpgconfandzenityare resolved to absolute paths, must be regular files, and must not be group/world writable. An unsafezenitymakes local approval fail closed instead of trusting a substituted dialog.Bounded resources. Input/output size caps, per-operation timeouts, a dedicated process group killed on timeout, and a 0700 temporary directory with 0600 files.
Verification result semantics
verify (and the embedded-signature result returned by decrypt) separates four ideas:
Field | Meaning |
| gpg emitted |
| additionally a |
| alias for |
| signer has full or ultimate trust; reported separately, not required for acceptance |
Decryption releases plaintext only when the embedded signature is policy_acceptable. A
mathematically valid but expired or revoked signature is never enough.
What it does not guarantee
Same-user isolation. Any process running as your OS user can read
~/.gnupg, connect to thegpg-agentsocket, or edit~/.config/gpg-mcp/config.toml. If the agent has shell access, the MCP policy is not an OS boundary. Use stronger isolation.A malicious MCP client. In
approval = "client"mode, the client renders the approval dialog. A malicious client can fake it. Useapproval = "local"(zenity/TTY, independent of the client) or a separate user/hardware token.Compromised gpg or gpg-agent. A compromised system binary can do anything. The binary checks reduce PATH hijacking, not supply-chain compromise.
Semantic safety. The server can show what is being signed (hash and preview) but cannot judge whether the content is safe to sign. That judgement is the human's.
Threat table
Threat | Control |
Agent asks for the private key | No tool returns it; response guard blocks accidental output |
Agent tries | Option is never constructed; no raw exec |
Agent supplies an option as a UID/fingerprint | Full-fingerprint validation; UIDs go into a parameter file, not argv |
Agent signs a malicious tag/release silently | Allow-list + approval showing key, size, SHA-256 and preview |
Agent decrypts stolen ciphertext | Allow-list + approval + recipient preflight |
Agent encrypts secrets to an attacker key | Named recipients in approval + optional |
Agent imports a secret key | Secret packets detected and rejected |
Agent deletes a key |
|
Agent revokes a key |
|
Agent submits a revocation certificate for another key | Certificate validated in an ephemeral keyring and bound to the requested fingerprint |
Agent asks for a revocation dry-run to mutate state |
|
Agent exploits a custom policy that gates only one composite constituent | Composite operations inherit every constituent's approval requirement |
Agent reads a payload from the audit ledger | Approval previews stay in the UI; audit stores only hashes/metadata |
Agent hides one recipient among visible ones | Any hidden recipient is rejected unless explicitly enabled |
Agent substitutes a fake local approval dialog |
|
Agent reads plaintext from a bad signature | Bad/expired/revoked embedded signatures are not policy-acceptable and abort |
Compression bomb / huge output | Capped streaming reads; process group killed |
PATH hijack | Absolute binary paths, ownership/mode checks |
Passphrase leak through argv/env/stdin | Pinentry only; no passphrase code path |
Unattended prompt | Timeout kills the process group; no approval bypass |
Architecture
MCP client (agent)
│ stdio JSON-RPC
▼
gpg-mcp (Python, mcp SDK v2)
│ fixed argv, stdin/stdout, --status-file
▼
gpg 2.4 ──► gpg-agent ──► pinentry (human passphrase)
│
▼
GNUPGHOME (encrypted private keys + public keyring)The server never opens private key files. It asks gpg to perform an operation; gpg-agent
unlocks the key only after pinentry succeeds.
Quick start
Requirements: Python 3.11+, GnuPG 2.2+ (2.4 recommended), a pinentry program.
git clone https://github.com/emiliano-go/gpg-mcp.git
cd gpg-mcp
uv sync
uv run gpg-mcp initinit creates an isolated keyring at ~/.local/share/gpg-mcp/gnupg (mode 0700) and a config
at ~/.config/gpg-mcp/config.toml (mode 0600). It writes a conservative gpg-agent.conf:
default-cache-ttl 60
max-cache-ttl 300
ignore-cache-for-signing
no-allow-external-cache
no-allow-mark-trusted
pinentry-timeout 120ignore-cache-for-signing means every signature asks pinentry again. Remove it only if you
understand the trade-off.
Check the installation:
uv run gpg-mcp doctorAdd it to your MCP client (example for a JSON mcpServers map):
{
"mcpServers": {
"gpg": {
"command": "gpg-mcp",
"args": ["serve", "--config", "/home/you/.config/gpg-mcp/config.toml"]
}
}
}Then, from the agent, create a key with create_key (the passphrase is entered into pinentry) and
use the returned fingerprint with sign, encrypt or decrypt.
The dedicated keyring starts empty.
gpg-mcpwill not copy or export your existing secret keys. To reuse an existing key, move it yourself out-of-band, or keep using~/.gnupgwith an explicitallowed_sign_keys/allowed_decrypt_keysallow-list.
Configuration
Config path: ~/.config/gpg-mcp/config.toml, or --config, or GPG_MCP_CONFIG.
Key | Default | Meaning |
|
| Isolated keyring. Must be |
|
|
|
| all high-risk ops | Operations that must be approved |
|
| Full fingerprints allowed to sign. Empty = any in a dedicated keyring, none in |
|
| Same rule for decryption |
|
| Optional recipient allow-list for encryption |
| unset | Advisory default; explicit |
|
| Keyserver search/fetch/send |
|
| Symmetric encrypt/decrypt |
|
| Permit |
|
| Permit decryption of hidden-recipient messages |
|
| Never enable in production |
|
| Input cap |
|
| Output cap |
|
| Per-operation timeout |
|
| Key generation / passphrase-change timeout |
|
| Config-only; |
|
| Append-only JSONL, |
Environment overrides: GPG_MCP_CONFIG, GPG_MCP_HOME, GPG_MCP_APPROVAL,
GPG_MCP_ENABLE_NETWORK, GPG_MCP_ENABLE_SYMMETRIC, GPG_MCP_AUDIT.
Tools
Read-only:
Tool | Purpose |
| List public keys or secret-key metadata (never secret material) |
| Full public metadata for one fingerprint |
| Armored/binary public key |
| OpenSSH public key for an authentication subkey |
| Inspect OpenPGP packets/keys without importing; rejects secret keys |
| Show recipients of a ciphertext without decrypting |
| Verify detached, cleartext or inline signatures |
| OpenPGP smartcard status |
| gpg version, sockets and agent cache options |
| Binary, keyring and policy health checks |
Crypto:
Tool | Purpose |
| Detached, cleartext or inline signature |
| Encrypt to full recipient fingerprints |
| Sign and encrypt |
| Decrypt with recipient preflight and signature check |
| Passphrase symmetric crypto (opt-in) |
Key management:
Tool | Purpose |
| Create a key; passphrase via pinentry |
| Modify a key |
| Old/new passphrase via pinentry |
| Import public material; secret packets rejected |
| Delete public or secret+public; typed confirmation |
|
|
| Validate and return the stored revocation certificate |
| Validate in an ephemeral keyring; |
| Validate and apply the stored certificate; typed confirmation |
Network (opt-in):
Tool | Purpose |
| Search the configured keyserver |
| Fetch by full fingerprint |
| Publish a public key |
Deliberately absent
export_secret_key/ secret-key backup.run_gpg/ raw argv passthrough.Any passphrase parameter.
--homedirfrom a tool.Arbitrary keyserver URLs per call.
--edit-keyinteractive menu.--trust-modelfrom user input.
Approval modes
client (default)
The server asks the MCP client to show an approval form. The form includes the operation, full key fingerprints and identities, payload size, SHA-256 and a short preview. It is implemented as an MCP resolver, so it works with both legacy and current protocol versions.
If the client did not declare elicitation support (for example, some CLI clients), the server falls
back to the local zenity dialog. That is still a human gate and is independent of the MCP client.
If neither elicitation nor a safe local dialog is available, the call fails closed with an
approval_unavailable error; it never auto-approves.
Composite operations require approval if any constituent requires it. sign_and_encrypt inherits
both sign and encrypt; encrypt(..., sign_with=...) inherits sign as well. A custom
require_approval = ["sign"] therefore still gates encryption-with-signing, and
require_approval = ["encrypt"] still gates sign_and_encrypt.
For revocation imports, the approval form shows the identity derived from the certificate itself, not merely the fingerprint passed as an argument. A certificate for a different key is rejected before approval is requested.
local
The server uses zenity (or fails closed when no display is available). This is independent of the
MCP client and is the better choice when you do not fully trust the client. Destructive operations
use a second zenity --entry prompt for the fingerprint suffix.
none
For disposable test keyrings only. The audit ledger records decision: skipped.
Keyring isolation
gpg-mcp init uses a dedicated keyring. That gives you:
a separate
gpg-agentcache from your everyday GnuPG use;a policy boundary where an empty allow-list means “this keyring only”;
a safe place for test keys and agent-specific keys.
The dedicated keyring is not a security boundary against the same OS user. For that, see below.
Stronger isolation
Separate OS user (software)
Run the signer as a different user that owns the keyring, and expose only a narrow Unix socket to the MCP server. The agent's user then cannot read the private keys or connect to the agent socket. This is the strongest software-only design and is a natural next step; the current release keeps the same-user model for simplicity.
Hardware token (recommended for high-value keys)
Put the private key on an OpenPGP smartcard or YubiKey. The key never exists on disk, and each
signature can require a PIN and a physical touch. gpg-mcp works unchanged: card_status reports
the card, and list_keys marks card-backed secret keys. Key generation on the card is done
out-of-band with gpg --edit-card.
systemd hardening
If you run the server as a service, at minimum:
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=%h/.local/share/gpg-mcp
RestrictAddressFamilies=AF_UNIXand run it as a dedicated user in the separate-user design.
Audit ledger
Every high-risk operation appends one JSON object to audit.jsonl:
{"ts":"2026-10-24T20:00:00Z","event":"operation","tool":"sign",
"fingerprint":"F759...E793","mode":"detached",
"payload_sha256":"...","size":42,"result":"ok"}The ledger contains no plaintext, no payload previews, no passphrases and no private key material.
Approval summaries are rendered for the human but never written to the ledger. The file is
append-only with mode 0600. gpg-mcp only tightens permissions on an audit directory it creates
itself; it never chmods an existing arbitrary parent such as /tmp.
It is not tamper-proof against the same OS user.
Testing
uv run pytest # never prompts; uses an ephemeral keyring
uv run ruff check .
# Optional: the two operations that intentionally need a human at pinentry
GPG_MCP_INTERACTIVE=1 uv run pytest tests/test_interactive.py -sThe default suite uses pinentry-program /bin/false in its throwaway keyring, so any unexpected
prompt fails the test instead of blocking. It creates an ephemeral keyring and runs real gpg
operations. It asserts, among other things, that:
no response contains
PRIVATE KEY BLOCK;no command line contains a forbidden option or a passphrase;
secret-key imports and secret-key revocation imports are rejected;
the default policy requires approval and typed confirmation for
import_revocation,revoke_keyanddelete_key;composite operations inherit constituent approval requirements;
revocation dry-runs do not modify the real keyring, and certificates for another key are rejected;
expired signatures, expired keys, revoked keys and missing public keys are never policy-acceptable;
decryption refuses ciphertext addressed to another key and mixed hidden/visible recipients;
the audit ledger contains no payload preview and does not change an existing directory's mode;
unsafe
zenity/binary permissions are rejected;option-injection attempts fail validation.
CI runs ruff and pytest on Python 3.11, 3.12 and 3.13 with GnuPG installed (.github/workflows/ci.yml).
Limitations
Text and base64 inputs are capped; large files should be handled out-of-band.
Keyserver search output is human-readable text, not a structured list.
Revocation uses the certificate generated at key creation time. If it is missing, generate one out-of-band with
gpg --gen-revoke. The target key must already be present in the configured keyring; revocation certificates for unknown keys are rejected.import_revocationhandles full primary-key revocation, not selective subkey or UID revocation.Smartcard key generation and PIN management are intentionally out of scope.
approval = "client"depends on the client rendering elicitation forms honestly.
License
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Security gateway for AI agents: policy, approval, and audited execution, no secrets shared.
Hosted CRM and email desk for AI agents: key per agent, audit log, one-click undo, opt-in approval.
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Etch is a signed audit chain for AI agent decisions, offline-verifiable against pinned public keys.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to securely access encrypted secrets (SSH keys, API tokens, passwords) with real-time user approval via Passkey, and supports SSH remote execution through the MCP protocol.MIT
- AlicenseNot gradedqualityDmaintenanceProvides a human-in-the-loop security layer for AI agents by intercepting file operations, explaining them with a local LLM, and enforcing a deterministic policy that requires user approval for risky actions.MIT
- AlicenseNot gradedqualityAmaintenanceA local proxy in front of any MCP server for developers on Claude Code or Cursor: see every tool call your agent makes, hold the risky ones for your approval, and keep a secret-redacted journal that is tamper-evident with an external anchor.76 npm1Apache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to list entries and metadata, run commands with secrets injected as environment variables while scrubbing every value from the output, copy secrets straight to the clipboard, and generate or store new secrets without their plaintext ever entering the conversation. Obvious dumpers are refused and destructive edits require explicit confirmation, closing off accidental exposure paths.71 PyPIMIT