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 可调用的标准工具”。


总结

  1. MCP Server 就是 AI 的工具包——几十行 Python 代码,让大模型具备操作本地环境的能力
  2. STDIO 适合个人使用HTTP+SSE 适合团队共享,按场景选择
  3. 安全是第一优先级:永远用 safe_path 或等价机制限制 AI 的文件访问范围
  4. 先用 mcp dev 调试通过,再接入客户端,能避免 80% 的配置坑

延伸阅读