消除大模型幻觉,让AI-IDE真正理解代码,打通LSP与AI的任督二脉

消除大模型幻觉,让AI-IDE真正理解代码,打通LSP与AI的任督二脉

当我看到 Cursor 等 AI-IDE 的日志里显示 Grepping for... 时,我心里"咯噔"一下。

Grepping?这不就是文本搜索吗!这能靠谱吗?如果大量变量名重名的话,那他不就乱套了吗?

Grep 是什么? grep 是一个源于 Unix 的强大命令行工具,用于在文件中进行基于"正则表达式"的文本搜索。它非常快,但不"理解"代码的语法结构。AI 助手使用它,就像用 Ctrl+F 在代码的"字符串"海洋里捞针,捞上来的可能是鱼,也可能是靴子。

在大型项目开发中,当你让 AI-IDE 修改你的代码时,它经常会写很多错误类型,对复杂的类型继承关系更是一头雾水。这是多个原因造成的:

  1. 这个 AI-IDE 太懒了,仅仅读取了部分文件,上下文不足时,他会基于以往的经验猜测,这部分数据大多来自预训练的数据,或者你以前的上下文进行猜测。
  2. 使用 Grep 正则搜索时,如果遇到重名的变量名,此时就只能看 LLM 的智商能不能分辨了。

这种感觉,就像你给了助理一张藏宝图,他却只会按图上的文字去"搜索"地名,而完全不理解等高线、比例尺和指南针。

真正能"看懂"代码的,是 VSCode 背后那个强大的大脑------语言服务协议(Language Server Protocol, LSP)

而 AI 模型与外部工具沟通,则依赖另一套语言------模型上下文协议(Model Context Protocol, MCP)

于是一个想法在我脑中萌生:我能不能搭建一座桥梁,让 AI-IDE 的 MCP "嘴巴",能直接连上 VSCode 的 LSP "大脑"?


📖 阅读本文,你将收获

我不想只展示一个项目,更希望分享整个探索过程中的思考与沉淀。跟随我的脚步,你将了解到:

  • 🤔 LSP vs Grep: 深入理解为什么 LSP 是现代 IDE 的基石,以及它如何从根本上超越传统的文本匹配。
  • 🤝 MCP 协议解密: 了解 MCP 如何像"通用语"一样,让不同的 AI 模型与外部工具顺畅交流。
  • 🌉 "造轮子"的艺术: 了解我是如何分析现有工具的优劣,并最终决定自己动手,打造一个更优的解决方案。
  • 🚀 VSCode 插件开发心法 : 从环境搭建、调试到打包发布,我将分享基于 antfu/starter-vscode 模板的全流程实战经验。

科普:LSP 与 MCP,驱动 AI 编程的两大引擎

首先解释一下常用名词:

  • LSP: Language Server Protocol,即语言服务协议,是 VSCode 的底层协议,用于与语言服务器通信。
  • MCP: Model Context Protocol,即模型上下文协议,是 AI 模型与外部工具通信的协议。
  • stdio: Standard Input Output,标准输入输出,是进程间通信的一种方式,也是 MCP 的默认通信方式。
  • SSE : Server-Sent Events,服务器发送事件,是 HTTP 的一种通信方式,在 MCP 中是弃用状态,改用 Streamable HTTP 代替。
  • Streamable HTTP: 流式 HTTP,是 HTTP 的一种通信方式,在 MCP 中是新标准,用于替代 SSE。
  • LLM: Large Language Model,即大语言模型,是 AI 模型的统称。

要理解这个插件的价值,我们必须先弄懂它所连接的两个世界:LSP 和 MCP。

LSP:代码世界的"语法导航"

在没有 LSP 的世界里,每个编辑器(VSCode, Sublime, Atom...)想要支持一门新语言(Python, Rust, Go...),都得自己从头写一套代码分析引擎。这是一个"M x N"的灾难。

