管理Tracecat技能
Tracecat MCP 提供了六个技能工具:list_skills、get_skill、prepare_skill_download、prepare_skill_upload、complete_skill_upload 和 publish_skill。
写入操作是分阶段执行的,而非内联。文件字节永远不会作为工具参数传递:prepare_skill_upload 只接收元数据——路径、SHA-256、大小、内容类型——并返回每个文件的短期 PUT URL;字节通过普通 HTTP 发送到这些 URL;然后 complete_skill_upload 附加这些暂存的二进制大对象并替换草稿。不要寻找内联的 content_base64 参数,也永远不要将文件字节读取到上下文中发送。
捆绑的辅助工具消除了手动操作中容易出错的部分:安全的目录遍历、拒绝符号链接、强制执行根 SKILL.md 以及 SHA-256 摘要。
上传技能
创建新技能和替换现有技能的四个步骤相同。唯一的区别是 prepare_skill_upload 接收的身份参数不同。
-
使用
list_workspaces解析目标工作区,然后调用list_skills。对于新技能,确认名称未被占用——省略skill_id总是会创建一个新行,因此第二次创建会产生两个名称相同但 slug 不同的技能。对于现有技能,获取其skill_id。 -
构建元数据数组:
python3 <this-skill-dir>/scripts/upload_skill_files.py metadata <local-skill-dir>它会原样打印
files数组:每个文件一个条目,包含path、sha256、size_bytes和content_type。不包含文件内容。 -
调用
prepare_skill_upload,参数为workspace_id和该files数组,外加以下之一:name(以及可选的description),并且不提供skill_id,用于创建技能;或者skill_id,并且不提供name/description,用于替换现有技能的草稿。
对于创建操作,传递与本地前置元数据声明的相同
name——服务器会在验证前将name和description合并到存储的根SKILL.md前置元数据中,因此参数不匹配会静默重写存储的内容。响应包含
skill_id、base_revision、created以及每个文件的upload_id、upload_url、method、headers和expires_at。 -
使用
PUT方法将每个文件的原始字节(直接从磁盘读取)发送到其upload_url,并附带返回的方法和标头。这些 URL 是短期有效的;检查expires_at,如果过期则重新准备,而不是重试过期的 URL。 -
调用
complete_skill_upload,参数为workspace_id、skill_id、步骤3中的base_revision,以及一个包含每个文件{"path": ..., "upload_id": ...}的files数组。这会替换草稿,但不会发布。 -
当技能应可附加到预设时,调用
publish_skill并传入skill_id。未发布的草稿无法被绑定。
base_revision 是一个并发保护。出现 draft_revision_conflict 意味着在步骤3之后有人编辑了草稿——调用 get_skill 查看当前的 draft_revision 以及一次完整树替换会删除的内容,然后重新准备。
更新正在使用的技能
一个仅止于 complete_skill_upload 的修复不会改变任何Agent运行的内容。按顺序执行三个步骤:
prepare_skill_upload(使用skill_id)→PUT字节 →complete_skill_upload。这会替换草稿,但不会发布。- 使用相同的
skill_id调用publish_skill。技能是版本化的;这会创建一个新的不可变版本,并更新技能的current_version_id。 - 使用消费预设的
skills列表调用update_agent_preset——即它已有的相同{"skill_id": ...}条目。每个绑定在写入时都会重新固定到该技能的当前已发布版本,因此在步骤2之前绑定的预设会继续运行旧版本,直到此调用重写它。
跳过步骤3是经典的失败案例:新版本存在,但预设从未看到它。
通过重新获取来验证。不带 path 的 get_skill 返回草稿清单——路径、摘要、大小、draft_revision、is_publishable、validation_errors——并且从不返回文件内容;带 path 的 get_skill 读取一个文件,当它是小的 UTF-8 文本时内联返回。list_skills 显示技能的 current_version_id;get_agent_preset 显示 skills[].skill_version_id 和 skills[].skill_version。确认两者都已更新。
这是常规、可逆的工作。每个已发布的版本都是不可变的且仍可绑定,因此可以通过发布更正后的树并重新绑定来撤销错误的推送。这样做,而不是叙述风险;将谨慎留给那些实际上无法撤销的操作。
声明技能的工具
一个技能自带其工具。在根 SKILL.md 的前置元数据中,在 metadata.tools 下声明它们,作为注册表动作名称或 mcp.<slug> / mcp.<slug>.<tool> 条目,每个技能最多64个:
---
name: slack-triage
description: 在Slack中发布和处理线程分类更新。
metadata:
tools:
- tools.slack.post_message
- tools.slack.list_replies
---
技能所附加到的每个预设,除了其自身的 actions 允许列表外,还会收到这些工具。一旦预设解析完成,这些工具就会被授予,无论模型是否打开该技能;只有技能的指令是按需加载的。
当工具自然地组合在一起并且多个Agent重复使用它们时,优先使用此方法,而不是在每个预设的 actions 上列出相同的工具——一次编辑和重新发布就会更新每个消费预设。当工具特定于某个Agent时,将其保留在该预设的 actions 上。
一个注意事项:预设的 namespaces 过滤器会应用于联合中的注册表工具,并静默阻止其范围之外的注册表工具。在将技能附加到受命名空间限制的预设后,检查 get_agent_preset 上的 tool_policy.blocked_tools。技能声明的 MCP 工具(mcp.<slug> / mcp.<slug>.<tool>)不会被 namespaces 过滤,也不会出现在那里。
更改 metadata.tools 会更改Agent可以执行的内容,因此它遵循与任何其他修复相同的三个步骤:替换草稿、publish_skill,然后 update_agent_preset 以重新固定绑定。读取生成的预设会显示在 tool_policy.actions 中作为单个扁平列表授予的工具,没有按技能的来源——你可以验证的是这个并集。
大小
字节不再通过模型上下文,但每个文件仍然有服务器端的大小限制,并且当树较小且仅包含文本时,更容易审查整个树。
- 优先使用 Markdown、YAML 和脚本。将大型二进制资产托管在其他地方并引用它。
- 将长篇幅的细节移动到 Agent 按需读取的
references/文件中,而不是放在SKILL.md中。 - 如果树太大难以推理,请将技能本身拆分为两个技能。不要在一次
complete_skill_upload调用中拆分一棵树——每次调用都会替换整个草稿,因此部分调用会删除它省略的所有内容。
硬性规则
- 省略
skill_id会创建;传递它会替换。切勿针对已存在的名称进行创建——slug 重试会使重复创建成功而不是失败。 - 每次写入都会替换整个草稿。本地目录中不存在的文件将从技能中删除。始终发送完整的树。
- 在调用
complete_skill_upload之前上传每个准备好的文件,并将每个路径及其upload_id传递回去。 - 永远不要将文件字节或 base64 内联到工具参数中,并且永远不要将上传或下载 URL 的字节读取到上下文中。流式传输到磁盘和从磁盘流式传输。
- 发布不会移动现有的预设绑定。重写预设的
skills列表以获取新版本。 - 拒绝符号链接的文件和目录,并且永远不要发送选定技能根目录之外的内容。辅助工具会强制执行此操作;不要绕过它。
- 根文件必须恰好是
SKILL.md,位于顶层,并且是有效的 UTF-8 编码。服务器会拒绝缺少该文件的文件集。 - 如果客户端无法执行本地脚本,请说明并给用户运行辅助工具的命令。不要用模型生成的摘要来替代。
参考资料
- 阅读 references/upload-contract.md 以了解字面的请求和响应形状,以及此路径保护和不保护的内容。
- 当上传、发布或预设重新绑定未生效时,阅读 references/troubleshooting.md。