首页 › tutorials › MCP服务器开发:Python实战全流程

Claude Code MCP服务器开发:Python实战全流程

概述

本教程面向有一定Python基础的开发者,从MCP协议原理讲起,逐步带你用Python FastMCP框架构建一个完整的MCP工具服务器,并接入Claude Code使用。学完后你将掌握MCP三大原语(Tools、Resources、Prompts)的实现方法、调试技巧和部署方案,具备为公司内部系统开发专属MCP工具的能力。

适用人群:后端开发者、DevOps工程师、AI应用开发者

前置条件

项目 要求
Python 3.10+
Node.js 18+(用于MCP Inspector调试)
Claude Code 已安装并配置
包管理器 uv 或 pip
基础知识 REST API概念、JSON格式

一、MCP协议核心架构

在写代码之前,理解MCP的通信模型至关重要。MCP(Model Context Protocol)是Anthropic推出的开放协议,目标是标准化AI模型与外部工具/数据源之间的连接方式——被称为"AI的USB-C接口"。

1.1 通信架构

图表1

图表 1

关键角色: - Host(宿主):Claude Code、Claude Desktop等AI应用 - Client(客户端):嵌入Host中的协议客户端,与Server保持1:1连接 - Server(服务器):暴露工具、资源、提示词的轻量级程序

1.2 传输机制

MCP支持两种传输方式:

传输方式 原理 适用场景
Stdio 通过stdin/stdout管道通信,JSON-RPC 2.0编码 本地开发、Claude Code集成(最常用)
Streamable HTTP HTTP POST请求 + SSE流式响应 远程部署、团队共享、生产环境

Claude Code中90%的场景使用Stdio传输,Server作为子进程被自动拉起。

1.3 连接生命周期

图表2

图表 2

生命周期分为三个阶段: 1. 初始化(Initialization):Client与Server交换能力声明 2. 运行(Operation):正常的请求-响应通信 3. 关闭(Shutdown):连接终止


二、三大原语:Tools、Resources、Prompts

MCP Server通过三种原语(Primitives)向AI暴露能力。理解何时使用哪种原语,是设计好服务器的关键。

2.1 Tools(工具)—— 模型主动调用的操作

Tools是模型控制(model-controlled)的原语。AI根据用户需求自主决定调用哪个工具、传什么参数。

适用场景:执行计算、调用API、修改数据、运行脚本等有副作用的操作。

2.2 Resources(资源)—— 应用驱动的数据暴露

Resources是应用控制(application-controlled)的原语。由Host应用决定何时读取,类似于REST API的GET请求。

适用场景:暴露文件内容、数据库记录、配置信息等只读数据。

2.3 Prompts(提示词)—— 可复用的模板

Prompts是用户控制(user-controlled)的原语。提供预定义的提示词模板,用户可以在对话中主动选择使用。

适用场景:标准化工作流模板、常用查询模式、角色扮演预设。

graph LR
    subgraph 谁来触发?
        T["Tools<br/>模型自主决策"]
        R["Resources<br/>应用按需读取"]
        P["Prompts<br/>用户主动选择"]
    end

    T -->|有副作用| Act["执行操作"]
    R -->|只读| Data["获取数据"]
    P -->|模板化| Tmpl["复用提示"]

    style T fill:#ffcdd2
    style R fill:#c8e6c9
    style P fill:#bbdefb

三、环境搭建

3.1 创建项目

# 创建项目目录
mkdir mcp-weather-server && cd mcp-weather-server

# 使用 uv 初始化(推荐)
uv init
uv add "mcp[cli]"

# 或使用 pip
pip install "mcp[cli]"

mcp[cli] 包含了 FastMCP 框架和 MCP Inspector 调试工具。

3.2 项目结构

mcp-weather-server/
├── pyproject.toml
├── weather_server.py    # 主服务器文件
├── .mcp.json            # Claude Code 配置(后文创建)
└── README.md

四、实战:构建天气预报MCP服务器

我们构建一个完整的天气预报MCP服务器,同时演示Tools、Resources、Prompts三种原语的用法。

4.1 完整服务器代码

创建 weather_server.py:

"""
天气预报 MCP Server
演示 Tools / Resources / Prompts 三种原语的完整实现
"""

import asyncio
import json
from datetime import datetime, timedelta
from typing import Optional

from mcp.server.fastmcp import FastMCP

# 初始化 FastMCP 服务器
mcp = FastMCP(
    name="weather-server",
    version="1.0.0",
    instructions="天气查询服务器,支持实时天气、预报和天气报告模板",
)

# ============================================================
# 模拟数据(实际项目中替换为真实 API 调用)
# ============================================================

WEATHER_DB = {
    "北京": {"temp": 22, "humidity": 45, "wind": "东北风3级", "condition": "晴", "aqi": 85},
    "上海": {"temp": 26, "humidity": 72, "wind": "东南风2级", "condition": "多云", "aqi": 62},
    "深圳": {"temp": 30, "humidity": 80, "wind": "南风2级", "condition": "阵雨", "aqi": 45},
    "成都": {"temp": 24, "humidity": 65, "wind": "微风", "condition": "阴", "aqi": 78},
    "广州": {"temp": 31, "humidity": 78, "wind": "南风3级", "condition": "雷阵雨", "aqi": 55},
}


def get_mock_forecast(city: str, days: int) -> list[dict]:
    """生成模拟天气预报数据"""
    base = WEATHER_DB.get(city, {"temp": 20, "humidity": 50, "wind": "微风", "condition": "晴", "aqi": 60})
    forecast = []
    conditions = ["晴", "多云", "阴", "小雨", "中雨", "阵雨", "雷阵雨"]
    for i in range(days):
        date = (datetime.now() + timedelta(days=i + 1)).strftime("%Y-%m-%d")
        import random
        forecast.append({
            "date": date,
            "temp_high": base["temp"] + random.randint(-3, 5),
            "temp_low": base["temp"] - random.randint(3, 8),
            "condition": random.choice(conditions),
            "humidity": base["humidity"] + random.randint(-10, 10),
        })
    return forecast


# ============================================================
# Tools(工具)—— 模型可调用的操作
# ============================================================

@mcp.tool()
def get_current_weather(city: str) -> str:
    """获取指定城市的当前天气信息

    Args:
        city: 城市名称,如"北京"、"上海"
    """
    if city not in WEATHER_DB:
        return f"错误:未找到城市 '{city}'。支持的城市:{', '.join(WEATHER_DB.keys())}"

    data = WEATHER_DB[city]
    result = (
        f"📍 {city} 当前天气\n"
        f"🌡️ 温度:{data['temp']}°C\n"
        f"💧 湿度:{data['humidity']}%\n"
        f"🌬️ 风力:{data['wind']}\n"
        f"☁️ 天气:{data['condition']}\n"
        f"🫁 AQI:{data['aqi']}"
    )
    return result


@mcp.tool()
def get_weather_forecast(city: str, days: int = 3) -> str:
    """获取指定城市未来几天的天气预报

    Args:
        city: 城市名称
        days: 预报天数,1-7天,默认3天
    """
    if city not in WEATHER_DB:
        return f"错误:未找到城市 '{city}'"

    days = max(1, min(7, days))
    forecast = get_mock_forecast(city, days)

    lines = [f"📅 {city} 未来{days}天天气预报\n"]
    for day in forecast:
        lines.append(
            f"  {day['date']} | "
            f"{day['condition']} | "
            f"{day['temp_low']}~{day['temp_high']}°C | "
            f"湿度 {day['humidity']}%"
        )
    return "\n".join(lines)


@mcp.tool()
def compare_cities(city1: str, city2: str) -> str:
    """对比两个城市的天气状况

    Args:
        city1: 第一个城市名称
        city2: 第二个城市名称
    """
    if city1 not in WEATHER_DB or city2 not in WEATHER_DB:
        missing = [c for c in [city1, city2] if c not in WEATHER_DB]
        return f"错误:未找到城市 {missing}"

    d1, d2 = WEATHER_DB[city1], WEATHER_DB[city2]
    diff = d1["temp"] - d2["temp"]
    warmer = city1 if diff > 0 else city2

    return (
        f"📊 天气对比:{city1} vs {city2}\n"
        f"温度:{d1['temp']}°C vs {d2['temp']}°C(温差 {abs(diff)}°C,{warmer}更暖)\n"
        f"湿度:{d1['humidity']}% vs {d2['humidity']}%\n"
        f"天气:{d1['condition']} vs {d2['condition']}\n"
        f"AQI:{d1['aqi']} vs {d2['aqi']}"
    )


