gpg-mcp について
gpg-mcp は、OpenPGP キーの署名、検証、暗号化、復号、管理を行う GnuPG MCP サーバーです。固定された型付き操作と承認・キー許可リストにより、秘密鍵を MCP 境界に出さず gpg-agent 内に保ちます。
主な機能
- 01データの署名と検証
- 02メッセージの暗号化と復号
- 03OpenPGP キーの管理
- 04高リスク操作の承認
- 05高リスク操作の監査
活用シーン
- 01秘密鍵を Agent に渡さず内容に署名
- 02許可リストのキーだけでメッセージを復号
- 03受け入れポリシーに沿った署名検証
MCP 対応
固定された型付き GnuPG 操作ツールを MCP クライアントに公開します。秘密鍵の情報とパスフレーズは MCP 境界を越えません。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
gpg-mcp
GnuPG 用の、セキュリティを重視したModel Context Protocol サーバーです。
gpg-mcp を使うと、AI エージェントは OpenPGP 鍵の署名、検証、暗号化、復号、管理を行えます。秘密鍵の情報を受け取ることは一切ありません。秘密鍵は gpg-agent に保持され、パスフレーズは pinentry にのみ渡されます。リスクの高い操作には人間による明示的な承認が必要で、破壊的な操作にはフィンガープリントの末尾を入力する必要があります。
脅威モデルを一文で表すと: MCP サーバーはポリシー境界であり、OS サンドボックスではありません。エージェントが秘密鍵を受け取ることや、秘密鍵を気づかれずに使用することを防ぎます。同じ OS ユーザーとしてエージェントがすでにシェルアクセスを持っている場合に備えて保護するには、署名処理を別のユーザーとして実行するか、ハードウェアトークンを使用してください(より強力な隔離を参照)。
開発の背景
多くの「GPG MCP」ラッパーは、薄い subprocess シェルです。command: str パラメーターを持つツールや、--export-secret-keys を要求できるエージェントがあれば、目的が損なわれます。それに対して gpg-mcp は、意図的に機能を制限した、固定の型付きインターフェイスを提供します。
- 任意のコマンド実行はできません。
run_gpg(args)ツールはありません。 - 秘密鍵のエクスポートやバックアップはできません。
--export-secret-keys、--export-secret-subkeys、--export-secret-ssh-keyにはアクセスできません。 - パスフレーズのパラメーターはありません。
--passphrase、--passphrase-fd、--passphrase-file、ループバック pinentry、環境変数や stdin でのパスフレーズ指定はできません。 --homedirによる上書きはできません。 キーリングは設定で固定されます。- 短い鍵 ID は使用できません。 操作には 40 桁または 64 桁の完全なフィンガープリントが必要です。
- 鍵を自動取得しません。 検証時にネットワークから鍵をひそかに取得することはありません。
- 非表示の受信者は認めません。 復号前に暗号文を検査し、許可された鍵宛てではないメッセージを拒否します。
- 応答ガードを備えています。 gpg が秘密鍵の情報と思われるものを出力すると、呼び出しは失敗し、出力は破棄されます。
セキュリティモデル
サーバーが保証すること
- 秘密鍵が MCP の境界を越えることはありません。 秘密鍵を一覧表示しても、返されるのはメタデータのみです。
署名と復号は
gpg-agentによって実行されます。 - サーバーがパスフレーズを目にすることはありません。
--pinentry-mode askが強制され、pinentry は人間と直接やり取りします。サーバーは argv、env、stdin からパスフレーズを読み取れません。 - 署名と復号には、許可リストに登録された鍵を使用します。
~/.gnupgを共有している場合、許可リストが空であれば、どちらも拒否されます。専用のキーリングでは、キーリング自体が許可リストの境界となります。 - 復号では受信者を確認します。 復号する前に
gpg --list-packetsを使用して、暗号文の宛先が許可された鍵であることを確認します。隠し受信者は、同じメッセージに表示された許可済みの受信者が含まれていても、デフォルトでは拒否されます。 - 埋め込み署名が無効な場合、復号を中止します。
verify=true(デフォルト)で、復号されたメッセージに不正な署名、期限切れの署名、または失効した署名が含まれている場合、平文は返されません。validはポリシー上の判定であり、暗号学的な結果は別途報告されます。複数の署名はNEWSIGシーケンスごとに集約されます。--proc-all-sigsに対応していない GnuPG では、パケットごとに集約されます。イベントの順序にかかわらず、いずれかの署名がポリシー上許容されない場合は、フェイルクローズで拒否されます。 - 認可にはフェイルクローズのレジストリを使用します。 高リスクの各ツールでは、登録済みの操作を指定します。不明な操作には承認が必要です。複合操作(
sign_and_encrypt、およびsign_withを指定したencrypt)には、構成要素となる各操作の承認要件が適用されるため、署名だけ、または暗号化だけを制限するポリシーを回避できません。 - 失効証明書は、インポート前に検証されます。 証明書には失効署名が含まれていなければなりません。また、秘密鍵パケットを拒否し、指定されたフィンガープリントと完全に一致する対象を示し、エフェメラルキーリングでその鍵が実際に失効することを確認できなければなりません。
apply=falseは真のドライランであり、実際のキーリングには一切触れません。 - 破壊的な操作には、入力による確認が必要です。 削除、失効、または失効証明書の適用には、オペレーターがフィンガープリントの末尾8文字を入力する必要があります。これは
destructive_confirmationで制御され、require_approvalとは独立しています。 - 高リスクの操作はすべて監査されます。 台帳には操作名、フィンガープリント、ペイロードの SHA-256、サイズ、判定が記録されます。平文、ペイロードのプレビュー、パスフレーズは記録されません。
- シェルは使用しません。 コマンドには
execve形式の argv 配列を使用します。シェルによる展開はなく、ユーザー文字列からオプションが注入されることもありません。 - 環境変数は最小限に制限します。
LD_*、GPG_AGENT_INFO、GPG_TTY、SSH_AUTH_SOCKなどの変数は gpg に渡されません。pinentry に必要な表示・セッション変数のみ保持されます。 - バイナリとパスを安全性の確認済みのものに限定します。
gpg、gpgconf、zenityは絶対パスで解決され、通常ファイルであり、グループまたはその他のユーザーによる書き込みが禁止されていなければなりません。安全でないzenityが見つかった場合、差し替えられたダイアログを信用せず、ローカル承認をフェイルクローズで拒否します。 - リソースを制限します。 入出力サイズの上限、操作ごとのタイムアウト、タイムアウト時に強制終了される専用プロセスグループ、および 0700 の一時ディレクトリと 0600 のファイルを使用します。有効なステータス出力上限を超えた場合、部分的に解析するのではなく、フェイルクローズで拒否します。
- 汎用インポーターでは失効させられません。
import_public_keyは、秘密鍵パケットと、鍵の素材に埋め込まれた失効署名を含むすべての失効署名を拒否します。失効にはimport_revocationを使用する必要があります。この操作では、対象が検証され、入力による確認が求められます。 - 実際に復号に使われた鍵を確認します。 復号後、
DECRYPTION_KEYステータスで許可済みの主鍵またはサブ鍵が指定されていることを確認します。指定されていない場合、平文は破棄されます。表示された受信者が許可済みでも、別の秘密鍵を使って暗号文が密かに復号されることはありません。 - GnuPG の設定は一貫しています。 実行処理では
--no-optionsと--pinentry-mode askを使用するため、悪意のあるgpg.confによって loopback pinentry が有効化されたり、その他のセキュリティ関連のデフォルト設定が変更されたりすることはありません。 - ファイルシステムのパスは、シンボリックリンクをたどらずに検査されます。 設定ファイルのパスは、ディレクトリのファイルディスクリプターと
O_NOFOLLOWを使ってたどり、開いたファイルはfstatで検証するため、検証と使用の間に競合が起きたり、パスの差し替えによって別の場所へ誘導されたりすることはありません。キーリングのパスは未解決のまま保持されます(GNUPGHOMEはシンボリックリンクの解決を行いません)。serve、doctor、initはキーリングの全祖先チェーンを検証し、シンボリックリンクになっているキーリングや祖先を拒否します。祖先はユーザーまたは root が所有し、sticky bit が設定されていない限り、グループまたはその他のユーザーによる書き込みが禁止されていなければなりません。不明なキーは拒否されます。既存の監査ファイルはfstatで検査し、通常ファイルでないファイルや所有者が異なるファイルは拒否されます。また、グループおよびその他のユーザーのアクセス権を0600に制限します。audit_required = trueの場合、監査に失敗すると操作も失敗します。initはO_EXCL|O_NOFOLLOWを使って設定ファイルを安全に作成し、シンボリックリンクになっているgpg-agent.confは、リンク先への書き込みや chmod を行わずに拒否します。存在しない設定、キーリング、監査の各ディレクトリは、検証済みのディレクトリディスクリプターを基準にコンポーネントごとに作成されるため、シンボリックリンクになっている祖先によって、拒否される前にパスが変更されたり、何かが作成されたりすることはありません。監査パスにも同じディレクトリファイルディスクリプターによる検査を適用するため、安全でない祖先によって台帳の保存先が変更されることはありません。 - 復号の経路を明確に区別します。
symmetric_decryptは、公開鍵の受信者も含むハイブリッド暗号文を拒否します。decryptは共通鍵暗号のみの暗号文を拒否し、GnuPG が実際に許可リストに登録された公開鍵を報告しない限り、平文を返しません。共通鍵暗号の経路にフォールバックすることはありません。したがって、symmetric_decryptのみを制限するカスタムポリシーをdecrypt経由で回避することはできません。ハイブリッド入力では、GnuPG による回復可能な共通鍵パスフレーズの失敗のみを許容します。ただし、公開鍵についてもDECRYPTION_KEYとDECRYPTION_OKAYが報告された場合に限ります。汎用のERROR/FAILUREイベントと、理由が説明されていないゼロ以外の終了コードは拒否されます。 - 複数署名の処理はフェイルクローズです。 デタッチ署名では、
--proc-all-sigsに対応していない GnuPG バージョンの場合、パケットごとのフォールバックを使用します。クリアテキスト署名、インライン署名、埋め込み署名では、対応している場合に--proc-all-sigsを使用します。古い GnuPG では、最初の署名が不正なメッセージの場合、最初の不正な署名の時点で列挙が停止することがありますが、そのメッセージは引き続き拒否されます。すべての署名が正常なメッセージでは、すべての署名が列挙されます。 - 共通鍵暗号による復号も同様に厳格です。
DECRYPTION_OKAYを必須とし、DECRYPTION_FAILED、BADMDC、ERROR、FAILUREを拒否します。また、理由が説明されていないゼロ以外の終了コードは致命的なエラーとして扱います。verify=falseの場合、ゼロ以外の終了コードを許容するのは、埋め込み署名の結果として明示的に認識された場合に限ります。これに関係のない GnuPG の失敗は、決して許容されません。
検証結果の意味
verify(および decrypt が返す埋め込み署名の結果)では、次の4つの概念を区別します。
| フィールド | 意味 |
|---|---|
cryptographically_valid | gpg が VALIDSIG を出力しました。署名の計算は正しいです |
policy_acceptable | さらに GOODSIG があり、署名の期限切れ、鍵の期限切れ、鍵の失効、公開鍵の欠落がありません |
valid | policy_acceptable の別名で、互換性のために維持されています |
identity_trusted | 署名者が完全または究極の信頼を得ています。別途報告され、受け入れの要件ではありません |
復号では、埋め込まれたすべての署名が policy_acceptable の場合にのみ、平文を出力します。数学的に有効でも期限切れまたは失効した署名だけでは不十分であり、不正な署名が後続の有効な署名によって隠されることもありません。結果には、NEWSIG シーケンスごとに1つのエントリーを含む signatures リストが含まれます。
保証されないこと
- 同一ユーザー内の分離。 OS のユーザーとして実行されているプロセスは、いずれも
~/.gnupgの読み取り、gpg-agentソケットへの接続、または~/.config/gpg-mcp/config.tomlの編集が可能です。エージェントがシェルにアクセスできる場合、MCP ポリシーは OS レベルの境界にはなりません。より強力な分離を使用してください。 - 悪意のある MCP クライアント。
approval = "client"モードでは、クライアントが承認ダイアログを表示します。悪意のあるクライアントはこれを偽装できます。approval = "local"(クライアントから独立した zenity)または別のユーザー/ハードウェアトークンを使用してください。 - 侵害された gpg または gpg-agent。 侵害されたシステムバイナリは何でも実行できます。バイナリのチェックは PATH の乗っ取りを軽減しますが、サプライチェーンの侵害を防ぐものではありません。
- 意味上の安全性。 サーバーは署名対象(ハッシュとプレビュー)を表示できますが、その内容が安全に署名できるかどうかは判断できません。その判断は人間に委ねられています。
脅威の一覧
| 脅威 | 対策 |
|---|---|
| エージェントが秘密鍵を要求する | 秘密鍵を返すツールはありません。レスポンスガードが誤った出力をブロックします |
エージェントが --export-secret-keys を試す | オプションは構築されず、生の exec もありません |
| エージェントが UID/フィンガープリントとしてオプションを指定する | フィンガープリント全体を検証します。UID では先頭の - と制御文字を拒否し、鍵の作成にはパラメーターファイルを使用します |
| エージェントが悪意のあるタグ/リリースに気づかれず署名する | 許可リストと、鍵、サイズ、SHA-256、プレビューを表示する承認 |
| エージェントが盗まれた暗号文を復号する | 許可リスト、承認、受信者の事前確認 |
| エージェントが攻撃者の鍵で秘密情報を暗号化する | 承認画面に名前付き受信者を表示し、必要に応じて allowed_recipients を使用します |
| エージェントが秘密鍵をインポートする | 秘密鍵パケットを検出して拒否します |
| エージェントが鍵を削除する | destructiveHint と、フィンガープリント末尾の入力 |
| エージェントが鍵を失効させる | destructiveHint と、フィンガープリント末尾の入力 |
| エージェントが別の鍵の失効証明書を送信する | 一時的なキーリングで証明書を検証し、要求されたフィンガープリントに紐付けます |
| エージェントが失効のドライランで状態を変更しようとする | apply=false は一時的なキーリングでのみ検証します |
| エージェントが、一方の複合構成要素だけを承認対象とするカスタムポリシーを悪用する | 複合操作は、すべての構成要素の承認要件を引き継ぎます |
| エージェントが監査台帳からペイロードを読み取る | 承認プレビューは UI 内にとどめ、監査にはハッシュ/メタデータのみを保存します |
| エージェントが表示されている受信者に隠れた受信者を混ぜる | 明示的に有効化されていない限り、隠れた受信者がいる場合は拒否します |
| エージェントが偽のローカル承認ダイアログを表示する | zenity にも gpg と同じ信頼できるバイナリのチェックを適用します |
| エージェントが不正な署名の平文を読み取る | 不正/期限切れ/失効済みの埋め込み署名はポリシー上許容されず、中止されます |
| エージェントが後続の有効な署名で不正な署名を隠す | NEWSIG ごとの集約では、許容できない署名が1つでもあると必ず失敗します |
| エージェントが承認なしでキーリングを変更するために鍵を取得する | fetch_key は承認が必要な書き込み操作として登録され、インポートしたフィンガープリントを報告します |
| エージェントが UID を介して承認ダイアログに改行や双方向テキストを挿入する | 承認前に1行形式へのエスケープと検証を行います |
| 既存の監査ファイルが全ユーザーから読み取り可能になっている | fstat と fchmod で 0600 に設定し、通常ファイルでないファイルや別の所有者のファイルは拒否します |
require_approval から破壊的操作が削除される | destructive_confirmation はデフォルトで引き続き、指定された末尾の入力を要求します |
| 汎用インポーター経由で失効証明書が送信される | パケットレベルで失効署名を拒否します。import_revocation を使用してください |
| 復号時に許可リストにない秘密鍵が気づかれず使用される | DECRYPTION_KEY の状態を確認し、一致しない場合は平文を破棄します |
| 巨大な/不完全な状態ファイルが成功として扱われる | 実効状態上限を適用し、部分的な状態は必ず失敗させます |
| ハイブリッドメッセージが公開鍵の許可リストを迂回して対称復号に回される | symmetric_decrypt は公開鍵の受信者を拒否し、decrypt は報告された許可リスト内の公開鍵を要求します |
対称鍵のみの入力で、制限の緩い decrypt の承認が使われる | decrypt は実行前に対称鍵のみの暗号文を拒否します |
| 古い GnuPG が最初の不正な署名で処理を停止する | 分離署名パケットを個別に検証し、必ず失敗する形で集約します |
| 検証とオープンの間に設定が差し替えられる | ディレクトリー fd によるパス走査、O_NOFOLLOW、およびオープンしたディスクリプターに対する fstat |
| 別のユーザーによってパスの祖先が差し替えられる | 祖先を dir fd で走査し、スティッキービットのない書き込み可能な祖先は拒否します |
init が仕掛けられた gpg-agent.conf のシンボリックリンクをたどる | lstat/O_NOFOLLOW と通常ファイル/所有者のチェック |
シンボリックリンクされたキーリングの祖先が GNUPGHOME の参照先を変える | 未解決のパスとそのすべての祖先チェーンを、serve/doctor/init の実行時に検証します |
| マルチバイトテキストが入力上限を大幅に超えるメモリーを割り当てる | 受け入れ前に、上限付きのチャンクで UTF-8 にエンコードします |
| 不可逆操作後に結果の監査記録が失敗する | 意図と結果の段階を記録し、結果の記録に失敗した場合は操作が完了している可能性を示します |
悪意のある gpg.conf が実行動作を変更する | --no-options と明示的な --pinentry-mode ask |
| グループ/全ユーザーが書き込み可能な設定がポリシーを変更する | 設定の整合性チェックで、安全でないファイルと不明なキーを拒否します |
| 監査書き込みの失敗が気づかれずに発生する | audit_required = true により操作が失敗します |
| 圧縮爆弾/巨大な出力 | 上限付きのストリーミング読み込みを行い、プロセスグループを強制終了します |
| PATH の乗っ取り | バイナリの絶対パスと、所有者/モードのチェック |
| argv/環境変数/stdin を介したパスフレーズ漏えい | Pinentry のみを使用し、パスフレーズを扱うコードパスはありません |
| 応答されないプロンプト | タイムアウト時にプロセスグループを強制終了し、承認の迂回はできません |
アーキテクチャ
MCP client (agent)
│ stdio JSON-RPC
▼
gpg-mcp (Python, mcp SDK v2)
│ fixed argv, stdin/stdout, --status-file
▼
gpg 2.2+ (2.4 shown) ──► gpg-agent ──► pinentry (human passphrase)
│
▼
GNUPGHOME (encrypted private keys + public keyring)
サーバーが秘密鍵ファイルを開くことはありません。操作は gpg に依頼し、pinentry による認証が成功した後にのみ、gpg-agent が鍵のロックを解除します。
インストール
PyPI:gpg-mcp。MCP Registry:io.github.emiliano-go/gpg-mcp。
クライアント設定でバージョンを固定すると、再起動時にサーバーのビルドが変わりません。
# run without a global install
uvx [email protected] serve
# or install a pinned CLI
uv tool install gpg-mcp==0.1.0
# or
pipx install gpg-mcp==0.1.0
# or
pip install gpg-mcp==0.1.0
初回実行時は、CLI をインストールしていない場合、これらのコマンドの先頭に uvx [email protected] を付けてください。
gpg-mcp init
gpg-mcp doctor
グローバルインストールを使用しない場合の MCP クライアント設定:
{
"mcpServers": {
"gpg": {
"command": "uvx",
"args": ["[email protected]", "serve"]
}
}
}
uv tool または pipx でインストールした場合:
{
"mcpServers": {
"gpg": {
"command": "gpg-mcp",
"args": ["serve"]
}
}
}
要件:Python 3.11 以降、GnuPG 2.2 以降(2.4 推奨)、pinentry、および approval = "local" を使用する場合は zenity。Linux およびその他の Unix 系システムに対応しています。Windows には対応していません。
レジストリーのエントリーは、次の URL で検索できます。
https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.emiliano-go/gpg-mcp
クイックスタート(ソースから)
要件:Python 3.11 以降、GnuPG 2.2 以降(2.4 推奨)、pinentry プログラム。
git clone https://github.com/emiliano-go/gpg-mcp.git
cd gpg-mcp
uv sync
uv run gpg-mcp init
init は ~/.local/share/gpg-mcp/gnupg に分離されたキーリング(モード 0700)を、~/.config/gpg-mcp/config.toml に設定ファイル(モード 0600)を作成します。安全性を重視した 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 120
ignore-cache-for-signing を設定すると、署名のたびに pinentry が再度表示されます。この設定は、トレードオフを理解している場合にのみ削除してください。
インストールを確認します。
uv run gpg-mcp doctor
MCP クライアントに追加します(JSON の mcpServers マップの例)。
{
"mcpServers": {
"gpg": {
"command": "gpg-mcp",
"args": ["serve", "--config", "/home/you/.config/gpg-mcp/config.toml"]
}
}
}
次に、エージェントから create_key を使って鍵を作成し(パスフレーズは pinentry に入力します)、返されたフィンガープリントを sign、encrypt、または decrypt で使用します。
**クライアントのタイムアウト:**承認と pinentry に、クライアントのデフォルトの MCP ツールタイムアウトを超える時間がかかることがあります。少なくとも 120 秒に設定してください。鍵の生成やパスフレーズの変更には、300 秒が余裕のあるデフォルト値です。opencode の場合:
"experimental": { "mcp_timeout": 300000 }ダイアログが開いている間にクライアントがタイムアウトした場合は、ダイアログをキャンセルしてください。サーバーが自動で承認することはなく、操作を拒否します。
専用のキーリングは空の状態から始まります。
gpg-mcpが既存の秘密鍵をコピーまたはエクスポートすることはありません。既存の鍵を再利用するには、別の方法で自分で移動するか、明示的なallowed_sign_keys/allowed_decrypt_keysの許可リストを設定して~/.gnupgを引き続き使用してください。
設定
設定ファイルのパス:~/.config/gpg-mcp/config.toml、または --config、または GPG_MCP_CONFIG。
| キー | デフォルト | 意味 |
|---|---|---|
gnupg_home | ~/.local/share/gpg-mcp/gnupg | 分離されたキーリングです。0700 にする必要があります。 |
approval | client | client(MCP エリシテーション)、local(zenity。ディスプレイが必要)、none(テスト専用) |
require_approval | すべての高リスク操作 | 承認が必要な操作 |
destructive_confirmation | true | 破壊的操作の際に入力するフィンガープリントの末尾です。require_approval とは独立しています |
allow_test_mode | false | サービス実行時に approval = "none" を使用するために必要です |
audit_required | true | 監査台帳に書き込めない場合、高リスク操作を失敗させます |
allowed_sign_keys | [] | 署名を許可する完全なフィンガープリントです。空の場合、専用キーリングではすべて、~/.gnupg ではなしを意味します |
allowed_decrypt_keys | [] | 復号についても同じルールです |
allowed_recipients | [] | 暗号化で使用できる宛先のオプションの許可リストです |
enable_network | false | キーサーバーの検索/取得/送信 |
enable_symmetric | false | 共通鍵暗号方式による暗号化/復号 |
allow_unprotected_keys | false | protect=false での鍵の作成を許可します(テスト用キーリングのみ) |
allow_hidden_recipients | false | 宛先非表示メッセージの復号を許可します |
max_input_bytes | 10485760 | 入力の上限 |
max_output_bytes | 20971520 | 出力の上限。ステータスファイルの上限は max(1 MiB, this value) です |
timeout_s | 120 | 操作ごとのタイムアウト |
keygen_timeout_s | 300 | 鍵生成/パスフレーズ変更のタイムアウト |
keyserver | hkps://keys.openpgp.org | 設定ファイルのみで指定可能です。hkps:// または https:// のみ |
audit_log | ~/.local/share/gpg-mcp/audit.jsonl | 追記専用の JSONL、0600 |
環境変数による上書き:GPG_MCP_CONFIG、GPG_MCP_HOME、GPG_MCP_APPROVAL、
GPG_MCP_ENABLE_NETWORK、GPG_MCP_ENABLE_SYMMETRIC、GPG_MCP_AUDIT、
GPG_MCP_ALLOW_TEST_MODE、GPG_MCP_AUDIT_REQUIRED。
ツール
読み取り専用:
| ツール | 目的 |
|---|---|
list_keys | 公開鍵または秘密鍵のメタデータを一覧表示します(秘密鍵の実体は決して表示しません) |
get_key | 1 つのフィンガープリントの公開メタデータをすべて表示します |
export_public_key | ASCII 装甲形式またはバイナリ形式の公開鍵 |
export_ssh_key | 認証用サブキーの OpenSSH 公開鍵 |
inspect | インポートせずに OpenPGP パケット/鍵を検査します。秘密鍵は拒否されます |
inspect_recipients | 復号せずに暗号文の宛先を表示します |
verify | 分離署名、クリアテキスト署名、またはインライン署名を検証します |
card_status | OpenPGP スマートカードのステータス |
agent_info | gpg のバージョン、ソケット、エージェントのキャッシュオプション |
doctor | バイナリ、キーリング、ポリシーの健全性チェック |
暗号処理:
| ツール | 目的 |
|---|---|
sign | 分離署名、クリアテキスト署名、またはインライン署名 |
encrypt | 宛先の完全なフィンガープリントを指定して暗号化します |
sign_and_encrypt | 署名して暗号化します |
decrypt | 許可リストに登録された鍵を使って公開鍵暗号文またはハイブリッド暗号文を復号します。対称鍵のみの入力には symmetric_decrypt を使用する必要があります |
symmetric_encrypt / symmetric_decrypt | パスフレーズによる対称暗号化(オプトイン) |
鍵管理:
| ツール | 目的 |
|---|---|
create_key | 鍵を作成します。パスフレーズは pinentry を介して指定します |
add_subkey、add_uid、set_expiry | 鍵を変更します |
change_passphrase | 古いパスフレーズと新しいパスフレーズを pinentry を介して指定します |
import_public_key | 公開鍵のデータをインポートします。秘密鍵パケットと失効署名は拒否されます |
delete_key | 公開鍵、または秘密鍵と公開鍵を削除します。確認の入力が必要です |
set_ownertrust | never/marginal/full を設定します。ultimate は CLI 専用です |
generate_revocation | 保存済みの失効証明書を検証して返します |
import_revocation | 一時的なキーリングで検証します。apply=false は実際に適用しないドライランです |
revoke_key | 保存済みの証明書を検証して適用します。確認の入力が必要です |
ネットワーク(オプトイン):
| ツール | 目的 |
|---|---|
search_keyserver | 設定されたキーサーバーを検索します |
fetch_key | 公開鍵を取得してインポートします。承認が必要で、インポートしたフィンガープリントを報告します |
send_key | 公開鍵を公開します |
意図的に含めていない機能
export_secret_key/秘密鍵のバックアップ。run_gpg/生の argv の受け渡し。- パスフレーズのパラメーター。
- ツールからの
--homedirの指定。 - 呼び出しごとの任意のキーサーバー URL。
--edit-keyの対話型メニュー。- ユーザー入力からの
--trust-modelの指定。
承認モード
client(デフォルト)
サーバーは MCP クライアントに承認フォームの表示を要求します。フォームには、操作、鍵の完全なフィンガープリントと ID、ペイロードサイズ、SHA-256、短いプレビューが含まれます。MCP の リゾルバー として実装されているため、旧バージョンと現行バージョンの両方のプロトコルで動作します。
クライアントがエリシテーションのサポートを宣言していない場合(たとえば、一部の CLI クライアント)、サーバーはローカルの zenity ダイアログにフォールバックします。これも人間による承認ゲートであり、MCP クライアントとは独立しています。エリシテーションも安全なローカルダイアログも利用できない場合、呼び出しは approval_unavailable エラーで安全側に失敗し、自動承認されることはありません。
複合操作では、構成要素のいずれかが承認を必要とする場合に承認が必要です。sign_and_encrypt は sign と encrypt の両方を引き継ぎます。また、encrypt(..., sign_with=...) も sign を引き継ぎます。そのため、require_approval = ["sign"] をカスタム設定すると、署名付き暗号化にも引き続き承認が必要になり、require_approval = ["encrypt"] を設定すると、sign_and_encrypt にも引き続き承認が必要になります。
破壊的操作では、destructive_confirmation = true(デフォルト)の場合、操作が require_approval から削除されていても、フィンガープリント末尾の文字列を入力する必要があります。これを false に設定すると、入力による確認だけが不要になります。操作が require_approval に残っている場合、承認は引き続き必要です。approval = "none" は、使い捨てのテスト用キーリングに限り、どちらも回避します。
失効のインポートでは、承認フォームに、引数として渡されたフィンガープリントだけでなく、証明書自体から導出された ID が表示されます。異なる鍵の証明書は、承認が要求される前に拒否されます。
local
サーバーはzenityを使用します(ディスプレイが利用できない場合はフェイルクローズします)。これはMCPクライアントから独立しており、クライアントを完全には信頼できない場合に適しています。破壊的な操作では、フィンガープリントの末尾を入力するために、2つ目のzenity --entryプロンプトを使用します。
none
使い捨てのテスト用キーリングでのみ使用してください。allow_test_mode = true(またはGPG_MCP_ALLOW_TEST_MODE=1)を設定し、かつ専用のGNUPGHOMEを構成しない限り、serveはこのモードを拒否します。監査台帳にはdecision: skippedが記録されます。実際の鍵では決して有効にしないでください。
キーリングの分離
gpg-mcp initは専用のキーリングを使用します。これにより、次のことが可能になります。
- 普段のGnuPGの使用時とは別の
gpg-agentキャッシュを使用できます。 - 空の許可リストが「このキーリングのみ」を意味するポリシー境界を設けられます。
- テスト用の鍵やエージェント専用の鍵を安全に保管できます。
専用のキーリングは、同じOSユーザーに対するセキュリティ境界ではありません。そのための方法については、以下を参照してください。
より強力な分離
OSユーザーの分離(ソフトウェア)
キーリングを所有する別のユーザーとして署名処理を実行し、MCPサーバーには限定されたUnixソケットのみを公開します。エージェントのユーザーは秘密鍵を読み取ることも、エージェントソケットに接続することもできません。これはソフトウェアのみで実現できる最も強力な設計であり、自然な次のステップです。現行リリースでは、簡潔さを優先して同一ユーザーモデルを採用しています。
ハードウェアトークン(価値の高い鍵に推奨)
秘密鍵をOpenPGPスマートカードまたはYubiKeyに保存します。鍵がディスク上に存在することはなく、署名ごとにPINと物理的なタッチを要求できます。gpg-mcpは変更なしで動作します。card_statusはカードとその鍵を報告し、list_keysは秘密鍵のメタデータを返します。カード上での鍵の生成は、gpg --edit-cardを使って別途行います。
systemdの強化
サーバーをサービスとして実行する場合は、最低限、次の設定を行ってください。
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=%h/.local/share/gpg-mcp
RestrictAddressFamilies=AF_UNIX
また、ユーザーを分離する設計では、専用ユーザーとして実行してください。
監査台帳
すべての高リスク操作で、audit.jsonlに1つのJSONオブジェクトが追記されます。
{"ts":"2026-10-24T20:00:00Z","event":"operation","phase":"outcome","tool":"sign",
"fingerprint":"F759...E793","mode":"detached",
"payload_sha256":"...","size":42,"result":"ok"}
台帳には、平文、ペイロードのプレビュー、パスフレーズ、秘密鍵の内容は一切含まれません。承認の要約は人間向けに表示されますが、台帳には書き込まれません。このファイルは追記専用で、モードは0600です。書き込みのたびに、既存の監査ファイルをfstatで検査します。通常ファイル以外のファイル、および別のユーザーが所有するファイルは拒否されます。また、グループまたはその他のユーザーにアクセス権がある既存ファイルは、fchmodで0600に制限されます。gpg-mcpが権限を制限するのは、自身が作成した監査ディレクトリのみです。/tmpのような既存の任意の親ディレクトリに対してchmodを実行することはありません。
audit_required = true(デフォルト)の場合、監査エントリを書き込めなければ高リスク操作は失敗します。audit_required = falseに設定するのは、使い捨てのテスト用キーリングで使用する場合に限ってください。記録にはphaseが付与されます。approval/intentは実行前に、outcomeは実行後に書き込まれます。outcomeの追記に失敗した場合、操作がすでに完了している可能性があることを明示するエラーが返されます。操作は再試行されません。同じOSユーザーに対して、この台帳が改ざんを防止するわけではありません。
テスト
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 -s
デフォルトのテストスイートでは、一時的なキーリングで pinentry-program /bin/false を使用するため、予期しないプロンプトが表示されるとブロックされずにテストが失敗します。一時的なキーリングを作成し、実際の gpg 操作を実行します。特に、次の点を検証します。
- 応答に
PRIVATE KEY BLOCKが含まれないこと。 - コマンドラインに禁止されたオプションやパスフレーズが含まれないこと。
- 秘密鍵のインポートと秘密鍵の失効証明書のインポートが拒否されること。
- デフォルトのポリシーで、
import_revocation、revoke_key、delete_keyに承認と入力による確認が必須であること。 - 複合操作が、構成要素の承認要件を引き継ぐこと。
- 失効処理のドライランで実際のキーリングが変更されず、別の鍵の証明書が拒否されること。
- 有効期限切れの署名、有効期限切れの鍵、失効した鍵、公開鍵が見つからない場合は、いずれもポリシー上許可されないこと。
- 埋め込み署名が複数ある場合、正常/不正の順序を問わずフェイルクローズすること。
fetch_keyが書き込み操作として登録され、承認が必須であること。- 複数行または双方向テキストを含む UID が承認ダイアログでエスケープされ、承認前に検証されること。
- 既存の
0644の監査ファイルが0600に変更される一方、シンボリックリンクと FIFO は拒否されること。 - 操作を
require_approvalから削除しても、破壊的操作の確認が引き続き必須であること。 - 失効証明書を
import_public_key経由で失効に使用できないこと。 - 実際の
DECRYPTION_KEYが許可リストに登録されている必要があること。 - 設定された上限を超えるステータス出力でフェイルクローズすること。
- 悪意のある
gpg.confが無視されること。 - 対称鍵のみの入力は
decryptで拒否され、ハイブリッド入力は許可リストに登録された公開鍵を通じて復号に成功すること。 symmetric_decryptのみを制限するカスタムポリシーをdecrypt経由で回避できないこと。- 設定ファイルの祖先ディレクトリが安全にたどられ、sticky ビットのない書き込み可能な祖先ディレクトリが拒否されること。
initがシンボリックリンクされたgpg-agent.confを拒否し、リンク先が存在しない設定ファイルのシンボリックリンクによってリンク先が作成されないこと。- 監査パスで、sticky ビットのない書き込み可能な祖先ディレクトリが拒否されること。
- 複数署名のクリアテキスト、インライン、埋め込みメッセージが正しく列挙・集約されること。
- ハイブリッド復号では、回復可能な対称鍵パスフレーズの失敗パターンのみが許容されること。
- 対称鍵復号では、説明のつかないゼロ以外の終了コードが拒否され、
verify=falseの場合に限り、認識済みの署名エラーが許容されること。 - 設定ファイル、キーリング、監査ディレクトリの作成時に、シンボリックリンクされた祖先ディレクトリ経由で書き込まれないこと。
- サーバー起動、doctor、init の時点で、シンボリックリンクされたキーリングパスと書き込み可能なキーリングの祖先ディレクトリが拒否されること。
- 無効なセキュアウォークパスによってディレクトリ記述子が漏えいしないこと。
- 複数署名が
--proc-all-sigsの有無を問わず列挙され、フェイルクローズすること。 - ハイブリッド方式の対称鍵/公開鍵暗号文が
symmetric_decryptで拒否されること。 - 監査の短い書き込み、意図/結果の各フェーズ、結果の失敗が適切に処理されること。
- リンク先が存在しない設定ファイルのシンボリックリンクと安全でない設定ファイルの親ディレクトリが拒否されること。
- 大きなメモリ割り当ての前に、マルチバイト UTF-8 がバイト数制限を超えること。
- 安全でない設定ファイル、不明な設定キー、暗黙の
approval="none"が拒否されること。 audit_required=trueの場合、安全でない監査ファイルでエラーが発生すること。- 鍵生成の組み合わせが検証され、一部のみのインポートが分類されること。
- ツールレジストリが、公開されているすべてのツールの読み取り/書き込みアノテーションと一致すること。
- 別の鍵宛ての暗号文と、非表示/表示の受信者が混在した暗号文の復号が拒否されること。
- 監査台帳にペイロードのプレビューが含まれず、既存のディレクトリのモードが変更されないこと。
- 安全でない
zenity/バイナリの権限が拒否されること。 - オプションインジェクションの試行が検証に失敗すること。
CI では、GnuPG をインストールした環境で Python 3.11、3.12、3.13 を使用し、ruff と pytest を実行します(.github/workflows/ci.yml)。
main は保護されており、3 つすべての CI ジョブに合格する必要があります。直接のプッシュは拒否されます。
制限事項
- テキストおよび Base64 の入力には上限があります。大きなファイルは別の方法で処理してください。
- キーサーバーの検索結果は、人が読めるテキストであり、構造化されたリストではありません。
- 失効には、キーの作成時に生成された証明書を使用します。見つからない場合は、別の方法で
gpg --gen-revokeを使用して生成してください。対象のキーは設定済みの キーリングにすでに存在している必要があります。不明なキーの失効証明書は拒否されます。import_revocationは 主キー全体の失効を処理します。サブキーや UID の選択的な失効には対応していません。 - スマートカードのキー生成と PIN 管理は、意図的に対象外としています。
approval = "client"は、クライアントが入力要求フォームを適切に表示することを前提としています。クライアントが入力要求を行えない場合、承認は検証済みのローカルなzenityダイアログにフォールバックします。- Linux/Unix のみ対応です。実装は Unix の権限、
os.getuid、POSIX プロセスグループ、 およびzenityに依存しており、Windows はサポートされていません。 generate_revocationは、失効に使用できる証明書を呼び出し元に返します。結果は 機密情報として扱い、オフラインで保管してください。承認プロンプトで影響について説明します。--proc-all-sigsに対応していない GnuPG バージョンでは、クリアテキスト、インライン、および埋め込み署名の列挙が、最初の不正な署名で停止する場合があります。それでも結果は拒否されます。分離署名では、パケットごとのフォールバックを使用します。
ライセンス
MIT。LICENSE を参照してください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。