LSP (语言服务协议) 就是为了解决这个问题而生的。它将"编辑器 UI"和"语言分析服务"进行了解耦。

  • 语言服务器 (Server): 一个独立的进程,它"精通"某一门语言,负责代码分析、错误检查、自动补全等所有"脏活累活"。

  • 编辑器 (Client): 只需要实现 LSP 客户端,就能通过标准化的 JSON-RPC 协议与任何语言服务器通信,从而"免费"获得该语言的智能支持。

    graph TD subgraph "VSCode 编辑器 (Client)" A[UI 界面] --> B[LSP Client]; end subgraph "语言服务器 (Server)" D[LSP Server] --> E[代码 AST 分析]; E --> F[符号定义/引用]; E --> G[类型推断]; end B <--> D; style B fill:#f60,stroke:#333,stroke-width:2px; style D fill:#b6b,stroke:#333,stroke-width:2px;

这就是为什么 LSP 比 grep 强大得多:LSP 理解代码的上下文和语义 ,而 grep 只认识字符串

MCP:AI 与工具的"通用翻译器"

与 LSP 解决编辑器与语言的"M x N"问题类似,MCP (模型上下文协议) 旨在解决"AI 模型"与"外部工具"之间的"N x M"集成难题。

在 MCP 出现之前,如果你想让 Claude 和 ChatGPT 都能使用你的 API,你可能需要为它们各自编写不同的适配器。如果我没记错的话,最早出现的应该 Function-Call

Function-Call

下面我带你快速了解 Function-Call 的原理。

  1. 打开 aistudio.google.com/ 需要科学上网
  2. 点击右边的 Function Calling ,创建一个函数,实际就是 JSON 描述而已,我这里是计算两数之和,没什么高大上的,都是很简单的原理。注意要用英文描述,否则 Gemini 会报错。
  3. 询问你的问题,比如 996+770等于几?,此时他会把你的问题转换成 JSON 格式,然后调用你创建的函数,并返回结果。但是这里是模拟,你需要自己填入结果
  4. 查看结果\
MCP

其实 MCP、Function-Call 都是一样的,很简单的原理。

  1. 定义 schema(即数据结构),任何语言都行,但是大家都爱 JSON
  2. 大模型自动提取对话的 参数,使用你的 schema,包装成合法的 JSON 格式,发送给你
  3. 你自己解析 JSON,然后 LLM(Large Language Model)会返回结果,此时 LLM 就能看到你返回的参数了。这样一来,LLM 就有无限的可能了

MCP 提供了一个统一的、供应商无关的接口,让任何 AI 模型都能通过一套标准协议来发现和调用外部工具。它就像一个通用翻译器,让说不同"方言"(来自不同公司)的 AI,都能和你这个"工具提供方"顺畅交流。

就跟你调用后端的接口,他给你数据一个道理。整个世界都是建立在通信(TCP/IP)之上的。


架构抉择:为什么选择 VSCode 插件?

在构思这个项目时,我面临一个关键的架构选择:是开发一个 VSCode 插件 ,还是创建一个独立的、通过命令行启动的 LSP 服务

乍一看,后者似乎更"纯粹"。社区为几乎每种主流语言都提供了独立的 LSP 实现,例如 typescript-language-server 用于 TypeScript,或 pylsp 用于 Python。理论上,我可以将这些服务包装起来,通过 MCP 暴露给 AI。

但深入思考后,我发现了这种方法的几个致命缺陷:

  • 巨大的资源浪费 : 启动一个独立的 LSP 服务,意味着它需要从头开始扫描、解析和索引整个项目的文件。这个过程不仅消耗大量的 CPU 和内存,而且完全是重复劳动------因为你正在使用的 VSCode,其内置的 LSP 客户端也正在做着一模一样的事情!
  • 高昂的开发与维护成本: 如果要支持多种语言,我就需要集成和管理多个不同的 LSP 服务,处理它们各自的环境依赖和版本兼容性问题。这会让我的"小桥"变成一个复杂的"交通枢纽",开发和维护成本会急剧上升。
  • 与 IDE 脱节: 独立服务无法轻易访问 VSCode 的工作区状态、用户配置等上下文信息,体验上会有割裂感。

