mcpField
Y
YunoHost MCP

YunoHost MCP

本地服务
github.com/imattau云与基础设施16 周前更新

关于 YunoHost MCP

YunoHost MCP 为 AI 客户端提供 YunoHost 管理、诊断和 _ynh 软件包开发能力,并以 Nostr 身份验证和策略控制请求。配套连接器可为不支持 NIP-98 签名的 MCP 客户端转发请求。

主要功能

  1. 01基于 Nostr 身份验证与角色策略
  2. 02提供受限的只读故障诊断
  3. 03支持 _ynh 软件包开发操作
  4. 04可选连接 Polypack 记忆服务
  5. 05连接器转发并签名远程 MCP 请求
  6. 06支持限时、限范围委派及高风险操作审批

适用场景

  1. 01通过 MCP 客户端管理 YunoHost
  2. 02排查日志、服务、网络及主机状态问题
  3. 03让不支持 NIP-98 的客户端连接远程服务
  4. 04为临时 Agent 委派受限访问权限

MCP 相关

服务器通过 stdio 或经 NIP-98 身份验证的 HTTP 向 MCP 客户端提供 YunoHost 工具。配套 yunohost-mcp-connect 以本地 stdio 接收客户端请求,并用 Nostr 密钥签名后转发到远程服务器。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"imattau-yunohost": {
"command": "uvx",
"args": [
"yunohost-mcp-connect"
]
}
}
}
启动命令uvx yunohost-mcp-connect

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypiyunohost-mcp-connect0.8.46

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

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 服务的认证门面。将 YUNOHOST_MCP_POLYPACK_URL 设置为该服务的回环 /mcp/ 端点;该 URL 必须解析为 127.0.0.1、::1 或 localhost,且 Polypack 仍是其数据库的唯一所有者。YunoHost MCP HTTP 端点仍是 Agent 应公开提供服务的唯一端点。

初始集成提供了受限的 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 Agent 辅助设置

此设置向导旨在由 AI Agent 在用户的本地机器上运行。它会生成新的客户端身份、写入所选 MCP 客户端配置,并输出剩余的唯一安全敏感步骤:在 YunoHost 服务上为生成的 npub 授予适当角色。

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。 设置可安全重复运行,会拒绝与现有服务定义冲突的配置,在更改现有配置前先创建备份,并且绝不会输出私钥。若希望 Agent 仅显示配置而不写入文件,请使用 --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 通信,并将每个请求转发到远程 --transport http 服务,并使用你自己的 Nostr 密钥签名。

yunohost-mcp-connect --remote-url https://your-yunohost-domain/mcp --key-file ~/.config/yunohost-mcp/key
  • --key-file(或 $YUNOHOST_MCP_CLIENT_KEY_FILE)指向一个存有十六进制或 nsec1... 私钥的文件——相比之下,--key/$YUNOHOST_MCP_CLIENT_KEY 会将密钥放在 argv/环境中,同一台机器上的其他进程可以读取这些内容,因此更推荐使用 --key-file。
  • --generate-key PATH 会将新私钥写入 PATH(权限为 0600;若文件已存在则拒绝覆盖),打印其 npub,然后退出,不会连接任何位置——这是获取 --key-file 的方式。为每个客户端使用不同的 PATH;请参阅下文“为每个客户端指定自己的密钥文件”。
  • --delegation-file(或 $YUNOHOST_MCP_CLIENT_DELEGATION_FILE)会在你自己的签名旁附上一个委托事件(PLAN.md 第 11 阶段),用于由所有者授予部分访问权限的一次性 Agent 身份。
  • 将 MCP 客户端的配置指向此命令(而非直接指向服务)——tools/list、tools/call、resources/list 和 resources/read 都会原样转发;授权、策略和审计仍由远程服务处理。

每个连接器都会独立于远程 YunoHost 握手启动本地 MCP 端点。如果某个已配置的 YunoHost 实例离线,该连接器会保持运行,在实例恢复前不会发现任何工具,并会在后续请求中重试;其他已配置的 MCP 服务仍可启动并保持可用。

事件排查

服务包含有限制的只读诊断工具,用于排查间歇性故障:

  • web_logs 解析 Nginx 访问和错误日志,包括 HTTP 状态和上游状态。
  • 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,或某个 venv 的 bin/ 目录——激活环境后运行 which yunohost-mcp-connect 即可查看)。

