前端直连模型 vs 完整 MCP:大模型驱动地图的原理与实践(技术栈Vue + Cesium + Node.js + WebSocket + MCP)

适合读者:完全新手、前端开发者、对大模型工具调用感兴趣的工程师

技术栈示例:Vue + Cesium + Node.js + WebSocket + MCP

教程目标:看懂并搭建一套"用户通过聊天输入指令,大模型决定调用工具,再驱动地图执行动作"的完整链路


目录

  • [1. 这篇教程要解决什么问题](#1. 这篇教程要解决什么问题)
  • [2. 先别写代码:先搞懂两个很像但本质不同的方案](#2. 先别写代码:先搞懂两个很像但本质不同的方案)
    • [2.1 方案一:前端直连模型](#2.1 方案一:前端直连模型)
    • [2.2 方案二:真正完整的 MCP](#2.2 方案二:真正完整的 MCP)
    • [2.3 它们最核心的区别](#2.3 它们最核心的区别)
  • [3. 为什么很多人一开始会把两套方案混在一起](#3. 为什么很多人一开始会把两套方案混在一起)
  • [4. 先建立整体认知:完整 MCP 里有哪些角色](#4. 先建立整体认知:完整 MCP 里有哪些角色)
  • [5. 完整 MCP 的时序图:一句"飞到上海"是怎么穿过整个系统的](#5. 完整 MCP 的时序图:一句“飞到上海”是怎么穿过整个系统的)
  • [6. 为什么这个架构更适合地图场景](#6. 为什么这个架构更适合地图场景)
  • [7. 本教程的 demo 架构](#7. 本教程的 demo 架构)
  • [8. 实战之前,先看一遍项目职责拆分](#8. 实战之前,先看一遍项目职责拆分)
  • [9. 实战搭建:从零把链路跑通](#9. 实战搭建:从零把链路跑通)
    • [9.1 第一步:浏览器只负责执行地图动作](#9.1 第一步:浏览器只负责执行地图动作)
    • [9.2 第二步:Node 负责 MCP Server + WebSocket 桥接](#9.2 第二步:Node 负责 MCP Server + WebSocket 桥接)
    • [9.3 第三步:Host 负责"模型 ↔ 工具"循环](#9.3 第三步:Host 负责“模型 ↔ 工具”循环)
    • [9.4 第四步:把"终端输入"升级成"页面输入"](#9.4 第四步:把“终端输入”升级成“页面输入”)
  • [10. 两种页面聊天方案对比:哪种是"真 MCP"](#10. 两种页面聊天方案对比:哪种是“真 MCP”)
  • [11. 一次完整调用的分层理解](#11. 一次完整调用的分层理解)
    • [11.4 把读者最容易追问的几个问题串起来理解](#11.4 把读者最容易追问的几个问题串起来理解)
  • [12. 新手最容易踩的坑](#12. 新手最容易踩的坑)
  • [13. 你可以如何扩展这套地图能力](#13. 你可以如何扩展这套地图能力)
  • [14. 学完这篇教程后,你应该真正记住什么](#14. 学完这篇教程后,你应该真正记住什么)

1. 这篇教程要解决什么问题

很多人第一次做"大模型驱动地图"的时候,都会有一个非常自然的想法:

我在网页上放一个聊天框,用户输入"飞到上海",然后把这句话发给模型;模型决定调用工具;前端收到工具调用后,直接控制地图飞过去。

这个想法并不算错,而且它确实能跑。

但问题在于:这不一定是完整的 MCP 架构。

如果你后面想把地图能力做成:

  • 可以被不同宿主统一调用
  • 可以被大模型按工具方式自动发现
  • 可以在前端、桌面、服务端之间分层复用
  • 可以持续扩展成"通用地图执行器"

那你很快就会发现:

"前端直接请求模型并本地执行工具" ,和
"真正完整的 MCP 链路",其实是两套不同的设计思路。

这篇教程,就是要把这两件事讲清楚。

我们会先讲原理,再讲架构,最后用一个 Vue + Cesium + Node + MCP 的最小 demo 把整个链路跑起来。


2. 先别写代码:先搞懂两个很像但本质不同的方案

2.1 方案一:前端直连模型

这是最容易想到、也最容易快速跑通的一种方式。

它的基本流程是:

  1. 用户在网页输入"飞到上海"
  2. 前端把用户消息和工具定义一起发给模型
  3. 模型返回:要调用 flyToShanghai
  4. 前端收到 tool_calls
  5. 前端本地执行 flyToShanghai
  6. 前端控制 Cesium 飞到上海
  7. 前端再把工具结果发回模型
  8. 模型生成最终回复

它的时序图长这样:

text 复制代码
用户
-> 前端页面
-> 大模型 API
-> 前端页面(收到 tool_calls)
-> 前端本地工具执行
-> Cesium 地图
-> 前端页面
-> 大模型 API
-> 前端页面
-> 用户

这个方案的优点非常明显:

  • 容易理解
  • 容易开发
  • 页面交互很直接
  • 没有额外的 Host 层

但它也有一个根本特点:

前端同时承担了"聊天客户端"和"工具执行器"两种角色。

这就是后面很多结构混乱的起点。


2.2 方案二:真正完整的 MCP

完整 MCP 不是前端自己决定怎么调用工具,而是由一个 Host 来负责:

  • 和用户对话
  • 和模型交互
  • 把工具提供给模型
  • 在模型决定调用工具后,真正去调用 MCP Server
  • 再把结果回灌给模型

在这个架构里,浏览器不再负责"模型调度",而只负责"地图动作执行"。

完整链路如下:

text 复制代码
用户
-> Host
-> 模型
-> Host
-> Node MCP Server
-> 浏览器
-> Cesium
-> 浏览器
-> Node MCP Server
-> Host
-> 模型
-> Host
-> 用户

这条链路里最关键的一句是:

模型不直接调用 Node。Host 才是真正替模型调用工具的人。

这是理解 MCP 的核心。


2.3 它们最核心的区别

很多人会觉得:

"前端直连模型"和"完整 MCP"不都是模型调用工具吗?

表面上看都像"工具调用",但真正的区别在于:

维度 前端直连模型 完整 MCP
谁和模型对话 前端页面 Host
谁拿到 tool_calls 前端页面 Host
谁真正调工具 前端页面 Host
Node 在哪里 可以没有 是 MCP Server
浏览器扮演什么 聊天端 + 执行端 纯执行端
是否符合完整 MCP 分层 不一定

最通俗的说法是:

  • 前端直连模型:页面自己又当指挥,又当工人
  • 完整 MCP:Host 当指挥,浏览器只负责干活

3. 为什么很多人一开始会把两套方案混在一起

因为它们在表面上太像了。

无论是哪种方案,你都会看到类似这些词:

  • tools
  • tool_calls
  • function calling
  • flyToShanghai
  • 地图飞行

于是就很容易误以为:

"我已经把 tools 发给模型了,模型也返回要调用的工具了,那我就是在做 MCP。"

实际上不一定。

真正的问题不是"有没有工具",而是:

谁在承担工具调度这件事。

如果是前端自己在做调度,那你更接近"前端直连模型 + 本地执行工具"。

如果是 Host 在调度,Node 暴露 MCP 工具,浏览器只执行动作,那才是完整 MCP。


4. 先建立整体认知:完整 MCP 里有哪些角色

在地图场景里,我们可以把系统拆成四个角色。

4.1 用户

负责说自然语言,比如:

  • 飞到上海
  • 把镜头拉近一点
  • 在当前位置加一个标记

用户只关心结果,不关心内部链路。

4.2 Host

Host 是这套系统的大脑调度层。

它负责:

  • 接收用户输入
  • 请求大模型
  • 把工具列表告诉模型
  • 当模型决定调用工具时,真正去调用 MCP Server
  • 把工具结果再喂回模型
  • 把最终自然语言回复返回给用户

你可以把 Host 理解成:

"懂模型、懂工具、懂对话流程"的中控台。

4.3 MCP Server

MCP Server 负责把系统能力暴露成标准工具。

比如:

  • flyToShanghai
  • flyToLocation
  • pingBrowser
  • addPoint
  • drawPolyline

它不直接负责和用户聊天,也不负责渲染地图。

它只负责:

"我这里有这些工具,你要调哪个,我帮你转成真实动作。"

4.4 浏览器 + Cesium

浏览器里的地图页面不再和模型直接对话,而是只做一件事:

接收动作命令,并在地图里执行。

比如收到:

json 复制代码
{
  "method": "flyTo",
  "params": {
    "longitude": 121.4737,
    "latitude": 31.2304,
    "height": 8000
  }
}

浏览器就调用 Cesium 相机飞行。

所以浏览器的角色是:

地图动作执行器。


5. 完整 MCP 的时序图:一句"飞到上海"是怎么穿过整个系统的

下面这张图,是整篇教程最重要的一张图。

text 复制代码
┌──────┐   ┌───────────────┐   ┌──────────┐   ┌──────────────┐   ┌──────────┐   ┌────────────┐   ┌─────────┐
│ 用户 │   │   MCP Host    │   │   模型    │   │  Node MCP    │   │ WebSocket │   │ 浏览器前端  │   │ Cesium  │
│      │   │               │   │  (LLM)   │   │   Server     │   │   通道     │   │ + Bridge    │   │ Scene   │
└─┬────┘   └──────┬────────┘   └────┬─────┘   └──────┬───────┘   └────┬─────┘   └─────┬──────┘   └────┬────┘
  │ "飞到上海"         │                   │                 │                  │                │               │
  │──────────────────>│                   │                 │                  │                │               │
  │                   │ 把用户消息发给模型   │                 │                  │                │               │
  │                   │──────────────────>│                 │                  │                │               │
  │                   │                   │ 判断需要用工具    │                  │                │               │
  │                   │<──────────────────│ tool_call:       │                  │                │               │
  │                   │                   │ flyToShanghai    │                  │                │               │
  │                   │ 调 Node MCP 工具   │                 │                  │                │               │
  │                   │────────────────────────────────────>│                  │                │               │
  │                   │                   │                 │ sendToBrowser()  │                │               │
  │                   │                   │                 │─────────────────>│                │               │
  │                   │                   │                 │                  │ JSON-RPC 消息   │               │
  │                   │                   │                 │                  │───────────────>│               │
  │                   │                   │                 │                  │                │ bridge.execute│
  │                   │                   │                 │                  │                │──────────────>│
  │                   │                   │                 │                  │                │ 相机飞到上海   │
  │                   │                   │                 │<─────────────────│ 执行结果        │               │
  │                   │<────────────────────────────────────│ tool result      │                │               │
  │                   │ 再把结果发给模型     │                 │                  │                │               │
  │                   │──────────────────>│                 │                  │                │               │
  │                   │<──────────────────│ 返回自然语言回复  │                  │                │               │
  │<──────────────────│                   │                 │                  │                │               │

这张图里最值得你盯住的两个点是:

5.1 模型并没有直接连 Node

模型只是返回:

我建议调用 flyToShanghai

真正去执行 callTool() 的,是 Host。

5.2 浏览器并不参与"思考"

浏览器只负责:

  • 收消息
  • 执行动作
  • 回结果

它不负责选择工具,也不负责和模型多轮推理。


6. 为什么这个架构更适合地图场景

地图类应用和普通文本工具类应用有一个很大的区别:

地图动作通常必须在浏览器或图形环境里执行。

比如:

  • 相机飞行
  • 地物绘制
  • 图层显示隐藏
  • 交互式选点
  • 实时动画

这些事情并不适合让 Node 自己去做。

Node 很适合做:

  • 协议层
  • 工具注册
  • 连接管理
  • 请求转发
  • 结果聚合

浏览器很适合做:

  • 渲染
  • 动画
  • 场景执行
  • 与 Cesium 交互

所以最自然的分工就是:

  • Host:负责智能调度
  • Node MCP Server:负责能力暴露与转发
  • 浏览器:负责地图执行

这比"前端自己包办一切"要更清晰,也更容易扩展。


7. 本教程的 demo 架构

我们用一套很小的 demo 来实现完整链路。

技术角色如下:

前端页面(Vue + Cesium)

负责:

  • 初始化 Cesium
  • 连接 Node 发来的 WebSocket 消息
  • 收到动作后调用 MiniBridge.execute()
  • 真正控制地图飞行

MiniBridge

负责把通用动作转成 Cesium API。

比如:

  • flyTo -> viewer.camera.flyTo(...)

Node index.ts

负责:

  • 启动 WebSocket Server
  • 启动 MCP Server
  • 注册 flyToShanghai / pingBrowser
  • 收到工具调用后把动作转发给浏览器

Host

最初可以是终端版 host.ts,后面再升级成页面版 host-web.ts

它负责:

  • 和模型对话
  • 获取 MCP 工具列表
  • 调用 MCP 工具
  • 把工具结果回灌给模型

8. 实战之前,先看一遍项目职责拆分

在真正开始编码之前,我们先把职责拆成三层。

第 1 层:执行层

执行层在浏览器里。

它只负责:

  • 连接 WebSocket
  • 执行收到的地图命令
  • 返回结果

第 2 层:工具层

工具层在 Node 里。

它只负责:

  • 注册工具
  • 维护会话
  • 把工具调用转发给浏览器

第 3 层:智能调度层

智能调度层在 Host 里。

它只负责:

  • 和模型交互
  • 决定是否调用工具
  • 触发 MCP 调用
  • 收尾生成自然语言回答

把职责拆干净以后,你会发现整个系统其实并不复杂。

复杂感大多来自于:

一开始把前端、模型、工具、地图执行全写在一个文件里。


9. 实战搭建:从零把链路跑通

下面开始进入真正的搭建过程。


9.1 第一步:浏览器只负责执行地图动作

这一层你可以理解成一个"地图机器人"。

它不思考,只执行。

一个非常典型的桥接器长这样:

js 复制代码
class MiniBridge {
  constructor(viewer) {
    this.viewer = viewer
  }

  async execute(cmd) {
    const action = cmd?.action
    const params = cmd?.params || {}

    switch (action) {
      case 'flyTo':
        await this.flyTo(params)
        return { success: true }
      default:
        return { success: false, error: `Unsupported action: ${action}` }
    }
  }

  flyTo(params) {
    const { longitude, latitude, height = 8000 } = params

    return new Promise((resolve) => {
      this.viewer.camera.flyTo({
        destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
        complete: resolve,
      })
    })
  }
}

这个桥的意义在于:

  • 上层不用直接知道 Cesium API
  • 上层只需要说"执行 flyTo"
  • 浏览器负责把它翻译成具体地图动作

这一步很关键,因为它让地图执行能力变成了一个可复用的动作接口。


9.2 第二步:Node 负责 MCP Server + WebSocket 桥接

这一步是整个架构的中间层。

Node 做两件事:

9.2.1 注册 MCP 工具

例如:

ts 复制代码
server.tool(
  'flyToShanghai',
  'Fly camera to Shanghai in connected browser Cesium scene.',
  {
    height: z.number().default(8000),
    duration: z.number().default(2),
  },
  async ({ height = 8000, duration = 2 }) => {
    const result = await sendToBrowser('flyTo', {
      longitude: 121.4737,
      latitude: 31.2304,
      height,
      duration,
      heading: 0,
      pitch: -45,
    })

    return {
      content: [{ type: 'text', text: JSON.stringify(result ?? { success: true }) }],
    }
  },
)

这里看起来像是在"飞到上海",但其实 Node 没有自己控制地图。

它做的只是:

  • 把工具名称暴露给外界
  • 收到工具调用时,转发一个 flyTo 命令给浏览器
9.2.2 通过 WebSocket 把动作发给浏览器

核心逻辑是这样的:

ts 复制代码
function sendToBrowser(method, params, timeoutMs = 15000) {
  return new Promise((resolve, reject) => {
    const ws = getBrowser(DEFAULT_SESSION)
    if (!ws || ws.readyState !== WebSocket.OPEN) {
      reject(new Error('No browser connected.'))
      return
    }

    const id = `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`

    pendingRequests.set(id, { resolve, reject, timer })

    ws.send(JSON.stringify({
      jsonrpc: '2.0',
      id,
      method,
      params,
    }))
  })
}

它的本质是:

把 MCP 工具调用转成浏览器可执行的动作消息。


9.3 第三步:Host 负责"模型 ↔ 工具"循环

这是完整 MCP 里最容易被忽略,但又最关键的一层。

Host 的工作流程是:

  1. 收到用户输入
  2. 请求模型
  3. 把工具列表发给模型
  4. 模型返回 tool_calls
  5. Host 调用 MCP Server
  6. Host 拿到工具结果
  7. Host 再把结果发给模型
  8. 模型生成最终回复

这个过程可以抽象成这样:

ts 复制代码
const tools = await client.listTools()

const first = await callChatCompletions(messages, openAITools)
const toolCalls = first?.choices?.[0]?.message?.tool_calls || []

for (const tc of toolCalls) {
  const result = await client.callTool({
    name: tc.function.name,
    arguments: JSON.parse(tc.function.arguments || '{}'),
  })

  secondMessages.push({
    role: 'tool',
    tool_call_id: tc.id,
    content: toolResultToText(result),
  })
}

const second = await callChatCompletions(secondMessages, openAITools)

很多新手在这里第一次真正理解到:

Host 是连接"模型世界"和"工具世界"的那个人。


9.4 第四步:把"终端输入"升级成"页面输入"

一开始,为了最小验证链路,你可以用终端版 Host:

  • 在终端输入"飞到上海"
  • Host 去请求模型
  • Host 再调用 MCP Server

这是最容易验证完整链路的方式。

但最终用户肯定希望在网页输入,而不是在终端输入。

这时,正确思路不是把"前端直连模型"改回来,而是:

给 Host 再包一层 HTTP 接口,让页面把聊天请求发给 Host。

于是链路就变成:

text 复制代码
页面输入
-> Host Web API
-> 模型
-> Host Web API
-> Node MCP Server
-> 浏览器执行地图动作
-> Node MCP Server
-> Host Web API
-> 模型
-> 页面

这一步很重要,因为它保证了:

  • 页面输入回来了
  • 但完整 MCP 链路没有被破坏

也就是说,页面输入完整 MCP 是可以同时成立的。


10. 两种页面聊天方案对比:哪种是"真 MCP"

到了这里,很多人会问:

"那页面聊天到底有几种实现方式?"

答案是两种。

10.1 方式 A:页面直接请求模型

text 复制代码
页面
-> 模型
-> 页面收到 tool_calls
-> 页面本地执行工具
-> 页面再请求模型

这种方式好处是简单,但页面承担了太多职责。

10.2 方式 B:页面请求 Host Web API

text 复制代码
页面
-> Host Web API
-> 模型
-> Host Web API
-> MCP Server
-> 浏览器地图执行
-> Host Web API
-> 模型
-> 页面

这种方式更符合完整 MCP,因为页面没有直接参与工具调度。

一句话判断标准

如果页面自己在处理 tool_calls,那通常不是完整 MCP。

如果页面只是把用户输入交给 Host,然后等待回复,那更接近完整 MCP。


11. 一次完整调用的分层理解

当用户在页面输入"飞到上海"时,可以从三个层面来看这件事。

11.1 语义层

模型在理解:

  • 用户想去哪里
  • 是否需要调用工具
  • 应该调用哪个工具

11.2 工具层

Host 和 MCP Server 在处理:

  • 工具发现
  • 工具参数
  • 工具调用
  • 结果返回

11.3 执行层

浏览器和 Cesium 在处理:

  • 相机控制
  • 地图飞行
  • 动画执行
  • 场景渲染

你把这三层分开看,就会觉得整个系统非常清晰。

11.4 把读者最容易追问的几个问题串起来理解

在真实学习过程中,很多人不会一下子就被"架构图"讲明白,反而会在写 demo、跑代码、看日志的时候不断冒出一些非常具体的问题。

这些问题看起来零散,但其实正好能帮助你把完整 MCP 理解得更扎实。

下面把几个最典型的问题串起来讲。

问题一:"大模型和 Node 这条链路到底怎么连起来的?"

这是最常见的困惑。

很多人第一次看到 index.ts 里的 McpServer,会本能地以为:

模型是不是直接连到了这个 Node 服务?

其实不是。

更准确的链路是:

text 复制代码
用户 -> Host -> 模型 -> Host -> Node MCP Server -> 浏览器 -> Cesium

也就是说:

  • 模型只负责"决定要不要调用工具"
  • Host 才是"真正去调用 MCP 工具的人"
  • Node MCP Server 只是"被调用的工具服务"
  • 浏览器只是"执行地图动作的地方"

所以真正连模型和 Node 的,不是模型自己,而是 Host

问题二:"host-web.ts 里没有把工具传给 StdioClientTransport,那工具是怎么来的?"

这也是一个特别典型的误解。

很多新手看到这段代码时,会以为:

ts 复制代码
const transport = new StdioClientTransport({
  command: getNpxCommand(),
  args: ['tsx', 'index.ts'],
})

是不是已经把工具传进去了?

其实没有。

这里的意思只是:

  • tsx index.ts 启动一个 MCP Server 进程
  • 再让 Host 通过 stdio 和这个进程通信

args 不是工具内容本身,它只是告诉 transport:去启动哪一个 Server。

真正的工具读取发生在后面:

ts 复制代码
await client.connect(transport)
const toolsRes = await client.listTools()
toolsCache = toolsRes.tools || []
openAITools = toolsCache.map(mcpToolToOpenAITool)

你可以把这几步理解成:

  1. args:找到餐厅地址
  2. connect:走进餐厅
  3. listTools:向餐厅要菜单
  4. openAITools:把菜单整理成模型能看懂的格式

所以工具并不是被"静态读取源码"拿到的,而是 index.ts 运行起来以后,通过 server.tool(...) 向外暴露,再由 Host 用 listTools() 动态读取出来的。

问题三:"后面看起来不是和前端直连一样吗?不也还是把 tools 发给模型吗?"

这个问题问得非常好,因为它正好点出了"哪里像、哪里不一样"。

答案是:

在模型接口这一层,确实很像;在系统架构这一层,本质上不一样。

两种方案后面都会做这件事:

text 复制代码
messages + tools -> model -> tool_calls

所以从模型的视角看,它们很像。

但系统架构上差别很大:

前端直连模型
  • 工具定义在前端
  • 工具由前端发给模型
  • 模型返回 tool_calls
  • 前端本地执行工具
完整 MCP
  • 工具定义在 MCP Server
  • Host 用 listTools() 动态发现工具
  • Host 把工具发给模型
  • 模型返回 tool_calls
  • Host 再用 callTool() 调 MCP Server
  • 浏览器只负责执行动作

所以更准确地说:

完整 MCP 不是"不把工具发给模型",而是把"工具定义权"和"工具执行调度权"从前端抽离了出来。

问题四:"既然终端里输入能飞到上海,为什么还要做页面输入?"

这是从"验证链路"过渡到"真正产品形态"的关键一步。

一开始用终端输入,是为了验证最小闭环:

text 复制代码
终端输入 -> Host -> 模型 -> Host -> MCP Server -> 浏览器 -> Cesium

当这条线通了以后,你已经证明:

  • Host 没问题
  • MCP Server 没问题
  • 浏览器执行链没问题

这时候再把"终端输入"换成"页面输入",本质上不是重新发明一套逻辑,而是把 Host 的入口从命令行换成 HTTP API

text 复制代码
页面输入 -> host-web.ts -> 模型 -> host-web.ts -> MCP Server -> 浏览器 -> Cesium

所以页面输入版的核心不是"让前端重新直连模型",而是:

页面把用户输入交给 Host,由 Host 继续完成完整 MCP 链路。

问题五:"我到底该怎么判断自己现在是不是在走完整 MCP?"

这个问题非常重要,因为很多 demo 表面上看都叫"工具调用",但架构上完全不是一回事。

最实用的判断标准只有一句:

看工具定义写在哪里,工具调度由谁负责。

如果是这样:

  • 前端自己写 getTools()
  • 前端自己发给模型
  • 前端自己执行 runLocalTool()

那本质上还是前端直连。

如果是这样:

  • index.tsserver.tool(...) 定义工具
  • Host 通过 listTools() 发现工具
  • Host 通过 callTool() 调用工具
  • 浏览器只负责执行动作

那才是完整 MCP。

你可以把这个判断标准记成一句话:

前端直连看的是"页面会不会调工具",完整 MCP 看的是"工具是不是独立存在于 MCP Server 里"。

问题六:"为什么我一开始总觉得两套方案混在一起?"

因为在学习初期,大家最先看到的都是这些显眼的词:

  • tools
  • tool_calls
  • flyToShanghai
  • bridge.execute
  • WebSocket

这些词在两套架构里都出现,所以很容易产生一种错觉:

"我都在用 tools 了,那我就是在做 MCP。"

真正需要区分的,其实不是关键词,而是角色分工

  • 谁是 Host
  • 谁是 MCP Server
  • 谁是浏览器执行器
  • 谁才是真正的工具源头

一旦你把角色看清楚,之前那些零散的疑问就会突然全部串起来。


你会发现,上面这些问题并不是无关的"零碎提问",而是学习过程中非常自然的一条理解升级路径:

  1. 先困惑模型和 Node 的关系
  2. 再困惑工具是怎么被发现的
  3. 再进一步意识到"为什么看起来和前端直连很像"
  4. 最后才真正明白:完整 MCP 的价值,不在"有没有 tools",而在工具定义、工具调度和动作执行被拆成了独立角色

12. 新手最容易踩的坑

12.1 把"tool calling"误以为就是 MCP

不是的。

tool calling 是模型返回工具调用决策。

MCP 是一整套 client-server 协议和角色分层。

12.2 让浏览器既当聊天端又当执行端

短期能跑,长期一定会乱。

因为你会发现:

  • 页面越来越重
  • 工具扩展越来越难
  • 复用性越来越差

12.3 误以为模型直接调用 Node

模型不会自己去连 Node。

真正调用 Node MCP 工具的是 Host。

12.4 忘了浏览器必须先连上 WebSocket

如果浏览器没有连上,Node 收到工具调用也没法执行地图动作。

12.5 同时起了两个 Server,导致端口冲突

比如 9100 已经被占用,再起一个同样监听 9100 的进程,就会报端口占用错误。


13. 你可以如何扩展这套地图能力

当"飞到上海"跑通以后,这套架构就已经具有扩展性了。

你可以继续加这些工具:

13.1 通用飞行类

  • flyToLocation({ longitude, latitude, height })
  • setCameraView(...)
  • lookAt(...)

13.2 地图绘制类

  • addPoint(...)
  • addPolyline(...)
  • addPolygon(...)

13.3 图层控制类

  • showLayer(...)
  • hideLayer(...)
  • toggleLayer(...)

13.4 场景查询类

  • getCurrentCamera()
  • getSelectedEntity()
  • pingBrowser()

13.5 复杂业务类

  • 无人机巡航
  • 路线规划
  • 智慧城市场景切换
  • 事件定位与高亮

这时候你会真正体会到完整 MCP 架构的价值:

你扩展的是"工具能力",而不是把业务越写越塞进页面里。


14. 学完这篇教程后,你应该真正记住什么

如果你只记住一句话,请记这句:

完整 MCP 不是"模型自己调地图",而是"Host 根据模型决策去调工具,再让浏览器执行地图动作"。

如果你再多记三句,那就是:

  1. 前端直连模型 能跑,但不等于完整 MCP
  2. Host 是"模型和工具之间的真正调度者"
  3. 浏览器 在地图场景里最适合做执行器,而不是全能中枢

最后,把两套方案再浓缩成最简版本:

前端直连模型

text 复制代码
页面自己又当聊天端,又当工具执行端

完整 MCP

text 复制代码
Host 负责调度,Node 负责工具,浏览器负责执行

这就是这篇教程想真正教会你的东西。


结语

从"让地图飞到上海"这件小事出发,你其实已经接触到了一个很有代表性的工程问题:

当大模型开始接管工具调用时,系统应该怎么分层,才不会越做越乱?

地图场景给了我们一个很典型的答案:

  • 把思考留给模型
  • 把调度交给 Host
  • 把能力暴露给 MCP Server
  • 把渲染执行留给浏览器

这样搭起来的系统,才真正适合继续长大。

如果你已经把"飞到上海"跑通了,那么下一步,不是继续把代码堆在页面里,而是:

把更多地图能力,整理成一套干净的 MCP 工具体系。

这时,你就真正迈进了"通过大模型聊天驱动地图系统"的工程化阶段。

相关推荐
2501_921649492 小时前
WebSocket 金融实时行情推送 API 实战解析:低延迟、高可用架构设计与落地
websocket·网络协议·金融·node.js
hljqwb2 小时前
Node.js NativeAddon 构建工具:node-gyp 安装与配置完全指南
node.js
鹏程十八少2 小时前
10. Android Shadow是如何实现像tinker热修复动态修复so(源码解析)
android·前端·面试
吴声子夜歌2 小时前
Node.js——事件的监听与触发
node.js
destinying2 小时前
性能优化之项目实战:从构建到部署的完整优化方案
前端·javascript·vue.js
我命由我123452 小时前
React - React Redux 数据共享、Redux DevTools、React Redux 最终优化
前端·javascript·react.js·前端框架·ecmascript·html5·js
英俊潇洒美少年2 小时前
数据驱动视图 vue和react对比
javascript·vue.js·react.js
面包爱阜阳2 小时前
使用 GitHub 和 Vercel 部署个人网站
node.js·github
Jinuss2 小时前
源码分析之React中的createContext/useContext详解
前端·javascript·react.js