mcp

13 min read,created at 2025-07-06
llmmcp

1 mcp介绍

MCP(model context protocal)是最近半年,在AI领域比较火的一个概念了,他是给大模型提供上下文的一种协议。

什么是上下文呢?上下文本质就是传给大模型的promptLLM中很多技术概念都是围绕上下文出现的。用户自己输入问题就是上下文的主要部分。但是有时候用户的输入不够,这时候我们可能需要借助RAG和知识库来丰富上下文,知识库是用户自己准备的大量数据,通过可检索的方式在调用LLM前,搜索出相关的内容。再然后知识库还是不够,例如我们想要查询互联网,最新天气等,离线的知识库是肯定没有相关数据的,所以需要调用外部工具,所以有了function calling,这些都是来丰富上下文的。

那么MCP又做了什么呢?很多地方说MCP就是把function calling给规范化标准化了,有的地方也是MCP就是替换function calling的。这些说法都不太准确。MCP是把提供上下文的协议给规范化了,不仅对函数调用的过程,也对RAG的过程产生影响。

那么RAGFunction calling的现有流程中有哪些“不规范”的地方呢?

1.1 RAG与MCP resource

RAG流程如下,我们需要自己完成R检索,A增强上下文的步骤,尤其是检索的这一步,我们需要自己在不同的知识库中,进行查询,可能用的是不同的数据库存储,不同的查询逻辑等。所以如果有多个知识库,这里就需要自己写一些定制化的逻辑。当然系统变复杂之后,我们可以把不同知识库的RAG封装成接口,入参是用户输入,出参是RAG增强后的prompt,这样就可以实现解耦了。但是至于这个接口怎么封装,传输方式,序列化方式等等,都是没有规范的,各个公司可以有自己的实现。

image

MCP就是这样一个规范,他规定接口的出入参采用jsonrpc2.0格式规范,并且规定了传输协议有stdiohttpstram2种形式,参考MCP就是在原有的链路调用中,在ai应用和RAG数据库查询之间加了一层适配层,这样ai应用就可以不用关心数据库调用部分的细节,如下图:

image

MCP server通过配置的方式注册到ai应用cherry studio中,配置文件类似这样:

{
  "mcpServers": {
    "github.com/modelcontextprotocol/servers/tree/main/src/github": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-github"
      ],
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": "xxx"
      },
      "disabled": false,
      "autoApprove": []
    },
    "demo": {
      "command": "node",
      "args": [
        "C:\\Users\\sunwu\\Desktop\\code\\mcp-node\\src\\a1.mjs"
      ],
      "disabled": false,
      "autoApprove": []
    }
  }
}

主要就是配置了command+args的启动指令,在配置第一次保存后,ai应用通过标准的接口去初始化,查询mcpserver中有哪些可用的资源,如下图是claude dev这个工具接入MCP server的截图,可以看到每个接入的mcp中的toolsresources,后者就是对应的静态资源数据的检索,其实对应的是RAG或者知识库的概念,即静态的、只读的、供查询的数据的接口,resources类型。

image

在知识库查询过程中,流程中插入了MCP server这个中间层

image

1.2 function calling与MCP tool

toolresource只是类型不同,流程上都需要对MCP server进行调用,所以和前面流程一致。MCP tool算是对function calling流程的增强

在原来的function calling流程中,用户把函数列表和问题一起发给LLM,后者根据上下文决定要调用某个函数后,将函数名和入参返回,再由Ai app调用具体的函数,最后这一步同样没有形成规范,如何调用,怎么调用等,不同的应用和插件都有自定义的形式,所以MCP server同样是规范化了这一步。下图是原来function calling流程

image

引入MCP后再函数调用者里套一层MCP server来标准化AI app的代码,但这其实还不够,因为function calling是部分模型支持的功能,有些模型可能不支持,为了让所有模型都能支持MCP tool,所以这里大多数支持MCP client的模型都采用定制化System prompt的形式。这里我们用cherry studio这个支持MCP的工具为例,先创建一个demomcp server他有个add函数运行加法,至于MCP server怎么写我们后面再说,这里我们先看流程。

image

然后诱导大模型用demo-add运行加法

image

抓包看到第一个请求,就返回了,将使用add来运算

image

这是因为System prompt中有列出可以使用的MCP server,我们之前也提到过claude dev也是类似的方式来让不支持function calling的模型,能够调用工具函数的。

image

