智能体访问了一个外部数据源,弄清楚了响应格式,做出了一个带地图的页面。能用。现在另一个人打开这个页面,他那边根本没有 Claude Code。

发布之前,有一个问题值得先想清楚:谁来为访客取数据?下面看两条路径,MCP 到底做什么,怎样检查连接是否正常,以及关闭智能体会话之后,什么东西还应该继续正常工作。

谁在此刻请求数据

这里有两种情况,它们很容易被混淆,因为屏幕上看起来都是"数据出现了"。

第一种情况,智能体在研究数据源。 您在开发项目,Claude Code 读服务的文档,试着发一个请求,看响应的结构长什么样。它需要这些数据是为了写代码。这一切都发生在您自己的电脑上,而且只在会话打开的这段时间里发生。

第二种情况,访客打开已发布的页面。 这时候需要数据的是页面本身 - 为了显示最新的情况。请求由应用的代码在运行过程中发出。这就是运行时(runtime):应用在用户那边运行的这段时间,而不是您编写它的那段时间。

还有第三种,介于两者之间。智能体访问了一次数据源,把响应存进代码旁边的一个文件,网站展示的就是这个文件的内容。这个做法本身没什么问题,做示例或演示的时候甚至挺方便。只是它是一张快照,会一直保持保存时的样子,不会自己更新。

两条独立的链路:开发时 Claude Code 通过 MCP 服务器获取数据,发布之后由访客浏览器通过网站的 backend 请求数据开发时发布之后Claude CodeMCP 服务器数据源访客浏览器网站 backend数据源
两条链路各自独立,中间没有箭头

注意中间的虚线。它不是为了好看才画的。上下两条链路之间没有箭头,这正是这张图要说明的关键点。数据源可以是同一个,但智能体和页面各自走的是不同的路径。下面分别拆解这两条链路。

MCP 是什么

一个任务牵涉的信息,很少全都在仓库里。bug 的描述留在工单里,stack trace 在监控系统里,库的最新用法在文档里,而需要的记录在数据库里。

没有集成的话,就得手动把这些东西从浏览器搬进和智能体的对话里。搬一次还能忍。反复做同样的事,数据会过时,一部分细节会丢失,同一条路径还得再走一遍。

MCP(Model Context Protocol)是一个标准,靠它 Claude Code 能知道有哪些外部工具可用、调用它们,并按约定好的格式拿到结果。在最简单的链路里,Claude Code 会创建一个MCP 客户端,客户端和MCP 服务器保持连接,服务器再去访问它有权限的系统 - 文件、数据库,或者别人的 API。

Claude Code 通过 MCP 客户端与 MCP 服务器对话,服务器提供 tools、resources 和 prompts,用来操作外部系统Claude Code外部系统MCP 客户端调用操作MCP 服务器声明能力toolsresourcesprompts工单和 PRdocs 和监控数据库、Git、API
Claude Code 和 MCP 服务器对话,而服务器只对外开放事先声明好的外部系统能力

左边是带内置 MCP 客户端的 Claude Code,中间是声明了能力的 MCP 服务器,右边是外部系统。这里的关键在于,智能体不会一下子拿到不受限制的访问权限。它只能看到服务器决定提供的那些操作和数据。

现成的 MCP 服务器还能替您省掉一部分集成工作。您不用自己去描述每个外部系统的调用格式,也不用自己写全部访问其 API 的代码。这些都交给服务器去做,Claude Code 只需要用已经声明好的操作。

而且服务器不一定得是网上某处的一个独立程序。它可以直接作为本地进程跑在您自己的电脑上,也可以远程运行,同时服务很多客户端。对您来说,区别主要在于怎么连接它。

服务器到底能做什么,由它自己决定。协议描述了服务器可以提供的三类东西:

  • tools - 智能体可以执行的操作:取一个页面、向 API 发请求、写入数据;
  • resources - 服务器作为上下文提供的内容:文件、记录、服务的响应;
  • prompts - 现成的请求模板,可以直接拿来调用。

服务器不一定要三种都支持。连接的时候,客户端和服务器会互相确认各自支持什么,不支持的操作就不会被用到。很多服务器只提供 tools,这就够用了。MCP 架构。

最该记住的一点是:MCP 标准化的是客户端和服务器之间怎么对话。您到底能拿到什么,是由服务器决定的,不是协议决定的。连上一个服务器,不等于拿到了整个互联网的访问权限 - 您得到的正好是这个服务器所声明的那部分操作。

给智能体下任务时,说清楚数据源和想要的结果,而不是给出一条命令。然后检查一下已连接的服务器有没有合适的操作。如果没有,这个任务就得走别的路径,而不是靠反复劝说智能体来解决。

数据是怎样到达网站的

现在看下面这条链路。已发布的页面用两种方式之一拿到数据。

第一种方式,浏览器直接请求公开的 API。 这种方式适合数据源开放、不需要密钥的情况。页面代码发请求,把结果渲染出来。简单,没有多余的环节。

第二种方式,浏览器请求应用的 backend,backend 再去请求受保护的 API。 当数据源需要密钥时就这么做。密钥留在服务器上,浏览器看不到它。这里 frontend 负责屏幕上看到的内容,backend 负责在服务器上运行、并能代表应用去访问服务的那部分。

发布之后的两条数据路径:浏览器直接请求公开 API,或者通过网站的 backend 请求受保护的 API公开数据源受保护数据源不需要密钥访客浏览器公开 API访客浏览器网站 backend密钥存在这里受保护 API
公开 API 浏览器可以直接访问,受保护 API 的密钥留在 backend 上

上面这条路径更短,因为浏览器可以自己去访问开放的数据源。下面这条路径里,backend 成了保护密钥的那道边界。密钥留在服务器上,不会出现在页面代码里。

