关于 Tailscale MCP Server
Tailscale MCP Server 将 Tailscale v2 管理 API 提供给 AI 助手,用于交互式管理 tailnet。它面向希望通过结构化工具跨设备、ACL、DNS、密钥、用户等领域组合读取和变更操作的管理员。
主要功能
- 01覆盖广泛的 Tailscale v2 管理 API
- 02提供类型化工具和结构化响应
- 03可配置工具组和只读模式
- 04通过写入提示控制变更确认
- 05可选本地 CLI 诊断和 API 资源
适用场景
- 01按最近活动时间或密钥过期设置查找设备
- 02审阅并总结 DNS 相关的审计变更
- 03起草并验证 ACL 变更
- 04轮换已使用较久的身份验证密钥
MCP 相关
该服务向 MCP 客户端提供 97 个管理 API 工具、6 个可选本地 CLI 诊断工具、一个目录工具和 4 项资源。工具组和只读设置用于控制可用能力。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
98收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| toolName | string | 否 | 要查询的特定工具,例如 'tailscale_delete_device'。省略则列出所有组。 |
详细介绍
@yawlabs/tailscale-mcp
点击一次即可将其添加到本地 Yaw MCP 配置,让你在每个 Yaw Terminal 会话中都能使用。也可以按照下文手动安装。
向 Agent 询问有关 tailnet 的问题,并让它根据答案采取行动。 97 个管理 API 工具 + 6 个可选的本地 CLI 诊断工具 + 1 个始终启用的目录工具 + 4 个资源,覆盖 Tailscale v2 API 的设备、ACL、DNS、密钥和信任凭据、用户、邀请、Webhook、日志流、Posture、服务和组织 tailnet。由 1900 多个单元测试和一套选择加入的实时 tailnet 集成测试提供支持。
由 Yaw Labs 构建并维护。
如果 API 已经存在,为什么还需要这个工具?
你可以使用 curl 调用 Tailscale API。它的目的不是取代 curl,而是让 Agent 无需编写脚本,就能在一次交互中组合多个端点,完成工作流:
- “哪些设备已 30 天未签到,且禁用了密钥过期?” — 列出设备,按
lastSeen筛选(在线设备没有该字段),再按keyExpiryDisabled筛选,并返回表格。三个端点,一个问题。 - “凌晨 2 点有人弄坏了 DNS——过去 24 小时内是谁改了什么?” — 拉取审计日志,筛选与 DNS 相关的操作人和端点,读取每项更改的前后内容,并用英文总结。
- “起草一项 ACL 更改,让
tag:mobile可以访问tag:dashboard,但不能访问tag:db,同时保留我的注释。” — 读取当前 HuJSON,提出最小化差异,根据 API 验证,并返回差异供你应用。 - “轮换所有超过 90 天的身份验证密钥,并打印新密钥。” — 遍历密钥,创建带有匹配标签的新密钥,并撤销旧密钥。
- “为我们的 CI 流水线创建一个 OAuth 客户端,权限限定为
devices:core:read和dns:read。” — 通过tailscale_create_key创建信任凭据,设置keyType=client,并仅返回一次凭据(请立即保存)。
curl 可以完成每一步。Agent 则能将它们组合起来。这正是它的价值所在,也是这些工具的设计目标——每个读取端点都是一等功能,方便 Agent 综合信息;每个写入端点都标记了 destructiveHint 或 idempotentHint,因此 MCP 客户端可以按照你的配置限制变更操作。
如果你只需要在 CI 作业中调用一个端点,请使用 curl——针对常见的从 Git 部署 ACL 场景,我们还提供了一个 CLI 子命令。MCP 适用于交互式、探索性的“我还不知道自己需要什么”工作。
为什么选择 MCP,而不是技能或 tailscale CLI?
问得好。两者各有用武之地。MCP 更胜一筹的地方:
- 广泛覆盖管理 API。
tailscaleCLI 的作用范围仅限于运行它的节点。ACL、用户、邀请、webhook、日志流、态势集成、身份验证密钥、OAuth 客户端和联合身份等管理功能,都位于 v2 HTTP API 中。你最终还是得调用curl。 - 类型化的工具接口,而非字符串解析。 每个工具都有经过 Zod 验证的输入架构和结构化响应。无需依赖脆弱的
tailscale status --json | jq管道,也不用担心它们会在架构演进时失效。 - 可供不同客户端使用,更新通过版本升级提供。 MCP 服务可用于 Claude Code、Claude Desktop、Cursor、Windsurf、VS Code 以及任何支持 MCP 的产品;符合 Agent Skills 标准的技能也能在不同 Agent 间使用,但它本质上是需要你维护的文本。更新通过
npx发布——Tailscale 新增端点时,无需任何人重写技能说明。 - 默认安全的写入操作。 每个工具都声明了
readOnlyHint/destructiveHint/idempotentHint,客户端因此可以跳过读取操作的确认,并要求确认变更操作。通过技能调用 CLI 则无法表达这些信息。 - 真正的测试。 1900 多个单元测试覆盖每个工具的输入验证、API 结构和错误处理。此外,还有一个可选的实时 tailnet 集成测试套件(
RUN_INTEGRATION_TESTS=1+ tailnet API 密钥),用于检测结构漂移。大多数技能只是没有专属测试层的简短 Markdown 提示词——如果供应商更改输出格式,没有任何机制会替你发现。
如果你已经有技能覆盖了 10% 的 Tailscale 工作流,那很好——继续使用。MCP 面向的是另外 90%。
Tailscale 自己的 MCP 端点和技能呢? 截至 2026-09-19,它们解决的是不同的问题,这里没有重复实现任何功能。Tailscale 的官方 MCP 工具是 Aperture 中两个 Alpha 版内置连接器:Tailnet 的 Tailnet_provision_node 会返回一次性身份验证密钥,让 Agent 在人员批准后加入一个新节点;Tailscale SSH 的 TailnetSSH_list_machines 和 TailnetSSH_run_command 会发现已启用 SSH 的机器,并在其中一台上运行一条命令。它们需要 Aperture。 tailscale/tailscale-skill 是一个 Alpha 版、仅提供知识的技能——它是参考资料,教 Agent 如何通过 curl 调用 v2 API,本身不提供服务。两者都不将管理 API 暴露为类型化工具,因此与本服务几乎没有重叠。Aperture 目前也无法代理本服务:本服务使用 stdio 通信,而 Aperture 只代理可通过 URL 访问的 Streamable-HTTP 或 SSE 服务。这两款产品都处于 Alpha 阶段,因此请将本段落中的日期视为其有效期的截止日期。
可信度依据
Reddit 上有人提出了公允的批评:一个声称“积极维护”却没有可见测试的新仓库,可信度确实为零。以下是实际可验证的内容:
- 1900 多个测试(
node --test),覆盖每个工具的输入验证、API 结构和错误处理。运行npm test,即可在本地查看测试通过。 - 本地发布流程,通过
release.sh完成:从工作站执行 lint、测试、版本升级、打标签、推送、npm 发布和 MCP Registry 发布。无需维护 CI 工作流。 - Dependabot 警报会显示在此仓库中,并会得到修复,而不是被忽略。
- 每个工具都对应 OpenAPI 规范中的真实端点。 如果某个工具出现在工具列表中,它调用的就是当前 v2 API 中有文档记载的路径——没有占位用的 404 工具。需要明确其局限:此仓库没有附带任何已记录的实时交互(
fixtures/live/有意保持为空),这里的任何说法也都不是基于对实时 tailnet 的观测。
问题和 PR 都会进行分流处理。如果发现问题,请提交一个——github.com/YawLabs/tailscale-mcp/issues。
快速开始
1. 设置 API 密钥
在 Tailscale Admin Console > Settings > Keys 获取 API 密钥,并将其设置为 MCP 客户端可读取的值。第 2 步中的 .mcp.json env 区块在所有平台上的用法都相同,也是首选方式;如果想改为在 shell 配置文件中导出(~/.bashrc、~/.zshrc、~/.config/fish/config.fish):
macOS / Linux / WSL(bash、zsh):
export TAILSCALE_API_KEY="tskey-api-..."
fish:
set -Ux TAILSCALE_API_KEY tskey-api-...
Windows(PowerShell 5.1 和 7)——[Environment]::SetEnvironmentVariable 会将设置持久保存到当前用户;而 $env: 仅在当前会话期间有效:
[Environment]::SetEnvironmentVariable('TAILSCALE_API_KEY', 'tskey-api-...', 'User')
2. 在项目根目录创建 .mcp.json
macOS / Linux / WSL:
{
"mcpServers": {
"tailscale": {
"command": "npx",
"args": ["-y", "@yawlabs/tailscale-mcp@latest"]
}
}
}
Windows:
{
"mcpServers": {
"tailscale": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@yawlabs/tailscale-mcp@latest"]
}
}
}
为什么 Windows 上还要多做一步? 在 Windows 上,
npx是一个.cmd文件,而 Node 20+ 会拒绝直接启动.cmd文件。用cmd /c包装是标准的解决方法。
3. 重启并批准
重启 Claude Code(或你的 MCP 客户端),并在出现提示时批准 Tailscale MCP 服务。
就是这么简单。现在可以向你的 Agent 提问:
“列出过去 7 天内未被检测到的 Tailscale 设备”
“总结审计日志中昨天发生的所有 ACL 变更”
“起草一条 ACL 规则,仅允许
tag:ci通过端口 5000 访问tag:registry”
工具太多?缩减工具集。
97 个工具确实不少。如果你已经配置了十几个 MCP 服务,并且客户端开始变得臃肿,可以缩减此服务公开的工具。可组合使用以下三个选项:
下面的
env区块仅显示正在讨论的变量。凭据来自环境,按快速开始中的说明设置即可——请将凭据保存在 shell 配置文件中,而不要放进客户端的 JSON 配置中;大多数系统上的该配置都可被所有用户读取,也很容易意外提交到版本库。
选项 1:TAILSCALE_PROFILE(预设,最简单)
{
"env": {
"TAILSCALE_PROFILE": "core"
}
}
minimal(20 个工具)——status、devices、audit。查看 tailnet 状态并读取审计日志。core(52 个工具)——增加acl、dns、keys、users。日常管理所需的工具集。full(97 个工具,默认)——全部工具。与省略环境变量相同。
配置名称不区分大小写,并会忽略前后空格(Core 会选择 core)。TAILSCALE_TOOLS 和 TAILSCALE_WRITE_GROUPS 中的组名区分大小写。
选项 2:TAILSCALE_TOOLS(显式指定组列表)
{
"env": {
"TAILSCALE_TOOLS": "devices,acl,dns,audit"
}
}
以逗号分隔的组名。如果两者都已设置,该选项会覆盖 TAILSCALE_PROFILE——当预设不完全符合需求时使用此选项。
有效组名:status、devices、acl、dns、keys、users、tailnet、org-tailnets、webhooks、posture、audit、invites、services、log-streaming。也可使用 local-cli 组,但仅当设置了 TAILSCALE_LOCAL_CLI=1 时可用——请参阅本地 CLI 集成(选择启用)。
选项 3:TAILSCALE_READONLY(移除变更操作)
{
"env": {
"TAILSCALE_PROFILE": "core",
"TAILSCALE_READONLY": "1"
}
}
设置为 1 或 true,即可移除所有未带有 readOnlyHint: true 的工具。此选项可与 TAILSCALE_PROFILE 或 TAILSCALE_TOOLS 叠加使用,效果为取交集——组合使用可最大限度精简工具集。
确认已加载的配置
服务启动时会将当前生效的筛选条件记录到 stderr:
@yawlabs/tailscale-mcp v0.22.0 ready (20 tools, profile=minimal, readonly)
如果同时设置了 TAILSCALE_PROFILE 和 TAILSCALE_TOOLS,则以 TAILSCALE_TOOLS 为准。横幅会标记配置文件已被覆盖,让优先级一目了然——无需猜测实际应用了哪个筛选条件:
@yawlabs/tailscale-mcp v0.22.0 ready (22 tools, profile=core (overridden by TAILSCALE_TOOLS), groups=devices,acl)
仅当使用实质性配置文件(minimal / core)时,才会显示“(overridden)”标记;profile=full 是无操作的预设,因此若同时设置了 TAILSCALE_TOOLS,则不会显示该标记。
如果未设置任何筛选条件,启动时会打印一条提示,指引你使用预设配置。
Agent 如何感知
以上内容都输出到 stderr——也就是 MCP 客户端的日志。模型看不到这些内容,因此被隐藏的工具和从未实现的工具,在 Agent 看来完全一样。这就是 Agent 最终会绕过限制,而不是报告限制的原因。
tailscale_tool_groups 可以弥补这一点。无论筛选条件如何,它始终都会注册,并以内联方式回答这个问题:
> "Why can't you delete that device?"
tailscale_tool_groups({ toolName: "tailscale_delete_device" })
{
"tool": "tailscale_delete_device",
"available": false,
"group": "devices",
"kind": "write",
"reason": "TAILSCALE_WRITE_GROUPS is set to \"dns\", which does not grant writes here",
"toEnable": "add \"devices\" to TAILSCALE_WRITE_GROUPS (e.g. \"dns,devices\")"
}
它能区分 Agent 原本无法区分的三种情况:
| 情况 | Agent 应采取的操作 |
|---|---|
| 在任何配置下都不存在此工具 | 寻找其他方法——没有任何设置能启用它 |
| 工具存在,但所属组未加载 | 将 toEnable 报告给你;不要绕过限制 |
| 工具存在且已加载,但其中的写入操作被隐藏 | 同上——应由你修复,而不是绕过限制 |
不带参数调用时,它会列出每个组及其可用性;对于任何被隐藏的组,还会列出恢复这些操作所需的确切环境变量更改。它不读取网络,也不需要凭据,因此即使服务配置错误,也能正常工作。
将写入权限限定到特定范围
TAILSCALE_WRITE_GROUPS 用于指定 Agent 可以写入的范围。其他所有内容仍可读取:
{
"env": {
"TAILSCALE_WRITE_GROUPS": "devices,keys"
}
}
这样会提供全部 41 个读取工具,以及 devices 和 keys 组中的 18 个写入工具,并隐藏其他 38 个写入工具——ACL、DNS、用户、tailnet、webhooks、posture、services、invites、org-tailnets 和 log-streaming 的写入工具都不会注册。未设置时不限制写入,这是默认配置。
请先阅读:此设置筛选的是工具列表,而不是 API 令牌。 无论配置如何,服务仍持有一个对整个 tailnet 拥有完全权限的凭据。拥有 shell 的 Agent 可以使用同一个令牌执行
curl api.tailscale.com,完成此设置所隐藏的所有操作。请将 Tailscale OAuth 客户端本身限制为你实际需要的范围(各工具组的范围)——这种限制在此进程之外仍然有效,而此设置则不然。TAILSCALE_WRITE_GROUPS是一种低门槛的凭据范围限制补充手段,而非替代方案。
授权实际包含什么
组名与 TAILSCALE_TOOLS 使用的相同。各组的写入工具如下:
| 组 | 写入工具 | 组 | 写入工具 |
|---|---|---|---|
devices | 13 | webhooks | 5 |
invites | 7 | posture | 3 |
dns | 6 | services | 3 |
keys | 5 | tailnet | 3 |
users | 5 | log-streaming | 3 |
org-tailnets | 2 | acl | 1 |
status、audit 和选择启用的 local-cli 组都不包含任何写入工具,因此授予这些组不会产生任何作用。
devices 是范围最广、也最可能被设置的授权。 在 compose 文件中,write=devices 看起来像是“设备管理”,但它会同时授权 delete_device、set_devices_authorized、expire_device、deauthorize_device、set_device_routes、set_device_tags 和 update_device_key,以及 rename_device。没有更细粒度的设置:严格来说,devices 组中“安全的子集”只有 rename_device;而一个有用值仅限于单个工具的设置,也就失去了设置的意义。
三种授权等同于 tailnet 管理员权限
keys、users 和 acl 不会被阻止——CI 的密钥轮换确实需要 keys——但授予这些权限时请知悉:
keys—tailscale_create_key会创建一个 OAuth 客户端,其范围由调用方指定,包括policy_file和all。该凭据的有效期会超过 Agent 的会话,并且不受此处任何设置限制。users—tailscale_update_user_role接受owner。acl—tailscale_update_acl会重写 tailnet 中所有主体的策略。
如果你的授权包含其中任何一项,服务会在启动时打印此提示。
它限制什么,以及不限制什么
它限制 Agent 可以在哪些区域写入,但不限制已授权区域内操作的严重程度:在已授权的组内,写入会在无人监督的情况下执行,包括那些会改变授权方向的操作。当有人在键盘前操作时,可以将它与 TAILSCALE_REQUIRE_APPROVAL=1 配合使用;但请注意,对于无人值守的 Agent,这种配合毫无作用,因为永不弹出提示的客户端会拒绝这些调用,而不是弹出提示。
优先级,以及配置错误时会发生什么
| 情况 | 结果 |
|---|---|
| 未设置、为空、仅含空白字符或仅含逗号 | 不启用写入限制。-e VAR 未指定值时,不得默默撤销所有写入权限。 |
同时设置了 TAILSCALE_READONLY=1 | 只读设置优先;横幅显示 readonly (TAILSCALE_WRITE_GROUPS ignored)。 |
名称拼写错误(devises) | 不授予任何权限,并指出拼写错误。与 TAILSCALE_TOOLS 不同,这里没有回退机制——如果拼写错误的写入授权触发回退,就会在你正要限制写入时交出全部 56 项写入权限。 |
部分拼写错误(devices,dnss) | 授予有效部分的权限,并对其余部分发出警告。 |
已授权的组未由 TAILSCALE_TOOLS / TAILSCALE_PROFILE 加载 | 该授权不起作用;系统会另外发出警告,因为这不是拼写错误。 |
none、all、off、* | 这些不是保留字,而是未知组名,因此不会授予任何权限。若要禁止写入,请使用 TAILSCALE_READONLY=1;若要允许所有写入,请将此项留空。服务会提示正确的写法。 |
名称区分大小写,与 TAILSCALE_TOOLS 一致。
升级承诺:升级此软件包绝不会扩大 Agent 可以写入的区域范围。新组不会自动获得任何人的授权,必须由人手动输入组名。不过,升级可能会在你已授权的区域内添加工具;固定测试会确保这一变更在差异中经过审核,而非悄然发生。
启动横幅会显示生效的配置:
@yawlabs/tailscale-mcp v0.22.0 ready (59 tools, write=devices,keys)
要求不可逆工具的批准
readOnlyHint / destructiveHint 只是建议性提示——MCP 规范规定客户端必须将注释视为不可信信息,而且大多数客户端不会据此限制操作。TAILSCALE_REQUIRE_APPROVAL=1 会发出更强的信号,受支持的客户端会强制执行:
{
"env": {
"TAILSCALE_REQUIRE_APPROVAL": "1"
}
}
随后,九个工具会附带 _meta["anthropic/requiresUserInteraction"],即使允许规则本会自动批准调用,也会强制显示确认提示:
| 工具 | 列入原因 |
|---|---|
tailscale_update_acl | 可能会让 tailnet 中的所有设备都无法访问;除非你已保存之前的 HuJSON(包括注释),否则它将不复存在 |
tailscale_delete_device | 该设备必须重新注册 |
tailscale_delete_user | 无法撤销删除 |
tailscale_delete_tailnet | 会销毁整个 tailnet |
tailscale_delete_key | 不会再次返回该密钥 |
tailscale_delete_oauth_app | 同上 |
tailscale_delete_webhook | 同上 |
tailscale_delete_log_stream_config | 同上 |
tailscale_delete_posture_integration | 同上 |
判断标准是**“此服务无法利用你仍持有的信息撤销操作”**,其范围比标注了 destructiveHint: true 的 23 个工具更窄。tailscale_suspend_user 被刻意排除在外,因为可以用 tailscale_restore_user 撤销该操作。tailscale_deauthorize_device 也同样如此(可用 tailscale_authorize_device 撤销),以及那些提供 get_* 对应工具、可在写入前读取的全量替换设置工具。
请务必自行选择启用,并在启用前阅读以下内容。 在从不显示提示的客户端模式下(例如无人值守的 Agent 或 CI 运行),此标志会拒绝这些调用,而不是执行它们。有人在键盘前时,这才是正确的默认行为;无人值守时则不合适,因此除非你主动设置,否则该功能保持关闭。
需要客户端支持此注释;Claude Code 从 v2.1.199 开始支持。无法识别该注释的客户端会忽略它,因此设置此变量不会比保持关闭更糟。
此外,以下工具始终会声明 _meta["anthropic/maxResultSizeChars"]:它们的响应大小取决于 tailnet 的规模,而非请求本身——tailscale_list_devices、tailscale_list_users、tailscale_get_acl、tailscale_diff_acl_access、tailscale_get_audit_log、tailscale_get_network_flow_logs、tailscale_local_status。这样一来,较大但合理的结果会保留在内联内容中,而不会被截断成文件引用,导致 Agent 必须在任务进行中再读回文件。最后一个工具需要选择启用:只有在 TAILSCALE_LOCAL_CLI=1 注册该工具时,它才会声明上限;否则该工具完全不存在。
与 mcp.hosting / mcph 配合使用
如果你通过 mcp.hosting 运行此服务(使用 @yawlabs/mcph 本地 Agent),这两层过滤可以无缝配合:
- 服务端——
TAILSCALE_PROFILE/TAILSCALE_TOOLS/TAILSCALE_READONLY会在 mcph 获取工具之前缩减工具范围。未加载的工具根本不会注册。 - 客户端——mcph 的
mcp_connect_activate({ tools: [...] })会进一步筛选tools/list中显示的工具。未列入该列表的工具仍可通过mcp_connect_dispatch访问,因此不会失去功能。
推荐 mcph 用户采用以下模式:在 mcp.hosting 服务配置中设置 TAILSCALE_PROFILE=core(或更精简的配置),然后让 mcph 在此基础上处理各会话的工具激活。这样,服务默认保持精简,而 mcp_connect_dispatch 则可满足临时需求所需的长尾工具。
身份验证
API 密钥(最简单): 在 shell 或 MCP 配置中设置 TAILSCALE_API_KEY。
OAuth(限定范围的访问): 如需精细权限,请改为设置 TAILSCALE_OAUTH_CLIENT_ID 和 TAILSCALE_OAUTH_CLIENT_SECRET。在 Tailscale Admin Console > Settings > Trust credentials 创建 OAuth 客户端,并授予你加载的工具组所需的下列范围。
服务会先检查 API 密钥,然后再回退到 OAuth。如果两者都未设置,工具会返回清晰的错误,告知你需要配置什么;服务仍会启动,因此 MCP 客户端不会陷入反复重启。
Tailnet: 默认自动使用凭据所属的 tailnet(-),这适用于大多数设置。如需明确指定,请将 TAILSCALE_TAILNET 设置为管理控制台中 Settings > General 下显示的 Tailnet ID,格式类似 T1234CNTRL。在 2025 年 10 月之前创建的 Tailnet 仍可使用其旧版组织名称;较新的 Tailnet 没有可用的此类名称。根据 OpenAPI 规范,无论哪种情况,Tailnet ID 都是首选标识符。
TAILSCALE_OAUTH_TAILNET — 指定目标为 仅供 API 使用的 tailnet(通过 tailscale_create_org_tailnet 创建)。使用普通的客户端凭据交换无法访问这些 tailnet:你需要使用属于创建该 tailnet 的 OAuth 客户端(all 范围)进行身份验证,并在令牌请求中指定目标。将此项设置为新 tailnet 的 ID。此设置刻意与 TAILSCALE_TAILNET 分开,以免更改其他用户的默认令牌交换行为。如果设置了此项,请将 TAILSCALE_TAILNET 留空(或设为 -),以便工具请求遵循令牌指定的目标——如果两者指向不同的 tailnet,所有限定 tailnet 的工具都会返回 403,且服务会在启动时发出警告。
按工具组划分的 OAuth 范围
OAuth 客户端为每个 TAILSCALE_TOOLS 组所需的范围。为你加载的组授予“读取”列中的范围,并为你允许其修改内容的组添加“写入”列中的范围;“备注”列列出各组除此之外的需求。all:read(所有读取范围)和 all(所有权限)是最宽泛的授权范围。
| 分组 | 读取 | 写入 | 备注 |
|---|---|---|---|
status | devices:core:read、feature_settings:read | 无 | 读取设备列表和 tailnet 设置;即使其中一项遭到拒绝,仍会返回另一项。 |
devices | devices:core:read、devices:routes:read、devices:posture_attributes:read | devices:core、devices:routes、devices:posture_attributes | 路由工具使用 devices:routes 读写权限对,态势属性工具使用 devices:posture_attributes 读写权限对;其余工具使用 devices:core。持有 devices:core 的凭据必须至少关联一个标签。 |
acl | policy_file:read、devices:core:read、devices:posture_attributes:read | policy_file、devices:posture_attributes | Tailscale 要求除 policy_file:read 和 policy_file 外,还须具备设备权限范围。如果省略 principals,由 tailscale_diff_acl_access 自行列出用户,则还需要 users:read。 |
dns | dns:read | dns | 未经验证。 OpenAPI 规范没有为任何 DNS 操作指定权限范围,而这些权限范围的来源——信任凭据文档——未列出 /dns/configuration。该端点是 tailscale_get_dns_configuration 和 tailscale_set_dns_configuration 所使用的端点。 |
keys | auth_keys:read、oauth_keys:read、federated_keys:read、api_access_tokens:read、oauth_apps:read | auth_keys、oauth_keys、federated_keys、api_access_tokens、oauth_apps | 密钥权限范围按密钥类型划分:auth_keys 用于身份验证密钥,oauth_keys 用于 OAuth 客户端,federated_keys 用于联合身份,api_access_tokens 用于个人 API 访问令牌(仅支持读取和删除)。只授予你要管理的类型;只有 all:read 和 all 能列出 tailnet 中的所有访问令牌。OAuth 应用工具使用 oauth_apps;如果 tailscale_create_oauth_app 发送 allowedNodeAttributes,还需要 devices:posture_attributes。 |
users | users:read | users | |
tailnet | feature_settings:read、account_settings:read | feature_settings、account_settings | 设置按字段划分:网络流日志需要 logs:network:read / logs:network,HTTPS 证书需要 networking_settings:read / networking_settings,两个由外部管理的 ACL 字段需要 policy_file:read / policy_file;其余设置由 feature_settings 管理。联系人工具使用 account_settings。 |
org-tailnets | tailnets:read | tailnets | tailscale_delete_tailnet 需要 all(参见上文的 TAILSCALE_OAUTH_TAILNET)。 |
webhooks | webhooks:read | webhooks | |
posture | feature_settings:read | feature_settings | 同一权限范围也控制大多数 tailnet 设置,因此能够管理态势集成的客户端也能更改这些设置。 |
audit | logs:configuration:read、logs:network:read | 无 | tailscale_get_audit_log 使用第一项,tailscale_get_network_flow_logs 使用第二项。 |
invites | device_invites:read | device_invites(仅删除) | 无法使用 OAuth 客户端的令牌创建、重新发送或接受设备邀请(tailscale_create_device_invite、tailscale_resend_device_invite、tailscale_accept_device_invite);创建、删除和重新发送用户邀请(tailscale_create_user_invite、tailscale_delete_user_invite、tailscale_resend_user_invite)则仅允许使用用户所有的密钥。请为这些操作使用 TAILSCALE_API_KEY。规范未为读取用户邀请指定权限范围。 |
services | services:read | services | tailscale_list_service_hosts、tailscale_get_service_device_approval 和 tailscale_set_service_device_approval 都需要 services 和 devices:core,因此其中两个读取工具无法用于只读客户端。 |
log-streaming | log_streaming:read | log_streaming | 向私有端点进行流式传输还需要 device_invites 和 policy_file。tailscale_create_aws_external_id 和 tailscale_validate_aws_trust_policy 都需要 log_streaming,尽管第二个是读取操作。 |
local-cli | 无 | 无 | 运行本地 tailscale 二进制文件,不调用管理 API。 |
作用域取自截至 2026-09-19 的 Tailscale OpenAPI 规范。该规范未列出的 DNS 行,以及 acl 行中的设备作用域,取自信任凭据文档。所有内容均来自文档,而非在 tailnet 上实测。 |
可靠性与调试
429 和网关错误重试(内置)。 对幂等方法(GET、PUT、DELETE)收到的 HTTP 429、502、503 和 504,最多重试 3 次,并遵循 Retry-After 标头(支持整数秒和 HTTP 日期两种格式)。若没有该标头,则使用带抖动的指数退避,每次等待最长 30s。无需设置环境变量——默认启用。像“轮换所有超过 90 天的密钥”这样的工作流,不会再因 Tailscale 的每租户速率限制而在循环中途失败;网关短暂故障也不会再导致整个工具调用失败:根据 OpenAPI 规范,每项设备和服务操作都注明可能返回 504,错误消息为“请求处理时间过长,请稍后重试”;日志读取操作则注明可能返回 502。HTTP 500 不会重试——这表示服务器未能处理请求,而不是前置服务放弃了请求。无论状态码如何,POST 和 PATCH 都不会重试。如果 DELETE 在网关错误后重试,随后收到 404,错误信息会说明这一点,因为超时的那次尝试可能已经删除了资源。
TAILSCALE_DEBUG=1 — 将每个 HTTP 方法、URL、状态码和耗时记录到 stderr。绝不会记录 Authorization 标头。当工具返回意外错误,而你想查看实际发出的请求时,可使用此选项。示例:
[tailscale-mcp] GET https://api.tailscale.com/api/v2/tailnet/-/devices
[tailscale-mcp] <- 200 (148ms)
TAILSCALE_MAX_CONCURRENT=N — 将正在处理的 API 请求数限制为 N。默认不设上限(不选择启用的用户不会有任何行为变化)。当 Agent 向限制比单次调用重试所能应对的限制更严格的 tailnet 大量并发发起请求时,此选项很有用。
TAILSCALE_REQUEST_BUDGET_MS=N — 每个请求的总实际耗时预算,包括重试及其等待时间。默认值为 90000(90s)。如果预计下一次重试的实际耗时会超出预算,调用会立即返回错误,而不会继续等待。对于网关 5xx,预测时还会计入刚刚失败的那次尝试所花的时间:504 只有在网关等待完毕后才会到达,因此重试很可能会再次耗费同样的时间;若把剩余预算都花在重试上,客户端收到的就会是静默无响应,而不是 504。从网关 5xx 开始继续重试的调用,之后也会受限于此预算的一半——默认 45s,低于 MCP 客户端常见超时时间下限 60s;这是因为这些尝试消耗的是网关等待时间,而非退避等待时间,一连串这样的尝试可能会超过客户端的超时时间,而一连串 429 重试则不会。提高此值也会相应提高该上限;429 重试链无论如何都会使用完整预算。如果 MCP 客户端的外层超时时间更短,可将此值调低。非幂等方法(POST、PATCH)绝不会重试——无论预算如何,都会立即返回。
TAILSCALE_RETRY_BASE_DELAY_MS=N — 重试之间指数退避的基础延迟;第 N 次尝试等待 base * 2^N(最长 30s,另加抖动)。默认值为 1000(1s),因此完整耗尽的重试链大约会等待 1s + 2s + 4s。此选项与 TAILSCALE_REQUEST_BUDGET_MS 配合使用:单独降低预算并不会增加重试次数,只会让默认退避更快耗尽预算并放弃。如果你想“积极重试、快速失败”,应同时调低这两个值。服务器提供的 Retry-After 标头始终优先于此值。
TAILSCALE_EXTRA_WEBHOOK_EVENTS=eventA,eventB — 用于启用 Tailscale 在此软件包最新版本发布后新增的 webhook 事件类型的备用选项。Webhook 工具会根据严格的静态目录验证 subscriptions,因此拼写错误和过期的事件名称会立即报错并给出清晰提示;如果静态目录尚未更新,而你需要使用全新事件,可在此处列出该事件(以逗号分隔),架构便会接受它。两个类别订阅(categoryTailnetManagement、categoryDeviceMisconfigurations)已在目录中,因此无需在此处列出。也请提交 issue,以便更新静态列表。
TAILSCALE_EXTRA_POSTURE_PROVIDERS=providerA,providerB — 用于设备态势集成提供商的同类备用选项。tailscale_create_posture_integration 会根据静态列表(falcon、fleet、huntress、intune、jamfpro、kandji、kolide、sentinelone)验证 provider;如果 Tailscale 新增了提供商,而此软件包尚未更新,可在此处列出它,无需等待新版本发布。此字段过去是封闭枚举,因此新支持的提供商会变得无法创建,而不只是无法验证。
更友好的错误消息。 显示前会展开格式为 {"message":"..."} 或 {"error":"..."} 的 JSON 错误响应正文,因此你看到的是文字说明,而不是原始 JSON。如果正文还包含 data 数组(ACL 端点会用它报告未通过的策略测试),数组内容会显示在消息下方,这样策略被拒绝时会指出哪个用户以及哪项断言未通过,而不只是显示 test(s) failed。401 错误仍会使用完整的多行身份验证错误格式化程序(适用时会附带 Windows 环境变量提示)。
本地 CLI 集成(可选启用)
大多数工具都与 Tailscale v2 管理 API 交互——它们描述的是tailnet。有时你想查询的是这台机器的视角:它是否真的已连接?它当前使用哪个 DERP 区域?从这里到 my-laptop 有多远?这些答案来自本地 tailscale 二进制文件,而不是管理 API。
设置 TAILSCALE_LOCAL_CLI=1(在 shell 中或 .mcp.json 的 env 块中),即可添加 6 个只读诊断工具:
| 工具 | 对应的 CLI 命令 | 用途 |
|---|---|---|
tailscale_local_status | tailscale status --json [--peers=false] [--active] | 查看这台机器的连接状态及其能看到的对等节点;peers: false 和 activeOnly: true 可缩小对等节点映射范围 |
tailscale_ping | tailscale ping <target> | 探测到另一个 tailnet 节点的延迟(直连或经 DERP 中继) |
tailscale_netcheck | tailscale netcheck --format=json | NAT 类型、DERP 延迟映射、IPv4/IPv6 支持情况 |
tailscale_local_version | tailscale version | 查看实际运行的客户端版本 |
tailscale_local_whoami | tailscale whoami | 查看这台机器以哪个用户和设备身份进行身份验证(需要 tailscale >= 1.102.1) |
tailscale_local_service_list | tailscale service list | 查看此节点可见的 Tailscale 服务(需要 tailscale >= 1.102.1) |
要求:必须能找到 tailscale 二进制文件。系统会先在 PATH 中查找,然后检查以下默认安装路径;TAILSCALE_BINARY 可通过你指定的绝对路径覆盖这两种查找方式。
| 平台 | PATH 之外的查找位置 | 备注 |
|---|---|---|
| macOS | /Applications/Tailscale.app/Contents/MacOS/Tailscale、/opt/homebrew/bin/tailscale、/usr/local/bin/tailscale | 标准安装会将 CLI 放在应用包内,且不会将其添加到 PATH。从 Dock 或 Spotlight 启动的 MCP 客户端继承的也是精简版 PATH,而非 shell 的 PATH,因此即使 tailscale 在终端中可以正常运行,直接查找也可能失败。 |
| Linux | /usr/bin/tailscale、/snap/bin/tailscale | snap 包装器不在某些精简版 PATH 中。 |
| Windows | — | 安装程序会将 tailscale.exe 添加到机器的 PATH。如果设置 TAILSCALE_BINARY,请使用 Windows 路径(C:/Program Files/Tailscale/tailscale.exe),而不要使用 Git Bash 路径(/c/...)——在 MSYS 提示符中输入时,这种写法会被转换;但从 JSON 配置或 .env 中读取时不会转换。 |
| WSL | /usr/bin/tailscale、/snap/bin/tailscale | 这些工具报告的是 Linux 节点,因此需要在发行版中安装 Tailscale 并运行 tailscaled。 在全新安装的 WSL 中,唯一可用的 tailscale 是 Windows 版本;Linux 进程无法执行 tailscale.exe。如果将 TAILSCALE_BINARY 指向 /mnt/c/.../tailscale.exe,工具报告的将是 Windows 主机的 Self、对等节点、whoami 身份和 netcheck 结果,而这里的每个工具都会声称报告的是“这台机器”的信息。系统刻意不会自动选用 tailscale.exe。 |
MCP 服务无需 root 权限即可运行这些工具——它们都只用于诊断,不会更改状态。需要提升权限的操作(tailscale up、set --advertise-routes、lock sign)不会开放。
启用此功能后,启动横幅会体现这一状态:@yawlabs/tailscale-mcp v0.22.0 ready (103 tools, local-cli=on)——这 6 个本地 CLI 工具是在默认的 97 个工具基础上额外添加的。
资源(4 个)
MCP 资源会公开只读数据,客户端无需调用工具即可浏览。
| 资源 | URI | 描述 |
|---|---|---|
| Tailnet 状态 | tailscale://tailnet/status | 设备数量和 tailnet 设置 |
| 设备 | tailscale://tailnet/devices | 所有设备的状态和 IP 地址 |
| ACL 策略 | tailscale://tailnet/acl | 完整的 ACL 策略(保留 HuJSON 格式) |
| DNS 配置 | tailscale://tailnet/dns | 名称服务器、搜索路径、分割 DNS、MagicDNS |
工具(97 + 6 个可选启用)
状态(1 个工具)
| 工具 | 描述 | |------|-------------| | `tailscale_status` | 验证 API 连接,查看 tailnet 信息和设备数量 |设备(17 个工具)
| 工具 | 描述 |
|---|---|
tailscale_list_devices | 列出设备(默认字段子集;fields: "all" 会添加路由、连接状态、SSH、发行版和设备状态身份信息)。设备在线时不会显示 lastSeen |
tailscale_get_device | 获取一台设备(使用 fields: "all" 获取完整记录) |
tailscale_authorize_device | 授权待处理设备 |
tailscale_deauthorize_device | 取消设备授权 |
tailscale_set_devices_authorized | 在一次调用中授权或取消授权多台设备(并行处理,按 ID 报告错误) |
tailscale_delete_device | 从 tailnet 中移除设备 |
tailscale_rename_device | 重命名设备(FQDN 或基础名称;空字符串会重置为操作系统主机名) |
tailscale_expire_device | 使设备密钥过期,强制重新认证 |
tailscale_get_device_routes | 获取已通告和已启用的子网路由 |
tailscale_set_device_routes | 启用或禁用子网路由 |
tailscale_get_device_posture_attributes | 获取设备的所有设备状态属性 |
tailscale_set_device_posture_attribute | 设置自定义设备状态属性(可选设置过期时间和审计日志备注) |
tailscale_delete_device_posture_attribute | 删除自定义设备状态属性 |
tailscale_set_device_tags | 为设备设置 ACL 标签 |
tailscale_set_device_ip | 设置设备的 Tailscale IPv4 地址 |
tailscale_update_device_key | 更新设备密钥设置(例如,禁用密钥过期) |
tailscale_batch_update_posture_attributes | 批量更新多台设备的自定义设备状态属性 |
ACL / 策略(5 个工具)— 保留 HuJSON 格式并确保 ETag 安全
| 工具 | 描述 |
|---|---|
tailscale_get_acl | 获取保留格式(HuJSON)并包含 ETag 的 ACL 策略 |
tailscale_update_acl | 更新 ACL 策略(为安全地并发编辑,必须提供 ETag;首次写入时使用 ts-default) |
tailscale_validate_acl | 验证策略,但不应用 |
tailscale_preview_acl | 预览将应用于用户或 IP 的规则 |
tailscale_diff_acl_access | 将拟定策略与当前策略进行比较 — 查看谁会获得或失去访问权限 |
DNS(11 个工具)
| 工具 | 描述 |
|---|---|
tailscale_get_nameservers | 获取 DNS 名称服务器 |
tailscale_set_nameservers | 设置 DNS 名称服务器 |
tailscale_get_search_paths | 获取 DNS 搜索路径 |
tailscale_set_search_paths | 设置 DNS 搜索路径 |
tailscale_get_split_dns | 获取拆分 DNS 配置 |
tailscale_set_split_dns | 设置拆分 DNS 配置(完全替换;null 会清除某个域) |
tailscale_update_split_dns | 更新拆分 DNS 配置(部分合并;null 会移除某个域) |
tailscale_get_dns_preferences | 获取 DNS 偏好设置(MagicDNS) |
tailscale_set_dns_preferences | 设置 DNS 偏好设置(MagicDNS) |
tailscale_get_dns_configuration | 获取统一 DNS 配置(一次调用获取所有设置) |
tailscale_set_dns_configuration | 设置统一 DNS 配置(一次调用设置所有设置) |
密钥 / 信任凭据(9 个工具)— 涵盖身份验证密钥、OAuth 客户端、联合身份和 OAuth 应用
| 工具 | 描述 | |------|-------------| | `tailscale_list_keys` | 列出密钥(默认集合取决于凭据;设置 `all=true` 可列出整个 tailnet 范围内的密钥:身份验证密钥、API 访问令牌、OAuth 客户端、联合身份) | | `tailscale_get_key` | 获取任意类型密钥的详细信息 | | `tailscale_create_key` | 创建身份验证密钥、OAuth 客户端(`keyType=client`)或联合身份(`keyType=federated`) | | `tailscale_delete_key` | 删除任意类型的密钥,包括此服务使用的 API 访问令牌 | | `tailscale_update_key` | 更新密钥的描述、范围、标签或联合声明设置 | | `tailscale_create_oauth_app` | 创建用于第三方设备配置的 OAuth App(Tailscale alpha) | | `tailscale_get_oauth_app` | 获取 OAuth App 的名称、重定向 URI 和范围 | | `tailscale_list_oauth_apps` | 列出 tailnet 中注册的所有 OAuth App | | `tailscale_delete_oauth_app` | 删除 OAuth App,撤销其配置设备的权限 |用户(7 个工具)
| 工具 | 描述 |
|---|---|
tailscale_list_users | 列出 tailnet 中的所有用户 |
tailscale_get_user | 获取特定用户的详细信息 |
tailscale_approve_user | 批准待处理的用户 |
tailscale_suspend_user | 暂停用户,撤销其访问权限 |
tailscale_restore_user | 恢复已暂停的用户 |
tailscale_update_user_role | 更新用户角色(owner、admin、member 等) |
tailscale_delete_user | 删除用户及其所有设备 |
Tailnet 设置(5 个工具)
| 工具 | 描述 |
|---|---|
tailscale_get_tailnet_settings | 获取 tailnet 设置(HTTPS、设备审批、密钥过期等) |
tailscale_update_tailnet_settings | 更新 tailnet 设置(HTTPS 证书、审批、自动更新、密钥过期、设备状态、区域路由、网络流量日志记录、外部 ACL 管理) |
tailscale_get_contacts | 获取 tailnet 联系人 |
tailscale_set_contacts | 设置 tailnet 联系人 |
tailscale_resend_contact_verification | 重新发送联系人的验证邮件 |
Webhooks(7 个工具)
| 工具 | 描述 |
|---|---|
tailscale_list_webhooks | 列出 Webhook |
tailscale_get_webhook | 获取特定 Webhook |
tailscale_create_webhook | 创建 Webhook(原始 JSON,或通过 providerType 设置为适用于 Slack / Mattermost / Google Chat / Discord 的格式) |
tailscale_update_webhook | 更新 Webhook 的端点 URL 和/或订阅 |
tailscale_delete_webhook | 删除 Webhook |
tailscale_rotate_webhook_secret | 轮换 Webhook 密钥 |
tailscale_test_webhook | 发送测试事件以验证 Webhook 是否正常送达 |
设备状态集成(5 个工具)
| 工具 | 描述 |
|---|---|
tailscale_list_posture_integrations | 列出设备状态集成 |
tailscale_get_posture_integration | 获取设备状态集成 |
tailscale_create_posture_integration | 创建设备状态集成 |
tailscale_update_posture_integration | 更新设备状态集成 |
tailscale_delete_posture_integration | 删除设备状态集成 |
Tailscale 服务(7 个工具)
| 工具 | 描述 |
|---|---|
tailscale_list_services | 列出 tailnet 中的所有 Tailscale 服务 |
tailscale_get_service | 获取特定服务的详细信息 |
tailscale_update_service | 更新服务配置 |
tailscale_delete_service | 删除服务 |
tailscale_list_service_hosts | 列出托管服务的设备 |
tailscale_get_service_device_approval | 获取设备对某项服务的审批状态 |
tailscale_set_service_device_approval | 批准或拒绝设备托管服务 |
组织 Tailnet(3 个工具)— 仅限 API 的 Tailnet;需要 OAuth 身份验证
以编程方式创建和删除整个 Tailnet — 适用于每个 Agent 的沙盒、每个租户的隔离环境,以及临时 CI 环境。组织默认可拥有 10 个 Tailnet(包括原有 Tailnet)。与此处的其他所有类别不同,这些端点位于 /organizations 下,仅使用 OAuth 客户端进行身份验证(创建时需要 tailnets 范围,之后访问该 Tailnet 需要 all),并且创建的 Tailnet 不会在管理控制台中管理。设置 TAILSCALE_OAUTH_TAILNET 即可对其中一个 Tailnet 执行操作。
| 工具 | 描述 |
|---|---|
tailscale_list_org_tailnets | 列出组织的 Tailnet(通过 limit / cursor 分页) |
tailscale_create_org_tailnet | 创建仅限 API 的 Tailnet;OAuth 客户端密钥只返回一次 |
tailscale_delete_tailnet | 删除 Tailnet(已配置的 Tailnet,或通过 tailnet 指定的 Tailnet)— 此操作不可撤销;confirmTailnet 必须与之匹配 |
日志流传输(7 个工具)
| 工具 | 描述 |
|---|---|
tailscale_list_log_stream_configs | 列出日志流传输配置(包括审计日志和网络日志) |
tailscale_get_log_stream_config | 获取指定日志类型的日志流传输配置 |
tailscale_set_log_stream_config | 设置日志发送位置(Axiom、Datadog、Splunk 等) |
tailscale_delete_log_stream_config | 删除日志流传输配置 |
tailscale_get_log_stream_status | 检查日志流传输是否成功 |
tailscale_create_aws_external_id | 创建或获取用于 S3 基于角色的日志流传输的 AWS 外部 ID(reusable 默认为 true,在关联之前会返回相同的 ID) |
tailscale_validate_aws_trust_policy | 验证用于 S3 日志流传输的 AWS IAM 角色信任策略 |
设备邀请(6 个工具)
| 工具 | 描述 |
|---|---|
tailscale_list_device_invites | 列出指定设备的设备邀请 |
tailscale_create_device_invite | 创建设备邀请 |
tailscale_get_device_invite | 获取设备邀请 |
tailscale_delete_device_invite | 删除设备邀请 |
tailscale_accept_device_invite | 接受设备共享邀请 |
tailscale_resend_device_invite | 重新发送设备邀请邮件 |
用户邀请(5 个工具)
| 工具 | 描述 |
|---|---|
tailscale_list_user_invites | 列出尚未接受的用户邀请 |
tailscale_create_user_invite | 创建用户邀请 |
tailscale_get_user_invite | 获取用户邀请 |
tailscale_delete_user_invite | 删除用户邀请 |
tailscale_resend_user_invite | 重新发送用户邀请邮件 |
日志记录(2 个工具)
| 工具 | 描述 |
|---|---|
tailscale_get_audit_log | 获取配置审计日志(谁在何时更改了什么);可选的服务端操作者 / 目标 / 事件筛选条件 |
tailscale_get_network_flow_logs | 获取设备之间的网络流量日志 |
本地 CLI(6 个工具,可选启用)— 请参阅 本地 CLI 集成
| 工具 | 描述 | |------|-------------| | `tailscale_local_status` | 本机看到的 tailnet 状态(自身连接状态、对等节点、DERP 区域);可通过 `peers: false` 或 `activeOnly: true` 缩小范围 | | `tailscale_ping` | 从本机探测到另一个 tailnet 节点的延迟 | | `tailscale_netcheck` | NAT 类型、DERP 延迟映射、IPv4/IPv6 支持诊断 | | `tailscale_local_version` | 本地 `tailscale` 二进制文件版本 | | `tailscale_local_whoami` | 本机通过哪个用户和设备进行身份验证(需要 tailscale >= 1.102.1) | | `tailscale_local_service_list` | 此节点可见的 Tailscale Services(需要 tailscale >= 1.102.1) |GitOps:从 CI 部署 ACL
对于简单的“合并后从 git 部署 ACL”工作流,你不需要 MCP 服务或 Agent——使用内置 CLI:
# PR check: validate the proposed policy without touching the tailnet
npx -y @yawlabs/tailscale-mcp@latest validate-acl tailscale/acl.json
# On merge: ETag fetch + validate + deploy with If-Match, fail-closed at every step
npx -y @yawlabs/tailscale-mcp@latest deploy-acl tailscale/acl.json
可在任何 CI 系统中运行。将 TAILSCALE_API_KEY 设置为环境变量;TAILSCALE_TAILNET 为可选项——不设置时会操作该密钥所属的 tailnet,或将其设置为 Tailnet ID 以明确指定。两个命令在任何失败时都会以非零状态码退出;deploy-acl 在没有 ETag 时会拒绝部署(因此并发的 Admin Console 编辑绝不会被悄悄覆盖),并会将 412 报告为并发编辑冲突,你可以通过重新运行来解决。
当验证报告策略测试失败时,CI 日志会列出用户和断言(For user [email protected]: / Errors found:),与上游 gitops-pusher 打印的详细信息相同。验证警告——例如未同步的 SCIM 组——也会导致运行失败,这与 gitops-pusher 和 Tailscale 自己的 Go 客户端一致;警告文本也会一并打印,方便你查看具体问题。
完整的 GitHub Actions 工作流——在 PR 上验证,合并后部署:
name: tailscale-acl
on:
pull_request:
paths: ["tailscale/acl.json"]
push:
branches: [main]
paths: ["tailscale/acl.json"]
jobs:
acl:
runs-on: ubuntu-latest
env:
TAILSCALE_API_KEY: ${{ secrets.TAILSCALE_API_KEY }}
TAILSCALE_TAILNET: T1234CNTRL # Tailnet ID from Settings > General; or omit: defaults to the key's tailnet
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22 }
- name: Validate ACL
if: github.event_name == 'pull_request'
run: npx -y @yawlabs/tailscale-mcp@latest validate-acl tailscale/acl.json
- name: Deploy ACL
if: github.event_name == 'push'
run: npx -y @yawlabs/tailscale-mcp@latest deploy-acl tailscale/acl.json
为确保部署可复现,请将 @latest 替换为固定版本。
如果你打算用
curl手动实现: Tailscale 的 ACL 端点仅在 GET 时返回ETag标头,而不会在 HEAD 时返回。使用curl -I(HEAD)获取 ETag 会悄悄返回空值——空的If-Match要么会导致不受保护地部署(覆盖并发编辑),要么会触发你的保护逻辑并使部署失败。请使用 GET 获取 ETag(curl -fsS -D - -o /dev/null ...),如果返回值为空,则让作业失败。上面的 CLI 会替你处理这些步骤。
可选: 锁定 Admin Console,防止手动编辑导致其与 git 内容不一致。请让你的 Agent:
“将 aclsExternallyManagedOn 设为 true,并将 aclsExternalLink 设为我们的 repo URL”
这会在 Tailscale Admin Console 中显示一个指向你 repo 的只读横幅。使用 MCP 进行读取和调查,并让 CI 负责部署。
要求
- 运行服务需要 Node.js 20.11+(开发需要 22+——测试脚本会将 glob 传递给
node --test,该功能从 Node 21 开始支持) - Tailscale API 密钥或 OAuth 客户端凭据
在 oam.js 上运行(可选)
oam.js 无需修改即可运行此服务,而 tailscale-mcp 命令始终只使用最新的 oam 版本,目前为 0.18.0。已针对已发布的 oam 0.18.0 进行验证:完整的 MCP 握手、全部 97 个管理 API 工具和 tailscale_tool_groups、全部 4 个资源、完全相同的错误响应,以及干净的 stdout 协议流——无论是使用随附的 bundle,还是直接使用 TypeScript 源码且不执行构建步骤,结果均一致。
oam 0.18.0 是最低要求。 这里设定最低版本是有原因的:0.9.0 之前的版本会通过 shell 运行 child_process.execFile 参数,按空白字符重新拆分参数,并执行参数中的 shell 元字符;而此服务的本地 CLI 工具会调用 tailscale 二进制文件,因此这是一个可触发的漏洞,并非理论上的问题。
tailscale-mcp 命令(bin/tailscale-mcp.mjs)选择运行时的方式:
TAILSCALE_MCP_RUNTIME=auto(默认值)——如果客户端已经通过 oam 0.18.0 或更高版本的oam run启动此服务,服务就在该进程中运行。否则,如果OAM_BIN指向的版本为 0.18.0 或更高版本,则使用它;否则会向找到的每个 oam 二进制文件查询版本,并选择不低于最低要求的最新版本——先检查已设置的OAM_INSTALL_DIR,然后在 Windows 上检查%LOCALAPPDATA%\oam\bin和~/.oam/bin,在其他系统上检查~/.oam/bin,最后检查PATH(版本相同时优先使用已安装的副本)。如果找不到符合条件的版本,则使用 Node。如果 oam 宿主版本低于 0.18.0,它绝不会直接运行此服务:而是转交给最新的可用 oam,或PATH中的 Node;如果两者都没有,则报错退出。每当查找 oam 时,stderr 都会说明被跳过的OAM_BIN及原因;只有在找不到可用的 oam 时,才会列出找到但跳过的 oam 二进制文件,并分别说明原因。TAILSCALE_MCP_RUNTIME=oam——与上述方式相同,但如果无法使用 oam,则报错退出,而不是回退到 Node。错误信息会针对实际发现的情况说明解决方法:对于低于最低要求的 oam,提示运行oam self-update;对于无法运行的二进制文件,提示检查该二进制文件;只有完全找不到 oam 时才提示安装 oam(而对于 oam 未发布构建的 linux-arm64,即使如此也不会提示安装)。TAILSCALE_MCP_RUNTIME=node——始终使用 Node:在npx下于当前进程中运行;如果客户端通过oam run启动命令,则转交给PATH中的 Node。
此值不区分大小写;其他任何值都会在 stderr 上发出警告,并按 auto 处理。在 Windows 上,只有 oam.exe 符合条件:绝不会运行 oam.cmd / oam.bat shim,且只有在找不到可用的 oam 时才会在 stderr 上列出它。
沙盒(选择启用)
设置 TAILSCALE_MCP_SANDBOX=1,即可在 oam 的 --permission 模型下运行:网络仅允许访问 api.tailscale.com:443——这是 bundle 会联系的唯一主机,包括 OAuth 令牌交换;所有请求均使用 HTTPS——并禁止文件系统访问。子进程权限仍会保留,因为本地 CLI 工具会调用 tailscale 二进制文件。该子进程会继承服务的环境变量;在沙盒下,环境中仅包含获准的变量。因此,除 TAILSCALE_* 设置外,允许列表还包括 PATH(用于查找二进制文件)以及 Windows 程序启动所需的变量——SYSTEMROOT、WINDIR、SYSTEMDRIVE、TEMP/TMP、USERPROFILE、HOMEDRIVE/HOMEPATH、APPDATA、LOCALAPPDATA——以及 HOME。oam 也会从经过筛选的环境中获取 Windows 添加到每个子进程的变量,因此如果没有这些变量,启动的 CLI 将没有 SYSTEMROOT。oam 会严格区分这些名称的大小写,因此在 Windows 上,启动器还会按环境实际使用的拼写授予每个变量(例如客户端从 Explorer 继承的 Path、SystemRoot、SystemDrive、windir)。
在沙盒下,TAILSCALE_BINARY 必须指向原生的 tailscale CLI,而不能指向基于 Node 的包装脚本:与 Node 一样,oam 会通过 NODE_OPTIONS 将权限标志传递给每个子进程,而 Node 会拒绝其中的 --allow-net 和 --allow-env,并在运行任何内容之前退出。原生 CLI 会忽略此变量。
之所以选择显式启用,而不是默认启用,是因为授权错误不会明确报错。对于未获授权的环境变量,oam 会将其从 process.env 中移除,而不是抛出异常,因此权限不足的 TAILSCALE_API_KEY 读取结果会是“未认证”,而不是“被拒绝”。启动器中的环境变量允许列表是根据已发布的 bundle 实际读取的内容生成的——如果添加了新的 process.env 查询,也要将其加入该列表。
沙盒由 tailscale-mcp 命令应用,该命令会为其启动一个全新的 oam——即使客户端是通过 oam run 启动该命令也是如此——因为 --permission 是进程级标志。如果找不到可用的 oam 来启动,TAILSCALE_MCP_RUNTIME=auto 仍会在不启用沙盒的情况下启动服务;设置 TAILSCALE_MCP_RUNTIME=oam 则会将此情况视为错误。TAILSCALE_MCP_RUNTIME=node 会使用 Node 运行,因此绝不会应用沙盒。
沙盒也会应用于 CLI 子命令,并且由于文件系统访问被拒绝,tailscale-mcp validate-acl <file> / deploy-acl <file> 无法读取策略文件。运行这些命令时,请取消设置 TAILSCALE_MCP_SANDBOX——例如,如果你在 shell 配置文件中为服务导出了该变量,请运行 TAILSCALE_MCP_SANDBOX= tailscale-mcp validate-acl policy.hujson。
{
"mcpServers": {
"tailscale": {
"command": "oam",
"args": ["run", "/path/to/tailscale-mcp/dist/index.js"]
}
}
}
请在自己的硬件上测量启动速度。 MCP 客户端会在每个会话中冷启动一次此服务,因此启动耗时才是真正需要付出的成本。在进行测量的机器上,Node 表现更好——在 10 次预热运行中,Node 耗时 437ms,而 oam run 耗时 1554ms(此前一轮 5 次运行的结果为 326ms 对 427ms;机器当时负载较高,因此具体数值有波动,应关注结果趋势)。这些运行使用的 oam 早于 0.18.0 的最低版本要求,此后也没有重新测量,因此不要将这些数据解读为当前的性能排名。
已发布的 tailscale-mcp 命令会优先选择找到的最新可用 oam(见上文)。未安装 oam 时,几乎没有额外开销:发现过程只检查文件是否存在,不会启动子进程;回退方案会在 npm 已启动的 Node 进程中运行服务。不过,安装了 oam 时,该命令会启动 Node,对找到的每个 oam 二进制文件运行 --version 以选出最新版本,然后才启动 oam,因此它总是比直接将客户端指向运行时更慢——oam 的配置如上所示,Node 则使用 node /path/to/tailscale-mcp/dist/index.js。TAILSCALE_MCP_RUNTIME=node 会完全跳过 oam。
在此代码库中,oam 确实有两个更胜一筹的用途,两者都需要显式启用,且都不会影响 npm 包:
npm run check:oam——通过oam check进行类型检查(使用 tsgo,即 TypeScript 7 原生实现)。实测耗时 4015ms,而tsc --noEmit为 7680ms,检查结果同样无错误。npx tsc --noEmit仍是通用默认方案。npm run build:binary:oam——通过oam compile构建独立二进制文件(实测约 57.7 MB)。文件会写入bin/<platform>-<arch>/(跨平台构建时则写入bin/$TAILSCALE_MCP_BINARY_TARGET/),之后由scripts/stage-release-asset.mjs按公开资源名称暂存该文件。如果你重新分发该二进制文件,它会包含 oam 的运行时,因此请同时附上 oam 的LICENSE、NOTICE和THIRD_PARTY_LICENSES.md。跨平台构建(TAILSCALE_MCP_BINARY_TARGET=<platform>-<arch>)会下载目标平台的 oam 发布二进制文件作为载体,并使用ssh-keygen -Y verify(OpenSSH 8.1+),根据scripts/oam-release-keys/中的 oam 发布密钥验证发布方签名的RELEASE-MANIFEST;若签名、清单或校验和验证失败,则会中止构建。对于早于 oam 开始签署发布版本的 v0.18.0 的OAM_VERSION,只有在其SHA256SUMS文件与scripts/oam-release-keys/presigning-sums中为该标签固定的摘要匹配后,才会进行校验。
源代码有意保持与运行时无关:任何地方都没有 oam: 导入,测试也继续使用 node:test。这样才能确保 Node 回退方案真正可用,而不只是名义上的回退。请注意,任何 oam 调用都会在工作目录的 oam/ 中写入字节码缓存——该目录已包含在 .gitignore 中。
贡献
欢迎贡献。有关 PR 工作流程和 AI Agent 指南,请参阅 CONTRIBUTING.md。除拼写错误修正外,如有其他改动,请先提交议题讨论,再发起 PR。
git clone https://github.com/YawLabs/tailscale-mcp.git
cd tailscale-mcp
npm install
npm run lint # Biome check
npm run lint:fix # Auto-fix
npm run build # tsc + esbuild bundle
npm test # node --test (full suite)
要针对你自己的 tailnet 进行集成测试,请设置 TAILSCALE_API_KEY 并运行 node dist/index.js。
安全
发现漏洞?请参阅 SECURITY.md——请使用 GitHub 的私密漏洞报告功能,而不要提交公开议题。
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。