返回的结果中有prompt中预设好的xml格式的函数调用描述,包括了函数名,函数参数:

image

此时cherry studio会识别xml,调用这个mcp server,调用返回3,然后将调用结果也给大模型再传过去,这是第二次LLM调用:

image

此时大模型应该总结下这个调用结果直接返回就好了,不过我这次调用的时候,大模型又尝试再次调用了add函数,所以这里有两次调用。所以我这个例子中调用了两次add,但是问题不大。

1.3 小结

上面例子中可以看出MCP其实没有提供功能和内容上的本质变化,只是在索要更多上下文的RAG function calling等流程中加了一层中间层。单页带来了很多好处,让RAGtool的接入更加规范化了。

比如之前在coze中接入插件,要符合coze的插件标准,如果后续agent想切到别的平台,还需要对插件进行修改。但是如果各个平台都支持MCP标准的话,ai app的切换,就不影响插件的切换了。

2 MCP server

MCP server主要有三种形式,一种是位于本机(ai app所在的机器),执行的内容也是本机,例如本机的文件操作;第二种是位于本机,但是执行内容是远程调用,例如githubmcp server可以查询github热点,就是远程调用的gh的API;第三种是mcp server本身就运行在远程服务器。

image

其中需要低延迟和操作系统或本地软件权限的,都是第一种,例如:文件操作、控制浏览器、控制桌面等;需要远程服务接口支持的则是第二种为主,例如刚才提到的githubmcp server,他就是在本机运行一个代理,来接受ai app的请求,通过转换后发送到remote endpoint;最后如果想要灵活的控制mcp server的升级、收集用户数据等,也会采用第三种方案,用单独的服务器运行mcp server,不过这种比较少。

2.1 写一个

$ mkdir mcp-node && cd mcp-node
$ npm init -y
$ npm i @modelcontextprotocol/sdk
$ vim index.mjs

把官方ts库的代码贴到index.mjs里,虽然官方说是ts-sdk,但是没有用ts语法,直接在js中也能运行

index.mjs
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Create an MCP server
const server = new McpServer({
  name: "demo-server",
  version: "1.0.0"
});

// Add an addition tool
server.registerTool("add",
  {
    title: "Addition Tool",
    description: "Add two numbers",
    inputSchema: { a: z.number(), b: z.number() }
  },
  async ({ a, b }) => ({
    content: [{ type: "text", text: String(a + b) }]
  })
);

// Add a dynamic greeting resource
server.registerResource(
  "greeting",
  new ResourceTemplate("greeting://{name}", { list: undefined }),
  { 
    title: "Greeting Resource",      // Display name for UI
    description: "Dynamic greeting generator"
  },
  async (uri, { name }) => ({
    contents: [{
      uri: uri.href,
      text: `Hello, ${name}!`
    }]
  })
);

// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
server.connect(transport);

直接运行node a1.mjs,窗口会阻塞没有任何反应,这是因为当前使用的是stdio交互模式的StdioServerTransport,需要将jsonrpc2.0的request通过stdio的形式传入进来,才会有反应,像下面这样:

image

但是这个控制台echo的方式实在是太麻烦了,所以为了方便调试可以使用npm i -g @modelcontextprotocol/inspector,然后运行mcp-inspector,打开给的带token的url。

image

打开后如下填写,主要是运行我们的index.mjs文件,这个inspector会与我们运行起来的进行的stdio进行交互。

image

连接本质是initailize的过程,此时我们可以看到如下页面,通过list xx按钮可以展示有哪些资源和工具。

image

这里我们在tool中展示下所有的函数,然后点击add,输入入参,点击run,得到了最后的结果,这就是测试了。

image

我们会看下,server的代码new McpServer是创建了McpServerregisterToolregisterResource则是分别注册了一个tool和一个resource,前者实现了加法,后者会返回打招呼。最后通过StdioServerTransport这个最简单的stdio交互的方式来启动server,注意只有本机可以用这个,如果是远程的话,需要用httpstream的形式。

3 MCP client

client的作用就是来调用server,要根据stdio httpstream具体是那种传输形式和server进行匹配的交互。此外在ai app中要支持可配置mcpserver,所以有些工具例如chatbox等是不支持mcp的,而另外一些如cherry studio是支持的,这是因为后者集成了MCP client的功能,能对MCP server进行调用。

client也有对应的sdk,这里我就不展示了,因为自己暂时没有写MCP client的需求,一般是自己写ai app或者平台才需要自己写client