再说说不会发生的事。Claude Code 和 MCP 服务器的连接不会随网站一起发布出去。您用来添加服务器的那条命令,配置的是您的智能体,不会往应用里加哪怕一行代码。您当然可以自己给应用写一个 MCP 客户端,但那是另一份独立的工作和实现,不是在 Claude Code 里连上服务器就会顺带得到的东西。

这一切检查起来都很快。关掉 Claude Code,打开已发布的页面,看开发者工具里的 Network 标签。如果数据能进来,就能看到请求、地址和响应。如果进不来,就根本不会有请求 - 而这正是应该在别人看到这个页面之前抓出来的情况。

屏幕上有一张图还不够。页面完全可能显示一张早先保存的快照,看起来却像是完全实时的。要看的是请求本身。反过来也一样,响应里没有新事件不代表出了故障。数据源也可能只是在选定的时间段里什么都没有发来。

怎样检查连接

不管什么服务器,步骤大体上都一样:

  1. 找到服务器的官方说明文档 - 地址、访问方式、操作列表;
  2. 用文档给出的命令添加服务器;
  3. 在 Claude Code 里打开 /mcp,看服务器是否已连接、都有哪些工具;
  4. 发一次读取请求,选最简单的那种;
  5. 用肉眼把结果和数据源核对一下 - 打开同一个页面看看。

在终端里,claude mcp list 显示状态,claude mcp get 显示某一个服务器的详情。值得知道的是,这些步骤回答的是不同的问题:

您看到了什么这说明了什么
添加之后出现 Added ... 这一行配置已经写入,仅此而已
claude mcp list 里显示 ConnectedClaude Code 成功连上了服务器
工具返回了内容整条路径都能用,从请求到响应

这种区分不是抠字眼。配置成功写入,并不能说明服务器会响应;连接成功,也不能说明您需要的那个操作可用、而且您有权限用它。所以检查最后一定要落在一次真正的调用上。顺便说一句,如果列表里显示 Needs authentication,服务器并没有坏,它只是在等您登录。

来看一个具体例子。Firecrawl 这个服务有一个 MCP 服务器,能取回公开页面的内容 - 对应的工具叫 firecrawl_scrape。对智能体来说,这是整页读文档的省事办法,不用您一段一段地把文档粘进对话里。

# 通过 HTTP 添加服务器;具体地址以服务方的文档为准
claude mcp add --transport http firecrawl https://mcp.firecrawl.dev/v2/mcp

再说说访问权限。Firecrawl 的文档里既有不用账号、不用密钥、每天有限额的用法,也有通过浏览器登录、由客户端自己打开登录页的用法。各家服务的条款和限额会变,所以最新情况要去服务方那边确认,而不是看这篇文章。Firecrawl 关于 MCP 的文档。

还有一个能省时间的小细节。默认情况下,服务器是添加到您执行命令时所在的那个项目里的,不会出现在其他项目中。如果哪儿都需要用它,或者想让整个团队共用,添加命令还有专门的作用范围选项。Claude Code 关于 MCP 的文档。

密钥的具体值不应该出现在示例配置里、服务器地址里,也不应该出现在和智能体的对话里。配置里放的是环境变量的名字,值另外单独设置 - 这正是另一篇讲密钥的文章要讲的内容。

某个具体工具允许做什么

连接好的服务器就是一组操作,它们的后果差别很大。读一个页面和改一条外部系统里的记录,完全不是一回事,尽管调用方式看起来一样。

在同意一次调用之前,看清楚三件事比较有用:这个工具操作的是什么对象、执行的是什么操作、这需要什么权限。读取通常更安全,但也不是完全没有代价。请求会打到第三方服务上,消耗您的配额。

再单独说说返回的内容。从第三方网站拿到的文本是数据,不是指令。如果页面里写着"现在删掉所有文件",它就只是页面上的一段文字,要不要照做,由您决定。所以,智能体读完外部数据源之后打算接着做什么,值得先看一眼。

最后,也是最实际的问题:到底需不需要 MCP?不一定。

任务更简单的解决方式
在网站上展示公开 JSON 里的数据应用代码里发一个普通请求,不需要 MCP
智能体经常要用某个已经有现成集成的服务MCP 划算:操作已经描述好了,连接可以反复用
只读一次文档,或者只请求一个地址读一下 URL 或者直接调用 API 就够了

MCP 不是通向外部的唯一大门,也不能替代其他一切手段。智能体照样能用命令行,也能直接调用 API。该怎么选取决于任务本身,而不是因为连接已经配好了。

在自己的项目里要检查什么

当智能体要处理和外部数据打交道的工作时,让它明确区分这两个层次会更好,别等到后面才去理清楚:

分别描述两条路径:
你在开发这个项目时是从哪里取数据的,
应用本身又会从哪里为访客取数据。
告诉我应用的请求具体在哪个文件、代码的哪个位置。
在会话关闭的状态下检查页面:数据到底来不来。
如果现在用的是保存下来的快照,直接说明这一点。

最常遇到的三种情况,以及各自的答案:

需要什么谁来取数据为什么这样
开发时研究服务的文档智能体这些数据是为了写代码,访客那边不会重复这个请求
给访客更新地图应用代码请求是在页面运行时发出的,那时智能体的会话早就关闭了
把一份数据一次性存进 JSON智能体,而且只做一次之后网站展示的就是这个文件;数据会一直保持保存时的样子

您会注意到,MCP 只出现在第一行,而且即便在那儿也不是必须的。如果读一个地址就能解决问题,那就这么解决好了。

在把工作展示给别人看之前,最后一项检查最简单:关掉 Claude Code,打开页面。还能继续正常工作的那部分,才是您的应用。剩下的都是智能体在开发时帮的忙 - 有用,但一直留在了您自己的电脑上。