掘金 人工智能 04月27日 12:43
MCP(Model Context Protocol)入门与实践
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

MCP(Model Context Protocol)是由Anthropic提出的开放标准协议,旨在标准化应用程序如何为LLM提供上下文,解决LLM与外部数据、工具的连接问题。它就像AI领域的“USB-C”,通过客户端-服务器架构,为AI应用提供统一、标准化的方式访问和处理数据,解决数据孤岛问题。文章介绍了MCP的核心概念、架构、通信机制,并对比了Function Calling,最后通过一个实战案例,演示了如何使用MCP调用高德API,实现交通态势实时查询。

💡 MCP的核心价值在于标准化LLM与外部资源的连接,解决了传统AI模型在数据访问、集成成本上的难题,为AI应用提供了连接万物的接口。

⚙️ MCP采用客户端-服务器架构,包括MCP主机、客户端、服务器、本地资源和远程资源。其中,MCP Client充当LLM和MCP Server之间的桥梁,负责获取工具、调用工具并返回结果。

📡 MCP支持两种通信机制:基于Stdio的本地通信和基于SSE的远程通信。两种机制均使用JSON-RPC 2.0格式进行消息传输,确保通信的标准化和可扩展性。

🆚 MCP与Function Calling的主要区别在于,MCP提供统一接口,实现互操作,并具备上下文管理能力,而Function Calling功能相对单一,且生态相对封闭。 MCP更像是一条“高速公路”,而Function Calling更像是一辆“汽车”。

🚦文章通过一个实战案例,演示了如何使用MCP调用高德API,实现交通态势实时查询。展示了MCP在实际应用中的可行性,以及如何通过MCP构建更灵活、更强大的AI应用。

一、MCP 基础概念

1.1 什么是 MCP?

定义:MCP(Model Context Protocol,模型上下文协议)是由Anthropic公司于2024年底提出的一种开放标准协议,它标准化了应用程序如何为 LLM 提供上下文,旨在解决大型语言模型(LLM)与外部数据、工具之间的连接交互问题。它为AI应用提供了一种统一的、标准化的方式来访问和处理数据(实时数据、本地数据),解决了AI模型的数据孤岛问题,为 AI 应用提供了连接万物的接口,被誉为 "AI 领域的 USB-C"。

核心价值

传统的AI模型面临几个关键挑战:

MCP的核心优势:

MCP 是 AI大模型 与数据(包括本地数据和互联网数据)之间的一座桥梁,通过 MCP 服务器和 MCP 客户端,大家只要都遵循这套协议,就能实现“万物互联”。比如,可以和数据和文件系统、开发工具、Web 和浏览器自动化、生产力和通信、各种社区生态能力全部集成,实现强大的协作工作能力。

1.2 核心架构

MCP 遵循客户端-服务器架构(client-server),其中包含以下几个核心概念:

其中,重点关注MCP Client和MCP Server。

MCP Client

MCP client 充当 LLM 和 MCP server 之间的桥梁,MCP client 的工作流程如下:

❶ MCP client 首先从 MCP server 获取可用的工具列表。❷ 将用户的查询连同工具描述通过 function calling 一起发送给 LLM。❸ LLM 决定是否需要使用工具以及使用哪些工具。❹ 如果需要使用工具,大模型向MCP client发出请求,MCP client 会通过 MCP server 执行相应的工具调用。❺ MCP server处理请求,处理结果返回给MCP client❺ MCP client将结果发送回 LLM。❻ LLM 基于所有信息生成自然语言响应。❼ MCP Hosts将响应展示给用户。

Claude Desktop 和Cursor都支持了MCP Server接入能力,它们就是作为 MCP client来连接某个MCP Server感知和实现调用。

MCP Server

MCP server 是 MCP 架构中的关键组件,它可以提供 3 种主要类型的功能:

