首页 › tutorials › Claude Code MCP服务器开发从零到上线全实战

Claude Code MCP服务器开发从零到上线全实战

概述

MCP(Model Context Protocol)是Anthropic推出的开放协议,标准化的AI模型与外部工具交互方式。本教程从架构原理出发,手把手带你用Python(FastMCP)和TypeScript两种语言各构建一个完整的MCP服务器,涵盖工具(Tools)、资源(Resources)、提示模板(Prompts)三大原语,最终接入Claude Code并通过MCP Inspector完成调试。适合有一定编程基础、希望扩展Claude Code能力的开发者和运维工程师。


一、MCP架构核心概念

在动手写代码之前,必须理解MCP的架构模型。MCP采用经典的客户端-服务器架构,但与HTTP服务不同,它专为LLM与工具交互而设计。

graph TB
    subgraph Host["宿主应用 (Host)"]
        CC["Claude Code / Claude Desktop"]
        Client["MCP Client"]
    end

    subgraph Servers["MCP Servers"]
        S1["📁 文件系统 Server"]
        S2["🗄️ 数据库 Server"]
        S3["🔧 自定义 Server"]
    end

    CC -->|"用户指令"| Client
    Client -->|"stdio / HTTP"| S1
    Client -->|"stdio / HTTP"| S2
    Client -->|"stdio / HTTP"| S3

    subgraph Primitives["MCP 三大原语"]
        T["🛠️ Tools<br/>执行操作"]
        R["📄 Resources<br/>暴露数据"]
        P["💬 Prompts<br/>提示模板"]
    end

    S1 --> T
    S2 --> R
    S3 --> P

1.1 三大原语详解

原语 英文 作用 典型场景
工具 Tools LLM可调用的函数/操作 查询数据库、发送通知、执行计算
资源 Resources LLM可读取的数据内容 文件内容、API响应、数据库记录
提示模板 Prompts 可复用的提示模板 代码审查模板、报告生成模板

1.2 传输协议

MCP支持两种传输方式:

Claude Code主要通过stdio与MCP服务器通信——它启动你的服务器进程,通过stdin/stdout交换JSON-RPC消息。


二、前置条件

工具 版本要求 用途
Python ≥ 3.10 FastMCP Python服务器
Node.js ≥ 18 TypeScript MCP服务器
Claude Code CLI 最新版 宿主客户端
pip / uv 最新版 Python包管理
npm / pnpm 最新版 Node包管理

环境检查命令:

# 检查Python版本
python --version  # 需要 >= 3.10

# 检查Node.js版本
node --version    # 需要 >= 18

# 检查Claude Code
claude --version

三、Python路线:用FastMCP构建服务器

3.1 项目初始化

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

# 创建虚拟环境
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

# 安装FastMCP(官方Python MCP框架)
pip install fastmcp httpx

为什么选FastMCP? FastMCP是MCP Python SDK内置的高级框架(mcp.server.fastmcp),由Prefect团队维护。一个@mcp.tool()装饰器就能把普通Python函数变成MCP工具,自动处理JSON-RPC协议、参数校验、类型推断。

3.2 最小可运行服务器

创建server.py:

from mcp.server.fastmcp import FastMCP

# 初始化MCP服务器,名称将显示在Claude Code中
mcp = FastMCP("weather-server")


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

    Args:
        city: 城市名称,如"北京"、"上海"

    Returns:
        天气信息描述字符串
    """
    # 这里使用模拟数据,实际项目中可接入天气API
    weather_data = {
        "北京": "☀️ 晴,温度 28°C,湿度 45%,北风3级",
        "上海": "🌧️ 小雨,温度 22°C,湿度 78%,东风2级",
        "深圳": "⛅ 多云,温度 30°C,湿度 65%,南风2级",
    }
    return weather_data.get(city, f"暂无{city}的天气数据")


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

    Args:
        city: 城市名称
        days: 预报天数,默认3天,最多7天

    Returns:
        天气预报信息
    """
    if days > 7:
        return "最多支持7天预报"

    forecasts = []
    for i in range(1, days + 1):
        forecasts.append(f"  第{i}天: 晴转多云,温度 20-30°C")

    return f"{city}未来{days}天预报:\n" + "\n".join(forecasts)


# 运行服务器(默认使用stdio传输)
if __name__ == "__main__":
    mcp.run()

