30秒速答
MCP Server 是你为 AI 应用编写的”插件”,让大模型能直接访问你的本地数据和服务。 只需几十行 Python 代码,Claude Desktop、Cursor 就能读写你的文件、查询数据库,就像给 AI 插上了 USB-C 数据线。
准备工作:理解 MCP 的通信架构
在动手之前,先搞清楚 MCP 的通信方式。MCP 支持两种传输模式:
| 模式 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| STDIO | 本地桌面应用(Claude Desktop、Cursor) | 零网络配置、绝对安全 | 仅限本机 |
| HTTP+SSE | 远程服务、Web 应用、团队共享 | 跨机器访问、易扩展 | 需处理认证和安全 |
本文先带你用 STDIO 模式搭建第一个 Server,再升级到 HTTP+SSE 模式。
所需环境:
- Python 3.10+
- 一个支持 MCP 的客户端(推荐 Claude Desktop 或 Cursor)
第一步:5 分钟搭建你的第一个 MCP Server
安装 SDK
pip install mcp
编写 Server 代码
创建一个 weather_server.py:
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationCapabilities
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import asyncio
# 创建 Server 实例
server = Server("weather-service")
@server.list_tools()
async def list_tools() -> list[Tool]:
"""告诉客户端:我能提供什么工具"""
return [
Tool(
name="get_current_weather",
description="获取指定城市的实时天气",
inputSchema={
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如 '北京'、'Shanghai'"
}
},
"required": ["city"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""处理客户端的工具调用"""
if name == "get_current_weather":
city = arguments.get("city", "未知")
# 这里可以接入真实天气 API
return [TextContent(
type="text",
text=f"{city}当前天气:晴,25°C,湿度45%,风速3级"
)]
return [TextContent(type="text", text="未找到该工具")]
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
InitializationCapabilities(
sampling={},
roots={}
)
)
if __name__ == "__main__":
asyncio.run(main())
配置 Claude Desktop
编辑 ~/Library/Application Support/Claude/claude_desktop_config.json(macOS):
{
"mcpServers": {
"weather-service": {
"command": "python",
"args": ["/你的路径/weather_server.py"]
}
}
}
重启 Claude Desktop,在对话中输入”查一下北京天气”,AI 就会自动调用你写的 Server。
第二步:构建一个真正有用的 File Reader Server
来写一个能让 AI 读写本地文件的 Server,这才是开发者日常高频需求。
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.server.models import InitializationCapabilities
from mcp.types import Tool, TextContent
import os
import asyncio
server = Server("local-file-service")
SAFE_DIR = os.path.expanduser("~/mcp-workspace") # 限制访问范围
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="read_local_file",
description="读取本地文件内容",
inputSchema={
"type": "object",
"properties": {
"filename": {"type": "string", "description": "文件名(相对 mcp-workspace)"}
},
"required": ["filename"]
}
),
Tool(
name="write_local_file",
description="写入内容到本地文件",
inputSchema={
"type": "object",
"properties": {
"filename": {"type": "string"},
"content": {"type": "string", "description": "要写入的文本内容"}
},
"required": ["filename", "content"]
}
),
Tool(
name="list_local_files",
description="列出工作目录中的所有文件",
inputSchema={
"type": "object",
"properties": {}
}
)
]
def safe_path(filename: str) -> str:
"""防止路径穿越攻击"""
abs_path = os.path.abspath(os.path.join(SAFE_DIR, filename))
if not abs_path.startswith(SAFE_DIR):
raise ValueError("禁止访问该路径")
return abs_path
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
try:
if name == "read_local_file":
path = safe_path(arguments["filename"])
with open(path, "r") as f:
content = f.read()
return [TextContent(type="text", text=content)]
elif name == "write_local_file":
path = safe_path(arguments["filename"])
with open(path, "w") as f:
f.write(arguments["content"])
return [TextContent(type="text", text=f"已写入 {path}")]
elif name == "list_local_files":
files = os.listdir(SAFE_DIR)
return [TextContent(type="text", text="\n".join(files))]
return [TextContent(type="text", text="未找到该工具")]
except Exception as e:
return [TextContent(type="text", text=f"错误:{str(e)}")]
async def main():
os.makedirs(SAFE_DIR, exist_ok=True)
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream,
InitializationCapabilities(sampling={}, roots={}))
if __name__ == "__main__":
asyncio.run(main())
安全提示:safe_path() 函数是关键防线——它确保 AI 只能访问 ~/mcp-workspace/ 目录,防止 AI 读取你的 .ssh 或 .env 文件。
第三步:升级到 HTTP+SSE 模式(团队共享版)
当你的 MCP Server 需要被团队成员或 Web 应用调用时,用 HTTP+SSE 模式:
from mcp.server import Server
from mcp.server.sse import SseServerTransport
from mcp.server.models import InitializationCapabilities
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import Response
import uvicorn
server = Server("shared-tools-service")
@server.list_tools()
async def list_tools():
# ... 同上,定义你的工具
pass
@server.call_tool()
async def call_tool(name, arguments):
# ... 同上,处理工具调用
pass
# 创建 HTTP 应用
sse = SseServerTransport("/messages")
async def handle_sse(request):
async with sse.connect_sse(
request.scope, request.receive, request._send
) as streams:
await server.run(
streams[0], streams[1],
InitializationCapabilities(sampling={}, roots={})
)
async def handle_messages(request):
await sse.handle_post_message(request.scope, request.receive, request._send)
return Response()
app = Starlette(routes=[
Route("/sse", endpoint=handle_sse),
Route("/messages", endpoint=handle_messages, methods=["POST"]),
])
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8080)
客户端连接时只需将配置改为:
{
"mcpServers": {
"shared-service": {
"url": "http://你的IP:8080/sse"
}
}
}
第四步:调试与排错指南
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| Claude Desktop 不显示工具 | JSON 配置路径错误 | 检查 command 路径是否绝对路径,python 是否在 PATH 中 |
| 工具调用超时 | Server 未正确启动 | 先手动运行 python server.py 看有无报错 |
| 中文乱码 | 编码问题 | 确保文件 UTF-8 编码,或设置环境变量 PYTHONIOENCODING=utf-8 |
| HTTP 模式连接失败 | 防火墙 / 端口占用 | 检查端口是否开放,换一个端口再试 |
safe_path 报”禁止访问” |
路径穿越检测触发 | 检查传入的 filename 是否包含 ../ |
开发建议:先用 mcp dev 命令(SDK 内置的调试工具)测试 Server 是否正确响应,再配置到 Claude Desktop,能省去大量排查时间。
现实场景:MCP Server 还可以做什么?
MCP 的边界由你的想象力决定。以下是开发者社区已经实现的真实案例:
- 数据库查询 Server:让 AI 直接写 SQL 查询 PostgreSQL/MySQL,返回表格结果
- Git 操作 Server:AI 帮你查看 diff、切换分支、提交代码
- 浏览器控制 Server:AI 操控 Puppeteer 抓取网页、填表单
- Notion/飞书集成 Server:AI 读写你的笔记和文档
- API 聚合 Server:把多个第三方 API 包装成统一工具集,AI 自动编排调用
核心理念:MCP Server 本质是”把任何能写成代码的功能,变成 AI 可调用的标准工具”。
总结
- MCP Server 就是 AI 的工具包——几十行 Python 代码,让大模型具备操作本地环境的能力
- STDIO 适合个人使用,HTTP+SSE 适合团队共享,按场景选择
- 安全是第一优先级:永远用
safe_path或等价机制限制 AI 的文件访问范围 - 先用
mcp dev调试通过,再接入客户端,能避免 80% 的配置坑
延伸阅读: