掘金 人工智能 04月29日 10:28
MCP(下)——跟着官方实现一个MCP
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入浅出地介绍了MCP(Model Context Protocol)的概念和运作机制,通过一个实际的例子,帮助读者理解MCP如何让大模型与外部世界交互。文章详细阐述了MCP-client和MCP-server的核心实现,并分析了MCP带来的好处,强调了其在促进生态发展方面的作用。

💡MCP是一个开放协议,它标准化了应用程序如何为大语言模型(LLMs)提供上下文信息。本质上,MCP通过function call机制,允许大模型与外部世界进行交互,从而扩展其能力。

⚙️MCP-client负责与MCP-server建立连接,获取工具列表,并将用户问题和工具调用结果传递给大模型。它通过StdioClientTransport类,利用标准输入输出(stdin/stdout)与MCP服务器进程通信。

🛠️MCP-server负责注册工具、处理工具调用请求,并返回结果给MCP-client。工具的定义包括名称、描述、模式和处理函数,允许大模型调用外部API或执行本地计算。

📈MCP促进了生态系统的发展,形成标准后,各家可以开发自己的MCP服务器,开发者可以在不同LLM之间方便切换。MCP提高了易用性、灵活性和安全性,推动大模型技术的应用。

图为 2025.04.27 拍摄于杭州美丽洲公园


前面两节我们讲清楚了 function/tool call 是什么以及如何手动调用工具 ,接下来我们再来理解 MCP 其实就很轻松了。

本文会通过一个实际的例子,带大家具象的看清楚 MCP 到底是个啥。

(大家下载代码后去配置一下 apikey ,就能把文中的例子跑起来)。

行文思路:

    概念和流程讲清楚把官方的例子跑起来讲清楚我的理解

MCP 概念

我们先认清一个事实:

    大模型训练完成后,其权重和参数就已经固定了无法自主学习或进化它对事物的认知局限于历史训练数据,之外的东西它是不知道的,比如天气预报、新闻;

模型的能力在于:

    拥有既定的认知框架具备“理解”和“推理”能力

第二点很重要,大模型能理解你的意图!还能根据你给的输入做出推理!最终预测出你要的答案。

我对于大模型的具象理解,感觉他就是一个受过高等教育的“人”。

如果仅局限于他自己的固定认知,无法和外界交互,那么在实际场景可能发挥不了多大的作用。

MCP 就是提供了一套标准,让大模型可以和外界发生交互,以发挥他最大的能量!


按照官方的定义:

MCP is an open protocol that standardizes how applications provide context to LLMs.

    MCP 是一个开放协议/标准;MCP 标准化了应用如何提供上下文给 llms (底层其实就是 function call,现在你应该很容易理解)

官方还贴了一张图:

现在我们尝试理解一下:

    MCP-client 基于 MCP 协议和 MCP server 建立连接,client 从 server 侧拉取它支持的工具列表;MCP-client 接收你的问题,传入刚刚从 server 侧拉取的 tools,然后发起大模型调用;大模型会智能决策是否需要调用工具,如果是就返回工具调用信息,然后发起工具调用请求;工具的具体执行在 server 侧,可能读取一些本地数据,也可以通过 web APIs 来发起远程请求;server 侧把工具执行的结果返回给 clientclient 拼接工具调用 message(这一步其实就是拼接上下文),然后再次调用大模型拿到回答。

来跑一个 MCP 实例吧

参考官方的实现,我已经写了一个例子放到 github 上了:github.com/StannyDai20…

开启服务的方式是,在根目录下启用一条命令行:

node <path_to_client_script> <path_to_server_script>

先来讲讲里面的核心实现:

MCP-client

入口函数的核心伪代码如下,核心就是连接 mcp-server 然后开启命令行的对话服务。

这里的 chatLoop 和我们第一节里的案例如出一撤

async function main() {    const mcpClient = new MCPClient();    await mcpClient.connectToServer(serverPath);    await mcpClient.chatLoop();}main();

client 类伪代码如下:

connectToServer 核心目的就是拉取 server 端的工具啦

import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";class MCPClient {  constructor {    this.tools = [];    this.mcp = new Client();  } /**  * 基于 mcp sdk  * 1. 建立和 client 和 server 端的连接  * 2. 拉取 mcp-server 提供的工具列表(tool list)  *   */  async connectToServer(serverScriptPath) {    this.transport = new StdioClientTransport({        command: process.execPath,        args: [serverScriptPath],    });    this.mcp.connect(this.transport);    // 核心目的就是拉取 server 端的工具呀    const toolsResult = await this.mcp.listTools();    this.tools = toolsResult.map(() => ...)  }  /**   * 根据给定输入,调用大模型   * 模型会自己感知需要调哪些工具   *    * 在需要工具调用的场景下   * 利用 mcp 提供的能力实现工具执行并拿到结果,然后再次调用大模型就可以拿到最后回答   */  async processQuery(query) {  }  /**  * 开启一个命令行 chatbot,内部调用 processQuery(query) 拿到模型回答结果  */  async chatLoop() {  }}