3.3 添加资源(Resources)

资源是LLM可以读取的数据,不需要执行操作时使用资源而非工具:

@mcp.resource("weather://cities/supported")
def get_supported_cities() -> str:
    """返回支持查询天气的城市列表"""
    cities = ["北京", "上海", "深圳", "广州", "成都", "杭州"]
    return "支持查询天气的城市:\n" + "\n".join(f"  - {c}" for c in cities)


@mcp.resource("weather://alert/{city}")
def get_weather_alert(city: str) -> str:
    """获取指定城市的天气预警信息

    Args:
        city: 城市名称
    """
    alerts = {
        "北京": "🟡 大风蓝色预警:预计未来24小时阵风可达7级",
        "上海": "🔴 暴雨橙色预警:预计未来6小时降雨量将达100mm以上",
    }
    return alerts.get(city, f"✅ {city}当前无天气预警")

资源URI使用协议://路径格式,支持路径参数模板(如{city})。

3.4 添加提示模板(Prompts)

提示模板是预定义的、可复用的提示词:

@mcp.prompt()
def weather_report_prompt(city: str) -> str:
    """生成天气报告的提示模板

    Args:
        city: 要生成报告的城市
    """
    return f"""请为{city}生成一份详细的天气分析报告,包含以下内容:
1. 当前天气状况概述
2. 未来3天天气预报
3. 出行建议
4. 健康提示(如紫外线指数、空气质量等)

请使用专业但易懂的语言,适合公众阅读。"""

3.5 完整服务器代码

将以上内容整合为完整的server.py:

#!/usr/bin/env python3
"""天气查询 MCP 服务器 - 演示MCP三大原语的完整实现"""

from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    "weather-server",
    version="1.0.0",
    description="天气查询服务 - 提供城市天气查询、预报和预警功能"
)


# ============ Tools(工具)============

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

    Args:
        city: 城市名称,如"北京"、"上海"
    """
    weather_data = {
        "北京": "☀️ 晴,温度 28°C,湿度 45%,北风3级",
        "上海": "🌧️ 小雨,温度 22°C,湿度 78%,东风2级",
        "深圳": "⛅ 多云,温度 30°C,湿度 65%,南风2级",
        "广州": "🌤️ 晴间多云,温度 32°C,湿度 70%,南风2级",
        "成都": "☁️ 阴,温度 24°C,湿度 60%,微风",
        "杭州": "🌦️ 阵雨,温度 25°C,湿度 75%,东风3级",
    }
    return weather_data.get(city, f"暂无{city}的天气数据")


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

    Args:
        city: 城市名称
        days: 预报天数,默认3天,最多7天
    """
    if days > 7:
        return "错误:最多支持7天预报"
    if days < 1:
        return "错误:天数必须大于0"

    forecasts = []
    conditions = ["☀️ 晴", "⛅ 多云", "🌤️ 晴间多云", "🌦️ 阵雨", "☁️ 阴"]
    for i in range(1, days + 1):
        cond = conditions[i % len(conditions)]
        high = 25 + (i % 5)
        low = 15 + (i % 3)
        forecasts.append(f"  第{i}天: {cond},温度 {low}-{high}°C")

    return f"📍 {city}未来{days}天预报:\n" + "\n".join(forecasts)


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

    Args:
        city1: 第一个城市
        city2: 第二个城市
    """
    w1 = get_current_weather(city1)
    w2 = get_current_weather(city2)
    return f"天气对比:\n\n📍 {city1}: {w1}\n📍 {city2}: {w2}"


# ============ Resources(资源)============

@mcp.resource("weather://cities/supported")
def get_supported_cities() -> str:
    """返回支持查询天气的城市列表"""
    cities = ["北京", "上海", "深圳", "广州", "成都", "杭州"]
    return "支持查询天气的城市:\n" + "\n".join(f"  - {c}" for c in cities)


@mcp.resource("weather://alert/{city}")
def get_weather_alert(city: str) -> str:
    """获取指定城市的天气预警信息"""
    alerts = {
        "北京": "🟡 大风蓝色预警:预计未来24小时阵风可达7级",
        "上海": "🔴 暴雨橙色预警:预计未来6小时降雨量将达100mm以上",
    }
    return alerts.get(city, f"✅ {city}当前无天气预警")


# ============ Prompts(提示模板)============

@mcp.prompt()
def weather_report_prompt(city: str) -> str:
    """生成天气分析报告的提示模板"""
    return f"""请为{city}生成一份详细的天气分析报告,包含:
1. 当前天气状况概述
2. 未来3天天气预报分析
3. 出行建议
4. 健康提示

请使用专业但易懂的语言。"""


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

四、TypeScript路线:用官方SDK构建服务器

4.1 项目初始化

# 创建项目
mkdir mcp-note-server && cd mcp-note-server
npm init -y

# 安装MCP SDK和类型校验库
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node

# 初始化TypeScript配置
npx tsc --init

修改tsconfig.json关键配置:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

4.2 构建笔记管理服务器

创建src/index.ts:

#!/usr/bin/env node

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

// 初始化MCP服务器
const server = new McpServer({
  name: "note-server",
  version: "1.0.0",
  description: "笔记管理服务 - 轻量级笔记CRUD操作",
});

// 内存存储(实际项目中替换为数据库)
interface Note {
  id: string;
  title: string;
  content: string;
  tags: string[];
  createdAt: string;
  updatedAt: string;
}

const notes: Map<string, Note> = new Map();

function generateId(): string {
  return Date.now().toString(36) + Math.random().toString(36).slice(2);
}

// ============ Tools ============

server.tool(
  "create_note",
  "创建一条新笔记",
  {
    title: z.string().describe("笔记标题"),
    content: z.string().describe("笔记内容"),
    tags: z.array(z.string()).optional().describe("标签列表"),
  },
  async ({ title, content, tags = [] }) => {
    const id = generateId();
    const now = new Date().toISOString();
    const note: Note = { id, title, content, tags, createdAt: now, updatedAt: now };
    notes.set(id, note);
    return {
      content: [{ type: "text" as const, text: `✅ 笔记已创建\nID: ${id}\n标题: ${title}` }],
    };
  }
);

server.tool(
  "list_notes",
  "列出所有笔记(支持按标签筛选)",
  {
    tag: z.string().optional().describe("按标签筛选"),
  },
  async ({ tag }) => {
    let filtered = Array.from(notes.values());
    if (tag) {
      filtered = filtered.filter((n) => n.tags.includes(tag));
    }
    if (filtered.length === 0) {
      return {
        content: [{ type: "text" as const, text: "📭 没有找到笔记" }],
      };
    }
    const list = filtered
      .map((n) => `- [${n.id}] ${n.title} (${n.tags.join(", ") || "无标签"}) ${n.updatedAt.split("T")[0]}`)
      .join("\n");
    return {
      content: [{ type: "text" as const, text: `📝 笔记列表 (${filtered.length}条):\n${list}` }],
    };
  }
);

server.tool(
  "read_note",
  "读取指定ID的笔记详情",
  {
    id: z.string().describe("笔记ID"),
  },
  async ({ id }) => {
    const note = notes.get(id);
    if (!note) {
      return {
        content: [{ type: "text" as const, text: `❌ 未找到ID为 ${id} 的笔记` }],
        isError: true,
      };
    }
    return {
      content: [
        {
          type: "text" as const,
          text: `# ${note.title}\n\n${note.content}\n\n---\n标签: ${note.tags.join(", ") || "无"}\n创建: ${note.createdAt}\n更新: ${note.updatedAt}`,
        },
      ],
    };
  }
);

server.tool(
  "delete_note",
  "删除指定ID的笔记",
  {
    id: z.string().describe("笔记ID"),
  },
  async ({ id }) => {
    const deleted = notes.delete(id);
    return {
      content: [
        {
          type: "text" as const,
          text: deleted ? `✅ 已删除笔记 ${id}` : `❌ 未找到笔记 ${id}`,
        },
      ],
    };
  }
);

// ============ Resources ============

server.resource("notes://all", "notes://all", async (uri) => {
  const allNotes = Array.from(notes.values());
  return {
    contents: [
      {
        uri: uri.href,
        text: JSON.stringify(allNotes, null, 2),
      },
    ],
  };
});

// ============ Prompts ============

server.prompt(
  "summarize_notes",
  "生成所有笔记的摘要报告",
  [],
  async () => {
    const count = notes.size;
    const allTags = new Set(Array.from(notes.values()).flatMap((n) => n.tags));
    return {
      messages: [
        {
          role: "user" as const,
          content: {
            type: "text" as const,
            text: `当前共有 ${count} 条笔记,使用了 ${allTags.size} 个标签(${[...allTags].join("、")})。\n请根据这些笔记内容生成一份结构化的知识摘要。`,
          },
        },
      ],
    };
  }
);

// ============ 启动服务器 ============

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  // 服务器已启动,通过stdio通信
}

main().catch((err) => {
  console.error("Server error:", err);
  process.exit(1);
});

4.3 构建与配置

在package.json中添加构建和启动脚本:

{
  "name": "mcp-note-server",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  },
  "bin": {
    "mcp-note-server": "./dist/index.js"
  }
}

构建项目:

npm run build

五、接入Claude Code

5.1 配置方式对比

Claude Code提供三种方式注册MCP服务器:

graph LR
    subgraph 方法1["CLI命令(推荐)"]
        A["claude mcp add"]
    end

    subgraph 方法2["项目级配置"]
        B[".mcp.json"]
    end

    subgraph 方法3["用户级配置"]
        C["~/.claude.json"]
    end

    A --> D["Claude Code"]
    B --> D
    C --> D

    style 方法1 fill:#4CAF50,color:#fff
    style 方法2 fill:#2196F3,color:#fff
    style 方法3 fill:#FF9800,color:#fff
方式 作用域 适用场景 配置文件
CLI命令 项目/用户 快速添加 自动写入配置
.mcp.json 项目级 团队共享,可提交Git 项目根目录
~/.claude.json 用户级 个人全局工具 用户主目录

5.2 方法一:CLI命令添加(推荐)

添加Python天气服务器:

# 在项目目录下执行(项目级)
claude mcp add weather-server -- python /path/to/mcp-weather-server/server.py

# 或添加到用户级(全局可用)
claude mcp add weather-server -s user -- python /path/to/mcp-weather-server/server.py

添加TypeScript笔记服务器:

claude mcp add note-server -- node /path/to/mcp-note-server/dist/index.js

带环境变量的配置:

claude mcp add weather-server \
  -e OPENWEATHER_API_KEY=your_key \
  -- python /path/to/server.py

5.3 方法二:.mcp.json项目配置

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

{
  "mcpServers": {
    "weather-server": {
      "command": "python",
      "args": ["/absolute/path/to/mcp-weather-server/server.py"],
      "env": {
        "OPENWEATHER_API_KEY": "your_api_key_here"
      }
    },
    "note-server": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-note-server/dist/index.js"]
    }
  }
}

⚠️ 安全提示:.mcp.json可能被提交到Git,不要在env中放真实密钥。使用环境变量引用或.claude/settings.json中的用户级配置。

5.4 方法三:用户级全局配置

编辑~/.claude.json,在mcpServers字段添加:

{
  "mcpServers": {
    "weather-server": {
      "command": "python",
      "args": ["/home/user/mcp-weather-server/server.py"],
      "env": {
        "OPENWEATHER_API_KEY": "sk-real-key-here"
      }
    }
  }
}

5.5 管理MCP服务器

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

# 查看某个服务器详情
claude mcp get weather-server

# 删除服务器
claude mcp remove weather-server

# 以JSON格式添加(复杂配置)
claude mcp add-json weather-server '{
  "command": "python",
  "args": ["./server.py"],
  "env": {"API_KEY": "xxx"}
}'

5.6 验证连接

启动Claude Code后,MCP服务器会自动加载。验证方式:

# 启动Claude Code
claude

# 在交互界面中输入
> 帮我查一下北京现在的天气

# 预期结果:Claude会调用weather-server的get_current_weather工具

也可以在Claude Code中直接检查:

/MCP        # 查看已连接的MCP服务器列表
/tools      # 查看所有可用工具(包括MCP工具)

六、调试与测试

6.1 MCP Inspector可视化调试

MCP Inspector是官方提供的交互式调试工具,可在浏览器中测试MCP服务器。

# 安装并启动Inspector
npx @modelcontextprotocol/inspector

# 指定Python服务器
npx @modelcontextprotocol/inspector python /path/to/server.py

# 指定Node服务器
npx @modelcontextprotocol/inspector node /path/to/dist/index.js

启动后浏览器会打开一个Web界面,提供以下功能:

功能模块 作用
Tools面板 列出所有工具,输入参数并执行,查看返回结果
Resources面板 浏览和读取资源URI
Prompts面板 测试提示模板
Connection面板 查看JSON-RPC通信日志

6.2 常见问题排查

问题1:服务器启动失败

# 手动测试服务器是否能运行
python /path/to/server.py
# 如果报错,检查Python版本和依赖

# 查看Claude Code日志
claude --debug

问题2:工具未出现在Claude Code中

# 检查MCP服务器是否被识别
claude mcp list

# 确认配置文件路径正确(使用绝对路径)
# 确认Python/node命令在PATH中可用
which python
which node

问题3:中文参数编码问题

Python服务器需确保UTF-8输出:

# 在server.py头部添加
import sys
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')

问题4:Claude Code权限提示

首次使用项目级.mcp.json中的MCP服务器时,Claude Code会弹出权限确认。这是安全机制。可在.claude/settings.json中预先授权:

{
  "permissions": {
    "allow": ["mcp__weather-server__*"]
  }
}

6.3 编写单元测试

为Python MCP服务器编写测试(使用pytest):

# test_server.py
import pytest
from server import mcp

def test_get_current_weather():
    """测试天气查询工具"""
    # FastMCP提供test client用于测试
    from mcp.server.fastmcp import FastMCP

    result = mcp._tool_manager.call_tool("get_current_weather", {"city": "北京"})
    assert "28" in result or "北京" in str(result)

def test_get_forecast_validation():
    """测试预报参数校验"""
    result = mcp._tool_manager.call_tool(
        "get_weather_forecast", {"city": "北京", "days": 10}
    )
    assert "最多支持7天" in str(result)

七、实战案例:代码审查MCP服务器

以下是一个面向开发团队的完整实战案例——一个代码审查MCP服务器,集成了Git操作、静态分析和报告生成。

7.1 项目结构

mcp-code-reviewer/
├── server.py           # 主服务器文件
├── analyzers/          # 分析模块
│   ├── __init__.py
│   ├── complexity.py   # 圈复杂度分析
│   └── security.py     # 安全检查
├── requirements.txt
└── README.md

7.2 服务器实现

# server.py
import os
import subprocess
import json
from pathlib import Path
from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    "code-reviewer",
    version="1.0.0",
    description="代码审查助手 - 分析Git变更、检测安全和复杂度问题"
)


def run_git(cmd: list[str], cwd: str | None = None) -> str:
    """执行Git命令并返回输出"""
    result = subprocess.run(
        ["git"] + cmd,
        capture_output=True,
        text=True,
        cwd=cwd or os.getcwd()
    )
    return result.stdout.strip()


@mcp.tool()
def analyze_git_diff(repo_path: str = ".", base_branch: str = "main") -> str:
    """分析当前分支与基准分支的代码差异

    Args:
        repo_path: Git仓库路径
        base_branch: 基准分支名,默认为main

    Returns:
        变更文件列表和统计信息
    """
    # 获取差异统计
    diff_stat = run_git(
        ["diff", "--stat", f"origin/{base_branch}...HEAD"],
        cwd=repo_path
    )

    # 获取变更文件列表
    diff_files = run_git(
        ["diff", "--name-only", f"origin/{base_branch}...HEAD"],
        cwd=repo_path
    )

    files = diff_files.split("\n") if diff_files else []

    # 按文件类型分类
    categories = {
        "🐍 Python": [], "📄 TypeScript": [], "📄 JavaScript": [],
        "🏗️ Config": [], "📦 Other": []
    }

    ext_map = {
        ".py": "🐍 Python", ".ts": "📄 TypeScript", ".tsx": "📄 TypeScript",
        ".js": "📄 JavaScript", ".jsx": "📄 JavaScript",
        ".json": "🏗️ Config", ".yaml": "🏗️ Config", ".yml": "🏗️ Config",
    }

    for f in files:
        ext = Path(f).suffix
        cat = ext_map.get(ext, "📦 Other")
        categories[cat].append(f)

    result = f"📊 变更统计(对比 {base_branch}):\n\n{diff_stat}\n\n"
    result += "📁 变更文件分类:\n"
    for cat, cat_files in categories.items():
        if cat_files:
            result += f"\n{cat}:\n"
            for f in cat_files:
                result += f"  - {f}\n"

    return result


@mcp.tool()
def check_python_complexity(file_path: str) -> str:
    """检查Python文件的圈复杂度

    Args:
        file_path: Python文件路径
    """
    try:
        with open(file_path, "r", encoding="utf-8") as f:
            lines = f.readlines()
    except FileNotFoundError:
        return f"❌ 文件不存在: {file_path}"

    # 简单的复杂度估算(计算if/for/while/and/or数量)
    complexity_keywords = ["if ", "elif ", "for ", "while ", "and ", "or ", "except "]
    total_complexity = 1  # 基础复杂度
    issues = []

    for i, line in enumerate(lines, 1):
        stripped = line.strip()
        if stripped.startswith("#"):
            continue
        for kw in complexity_keywords:
            if kw in stripped:
                total_complexity += 1

    if total_complexity > 15:
        level = "🔴 高复杂度"
    elif total_complexity > 8:
        level = "🟡 中等复杂度"
    else:
        level = "🟢 低复杂度"

    return (
        f"📋 复杂度分析: {file_path}\n"
        f"圈复杂度: {total_complexity} {level}\n"
        f"总行数: {len(lines)}\n"
        f"代码行数: {sum(1 for l in lines if l.strip() and not l.strip().startswith('#'))}"
    )


@mcp.tool()
def generate_review_summary(repo_path: str = ".", base_branch: str = "main") -> str:
    """生成代码审查摘要报告

    Args:
        repo_path: 仓库路径
        base_branch: 基准分支
    """
    diff_info = analyze_git_diff(repo_path, base_branch)

    # 获取提交信息
    commits = run_git(
        ["log", f"origin/{base_branch}...HEAD", "--oneline"],
        cwd=repo_path
    )

    return (
        f"📋 代码审查摘要报告\n"
        f"{'='*40}\n\n"
        f"{diff_info}\n\n"
        f"💬 提交记录:\n{commits}\n\n"
        f"建议审查重点:\n"
        f"  1. 检查新增文件的安全性和复杂度\n"
        f"  2. 确认配置文件变更是否符合预期\n"
        f"  3. 验证测试覆盖率"
    )


@mcp.resource("review://template")
def get_review_template() -> str:
    """代码审查模板"""
    return """## 代码审查清单

