MCP又是个什么东西?
摘要:了解 MCP 与 LLM 的交互方式
打个小广告,如果希望 claude 可以获取网页信息,可以尝试一下我之前开发的: mcp-web-content-pick。本地服务,隐私无忧,开箱即用。
引言:从孤岛到桥梁
想象一下,你有一个超级智能的外星朋友,他知道关于宇宙的一切知识,但有个致命问题:他被锁在一个没有窗户的房间里,只能通过纸条与外界交流。你想让他帮你查询今天的天气,但他无法上网;你想让他帮你计算复杂的数学问题,但他没有计算器;你想让他帮你控制智能家居,但他无法连接到你的设备。
这就是当前大语言模型(LLMs)的处境。它们拥有惊人的能力,但被限制在自己的”文本世界”里,无法直接获取或操作外部资源。这就是为什么我们需要一种标准化的方式,让这些模型能够与外部世界交互——这正是 Model Context Protocol (MCP) 的使命。
MCP 是什么:给 LLM 装上”超能力插座”
Model Context Protocol(模型上下文协议)是一个开放标准,旨在规范大语言模型(LLM)与外部系统之间的通信。如果把 LLM 比作一个超级大脑,那么 MCP 就像是一个标准化的”插座”,让各种”能力插头”都能轻松接入这个大脑。
MCP 的核心功能
- 资源访问:允许 LLM 读取各种数据源,就像给盲人配上了眼睛
- 工具使用:让 LLM 能够调用外部功能,就像给瘫痪的人装上了机械臂
- 提示模板:定义标准化的交互模式,就像是给外星人准备了地球生存指南
把 MCP 比作城市交通系统可能更容易理解:
- 资源 (Resources) 是图书馆和数据中心,LLM 可以去那里查阅信息
- 工具 (Tools) 是专业服务,LLM 可以委托这些服务完成特定任务
- 提示 (Prompts) 是标准的互动剧本,让交流更高效
为什么需要 MCP:解决”魔法黑盒”问题
现在,你可能会问:“我们已经有了一堆让 LLM 访问外部世界的方法,为什么还需要 MCP?”
想象你在组装一套家具,但每个零件都来自不同制造商,螺丝和螺孔大小各异,说明书语言不同。这就是当前 LLM 集成的状况——每个应用都在发明自己的轮子。
没有 MCP 的世界:
- 碎片化接口:每个应用都有自己的方式与 LLM 交互,就像每个国家都有自己的电源插座标准
- 重复开发:开发者不断”重新发明轮子”,浪费时间和资源
- 复杂集成:将 LLM 与现有系统集成需要大量定制工作
- 安全隐患:没有标准化的安全措施,容易引入漏洞
有了 MCP 的世界:
- 统一标准:一套协议连接所有模型和应用,就像 HTTP 连接了所有网站
- 即插即用:新功能可以轻松添加到任何支持 MCP 的系统
- 专注核心:开发者可以专注于创造价值,而不是搭建基础设施
- 安全可控:标准化的安全措施保护模型和数据
如何使用 MCP:从 Hello World 开始
让我们动手创建一个简单的 MCP 服务器,体验一下这个协议的强大之处。我们将使用 TypeScript 和官方的 SDK。
首先,安装 MCP SDK:
npm install @modelcontextprotocol/sdk
基本示例:计算器服务器
这个例子创建了一个简单的 MCP 服务器,提供基本的计算功能:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod"; // 用于参数验证
// 创建 MCP 服务器
const server = new Server({
name: "计算器服务",
version: "1.0.0"
});
// 添加加法工具
server.tool(
"add", // 工具名称
{ a: z.number(), b: z.number() }, // 参数定义
async ({ a, b }) => {
// 执行加法并返回结果
return {
content: [{
type: "text",
text: String(a + b)
}]
};
}
);
// 添加数学常数资源
server.resource(
"constants", // 资源名称
"math://constants", // 资源 URI
async (uri) => {
// 返回常用数学常数
return {
contents: [{
uri: uri.href,
text: JSON.stringify({
PI: Math.PI,
E: Math.E,
GOLDEN_RATIO: 1.618033988749895,
SQRT2: Math.SQRT2
}, null, 2)
}]
};
}
);
// 启动服务器(使用标准输入/输出作为通信通道)
const transport = new StdioServerTransport();
await server.connect(transport);
这段代码做了什么?它创建了一个小型 MCP 服务器,提供了:
- 一个加法工具:LLM 可以用它计算两个数字的和
- 一个数学常数资源:LLM 可以查询常用的数学常数
使用 MCP 就像是给 LLM 安装了计算器和数学参考手册,让它能够进行之前做不到的计算。
更复杂的例子:天气查询服务
让我们再来看一个更实用的例子——天气查询服务:
import { Server, ResourceTemplate } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// 创建 MCP 服务器
const server = new Server({
name: "天气助手",
version: "1.0.0"
});
// 添加城市天气资源(带参数的动态资源)
server.resource(
"cityWeather",
new ResourceTemplate("weather://city/{cityName}", { list: undefined }),
async (uri, { cityName }) => {
// 在实际应用中,这里会调用天气 API
// 这里简化为返回模拟数据
return {
contents: [{
uri: uri.href,
text: `${cityName}的天气信息:晴天,温度25°C,湿度60%`
}]
};
}
);
// 添加天气预报工具
server.tool(
"forecast",
{ city: z.string(), days: z.number().min(1).max(7) },
async ({ city, days }) => {
// 在实际应用中,这里会调用天气 API 获取预报
const forecast = Array(days).fill(0).map((_, i) =>
`第${i+1}天: ${Math.random() > 0.5 ? '晴天' : '多云'}, ${20 + Math.floor(Math.random() * 10)}°C`
).join('\n');
return {
content: [{
type: "text",
text: `${city}未来${days}天的天气预报:\n${forecast}`
}]
};
}
);
// 添加天气咨询提示模板
server.prompt(
"weatherAdvice",
{ city: z.string() },
({ city }) => ({
messages: [{
role: "user",
content: {
type: "text",
text: `请根据${city}的天气情况,给我适合的着装和活动建议。`
}
}]
})
);
// 启动服务器
const transport = new StdioServerTransport();
await server.connect(transport);
这个更完整的例子展示了 MCP 的三个核心组件:
- 资源:城市天气信息,LLM 可以查询特定城市的当前天气
- 工具:天气预报功能,LLM 可以获取未来几天的天气预测
- 提示模板:天气咨询模板,提供标准化的咨询方式
MCP 的工作原理:幕后解析
你可能好奇 MCP 到底是如何工作的。想象一下餐厅里的点餐系统:顾客(LLM)通过菜单(定义好的接口)向服务员(MCP 协议)点餐,服务员将订单传递给厨房(外部系统),然后将美食(数据/结果)送回给顾客。
MCP 的通信流程
- 连接建立:LLM 应用与 MCP 服务器建立连接
- 能力发现:应用了解服务器提供哪些资源、工具和提示
- 请求执行:应用发送请求(查询资源、调用工具、使用提示)
- 响应处理:服务器处理请求并返回结果
- 上下文整合:LLM 将获取的信息整合到对话上下文中
MCP 使用 JSON 格式的消息进行通信,类似于这样:
// 工具调用请求
{
"type": "call_tool_request",
"id": "req-123",
"params": {
"name": "forecast",
"arguments": {
"city": "北京",
"days": 3
}
}
}
// 工具调用响应
{
"type": "call_tool_response",
"id": "resp-123",
"request_id": "req-123",
"result": {
"content": [
{
"type": "text",
"text": "北京未来3天的天气预报:\n第1天: 晴天, 25°C\n第2天: 多云, 23°C\n第3天: 晴天, 26°C"
}
]
}
}
通信方式
MCP 支持多种通信方式,适应不同的应用场景:
- 标准输入/输出 (stdio):适用于命令行工具和本地应用
- HTTP + Server-Sent Events (SSE):适用于远程服务和网络应用
- WebSockets:提供双向实时通信
MCP 的进阶应用:解锁更多可能
掌握了基础知识后,让我们探索 MCP 的一些进阶应用场景。
数据库集成
将 MCP 与数据库结合,让 LLM 能够查询和分析数据:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import sqlite3 from "sqlite3";
import { promisify } from "util";
// 创建 MCP 服务器
const server = new Server({
name: "数据库助手",
version: "1.0.0"
});
// 辅助函数:创建数据库连接
const getDb = () => {
const db = new sqlite3.Database("company.db");
return {
all: promisify(db.all.bind(db)),
close: promisify(db.close.bind(db))
};
};
// 暴露数据库架构作为资源
server.resource(
"schema",
"db://schema",
async (uri) => {
const db = getDb();
try {
const tables = await db.all(
"SELECT name, sql FROM sqlite_master WHERE type='table'"
);
return {
contents: [{
uri: uri.href,
text: tables.map(t => t.sql).join("\n\n")
}]
};
} finally {
await db.close();
}
}
);
// 添加 SQL 查询工具
server.tool(
"query",
{ sql: z.string() },
async ({ sql }) => {
const db = getDb();
try {
// 安全检查(实际应用中需要更严格的检查)
if (sql.toLowerCase().includes("drop") ||
sql.toLowerCase().includes("delete")) {
return {
content: [{ type: "text", text: "禁止执行破坏性操作" }],
isError: true
};
}
const results = await db.all(sql);
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
} catch (err) {
return {
content: [{ type: "text", text: `错误: ${err.message}` }],
isError: true
};
} finally {
await db.close();
}
}
);
// 启动服务器
const transport = new StdioServerTransport();
await server.connect(transport);
这个例子创建了一个数据库助手,它可以:
- 提供数据库表结构信息(资源)
- 执行 SQL 查询并返回结果(工具)
这样,LLM 就能够了解数据库结构,并根据用户需求执行查询,实现类似”数据分析师”的功能。
文件系统访问
让 LLM 能够读取和(有限制地)写入文件:
import { Server, ResourceTemplate } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import fs from "fs/promises";
import path from "path";
// 创建 MCP 服务器
const server = new Server({
name: "文件助手",
version: "1.0.0"
});
// 设置安全的根目录
const ROOT_DIR = "./safe_files";
// 文件读取资源
server.resource(
"file",
new ResourceTemplate("file://{filePath*}", { list: async (uri) => {
// 列出目录内容
const dirPath = path.join(ROOT_DIR, uri.pathname.slice(1) || "");
try {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
return entries.map(entry => {
const entryPath = path.join(uri.pathname.slice(1) || "", entry.name);
return { uri: `file:/${entryPath}` };
});
} catch (err) {
return [];
}
}}),
async (uri, { filePath }) => {
// 安全检查:确保路径在允许范围内
const normalizedPath = path.normalize(filePath);
if (normalizedPath.startsWith("..")) {
return {
contents: [{
uri: uri.href,
text: "访问被拒绝:路径超出允许范围"
}]
};
}
// 读取文件内容
const fullPath = path.join(ROOT_DIR, normalizedPath);
try {
const stat = await fs.stat(fullPath);
if (stat.isDirectory()) {
// 如果是目录,列出内容
const entries = await fs.readdir(fullPath);
return {
contents: [{
uri: uri.href,
text: `目录 ${normalizedPath}:\n${entries.join("\n")}`
}]
};
} else {
// 读取文件内容
const content = await fs.readFile(fullPath, "utf-8");
return {
contents: [{
uri: uri.href,
text: content
}]
};
}
} catch (err) {
return {
contents: [{
uri: uri.href,
text: `错误: ${err.message}`
}]
};
}
}
);
// 文件写入工具
server.tool(
"writeFile",
{
path: z.string(),
content: z.string(),
createDirectories: z.boolean().optional().default(false)
},
async ({ path: filePath, content, createDirectories }) => {
// 安全检查
const normalizedPath = path.normalize(filePath);
if (normalizedPath.startsWith("..")) {
return {
content: [{ type: "text", text: "访问被拒绝:路径超出允许范围" }],
isError: true
};
}
const fullPath = path.join(ROOT_DIR, normalizedPath);
try {
// 如果需要创建目录
if (createDirectories) {
await fs.mkdir(path.dirname(fullPath), { recursive: true });
}
// 写入文件
await fs.writeFile(fullPath, content);
return {
content: [{
type: "text",
text: `文件已成功写入到 ${normalizedPath}`
}]
};
} catch (err) {
return {
content: [{ type: "text", text: `错误: ${err.message}` }],
isError: true
};
}
}
);
// 启动服务器
const transport = new StdioServerTransport();
await server.connect(transport);
这个文件助手允许 LLM:
- 浏览和读取文件系统中的文件(资源)
- 在安全的范围内创建和写入文件(工具)
这非常适合用于文档管理、代码生成等场景。
MCP 的安全与隐私:守护大门
给 LLM 提供访问外部世界的能力是把双刃剑——方便是一方面,安全风险是另一方面。就像给一个强大的机器人提供了活动能力,我们需要确保它不会因为误解指令而造成伤害。
MCP 的安全特性
- 访问控制:服务器可以严格控制 LLM 能够访问哪些资源和工具
- 参数验证:使用类型系统(如 Zod)确保输入符合预期
- 沙箱执行:在受限环境中执行操作,防止恶意代码
- 审计日志:记录所有请求和操作,便于追踪和分析
安全最佳实践
在实现 MCP 服务器时,应遵循以下安全原则:
// 安全最佳实践示例
// 1. 严格验证所有输入
server.tool(
"secureOperation",
{
input: z.string().min(1).max(100), // 限制输入长度
options: z.object({ // 验证选项结构
mode: z.enum(["safe", "normal"]), // 限制为预定义选项
depth: z.number().int().min(1).max(10) // 限制数值范围
})
},
async ({ input, options }) => {
// 实现...
}
);
// 2. 限制资源访问范围
server.resource(
"secureResource",
new ResourceTemplate("secure://{path*}", {}),
async (uri, { path }) => {
// 安全检查:路径规范化和验证
const normalizedPath = normalizePath(path);
if (!isPathAllowed(normalizedPath)) {
return { contents: [{ uri: uri.href, text: "访问被拒绝" }] };
}
// 实现...
}
);
// 3. 错误处理和信息隐藏
try {
// 执行操作...
} catch (err) {
// 隐藏敏感的错误细节
return {
content: [{
type: "text",
text: "操作失败,请联系管理员" // 不暴露具体错误
}],
isError: true
};
}
// 4. 实现速率限制
const requestCounter = new Map();
function checkRateLimit(clientId) {
const now = Date.now();
const recentRequests = requestCounter.get(clientId) || [];
// 清理旧请求记录
const recentWindow = recentRequests.filter(time => now - time < 60000);
// 检查是否超过限制(例如每分钟 100 个请求)
if (recentWindow.length >= 100) {
return false;
}
// 更新请求记录
recentWindow.push(now);
requestCounter.set(clientId, recentWindow);
return true;
}
MCP 的未来:展望
MCP 仍处于发展的早期阶段,但它的潜力是巨大的。就像 HTTP 成为了网络通信的基础协议,MCP 有望成为 AI 模型与外部世界交互的标准协议。
未来发展方向
- 更丰富的媒体类型:支持图像、音频、视频等多模态交互
- 更智能的缓存机制:优化性能和响应速度
- 分布式协作:多个 MCP 服务器协同工作,形成能力网络
- 身份与权限管理:更精细的访问控制和用户身份验证
- 跨模型标准化:不同模型之间的无缝协作
实际应用场景
MCP 的应用前景广阔,可以赋能各种场景:
- 企业知识库:LLM 可以访问公司内部文档、规章制度、产品信息
- 个人助理:管理日程、电子邮件、文件,执行日常任务
- 开发辅助:查询代码库、API 文档,生成和测试代码
- 数据分析:直接查询和分析大型数据集,生成报告和可视化
- 物联网控制:安全地管理和控制智能家居和其他联网设备
结语:迈向 AI 新纪元
Model Context Protocol 不仅仅是一个技术规范,它代表了 AI 发展的一个重要里程碑。就像互联网协议栈将计算机连接成网络一样,MCP 有潜力将 AI 模型与外部世界无缝连接,创造全新的可能性。
想象一下将来的场景:你的 AI 助手不再只是一个聊天工具,而是能够为你预订机票、分析财务报表、控制智能家居、编写并运行代码、搜索和整理大量文档——所有这些都通过标准化的 MCP 协议实现。
作为开发者,现在正是探索和参与这一革命性技术的最佳时机。无论你是构建 LLM 应用、创建 MCP 服务器,还是贡献协议本身,都有机会在这个新兴领域留下你的印记。
MCP 就像是给 LLM 装上了”超能力插座”,而你,则是创造这些”超能力”的人。未来已来,就看我们如何塑造它。
资源链接:
- MCP 官方网站:https://modelcontextprotocol.io/
- GitHub 仓库:https://github.com/modelcontextprotocol
- TypeScript SDK:https://github.com/modelcontextprotocol/typescript-sdk
- MCP 规范:https://modelcontextprotocol.io/specification
开始你的 MCP 之旅吧,让我们一起解锁 AI 的无限可能!