-
Notifications
You must be signed in to change notification settings - Fork 12
Description
1. 前言
在AI快速发展的今天,各种AI工具层出不穷,但它们之间的协作却一直是个难题。
你是否遇到过这样的困扰:想让ChatGPT访问你的本地文件,或者让Claude调用你的数据库?
今天,我们来聊聊Anthropic推出的Model Context Protocol(MCP),一个可能彻底改变AI工具生态的协议。
2. 什么是 MCP?
MCP 即模型上下文协议(Model Context Protocol),是 Anthropic 公司推出的开放协议。
它标准化了应用程序向大语言模型提供上下文信息的方式,如同 “AI 应用的 USB-C 端口”,通过 MCP Server 中间层,让 AI 模型能以统一方式访问文件系统、数据库、API 等外部资源,简化 AI 应用与外部资源的集成流程 。
3. 为什么会出现 MCP?
在 MCP 出现之前,大型语言模型虽然具备强大的推理和生成能力,但存在明显的局限性:
- 知识时效性差:模型的知识通常止于训练数据的截止日期,无法获取最新信息。
- 专有数据获取困难:对于企业内部的数据库、文档仓库等信息孤岛,模型完全不了解。
- 整合代价高昂:不同的 AI 应用要接入数据库、API 或第三方工具,都需针对每个数据源编写单独的连接器,导致维护成本高、重复劳动多。
业界曾尝试通过多种方式解决这些问题:
- 自定义 API 集成:为每个数据源单独开发连接器
- 插件机制:如 OpenAI 插件
- 检索增强生成(RAG):通过向量检索提供相关信息
- 工具调用框架:如 LangChain
然而,这些方案普遍存在开发成本高、代码维护复杂、缺乏统一性等问题。
MCP 的出现,正是为了通过标准化协议,大幅简化 LLM 相关的 Agent/工作流的开发、维护成本和模型切换成本。
4. MCP 核心概念
MCP 采用模块化的客户端-服务器(Client-Server)架构,旨在解耦 AI 助手与后端服务之间的关系。
下面我们来详细了解 MCP 的核心概念:
4.1 通信机制
MCP 协议支持多种传输机制:
-
Stdio(标准输入/输出):
-
通过标准输入/输出流传输数据
-
适用于在同一台机器上运行的客户端和服务器之间的本地通信
-
客户端通常以子进程启动 MCP Server,通过 stdin 发送消息,从 stdout 接收响应
-
一般通过 npx、uvx 启动的这种都属于 stdio
-
-
HTTP with SSE(Server-Sent Events):
-
结合 HTTP 和 SSE 实现跨网络的实时数据传输
-
适用于需要访问远程资源或分布式部署的场景
-
客户端通过 HTTP POST 发送请求,服务器通过 SSE 流式返回响应
-
这种方式将要被废弃,推荐使用 Streamable 的形式。
-
-
Streamable HTTP:
-
改进 HTTP with SSE 的不足,如不支持连接恢复、要求服务器维护长连接等
-
移除了专门的
/sse端点,所有通信通过单一端点进行 -
服务器可以灵活选择返回普通的 HTTP 响应或升级为 SSE 流
-
引入了会话 ID 机制以支持状态管理和连接恢复
-
所有这些通信机制都使用 JSON-RPC 2.0 格式进行消息传输。
4.2 MCP Host(主机)
MCP Host 是运行大模型的应用程序或环境,是发起请求的 LLM 应用程序。例如:
-
Claude desktop
-
Cursor 编辑器
-
各类 AI Agent
-
对话机器人
主机负责:
-
管理客户端实例的生命周期
-
控制连接权限
-
执行安全策略
-
协调 AI/LLM 集成
-
处理用户输入并生成响应
一个 host 可以对多个 MCP Client,Client 会在 host 里面创建和实例化,可以将 MCP Host 视为集成了大型语言模型的应用前端
4.3 MCP Client(客户端)
MCP Client 位于主机程序内部,充当主机进程与服务器之间的抽象接口层。客户端与 MCP Server 保持 1:1 的连接,充当 LLM 和 MCP Server 之间的桥梁。
客户端负责:
-
规范化通信
-
处理协议转换
-
发现可用服务
-
发送调用请求并获取结果
-
根据模型或用户的请求构建标准化的 MCP 消息
4.4 MCP Server(服务器)
MCP Server 是轻量级节点或服务进程,通过标准化的 MCP 协议向客户端提供特定的能力。服务器可以是本地的也可以是远程的,每个服务器专注于特定任务,充当模型与实际资源之间的中介。
MCP Server 可以提供三种核心能力:
-
工具(Tools):
-
可被 LLM 调用的方法或执行器
-
用于帮助 LLM 执行特定的任务,如搜索网络、执行计算或访问外部 API 等
-
工具是模型控制的,LLM 根据上下文决定使用哪个工具
-
工具定义包括名称、描述和输入 Schema
-
-
资源(Resources):
-
类似文件的数据,可以被客户端读取
-
包括文本文档、结构化数据、图像等
-
遵循 URI 格式,可以包含文本或二进制数据
-
资源旨在由应用程序驱动,可以作为附件可供用户添加到上下文中
-
-
提示(Prompts):
-
预先编写的 prompt 模板,帮助用户完成特定任务
-
可以包含指令、示例和格式要求等
-
提示是用户可控的,用户可以选择何时运行它们
-
可复用的提示词模板或工作流程
-
4.5 本地资源和远程服务
除了上述核心组件外,MCP 架构还包括:
-
本地资源:MCP 服务器可以安全访问的本地计算机中的文件、数据库和服务。由于服务器部署在本地,敏感数据无需上传云端,安全性得以增强。
-
远程服务:MCP 服务器可以连接到的通过互联网可用的外部系统或服务,例如通过 API 访问的外部系统。MCP 服务器可以封装这些远程资源,使模型以统一方式访问互联网信息或调用在线服务。
5. MCP 的生命周期和连接流程
MCP 为客户端-服务器连接定义了严格的生命周期,以确保正确的功能协商和状态管理。生命周期分为三个阶段:初始化、操作和关闭。
5.1 初始化阶段(Initialization)
初始化阶段是客户端与服务器的首次通信,双方协商协议版本及所支持的功能:
-
客户端发送
initialize请求,声明协议版本和客户端能力 -
服务器返回响应确认支持的能力与协议版本
-
客户端通知服务器初始化完成,双方建立通信连接
5.2 操作阶段(Operation)
初始化完成后,客户端和服务端之间进行正常的协议通信,此阶段包括:
-
能力发现:
-
客户端请求工具列表(
tools/list) -
客户端请求资源列表(
resources/list) -
客户端请求提示模板列表(
prompts/list)
-
-
工具调用:
-
客户端发送
tools/call请求,附上工具名称和具体参数 -
服务器执行操作并返回结果
-
-
资源访问:
-
客户端通过
resources/read请求访问资源 -
服务器返回资源内容
-
-
提示使用:
-
客户端通过
prompts/run执行提示模板 -
服务器返回处理后的提示
-
-
事件订阅:
-
客户端可订阅资源变更通知
-
服务器在资源变更时通知客户端
-
-
上下文采样:
-
服务器可请求客户端进行 LLM 补全
-
客户端返回补全结果
-
5.3 关闭阶段(Shutdown)
当会话需要结束时,客户端或服务器可以正常终止连接:
-
客户端发送
shutdown请求 -
服务器确认关闭
-
客户端发送
exit通知,连接终止
5.4 MCP 调用流程
当用户以自然语言提出请求时,MCP 的工作流程如下:
暂时无法在飞书文档外展示此内容
-
请求解析:
-
用户以自然语言提出请求
-
MCP Host(主机进程)接收并处理用户请求
-
-
工具发现阶段:
-
MCP Client 查询可用的 MCP Server
-
客户端发送
tools/list请求获取工具列表 -
服务器返回工具列表及其 JSON Schema 定义
-
客户端收集工具信息完成
-
-
模型决策阶段:
-
MCP Host 将用户请求和可用工具列表一起发送给大语言模型(LLM)
-
LLM 分析问题并决定是否需要调用外部工具
-
如果需要调用工具,LLM 选择合适的工具并构造所需参数
-
-
工具调用阶段:
-
MCP Client 发送
tools/call请求,附上工具名称和具体参数 -
MCP Server 接收请求
-
服务器执行相应操作(如查询数据库、调用 API 等)
-
服务器获取执行结果并返回给客户端
-
MCP Client 接收结果
-
-
响应生成阶段:
-
工具调用的结果被发送回 LLM
-
LLM 基于工具返回的信息生成最终回答
-
MCP Host 处理 LLM 的回答
-
向用户展示自然语言响应
-
整个过程对用户是透明的,提供了流畅的自然语言交互体验。工具调用的时机、选择和参数完全由大模型基于上下文自主推理决策,而不是由应用层预先决定调用哪个工具。
6. 从零实现 MCP
Model Context Protocol 官网有很多详细的实现例子,他们推荐使用类似 Cursor、Trae、Cline 这样的编程工具来帮快速实现 MCP。
参考这一章节:构建 MCP 并运用 LLM 进行学习
我把下面实现的几个 Demo 都放到了 Github:https://github.com/yinguangyao/mcp-demo
6.1 准备文档
在开始之前,请收集必要的文档以帮助 Claude 了解 MCP:
- 访问 https://modelcontextprotocol.io/llms-full.txt 并复制完整的文档文本
- 导航到 MCP TypeScript SDK 或 Python TypeScript SDK 存储库
- 复制 README 文件和其他相关文档
- 将这些文档粘贴到您与 Claude 的对话中
怎么向 AI 描述自己的诉求呢?官网也给了详细的描述模板:
提供文档后,请向 Claude 清晰描述您想要构建的服务器类型。请具体说明:
- 您的服务器将公开哪些资源
- 它将提供什么工具
- 它应该提供任何提示
- 需要与哪些外部系统交互
例如:
Build an MCP server that:
- Connects to my company's PostgreSQL database
- Exposes table schemas as resources
- Provides tools for running read-only SQL queries
- Includes prompts for common data analysis tasks
6.2 实现 stdio 版本 time server
我比较习惯使用 Cursor,所以都是以 Cursor 来演示。
如果使用的是 Cursor 的话,可以将上面的几个文档放到 rules 目录下面,然后就可以让 Cursor 帮你开始写 MCP 了,我们先从一个简单的获取系统时间的 MCP 开始吧。
我本地创建了一个 mcps 的项目,让 Cursor 帮我创建一个 time 目录,实现一个获取当前时间的 mcp:
这个 mcp 比较简单,AI 几分钟就已经写好了。
实现 MCP 服务器的核心步骤如下:
第一步:搭建框架
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 创建MCP服务器
const server = new McpServer({
name: "time-server",
version: "1.0.0"
});这里我们导入必要的依赖,并创建一个名为"time-server"的 MCP 服务器实例。
第二步:注册工具函数
server.registerTool(
"getCurrentTime",
{
description: "获取当前时间,可选择不同格式和时区",
inputSchema: {
format: z.enum(["iso", "date", "time", "timestamp", "full"]),
timezone: z.string().optional()
},
annotations: {
title: "获取当前时间",
readOnlyHint: true,
openWorldHint: false
}
},
async ({ format, timezone }) => {
// 实际功能实现...
}
);每个工具注册需要三部分:
- 工具名称
- 工具元数据(描述、输入模式、注解)
- 工具实现函数
我们的示例实现了两个工具:一个获取当前时间,另一个列出可用时区。
第三步:连接传输层
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("时间工具服务已启动");
}这里我们使用标准输入/输出(stdio)作为通信通道,连接 AI 模型和服务器。
完整代码实现如下:
/**
* Model Context Protocol (MCP) - Time Tool
* 提供获取当前时间的各种格式的工具
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 创建MCP服务器
const server = new McpServer({
name: "time-server",
version: "1.0.0"
});
// 添加获取当前时间的工具
server.registerTool(
"getCurrentTime",
{
description: "获取当前时间,可选择不同格式和时区",
inputSchema: {
format: z.enum(["iso", "date", "time", "timestamp", "full"]).describe("时间格式: 'iso'(ISO格式), 'date'(年月日), 'time'(时分秒), 'timestamp'(时间戳), 'full'(完整格式)"),
timezone: z.string().optional().describe("时区,例如 'Asia/Shanghai',不填则默认使用系统时区")
},
annotations: {
title: "获取当前时间",
readOnlyHint: true, // 该工具是只读的,不修改系统状态
openWorldHint: false // 该工具不与外部世界交互
}
},
async ({ format, timezone }) => {
// 创建时间对象
const now = new Date();
// 根据不同格式返回时间
try {
let result: string;
switch (format) {
case 'iso':
result = now.toISOString();
break;
case 'date':
result = now.toLocaleDateString(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
timeZone: timezone
});
break;
case 'time':
result = now.toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: timezone
});
break;
case 'timestamp':
result = now.getTime().toString();
break;
case 'full':
result = now.toLocaleString(undefined, {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZone: timezone
});
break;
default:
result = now.toISOString();
}
return {
content: [
{
type: "text",
text: result
}
]
};
} catch (error) {
return {
isError: true,
content: [
{
type: "text",
text: `获取时间出错: ${error instanceof Error ? error.message : String(error)}`
}
]
};
}
}
);
// 添加获取时区列表的工具
server.registerTool(
"listTimeZones",
{
description: "获取可用时区列表",
annotations: {
title: "列出时区",
readOnlyHint: true,
openWorldHint: false
}
},
async () => {
// 简化的时区列表
const timeZones = [
"UTC",
"Asia/Shanghai",
"Asia/Tokyo",
"Europe/London",
"Europe/Paris",
"America/New_York",
"America/Los_Angeles",
"Australia/Sydney"
];
return {
content: [
{
type: "text",
text: JSON.stringify(timeZones, null, 2)
}
]
};
}
);
// 连接到stdio传输
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("时间工具服务已启动");
}
main().catch(error => {
console.error("启动时间服务失败:", error);
process.exit(1);
}); 这个 ts 文件 build 之后就可以直接用 node 命令运行了。
至此,这个 mcp server 就算完成了,那如何测试功能是否正常呢?
这里推荐两种方式:
- 内置到 cursor、trae 这类编程工具里面,在 Agent 模式下告诉他们获取当前时间
{
"mcpServers": {
"time": {
"command": "node",
"args": ["你的项目路径/time/dist/index.js"]
}
}
}- 使用 @modelcontextprotocol/inspector 进行调试。切换到 time 目录下面执行下面这句:
npx @modelcontextprotocol/inspector node dist/index.js打开 http://127.0.0.1:6274/ 可以看到这个界面,中间区域可以选择 List Tools,也可以选中一个 Tool 之后调试。
从上面的 Gif 图可以看到,建立连接的好似好先触发了一次 initialize 请求,获取了 mcp 服务的信息。
然后调用 tools/list 接口获取到当前支持的所有 mcp tools,最后通过 tools/call 实现工具调用。这个表现和前面的生命周期是一致的。
6.3 实现 sse 版本的 time server
sse 版本会在起一个服务,可以通过 HTTP 的形式访问这个服务。我们可以将刚刚 Stdio 版本的 time server 让 Cursor 帮忙修改一下。
如果生成效果不好,可以将 TypeScript Sdk 项目的 examples/simpleSseServer.ts 让它参考一下。
大家好!上次我们聊了如何实现一个基础 MCP 服务器,今天我们再进阶一步:如何通过 SSE(Server-Sent Events)技术让 AI 不仅能获取数据,还能订阅实时更新!一起来看看这个进阶版的实现吧!
从 HTTP 服务器开始
import express from "express";
import cors from "cors";
// 其他导入...
const app = express();
const PORT = process.env.PORT || 3001;
app.use(cors());
app.use(express.json());不同于上次的命令行工具,这次我们创建了一个真正的 Web 服务,对外提供 HTTP 接口。这意味着任何能发出 HTTP 请求的客户端(网页、App 等)都能使用我们的服务!
神奇的 SSE 传输
// 存储活跃的传输连接
const transports: Record<string, SSEServerTransport> = {};
app.get("/mcp", async (req: Request, res: Response) => {
const transport = new SSEServerTransport('/messages', res);
const sessionId = transport.sessionId;
transports[sessionId] = transport;
// 后续代码...
});SSE(Server-Sent Events)是一种允许服务器主动向客户端推送数据的技术。这意味着 AI 不必反复询问"有更新吗?",服务器可以主动告诉它"嘿,有新数据了!"
新增的订阅通知能力
server.registerTool(
"subscribeTimeUpdates",
{
// 配置...
},
async (params, { sendNotification }) => {
// 初始通知
await sendNotification({
method: "notifications/message",
params: { level: "info", data: `开始订阅时间更新...` },
});
// 循环发送更新...
for (let i = 0; i < updateCount; i++) {
await sleep(interval);
// 发送更新通知...
}
}
);这是本次实现的亮点:AI 可以订阅一个时间序列,服务器会按指定间隔主动推送最新时间。想想这意味着什么?AI 终于可以接收实时数据流了!
会话管理与资源清理
// 请求处理
app.post("/messages", async (req: Request, res: Response) => {
const sessionId = req.query.sessionId as string | undefined;
const transport = transports[sessionId];
// 处理请求...
});
// 资源清理
process.on("SIGINT", async () => {
for (const sessionId in transports) {
await transports[sessionId].close();
}
process.exit(0);
});专业的服务需要妥善管理客户端会话和资源清理,避免内存泄漏和僵尸连接。
架构亮点
与基础版相比,SSE 版 MCP 服务增加了这些亮点:
- 双向通信:不仅能响应请求,还能主动推送
- 长连接管理:维护客户端会话状态
- Web 接口:通过标准 HTTP 协议对外提供服务
- 实时通知:支持数据订阅和更新推送
- 优雅退出:妥善处理资源清理
这种架构让 AI 模型具备了"感知实时世界"的能力,可以应用于股票行情、天气预报、IoT 设备监控等需要实时数据的场景。
调试 SSE 服务之前,需要先在本地启动这个服务,然后将 URL 配置到 Cursor 里面,这里只演示 modelcontextprotocol/inspector。
6.4 实现 mcp client
前面我们分析了 MCP 服务器的实现,接着我们来看看另一半:客户端实现。
一个完整的 AI 工具生态系统需要服务端和客户端两部分协同工作,就像我们熟悉的 C/S 架构一样。
Client 的实现也比较简单,也就是创建一个 Client 实例,支持外部传入 MCP Server 配置,创建 Transport,建立连接的一个过程。
基础客户端 McpClient
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
import { McpClientOptions } from "./interfaces/McpClientOptions";
import { McpClientError, toMcpError } from "./utils/errorHandling";
/**
* Base MCP Client class that provides common functionality
*/
export class McpClient {
protected client: Client;
protected transport: Transport | null = null;
protected connected: boolean = false;
protected options: McpClientOptions;
/**
* Create a new MCP client
* @param options Client configuration options
*/
constructor(options: McpClientOptions) {
this.options = options;
// 创建一个 Client 实例
this.client = new Client({
name: options.name,
version: options.version
}, {
capabilities: options.capabilities || {}
});
}
/**
* Connect to an MCP server using the provided transport
* @param transport The transport to use for connection
*/
async connect(transport: Transport): Promise<void> {
try {
this.transport = transport;
// 连接到 transport
await this.client.connect(transport);
this.connected = true;
} catch (error) {
throw toMcpError(error);
}
}
/**
* Check if the client is connected
*/
isConnected(): boolean {
return this.connected;
}
/**
* Close the connection to the server
*/
async close(): Promise<void> {
try {
if (this.connected && this.transport) {
await this.client.close();
this.connected = false;
}
} catch (error) {
throw toMcpError(error);
}
}
/**
* List available tools from the server
*/
async listTools() {
this.checkConnected();
try {
return await this.client.listTools();
} catch (error) {
throw toMcpError(error);
}
}
/**
* Call a tool on the server
* @param name Tool name
* @param args Tool arguments
*/
async callTool(name: string, args: Record<string, any>) {
this.checkConnected();
try {
return await this.client.callTool({
name,
arguments: args
});
} catch (error) {
throw toMcpError(error);
}
}
/**
* Check if the client is connected, throw an error if not
* @private
*/
private checkConnected() {
if (!this.connected) {
throw new McpClientError('MCP client is not connected', -32000);
}
}
} 创建一个 SSE Client:
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { McpClient } from "../McpClient";
import { McpClientOptions } from "../interfaces/McpClientOptions";
/**
* Options specific to SSE transport
*/
export interface McpSseOptions extends McpClientOptions {
/** Base URL for the MCP server */
baseUrl: URL | string;
/** Optional headers to include with requests */
headers?: Record<string, string>;
/** Optional fetch implementation to use */
fetch?: typeof fetch;
/** Optional EventSource implementation to use */
EventSource?: any;
/** Optional timeout in milliseconds */
timeout?: number;
}
/**
* MCP client that connects to a server via Server-Sent Events (SSE)
*/
export class McpSseClient extends McpClient {
private sseOptions: McpSseOptions;
/**
* Create a new SSE MCP client
* @param options Configuration options
*/
constructor(options: McpSseOptions) {
super(options);
this.sseOptions = options;
}
/**
* Connect to an MCP server via SSE
*/
async connectSse(): Promise<void> {
const baseUrl =
this.sseOptions.baseUrl instanceof URL
? this.sseOptions.baseUrl
: new URL(this.sseOptions.baseUrl);
const transport = new SSEClientTransport(baseUrl);
await this.connect(transport);
}
}使用:
import { McpSseClient } from '../src/index';
/**
* 时间工具MCP客户端示例
* 通过SSE连接time-sse服务
*/
async function main() {
console.log('=== Time MCP 客户端示例 (SSE) ===');
// 首先启动time-sse服务器
console.log('请确保已经启动time-sse服务器 (执行 cd ../time-sse && node dist/index.js)');
// 创建客户端
const client = new McpSseClient({
name: 'time-sse-client',
version: '1.0.0',
baseUrl: 'http://localhost:3001/mcp'
});
try {
// 连接到MCP服务器
await client.connectSse();
console.log('成功连接到Time-SSE MCP服务器');
// 列出所有可用工具
const toolsResult = await client.listTools();
console.log(`\n可用工具数量: ${toolsResult.tools.length}`);
for (const tool of toolsResult.tools) {
console.log(`\n工具名称: ${tool.name}`);
console.log(`描述: ${tool.description || '无描述'}`);
// 测试调用工具
if (tool.name === 'getCurrentTime') {
console.log('\n调用getCurrentTime工具:');
const result = await client.callTool('getCurrentTime', {
format: 'full',
timezone: 'Asia/Shanghai'
});
console.log('当前时间:', result);
}
}
} catch (error) {
console.error('错误:', error);
console.log('\n请确保time-sse服务器已经启动并监听在http://localhost:3001/mcp');
} finally {
try {
// 关闭连接
await client.close();
console.log('\n连接已关闭');
} catch (error) {
console.error('关闭连接时出错:', error);
}
}
}
// 运行示例
main().catch(err => console.error('程序错误:', err)); 6.5 基础调用示例
import { McpStdioClient } from "./McpStdioClient";
async function main() {
// 创建客户端
const client = new McpStdioClient({
name: "weather-app",
version: "1.0.0",
command: "node",
args: ["./weather-service.js"]
});
try {
// 连接服务器
await client.connectStdio();
// 获取可用工具列表
const tools = await client.listTools();
console.log("可用工具:", tools);
// 调用天气预报工具
const forecast = await client.callTool("getWeatherForecast", {
location: "Beijing",
days: 3
});
// 处理结果
console.log("天气预报:", forecast.content[0].text);
} catch (error) {
console.error("操作失败:", error.message);
} finally {
// 关闭连接
await client.close();
}
}
main();6.6 客户端与服务端通信详解
JSON-RPC 通信协议
MCP 基于 JSON-RPC 2.0 协议实现通信,每次交互包含这些关键字段:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "getCurrentTime",
"arguments": { "format": "full", "timezone": "Asia/Shanghai" }
},
"id": "request-123"
}服务端会返回对应的响应:
{
"jsonrpc": "2.0",
"result": {
"content": [
{ "type": "text", "text": "2023-04-01 12:34:56" }
]
},
"id": "request-123"
}完整交互流程
一次典型的客户端-服务端交互包含以下步骤:
-
建立连接:客户端初始化传输层并连接到服务端
const client = new McpStdioClient({ name: "example-app", version: "1.0.0", command: "node", args: ["time-server.js"] }); await client.connectStdio();
-
发现可用工具:客户端查询服务端提供的功能
const tools = await client.listTools(); // 返回: [{ name: "getCurrentTime", description: "获取当前时间..." }, ...]
-
调用工具:客户端请求服务端执行特定功能
const result = await client.callTool("getCurrentTime", { format: "full", timezone: "Asia/Shanghai" }); // 结果: { content: [{ type: "text", text: "2023-04-01 12:34:56" }] }
-
处理通知(仅SSE等支持的传输层):接收服务端的主动推送
client.on("notification", (notification) => { console.log(`收到通知: ${notification.params.data}`); });
-
关闭连接:释放资源,结束通信
await client.close();
7. 总结
MCP 客户端是连接 AI 模型与外部世界的桥梁,它通过标准化的协议和接口,让 AI 能够访问各种实时数据和功能。通过优雅的抽象设计和传输层分离,MCP 框架可以适应各种通信场景,为 AI 应用提供坚实的技术基础。
无论你是想扩展 AI 的能力,还是为现有系统增加 AI 功能,理解 MCP 客户端的工作原理都是至关重要的一步。希望这篇深度解析能帮助你更好地掌握和运用这一技术!
参考阅读
- deepwiki 关于 typescript-sdk 的分析:https://deepwiki.com/modelcontextprotocol/typescript-sdk
- deepwiki 关于 langchainjs-mcp-adapters 的分析:https://deepwiki.com/langchain-ai/langchainjs-mcp-adapters