# ============================================================
# Resources(资源)—— 应用可读取的数据
# ============================================================

@mcp.resource("weather://cities/supported")
def get_supported_cities() -> str:
    """返回支持查询的所有城市列表"""
    cities = list(WEATHER_DB.keys())
    return json.dumps({"cities": cities, "total": len(cities)}, ensure_ascii=False, indent=2)


@mcp.resource("weather://cities/{city}/snapshot")
def get_city_snapshot(city: str) -> str:
    """获取指定城市的天气快照(JSON格式)"""
    if city not in WEATHER_DB:
        return json.dumps({"error": f"城市 '{city}' 不在支持列表中"}, ensure_ascii=False)
    return json.dumps(
        {"city": city, "timestamp": datetime.now().isoformat(), "data": WEATHER_DB[city]},
        ensure_ascii=False,
        indent=2,
    )


# ============================================================
# Prompts(提示词模板)—— 用户可选的预设模板
# ============================================================

@mcp.prompt()
def daily_weather_briefing(city: str = "北京") -> str:
    """生成每日天气简报的提示词模板

    Args:
        city: 目标城市,默认北京
    """
    return (
        f"请为{city}生成一份详细的每日天气简报,包含以下内容:\n"
        f"1. 当前天气状况概述\n"
        f"2. 温度变化趋势分析\n"
        f"3. 穿衣建议(基于温度和风力)\n"
        f"4. 出行建议(基于天气状况和AQI)\n"
        f"5. 健康提醒(如紫外线、花粉等)\n\n"
        f"请用友好的语气,适合在早晨阅读。"
    )


@mcp.prompt()
def travel_planning(cities: str = "北京,上海") -> str:
    """旅行规划辅助的提示词模板

    Args:
        cities: 逗号分隔的城市列表
    """
    city_list = [c.strip() for c in cities.split(",")]
    return (
        f"我正在规划一次途经 {', '.join(city_list)} 的旅行。\n"
        f"请帮我:\n"
        f"1. 查询每个城市的当前天气\n"
        f"2. 对比各城市天气差异\n"
        f"3. 根据天气情况推荐最佳出行顺序\n"
        f"4. 给出每个城市的行李打包建议\n"
        f"5. 标注需要注意的恶劣天气风险"
    )


# ============================================================
# 启动服务器
# ============================================================

if __name__ == "__main__":
    mcp.run()

4.2 关键代码解析

FastMCP的装饰器魔法:FastMCP利用Python的类型注解(Type Hints)和文档字符串(Docstrings)自动生成MCP协议需要的工具描述和参数Schema。

@mcp.tool()
def get_current_weather(city: str) -> str:
    """获取指定城市的当前天气信息    ← 描述自动提取

    Args:
        city: 城市名称,如"北京"、"上海"  ← 参数说明自动提取
    """

这段代码被FastMCP处理后,会自动生成如下MCP工具定义:

{
  "name": "get_current_weather",
  "description": "获取指定城市的当前天气信息",
  "inputSchema": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名称,如\"北京\"、\"上海\""
      }
    },
    "required": ["city"]
  }
}

Resource的URI模板:@mcp.resource("weather://cities/{city}/snapshot") 中的 {city} 是动态参数,Client可以通过不同的URI读取不同城市的快照数据。


五、接入Claude Code

5.1 项目级配置(推荐)

在项目根目录创建 .mcp.json:

{
  "mcpServers": {
    "weather": {
      "command": "uv",
      "args": ["run", "weather_server.py"],
      "cwd": "/absolute/path/to/mcp-weather-server"
    }
  }
}

