写给想搞懂 MCP 的开发者,不讲废话,直接上干货
先说一个让人头疼的问题
你有没有遇到过这种情况:
你想让 AI 帮你查 GitHub 上的 issue,它说:“我没有访问 GitHub 的权限。”
你想让 AI 帮你操作数据库,它说:“我无法连接到您的数据库。”
你想让 AI 读取本地文件,它说:“我没有文件系统访问权限。”
每次都要自己写一堆胶水代码,把 AI 和各种工具连起来。写完这个项目,下个项目又得重新写一遍。
这就是 MCP 要解决的问题。
MCP 到底是什么?
MCP 全称是 Model Context Protocol(模型上下文协议),是 Anthropic 在 2024 年底推出的一个开放标准。
一句话解释:
MCP 是 AI 的"USB 接口"——定义了一套标准,让任何 AI 应用都能用同一种方式接入任何外部工具和数据源。
在 MCP 出现之前,每个 AI 应用想接入外部工具,都要自己造轮子,写各种适配代码。就像早年电脑接外设,每个厂商接口都不一样,乱得一塌糊涂。
MCP 就是那个统一的 USB 标准——工具只需要实现一次,所有支持 MCP 的 AI 应用都能直接用。
MCP 的三个核心角色
理解 MCP,先搞清楚三个角色:
┌─────────────────────────────────────────┐
│ MCP Host(宿主) │
│ Claude Desktop / Cursor / 你的应用 │
└──────────────────┬──────────────────────┘
│ MCP 协议
┌──────────┴──────────┐
↓ ↓
┌──────────────┐ ┌──────────────────┐
│ MCP Server │ │ MCP Server │
│ (文件系统) │ │ (数据库) │
└──────────────┘ └──────────────────┘
- MCP Host(宿主) :使用 AI 的应用,比如 Claude Desktop、Cursor、你自己写的 AI 应用
- MCP Server(服务端) :提供具体能力的服务,比如文件读写、数据库查询、GitHub 操作
- MCP Client(客户端) :内嵌在 Host 里,负责和 Server 通信
你只需要写一个 MCP Server,所有支持 MCP 的 Host 都能直接用你的工具。
MCP Server 能提供什么?
MCP Server 可以暴露三种东西:
🔧 Tools(工具)
AI 可以调用的函数,类似 Function Calling。
查询数据库、发送邮件、操作文件、调用 API...
📄 Resources(资源)
AI 可以读取的数据,类似文件系统。
本地文件、数据库记录、API 返回的数据...
💬 Prompts(提示词模板)
预定义的提示词,可以复用。
代码审查模板、文档生成模板、分析报告模板...
来一个真实的代码示例(JavaScript)
用 @modelcontextprotocol/sdk 写一个最简单的 MCP Server:
安装依赖
npm install @modelcontextprotocol/sdk
写一个天气查询 MCP Server
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
// 创建 MCP Server
const server = new McpServer({
name: 'weather-server',
version: '1.0.0',
});
// 注册一个 Tool:查询天气
server.tool(
'get_weather',
'获取指定城市的实时天气信息',
{
city: z.string().describe('城市名称,例如:北京、上海、广州'),
},
async ({ city }) => {
// 实际项目里这里调用真实天气 API
const weatherData = {
北京: { temperature: 22, condition: '晴', humidity: 40 },
上海: { temperature: 25, condition: '多云', humidity: 65 },
广州: { temperature: 30, condition: '阵雨', humidity: 80 },
};
const weather = weatherData[city];
if (!weather) {
return {
content: [{ type: 'text', text: `未找到城市:${city}` }],
};
}
return {
content: [
{
type: 'text',
text: `${city}天气:${weather.condition},气温 ${weather.temperature}°C,湿度 ${weather.humidity}%`,
},
],
};
}
);
// 注册一个 Resource:读取本地文件
server.resource(
'local-file',
'file://{path}',
async (uri) => {
const path = uri.pathname;
const fs = await import('fs/promises');
try {
const content = await fs.readFile(path, 'utf-8');
return {
contents: [{ uri: uri.href, mimeType: 'text/plain', text: content }],
};
} catch (error) {
throw new Error(`读取文件失败:${error.message}`);
}
}
);
// 启动 Server(通过标准输入输出通信)
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Weather MCP Server 已启动');
就这么几十行,一个 MCP Server 就写好了。
在 Claude Desktop 里使用
在 Claude Desktop 的配置文件里加上你的 Server:
// ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/path/to/your/weather-server.js"]
}
}
}
重启 Claude Desktop,它就能直接调用你的天气查询工具了。
一个更完整的例子:数据库查询 Server
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import Database from 'better-sqlite3';
const server = new McpServer({
name: 'sqlite-server',
version: '1.0.0',
});
const db = new Database('./mydata.db');
// Tool:执行 SQL 查询
server.tool(
'query_database',
'执行 SQL 查询并返回结果,只支持 SELECT 语句',
{
sql: z.string().describe('要执行的 SQL 查询语句,只允许 SELECT'),
},
async ({ sql }) => {
// 安全检查:只允许 SELECT
if (!sql.trim().toUpperCase().startsWith('SELECT')) {
return {
content: [{ type: 'text', text: '错误:只允许执行 SELECT 查询' }],
isError: true,
};
}
try {
const rows = db.prepare(sql).all();
return {
content: [
{
type: 'text',
text: JSON.stringify(rows, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `查询失败:${error.message}` }],
isError: true,
};
}
}
);
// Tool:获取表结构
server.tool(
'get_table_schema',
'获取数据库中指定表的结构信息',
{
table_name: z.string().describe('表名'),
},
async ({ table_name }) => {
try {
const schema = db
.prepare(`PRAGMA table_info(${table_name})`)
.all();
return {
content: [
{
type: 'text',
text: JSON.stringify(schema, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `获取表结构失败:${error.message}` }],
isError: true,
};
}
}
);
// Resource:暴露数据库表列表
server.resource(
'database-tables',
'db://tables',
async () => {
const tables = db
.prepare(`SELECT name FROM sqlite_master WHERE type='table'`)
.all();
return {
contents: [
{
uri: 'db://tables',
mimeType: 'application/json',
text: JSON.stringify(tables, null, 2),
},
],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
配置好之后,你就可以直接对 Claude 说:“帮我查一下 users 表里上个月注册的用户有多少”,它会自己去查数据库,给你真实的结果。
MCP 的通信方式
MCP Server 和 Host 之间有两种通信方式:
Stdio(标准输入输出)
适合本地工具,Server 作为子进程运行:
const transport = new StdioServerTransport();
await server.connect(transport);
HTTP + SSE(服务端推送)
适合远程服务,Server 作为独立的 HTTP 服务运行:
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express';
const app = express();
const transport = new SSEServerTransport('/messages', res);
await server.connect(transport);
app.listen(3000);
MCP vs Function Calling,傻傻分不清楚?
很多人会问:MCP 和 Function Calling 有什么区别?
| 对比项 | MCP | Function Calling |
|---|---|---|
| 定位 | 标准协议,跨应用复用 | 单次对话内的工具调用 |
| 复用性 | 写一次,所有 Host 都能用 | 每个应用自己实现 |
| 适合场景 | 工具生态、持久化服务 | 单个应用内的功能扩展 |
| 部署方式 | 独立进程 / 远程服务 | 内嵌在应用代码里 |
| 标准化 | 有统一协议规范 | 各家 API 格式不同 |
简单来说:
- Function Calling 是"这个应用里我能调用哪些函数"
- MCP 是"我写了一个工具,所有 AI 应用都能用"
两者不是替代关系,很多 MCP Server 内部就是用 Function Calling 的思路实现的。
现成的 MCP Server,拿来就用
不想自己写?社区已经有大量现成的 MCP Server:
| Server | 功能 | 地址 |
|---|---|---|
@modelcontextprotocol/server-filesystem | 本地文件读写 | 官方 |
@modelcontextprotocol/server-github | GitHub 操作 | 官方 |
@modelcontextprotocol/server-postgres | PostgreSQL 查询 | 官方 |
@modelcontextprotocol/server-brave-search | Brave 搜索 | 官方 |
@modelcontextprotocol/server-slack | Slack 消息 | 官方 |
mcp-server-sqlite | SQLite 查询 | 社区 |
安装官方文件系统 Server:
npx @modelcontextprotocol/server-filesystem /your/directory
加到 Claude Desktop 配置:
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"@modelcontextprotocol/server-filesystem",
"/Users/yourname/Documents"
]
}
}
}
配置完重启,Claude 就能直接读写你的文档目录了。
几个容易踩的坑
坑 1:忘了处理错误返回
MCP Tool 的返回里有个 isError 字段,出错时要设置为 true,这样 AI 才知道调用失败了,会尝试其他方式。
// ❌ 错误做法:直接 throw
throw new Error('查询失败');
// ✅ 正确做法:返回错误信息
return {
content: [{ type: 'text', text: `查询失败:${error.message}` }],
isError: true,
};
坑 2:Tool 描述不够清晰
和 Function Calling 一样,Tool 的描述越详细,AI 越知道什么时候该用它。
坑 3:没有做权限控制
MCP Server 有访问本地资源的能力,一定要做好权限控制,别让 AI 随便删文件、执行危险 SQL。
// 数据库操作只允许 SELECT
if (!sql.trim().toUpperCase().startsWith('SELECT')) {
return { content: [{ type: 'text', text: '只允许 SELECT 查询' }], isError: true };
}
// 文件操作限制在指定目录
if (!filePath.startsWith(ALLOWED_DIR)) {
return { content: [{ type: 'text', text: '不允许访问该目录' }], isError: true };
}
坑 4:Stdio 模式下别用 console.log
Stdio 模式下,标准输出是用来和 Host 通信的,你的日志要用 console.error 输出到标准错误,否则会破坏通信协议。
// ❌ 会破坏通信
console.log('Server 启动了');
// ✅ 正确做法
console.error('Server 启动了');
进阶:给你的 AI 应用接入 MCP
不只是 Claude Desktop,你自己写的 AI 应用也可以作为 MCP Host:
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
// 连接到 MCP Server
const transport = new StdioClientTransport({
command: 'node',
args: ['./weather-server.js'],
});
const client = new Client({ name: 'my-app', version: '1.0.0' }, {});
await client.connect(transport);
// 列出所有可用工具
const { tools } = await client.listTools();
console.log('可用工具:', tools.map(t => t.name));
// 调用工具
const result = await client.callTool({
name: 'get_weather',
arguments: { city: '北京' },
});
console.log('结果:', result.content[0].text);
把这些工具列表传给 OpenAI 或其他大模型,就实现了完整的 MCP Host。
总结
MCP 的核心思想就一句话:
定义一套标准,让 AI 工具的开发和使用彻底解耦——工具只需要写一次,所有 AI 应用都能用。
它解决的是 AI 生态里的"碎片化"问题,让工具开发者和 AI 应用开发者都能专注自己的事。
对于开发者来说,现在是入场 MCP 生态的好时机:
- 用现成的 Server:直接用官方和社区的 MCP Server,快速扩展 AI 能力
- 写自己的 Server:把你的业务系统包装成 MCP Server,让 AI 直接操作
- 做 MCP Host:在你的 AI 应用里支持 MCP,接入整个工具生态
推荐学习路径:
- 在 Claude Desktop 里配置一个官方 MCP Server(比如文件系统)
- 跑通上面的天气查询示例
- 把你自己项目里的一个接口包装成 MCP Server
跑通了,你就真正理解 MCP 了。