Web、LSP、MCP 与 Skill
稳定工具名、可替换提供者和渐进式能力披露
结论:四种扩展面,四套不同契约
DeepSeek Harness 没有把所有外部能力压成一个通用插件 API。Web 在可替换 provider 之上保留产品自有的稳定工具名;LSP 在按扩展名路由的语言服务器之上保留一组封闭操作;MCP 动态导入 provider 自有的名称与 schema;Skill 先发布精简路由目录,只在需要时揭示受信任的指令正文。共同哲学不是“万物皆工具”,而是“把稳定、类型化的 seam 放在能够诚实拥有语义的最窄层”。
这种拆分尤其利于审计,因为每个表面回答不同的问题:公开名称由谁拥有、provider 由谁选择、每次模型请求付出多少上下文、非信任内容或高权限内容在哪里进入执行。把四者视为同一种机制,会掩盖它们最重要的安全与缓存属性。
1. 能力拓扑:每个表面把“稳定性”放在不同层
| 表面 | 稳定的模型可见单元 | 可替换/动态单元 | 选择时点 | 默认暴露 |
|---|---|---|---|---|
| Web | web_search / web_fetch | search 与 fetch provider | 执行时按配置 ID 或唯一可用 provider | 仅 Search |
| LSP | 包含四种操作的单一 lsp 工具 | 扩展名到语言服务器的路由 | 文件最后一个扩展名 | 可选示例,不在发布的 base 中 |
| MCP | 每个发现工具一个 mcp__server__raw | 完整远端工具代际 | 服务器发现与重新同步 | 默认关闭的示例 |
| Skill | 单一 skill loader 加摘要目录 | 胜出的 provider candidate 与加载正文 | 先 scope layer,再在 layer 内按 rank | 随编码 preset 发布 |
架构把稳定性花在收益最大的地方。Web 与 LSP 能定义可移植的产品词汇,因此 schema 固定;MCP 的存在目的正是接受外部服务器词汇,因此稳定的是身份和代际替换;Skill 则稳定发现元数据,把昂贵且有权威性的正文延后。
2. Web:Provider 选择确定且每次调用重新判断
web_search(query)
→ ctx.web.search(request)
├─ 已配置 ID 存在且可用 → 使用
├─ 已配置 ID 缺失/不可用 → 类型化失败
├─ 未配置 + 恰好一个可用 → 自动选择
├─ 未配置 + 多个可用 → 歧义失败
└─ 没有可用 provider → 不可用失败
→ 将 source 截断到 maxResults
Search 与 fetch 使用不同 registry,重复 ID 会失败,provider 可用性在执行时检查。环境覆盖写入同两个配置字段,而不是另建隐藏优先级链。未配置时若有多个 provider 可用,执行会失败并要求显式选择。CAP-WEB-SELECTION
结果词汇有意保持很小:search 返回可选回答文本与可引用来源;fetch 返回最终 URL、状态码、封闭的 HTML/text 正文联合以及截断标志。非 2xx 是资源状态而不是传输异常。CAP-WEB-TYPES
3. 稳定工具可见性与 Provider 健康状态解耦
dsh-tool-web 拥有 schema、提示指引、结果上限和呈现;它的 search/fetch 开关决定工具是否注册,具体 provider 不参与。因此,即便配置的 provider 暂时缺失或不可用,已启用工具仍留在模型目录中,只在真正调用时以结构化元数据失败。CAP-WEB-TOOL
这在凭证故障与 provider HMR 期间保护工具 schema 和 KV 前缀稳定。代价是“模型看得到工具”不再是 readiness 信号;运维必须另有 provider 健康视图,否则第一次用户可见探测就是失败调用。
4. 发布版 Web 仅启用 Search,因为 Fetch 明确缺少 SSRF 防线
Base composition 挂载 ctx.web,把 search 固定到 deepseek-official,挂载 DeepSeek provider,以 60 秒工具预算暴露 web_search,并设置 fetch:false;没有挂载 fetch provider。配置注释明确给出原因:本地 fetcher 延后了 SSRF/私网保护,而目标由模型选择。CAP-WEB-SHIPPED
可选 HTTP fetcher 仍执行有效的传输卫生:仅允许 HTTP(S)、禁止 URL 内嵌凭证、限制 URL 长度、仅跟随同源重定向、限制为可支持的文本类型、按声明 charset 解码、统一超时、字节与字符上限,并且不携带浏览器 cookie 或环境凭证。它明确警告私网阻断尚未实现。CAP-WEB-FETCH-POLICYCAP-WEB-FETCH-TRANSPORTCAP-WEB-FETCH-CAPS
5. DeepSeek Search 是辅助模型调用,不复用主 LLM Adapter
web_search query
→ 解析一份 settings + credential 快照
→ 追加无密钥 web/deepseek-search-llm-request
→ POST Anthropic-compatible /messages
model: deepseek-v4-flash
tool: web_search_20250305
→ 必须存在 web_search_tool_result block
→ 按 URL 连接 cited_text、去重并归一化
Provider 使用自己的原生 fetch 客户端和 Anthropic-compatible 请求,而不是 ctx.llm。每个操作只快照一次 endpoint、密钥来源、model、token 上限和 search-use 上限;dispatch 前记录完整无密钥辅助请求;拒绝重定向;同时发送两种支持的 API key header。CAP-WEB-DEEPSEEK-WIRECAP-WEB-DEEPSEEK-REQUESTCAP-WEB-DEEPSEEK-SETTINGS
响应若没有原生搜索结果 block 就是错误。结果项按 URL 去重,snippet 则来自分离的 citation text block。最终 maxResults 截断仍由 Web seam 拥有,而非该 adapter。CAP-WEB-DEEPSEEK-MAP
一次可见 web_search 是一条额外计费模型请求,拥有独立失败与延迟路径。它在发起 Session 中可观察,但不会经过主 adapter 的 retry、model selection 或 streaming 实现。
6. LSP 刻意拒绝变成通用 JSON-RPC
LSP service 只导出 goToDefinition、findReferences、goToImplementation 和 hover。Provider 原子保留一个 ID 和一组互斥的归一化文件扩展名。Registry 在任何 mutation 前验证完整映射;冲突不会发布任何内容,dispose 会一起释放全部路由。Query 按文件最后一个小写扩展名选择——foo.d.ts 按 .ts 路由——且没有原始 JSON-RPC 逃生口。CAP-LSP-REGISTRY
Seam 因为理解所有操作,才能统一 position、result、cap、error、cancellation 与只读 retry。任意 JSON-RPC 虽能最大化协议覆盖,却会把兼容和安全重新推回 prompt 与 provider 特有行为。
7. lsp 是精确辅助,不是通用代码浏览器
单一只读工具接受封闭 operation enum、文件路径以及从 1 开始的 UTF-16 行/字符。它必须取得调用 Session 的 workspace,绝不 fallback;随后转换到归一化 seam,限制 location 数和渲染字符,并携带 60 秒超时。其 prompt 明确建议普通导航先用 search/read,只有文本导航歧义或修改需要精确符号关系时才用 LSP。CAP-LSP-TOOL
这是重要克制:LSP 不替代源码读取。它只针对当前文件文本回答语义导航问题,再返回有界 location 或 hover 内容供模型继续读取核验。
8. Stdio LSP Backend 与文件系统、进程共享同一执行世界
query(file, workspace)
→ ctx.fs 规范化 workspace 并证明 containment
→ ctx.fs 流式读取一份完整、有字节上限的 source
→ 每 canonical workspace 一个队列
→ 经 ctx.subprocess 懒启动单一 server process
→ didOpen → query → didClose
→ transport failure 时替换一次并重放只读 query
插件在注册任何 provider 前验证所有 server config 并解析所有 executable;registry 冲突会回滚已注册项。进程在首个匹配 query 时才启动;teardown 前先移除路由;所有兄弟进程 dispose 结算后才传播错误。CAP-LSP-STDIO-LOAD
每个 canonical workspace 拥有一个池化 server 和一条覆盖 source read、document open、query、close 的队列。源码字节来自 ctx.fs,server 经 ctx.subprocess 启动。因为操作集合只读,transport failure 可以替换选中进程并透明重试一次。CAP-LSP-STDIO-POOLCAP-LSP-HOST-FS
9. LSP 已实现,并以可选能力形式提供示例
仓库在可选的 Headless/E2B composition 中演示 LSP,由 operator 提供具体 server command 和 extension map。该示例固定了 typescript-language-server,并且只有在 service 与 stdio provider 同时存在时才暴露 tool。CAP-LSP-OPTIONAL
包存在不等于产品可用。有效的 UI 或能力清单必须区分“代码存在”“provider 已配置”“工具对该 Agent 可见”和“provider 当前 ready”。
10. MCP 把 Server 自有工具代际导入原生 Registry
一个 MCP client 实例拥有一个 stdio 或 Streamable HTTP server。稳定的本地 serverName 保留 namespace,每个发现的 raw name 变成 mcp__<serverName>__<rawName>。非法字符或超长名称会附加由身份确定的 hash 后缀,因此重连顺序不会重命名工具,归一化碰撞也变得极不可能。CAP-MCP-CONFIGCAP-MCP-NAMING
Activation 等待首次连接与完整工具发现。failOnStartupError:true 会拒绝并回滚插件;否则可以在没有工具的情况下完成激活,由 supervisor 继续 retry。重复的 live server namespace 在连接开始前就失败。CAP-MCP-STARTUP
11. 工具重同步的 Fetch 阶段事务化,但注册冲突会丢弃旧代际
取完所有分页
不接触 registry,先构建完整下一代 definition map。
Fetch 失败时保留
网络、schema 或重复列表失败会保留上一代。
替换
先 dispose 上一代,再注册全部下一代 definition。
碰撞时 fail closed
若外来工具占据 namespace,回滚部分新代际到零。
Fetch 阶段是 last-known-good,注册阶段则是全新或全无,而不是回滚到旧代。所有首次、notification 和 reconnect sync 都跨 client generation 串行化,避免替换交错。CAP-MCP-SYNCCAP-MCP-RECONNECT
12. MCP Recovery 在耗尽尝试预算前保留最后一份有效目录
Transport close 启动有界指数退避。故障期间最后一代有效注册仍可见,但调用会因 generation 不可用而失败。连接稳定足够久会重置 outage budget;crash loop 最终会注销所有工具并停止,直到 reload/restart。若一个 generation 无法在有界 barrier 内证明 close,系统会停止重连,而不是重叠两个 stdio server process。Dispose 会取消 timer、关闭当前 client、drain 连接 attempt 与 sync chain,最后注销当前代际。CAP-MCP-RECONNECTCAP-MCP-GENERATION
与 Web 类似,MCP schema 可以在执行暂时不可能时仍然可见;不同之处在于,MCP recovery 若发现 server 工具列表变化,可以替换完整模型可见目录。
13. Canonical MCP Result 丰富,原生模型历史则刻意有损
| 数据 | 执行本地 canonical value | Native 模型渲染 |
|---|---|---|
| Text block | 完整 JSON block | 以换行连接文本 |
| Image/audio/resource | 完整 JSON block | 简短“content discarded”占位符 |
structuredContent | 保留;支持的广告 schema 会执行校验 | 不会单独渲染成 rich content |
isError:true | 转成抛出的执行失败 | ToolRuntime 的归一化 error result |
| 要求 task 的工具 | 拒绝 | 不支持执行错误 |
Bridge 防御性验证网络值,为 programmatic/Code Mode caller 保留完整 JSON;若 advertised output schema 使用不支持的词汇,则退回无约束 JSON。Native context 有意丢弃非文本 payload。CAP-MCP-RESULT
14. MCP Stdio 不经过 Harness 的 Subprocess/Sandbox Spawn 路径
Stdio transport 复用 subprocess 包的环境清洗定义,再合并显式配置环境;但真正 child spawn 由 MCP SDK 拥有,不调用 ctx.subprocess。因此它不会自动继承 Shell 与 LSP 使用的执行世界、sandbox runner、remote process provider、process-tree contract 或 output collector。Streamable HTTP 同样把 transport 交给 SDK,并接受配置 header。CAP-MCP-TRANSPORT
这是本章最重要的跨表面不一致。LSP 刻意把文件读取与 server process 放在共享 seam 后;MCP stdio 只共享按名称清理凭证的规则。除非另行约束 Host 或 MCP command,部署方若假设“所有 child process 都在 sandbox 内”,就是错误的。
15. MCP 互操作很广,但 Bridge 比协议本身窄
Bridge 只消费 Tools;MCP Resources、Prompts 与 server instructions 都没有 consumer。连接/发现 timeout 继承 SDK,没有暴露为插件配置。Streamable HTTP request failure 不一定触发 stdio 风格 supervisor 路径。Native 非文本 projection 有损,要求 task 的执行不支持,不支持的 output-schema 词汇失去验证。CAP-MCP-LIMITS
默认组合不发布任何 MCP server。仓库中的 memory 集成明确是默认关闭的参考 overlay;operator 自己安装配置第三方 server,并拥有其存储、身份、model、embedding 和 licensing 行为。CAP-MCP-DEFAULT-OFF
16. Skill 是两阶段 Prompt 能力:廉价路由,按需加载权威
provider list() → SkillCandidate summary
→ layered merge 与 cache
→ durable <available_skills> name + description
→ 模型调用 skill(name) 或用户显式调用 /name
→ provider get(winning candidate)
→ canonical <skill_content> body + resource base
Summary 把路由数据与完整 definition 分离,并带有两个独立权限:modelInvocable 与 userInvocable。Resource base 可以是 directory、URL 或 opaque provider instruction。完整 body 是异步 provider load,不会存进每个目录项。CAP-SKILL-TYPES
Wrapper 会转义 framing metadata,但逐字嵌入 instruction body,因为 Skill 被定义为 trusted local content。Resource hint 要求模型仅在需要时加载被引用的 asset。CAP-SKILL-RENDER
17. Scope 高于 Rank;Rank 只在同一 Layer 内裁决
| Filesystem source | Rank | 同一 layer 内含义 |
|---|---|---|
Project .dsh/skills | 100 | 本地最高优先级 |
Project .agents/skills | 200 | 低于 project-native skill |
| Runtime registration | 250 | 位于 project 与 custom/user root 之间 |
| Custom root | 300 | Operator 提供的 root |
User .dsh/skills | 400 | User-native fallback |
User .agents/skills | 500 | Shared-agent fallback |
| Bundled | 600 | 最低 filesystem 优先级 |
Registry 按 global、ancestor、exact-Agent layer 合并;更近 layer 的同名 entry 直接替换外层 entry。只有同一 layer 中的 candidate 才按 rank 和注册顺序排序。完整 observation 按 cwd、scope chain 和 revision 缓存;不完整 discovery 从不缓存;一次 revision race 会得到有界重试。CAP-SKILL-LAYERSCAP-SKILL-COLLECT
18. Filesystem Provider 经执行世界 FS 读取,却用 Host Watcher 检测变化
Workspace-sensitive discovery 按上述 rank 构建 project、custom、user 和 bundled root。存在 ctx.fs 时,普通 root 经它列举和读取;受信 bundled host root 可以直接使用 Node filesystem。面向模型的 write/edit observation 会同步 invalidate 匹配 root。Host-local watcher 受最大保留 project 数约束,并合并 invalidation。CAP-SKILL-FS-ROOTSCAP-SKILL-FS-TRUST
Discovery 内容可以来自 remote/sandbox filesystem,而 watcher 机制仍位于 Host。Provider 会把 watcher failure 明确表示为 incomplete observation,使模型目录保留最后有效版本并稍后重试,而不是发布瞬时空列表。
19. 模型调用与用户调用经过不同、均会重新验证的路径
| 路径 | Discovery gate | Load gate | 耐久/模型可见结果 |
|---|---|---|---|
模型调用 skill(name) | Summary 仍存在且 model-invocable | 重新检查 loaded definition | 包含正文的普通 tool call/result |
User message 调用 /name | 只扫描直接 source.kind=user 文本 | Loaded definition 必须 user-invocable | 最后追加的 instructions-form user context |
| 未知/user-disabled gesture | 没有被认可的权威 | 不注入 | 保持普通 prose |
Model tool 先 list 再 load,并在两个边界都检查 modelInvocable。显式用户路径直接 load 并检查 userInvocable,因此它是 model-disabled/user-enabled Skill 的唯一入口。两条路径使用同一 canonical renderer。CAP-SKILL-TOOL-LOADCAP-SKILL-USER-INVOKE
20. Skill Catalog 是耐久替换状态,不是每次重建的 System Prompt 大块
每个合格 pre-step 中,插件只发布排序后的 model-invocable name 与有界 description,而且只在自己的精确 skill registration 可见时发布。Durable source 直接存储这些 entry。Digest 变化会追加一份完整 replacement catalog;空 replacement 显式废止旧名称;incomplete discovery 不发布;compaction 隐藏可见目录后,下一次完整 observation 会重新建立它。CAP-SKILL-CATALOG
新增 Skill 不会把每份正文塞进每次请求。Catalog change 是 append-only suffix fact,正文只出现在被选中的 tool result 或显式 invocation 中。代价是历史重复:whole-list replacement 按目录大小付 token;只修改 body 不改变 summary digest;旧的 loaded body 仍是历史 Session fact。
Base 拥有分层 Skill registry,coding preset 则把 filesystem discovery 与 loader 挂到自身 Agent scope,使 global repository contribution 与 preset-local shadow 可以共存。CAP-SKILL-BASECAP-SKILL-SHIPPED
21. 信任矩阵:“外部”在这些表面中并非同一含义
| 边界 | 数据权威 | 执行权威 | 主要风险 |
|---|---|---|---|
| Web search result | 远端、不受信内容 | 本身不启动本地进程 | Prompt injection 与引用质量 |
| Web fetch URL | 模型选择的远端目标 | Host 网络可达性 | SSRF/私网访问 |
| LSP server | 本地 source 加 server response | 共享执行世界中的进程 | Operator 选择的 executable 与 workspace 内容 |
| MCP server | 远端 schema 与 result block | 外部 action 或 SDK-spawn child | 动态能力、认证与 spawn confinement |
| Skill body | 按设计即 trusted instruction | 间接支配后续工具选择 | 可写 Skill root 成为 prompt-authority root |
最值得复用的经验是显式命名 prompt authority。转义类 XML framing 无法把 Skill body 变成不受信内容,因为遵循正文就是它的目的;反过来,Web page 不能仅因来自第一方搜索工具就获得同等权威。Path policy、provider authentication 与 prompt provenance 解决的是不同问题。
22. 聚焦 Codex 对比:相似的渐进披露,更丰富的文档化 MCP 策略
本节只使用 OpenAI 公开的 Web search、Model Context Protocol 与 Customization 文档,读取日期为 2026-08-13。DeepSeek Harness 一侧是固定源码级分析;Codex 一侧仅作公开文档级描述。本节不推断私有服务或未公开实现。
| 维度 | DeepSeek Harness 固定源码 | Codex 公开文档 |
|---|---|---|
| Web | Provider registry;发布 DeepSeek 辅助 search;因缺 SSRF 防线而关闭 fetch | 第一方 search;本地默认 cached,可选 live/disabled;明确把结果视作不受信输入 |
| MCP transport | Stdio 与 Streamable HTTP;仅 Tools | Stdio 与 Streamable HTTP;文档覆盖 server instructions 与更丰富认证 |
| MCP control | 每 server 启动严格度、call timeout、reconnect;本 bridge 没有 tool allow/deny 或 per-tool approval | 文档化 startup/tool timeout、required server、tool allow/deny、server/per-tool approval mode、OAuth 与 bearer 选项 |
| Skill | 摘要目录、按需正文、独立 model/user invocation、耐久替换 fact | 先 metadata,选择后加载 SKILL.md,按需读取 reference/script;implicit 或显式 $ invocation |
| LSP | 封闭四操作 seam 与可选 stdio backend | 本次读取的官方手册未记录独立 LSP tool surface;不做 parity 结论 |
DeepSeek Harness 源码使 provider selection、catalog durability、result projection 与 process seam 极易审计;Codex 的公开 MCP 表面则记录了更完整的 operator policy plane——authentication、filtering、required startup、server guidance 与 per-tool approval。两者都公开描述 Skill 的渐进披露;DeepSeek 源码还直接展示 replacement catalog 如何成为 Session fact。
23. 可迁移的设计规则
稳定最窄且诚实的词汇
产品语义用固定工具;只有互操作确实需要时才导入动态 schema。
分离可见性与 readiness
稳定目录有利缓存,但 provider health 必须独立可观察。
显式定义 generation swap
分别定义 discovery failure、registration conflict、transport loss 与 recovery 耗尽。
把 instruction store 当作 authority store
可写 Skill root 不只是文档存储,它能引导后续高权限调用。
本章最深的模式是“带类型所有权的渐进能力披露”。Web 把 provider 多样性藏在稳定产品语义后;LSP 把庞大协议压成四个可靠问题;MCP 接受动态性,却用确定性身份与生命周期代际约束它;Skill 让路由廉价、指令权威显式。剩余债务正出现在表面跨越自己不拥有的 seam 处:没有网络策略的 fetch、绕开 subprocess 世界的 MCP spawn,以及扎根可变文件的受信 Skill body。
以上全部 DeepSeek 结论均绑定固定 baseline 的生产源码、发布配置、示例与 package 文档。本次交付还在隔离临时根目录下运行了覆盖 Web、LSP、MCP 与 Skill 的 16 个聚焦 Vitest 文件:454 项全部通过。Codex 对比则单独限定为三个链接的官方页面及读取日期。运行时验证不会把可选组合变成发布默认,package test 通过也不证明真实网络安全或第三方 server 正确性。
我的学习体会
内容仅自动保存到当前浏览器,不上传、不进入仓库。你可以导出 Markdown 自行归档。