Files
clz 3c0cd86352 docs: add MCP, plugin/LSP tool, and RAG notes
- notes/mcp-overview.md: MCP 协议概览,跟 tool use 的分层差异
- notes/mcp-client-internals.md: MCP client 内部实现(发现/连接/catalog)
- notes/plugin-tools.md: plugin tool 与 native/MCP 的对比
- notes/lsp-tools.md: LSP 协议科普 + OpenClaw LSP tool 包装
- notes/rag-overview.md: RAG 与向量化(向量只用来检索,prompt 用原文)
- notes/blog/mcp-101.md: 面向开发者的 MCP 科普,LSP 类比引入
- README 索引更新
2026-06-16 00:59:32 +08:00

11 KiB

LSP Tool

基于 OpenClaw 2026.6.2 版本源码。

本文先讲 LSP(Language Server Protocol)是什么——它是 IDE 世界里 一个很重要的协议,但跟 AI 领域关系不大,所以单独介绍——再讲 OpenClaw 怎么把它包装成 agent 的 tool。

LSP 是什么

它解决什么问题

写编辑器的人原来面临一个 M × N 问题:

  编辑器 (M 个)        语言支持 (N 种)
  ┌────────────┐      ┌──────────┐
  │ VSCode     │ ↔↔↔↔ │ Python   │
  │ Vim        │ ↔↔↔↔ │ Go       │
  │ Emacs      │ ↔↔↔↔ │ TypeScript│
  │ Sublime    │ ↔↔↔↔ │ Rust     │
  │ JetBrains  │ ↔↔↔↔ │ Java     │
  └────────────┘      └──────────┘

每个编辑器都得给每种语言写一遍 "hover 显示类型"、"跳转到定义"、 "查找所有引用"、"实时报错"...这些功能。M 个编辑器 × N 种语言 = M × N 份重复实现。

LSP 是微软 2016 年提出的解法:把"语言能力"抽出来变成一个独立的 服务器进程,编辑器和服务器之间用一套标准协议通信。

  编辑器                    Language Server
  ┌────────┐    LSP        ┌──────────────┐
  │ VSCode │ ←─────JSON-RPC→│ tsserver     │  ← TypeScript
  └────────┘                └──────────────┘

  ┌────────┐                ┌──────────────┐
  │ Vim    │ ←──────────────│ gopls        │  ← Go
  └────────┘                └──────────────┘

  ┌────────┐                ┌──────────────┐
  │ Emacs  │ ←──────────────│ rust-analyzer│  ← Rust
  └────────┘                └──────────────┘

现在变成 M + N:每个编辑器实现一次 LSP client,每种语言实现一次 LSP server。VSCode 装 Rust 插件 = 它在 VSCode 端走 LSP client, 背后跑一个 rust-analyzer 进程。

协议形态

LSP 用 JSON-RPC 2.0,跑在 stdio 之上(也支持其他 transport,但 stdio 是默认)。消息分帧用 HTTP 风格的 Content-Length header:

Content-Length: 213\r\n
\r\n
{"jsonrpc":"2.0","id":1,"method":"textDocument/hover","params":{...}}

典型方法名:

  • initialize — 握手,client 告诉 server 自己支持什么, server 回 capabilities(支不支持 hover、definition 等)
  • textDocument/didOpen / didChange / didClose — 通知文件状态
  • textDocument/hover — 给我光标位置的类型/文档
  • textDocument/definition — 跳转到定义
  • textDocument/references — 找所有引用
  • textDocument/completion — 自动补全
  • textDocument/publishDiagnostics — server 主动推:这里有错

每个方法的参数和返回值都在 LSP 规范里固定下来,所以一个 LSP server 可以同时被 VSCode、Vim、Emacs 用。

跟 MCP 的对照

