MCP(Model Context Protocol,模型上下文协议) 是由 Anthropic 推出的一种开放标准,旨在统一大型语言模型(LLM)与外部数据源和工具之间的通信协议。
MCP 的主要目的在于解决当前 AI 模型因数据孤岛限制而无法充分发挥潜力的难题,MCP 使得 AI 应用能够安全地访问和操作本地及远程数据,为 AI 应用提供了连接万物的接口。
MCP是在OpenAI的Function call和GPTs之后出现的,那么它一定是看到了Function call的开发和使用的复杂性,而且看到了GPTs的封闭和不开放性。
Function call可以说是MCP的灵感的来源和基础功能,可以理解Function call等同于工具(tools)。大模型(LLM)能够理解工具的描述和参数的规定,然后,会从一堆工具中去选择一个和几个工具来解决用户提出的问题。没有LLM能自动选择和调用工具的能力,也没有MCP这个概念的可能。这是MCP的源头和基础,不得不说,这是OpenAI的巨大原始贡献。
但是,Function call,或者 Tools 使用,对很多开发者,就不用说一般用户了,尽管能用大模型来copilot,还是很不方便,网络访问,权限控制,流程控制,错误处理,可靠性,版本维护,升级改动,等等,一大堆事情都要去编程处理。而且,对不同的应用场景,可能还需要定制化。
既然Function call这么复杂不好用,那么OpenAI提出了GPTs的概念,说白的,GPTs就类似于微信的小程序,它的底层实现,依然是基于Function call,做好的一个一个GPTs,用户可以选择使用哪一个。这儿的问题是,GPTs相当于是已经包装好的闭源应用,只能在OpenAI平台上使用,不开放,不是生态玩法(可能只是OpenAI生态)。你给OpenAI做了个GPT,对别的平台,你还得做一个,这样,对服务商,开发者,依然不方便,不利于市场拓展。
由此看到,Function call太底层,GPTs太高层,这时,就有了MCP这样处于中间层的协议或产品出来了。
MCP 遵循客户端-服务器架构(client-server),其中包含以下几个核心概念:
- MCP 主机(MCP Hosts):发起请求的 LLM 应用程序(例如 Claude Desktop、IDE 或 AI 工具)。
- MCP 客户端(MCP Clients):在主机程序内部,与 MCP server 保持 1:1 的连接。
- MCP 服务器(MCP Servers):为 MCP client 提供上下文、工具和 prompt 信息。
- 本地资源(Local Resources):本地计算机中可供 MCP server 安全访问的资源(例如文件、数据库)。
- 远程资源(Remote Resources):MCP server 可以连接到的远程资源(例如通过 API)。
MCP server 是 MCP 架构中的关键组件,它可以提供 3 种主要类型的功能:
- 资源(Resources):类似文件的数据,可以被客户端读取,如 API 响应或文件内容。
- 工具(Tools):可以被 LLM 调用的函数(需要用户批准)。
- 提示(Prompts):预先编写的模板,帮助用户完成特定任务。
这些功能使 MCP server 能够为 AI 应用提供丰富的上下文信息和操作能力,从而增强 LLM 的实用性和灵活性。
你可以在 MCP Servers Repository 和 Awesome MCP Servers 这两个 repo 中找到许多由社区实现的 MCP server。使用 TypeScript 编写的 MCP server 可以通过 npx 命令来运行,使用 Python 编写的 MCP server 可以通过 uvx 命令来运行。
Resource可以是如下类型:
文件内容
数据库记录
API响应结果
图片等多媒体内容
…
Resources更多的是用于固定的资源获取,如果是一些更复杂的动态数据获取,还是需要使用Tools来实现。
协议内容
每个对象都有唯一的URI标识:[protocol]/[host]/[path],例如
file:///home/user/documents/report.pdf
postgres://database/customers/schema
客户端获取所有资源,共有两种方式:
- 获取direct resources
- 获取资源模板,一些动态资源可以通过这种方式返回
1. 获取direct resources
// Request
{
method: "resources/list"
}
// Response
{
uri: string; // 唯一标识
name: string; // 资源名称
description?: string; // 描述
mimeType?: string; // 资源类型
}
2. 获取资源模板,一些动态资源可以通过这种方式返回
// Request
{
method: "resources/templates/list"
}
// Response
{
uriTemplate: string; // RFC 6570 格式的uri,例如:/users/{userId}
name: string; // 资源名称
description?: string; // 描述
mimeType?: string; // 资源类型
}
客户端读取指定资源,返回文本或base64后的内容
// Request
{
method: "resources/get",
params: {
uri: uri
}
}
// Response
{
contents: [
{
uri: string; // 资源uri
mimeType?: string; // 资源类型
// 下面二选一:
text?: string; // 文本内容
blob?: string; // base64后的二进制内容
}
]
}
创建一个全局可复用的提示词模板,来标准化与LLM的交互,服务端只暴露这些prompts,供用户端选择使用。
prompt的定义结构为:
客户端获取prompts列表, 只会包含prompt的唯一标识以及生成提示词需要的参数
客户端请求从服务端获取生成的prompt,需要传入要求的参数
{
name: string; // 唯一标识
description?: string; // 描述
arguments?: [ // 参数列表
{
name: string; // 参数唯一标识
description?: string; // 参数描述
required?: boolean; // 是否必填
}
]
}
客户端获取prompts列表, 只会包含prompt的唯一标识以及生成提示词需要的参数
// Request
{
method: "prompts/list"
}
// Response
{
prompts: [
{
name: "analyze-code",
description: "Analyze code for potential improvements",
arguments: [ // 这里表示prompt需要一个language参数ss
{
name: "language",
description: "Programming language",
required: true
}
]
}
]
}
客户端请求从服务端获取生成的prompt,需要传入要求的参数
// Request
{
method: "prompts/get",
params: {
name: "analyze-code",
arguments: {
language: "python"
}
}
}
// Response
{
description: "Analyze Python code for potential improvements",
messages: [
{
role: "user",
content: {
type: "text",
text: "Please analyze the following Python code for potential improvements:\n\n```python\ndef calculate_sum(numbers):\n total = 0\n for num in numbers:\n total = total + num\n return total\n\nresult = calculate_sum([1, 2, 3, 4, 5])\nprint(result)\n```"
}
}
]
}
这种设计的好处就是:prompt的生成可以由MCP服务端根据不同入参去做不同返回,而不仅仅只是字符串占位而已。
使用装饰器mcp.prompt可以自动生成prompts协议格式:
基于python的类型注解生成出prompt入参的schema
基于inspect工具提取函数描述作为prompt的描述
@mcp.prompt()
def echo_prompt(message: str) -> str:
“”“测试prompt”“”
return f"Please process this message: {message}"
工具在MCP中被设计成通过模型来操控,是得LLM能与外部系统交互,能在现实世界执行动作。MCP Server需要将工具暴露给客户端,并由LLM决定如何执行,同时每个工具需要有一个唯一标识
客户端获取服务端所有工具列表
// Request
{
method: “tools/list”
}
// Response
{
“tools”: [
{
“name”: “get_alerts”,
“description”: "获取天气预警信息\n Args:\n state: 州名\n ",
“inputSchema”: {
“properties”: {
“state”: {
“title”: “State”,
“type”: “string”
}
},
“required”: [
“state”
],
“title”: “get_alertsArguments”,
“type”: “object”
}
}
]
}
这里的schema协议用的json schema,openai的chat api中参数response_format也是支持使用该协议来规范大模型输出。
客户端调用tool执行
// Request
{
method: “tools/call”
}
// Response
{
“content”: “”
}
mcp协议规定工具定义的规范,是用实现的sdk的mcp.tool装饰器,不需要再额外去编写规范文档,能够自动生成工具定义:
基于python的类型注解生成出入参的schema
基于inspect工具提取函数描述
@mcp.tool()
async def tool_a(state: str) -> str:
“”“获取天气预警信息
Args:
state: 州名
“””
…
参考文档:https://zhuanlan.zhihu.com/p/19707405738
MCP client 充当 LLM 和 MCP server 之间的桥梁,MCP client 的工作流程如下:
- MCP client 首先从 MCP server 获取可用的工具列表。
- 将用户的查询连同工具描述通过 function calling 一起发送给 LLM。
- LLM 决定是否需要使用工具以及使用哪些工具。
- 如果需要使用工具,MCP client 会通过 MCP server 执行相应的工具调用。
- 工具调用的结果会被发送回 LLM。
- LLM 基于所有信息生成自然语言响应。
- 最后将响应展示给用户。
你可以在 Example Clients 找到当前支持 MCP 协议的客户端程序。
本文将会使用 Claude Desktop 作为 MCP client,你可以在此页面下载安装:https://claude.ai/download
接下来演示通过 PostgreSQL MCP Server 使 LLM 能够基于 PostgreSQL 中的数据来回答问题。
首先使用 Docker 启动 PostgreSQL 服务。
docker run -d --name postgres \
-e POSTGRES_PASSWORD=postgres -p 5432:5432 \
postgres
在 PostgreSQL 中创建数据库和表,并插入数据。
-- 登录 PostgreSQL
docker exec -it postgres psql -U postgres
-- 创建数据库
CREATE DATABASE shopdb;
-- 连接到新创建的数据库
\c shopdb;
-- 创建 users 表
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL
);
-- 创建 orders 表
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_date TIMESTAMP NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
user_id INT REFERENCES users(user_id)
);
-- 插入示例数据
INSERT INTO users (first_name, last_name, email) VALUES
('John', 'Doe', '[email protected]'),
('Jane', 'Smith', '[email protected]'),
('Alice', 'Johnson', '[email protected]');
INSERT INTO orders (order_date, total_amount, user_id) VALUES
('2025-01-05 10:30:00', 150.75, 1),
('2025-01-06 11:00:00', 200.50, 2),
('2025-01-07 12:45:00', 120.25, 1);
在 Claude Desktop 中配置 PostgreSQL MCP Server 的连接信息,具体内容可以参考:For Claude Desktop Users。最终是在 claude_desktop_config.json 文件中添加如下内容:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://postgres:[email protected]/shopdb"
]
}
}
}
配置完毕后,重启 Claude Desktop。一切正常的话,你应该能在输入框的右下角看到一个锤子图标。点击锤子图标,可以看到 PostgreSQL MCP Server 提供的工具信息。
首先来问一个简短的问题:数据库中有哪些表? Claude 会判断出需要调用 MCP server 来查询 PostgreSQL 中的数据。这里会弹出一个窗口,需要用户授权。
点击 Allow 后,Claude 成功返回了结果。
接下来我们可以增加一点难度:查询金额最高的订单信息。在数据库中有两张表 users 和 orders,要想得到完整的订单信息,需要先去查询 orders 表中金额最高的一条记录,然后根据 user_id 这个外键再去查询 users 表中对应的用户信息。
从下面的输出可以发现 Claude 一开始是不知道数据库中的表结构的,因此先发送请求分别确定 orders 表和 users 表中相应的字段,然后再对两张表进行 join 查询。
点击 View Result from query from postgres 可以看到 Claude Desktop 向 MCP server 发送的请求以及得到的响应,说明这个结果确实是从 PostgreSQL 数据库中查询得到的。
你也可以复制这条 SQL 语句到数据库中查询进行确认。
总结
本文带领读者快速入门了 MCP(模型上下文协议),介绍了其架构、核心概念以及实际应用场景。通过演示 Claude Desktop 结合 PostgreSQL MCP Server 查询数据库的场景,展示了 MCP 如何增强 LLM 与外部数据源的交互能力。后续文章还会继续分享 MCP server 和 MCP client 开发的相关内容,欢迎持续关注。
参考资料
Model Context Protocol 官方文档:https://modelcontextprotocol.io/introduction
深度解析:Anthropic MCP 协议:https://mp.weixin.qq.com/s/ASmcjW53HKokdYt1m-xyXA
欢迎关注
http://weixin.qq.com/r/_hKehkDEUR0urQTl90fo (二维码自动识别)