关于 TrueNAS SCALE
TrueNAS SCALE 是面向 TrueNAS SCALE 的 MCP 服务,可读取并管理存储、共享、应用和系统状态。它支持 HTTP 与 stdio,使用每位用户自己的 API 密钥,并默认仅开放读取操作。
主要功能
- 01查看存储与系统状态
- 02管理共享和应用
- 03按用户 API 密钥授权
- 04默认仅启用读取工具
- 05可选启用写入操作
适用场景
- 01检查 TrueNAS 存储状态
- 02查看和管理共享及应用
- 03从远程 MCP 客户端连接 NAS
MCP 相关
MCP 服务通过 Streamable HTTP 或 stdio 向客户端提供 TrueNAS 操作工具;HTTP 请求由调用者携带个人 API 密钥,写入工具默认关闭。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm -e TRUENAS_MCP_TARGET -e TRUENAS_MCP_ALLOW_PLAINTEXT -e TRUENAS_MCP_TARGET_INSECURE -e TRUENAS_MCP_ENABLE_WRITES ghcr.io/cedricziel/truenas-mcp:0.4.2TRUENAS_MCP_TARGETTrueNAS host to manage, optionally `host:port`. Requires TrueNAS SCALE 25.04 or later.TRUENAS_MCP_ALLOW_PLAINTEXTServe MCP without TLS. Set here because the listener is published on loopback only; drop it and supply TRUENAS_MCP_TLS_CERT/KEY if the port is reachable from elsewhere.TRUENAS_MCP_TARGET_INSECUREAccept the TrueNAS certificate unverified while keeping the connection encrypted. TrueNAS ships a self-signed certificate for `CN=localhost`, so no address you can reach it by will validate.TRUENAS_MCP_ENABLE_WRITESExpose the mutating tools. Read-only by default; unrecoverable operations stay unreachable either way.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/cedricziel/truenas-mcp:0.4.2Streamable HTTP工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
truenas-mcp
适用于 TrueNAS SCALE 的 MCP 服务。以读取为先,可部署为 TrueNAS 应用,并使用每位用户自己的 API 密钥进行身份验证。
状态:可用,但尚处早期阶段。 设计中的每项功能均已实现,并在一台运行 TrueNAS 26 的设备上完成验证。预计会有一些不足,而非功能缺失。
存在的原因
iX 提供了官方的 truenas/truenas-mcp,
如果它符合你的需求,就应该使用它。开发这个项目,是因为它有三项做不到的事:
- 它只支持 stdio,因此无法作为容器部署在 NAS 上,再从其他位置访问。
- 它持有一个服务器级别的 API 密钥,因此无论调用者如何进行身份验证,所有人都能获得相同的访问权限。
- 它的应用覆盖范围受目录形态限制——安装、卸载、浏览——无法拉取新镜像并重新部署已在运行的应用。
设计
三项理念构成了设计的核心。
凭证即授权。 调用者提供自己的 TrueNAS API 密钥;此服务不存储任何密钥。会话只能访问该用户的密钥所允许的内容,密钥可在 TrueNAS UI 中撤销,也没有共享密钥需要担心泄露。身份验证和授权不再是两个可能产生分歧的系统。通过 stdio 提供服务时,密钥改由环境变量传入,因为没有请求可以携带密钥;但客户端会为每位用户启动一个独立进程,因此仍使用该用户自己的密钥。此设计要避免的是用一个密钥代表多个调用者,而不是配置本身。
读取和写入采用不同的工具形态。 读取操作按关注领域归入带有 op 枚举的工具,因为这些操作的大多数参数相同,而且 815 个中间件方法不可能各自成为一个工具。写入操作则各自作为独立工具——MCP 注释是针对每个工具设置的,因此若将安全操作与破坏性操作放在同一个 op 参数后面,两者就会共用同一个确认门槛;用户若厌倦了确认 list_pools,就会将这个也能导出池的工具加入允许列表。
默认只读。 只有显式启用后,才会提供修改工具。此外,无论采用何种配置,都无法调用黑名单中无法恢复的操作;黑名单约束的是参数值,而不仅仅是方法名称——删除应用可以恢复,而连同卷一起删除则无法恢复,尽管二者调用的是同一个方法。
这些设计理念都在真实设备上进行了验证,而非纸上推演;其中好几项还因实际测量结果而被推翻。
要求
- TrueNAS SCALE 25.04 或更高版本。TrueNAS 26 已移除 REST API;此服务仅支持有版本控制的 JSON-RPC 2.0 WebSocket API。
- 每位用户都需要一个 TrueNAS API 密钥。请在 Credentials → API Keys 中创建。
部署为 TrueNAS 应用
复制 deploy/truenas-custom-app.yaml,调整
TRUENAS_MCP_TARGET,然后粘贴到 Apps → Discover → Install via YAML 中。
此应用不会挂载主机套接字,也不会请求特权访问。即使运行在同一台设备上,此服务也会通过网络连接中间件,因此每个连接都携带用户身份,而不是获得等同于 root 的套接字访问权限。
在其他位置运行
服务无需运行在它所管理的机器上,而且不在该机器上运行还有充分理由:若将其安装为 TrueNAS 应用,那么设备恰好在故障时服务也无法使用——而这正是你最想询问设备出了什么问题的时候。
docker run -p 8080:8080 \
-e TRUENAS_MCP_TARGET=nas.local \
-e TRUENAS_MCP_TLS_CERT=/tls/cert.pem \
-e TRUENAS_MCP_TLS_KEY=/tls/key.pem \
ghcr.io/cedricziel/truenas-mcp:main
运行二进制文件
每个 GitHub 发行版
都附带适用于 Linux、macOS 和 Windows 的 amd64 与 arm64 二进制文件,以及
checksums.txt。配置仅通过环境变量完成——没有配置文件,唯一的标志是 --stdio 和 --healthcheck。
默认情况下,二进制文件是一个 HTTP 服务。运行它不会自动让客户端发现并使用它;它会监听一个端口,客户端通过 URL 连接到它。对于会自行启动服务的客户端,请参阅下文的 通过 stdio 提供服务。
TRUENAS_MCP_TARGET=nas.local \
TRUENAS_MCP_LISTEN=127.0.0.1:8080 \
TRUENAS_MCP_TARGET_INSECURE=true \
TRUENAS_MCP_ALLOW_PLAINTEXT=true \
./truenas-mcp
将客户端指向 http://localhost:8080/mcp,并通过 Authorization: Bearer 标头发送 TrueNAS API 密钥,与连接客户端中的做法相同。
通常需要设置 TRUENAS_MCP_TARGET_INSECURE,原因见关于两个 TLS 设置:TrueNAS 会提供一个适用于 CN=localhost 的自签名证书,该证书无法通过任何其他地址的验证。此处可以合理地设置 TRUENAS_MCP_ALLOW_PLAINTEXT,因为 TRUENAS_MCP_LISTEN 会将监听器绑定到回环地址——只能从同一台机器访问——而该章节指出,这是使用明文传输的必要条件。默认绑定地址为 :8080,即所有网络接口;因此,如果移除该设置但仍保留明文传输,API 密钥就会暴露在网络上。
通过 stdio 提供服务
有些客户端会将服务作为子进程启动,并通过其标准输入和标准输出与之通信,而不是连接到 URL。--stdio 会以这种方式提供相同的工具。
claude mcp add --scope user truenas \
--env TRUENAS_MCP_TARGET=nas.local \
--env TRUENAS_MCP_TARGET_INSECURE=true \
--env TRUENAS_MCP_API_KEY=$YOUR_TRUENAS_API_KEY \
-- /path/to/truenas-mcp --stdio
此处没有请求可携带标头,因此密钥改为从 TRUENAS_MCP_API_KEY 获取。这并不是 HTTP 传输所避免的那种共享密钥:客户端会为每个用户启动一个进程,因此传入的密钥属于该用户,进程只能访问该密钥允许访问的内容。在 HTTP 模式下,同一进程会为多个调用方提供服务,配置的密钥将由所有调用方共享,因此该模式会拒绝使用同一个变量。
监听器相关的设置均不适用。TRUENAS_MCP_LISTEN、两个 TLS 设置和 TRUENAS_MCP_ALLOW_PLAINTEXT 都会被忽略,并发出警告而非报错,因为没有监听器时,这些设置都不会削弱安全性。--healthcheck 与 --stdio 不能同时使用,因为它会探测一个从未启动的监听器。
与目标而非监听器有关的设置仍然有效,包括
TRUENAS_MCP_TARGET_INSECURE 和 TRUENAS_MCP_ENABLE_WRITES。
配置
所有配置均通过环境变量提供;不需要配置文件或持久化卷。配置无效时,程序会拒绝启动,而不会以降级模式运行。
| 变量 | 默认值 | 含义 |
|---|---|---|
TRUENAS_MCP_TARGET | 必填 | TrueNAS 主机,可选填写 host:port |
TRUENAS_MCP_LISTEN | :8080 | 绑定地址 |
TRUENAS_MCP_TLS_CERT / TRUENAS_MCP_TLS_KEY | — | 通过 TLS 提供 MCP 服务 |
TRUENAS_MCP_ALLOW_PLAINTEXT | false | 不使用 TLS 提供服务(见下文) |
TRUENAS_MCP_TARGET_INSECURE | false | 接受未经验证的目标证书 |
TRUENAS_MCP_TARGET_ALLOW_PLAINTEXT | false | 不使用 TLS 连接目标 |
TRUENAS_MCP_ENABLE_WRITES | false | 暴露可修改数据的工具 |
TRUENAS_MCP_API_KEY | — | --stdio 的凭据;否则会被拒绝 |
TRUENAS_MCP_OAUTH_ISSUER | — | 启用 OAuth;服务器自身可从外部访问的基础 URL |
TRUENAS_MCP_OAUTH_ENCRYPTION_KEY | 自动生成 | 64 个十六进制字符(32 字节);见下文 |
TRUENAS_MCP_OAUTH_ACCESS_TOKEN_TTL | 1h | 已签发的 OAuth 访问令牌有效时长 |
TRUENAS_MCP_OAUTH_REFRESH_TOKEN_TTL | 720h(30 天) | 刷新令牌有效时长;0 表示禁用刷新令牌 |
HTTP 传输不支持配置凭据。 调用方需在每个请求中自行提供凭据;如果未使用 --stdio 却设置了 TRUENAS_MCP_API_KEY,程序会报错并拒绝启动,而不会静默回退。通过 stdio 通信时,请求中无法携带密钥,且进程只服务于单个用户,因此该变量用于传入该用户的密钥——参见通过 stdio 提供服务。
关于两项 TLS 设置
传输协议方案与证书验证是刻意分开设置的。
TrueNAS 使用自签名证书,证书颁发给 CN=localhost,且 SAN 中仅有 DNS:localhost,因此无论通过哪个地址访问,都无法通过验证。解决方法是设置 TRUENAS_MCP_TARGET_INSECURE=true:连接仍会加密,只是不进行身份验证。如果因为证书问题而改用明文传输,TrueNAS 就会明文接收到你的 API 密钥,并会将其撤销。
TRUENAS_MCP_ALLOW_PLAINTEXT 关系到调用方跨越的边界,该边界承载着调用方的 API 密钥。如果服务器前方有反向代理负责终止 TLS,那么这样设置是正确的;如果明文监听器可被直接访问,则不应这样设置。
连接客户端
claude mcp add --scope user --transport http truenas \
https://your-host/mcp \
--header "Authorization: Bearer $YOUR_TRUENAS_API_KEY"
对于无法设置 Authorization 标头的客户端,也可以通过 X-TrueNAS-API-Key 发送密钥。未提供这两种标头的请求会被拒绝,并返回 401。
如果客户端会启动服务器,而不是连接到已运行的服务器,请改用通过 stdio 提供服务。
连接 OAuth 客户端(Claude.ai 及类似客户端)
Claude.ai 这类托管 MCP 客户端不会直接持有 TrueNAS API 密钥——它只使用 OAuth 2.1:发现、动态客户端注册(RFC 7591),以及带 PKCE 的授权码流程。设置颁发者 URL 即可启用:
docker run -p 8080:8080 \
-e TRUENAS_MCP_TARGET=nas.local \
-e TRUENAS_MCP_TLS_CERT=/tls/cert.pem \
-e TRUENAS_MCP_TLS_KEY=/tls/key.pem \
-e TRUENAS_MCP_OAUTH_ISSUER=https://your-host \
-e TRUENAS_MCP_OAUTH_ENCRYPTION_KEY=$(openssl rand -hex 32) \
ghcr.io/cedricziel/truenas-mcp:main
TRUENAS_MCP_OAUTH_ISSUER 必须是客户端可访问此服务的确切基础 URL(末尾不要加斜杠)。TRUENAS_MCP_OAUTH_ENCRYPTION_KEY 用于封装此进程颁发的每个客户端注册、授权码和令牌;不设置也能正常工作,但每次重启都会生成新密钥,并使所有尚未失效的凭据失效——除非只是短暂试用,否则请明确设置。TrueNAS 凭据以及客户端/令牌数据库都不会存储在任何地方:OAuth 颁发的所有内容都自包含,并通过这一个密钥进行验证。有关原因,请参阅 openspec/changes/oauth-dcr-authorization/design.md。
像使用上面的原始 API 密钥一样,将客户端指向 https://your-host/mcp;客户端会通过 /.well-known/oauth-protected-resource 自动发现其他所有信息——授权服务器、注册地址,以及向资源所有者发送请求的地址。首次连接时会打开浏览器授权页面,要求输入 TrueNAS 用户名(可选,仅你自己可见)和 API 密钥;在提交这些信息且目标服务器接受之前,不会向客户端授予任何权限。
TRUENAS_MCP_ALLOW_PLAINTEXT 的作用与原始 API 密钥方式相同:它适用于在边缘终止 TLS 并将明文流量转发到此进程的反向代理,不适用于在未加密的情况下通过开放互联网公开授权页面。
上面的原始 API 密钥方式仍可照常与 OAuth 并行使用——启用 OAuth 是增加第二种接入方式,并不会取代第一种。
当前状态
已实现:
- Streamable HTTP 传输、按会话设置凭据,以及未提供凭据时返回
401 - 支持动态客户端注册的 OAuth 2.1,适用于仅支持 OAuth 的客户端(Claude.ai 及类似客户端)——请参阅连接 OAuth 客户端
- 在单个进程凭据下使用 Stdio 传输,适用于启动服务而非连接到已有服务的客户端
- JSON-RPC 中间件客户端:在一个连接上并发调用;结构化错误可区分无法访问、未通过身份验证、未获授权、受到速率限制等情况;中断的请求会报告为_可能已应用_
- 连接断开时重新连接会话,并拒绝对低于 25.04 的版本运行
- 容器映像、CI、GHCR 发布、TrueNAS 应用部署
**尚未实现:**通过资源_订阅_获取作业进度。轮询可覆盖相同场景,而且设计将其视为可靠的方式——订阅始终只是对轮询的增强,并且 MCP 客户端对此功能的支持有限。
工具
| Tool | Operations |
|---|---|
storage | list_pools、show_pool、list_datasets、show_dataset、list_snapshots |
system | info、alerts、list_services、update_status、version、audit_log |
sharing | list_smb、show_smb、smb_acl、list_nfs、show_nfs、list_web |
virtualization | list_vms、show_vm、vm_devices、list_containers、show_container、container_devices |
backup | list_cloud_syncs、show_cloud_sync、cloud_credentials、list_replications、show_replication、list_rsync_tasks、list_snapshot_tasks |
filesystem | list_directory、stat、space、acl、read |
apps | list、show、config、containers、outdated_images、upgrade_summary、rollback_versions、used_ports |
catalog | list、categories、show |
jobs | list、show |
search_methods | 按名称查找 middleware 方法 |
describe_method | 方法参数的摘要 |
call_method | 直接调用方法 |
inventory | 一次调用即可汇总整个设备;以 MCP App 的形式呈现 |
server_info | — |
system_info | — |
每个工具都声明完整的 MCP 注解集:title、readOnlyHint、
destructiveHint、idempotentHint、openWorldHint。规范中
destructiveHint 和 openWorldHint 的默认值为 true,因此未设置字段
并不表示“未知”,而是表示“按最坏情况处理”——如果将读取工具视为
具有破坏性,就会在安全操作时弹出提示,反而会让人学会跳过真正重要的提示。
每个读取工具背后的方法都会根据目标系统自身的 RBAC 元数据进行验证,
以授予 READONLY_ADMIN 权限,因此“此工具无法修改数据”是经过检查的,而非仅仅声称如此。
唯一的例外是 filesystem read。它会返回文件文本,最多读取
64 KiB,并拒绝二进制文件。它不会更改任何内容,但 middleware 的
filesystem.get 需要 FULL_ADMIN 权限,因此只读密钥会收到明确的“无权
访问”提示。文件通过 core.download 流式传输,因此
call_method 会拒绝 filesystem.get,以及所有其他需要下载或上传管道的方法。
apps 的 outdated_images、upgrade_summary 和
rollback_versions 操作旨在让调用方在写入层执行操作之前,先决定_是否_采取行动——没有它们,变更操作界面就会迫使模型猜测。三者都接受一个应用 name;中间件没有对应的全局范围操作。
catalog 回答“我可以安装什么”,apps 回答“已安装什么”——有意将它们设计成两个工具,而不是一个工具上的两个操作,因为模型在名称明确的工具中进行选择,效果优于在一个职责过多的工具中选择操作。默认情况下,catalog list 只返回标识和版本字段:底层方法会返回大约 400 个条目,每个条目都包含完整的 HTML 自述文件、配置架构和版本历史记录,因此不加筛选的浏览会比促使 apps list 采用自身筛选机制的问题严重一个数量级,耗尽调用方的上下文。可以通过 category 缩小范围,其可选值来自 catalog categories;full=true 仍会返回所有内容。catalog show 按名称返回一个条目的完整记录,无需单独调用 catalog.get_app_details。
写入工具
默认禁用。设置 TRUENAS_MCP_ENABLE_WRITES=true 以启用这些工具。
| 工具 | 效果 | 标注 |
|---|---|---|
app_pull_images | 拉取最新镜像并重新部署 | 破坏性 |
app_redeploy | 不拉取镜像,直接重新部署 | 破坏性 |
app_stop | 停止正在运行的应用 | 破坏性、幂等 |
app_upgrade | 升级到较新版本 | 破坏性 |
app_rollback | 回滚不良升级或拉取操作 | 破坏性 |
app_start | 启动已停止的应用 | 幂等 |
create_snapshot | 为数据集创建快照 | 添加性 |
create_smb_share | 通过 SMB 共享路径 | 添加性 |
update_smb_share | 更改 SMB 共享 | 破坏性 |
delete_smb_share | 停止通过 SMB 共享 | 破坏性 |
create_nfs_export | 通过 NFS 导出路径 | 添加性 |
update_nfs_export | 更改 NFS 导出 | 破坏性 |
delete_nfs_export | 停止通过 NFS 导出 | 破坏性 |
set_smb_share_acl | 设置谁可以连接到共享 | 破坏性 |
set_path_acl | 设置路径的文件系统权限 | 破坏性 |
共享和权限配置才是重点。 这是运行 TrueNAS 最棘手的部分,也是破坏性最小的部分:配置错误的共享只会引来支持工单,不会导致数据丢失。将这项工作交给助手,正是此服务的用途。
真正的危险在于一个_参数_,而非方法。filesystem.setacl 接受 recursive、traverse 和 stripacl;同时启用 recursive 和 stripacl 会遍历整个数据集并丢弃所有 ACL,导致用户无法访问数 TB 的数据,而且如果不知道先前的权限设置,就无法撤销。为防止这种情况,这三个参数值都会被永久拒绝;因此,仍可设置单个路径的 ACL,但不能使用不受限制的形式。这一区别正是拒绝列表针对参数值而非方法名称进行限制的根本原因。
每个工具都是独立的,因此每个工具都需要单独征得同意——若将它们打包在一个 op 后面,app_stop 就会和 app_start 受到同一道限制。其他工具启用时,app_rollback 也会一并启用;正是这条恢复路径,让开放这些工具变得可接受。
变更操作不会阻塞。它们会立即返回 job_id;随后可通过 jobs(op="show", job_id=…) 跟踪任务。
所有配置下均拒绝: 导出存储池、删除数据集、擦除磁盘、解除引导设备连接、销毁快照,以及带有 remove_ixvolumes 的 app.delete,因为这里的危险在于参数,而非方法。以上操作均无法通过开关启用;请使用 Web 界面。
关于应用日志: TrueNAS 通过 app.container_log_follow 事件源公开容器输出,而非通过 JSON-RPC 方法。apps(op="logs", name=...) 返回带时间戳的有限长度日志尾部,而非实时日志流。如果应用有多个容器,请先调用 apps(op="containers", name=...),然后将一个返回的 ID 作为 container 传入。
发现机制的逃生口
中间件在 74 个命名空间中有 815 个方法。大多数都不值得专门做成工具,因此 search_methods / describe_method / call_method 可以覆盖剩余部分,无需每次发布都修改代码。
方法是否可访问,由目标自身的 RBAC 元数据决定,而不是根据方法名称猜测:只有授予 READONLY_ADMIN 权限的方法才可读,而修改方法则需要写入权限级别。这是中间件自身给出的判断,因此结果准确,并且能跟随 API 版本变化,无需在此处修改。它覆盖了 94% 的 API——411 个可读方法,359 个可修改方法。
有意不开放剩余的 6%:
core.bulk可调用任意方法;若开放,就会绕过拒绝列表、写入权限级别以及此处的所有其他关卡。auth.*应由服务端管理。调用者若能操作它,就可以创建一个在会话结束后仍有效、且永远不会出现在 API 密钥 UI 中的令牌——这是操作员从未签发过的凭据。- 完全未声明任何角色的方法。 在此目标中,这些方法属于会话和协议层的基础设施,并非无害的读取操作,因此“无需权限检查”被视为风险未知,而非没有风险。
describe_method 会做摘要,而非直接输出全部内容。在实时目标上测得,sharing.smb.create 的架构约有 31,000 个字符,directoryservices.update 的架构约有 53,000 个字符;此外,与小型且密集的架构相比,大模型对大型稀疏架构的填充也不够准确,因此忠实地完整输出成本更高,效果也更差。确实需要完整内容时,请传入 full=true。
资源
| URI | 内容 |
|---|---|
truenas://alerts | 当前告警 |
truenas://system/health | 版本、主机名、运行时间、硬件 |
truenas://pools | 包含容量和健康状态的存储池 |
truenas://apps | 已安装的应用及其状态 |
truenas://job/{id} | 长时间运行操作的进度 |
truenas://docs/query-filters | call_method 的筛选语法 |
truenas://docs/dataset-properties | ZFS 字段含义和继承规则 |
资源与工具的区别在于_控制方_,而非成本:工具由模型控制,资源则由人附加。资源在有人指向它时最有用——无需往返调用,也不占用工具预算;而如果必须让模型去查找资源,效果就不好,因为模型驱动的资源访问要通过通用的列表/读取工具进行,又会引入原本想要避免的往返调用。
因此:此处的可寻址实体和参考资料使用资源;任何需要计算或参数化的内容仍使用工具。文档资源是此设计中最有价值的部分——它们只需讲解一次筛选语法和 ZFS 语义,而不必在每个工具描述中重复说明,否则每次请求都会产生相应的 token 开销。我们通过测试确保工具描述不会重复这些内容。
MCP Apps
MCP App 是一种工具,其结果可由宿主渲染为交互式视图,而非文本。工具携带 _meta.ui.resourceUri,用于指定一个 ui:// 资源;该资源是以 text/html;profile=mcp-app 提供的自包含 HTML 文档,宿主会在沙箱 iframe 中加载它,并通过 postMessage 与其通信。没有此扩展的宿主会忽略这些元数据,并获取普通的结构化结果,因此应用不会给普通客户端带来任何负担。
| App | Tool | Resource | 显示内容 |
|---|---|---|---|
| Inventory | inventory | ui://truenas/inventory | 池及其容量、数据集、应用、VM、容器、共享和警报;筛选、刷新 |
每个应用都以内联方式提供:不使用外部脚本、样式表、图像或连接。对于未声明 CSP 的应用,宿主默认采用的策略会禁止所有这些内容;而声明域名则意味着放弃沙箱,并依赖 CDN 从宿主运行的任何位置都能访问。测试会拒绝引用这些内容的应用。
inventory 会分别获取每个部分,并分别报告结果。即使 API 密钥可以读取池但不能读取应用,仍会返回池数据,并在 errors.apps 下指出拒绝原因,而不是在密钥无法查看的第一个部分处导致整个调用失败。只有在完全无法读取任何部分时,调用才会失败。
注册表位于 internal/apps;添加应用只需在其中添加一个条目,以及该条目指定的工具。Go 测试会检查每个条目是否符合扩展约定,而 make test-apps 会在真实浏览器中使用模拟宿主运行这些视图。
MCP Tasks
每个有变更的工具都会启动一个中间件作业,并返回其 ID。Tasks 扩展(io.modelcontextprotocol/tasks,自协议 2026-07-28 起为稳定版)正是协议为此定义的形式,因此作业会作为任务公开,而不是通过第二套生命周期来管理。
在每个请求随附的能力声明中声明此扩展的客户端,会收到 CreateTaskResult 和任务 ID,取代写入工具通常返回的结果。客户端通过 tasks/get 轮询,该接口会报告作业状态和进度,并在作业结束后返回工具同步执行时本会生成的结果:completed 携带作业的返回值,failed 携带错误,而已中止的作业状态为 cancelled。tasks/cancel 会请求目标中止作业。未声明此扩展的客户端会收到一贯的作业已启动结果,而 jobs 无论如何都会跟踪作业。
任务 ID 由每个服务分别生成,且每个凭据都会构建一个服务,因此任务只能通过启动它的凭据访问。映射保存在内存中:重启后,任务 ID 将无法识别,tasks/get 会说明这一点。只有启用写入层时才会公布此扩展,因为只读服务不会启动作业。
发布
发布流程完全通过 release-please 执行,不会在其他地方进行。它会读取 main 上的常规提交历史记录,保持一个包含下一版本和变更日志的发布 PR 处于打开状态,而发布则通过合并该 PR 来完成。
push to main ──▶ ci.yml test, lint, publish :main and :sha-<commit>
└─▶ release.yml maintain the release PR
│
merge PR ─────────┴─▶ tag vX.Y.Z, GitHub release,
publish :X.Y.Z :X.Y :latest
ci.yml 特意不对标签作出响应,因此不会出现没有对应发布的版本标签。发布作业会在发布前针对带标签的提交重新运行测试——标签指向的提交与 CI 上次检查的提交不同,而发布是否可信,取决于把关的测试是否通过。
每次发布还会附带适用于 Linux、macOS 和 Windows 的二进制文件,以及一个 checksums.txt,并同时提供容器映像。
令牌。 设置一个 RELEASE_PLEASE_TOKEN 仓库机密,其值为具有 contents: write 和 pull-requests: write 权限的 PAT。如果未设置,工作流会退回使用 GITHUB_TOKEN。它可以正常工作,但无法触发下游工作流,因此发布标签不会启动发布作业。
开发
make test # unit tests
make test-apps # browser tests for the MCP Apps; needs Node 22
make lint # go vet + golangci-lint
make build
make image
集成测试需要连接到正在运行的 TrueNAS,因此不会包含在 make test 中:
TRUENAS_TEST_URL=wss://nas.local/api/current \
TRUENAS_TEST_API_KEY=... \
TRUENAS_TEST_INSECURE=true \
go test -tags=integration ./...
本 MCP 服务应遵循的行为记录在能力规范中,而不是从代码中推断;其中的每个场景都是一个待编写的测试用例。
许可证
MIT。请参阅 LICENSE。
这里的中间件客户端是自行编写的,并非改编自 truenas/truenas-mcp;后者采用 GPL-3.0 许可证,这正是本项目能够自由选择许可证的原因。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。