相比之下,开发 VSCode 插件的优势则显而易见:

  • 零资源冗余 : 插件直接复用 VSCode 已经建立 的 LSP 连接和分析结果。AI 通过插件查询时,相当于直接向 VSCode 的"大脑"提问,无需任何额外的文件分析开销。
  • 开箱即用的多语言支持 : 只要 VSCode 能识别某个语言(通过安装对应的语言插件),我的插件就能为该语言提供 LSP 服务。我无需为 TypeScript, Python, Go, Rust 等每一种语言做任何特殊适配。
  • 轻量且专注: 插件的职责变得非常纯粹------仅仅是作为 VSCode 内部命令和 MCP 协议之间的"翻译官"。这大大降低了开发复杂度和维护成本。

这个决策,奠定了项目的基石:与其在 IDE 之外重建一个重复、笨重的轮子,不如聪明地利用 IDE 自身强大的能力。

挫败的探索:为什么最终决定"造轮子"?

确定了"VSCode 插件"这条路后,我满怀信心地认为市面上肯定有现成的解决方案。我的确找到了几个类似的工具,比如用 Python 编写的 Serena(尽管它不是 VSCode 插件,也印证了我对独立服务的担忧)。

然而,我的尝试过程却异常坎坷。我发现这些工具普遍存在一些问题:

  1. 环境臃肿 : 比如 Serena 需要完整的 Python 环境,对于一个只想增强 VSCode 功能的用户来说,这个依赖太重了。
  2. 架构笨重: 它们大多作为独立的后台服务运行,需要用户手动管理其生命周期,而不是像 VSCode 插件一样"即开即用,即关即停"。
  3. 功能冗余: 提供了太多与 AI IDE 本身重叠的功能,不够专注,反而增加了复杂性。
  4. "失灵": 这是最致命的。我尝试了 4 个不同的工具,尽管 MCP 是通用的,但是它们的描述都是 Claude 如何使用,我在 Cursor 中配置后,LSP 相关功能无一例外地失败了。

在花费了大量时间却一无所获后,我意识到,与其在这些不可靠的"旧桥"上修修补补,不如自己从头建一座新的。我的目标很明确:

  • 纯粹: 只做一件事------桥接 LSP 和 MCP。
  • 轻量: 以 VSCode 插件形式存在,无额外依赖。
  • 可靠: 专注核心 LSP 功能,确保它在所有支持 MCP 的 AI 工具中都能稳定运行。

探索之旅:从 0 到 1 的 VSCode 插件开发

从一个想法到能在 VSCode Marketplace 上被千万人使用的插件,这个过程比想象中更简单。VSCode 为开发者提供了完善的工具链和丝滑的开发体验。

环境搭建与项目初始化

虽然可以通过官方的 yo code 生成器从零开始,但我更推荐直接使用社区维护的优秀模板,例如 antfu/starter-vscode

bash 复制代码
git clone https://github.com/antfu/starter-vscode.git my-lsp-plugin
cd my-lsp-plugin
pnpm install

这为我节省了大量花在工程配置上的时间,让我能立刻专注于核心逻辑的开发。按下 F5,一个包含插件的"扩展开发宿主"窗口就会立刻启动,开启你的调试之旅。

插件核心:package.jsonvscode API

一个 VSCode 插件的核心是 package.json 清单文件和 vscode 全局 API。

  • package.json: 它不仅仅是 Node.js 的依赖描述文件,更是插件的"户口本"。

    • name, publisher, version: 插件的身份标识。
    • engines.vscode: 声明插件兼容的 VSCode 版本。
    • activationEvents: 极其重要。它告诉 VSCode 在什么"时机"激活你的插件,避免不必要的性能开销。例如,只在打开某个类型的文件,或执行某个命令时才加载插件。
    • contributes: 插件的"贡献点",定义了插件向 VSCode 添加了哪些新功能,比如新的命令、设置、侧边栏视图等。
  • vscode API : 插件的"瑞士军刀"。vscode 模块提供了海量的 API,让你能与编辑器的方方面面交互。

    • vscode.window: 用于创建和管理通知、状态栏、输入框等 UI 元素。
    • vscode.workspace: 提供对当前工作区文件、配置的访问能力。
    • vscode.commands.executeCommand: 这是一个强大的"传送门"。VSCode 将其海量的内部能力都通过"命令"的形式暴露出来。我们不需要知道"查找定义"的具体实现,只需要像调用一个函数一样,向 VSCode 发出指令即可。

