最近在学习 AI 领域相关知识,接触到了 MCP(Model Context Protocol),感觉很有意思,所以做了一些探索和实践,这里做下记录和分享。PS:文中关于 MCP 的概念介绍主要参考了 Akshay 在 X 上的分享 Model Context Protocol (MCP), clearly explained,基于此做了翻译和补充。
什么是 MCP ? #
MCP(Model Context Protocol,模型上下文协议)是一种通信协议,旨在解决大型语言模型(LLM)与外部数据源及工具之间的无缝集成。它允许 AI 模型获取更丰富的上下文信息,并调用外部工具执行操作,从而提供更准确、更有价值的响应。
MCP 就像 AI 应用的 USB-C 接口。正如 USB-C 为设备与各类配件提供了标准化的连接方式,MCP 也为 AI 应用与不同数据源和工具之间的连接实现了标准化。

MCP 架构 #
MCP 核心遵循客户端-服务端架构,在这种架构下,一个主机应用程序可以连接到多个服务器。核心组件包含:Host
、Client
、Server
。

MCP Host
组件指的是一些 AI 应用程序,例如 Claude APP、Cursor,它为 AI 交互提供环境,能够访问工具和数据,并且运行 MCP Client
。 MCP Client
组件在 MCP Host
内部运行,用于实现与 MCP Server
的通信。

MCP Server
组件主要是暴露具体特定功能的实现,例如访问 API、Database、File System 等。MCP Server
提供了三项关键能力:
Tools
:为 LLM 提供一些特定工具,允许 LLM 通过工具调用执行特定任务。Resources
:为 LLM 提供一些数据和内容。Prompts
:为 LLM 提供可复用的提示词模版和工作流。

MCP 通信 #
Capability Exchange(能力交换) #
在给定的上下文中,客户端向服务器发送初始化请求以了解服务器的能力。服务器则回应其能力的详细信息。例如,一个 Weather API 服务器提供可用的 Tools
来调用 API,Prompts
以及将 API 文档作为 Resource
。这里的 "能力交换" 指的就是 Client
与 Server
之间关于 MCP Server
功能和资源的信息交互过程。

Message Exchange(消息交换) #
在 MCP Client
和 MCP Server
确认连接成功后,就可以继续进一步的消息交换。与传统的单向 API 通信不同的是,MCP 通信是双向的,这意味着 Client
和 Server
可以同时发送和接收消息。如果需要,客户端也可以允许 MCP Server
在不需要 API 密钥的情况下利用 MCP Host
的 AI 能力(e.g. LLM completions or generations),而 MCP Client
则维护对模型访问和权限的控制。