为每个客户端分别使用一个密钥文件。 YUNOHOST_MCP_CLIENT_KEY_FILE 就是身份标识——用于签署请求的密钥决定了该身份在服务端的角色和权限范围,除此之外别无他物。如果让两个不同的客户端(或同一客户端的两个不同配置文件——项目本地的 .codex/config.toml 会覆盖 ~/.codex/config.toml)指向同一个密钥文件,第二个客户端就会悄无声息地以第一个客户端的身份进行身份验证,并拥有完全相同的权限——不会报错,也不会有任何迹象。请为每个客户端生成新密钥,而不要复制已经可用的密钥:

yunohost-mcp-connect --generate-key ~/.config/yunohost-mcp/claude-desktop.key
yunohost-mcp-connect --generate-key ~/.config/yunohost-mcp/codex.key

每次运行都会显示新密钥的 npub——请为该特定客户端授予适当的角色(见下文“授予临时 Agent 身份访问权限”,或直接查看 identity.toml),不要使用你已授予其他客户端的角色。--generate-key 不会覆盖已存在的文件。

Claude Desktop(claude_desktop_config.json — 设置 → 开发者 → 编辑配置):

{
  "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 授予此客户端实际适用的角色——不要使用你已授予其他客户端的角色。

如果要使用委派的(临时)身份,而不是你自己的密钥,请添加 YUNOHOST_MCP_CLIENT_DELEGATION_FILE,并将其指向 yunohost-mcp-delegate 生成的文件(见下文)。编辑配置后,请重启客户端——两者都只会在启动时读取一次该文件。

授予临时 Agent 身份访问权限:yunohost-mcp-delegate

identity.toml 条目会永久授予某个特定公钥访问权限(除非编辑该条目)。委派(PLAN.md Phase 11)是授予访问权限的另一种方式:所有者使用 Agent 自己的临时密钥,签署一份有期限且限定权限范围的授权,无需将该密钥添加到 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 是 Agent 自己的公钥(和往常一样,它必须使用自己的密钥为 NIP-98 请求签名——委派不会取代签名要求,只会额外授予访问权限)。
  • --remote-url 会自动获取服务端公钥和此所有者当前拥有的权限范围——无需手动输入服务端公钥,并且会标记超出权限的 --scope/--role 请求(服务端授予的权限永远不会超过委派者当前拥有的权限;见 auth/delegation.py)。如果你更愿意直接提供服务端公钥,请传入 --server。
  • --scope/--role(可重复使用、可组合)用于选择要授予的权限;--ttl(例如 24h、7d)用于设置有效时长——服务端会拒绝任何超过 30 天的时长。
  • 输出内容是已签名的委派事件:签发后即可作为持有者凭证使用。请通过你信任的渠道将文件交给 Agent,并让其通过 yunohost-mcp-connect --delegation-file agent-delegation.json 使用(与交付 API 密钥的方式相同)。
  • 若要在委派到期前撤销它,请将其 id(签名后打印)添加到 revoked_delegations.toml——这与移除委派者自己的 identity.toml 条目相互独立,且粒度更细。移除该条目会撤销此人签发过的所有委派。

批准高风险操作:yunohost-mcp-approve

一些操作(system_upgrade、backup_restore、backup_delete、system_migrate、user_delete、权限更改、防火墙更改)除了请求者本人确认外,还需要所有者共同签署(PLAN.md Phase 13,solo 配置文件——完整设计请参阅打包仓库中的 docs/owner-approval-plan.md)。请求者的调用会暂停,并返回 confirmation_id;配置的所有者通过 yunohost-mcp-approve 审核并批准,同时使用自己的 NIP-46 远程签名应用(Amber、nsec.app 等)签名——其私钥绝不会接触此服务或发起请求的 Agent 所用的计算机。

DNS 发布、目录发布和软件包测试生命周期操作也需要所有者共同签署。软件包测试写入操作必须先使用 package_test_prepare;返回的短期会话会绑定到一个请求者、一个候选源和一个应用 ID。请将软件包测试视为特权操作,因为候选软件包脚本会以 YunoHost 权限执行。

对于反向代理部署,请将 YUNOHOST_MCP_PUBLIC_BASE_URL 设置为 NIP-98 签名中使用的确切公共源站地址(例如 https://mcp.example.org)。带有意外 Host 标头的请求会被拒绝。请将本地 stdio 传输限制为可信的本地客户端:它会有意授予本地进程完整的管理员权限。

可选的 NostrHost 控制平面桥接

在 NostrHost 衍生版本上,参考 FastMCP 服务可以将已注册的操作提交到本地签名控制平面,而不是在 MCP 进程中执行。设置 YUNOHOST_MCP_NATIVE_CONTROL_PLANE_ENABLED=true 启用此功能,设置 YUNOHOST_MCP_NATIVE_CONTROL_PLANE_RELAY,并在 YUNOHOST_MCP_NATIVE_CONTROL_PLANE_AGENT_KEY_PATH 指定一个专用的 64 位十六进制 Agent 密钥。MCP 服务仅发布签名请求;控制平面守护进程负责执行范围检查、管理员审批、操作执行和审计发布。

对于桥接调用,专用桥接密钥是签名的 requester,经过身份验证的 MCP 调用方的 Nostr 公钥则作为签名的 actor 标签,包含在请求、执行、结果和状态快照记录中。这样一来,Agent 和人类用户都能成为一等审计身份,同时授权仍以通过密码学手段提交请求的密钥为依据。

一次性设置,在所有者保存签名器应用的任意设备上执行:

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 操作失败或未经验证,都不会改变目录结果。签名身份必须已拥有所需的频道密钥;接受邀请和分配角色是两个独立操作。

每个 Agent 独立的公告密钥。 默认情况下,所有公告都使用上述共享机器人密钥签名。也可以在 identity.toml 条目中设置 armada_key_path,指定一个专用的、由 root 拥有且权限模式为 0600 的密钥文件,这样该身份自己的公告和 armada_join 调用就会使用其专属的 Concord 身份,而不是共享机器人身份:

[identity."npub1..."]
name = "release-bot-a"
roles = ["package-developer"]
armada_key_path = "/etc/yunohost-mcp/armada-bot-a.key"

密钥文件就位后,该身份必须自行调用一次 armada_join——拥有独立密钥并不意味着已加入社区或获得频道访问权限。这仍然是由服务器持有、以文件为载体的凭据,与共享机器人密钥一样,绝不是该身份自己的 NIP-98 签名密钥。

使用 Agent 自己的密钥发布公告,完全无需密钥文件。 通过 yunohost-mcp-connect(bridge.py)连接的 MCP Agent,已在本地持有自己的 Nostr 密钥,用于签署每个 NIP-98 请求——它可以用同一密钥以自己的身份发布公告和加入频道,无需在服务器端配置任何凭据。为此,请使用 catalog_announce_draft/catalog_announce_submit 和 armada_join_draft/armada_join_submit,而不是一次性工具:

  1. catalog_announce_draft(source, catalogue_publication)(或 armada_join_draft())会完成所有无需秘密信息的路由和频道密钥操作,并返回一个未签名事件以及一个短期有效的 draft_id。
  2. Agent 在本地为该事件原样签名。桥接程序通过本地专用的 sign_nostr_event(kind, tags, content, created_at) 工具自行处理此请求(绝不会转发到远程服务,且仅限于 Concord seal 事件)——私钥始终不会离开桥接进程。
  3. catalog_announce_submit(draft_id, signed_event)(或 armada_join_submit)会验证签名是否与签发的内容完全一致,以及请求者身份是否匹配,然后发布事件。

共享机器人密钥/armada_key_path 仍用于 catalog_publish 的 YUNOHOST_MCP_ARMADA_AUTO_ANNOUNCE 钩子,以及任何非交互式/stdio 调用方,因为它们都无法完成这次两步往返流程。

开发

uv sync --group dev
uv run pytest -q

创建发布标签

维护者可以从要发布的分支或提交手动运行 GitHub Actions Tag release 工作流,并提供不带 v 前缀的版本号(例如,0.1.1)。该工作流会验证版本号、拒绝覆盖已有标签、在 Python 3.11 和 3.12 上运行完整的 CI 检查,之后才推送带注释的 v<version> 标签。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。