注意:使用 uv run 会自动处理虚拟环境。如果用系统Python,改为 "command": "python", "args": ["weather_server.py"]。

5.2 命令行添加

# 添加到项目级别
claude mcp add weather uv run weather_server.py

# 添加到用户全局级别(所有项目可用)
claude mcp add --scope user weather uv run /path/to/weather_server.py

# 通过JSON直接添加(适合复杂配置)
claude mcp add-json weather '{"command":"uv","args":["run","weather_server.py"]}'

5.3 验证连接

# 列出已配置的MCP服务器
claude mcp list

# 输出示例:
# weather: uv run weather_server.py
#   Scope: project (.mcp.json)
#   Status: connected ✓

在Claude Code对话中测试:

> 北京现在天气怎么样?

# Claude Code 会自动调用 get_current_weather("北京")
# 📍 北京 当前天气
# 🌡️ 温度:22°C
# ...

5.4 管理命令速查

claude mcp list            # 列出所有服务器
claude mcp remove weather  # 移除服务器
claude mcp reset           # 重置所有配置

六、调试与排错

6.1 MCP Inspector——可视化调试利器

MCP Inspector是官方提供的Web调试工具,可以独立测试Server,不需要通过Claude Code。

# 方法一:使用 mcp CLI
mcp dev weather_server.py

# 方法二:直接用 npx
npx @modelcontextprotocol/inspector uv run weather_server.py

Inspector会在浏览器中打开一个Web界面,提供:

6.2 常见问题排查

问题 原因 解决方案
Server不显示在 mcp list 配置文件路径错误 检查 .mcp.json 是否在项目根目录
mcp list 显示但工具不可用 Python依赖缺失 运行 uv add "mcp[cli]" 安装完整依赖
工具调用超时 Server启动慢/代码报错 先用 mcp dev 独立测试Server
中文参数乱码 编码问题 确保文件UTF-8编码,系统locale正确
command not found 命令路径问题 使用绝对路径:/home/user/.local/bin/uv

6.3 日志调试技巧

在Server代码中添加日志输出(输出到stderr,不会干扰MCP的stdio通信):

import sys

@mcp.tool()
def get_current_weather(city: str) -> str:
    print(f"[DEBUG] 收到天气查询请求: city={city}", file=sys.stderr)
    # ... 业务逻辑
    print(f"[DEBUG] 返回天气数据: {data}", file=sys.stderr)
    return result

查看Claude Code的MCP日志:

# Claude Code 的 MCP 日志通常在
~/.claude/logs/

七、进阶:生产级开发实践

7.1 错误处理模式

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("production-server")

@mcp.tool()
def query_database(sql: str) -> str:
    """执行SQL查询(只读)"""
    try:
        # 参数验证
        if not sql.strip().upper().startswith("SELECT"):
            return "错误:仅支持SELECT查询"

        # 执行查询(伪代码)
        result = execute_sql(sql)
        return json.dumps(result, ensure_ascii=False, indent=2)

    except ConnectionError:
        return "错误:数据库连接失败,请检查网络"
    except TimeoutError:
        return "错误:查询超时,请简化查询条件"
    except Exception as e:
        # 不要暴露内部堆栈信息
        return f"错误:查询执行失败 - {type(e).__name__}"

关键原则: - 永远返回字符串,不要抛出未捕获的异常 - 区分用户输入错误和系统内部错误 - 错误消息对AI友好——帮助Claude理解失败原因并调整策略 - 不要暴露内部堆栈信息或敏感数据

7.2 环境变量与配置

import os

@mcp.tool()
def call_external_api(endpoint: str) -> str:
    """调用外部API"""
    api_key = os.environ.get("MY_API_KEY")
    if not api_key:
        return "错误:未配置API密钥,请设置 MY_API_KEY 环境变量"
    # ...

在 .mcp.json 中传递环境变量:

{
  "mcpServers": {
    "my-server": {
      "command": "uv",
      "args": ["run", "server.py"],
      "env": {
        "MY_API_KEY": "sk-xxx",
        "DB_HOST": "localhost:5432"
      }
    }
  }
}

