MCP 与 LSP 集成
先记住一句话
如果说 Claude Code 的内置工具解决的是“我自己会什么”,
那 MCP 解决的就是“我还能接入谁”。
很多人第一次接触 MCP,会把它理解成“插件协议”。
这个理解不能算错,但太浅了。
从 Claude Code 源码来看,MCP 更像一层 外部能力接入总线:
- 外部工具可以接进来
- 外部资源可以接进来
- 外部 prompts 可以接进来
- 外部 skills 也可以接进来
真正重要的不是“又多了几个工具”,而是 Claude Code 的能力边界可以在运行时被重新扩展。
Claude Code 里的 MCP,不只是调一个 server
从源码看,Claude Code 对 MCP 做的事情远比“连上一个 server”多。
services/mcp/client.ts 负责的是完整的 MCP 客户端层,里面至少覆盖了这些事:
- 不同传输方式的连接
- OAuth 和认证刷新
- tools/list 拉取
- resources/list 拉取
- prompts/list 拉取
- 工具调用时的重试和交互
- 资源工具注入
- MCP skills 拉取
也就是说,Claude Code 不是把 MCP 当成一个外围插件,而是直接把它放进主运行时里。
Claude Code 支持哪些 MCP 连接方式
源码里的配置类型能看到,Claude Code 支持不止一种 MCP 连接方式:
stdiossehttpws- 还有给 IDE 和内部场景预留的几种特殊类型
对普通用户来说,可以简单理解成三大类:
-
本地子进程 MCP
比如你在本机启动一个 MCP server,Claude Code 通过标准输入输出跟它通信。 -
远程 HTTP MCP
Claude Code 直接去连一个远程 MCP 服务。 -
远程 WebSocket MCP
更适合持续连接、持续交互的场景。
这点很关键。
它说明 MCP 在 Claude Code 里不是“只支持本地玩具 server”,而是从一开始就按真实接入场景设计的。
MCP 接入后,最核心的一步是什么
最核心的一步是:标准化。
MCP server 暴露出来的原始能力,Claude Code 不会直接丢给模型。
它会先做一次“翻译”。
比如在 fetchToolsForClient() 里,Claude Code 会先请求:
tools/list
然后把每个 MCP 工具包装成统一的 Tool 结构。
这一步之后,MCP 工具和内置工具在运行时里就基本站到同一个层面了。
你可以把这一步理解成:
Claude Code 先把“外部世界的能力”,翻译成“自己运行时能理解的能力”。
这就是为什么模型眼里,很多 MCP 工具和内置工具看起来没有本质区别。
它接进来的不只是工具
很多文章讲 MCP,只讲工具调用。
但 Claude Code 源码里更有意思的一点是:它明显把 MCP 看成了 多种能力的统一入口。
除了 tools/list,它还会拉:
resources/listprompts/list
而且当服务器支持 resources 时,Claude Code 还会额外把两个内置桥接工具塞进当前工具集:
ListMcpResourcesToolReadMcpResourceTool
这两个工具的意义很大,因为它们把“资源”也变成了模型能主动访问的对象。
简单说:
- 工具:可以执行动作
- 资源:可以读取外部数据
- prompts:可以提供外部提示模板
这比“插件提供几个 API”要高级得多。
ListMcpResourcesTool 和 ReadMcpResourceTool 在干什么
这是源码里一个很值得讲的细节。
Claude Code 没有让模型直接跟 resources/list、resources/read 协议打交道,
而是自己做了一层桥接。
ListMcpResourcesTool
它负责列出当前所有已连接 MCP server 暴露出来的资源。
如果你指定了某个 server,就只列那个 server 的资源。
它的作用很像:
- 先看都有什么可读资源
- 再决定下一步读哪个
ReadMcpResourceTool
它负责真正读取指定资源。
这个工具有个很工程化的细节:
如果资源内容是二进制 blob,它不会把一大坨 base64 直接塞回上下文,而是先落盘,再把路径和说明返回。
这说明 Claude Code 在设计 MCP 时,不只是为了“协议打通”,还在认真处理上下文大小和结果可用性问题。
MCP 能力不是固定的,它会动态刷新
这是 Claude Code 里很有料的一个实现点。
很多人以为:
- 启动时连上 MCP
- 拉一遍工具
- 后面就结束了
但 Claude Code 不是这样。
在 useManageMCPConnections.ts 里,它专门监听:
tools/list_changedprompts/list_changedresources/list_changed
一旦 MCP server 说“我的能力变了”,Claude Code 就会刷新本地缓存,把新的工具、prompts、resources 重新拉进来。
更进一步,在 query.ts 里,主循环每轮之间还会执行一次 refreshTools(),这样新接入的 MCP server 可以在下一轮立刻变成可用工具。
这意味着什么?
这意味着 Claude Code 的能力图谱不是启动时写死的,
而是会随着 MCP server 状态变化而动态更新。
这比“静态插件列表”强很多。
MCP Skills 为什么值得单独讲
在 Claude Code 源码里,MCP skills 也属于 MCP 生态的一部分。
但它和本地 skills 有一个重要区别:
MCP skills 被明确当成远程且不可信内容来处理。
在 loadSkillsDir.ts 里有一句非常关键的注释:
- MCP skills are remote and untrusted
- never execute inline shell commands
这句话的意思很直白:
- 本地 skill 里的某些动态能力可以展开执行
- 但 MCP skill 不行
为什么?
因为本地 skill 是你机器上的内容,
MCP skill 则可能来自远程 server。
Claude Code 在这里明显做了安全边界区分。
所以如果你要一句话理解:
Claude Code 不是简单地“支持 MCP skills”,而是支持它,同时明确降低它的信任级别。
这是很成熟的运行时思路。
MCP 还有一个容易被忽略的点:用户交互是协议化的
源码里还有 Elicitation 相关逻辑。
它解决的问题是:
如果 MCP server 在执行过程中需要用户补充信息,怎么办?
比如:
- 需要登录授权
- 需要确认某个操作
- 需要补一个参数
Claude Code 没有把这些情况写成一堆 server-specific if/else,
而是让 MCP server 通过协议提出交互请求,客户端统一接管。
所以从产品体验上看,你会觉得:
- 明明是外部 server
- 但交互过程还是很像 Claude Code 自己的一部分
这背后就是协议化带来的好处。
那 LSP 在这里扮演什么角色
LSP 这一条线没有 MCP 那么“开放生态”,但也很重要。
MCP 更像是:
- 让 Claude Code 接入外部系统
- 接入外部资源
- 接入外部工具
LSP 更像是:
- 让 Claude Code 接入语言服务器
- 拿到诊断、符号、引用、语义信息
也可以简单理解成:
- MCP 是向外扩能力
- LSP 是向内补语义
Claude Code 为什么这么重视 MCP
因为做 Agent 到最后,真正限制能力上限的,往往不是模型本身,而是:
- 能不能接公司内部系统
- 能不能接外部知识和资源
- 能不能把新能力无缝注入当前会话
MCP 正好解决的就是这件事。
它让 Claude Code 从“我会这些内置能力”变成:
我可以在运行时把更多能力接进来,而且还能用统一方式交给模型使用。
这就是 Claude Code 走向平台化最明显的标志之一。
小结
理解 Claude Code 里的 MCP,最重要的是抓住这 4 句话:
- MCP 接进来的不只是工具,还有资源、prompts 和 skills。
- Claude Code 会把这些外部能力统一翻译成自己的运行时对象。
- MCP 能力不是静态的,server 变了,会话能力也会动态刷新。
- Claude Code 对 MCP 不是“能接就接”,而是带着权限、缓存、交互和安全边界去接。
最后用一句最简单的话收住:
- MCP:把外部世界接进 Claude Code
- LSP:把语言智能接进 Claude Code