跳到正文
astorlm
语言: 简体中文
← 地图

第 10 关

MCP

到目前为止,每个工具都是你写进自己智能体里的函数。MCP 是一个标准插头:任何程序都能通过它提供工具,任何智能体都能用。
1/19 手风琴褶数:
  • user
  • assistant
  • tool_result
Dream Land。两个 MCP 服务器站在各自的门前:weather 和 maps,各是一个独立的程序。Kirby 是智能体里的 MCP 客户端,用同一张嘴就能接上任何服务器。

EventBus

问题

智能体要接触的每一个服务(日历、代码仓库、数据库、浏览器),都意味着手写一个工具:它的 schema、代码、错误处理。然后下一个智能体,或下一个应用,又把同样的工具重写一遍。而服务一变,每份拷贝都各自坏掉。

解决方案

Model Context Protocol(MCP)把两边分开。MCP 服务器是提供工具的程序,由构建服务的人写一次。MCP 客户端住在智能体里,负责说这个协议。它们可以跑在同一台机器上,服务器作为子进程通过 stdin 和 stdout 通信;也可以相隔很远,通过 HTTP 通信。对话总是同样的三步:

  • initialize

    握手:客户端和服务器互报身份,以及各自说的是哪个版本的协议。

  • tools/list

    服务器描述它的工具:名称、描述和输入 schema,正是第 3 关讲的那三样东西。

  • tools/call

    每当模型要用其中一个,客户端就发出工具名和参数,再拿回结果。

你拿到的工具就是普通工具。它们加入随每个请求发送的工具列表,循环像运行其他工具一样运行它们,模型也分辨不出它们从哪儿来。而且因为插头是标准的,同一个服务器在 Claude Code、Codex、Cursor、VS Code 或你自己的智能体里都能用。

角色

还是那群熟悉的角色,这次在 Dream Land,还多了一位新朋友。

卡比 MCP 客户端
你智能体的一部分。一张能对上任何服务器的嘴,复制每个服务器会做的事。
敌人 MCP 服务器
各自独立的程序,各守一扇门。卡比复制完后它们会回到门前:它们还在继续运行。
能力 tools/list
PARASOL 和 WHEEL:每个服务器描述的工具,现在出现在模型读取的状态栏里。
线缆 协议
每个服务器用的都是同一种插头。一个 tools/call 顺着线缆过去,结果再顺着回来。
Astor 循环
照常跑循环,把每个 MCP 调用交给卡比。
神谕者 模型
按名称和描述从工具箱里挑选,不管是不是 MCP。

代码

使用 astorlm:mountMcpServer() 负责连接、列出工具并做适配,工具名为 <服务器>__<工具>,这样两个服务器就不会撞名。它的 tools 像其他工具一样放进智能体,close() 负责挂断。

从零手写:一个 stdio 客户端就是一个子进程加 JSON-RPC,每行一个 JSON 对象:握手、tools/list,再为每个工具写一个发送 tools/call 的函数。然后把这些工具放进第 2 关的循环。

import { OpenAIProvider, createLocalAgent, mountMcpServer } from 'astorlm'

// 1. Mount each MCP server: connect, ask for its tools (tools/list), adapt them.
//    Each one is a separate program; nobody wrote its schemas in your code.
const weather = await mountMcpServer({
  name: 'weather', // becomes the prefix: weather__forecast, weather__alerts
  transport: { type: 'stdio', command: 'npx', args: ['-y', 'dreamland-weather-mcp'] }, // runs it as a child process
})

const maps = await mountMcpServer({
  name: 'maps',
  transport: { type: 'http', url: 'https://maps.dreamland.example/mcp' }, // or a server that's already running
})

const agent = await createLocalAgent({
  // Any OpenAI-compatible endpoint: OpenAI, Ollama, LM Studio, vLLM, a proxy…
  provider: new OpenAIProvider({
    baseURL: 'http://localhost:11434/v1', // e.g. Ollama's default address
    model: 'your-model', // e.g. 'llama3.1', 'gpt-4o-mini'
    apiKey: 'YOUR_API_KEY', // local servers usually ignore it
  }),
  // 2. MCP tools are tools: mix them with your own, or keep only the ones you need.
  tools: [...weather.tools, ...maps.tools],
  maxTurns: 10,
})

agent.on('tool-start', ({ name }) => console.log('→', name)) // maps__route, then weather__forecast

try {
  await agent.run('Picnic at the Fountain of Dreams tomorrow. How do I get there from Cappy Town, and will it rain?')
} finally {
  // 3. Hang up when you're done: a stdio server is a process of yours.
  await Promise.all([weather.close(), maps.close()])
}

注意事项

  • 每个工具都会随每个请求发送。一个有四十个工具的服务器,每一轮都会多出四十份 schema,列表太长也会让模型更难选对。只挂载需要的服务器,只传入用得上的工具:weather.tools.filter(…)。
  • 服务器是你运行的代码。stdio 服务器以你的权限、在你的机器上运行。把它当成依赖对待:从可信的来源安装,并锁定版本。
  • 描述也是指令。模型会把每条工具描述当成你写的来读,把每个结果当成可能夹带命令的数据。一个你控制不了的服务器,可以通过这两条路中的任何一条操纵你的智能体(第 18 关)。
  • 远程服务器需要凭证。走 HTTP 时,在 headers 里传一个 token,权限只限于智能体需要的范围,绝不要用你自己那把万能钥匙。
  • 打开的要记得关。stdio 服务器的寿命和它的连接一样长。智能体用完就关掉,否则它会在后台一直跑下去。