维度 LSP MCP
目标受众 IDE / 编辑器 LLM 应用
服务器干啥 提供代码分析(hover/跳转/补全) 提供任意工具(读文件/查搜索/...)
方法集合 固定一套 textDocument/* server 自己声明 tools/list
协议层 JSON-RPC 2.0 + Content-Length JSON-RPC 2.0
提出者 微软 Anthropic
年份 2016 2024

两个协议长得很像(stdio + JSON-RPC),解决的是不同领域的同一种 M × N 问题——把可复用的能力(代码分析 / agent 工具)抽到独立进程。 某种意义上 MCP 是 LSP 思路在 AI 时代的延续。

OpenClaw 怎么用 LSP

核心实现 src/agents/agent-bundle-lsp-runtime.ts,第一行注释:

Minimal LSP JSON-RPC framing over stdio (Content-Length header + JSON body).

不是用现成的 LSP client 库,而是自己在 OpenClaw 里实现了 LSP 协议的 最小子集(Content-Length 分帧 + JSON-RPC),spawn 语言服务器子进程, 跑 initialize 握手拿能力。

暴露的 tool

根据语言服务器 initialize 返回的 capabilities 动态生成 (agent-bundle-lsp-runtime.ts:317 buildLspTools):

Tool 名 LSP 方法 用途 capability 门槛
lsp_hover_<server> textDocument/hover 看符号的类型/文档 hoverProvider
lsp_definition_<server> textDocument/definition 跳到定义 definitionProvider
lsp_references_<server> textDocument/references 找所有引用 referencesProvider

如果语言服务器没声明对应 capability(比如 hoverProvider: false), 对应 tool 就不会被注册。

工具参数都长一样(uri: string, line: number, character: number), 因为 LSP 协议本身就是这套 textDocument + position 的形态。

注意没有 completion:agent 不需要交互式补全,它直接看完整文件就行。 OpenClaw 只挑了"看完整代码不能直接得到的语义信息"做工具——hover 拿 推断类型,definition/references 需要跨文件 AST 跳跃。

跟 MCP / plugin 平行

维度 LSP tool MCP tool plugin tool
协议 LSP(textDocument/*) MCP(tools/call) OpenClaw Plugin SDK
进程 stdio 子进程 stdio 子进程 / HTTP 同进程
Tool 名 hardcode 三个 + server 后缀 server 自报 tools/list plugin 代码静态声明
配置来源 bundle plugin manifest bundle plugin + 用户 config plugin manifest
用户能加吗 现在不能 能(openclaw mcp add) 装新 plugin 即可
Catalog 动态性 静态(三个固定 tool) 动态(server 决定) 静态
模型视角 跟其他 tool 没区别 跟其他 tool 没区别 跟其他 tool 没区别

跟 MCP 最大不同:LSP 的 tool 集是 OpenClaw 写死的(hover/definition /references 三个),只是是否启用看 server capability。MCP 是 server 想暴露啥就 tools/list 出啥,完全运行时决定。

这是 LSP 协议本身决定的——LSP 的方法集就那么固定一套,不像 MCP 让 server 自由声明工具。OpenClaw 只是把 LSP 方法翻译成 agent 理解的 tool。

在 attempt 时序里

跟 MCP 完全平行(回看 mcp-client-internals.md 的 "Attempt 引爆点"):

// src/agents/embedded-agent-runner/run/attempt.ts:1502-1549
const bundleMcpRuntime = bundleMcpSessionRuntime
  ? await materializeBundleMcpToolsForRun({...})
  : undefined;
bundleLspRuntime = bundleLspEnabled
  ? await createBundleLspToolRuntime({...})     // ← LSP 在这里 spawn + initialize
  : undefined;

const allowedBundleMcpTools = applyEmbeddedAttemptToolsAllow(bundleMcpRuntime?.tools ?? [], ...);
const allowedBundleLspTools = applyEmbeddedAttemptToolsAllow(bundleLspRuntime?.tools ?? [], ...);
const allowedBundledTools = [...allowedBundleMcpTools, ...allowedBundleLspTools];

每次 attempt 开始时,两条 runtime 各自独立 spawn(MCP / LSP), 各自维护 session,各自 dispose;最后合到同一个扁平 tool list 给模型。

典型用例

模型要回答 "把 runEmbeddedAttempt 的所有调用方都列出来"。可能的工具组合:

  1. Grep "runEmbeddedAttempt" ——快,但会被注释、字符串、相似名字误伤
  2. lsp_references_typescript ——准,基于 AST 分析,但要先定位符号的 精确 (uri, line, character)
  3. 常见组合:先 Grep 找出大致位置 → Read 确认 → lsp_references_typescript 拿精确引用列表

LSP tool 的价值在于 精确度——code intelligence 这件事 grep 替代不了:

  • Grep 找的是文本匹配,会把字符串字面量、注释里的同名词都算进来
  • lsp_references 走 AST + 类型系统,能区分 foo.runEmbeddedAttempt 调的是哪个 foo,跨 import 跳得对

类似地 lsp_hover 拿 inferred type 是 grep / read 替代不了的—— TypeScript 的 const x = foo() 你想知道 x 是啥类型,只有 tsserver 能告诉你。

配置怎么写

跟 MCP server 一样,LSP server 从 plugin bundle manifest 读 (src/plugins/bundle-lsp.tssrc/agents/embedded-agent-lsp.ts), plugin 在自己的 manifest 里声明 lspServers,字段形态跟 MCP 的 stdio server 几乎一样(commandargsenvcwd)。

OpenClaw 实际上复用了 MCP 的 stdio launch config 结构—— agent-bundle-lsp-runtime.ts 里直接 import 了 resolveStdioMcpServerLaunchConfigdescribeStdioMcpServerLaunchConfig,因为两者都是 "spawn 一个本地 stdio 子进程跑 JSON-RPC",launch 部分可以共享。

现状的限制

src/agents/embedded-agent-lsp.ts:22 有一条注释:

User-configured LSP servers could override bundle defaults here in the future.

意思是当前只接受 plugin bundle 声明的 LSP server,用户 config 还 没接通。MCP 已经走得更远(用户 config 可以加自己的 server),LSP 还 停在 plugin 预置的阶段。

退避逻辑也比 MCP 简化——没有 "连续 3 次失败冷却 60s" 这种保护, 失败就直接报错给 agent。

心智模型一句话

LSP 是把 "编辑器智能" 抽出来的协议,跟 MCP 是把 "agent 工具" 抽出来的 协议同源同构(stdio + JSON-RPC + 服务器自报能力)。OpenClaw 借了 LSP 这套现成的代码分析基础设施,把 hover/definition/references 三个最有 agent 价值的能力包装成 tool,放进 agent 的扁平 tool 列表里。模型层面 跟其他 tool 没任何区别。

相关