安全提醒:.mcp.json 可能被提交到Git。对于敏感密钥,建议在 env 中引用系统环境变量或使用 ${MY_API_KEY} 语法(如果支持)。

7.3 异步操作与进度通知

对于耗时操作,MCP支持进度通知:

@mcp.tool()
async def batch_process(items: list[str]) -> str:
    """批量处理数据项"""
    results = []
    total = len(items)

    ctx = mcp.get_context()  # 获取MCP上下文

    for i, item in enumerate(items):
        result = await process_single(item)
        results.append(result)

        # 发送进度通知
        await ctx.report_progress(
            progress=i + 1,
            total=total,
        )

    return f"处理完成:{len(results)} 项成功"

7.4 Streamable HTTP部署(远程服务器)

当需要将MCP Server部署到远程服务器供团队共享时,使用Streamable HTTP传输:

# server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("remote-server", host="0.0.0.0", port=8080)

@mcp.tool()
def hello(name: str) -> str:
    return f"Hello, {name}!"

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

Claude Code中配置远程服务器:

{
  "mcpServers": {
    "remote-tool": {
      "url": "https://your-server.com/mcp",
      "headers": {
        "Authorization": "Bearer your-token"
      }
    }
  }
}

7.5 Docker化部署

# Dockerfile
FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

CMD ["python", "server.py"]

.mcp.json 中使用Docker运行:

{
  "mcpServers": {
    "docker-tool": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "my-mcp-server"]
    }
  }
}

八、MCP Server与Claude Code Hooks联动

Hooks是Claude Code的另一层自动化机制,可以与MCP Server协同工作:

图表4

图表 4

在 .claude/settings.json 中配置Hook,对MCP工具调用进行前置校验:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__weather__query_database",
        "hooks": [
          {
            "type": "command",
            "command": "python validate_sql.py"
          }
        ]
      }
    ]
  }
}

九、TypeScript MCP Server速览

如果团队更熟悉TypeScript,MCP官方同样提供了一流的TypeScript SDK:

// server.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({ name: "my-tools", version: "1.0.0" });

// 使用 Zod 定义参数Schema
server.tool(
  "get_weather",
  "获取城市天气",
  { city: z.string().describe("城市名称") },
  async ({ city }) => ({
    content: [{ type: "text", text: `${city}: 25°C, 晴` }],
  })
);

// 启动
const transport = new StdioServerTransport();
await server.connect(transport);

.mcp.json 配置:

{
  "mcpServers": {
    "ts-server": {
      "command": "npx",
      "args": ["tsx", "server.ts"]
    }
  }
}

十、架构决策指南

何时选择Python vs TypeScript

考量维度 Python (FastMCP) TypeScript (MCP SDK)
上手速度 极快(装饰器+自动Schema) 快(需手动Zod定义)
生态集成 数据科学、AI/ML、运维脚本 Web开发、前端工具链
类型安全 运行时校验 编译时+运行时双重保障
异步支持 asyncio,需要理解事件循环 原生async/await
部署方式 uv/pip/Docker npx/Docker

选择建议


完整工作流总结

图表5

图表 5


延伸阅读

训练营适用性评估

维度 评估
适用训练营 AI工程化实战营、Python后端进阶营、DevOps自动化训练营
教学时长 3-4小时(含动手实践)
难度等级 中等(需Python基础)
实验环境 每人一台有网络连接的开发机,预装Python 3.10+和Claude Code
分组建议 2-3人一组,每组开发不同主题的MCP Server(天气、数据库、CI/CD等)
考核方式 每组演示自己开发的MCP Server在Claude Code中的运行效果

参考来源

  1. Build an MCP Server — Model Context Protocol — MCP官方构建服务器教程,含FastMCP完整示例
  2. MCP Architecture: Tools, Resources, and Prompts Explained — MCP三大原语架构深度解析
  3. Claude Code MCP Server 配置指南与实战用例 — Claude Code中MCP配置的中文实战指南
  4. Production-Ready MCP Servers: Security, Testing & Deployment — 生产级MCP服务器开发全攻略
  5. FastMCP Python SDK — Building Servers — FastMCP框架官方API文档与装饰器用法

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入