### 功能性
- [ ] 代码实现了预期功能
- [ ] 边界条件已处理
- [ ] 错误处理完善

### 安全性
- [ ] 无硬编码密钥/密码
- [ ] 输入已校验和清理
- [ ] 无SQL注入/XSS风险

### 可维护性
- [ ] 命名清晰有意义
- [ ] 复杂度可控
- [ ] 注释充分且准确

### 测试
- [ ] 单元测试覆盖关键路径
- [ ] 集成测试通过
- [ ] 性能测试(如适用)"""


@mcp.prompt()
def code_review_prompt(file_path: str) -> str:
    """对指定文件进行深度代码审查

    Args:
        file_path: 要审查的文件路径
    """
    return f"""请对文件 {file_path} 进行全面的代码审查。

审查维度:
1. **代码质量**:命名规范、代码风格、可读性
2. **安全性**:潜在漏洞、敏感信息泄露、输入校验
3. **性能**:不必要的计算、内存泄漏、N+1查询
4. **可维护性**:耦合度、复杂度、可扩展性
5. **测试**:测试覆盖、边界条件、错误路径

请用中文输出审查报告,按严重程度分级(🔴严重 🟡警告 🟢建议)。"""


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

7.3 注册到Claude Code

# 添加服务器
claude mcp add code-reviewer \
  -s project \
  -- python /path/to/mcp-code-reviewer/server.py

# 验证
claude mcp list

使用示例:

请分析当前分支相对于main的代码变更

请检查 src/api/auth.py 的复杂度

请生成代码审查摘要报告

八、生产级最佳实践

8.1 安全原则

graph TB
    S["MCP服务器安全"] --> A["输入验证"]
    S --> B["权限最小化"]
    S --> C["敏感信息保护"]
    S --> D["错误处理"]

    A --> A1["使用Zod/Pydantic校验参数"]
    A --> A2["限制文件系统访问范围"]
    A --> A3["防止命令注入"]

    B --> B1["只暴露必要的工具"]
    B --> B2["不直接暴露数据库连接"]
    B --> B3["使用只读权限优先"]

    C --> C1["环境变量存储密钥"]
    C --> C2["不在日志中输出敏感数据"]
    C --> C3["响应中脱敏处理"]

    D --> D1["所有异常捕获并返回友好消息"]
    D --> D2["设置超时限制"]
    D --> D3["记录操作审计日志"]

8.2 性能优化

优化点 实践方案
工具粒度 保持工具功能单一,避免一个工具做太多事
返回数据 控制返回数据量,避免返回超大文本
异步操作 Python使用async def,TypeScript使用async/await
缓存 对频繁调用的数据实现缓存层
超时 设置合理的工具执行超时

8.3 Python异步工具示例

import httpx
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("async-example")

@mcp.tool()
async def fetch_api_data(url: str) -> str:
    """异步获取API数据

    Args:
        url: API端点URL
    """
    async with httpx.AsyncClient(timeout=10.0) as client:
        try:
            response = await client.get(url)
            response.raise_for_status()
            return f"✅ 状态码: {response.status_code}\n数据长度: {len(response.text)} 字符"
        except httpx.TimeoutException:
            return "❌ 请求超时"
        except httpx.HTTPStatusError as e:
            return f"❌ HTTP错误: {e.response.status_code}"

8.4 发布与分发

将MCP服务器发布为可复用的npm包或Python包:

Python包发布流程:

# 1. 创建pyproject.toml
# 2. 添加console_scripts入口
# 3. 构建并发布
pip install build
python -m build
pip install twine
twine upload dist/*

npm包发布流程:

# 1. 配置package.json的bin字段
# 2. 在入口文件添加shebang:#!/usr/bin/env node
# 3. 发布
npm publish

用户安装后只需一行命令注册:

# Python包
claude mcp add my-tool -- my-tool-server

# npm包
claude mcp add my-tool -- npx -y my-mcp-server

九、完整工作流程总结

flowchart TD
    A["🚀 需求分析"] --> B["选择技术栈"]
    B --> B1{"Python / TypeScript?"}
    B1 -->|"Python"| C1["安装FastMCP<br/>pip install fastmcp"]
    B1 -->|"TypeScript"| C2["安装MCP SDK<br/>npm install @modelcontextprotocol/sdk"]

    C1 --> D["实现三大原语"]
    C2 --> D

    D --> D1["🛠️ Tools"]
    D --> D2["📄 Resources"]
    D --> D3["💬 Prompts"]

    D1 --> E["本地测试"]
    D2 --> E
    D3 --> E

    E --> E1["MCP Inspector<br/>可视化调试"]
    E --> E2["单元测试"]

    E1 --> F["注册到Claude Code"]
    E2 --> F

    F --> F1["claude mcp add"]
    F --> F2["或 .mcp.json"]

    F1 --> G["集成测试"]
    F2 --> G

    G --> G1{"通过?"}
    G1 -->|"是"| H["📦 打包发布"]
    G1 -->|"否"| D

    H --> I["npm publish / PyPI upload"]
    I --> J["🎉 完成"]

十、训练营适用性评估

维度 评估
适合训练营类型 AI开发训练营、DevOps工具链训练营、企业内部工具开发培训
前置要求 Python或TypeScript基础,命令行操作经验
建议教学时长 4-6小时(含实战练习)
模块拆分建议 理论1h + Python实战1.5h + TypeScript实战1.5h + 调试部署1h
难度等级 ⭐⭐⭐ 中等(需要编程基础,但MCP封装大大降低了难度)
实操价值 极高——学员完成后可将团队内部工具直接接入Claude Code

参考来源

  1. Build an MCP server - Model Context Protocol 官方文档 — MCP服务器构建官方指南
  2. Connect Claude Code to tools via MCP - Claude Code Docs — Claude Code MCP集成官方文档
  3. FastMCP GitHub - PrefectHQ — FastMCP Python框架源码和文档
  4. How to Build Your First MCP Server for Claude (Step-by-Step, 2026) — TypeScript MCP服务器完整教程
  5. MCP Inspector - Model Context Protocol — MCP调试工具官方文档
  6. @modelcontextprotocol/sdk - npm — TypeScript MCP SDK官方包

[!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学通信知识星球海报-扫码加入