MCP 不完全入门

What Is MCP

MCP(Model Context Protocol) 是 Anthropic 在 2024 年 11 月在 Introducing the Model Context Protocol 一文中提出的一种开放标准,是将 AI 助手连接到数据所在系统(包括内容存储库、业务工具和开发环境)的新标准。其目的是帮助前沿模型产生更好、更相关的响应。

该架构非常简单:开发人员可以通过 MCP 服务器公开其数据,也可以构建连接到这些服务器的 AI 应用程序(MCP 客户端)。

mcp-like-usb

我们可以将 MCP 视为 AI 应用的 USB-C 端口。正如 USB-C 提供了一种将设备连接到各种外围设备和配件的标准化方式一样,MCP 提供了一种将 AI 模型连接到不同数据源和工具的标准化方式。

  • MCP 主机(Host): 这些是需要访问外部数据或工具的应用程序(如 Claude Desktop 或 AI 驱动的 IDE)
  • MCP 客户端(Client): 它们与 MCP 服务器保持专用的一对一连接
  • MCP 服务器(Server): 轻量级服务器,通过 MCP 公开特定功能,连接到本地或远程数据源

正如 USB-C 简化了我们将不同设备连接到计算机的方式一样,MCP 简化了 AI 模型与数据、工具和服务交互的方式

Why MCP

  • 上下文窗口限制(Context Window Limitation):LLM 只能处理有限长度的输入文本(Token 数量)。当对话变长或需要参考大量文档时,必须有策略地选择哪些信息放入上下文中。

  • 信息优先级(Information Prioritization):并非所有信息都同等重要。系统提示通常最重要,其次是当前用户输入和最相关的历史/知识。需要一种机制来决定信息的优先级和排列顺序。

  • 结构化输入(Structured Input):简单地将所有文本堆砌在一起可能效果不佳。MCP 旨在定义一种更结构化的方式来组织上下文,让模型更容易区分不同类型的信息(如指令、历史、知识)。

  • 一致性和可控性(Consistency and Controllability):通过明确的上下文管理规则,可以更好地控制模型的行为,使其表现更一致、更符合预期。

How To Use MCP

接下来用到的:

  • 客户端:Cherry Studio
  • 模型调用接口:硅基流动/Ollama
  • 模型:Qwen2.5-7B-Instruct 等

Step 1 - 找一个开源的 MCP Server(以高德地图 mcp-amap 为例)

一般开源的 MCP Server 说明里会有配置和命令,比如高德提供的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"mcpServers": {
"amap-maps": {
"command": "npx",
"args": [
"-y",
"@amap/amap-maps-mcp-server"
],
"env": {
"AMAP_MAPS_API_KEY": "您在高德官网上申请的key"
}
}
}
}

这里需要一个 API key,可以在高德开放平台获取

Step 2 - 在 Cherry Studio 中配置 MCP 服务器

MCP 现在一共有两种模式:

  • Stdio:主要用在本地服务上,操作你本地的软件或者本地的文件,比如 Blender 这种就只能用 Stdio 因为他没有在线服务
  • SSE :主要用在远程服务上,这个服务本身就有在线的 API,比如访问你的谷歌邮件,谷歌日历等。

目前大多数开源 MCP Server 使用的都是 Stdio 的标准输入输出模式。

也可以通过在右上角通过编辑 MCP 配置直接导入配置。

Step 3 - 选择一个支持工具调用(包含工具图标)的模型

这里以硅基流动提供的 Qwen2.5-7B 的推理模型为例,测试时需要打开工具栏中的 MCP 服务器。

理论上所有模型都可以支持工具调用,Cherry Studio 为了更好的效果选择仅支持经过工具调用相关训练的模型,参考 https://github.com/CherryHQ/cherry-studio/discussions/3827

Step 4 - 测试工具调用功能

首先直接询问“帮我查找南京南站附近的酒店”:

可以发现模型虽然调用了工具给出了一些答案,但是并不准确。观察调用过程可以发现只做了简单的接口调用,参数设置也不准确。

接下来我们尝试引导模型,先查找“南京南站”的坐标(location),再根据坐标查询附近一公里内的酒店,可以看到模型给出了一些预期的结果:

目前可以看出 7B 的模型在很多语义和工具调用的处理上并不精确,经常直接简单调用单一接口返回。所以需要通过提示词手动拆分工具调用的步骤才能获取相对准确的结果。

测试使用 gemini-2.5-pro-exp-03-25 这种能力比较强的模型的话,模型就有分析工具接口区别,先查询“南京南站“坐标,再根据获取的坐标查询附近”酒店”的能力。

How Does MCP Work

mcp-architechure

当我们问了一个问题时:

  1. 主机(ChatGPT/Cursor/Cherry Studio)将你的问题发送给 LLM。
  2. LLM 分析(客户端从服务器获取的)可用的工具,并决定使用哪一个(或多个)。
  3. LLM 通过 MCP Server 执行所选的工具。
  4. 工具执行的结果被返回模型。
  5. 模型结合执行结果整理回复。
  6. 回复用户。

how-does-mcp-work-zhihu

模型如何智能选择工具?

我们通过将工具的具体使用描述以文本的形式传递给模型,供模型了解有哪些工具以及结合实时情况进行选择。模型是通过 prompt engineering,即提供所有工具的结构化描述和 few-shot 的 example 来确定该使用哪些工具。