比如,在本项目中,实现 textDocument/definition(查看符号定义)功能的核心代码就是如此:

传入:

  1. 文件路径
  2. 行号
  3. 列号

返回:

  1. 详细符号信息
typescript 复制代码
const definitions = await vscode.commands.executeCommand<vscode.Location[]>(
  'vscode.executeDefinitionProvider', // "VSCode,帮我执行'定义提供者'"
  document.uri, // 在哪个文件
  position,     // 在哪个位置
)

只需要完成所有的 LSP 功能,然后就能通过 MCP 服务调用了

调试与发布

  • 调试 : 只需在代码中打上断点,然后按下 F5。VSCode 会自动启动一个"扩展开发宿主"新窗口,我们的插件就在其中运行。所有在主窗口中的调试操作(单步执行、变量悬停、查看调用栈)都能无缝作用于插件代码。

  • 打包与发布 : 当插件开发完毕,vsce(Visual Studio Code Extensions)这个命令行工具让分发变得异常简单。

    1. 打包 : 运行 vsce package --no-dependencies,一个 .vsix 格式的插件包就生成了。
    2. 发布 : 首先,你需要在 Azure DevOps 上创建一个组织并获取一个"个人访问令牌 (PAT)",然后在 VSCode Marketplace 上创建"发布者"。最后,通过 vsce login <发布者ID> 登录,再用 vsce publish 就可以将你的插件发布到全世界了!

如果需要让 Cursor 也能使用,需要发布到 open-vsx.org/


深入 MCP:构建你自己的"工具集"

光有 LSP 作为 IDE 的桥梁还不够,我们还需要了解如何让 AI 使用我们的工具。

所以需要开发一个 MCP 服务。

MCP 开发三步走

假设我们要将 VSCode 的 LSP 功能封装成 MCP 工具,让 AI 可以查询。

  1. 安装依赖 : 我们需要 MCP 的 TypeScript SDK 和 zod 用于数据校验。

    bash 复制代码
    pnpm add @modelcontextprotocol/sdk zod
  2. 定义工具 : 使用 zod 定义工具的输入,这能确保 AI 传入的数据格式是正确的,并能提供清晰的描述。

    typescript 复制代码
    import { z } from 'zod'
    
    const lspInputSchema = {
      uri: z.string().describe('目标文件的 URI'),
      line: z.number().describe('行号'),
      character: z.number().describe('字符位置'),
    }
  3. 创建服务器并注册工具 : 我们用 @modelcontextprotocol/sdk 创建一个 MCP 服务器,并将刚刚定义的工具注册进去。

    typescript 复制代码
    import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
    
    const server = new McpServer({
      name: 'lsp-mcp-server',
      version: '1.0.0',
    })
    
    server.registerTool(
      'get_hover',
      {
        title: '获取悬停信息',
        description: '根据文件 URI 和位置,获取代码符号的悬停提示信息 (类似 VSCode 中的鼠标悬停)',
        inputSchema: lspInputSchema,
      },
      async (input) => {
        // 核心逻辑:调用 VSCode API
        const result = await getHoverFromVSCodeAPI(input.uri, input.line, input.character)
        return {
          content: [{ type: 'text', text: JSON.stringify(result) }]
        }
      }
    )
    
    // ... 启动服务器的代码

    MCP 支持多种通信方式,如 stdio(用于本地进程通信)和 Streamable HTTP(用于网络服务)。 本项目采用后者,通过一个本地 HTTP 服务器暴露 MCP 端点。