资源(Resources):将 Server 上的数据和内容开放给大语言模型,可以是文本或二进制数据。如 API 响应、文件内容、图像等。○ 工具(Tools):可以被调用的函数,这些函数可由客户端调用,并由 LLM 使用来执行操作。○ 提示(Prompts):预先编写的可复用的提示词模板和工作流程。

这些功能使 MCP server 能够为 AI 应用提供丰富的上下文信息和操作能力,从而增强 LLM 的实用性和灵活性。

一个形象的比喻

我在网上看到了一个比较形象的比喻,感觉还不错,摘录到这里,如果需要查看原文请点这里首先,用户让 LLM 调用工具,就好比我(特工少女,即 User)让男朋友(LLM)去小店(软件服务提供商)买东西(Data & Tool)并使用。如果模型接到的任务则可能需要多个工具调用(Tool Use),这就好比我跟男朋友讲的需求比较复杂,比如做从零做一道番茄炒蛋。

然后男朋友通过规划(Planning)决定去苏泊尔买个锅和铲,去菜场买个西红柿和鸡蛋,再去便利店买个糖和盐,以此来完成番茄炒蛋这道菜。但是在传统的 AI 应用开发过程中,男朋友(LLM)能去的小店(能使用的工具)都需要开发者预先定义线路(编写代码)。

但是,男朋友(LLM)每去一个新地方,都要造一条新路(编写代码),似乎不太方便。于是后来字节扣子、百度千帆、阿里云百炼等平台出现了,他们就好比是开了一个商超。商超跟各种小店说,我这里人气旺,你们直接按我们的要求来货架上架你们的商品就行,再跟我(User)说我这现成的商品多,以后让男朋友(LLM)来这买就行。

小店们觉得这事不错,便遵守平台一定的上架规范,开始把他们的商品(Tool)变成了插件(Plugin),我(User)提的好多需求,男朋友(LLM)在这些平台上就能一站式满足了,选几个插件直接用就行,方便了挺多。

但是,Anthropic 干了一个什么事情呢?他想搞电商!

他跟小店们说,不同平台的插件规范不太一样,你们去各个平台上架插件也挺累的。咱们搞个新玩法,能让天下的男朋友(LLM)都很容易的用到更多商品(调用更多工具)。你们呢按照我这个标准,把你们的商品都做一个数字化的标准信息处理(即变成了 MCP Server),我们再定义一个叫电商组件的东西(即 MCP Client),你们的 MCP Server 要跟电商组件有互动,只要遵守一个开放的规则,这部分就是 MCP 协议,即 Server 和 Client 的通信协议。

此外,Anthropic 基于这个电商平台(Client)的理念做了一个淘宝的外壳,这个淘宝就好比没有包含 LLM 的 Claude Desktop。在这基础上,如果再加上 Claude 的模型,就相当于让“男朋友刷淘宝”,这一整体就构成了 MCP Host。

MCP Server 只管提供各种工具,MCP Host 只管通过 MCP Client 获取并使用这些工具。然后淘宝上玲琅满目的商品很多,就形成了 MCP Market。

1.3 通信机制

关于Client和Server之间的通信方式,MCP 协议支持两种主要的通信机制:基于Stdio(标准输入输出)的本地通信和基于SSE(Server-Sent Events)的远程通信。

这两种机制都使用 JSON-RPC 2.0 格式进行消息传输,确保了通信的标准化和可扩展性。

**本地通信:**通过 stdio 传输数据,适用于在同一台机器上运行的客户端和服务器之间的通信。**远程通信:**利用 SSE 与 HTTP 结合,实现跨网络的实时数据传输,适用于需要访问远程资源或分布式部署的场景。

连接过程

    客户端发送包含协议版本和功能的请求initializeServer 使用其协议版本和功能进行响应客户端发送通知作为确认initialized正常消息交换开始

二、MCP与其他技术的比较

2.1 与Function Calling对比