以官方 example 中的 prompt 为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
你是一个乐于助人的助手,可以使用以下工具:

{tools_description}

请根据用户的问题选择合适的工具。如果不需要使用工具,请直接回复。

重要提示:当你需要使用工具时,你必须且仅能使用下方确切的 JSON 对象格式进行回应,不能包含任何其他内容:

{
"tool": "tool-name",
"arguments": {
"argument-name": "value"
}
}

在收到工具的响应后:

1. 将原始数据转化为自然的、对话式的回应。
2. 保持回应简洁且信息丰富。
3. 专注于最相关的信息。
4. 使用用户问题中的适当上下文。
5. 避免简单地重复原始数据。

请仅使用上方明确定义的工具。

How To Build Your Own MCP

选择开发工具

目前开源的大多 MCP 服务器都是使用官方的 Typescript MCP SDK 或 Python MCP SDK 实现的。很多教程已经包含了上述 SDKs,这里出于简单考虑,直接使用 Go 演示。

更新:GoLang 官方包加入了 mcp sdk https://github.com/golang/tools/tree/master/internal/mcp

注意:MCP 官方尚未引入 go-sdk https://github.com/orgs/modelcontextprotocol/discussions/224

mcp-go 作为当下流行的 MCP Go 实现的开源库具有以下特点:

  • 快速 :高级接口意味着更少的代码和更快的开发速度
  • 简单 :使用最少的样板构建 MCP 服务器
  • 完整 :MCP Go 旨在提供核心 MCP 规范的完整实现

这里使用官方提供的计算器功能作为演示内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package main  

import (
"context"
"errors" "fmt"
"github.com/mark3labs/mcp-go/mcp" "github.com/mark3labs/mcp-go/server")

func main() {
// Create a new MCP Server
s := server.NewMCPServer(
"Calculator Demo",
"1.0.0",
server.WithResourceCapabilities(true, true),
server.WithLogging(),
)

// Add a calculator tool
calculatorTool := mcp.NewTool("calculate",
mcp.WithDescription("Perform basic arithmetic operations"),
mcp.WithString("operation",
mcp.Required(),
mcp.Description("The operation to perform (add, subtract, multiply, divide)"),
mcp.Enum("add", "subtract", "multiply", "divide"),
),
mcp.WithNumber("x",
mcp.Required(),
mcp.Description("First number"),
),
mcp.WithNumber("y",
mcp.Required(),
mcp.Description("Second number"),
),
)

// Add the calculator handler
s.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
op := request.Params.Arguments["operation"].(string)
x := request.Params.Arguments["x"].(float64)
y := request.Params.Arguments["y"].(float64)

var result float64
switch op {
case "add":
result = x + y
case "subtract":
result = x - y
case "multiply":
result = x * y
case "divide":
if y == 0 {
return nil, errors.New("Cannot divide by zero")
}
result = x / y
}

return mcp.NewToolResultText(fmt.Sprintf("%.2f", result)), nil
})

// Start the server
if err := server.ServeStdio(s); err != nil {
fmt.Printf("Server error: %v\n", err)
}
}

这里先简单介绍下 MCP 中的一些核心概念:

工具(Tools)

上面的 demo 包含了 MCP 的核心概念之一:工具

工具可让我们通过 MCP 服务器执行操作,类似于 REST API 中的 POST 端点。每个工具都应该具备以下特性:

  • 发现 :客户端可以通过 tools/list 端点列出可用的工具
  • 调用 :使用 tools/call 端点调用工具,服务器执行请求的操作并返回结果
  • 灵活性 :工具范围从简单的计算到复杂的 API 交互

资源(Resources)

除此之外,MCP Server 还可以提供资源

资源是我们向模型公开数据的方式,类似于 REST API 中的 GET 端点。资源可以是任何内容 - 文件、API 响应、数据库查询、系统信息等。资源可以是:

  • 静态(固定 URI):如 docs://readme
  • 动态(使用 URI 模板):如 users://{id}/profile

提示(Prompts)

提示是可重复使用的模板,可帮助我们有效地与服务器交互。它们就像是编码到服务器中的“最佳实践”。

提示旨在由用户控制 ,这意味着它们从服务器公开到客户端,以便用户能够明确选择它们以供使用。

实际上目前大多数 MCP Client 都仅支持了上述的工具部分,毕竟工具是我们拓展 LLM 能力的主要目标。

使用

mcp-inspector 是官方提供的 MCP Server 测试工具,本节旨在介绍使用(并且更新后的 Cherry Studio 支持在界面显示工具方法),因此跳过工具测试的流程。

接下来我们就可以把编译后的可执行文件丢给 Cherry Studio:

这里也可以直接在下面看到我们提供的接口和说明。由于我们的 MCP Server 本身就是一个可执行文件,所以不需要额外的命令(upx、npx)来运行。

接下来我们测试一下基本功能:

第一次我们没有打开 MCP Server 的开关,可以看出模型给了一个“猜测“的数字(几乎可以骗过去),但是仔细观察就可以发现个别数字和结果不一致。

当我们打开 MCP Server 开关后,可以看到模型正常调用我们提供的接口,并得到正确的计算结果。

References