StdioClientTransport 类

StdioClientTransport 类是 MCP 客户端的传输层实现,负责通过标准输入输出(stdin/stdout)与 MCP 服务器进程通信。

【核心功能】

简化版伪代码如下

class StdioClientTransport {  constructor(server) {    this.process = null;    this.serverConfig = server;  }  async start() {    // 启动子进程    this.process = spawn(this.serverConfig.command, this.serverConfig.args);    // 设置事件监听    this.process.stdout.on('data', (data) => {        // 处理服务器返回的消息    });    this.process.on('error', (error) => {        // 处理错误    });  }  send(message) {    // 发送消息到服务器    this.process.stdin.write(JSON.stringify(message));  }  close() {    // 关闭连接    this.process.kill();  }}

【核心总结】

结合案例的启动方式:node <path_to_client_script> <path_to_server_script>

这里我们发现:mcp-client 是运行在主进程,mcp-server 是运行在子进程,两者通过 stdio(标准输入输出) 实现进程间通信


接下来再看下 processQuery 的核心实现

async processQuery(query) {    // 1. 构建初始消息    const messages = [{ role: "user", content: query }];    // 2. 调用模型获取响应    const response = await this.callModel({        messages,        tools: this.tools    });    // 3. 处理模型响应    const { tool_calls, content } = response.choices[0].message;    // 4. 如果需要调用工具    if (tool_calls) {        // 并行执行所有工具调用        const results = await Promise.all(tool_calls.map(call =>             this.mcp.callTool({                name: call.function.name,                arguments: JSON.parse(call.function.arguments)            })        ));        // 5. 将工具结果添加到消息历史        messages.push(...);        // 6. 再次调用模型获取最终答案        return await this.callModel({ messages });    }    // 7. 直接返回内容    return content;}

MCP-server

server 侧相比 client 要直观的很多:

    调用 sdk 初始化一个 server 实例注册工具建立连接,把服务跑起来

核心伪代码如下:

// Create server instanceconst server = new McpServer({  name: "weather",  version: "1.0.0",});// 注册工具server.tool(name, description, schema, handler);// Start the serverasync function main() {  const transport = new StdioServerTransport();  await server.connect(transport);  console.error("MCP Server running on stdio");}main()

工具的定义格式

关于工具的定义我们可以统一写成对象声明:

{    name: "calculate",    description: "Calculate the sum of two numbers",    schema: {        // 这里定义的是【入参类型】,代码里基于 zod,是一个工具库,可以直观的生成 schema 裸数据    },    handler: async (params) => {        // 函数体内容...    }};

代码里一共三个工具,前两个都是官方提供的,第三个是自己尝试加了一个工具

    get-alerts:查询天气预警的,会发生实际 http 调用get-forecast:查询天气预报的,会发生实际 http 调用calculate 简版计算器,本地计算

测试截图

case 1:夏威夷的天气情况查询,包含 1 次工具调用

case 2: 一句话包含 2 次工具调用

case 3:我自己加的计算器工具,测试一下

当然也有一些不太对的地方,比如这里提问 8 的 10次方,ai 回复的工具调用信息不太对,num2 应该也是 8 而非10.

当然我们目前实现中,也不支持多次工具调用

MCP 有啥好处

MCP 提供了 AI 连接外部应用的标准,这个标准大家都认

形成标准之后,各家都去开发自己的 MCP 服务器,例如 github-mcp-server、Google-map-server

设想在标准之前,想让大模型实现获取 github 数据、获取谷歌地图数据,是不是只能手动定制调用他们的开放 API,可能还不一定提供;

另外不同 LLM 提供商返回格式有差别,开发者可能需要手动处理兼容,标准出来之后一定会促进各厂商遵循基础的统一格式,允许开发者方便的在不同 LLM 之间来回切换;

所以我觉得核心的好处就是:标准形成后,生态起来了。

生态起来之后,大家的力量都汇聚在一起,未来在易用性、灵活性、安全性都会做的越来越好。

总结

本文首先结合官网关于 MCP 的概念以及官方的示意图,尝试解读 MCP 的系统流程。

然后结合一个具体的代码实例,阐述了 mcp-client 和 mcp-server 侧的核心实现,并且跑了几个工具调用的case;大家动手把代码跑起来,会有更深的体会。

最后说明了MCP标准出来后带来哪些好处。

当然了,以上都是我目前的个人理解,不一定对,欢迎大家一起参与讨论。

我相信拥有了和外界交互能力的大模型,将会渗透到我们工作和生活的方方面,发挥其更大的能量!

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

MCP 大模型 function call 工具调用 AI
相关文章