MCP 入门指南:让 AI 连接真实世界

本文将带你从零开始理解 MCP(Model Context Protocol),并通过 TypeScript 和 Python 两种语言实现一个完整的 MCP Server。
大语言模型很强大,但它们被困在"沙盒"里------无法读取你的本地文件、无法查询你的数据库、无法调用你的内部 API。每个 AI 应用都在重复造轮子:写 Function Calling 的适配代码、处理工具调用的序列化、管理上下文注入...
MCP(Model Context Protocol) 就是为了解决这个问题而生的。它是 Anthropic 在 2024 年底开源的协议标准,目标是成为 AI 模型与外部世界交互的"USB 接口"------一次实现,处处可用。
什么是 MCP?
MCP 是一个开放协议,定义了 AI 应用(如 Claude Desktop、IDE 插件)与外部工具/数据源之间的通信标准。
核心价值:
| 传统方式 | MCP 方式 |
|---|---|
| 每个 AI 应用自己实现工具调用 | 统一协议,一次实现多处复用 |
| 工具代码与应用紧耦合 | Server 独立部署,即插即用 |
| 安全边界模糊 | 明确的权限模型和沙盒隔离 |
简单来说:你写一个 MCP Server,就能让所有支持 MCP 的 AI 应用使用你的工具。
核心概念
MCP 架构包含三个核心角色:
┌─────────────────────────────────────────────────────────┐
│ Host │
│ (Claude Desktop / IDE / 自定义 AI 应用) │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Client │ │ Client │ │ Client │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
└────────┼─────────────┼─────────────┼────────────────────┘
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Server │ │ Server │ │ Server │
│ (文件) │ │ (数据库) │ │ (API) │
└─────────┘ └─────────┘ └─────────┘
- Host:运行 AI 模型的宿主应用,负责管理多个 Client 连接
- Client:由 Host 创建,与 Server 保持 1:1 连接,处理协议通信
- Server:独立进程,暴露工具(Tools)、资源(Resources)和提示词(Prompts)
通信基于 JSON-RPC 2.0,支持两种传输方式:
- stdio:通过标准输入/输出通信,适合本地进程
- HTTP + SSE:适合远程服务,支持服务端推送
MCP 能做什么
MCP Server 可以向 AI 暴露三种能力:
Tools(工具)
让 AI 执行操作。工具由 AI 模型主动调用,类似 Function Calling。
json
{
"name": "search_files",
"description": "在指定目录中搜索文件",
"inputSchema": {
"type": "object",
"properties": {
"path": { "type": "string" },
"pattern": { "type": "string" }
}
}
}
典型场景:执行 shell 命令、查询数据库、调用 API、发送消息
Resources(资源)
让 AI 读取数据。资源是只读的,由应用程序控制何时加载。
json
{
"uri": "file:///project/config.json",
"name": "项目配置",
"mimeType": "application/json"
}
典型场景:读取文件内容、获取数据库 schema、加载配置信息
Prompts(提示词模板)
预定义的提示词,可带参数,方便复用。
json
{
"name": "code_review",
"description": "代码审查模板",
"arguments": [
{ "name": "language", "required": true }
]
}
典型场景:代码审查模板、SQL 生成模板、文档生成模板
快速上手
环境准备
TypeScript 版本:
bash
# 需要 Node.js 18+
node --version
# 创建项目
mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
# 初始化 TypeScript
npx tsc --init
Python 版本:
bash
# 需要 Python 3.10+
python --version
# 创建项目
mkdir my-mcp-server && cd my-mcp-server
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 安装依赖
pip install mcp
TypeScript 实现
创建 src/index.ts,实现一个简单的天气查询工具:
typescript
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",
});
// 模拟天气数据
const weatherData: Record<string, { temp: number; condition: string }> = {
beijing: { temp: 22, condition: "晴" },
shanghai: { temp: 25, condition: "多云" },
shenzhen: { temp: 28, condition: "阵雨" },
};
// 注册工具
server.tool(
"get_weather",
"获取指定城市的天气信息",
{
city: z.string().describe("城市名称(拼音)"),
},
async ({ city }) => {
const weather = weatherData[city.toLowerCase()];
if (!weather) {
return {
content: [
{
type: "text",
text: `未找到城市 "${city}" 的天气数据`,
},
],
};
}
return {
content: [
{
type: "text",
text: `${city} 天气:${weather.condition},温度 ${weather.temp}°C`,
},
],
};
}
);
// 启动服务器
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP Server 已启动");
}
main().catch(console.error);
配置 package.json:
json
{
"name": "weather-mcp-server",
"version": "1.0.0",
"type": "module",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsx src/index.ts"
}
}
Python 实现
创建 server.py,实现相同的天气查询工具:
python
from mcp.server.fastmcp import FastMCP
# 创建 MCP Server 实例
mcp = FastMCP("weather-server")
# 模拟天气数据
weather_data = {
"beijing": {"temp": 22, "condition": "晴"},
"shanghai": {"temp": 25, "condition": "多云"},
"shenzhen": {"temp": 28, "condition": "阵雨"},
}
@mcp.tool()
def get_weather(city: str) -> str:
"""获取指定城市的天气信息
Args:
city: 城市名称(拼音)
"""
weather = weather_data.get(city.lower())
if not weather:
return f'未找到城市 "{city}" 的天气数据'
return f"{city} 天气:{weather['condition']},温度 {weather['temp']}°C"
# 也可以添加资源
@mcp.resource("weather://cities")
def list_cities() -> str:
"""返回支持的城市列表"""
return "\n".join(weather_data.keys())
if __name__ == "__main__":
mcp.run()
运行方式:
bash
# 直接运行(stdio 模式)
python server.py
# 或使用 mcp 命令行工具测试
mcp dev server.py
在 Claude Desktop 中测试
编辑 Claude Desktop 配置文件:
- macOS :
~/Library/Application Support/Claude/claude_desktop_config.json - Windows :
%APPDATA%\Claude\claude_desktop_config.json
TypeScript 版本配置:
json
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["/absolute/path/to/dist/index.js"]
}
}
}
Python 版本配置:
json
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/absolute/path/to/server.py"]
}
}
}
重启 Claude Desktop 后,在对话中输入:
"北京今天天气怎么样?"
Claude 会自动调用 get_weather 工具并返回结果。
实战案例:文件搜索工具
下面实现一个更实用的案例------文件搜索工具,支持在指定目录中按名称或内容搜索文件。
TypeScript 版本
typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "fs/promises";
import * as path from "path";
const server = new McpServer({
name: "file-search-server",
version: "1.0.0",
});
// 递归搜索文件
async function searchFiles(
dir: string,
pattern: string,
results: string[] = []
): Promise<string[]> {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
// 跳过 node_modules 和隐藏目录
if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
await searchFiles(fullPath, pattern, results);
}
} else if (entry.name.toLowerCase().includes(pattern.toLowerCase())) {
results.push(fullPath);
}
}
return results;
}
// 搜索文件名
server.tool(
"search_by_name",
"按文件名搜索文件",
{
directory: z.string().describe("搜索的根目录"),
pattern: z.string().describe("文件名匹配模式"),
},
async ({ directory, pattern }) => {
try {
const results = await searchFiles(directory, pattern);
return {
content: [
{
type: "text",
text:
results.length > 0
? `找到 ${results.length} 个文件:\n${results.join("\n")}`
: "未找到匹配的文件",
},
],
};
} catch (error) {
return {
content: [{ type: "text", text: `搜索失败: ${error}` }],
isError: true,
};
}
}
);
// 搜索文件内容
server.tool(
"search_by_content",
"按内容搜索文件",
{
directory: z.string().describe("搜索的根目录"),
keyword: z.string().describe("要搜索的关键词"),
extension: z.string().optional().describe("文件扩展名过滤,如 .ts"),
},
async ({ directory, keyword, extension }) => {
const results: { file: string; line: number; content: string }[] = [];
async function search(dir: string) {
const entries = await fs.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (!entry.name.startsWith(".") && entry.name !== "node_modules") {
await search(fullPath);
}
} else if (!extension || entry.name.endsWith(extension)) {
try {
const content = await fs.readFile(fullPath, "utf-8");
const lines = content.split("\n");
lines.forEach((line, index) => {
if (line.includes(keyword)) {
results.push({
file: fullPath,
line: index + 1,
content: line.trim().slice(0, 100),
});
}
});
} catch {
// 跳过无法读取的文件
}
}
}
}
await search(directory);
return {
content: [
{
type: "text",
text:
results.length > 0
? results
.slice(0, 20)
.map((r) => `${r.file}:${r.line}\n ${r.content}`)
.join("\n\n")
: "未找到包含该关键词的文件",
},
],
};
}
);
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
}
main().catch(console.error);
Python 版本
python
import os
from pathlib import Path
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("file-search-server")
@mcp.tool()
def search_by_name(directory: str, pattern: str) -> str:
"""按文件名搜索文件
Args:
directory: 搜索的根目录
pattern: 文件名匹配模式
"""
results = []
root = Path(directory)
if not root.exists():
return f"目录不存在: {directory}"
for path in root.rglob("*"):
# 跳过隐藏目录和 node_modules
if any(part.startswith(".") or part == "node_modules"
for part in path.parts):
continue
if path.is_file() and pattern.lower() in path.name.lower():
results.append(str(path))
if results:
return f"找到 {len(results)} 个文件:\n" + "\n".join(results[:50])
return "未找到匹配的文件"
@mcp.tool()
def search_by_content(
directory: str,
keyword: str,
extension: str | None = None
) -> str:
"""按内容搜索文件
Args:
directory: 搜索的根目录
keyword: 要搜索的关键词
extension: 文件扩展名过滤,如 .py
"""
results = []
root = Path(directory)
if not root.exists():
return f"目录不存在: {directory}"
pattern = f"*{extension}" if extension else "*"
for path in root.rglob(pattern):
if any(part.startswith(".") or part == "node_modules"
for part in path.parts):
continue
if not path.is_file():
continue
try:
content = path.read_text(encoding="utf-8")
for i, line in enumerate(content.split("\n"), 1):
if keyword in line:
results.append(f"{path}:{i}\n {line.strip()[:100]}")
except (UnicodeDecodeError, PermissionError):
continue
if results:
return "\n\n".join(results[:20])
return "未找到包含该关键词的文件"
if __name__ == "__main__":
mcp.run()
最佳实践
安全注意事项
typescript
// 1. 路径验证:防止路径遍历攻击
import * as path from "path";
function validatePath(userPath: string, allowedRoot: string): string {
const resolved = path.resolve(allowedRoot, userPath);
if (!resolved.startsWith(allowedRoot)) {
throw new Error("路径越界");
}
return resolved;
}
// 2. 输入验证:使用 zod 定义严格的 schema
const schema = {
command: z.enum(["read", "list"]), // 限制允许的命令
path: z.string().max(500), // 限制长度
};
// 3. 权限最小化:只暴露必要的工具
错误处理
typescript
server.tool("safe_operation", "安全操作示例", {}, async () => {
try {
const result = await riskyOperation();
return {
content: [{ type: "text", text: result }],
};
} catch (error) {
// 返回友好的错误信息,避免泄露内部细节
return {
content: [
{
type: "text",
text: `操作失败: ${error instanceof Error ? error.message : "未知错误"}`,
},
],
isError: true, // 标记为错误响应
};
}
});
调试技巧
bash
# 1. 使用 MCP Inspector 调试(推荐)
npx @modelcontextprotocol/inspector node dist/index.js
# 2. 日志输出到 stderr(stdout 用于协议通信)
console.error("Debug:", someValue);
# 3. Python 使用 mcp dev 命令
mcp dev server.py
性能优化
- 对于耗时操作,返回进度信息
- 大量数据分页返回,避免一次性返回过多内容
- 缓存频繁访问的资源
总结与资源
本文要点
- MCP 是什么:AI 与外部工具交互的标准化协议
- 核心架构:Host → Client → Server 的分层设计
- 三大能力:Tools(执行)、Resources(读取)、Prompts(模板)
- 快速开发 :TypeScript 用
@modelcontextprotocol/sdk,Python 用FastMCP
下一步
- 尝试编写自己的 MCP Server
- 探索社区已有的 Server 实现
- 在实际项目中集成 MCP 能力
资源链接
| 资源 | 链接 |
|---|---|
| MCP 官方文档 | https://modelcontextprotocol.io |
| TypeScript SDK | https://github.com/modelcontextprotocol/typescript-sdk |
| Python SDK | https://github.com/modelcontextprotocol/python-sdk |
| 官方 Server 示例 | https://github.com/modelcontextprotocol/servers |
| MCP Inspector | https://github.com/modelcontextprotocol/inspector |
社区优秀 Server 推荐
- filesystem: 文件系统操作
- sqlite: SQLite 数据库查询
- github: GitHub API 集成
- slack: Slack 消息发送
- puppeteer: 浏览器自动化
如有问题或建议,欢迎在评论区交流!