调试 MCP 服务

调试 MCP 的最佳工具是官方提供的 @modelcontextprotocol/inspector。它是一个 Web UI,可以连接到你的 MCP 服务器,让你直观地测试工具、查看请求和响应。

使用 @modelcontextprotocol/inspector

MCP Inspector 是一个专门用于测试和调试 MCP 服务器的开发工具。它由两个主要组件组成:

  1. MCP Inspector 客户端 (MCPI):基于 React 的 Web UI
  2. MCP 代理 (MCPP):Node.js 服务器,充当协议桥梁

安装和启动 Inspector

使用 npx 快速启动:

bash 复制代码
npx @modelcontextprotocol/inspector

检查特定的 MCP 服务器:

bash 复制代码
npx @modelcontextprotocol/inspector node build/index.js

Inspector 的代理负责连接 Web UI 和您的 MCP 服务器 。出于安全考虑,Inspector 代理服务器默认需要身份验证。当 Inspector 启动时,会生成一个随机会话令牌并打印到控制台 。

为方便起见,Inspector 会自动在 URL 中预填充此令牌打开浏览器(例如,http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=...),确保无缝的初始连接。如果 Inspector 已经打开,您可以手动将"代理会话令牌"输入到侧边栏的"配置"按钮中,然后单击"保存"以应用设置


打通任督二脉:在 AI 助手中配置 MCP

打开插件市场(按下 Ctrl + Shift + X)搜索:LSP MCP,安装并启动后,会在本地启动一个 MCP 服务。

  1. 确认服务地址 : 插件默认在 http://localhost:9527/mcp 提供服务。你可以在 VSCode 的设置中搜索 lsp-mcp.port 来修改端口。

  2. 配置 AI 助手: 打开 AI 助手的设置,找到 MCP 的配置选项

Cursor

json 复制代码
{
  "mcpServers": {
    "lsp": {
      "url": "http://127.0.0.1:9527/mcp"
    }
  }
}

Roo Code

json 复制代码
{
  "mcpServers": {
    "lsp": {
      "type": "streamable-http",
      "url": "http://127.0.0.1:9527/mcp",
      "disabled": false
    }
  }
}

局限性:这并非银弹

需要坦诚的是,这个插件并非万能。它的核心价值在于为 AI 提供了精准的代码符号导航与理解能力

  • 它擅长 : 查找定义查找引用悬停提示符号重命名。在这些场景下,它的准确度远超任何基于文本搜索的方案。
  • 它不擅长: 直接生成大段的业务逻辑。它的作用是为 AI 提供更精确的"地图",让 AI 在你已有的代码上进行分析和修改时,不会"迷路"。

在少数但关键的场景下,它能发挥巨大的作用,显著提升 AI 编程助手的"智商"。

相关推荐
散峰而望1 天前
基本魔法语言数组 (一) (C语言)
c语言·开发语言·编辑器·github·visual studio code·visual studio
大模型真好玩1 天前
LangChain1.0速通指南(一)——LangChain1.0核心升级
人工智能·agent·mcp
“负拾捌”2 天前
基于NodeJs实现一个MCP客户端(会话模式和无会话模式)
javascript·ai·node.js·大模型·mcp
AmazingKO2 天前
推送报错403怎么办?vscode推送项目到github
chatgpt·github·visual studio code·竹相左边
清沫3 天前
规训 AI Agent 实践
前端·ai编程·cursor
coder_pig3 天前
【独家实测】Cursor 2.0 发布,花一分钟看看都更新了啥
aigc·ai编程·cursor
华为云开发者联盟3 天前
【新特性】 版本速递 | 华为云Versatile智能体平台 新增特性介绍(2025年10月发布)
人工智能·华为云开发者联盟·ai agent·mcp·华为云versatile
字节跳动安全中心3 天前
开源!可信MCP,AICC机密计算新升级!
安全·llm·mcp
大模型真好玩3 天前
低代码Agent开发框架使用指南(六)—Coze 变量与长期记忆
人工智能·coze·mcp