MCP 消息格式 #
MCP 消息格式采用 JSON-RPC 2.0
标准,主要有三种消息类型:
1. Requests
:这些请求需要响应。它们包含 method
和可选的 params
#
{
jsonrpc: "2.0",
id: number | string,
method: string,
params?: object
}
2. Responses
:响应请求时发送的响应。响应包含 result
(成功) 或 error
(失败) #
{
jsonrpc: "2.0",
id: number | string,
result?: object,
error?: {
code: number,
message: string,
data?: unknown
}
}
3. Notifications
:这些是单向消息,不需要响应。与请求一样,它们包含 method
和可选的 params
。 #
{
jsonrpc: "2.0",
method: string,
params?: object
}
MCP 传输机制 #
MCP 支持不同的传输机制进行通信。其中有两种内置的传输机制:
1. Standard Input/Output (stdio) #
这种传输机制使用标准输入输出流进行通信,适用于本地集成和命令行工具。
// example (Server)
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
const server = new McpServer({
name: 'example-server',
version: '1.0.0'
}, { capabilities: {} })
const transport = new StdioServerTransport()
await server.connect(transport)
// example (Client)
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
const client = new Client({
name: 'example-client',
version: '1.0.0'
}, { capabilities: {} })
const transport = new StdioClientTransport({
command: './server', // Path to your server executable
args: ['--option', 'value'] // Optional arguments
})
await client.connect(transport)
使用 stdio 传输机制 时:
- MCP Client将 MCP Server 作为子进程启动。
- 服务器通过
stdin
接收 JSON-RPC 消息并通过stdout
做出响应。 - 消息必须以换行符分隔。
- MCP Server 可能用
stderr
记录日志。 - 重要提示:MCP Server 不得向
stdout
写入任何无效的 MCP 消息,MCP Client 也不得向 MCP Server 的stdin
写入任何无效的 MCP 消息。
2. Server-Sent Events (SSE) #
这种传输机制使用 HTTP POST 请求进行客户端到服务器通信,并使用 Server-Sent Events
进行服务器到客户端流式传输。
// example (Server)
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express, { Response } from 'express';
const server = new McpServer({
name: "example-server",
version: "1.0.0"
}, { capabilities: {} });
const app = express();
app.use(express.json());
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/messages", res as Response);
await server.connect(transport);
// Store the transport instance for later use. For simplicity, we assume a single client here.
app.locals.transport = transport;
});
app.post("/messages", async (req, res) => {
const transport = app.locals.transport;
await transport.handlePostMessage(req, res);
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
// Note: For simplicity, this example doesn't handle routing for multiple connections.
// In a production environment, you'd need to route messages to the correct transport instance
// based on some identifier (e.g., a session ID).
// example (Client)
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'
const client = new Client({
name: 'example-client',
version: '1.0.0'
}, { capabilities: {} })
const transport = new SSEClientTransport(
new URL('http://localhost:3000/sse')
)
await client.connect(transport)
3. Custom Transport #
MCP 支持自定义传输机制,自定义传输机制需要实现 Transport
接口:
interface Transport {
// Start processing messages
start(): Promise<void>;
// Send a JSON-RPC message
send(message: JSONRPCMessage): Promise<void>;
// Close the connection
close(): Promise<void>;
// Callbacks
onclose?: () => void;
onerror?: (error: Error) => void;
onmessage?: (message: JSONRPCMessage) => void;
}
MCP Server 实践 #
在对 MCP 有了基本了解后,我分别在工作和开源中做了两个 MCP Server 的实践,下面跟大家做下简单的分享:
BFF MCP Server #
公司团队内部有一个 BFF 服务,作为前端和后端之间的桥梁,主要负责面向后端进行微服务聚合和数据编排、面向前端提供视图层数据处理。
在日常的前后端 API 联调过程中,API 文档无疑是最重要的资源。如果能让前端开发人员通过 Chat with AI 直接获取 API 文档的详细内容,将极大提升 API 对接效率。在我们团队中,我们主要使用 Apifox 来维护 API 文档。因此,我们只需创建一个 MCP Server 并定义相应的 Tools 与 Apifox API 交互,即可实现通过 Chat with AI 查询 API 文档的功能。
在 BFF架构中,一个 API 可能会聚合多个微服务及其相关接口。当后端开发人员排查问题时,往往需要查看 BFF API 所依赖的微服务信息。在我们现有的 BFF 实现中,我们已通过特定技术方案生成了 API 依赖关系图。同样地,我们只需定义一个 Tools 来动态获取这些依赖关系图,就能让后端开发人员通过 Chat with AI 快速获取所需信息,有效解决他们在开发过程中遇到的问题。

Vue MCP Server #
在 Vue 应用中,我们同样可以实现一个 MCP Server,来实现通过 Chat with AI 查询 Vue 应用中的数据信息,例如组件树、组件数据、路由信息等。
在 Vue MCP Server 的实现中,我主要利用了 Vite Dev Server 来搭建 MCP Server 的 SSE 通信层。通过集成 Vue DevTools Kit
,我们可以获取 Vue 应用的运行时数据。同时,借助 Vite Dev Server 提供的通信能力,我们能够与 Vue 应用进行实时交互,从而实现一个功能完备的 Vue MCP Server。具体实现流程如下图所示:

接下来我们来看一下 Vue MCP Server 提供的能力:
获取 Vue 应用组件树 #

获取 Vue 应用组件数据 #

获取 Vue 应用路由信息 #

获取 Pinia Store 树 #

获取 Pinia Store 数据 #

Vue MCP 目前已在 Github 上开源,如果你对它感兴趣,可以在这里查看更多信息。