YunoHost MCP について
YunoHost MCPは、Nostr認証とポリシー制御のもとで、AIクライアントにYunoHostの管理、診断、_ynhパッケージ開発機能を提供します。NIP-98署名に対応しないMCPクライアント向けの接続ツールもあります。
主な機能
- 01Nostr認証とロールベースのポリシー
- 02出力を制限した読み取り専用の障害診断
- 03_ynhパッケージ開発用ツール
- 04Polypackメモリーサービスとのオプション接続
- 05接続ツールによるリモートMCPリクエストの署名と転送
- 06期限・範囲付き委任と高リスク操作の承認
活用シーン
- 01MCPクライアント経由でYunoHostを管理
- 02ログ、サービス、ネットワーク、ホスト状態の調査
- 03NIP-98署名に対応しないクライアントからリモート接続
- 04一時的なエージェントに範囲を限定してアクセスを付与
MCP 対応
stdioまたはNIP-98認証付きHTTPで、MCPクライアントにYunoHostのツールを提供します。付属のyunohost-mcp-connectはローカルのstdioリクエストを受け取り、Nostr鍵で署名してリモートサーバーへ転送します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
yunohost-mcp
YunoHost 向けのセキュアな MCP サーバーです。Nostr 認証、ポリシーによる制御、監査可能な管理・診断、および AI クライアント(Codex、Claude、OpenCode、ChatGPT 対応の MCP クライアント)向けの _ynh パッケージ開発を提供します。
設計と段階的なロードマップの全容については PLAN.md を、YunoHost の実際の Python API との対応については PHASE0_INVESTIGATION.md を参照してください。
サーバーの実行
yunohost-mcp --transport stdio # local development, fully trusted (no NIP-98 handshake)
yunohost-mcp --transport http --host 127.0.0.1 --port 8765 # NIP-98-authenticated, remote-capable
デフォルトでは fake_yunohost は無効(実モード)です。YunoHost がインストールされていないマシンで用意されたデータを使って実行するには、YUNOHOST_MCP_FAKE_YUNOHOST=true を設定してください。identity.toml の形式(公開鍵からロールへのマッピング。HTTP リクエストで何かを実行する前に必須)については identity.example.toml を参照してください。
オプションのローカル Polypack メモリー
このサーバーは、別途インストールされた Polypack MCP サービスの認証済みファサードとして動作できます。サービスのループバック /mcp/ エンドポイントを YUNOHOST_MCP_POLYPACK_URL に設定してください。この URL は 127.0.0.1、::1、または localhost に解決される必要があり、Polypack が引き続きデータベースの唯一の所有者となります。エージェントに公開すべきエンドポイントは、引き続き YunoHost MCP の HTTP エンドポイントのみです。
初期統合では、制限付きの memory_get、memory_list_contexts、memory_recall、memory_context、memory_thread、memory_store、memory_feedback ツールを提供します。メモリーの読み取り、書き込み、フィードバックには、それぞれ個別の memory.read、memory.write、memory.feedback スコープが必要です。書き込みはロックされて監査され、監査ログには内容ではなく長さとハッシュのみが記録されます。出所情報はこのサーバーによって付与され、ユーザーメタデータで予約済みの _yunohost_* フィールドを偽装することはできません。この統合はオプションであり、Polypack がインストールされていない場合や設定されていない場合でも、YunoHost のコアツールは引き続き動作します。
クライアントツールのインストール
推奨するクライアント側の方法は uvx です。ユーザーが Python の仮想環境を管理しなくても、コマンド用の分離環境が作成されます。
uvx --from yunohost-mcp-connect yunohost-mcp-connect --help
コマンドラインに常時インストールする場合は、次を使用してください。
uv tool install yunohost-mcp-connect
パッケージは python3 -m pip install yunohost-mcp-connect でもインストールできます。この方法は主に、開発時や uv が利用できないシステムで役立ちます。
エージェントによるセットアップ
セットアップウィザードは、ユーザーのローカルマシンで AI エージェントが実行するよう設計されています。新しいクライアント ID を生成し、選択した MCP クライアントの設定を書き込み、残るセキュリティ上重要な手順として、生成された npub に適切なロールを YunoHost サーバーで付与する方法を表示します。
uvx --from yunohost-mcp-connect yunohost-mcp-connect setup \
--server https://your-yunohost-domain/mcp \
--client codex \
--format json
対応クライアントは codex、claude-desktop、claude-code、gemini、hermes、opencode です。
セットアップは安全に再実行でき、競合するサーバー定義がある場合は処理を拒否します。既存の設定を変更する前にバックアップを作成し、秘密鍵を表示することはありません。エージェントが設定を書き込まずに表示する場合は、--print-only を使用してください。npub にロールを付与してクライアントを再起動した後、接続を診断します。
uvx --from yunohost-mcp-connect yunohost-mcp-connect doctor \
--server https://your-yunohost-domain/mcp \
--key-file ~/.config/yunohost-mcp/codex.key \
--format json
クライアントの接続:yunohost-mcp-connect
一般的な MCP クライアント(Claude Desktop、標準的な Codex のインストールなど)には、NIP-98 Authorization ヘッダーに署名する手段がありません。これはこのサーバー固有の仕組みです。yunohost-mcp-connect はそのギャップを埋めます。実際のクライアントとは stdio 経由で通常の MCP をやり取りする小さなローカルプロセスであり、各リクエストをユーザー自身の Nostr キーで署名して、リモートの --transport http サーバーに転送します。
yunohost-mcp-connect --remote-url https://your-yunohost-domain/mcp --key-file ~/.config/yunohost-mcp/key
--key-file(または$YUNOHOST_MCP_CLIENT_KEY_FILE)には、16 進数またはnsec1...形式の秘密鍵を格納したファイルを指定します。同じマシン上の他のプロセスから読み取れる場所である argv/環境変数にキーを渡す--key/$YUNOHOST_MCP_CLIENT_KEYよりも、この方法を推奨します。--generate-key PATHはPATHに新しい秘密鍵を書き込み(0600。既存ファイルへの上書きは拒否)、npub を出力して、どこにも接続せずに終了します。これが最初に--key-fileを取得する方法です。クライアントごとに異なるPATHを使用してください。下記の「クライアントごとに専用のキー ファイルを用意する」を参照してください。--delegation-file(または$YUNOHOST_MCP_CLIENT_DELEGATION_FILE)を指定すると、自分の署名とともに委任イベント(PLAN.md Phase 11)を提示します。これは、オーナーがアクセス権の一部を付与した一時的なエージェント ID に使用します。- MCP クライアントの設定では、サーバーを直接指定するのではなく、このコマンドを指定してください。
tools/list、tools/call、resources/list、resources/readはそのまま転送されます。認可、ポリシー、監査は引き続きリモートサーバーで行われます。
各コネクターは、リモートの YunoHost ハンドシェイクとは独立してローカル MCP エンドポイントを起動します。設定されている YunoHost インスタンスのいずれかがオフラインでも、そのコネクターは稼働し続けます。インスタンスが復帰するまでは検出されたツールがないことを報告し、後続のリクエストで再試行します。他の設定済み MCP サーバーは引き続き起動し、使用できます。
インシデントの調査
サーバーには、断続的な障害を調査するための、範囲が制限された読み取り専用の診断機能が含まれています。
- web_logs は、HTTP ステータスとアップストリームのステータスを含む Nginx のアクセスログ/エラーログを解析します。
- journal_query は、許可リストに登録されたサービス、SSH、ファイアウォール、カーネル、OOM、systemd のジャーナルを読み取ります。
- system_snapshot、service_history、ssh_diagnose、network_snapshot は、ホスト、再起動、アクセス制御、ソケットの状態を公開します。
- http_probe は HTTP(S) エンドポイントをテストし、incident_snapshot は指定した時間範囲の主な証拠を収集します。
ログのパス、出力制限、コマンドのタイムアウト、プライベートな HTTP ターゲットを許可するかどうかは、YUNOHOST_MCP_* 設定で構成できます。これらのツールはすべて読み取り専用で、出力範囲に上限があります。
Claude Desktop または Codex への接続
どちらもサーバーに直接接続するのではなく、yunohost-mcp-connect を指定します。ブリッジが、各リクエストに Nostr キーで署名するためです。yunohost-mcp をインストールした環境内にある yunohost-mcp-connect のフルパスを指定してください(例:~/.local/pipx/venvs/yunohost-mcp/bin/yunohost-mcp-connect、または仮想環境の bin/ ディレクトリ。仮想環境を有効にした後で which yunohost-mcp-connect を実行すれば確認できます)。
クライアントごとに別々のキーファイルを使用してください。 YUNOHOST_MCP_CLIENT_KEY_FILE がそのまま ID です。リクエストに署名するキーによって、サーバー上でのロールとスコープが決まり、それ以外の要素は関係ありません。2 つの異なるクライアント(または同じクライアントの異なる設定ファイル。プロジェクトローカルの .codex/config.toml は ~/.codex/config.toml より優先されます)に同じキーファイルを指定すると、2 つ目のクライアントは気づかれないまま 1 つ目のクライアントとして認証され、権限もまったく同じになります。エラーは発生せず、気づく手がかりもありません。すでに使えているキーをコピーするのではなく、クライアントごとに新しいキーを生成してください。
yunohost-mcp-connect --generate-key ~/.config/yunohost-mcp/claude-desktop.key
yunohost-mcp-connect --generate-key ~/.config/yunohost-mcp/codex.key
各コマンドは新しいキーの npub を表示します。その特定のクライアントに適切なロールを付与してください(下記の「使い捨てエージェント ID へのアクセス権の付与」を参照するか、identity.toml を直接編集してください)。別のクライアントにすでに付与したロールを使わないでください。--generate-key は、すでに存在するファイルを上書きしません。
Claude Desktop(claude_desktop_config.json — Settings → Developer → Edit Config):
{
"mcpServers": {
"yunohost-mcp": {
"command": "/full/path/to/yunohost-mcp-connect",
"env": {
"YUNOHOST_MCP_CLIENT_REMOTE_URL": "https://your-yunohost-domain/mcp",
"YUNOHOST_MCP_CLIENT_KEY_FILE": "/home/you/.config/yunohost-mcp/claude-desktop.key"
}
}
}
}
Codex CLI(~/.codex/config.toml):
[mcp_servers.yunohost-mcp]
command = "/full/path/to/yunohost-mcp-connect"
[mcp_servers.yunohost-mcp.env]
YUNOHOST_MCP_CLIENT_REMOTE_URL = "https://your-yunohost-domain/mcp"
YUNOHOST_MCP_CLIENT_KEY_FILE = "/home/you/.config/yunohost-mcp/codex.key"
新しいキーごとに yunohost-mcp-connect --key-file <path> を一度実行すると、その npub が表示されます(yunohost-mcp-connect: signing as npub1...)。その後、identity.toml で、そのクライアントに実際に適切なロールを npub に付与してください。別のクライアントにすでに付与したロールは使わないでください。
自分のキーではなく、委任された(使い捨ての)ID を使う場合は、yunohost-mcp-delegate が生成したファイル(下記参照)を指す YUNOHOST_MCP_CLIENT_DELEGATION_FILE を追加してください。設定を編集した後、クライアントを再起動してください。どちらもこのファイルを起動時に一度だけ読み込みます。
使い捨てエージェント ID へのアクセス権の付与:yunohost-mcp-delegate
identity.toml のエントリは、特定の公開鍵に恒久的なアクセス権を付与します(編集するまで有効です)。もう 1 つのアクセス権付与方法が、委任(PLAN.md Phase 11)です。オーナーは、エージェント自身の使い捨てキーに対して、有効期間が短くスコープを限定した権限を署名して付与できます。この方法なら、そのキーを identity.toml に追加したり、秘密鍵を渡したりする必要はありません。yunohost-mcp-delegate は、オーナーが委任を作成するためのコマンドです。
yunohost-mcp-delegate --key-file ~/.config/yunohost-mcp/key \
--delegate npub1... \
--remote-url https://your-yunohost-domain/mcp \
--role readonly --ttl 24h \
--out agent-delegation.json
--delegateはエージェント自身の公開鍵です(エージェントは通常どおり、自分の NIP-98 リクエストに自分で署名する必要があります。委任は署名の代わりになるものではなく、付与する権限を追加するだけです)。--remote-urlを指定すると、サーバーの公開鍵と、このオーナー自身が現在持っているスコープを自動的に取得します。サーバーの公開鍵を手動で入力する必要はなく、--scope/--roleで広すぎる権限を要求した場合も警告されます(サーバーが付与できるのは、委任者自身が現在持っているスコープまでです。auth/delegation.pyを参照してください)。サーバーの公開鍵を直接指定する場合は、代わりに--serverを渡してください。--scope/--role(複数指定可能。組み合わせも可能)で付与する権限を選び、--ttl(例:24h、7d)で有効期間を指定します。サーバーは 30 日を超える有効期間を拒否します。- 出力されるのは署名済みの委任イベントです。発行後はベアラー認証情報として機能します。信頼できるチャネルを使って(API キーを渡す場合と同様に)、エージェントにファイルを渡し、
yunohost-mcp-connect --delegation-file agent-delegation.jsonで使用させてください。 - 有効期限前に委任を取り消すには、署名後に表示される
idをrevoked_delegations.tomlに追加してください。これは、委任者自身のidentity.tomlエントリを削除する方法(その場合、その委任者が発行したすべての委任が取り消されます)とは独立しており、より細かく取り消しを制御できます。
高リスク操作の承認:yunohost-mcp-approve
一部の操作(system_upgrade、backup_restore、backup_delete、system_migrate、user_delete、権限の変更、ファイアウォールの変更)には、リクエストを行ったユーザー自身の確認に加えて、オーナーによる連署が必要です(PLAN.md フェーズ 13、「solo」プロファイル。詳しい設計については、パッケージングリポジトリの docs/owner-approval-plan.md を参照してください)。リクエストを行ったユーザーの呼び出しは confirmation_id を返して一時停止し、設定済みのオーナーが yunohost-mcp-approve で確認して承認します。オーナーは自身の NIP-46 リモート署名アプリ(Amber、nsec.app など)を使って署名します。オーナーの秘密鍵がこのサーバーや、リクエストを行ったエージェントのマシンに渡ることはありません。
DNS の公開、カタログの公開、パッケージテストのライフサイクル操作にも、オーナーによる連署が必要です。パッケージテストで書き込みを行う場合は、まず package_test_prepare を使用する必要があります。返される短期間有効なセッションは、操作を 1 人のリクエストユーザー、候補ソース、およびアプリ ID に紐づけます。候補パッケージのスクリプトは YunoHost の権限で実行されるため、パッケージテストは特権操作として扱ってください。
リバースプロキシを使ってデプロイする場合は、NIP-98 の署名で使用する正確な公開オリジン(例:https://mcp.example.org)を YUNOHOST_MCP_PUBLIC_BASE_URL に設定してください。予期しない Host ヘッダーを含むリクエストは拒否されます。ローカルの stdio トランスポートは、信頼できるローカルクライアントに限定してください。このトランスポートは、ローカルプロセスに意図的に完全な管理者権限を付与します。
オプションの NostrHost コントロールプレーンブリッジ
NostrHost の派生環境では、リファレンス FastMCP サーバーは、登録済みの操作を MCP プロセス内で実行する代わりに、ローカルの署名付きコントロールプレーンに送信できます。YUNOHOST_MCP_NATIVE_CONTROL_PLANE_ENABLED=true を有効にし、YUNOHOST_MCP_NATIVE_CONTROL_PLANE_RELAY を設定して、専用の 64 文字の 16 進数エージェントキーを
YUNOHOST_MCP_NATIVE_CONTROL_PLANE_AGENT_KEY_PATH に用意します。MCP サーバーは署名付きリクエストのみを公開し、コントロールプレーンデーモンがスコープの確認、管理者の承認、実行、監査情報の公開を行います。
ブリッジ経由の呼び出しでは、専用のブリッジキーが署名付きの requester となり、認証済みの MCP 呼び出し元の Nostr 公開鍵は、リクエスト、実行、結果、状態スナップショットの各レコードで署名付きの actor タグとして付与されます。これにより、認可はリクエストを暗号学的に送信したキーに基づいたまま、エージェントと人間のユーザーを第一級の監査 ID として扱えます。
所有者が署名アプリを使うデバイスで、一度だけセットアップします。
yunohost-mcp-approve pair --owner-npub <owner-npub>
署名アプリで開くための nostrconnect:// URI が表示されます。これにより、後から再接続できるセッションがローカルに保存され、以降の承認で再ペアリングする必要がなくなります。署名アプリ自体から bunker:// 接続文字列をエクスポートできる場合(アプリ独自の「接続を追加」機能)、yunohost-mcp-approve pair --owner-npub <owner-npub> --bunker-uri <uri> を実行すると、それを使ってすぐに接続できます。開くリンクも、待ち時間もありません。ペアリングでは所有者キーの明示が必要なため、トランスポートエンドポイントを設定済みの所有者に置き換えることはできません。
特定の保留中の操作を確認して承認するには、次のようにします。
yunohost-mcp-approve approve --server https://your-yunohost-domain/mcp --confirmation-id confirm-...
サーバーから信頼できる保留中操作のレコードを取得し(ローカルで指定されたプランは決して信頼しません)、正確なツール名、引数、operation_hash を表示します。署名付き承認を送信する前に、yes と入力する必要があります。承認されると、元のリクエスト元は呼び出しを再試行できます。
自動プッシュ承認。 ペアリング済みであれば、通常、この手順はまったく必要ありません。require_owner_signature 操作がリクエストされた時点で、サーバーは同じペアリング済みセッションを再利用してライブ NIP-46 接続を開き、その場で読みやすい短い承認イベントに署名するよう署名アプリに求めます。署名アプリにプッシュ通知が届くため、コマンドを実行する必要はありません。そこで承認するとチケットが直接承認済みになります。拒否、タイムアウト(YUNOHOST_MCP_OWNER_PUSH_APPROVAL_TIMEOUT_SECONDS、既定値は 90 秒)、またはセッションがまだペアリングされていない場合は、上記の手動による yunohost-mcp-approve approve のためにチケットが保留状態のままになります。YUNOHOST_MCP_OWNER_PUSH_APPROVAL_ENABLED=false を設定すると、機能全体を無効にできます。
Armada の任意のお知らせ
パッケージ開発者ロールには communications.armada.write が含まれています。Armada 統合が有効な場合、armada_join は Guestbook Join を公開することで、ボット用に設定された Concord 招待を明示的に受諾します。catalog_publish の成功後、その JSON 結果を指定して catalog_announce を呼び出すと、パッケージリポジトリと同じ名前のチャンネル(例:ditto_ynh)に投稿されます。catalog_announcement_status では、永続化された配信状態を確認できます。
お知らせはデフォルトで無効になっており、ベストエフォートで実行されます。有効にするには、root 所有でモード 0600 のボットキーと招待 URL のファイルを用意し、次を設定します。
YUNOHOST_MCP_ARMADA_ENABLED=true
YUNOHOST_MCP_ARMADA_BOT_KEY_PATH=/etc/yunohost-mcp/armada-bot.key
YUNOHOST_MCP_ARMADA_COMMUNITY_INVITE_PATH=/etc/yunohost-mcp/armada-community.invite
YUNOHOST_MCP_ARMADA_AUTO_ANNOUNCE=true を設定すると、カタログへの登録が成功した後に catalog_publish が自動的にお知らせを試みます。Armada 操作が失敗または未検証でも、カタログの結果は変わりません。署名に使う ID には、必要なチャンネルキーがすでに必要です。招待の受諾とロールの割り当ては別々の操作です。
エージェントごとのお知らせキー。 デフォルトでは、すべてのお知らせは上記の共有ボットキーで署名されます。代わりに identity.toml のエントリで armada_key_path を設定すると、専用の root 所有・モード 0600 のキーファイルを指定できます。これにより、その ID のお知らせと armada_join 呼び出しでは、共有ボットではなく、その ID 固有の Concord ID が使われます。
[identity."npub1..."]
name = "release-bot-a"
roles = ["package-developer"]
armada_key_path = "/etc/yunohost-mcp/armada-bot-a.key"
キーファイルを配置したら、その ID 自身が一度 armada_join を呼び出す必要があります。別個のキーを持っているだけでは、コミュニティへの参加やチャンネルへのアクセスが保証されるわけではありません。これは共有ボットキーと同様、サーバーが保持するファイルベースの認証情報であり、その ID 自身の NIP-98 署名キーではありません。
キー・ファイルなしで、エージェント自身のキーを使ってお知らせする方法。 yunohost-mcp-connect(bridge.py)経由で接続された MCP エージェントは、すべての NIP-98 リクエストに署名するための独自の Nostr キーをすでにローカルに保持しています。ワンショットのツールの代わりに catalog_announce_draft/catalog_announce_submit と armada_join_draft/armada_join_submit を使うことで、サーバー側に認証情報を一切用意せず、同じキーでお知らせと参加を行えます。
catalog_announce_draft(source, catalogue_publication)(またはarmada_join_draft())は、秘密情報を必要としないルーティングとチャンネルキーの処理をすべて行い、未署名イベントと有効期間の短いdraft_idを返します。- エージェントは、そのイベントにローカルで署名します。ブリッジはローカル専用の
sign_nostr_event(kind, tags, content, created_at)ツールを使ってこれを処理します(リモートサーバーには決して転送されず、Concord seal イベントに制限されています)。秘密キーがブリッジプロセスの外に出ることはありません。 catalog_announce_submit(draft_id, signed_event)(またはarmada_join_submit)は、署名が発行された内容と正確に一致し、かつリクエストした ID のものであることを検証してから、公開します。
共有ボットキー/armada_key_path は、catalog_publish の YUNOHOST_MCP_ARMADA_AUTO_ANNOUNCE フックと、非対話型/stdio の呼び出し元では引き続き必要です。どちらも、この 2 段階のやり取りを実行できないためです。
開発
uv sync --group dev
uv run pytest -q
リリースタグの作成
メンテナーは、リリース対象のブランチまたはコミットから GitHub Actions の Tag release ワークフローを手動で実行できます。v 接頭辞なしのバージョン(例:0.1.1)を指定します。このワークフローはバージョンを検証し、既存のタグの上書きを拒否して、Python 3.11 および 3.12 で CI チェックをすべて実行した後にのみ、注釈付きの v<version> タグをプッシュします。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。