Function Calling,最初是由OpenAI在2023年6月作为其API的一部分提出的,是一种函数调用机制,允许LLM通在生成内容的过程中调用外部函数或服务,从而获取更多能力。现在,很多其他大模型也借鉴了这种概念,纷纷推出了自己的function calling 。借助这个功能,可以调用外网检索引擎、天气查询、图片生成服务、音乐生成服务调用等。

对比项目MCPFunction Calling
定义模型和其它设备集成的标准接口,包含:工具 Tools、资源 Resources、提示词 Prompts将模型连接到外部数据和系统,平铺式的罗列 Tools 工具。和 MCP Tool 不同的在于:MCP Tool 的函数约定了输入输出的协议规范。
性质协议功能
协议JSON-RPC,支持双向通信、可发现性、更新通知能力。JSON-Schema,静态函数调用。
调用方式Stdio / SSE / 同进程调用同进程调用 / 编程语言对应的函数
范围通用(多数据源、多功能)特定场景(单一数据源或功能)
目标统一接口,实现互操作扩展模型能力
实现基于标准协议没有统一的标准,各个大模型提供厂商都有自己的接入方式
开发复杂度低:通过统一协议实现多源兼容高:需要为每个任务单独开发函数
复用性高:一次开发,可多场景使用低:函数通常为特定任务设计
灵活性高:支持动态适配和扩展低:功能扩展需要额外开发
适用场景动态、复杂场景,如跨平台数据访问和整合简单任务。如天气查询、翻译等

Function calling 功能单一,不具备上下文管理能力,一次只执行一个任务,一般是同步进行调用。没有特定的标准,需要厂商或者我们自己去开发实现。生态相对封闭,没有多少开源项目,定制化比较多。MCP 功能复杂,可以支持多轮对话,一次能调用多个任务,也可以异步执行。有统一的标准,只要是适配了 MCP 的 Server 都可以用统一的代码去调用,而且除了工具调用,还提供了提示词和上下文资源。

2.2 举例

MCP 的设计解决了这个问题,具体方法如下:

当然,mcp协议和function call两者并不直接对立的,反而是进行相互补充:

三、实战案例:交通态势实时查询

3.1 需求

开发一个支持自然语言查询的某市某条道路的实时交通态势,通过 MCP 调用高德 API。

3.2 实现MCP Server

准备:高德ApiKey、安装@modelcontextprotocol/sdk

代码如下:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";import { z } from "zod";// 通过命令行获取keyconst API_KEY = process.argv[2];/** * @description 创建服务器实例 */const server = new McpServer(  {    name: "amap-traffic-mcp-server",    version: "1.0.0",  },  {    capabilities: {      resources: {},      tools: {},      prompts: {},    },    instructions: "交通态势查询服务,使用高德API",  });// 注册交通态势查询工具server.tool(  "get_traffic",  "线路交通态势查询",  {    city: z.string().describe("城市名称"),    name: z.string().describe("道路名,街道名字"),  },  async ({ city, name }) => {    try {      // URL 编码参数      const encodedCity = encodeURIComponent(city);      const encodedName = encodeURIComponent(name);      // https://lbs.amap.com/api/webservice/guide/api-advanced/traffic-situation-inquiry      // 调用高德地图 API      const response = await fetch(        `https://restapi.amap.com/v3/traffic/status/road?key=${API_KEY}&level=5&name=${encodedName}&city=${encodedCity}`      );      if (!response.ok) {        throw new Error(`API请求失败: ${response.status}`);      }      const data = await response.json();      // 检查 API 响应状态      if (data.status !== "1") {        throw new Error(`高德地图API错误: ${JSON.stringify(data)}`);      }      // 检查是否有交通信息      if (!data.trafficinfo) {        return {          content: [            {              type: "text",              text: `未找到 ${city}${name} 的交通信息`,            },          ],        };      }      const { description, evaluation } = data.trafficinfo;      // 计算道路通畅程度      const status = evaluation?.status || "未知";      let statusText = "";      switch (status) {        case "1":          statusText = "通畅";          break;        case "2":          statusText = "基本通畅";          break;        case "3":          statusText = "轻度拥堵";          break;        case "4":          statusText = "中度拥堵";          break;        case "5":          statusText = "重度拥堵";          break;        default:          statusText = "未知";      }      return {        content: [          {            type: "text",            text: `${city}${name}当前路况:${statusText}\n\n${              description || "暂无详细描述"            }`,          },          {            type: "text",            text: evaluation              ? `\n\n道路状况统计:\n- 畅通路段:${evaluation.expedite}\n- 缓行路段:${evaluation.congested}\n- 拥堵路段:${evaluation.blocked}\n- 未知路段:${evaluation.unknown}`              : "",          },        ],      };    } catch (error) {      console.error("交通信息查询失败:", error);      throw new Error(        error instanceof Error ? error.message : "服务调用失败,请稍后重试"      );    }  });/** * @description 启动服务器 */async function main() {  try {    const transport = new StdioServerTransport();    await server.connect(transport);    console.error("交通态势查询服务已启动");  } catch (error) {    console.error("服务启动失败:", error);    process.exit(1);  }}main();

