OpenViking 可选 MCP 工具实战指南:tree、write/edit 与 watch 生命周期管理
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
导读
OpenViking 通过viking://URI 提供长期语义记忆存储,并以一组 MCP 工具向 AI Agent 开放"召回 + 沉淀"能力。除每套部署都具备的核心工具外,较新版本与特定托管模式下还会额外注册tree、write、edit、list_watches、cancel_watch等可选工具。本文将基于 optional-tools.md 与仓库内 MCP 服务端实现,完整讲解这些工具的可用前提、调用语义、适用场景与安全边界,并结合源码给出底层原理,使读者能安全、正确地使用它们精确落盘文档、感知陌生命名空间结构、管理自动刷新订阅。
一、核心工具与可选工具:先弄清"注册了什么"
SKILL.md 将 openviking-memory 技能的工具划分为两部分:
- 核心工具(所有受支持的部署均提供):召回侧
find/search/read/list/grep/glob,持久化侧remember/add_resource,维护侧forget/health。 - 可选工具:
tree、write、edit、list_watches、cancel_watch。它们是否存在,取决于服务端版本与托管模式。
因此使用可选工具的第一原则是:先检查当前会话实际注册的工具列表,只阅读与真实存在工具对应的章节,绝不调用未注册的工具。SKILL.md 的核心闭环(任务开始find/search/read召回,工作期间与结束后remember/add_resource沉淀)不依赖任何可选工具即可运转——它们只是锦上添花。
可用性总览
根据 optional-tools.md 给出的可用性矩阵:
| 工具 | 服务端要求 | 托管云服务 |
|---|---|---|
tree | ≥ 0.4.14 | 云服务滚动升级到 0.4.14 之后 |
write,edit | ≥ 0.4.14 | 云服务滚动升级到 0.4.14 之后 |
list_watches,cancel_watch | ≥ 0.3.18,仅自托管 / 私有部署 | 不暴露 |
托管云服务采用无状态多实例的承载方式,因此即使底层版本已包含list_watches/cancel_watch这类账号级有状态工具,也会在云侧被裁剪掉。换句话说:工具的取舍并非只看版本号,还要看运行形态(有状态单实例 vs 无状态多实例)。
二、tree(uri, level_limit?):在陌生命名空间里先建立全局方位感
tree用于获取某个viking://作用域下的递归目录树,比list的单一层级更深入。它的定位是"先宏观后微观":进入一个陌生的作用域时,先用tree看清整体布局,再决定具体要read哪些文件。反过来,如果目标是一个已知的单一目录,应优先用更省成本的list。
服务端实现中的完整参数
在 mcp_endpoint.py 中,tree的实际签名为:
| 参数 | 默认值 | 说明 |
|---|---|---|
uri | viking:// | 起始作用域 URI |
level_limit | 3 | 递归深度上限(即文档中的level_limit?可选参数) |
node_limit | 1000 | 返回条目总数上限,超出会截断并附提示 |
include_abstract | false | 设为true时同时输出每个文件的摘要行,便于在陌生目录中快速定位,但更慢 |
服务端底层调用service.fs.tree(...),并区分output="abstract"与output="original"两种输出形态。返回结果会按目录层级做缩进,文件附带字节大小;当include_abstract开启时,每个文件下方还会追加一行摘要。若指定作用域下没有任何内容,工具返回(nothing under {uri}),不会抛出异常;若条目数达到node_limit,则明确提示"已按 node_limit 截断,请收窄 URI 或调大上限"。
与 list / glob / grep 的分工
服务端 docstring 给出了清晰的选型建议:
list:只看单一目录层级(最省);tree:需要某个作用域的完整目录树全貌;glob:按文件名模式查找;grep:按内容正则匹配(精确文本检索)。
对应地,语义检索应使用search。tree与这些工具正交配合,构成"定位-打开-检索"的完整探索链路。
三、write(uri, content, mode?)与edit(uri, ...):精确落盘,补足remember的语义盲区
remember的价值在于让服务端自主提取并归档记忆(偏好、实体、事件、经验),但它的落盘位置与格式由服务端决定。当 Agent 需要在已知 URI 上精确持久化一份文档时,就该用write/edit这对可选工具。
write负责替换、追加或新建文件。参考文档给出的一条约束是:新建文件前需保证父目录已存在。edit在已有文件内部做定向字符串替换。优先用edit而不是整体重写文件;如果本地持有的文件副本可能已过期,务必先重新read再编辑。
适用场景有两类:自己的用户根目录下的精修笔记(viking://~/),以及共享参考资料(viking://resources/)。当这两种定位都无法命中时,回退到 SKILL.md 描述的remember路径。
服务端实现的write语义
write的完整签名为write(uri, content, mode, wait, timeout)(见 mcp_endpoint.py),其中mode取值:
| mode | 行为 |
|---|---|
replace(默认) | 覆盖文件;目标不存在时,服务端实现会回退到create完成"不存在则新建"的兜底 |
create | 严格新建:目标已存在则失败 |
append | 追加到已有文件末尾;文件不存在则失败 |
两个值得注意的实现细节:
- 新建文件扩展名有白名单:无论是
replace新建还是create,新文件必须以.md.txt.json.yaml.yml.toml.py.js.ts之一结尾。 - 可写作用域受限:
viking://resources/、viking://user/{user_id}/、viking://agent/可写;viking://~是调用方用户根目录的别名。用户子树中的skills/、peers/、privacy/、sessions/为只读,禁止写入。
写入完成后,语义搜索索引会在后台刷新。工具返回时会附上semantic=.../vector=.../overview=...的索引状态提示;如果状态为queued,说明后台更新尚未完成——此时如需紧随其后的搜索立刻命中新内容,应传wait=true阻塞等待索引生效。
edit的精确匹配与失败安全
edit的核心约束是old_string必须与文件当前内容逐字精确匹配(含缩进与换行),因此调用前用read获取最新内容几乎是一种强制前置(mcp_endpoint.py)。它的行为规则:
old_string为空 → 直接拒绝;- 文件中找不到
old_string→ 报错且文件保持不变,提示重新read; - 匹配到多处且
replace_all=false→ 报错,要求补充更多上下文使字符串唯一,或显式设replace_all=true; - 传
new_string=""等价于删除匹配片段。
对记忆文件执行edit会保留其元数据(不会像整体重写那样破坏文件级元信息),编辑后同样触发后台索引刷新,可用wait=true等待。
四、list_watches()与cancel_watch(to_uri):管理自动刷新订阅
add_resource在导入远程 URL(http(s)、git、ssh 等)时支持watch_interval参数(单位:分钟),用于创建定时自动刷新订阅:服务端按周期重新抓取并重新嵌入整个资源。list_watches/cancel_watch就是对这类订阅进行查看与取消的两个管理工具,它们仅存在于自托管 / 私有部署(参见前文可用性矩阵与云服务裁剪原因)。
list_watches():查看当前账号的活跃订阅
服务端实现见 mcp_endpoint.py:工具会检查watch_scheduler是否运行,随后按当前账号/用户/角色过滤出可见的任务(无可见性越权问题——不可见的任务会被静默过滤而不是抛权限错误)。每个任务输出一行,包含:
- 目标 URI(
to_uri); - 检查周期(
interval=...m,分钟); - 状态(
active或paused); - 下次执行时间(
next=...)。
若没有任何任务,返回No watch tasks.。
cancel_watch(to_uri):按目标 URI 停止订阅
取消接口以目标 URI 为键(例如viking://resources/volcengine/OpenViking),而非任务 ID(mcp_endpoint.py)。语义要点:
- 目标是幂等的:若 URI 上不存在任务,返回
No watch task found for {to_uri};若查找与删除之间恰好被其他调用方并发取消,也按"用户想要的结果已达成"统一回报成功。 - 越权操作会显式抛出并返回
Permission denied for {to_uri}。
重要安全边界:取消订阅属于破坏性操作。文档与工具注释都强调——只处理用户明确要求处理的 watch,不要擅自取消他人的订阅。
MCP 只暴露"最小闭包"
源码注释(mcp_endpoint.py)明确指出:面向 Agent 的 MCP 接口刻意只暴露list_watches/cancel_watch这一最小闭包,暂停 / 恢复 / 触发 / 更新操作不通过 MCP 暴露——它们要么对 Agent 价值低,要么容易诱发未经授权的自主决策。需要这些能力的用户应改用 REST 控制面(/api/v1/watches)或ov task watchCLI 子命令组(pause、resume、trigger、update --interval等)。三处控制面镜像关系可参考 15-watches.md:REST 支持PATCH /api/v1/watches部分更新watch_interval、is_active等,而is_active与watch_interval相互正交——翻转is_active可保留配置周期地暂停/恢复。
关于watch_interval的取值建议
add_resource的watch_interval默认0(不创建 watch)。服务端建议:除非源变化很快,否则优先取 ≥ 1440(24 小时),因为每次刷新都会对整份资源重新做嵌入处理,成本与周期直接相关。该参数仅对远程 URL 导入生效。
五、把这些工具放进"召回 + 沉淀"闭环的决策规则
综合参考文档与 SKILL.md,可在实践中遵循如下规则:
- 会话开始时先确认注册表:如果会话注册了任意可选工具,使用前阅读 optional-tools.md 的对应章节;只调用真实注册的工具,不要回退到裸 HTTP 调用。
- 精确文档 vs 提取记忆二选一:
- 需要服务端自主抽取(偏好、决定、经验教训)→
remember(messages); - 需要在已知位置保存一份精确、可复现的文档(用户根目录笔记、
viking://resources/下的共享参考材料)→write/edit; - 两个可写定位都不可用 → 回退
remember。
- 需要服务端自主抽取(偏好、决定、经验教训)→
- 编辑前先
read:edit要求old_string精确匹配,过期副本必然导致失败,报错信息也会要求重读。 - 用
tree建立方位感:面对陌生作用域先tree,需要单层明细再用list;include_abstract=true能进一步加速定位。 - watch 只读不擅动:
list_watches帮助确认账号下有哪些自动刷新订阅;cancel_watch只在你确认用户意图后使用,因为取消订阅是破坏性操作。
六、深入阅读
- 技能定义与"召回 + 沉淀"完整闭环:SKILL.md
- 可选工具参考(本文主体):optional-tools.md
- 所有 MCP 工具的服务端实现(含
tree/write/edit/list_watches/cancel_watch/add_resource的完整签名与注释):mcp_endpoint.py - watch 任务的 REST / CLI / MCP 三侧控制面对照: 15-watches.md
【免费下载链接】OpenVikingSelf-evolving Context Database for AI Agents. Unify Agent Memory, Knowledge RAG and Skills.项目地址: https://gitcode.com/GitHub_Trending/op/OpenViking
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考