在开发的时候,可以使用@modelcontextprotocol/inspector进行调试。

3.3 实现MCP Client

import { Client } from "@modelcontextprotocol/sdk/client/index.js";import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";const transport = new StdioClientTransport({  command: "node",  args: ["/Users/bwrong/0WorkSpace/00.MyStudy/mcp-demo/server/build/index.js",'API-KEY']});const client = new Client(  {    name: "traffic-client",    version: "1.0.0"  });await client.connect(transport);console.log("connect success");// List prompts// const prompts = await client.listPrompts();// Get a prompt// const prompt = await client.getPrompt({//   name: "example-prompt",//   arguments: {//     arg1: "value"//   }// });// List resources// const resources = await client.listResources();// Read a resource// const resource = await client.readResource({//   uri: "file:///example.txt"// });const tools = await client.listTools();console.log(tools);// Call a toolconst result = await client.callTool({  name: "get_traffic",  arguments: {    city: "成都市",    name: "天府大道北段"  }});console.log(result);

在Client中可以连接Server,然后获取它提供的resource、prompt、tools,并可以执行调用。

当然我们也可以使用现有的一些支持MCP集成的应用来调用,不同的应用支持的功能可能有差异,具体可查看这里。下面我们使用cursor来演示。

    打开cursor的MCP配置文件。添加配置
{  "mcpServers": {    "amap-traffic-mcp-server": {      "type": "stdio",      "command": "node",      "args": [        "/Users/bwrong/0WorkSpace/00.MyStudy/mcp-demo/server/build/index.js",        "111111111111111111" // 你的高德API-key      ]    }  }}
    正常情况可以看到mcp列表中已出现添加的MCP及它提供的tools在chart面板中输入“帮我查询成都市天府大道北段道路交通情况"。可以看到它调用了MCP,并返回了结果当然,如果你的MCP想共享给别人使用,那么可以发布到npm上,在package.json中添加如下内容,支持使用npx运行。
"bin": {    "amap-traffic-mcp-server": "./build/index.js"}

然后进行发布,发布后,别人就可以使用如下方式进行集成了。

"amap-traffic-mcp-server": {  "type": "stdio",  "command": "npx",  "args": [    "amap-traffic-mcp-server",    "cc2b744799eaa97ade23c468e272c731"  ]}

更多代码亲查看这里

四、使用社区现有的MCP服务

MCP的出现,极大降低了大模型调用外部海量工具、软件、接口的门槛,越来越多的软件及工具厂商参与到 MCP 服务(MCP Server)生态的建设中,将其产品能力包装为 MCP 服务开放给 AI 模型使用。以下是一些MCP托管平台:

一些有意思的MCP Server:

参考文档:

Fish AI Reader

Fish AI Reader

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

FishAI

FishAI

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

联系邮箱 441953276@qq.com

相关标签

MCP协议 LLM AI应用 Function Calling 高德API
相关文章