02-快速构建MCP服务器

概述

在上一篇《MCP协议入门指南》中,我们介绍了 MCP 协议的基本概念和核心组件。本文将详细介绍如何使用 FastMCP 框架快速构建完整的 MCP 服务器,包括数学计算服务器和 BMI 计算服务器,并展示如何定义工具、资源和提示模板。

FastMCP框架简介

FastMCP 是一个用于快速构建 MCP 服务器的 Python 框架,它提供了简洁的 API 和强大的功能。

核心特性

  • 装饰器语法: 使用 @mcp.tool(), @mcp.resource(), @mcp.prompt() 快速定义组件
  • 自动类型推断: 自动生成 JSON Schema
  • 多传输支持: 同时支持 stdio 和 HTTP 传输
  • 开发友好: 内置开发工具和调试功能

安装

# 使用 uv
uv add fastmcp

# 或使用 pip
pip install fastmcp

项目准备

项目结构

mcp_demo/
├── math_mcp_server_stdio.py       # 数学计算服务器(Stdio模式)
├── math_mcp_server_http.py        # 数学计算服务器(HTTP模式)
├── bmi_mcp_server.py              # BMI计算服务器
└── pyproject.toml                 # 项目配置

依赖配置

pyproject.toml 配置:

[project]
name = "mcp-demo"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
    "fastmcp>=2.14.3",
    "mcp>=0.9.0",
]

构建数学计算 MCP 服务器

基础结构

首先创建数学计算服务器的基础结构:

from mcp.server.fastmcp import FastMCP
import math

# 创建 MCP 服务器实例
mcp = FastMCP("Math")

# 服务器版本信息
SERVER_VERSION = "1.0.0"

定义提示模板

提示模板用于定义 AI 助手的行为模式。

1. 系统提示模板
@mcp.prompt()
def system_prompt() -> str:
    """
    系统提示模板
    定义AI助手的基本行为和角色
    """
    return """
You are an AI assistant specialized in mathematical calculations.
- Use the available tools when needed to perform calculations
- Always explain your reasoning before calling a tool
- Show step-by-step solutions
- Provide clear and concise answers
- If a calculation is needed, use the appropriate tool
"""
2. 数学问题提示模板
@mcp.prompt()
def example_prompt(question: str) -> str:
    """
    数学问题提示模板
    用于引导AI助手回答数学问题
    """
    return f"""
You are a math assistant. Answer the question step by step.
Question: {question}
Show your work and explain each step clearly.
"""
3. 计算指导模板
@mcp.prompt()
def calculation_guide(operation: str) -> str:
    """
    计算指导模板
    提供特定运算类型的指导
    """
    guides = {
        "addition": "To add two numbers, combine their values.",
        "multiplication": "To multiply, add one number to itself the number of times specified by the other.",
        "division": "To divide, split one number into equal parts of another.",
        "subtraction": "To subtract, find the difference between two numbers."
    }
    return f"""
Calculation Guide for {operation}:
{guides.get(operation.lower(), "General calculation method")}
Always use the appropriate tool for accurate results.
"""

定义资源

资源提供静态或动态的数据访问能力。

1. 数学常量资源
@mcp.resource("constant://pi")
def get_pi() -> str:
    """
    圆周率常数
    返回数学常数π(Pi)的高精度值
    """
    return f"{math.pi}"

@mcp.resource("constant://e")
def get_euler_number() -> str:
    """
    自然常数
    返回自然对数的底数e(欧拉数)的高精度值
    """
    return f"{math.e}"

@mcp.resource("constant://golden_ratio")
def get_golden_ratio() -> str:
    """
    黄金比例
    返回黄金比例φ的值(约等于1.618...)
    """
    return f"{(1 + math.sqrt(5)) / 2}"
2. 动态资源
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
    """
    个性化问候
    根据提供的名字返回问候语
    """
    return f"Hello, {name}! Welcome to the Math MCP Server."
3. 配置资源
@mcp.resource("config://app")
def get_config() -> str:
    """
    应用配置
    返回服务器配置信息(JSON格式)
    """
    import json
    config = {
        "server_name": "Math MCP Server",
        "version": "1.0.0",
        "features": ["basic_arithmetic", "constants", "prompts"],
        "supported_operations": ["add", "subtract", "multiply", "divide", "power", "sqrt", "factorial"],
        "transport": "stdio"
    }
    return json.dumps(config, indent=2, ensure_ascii=False)
4. 服务器能力资源
@mcp.resource("info://capabilities")
def get_capabilities() -> str:
    """
    服务器能力信息
    详细说明服务器提供的功能
    """
    capabilities = """
Math MCP Server Capabilities:

Tools:
  - add(a, b): Addition of two numbers
  - subtract(a, b): Subtraction of two numbers
  - multiply(a, b): Multiplication of two numbers
  - divide(a, b): Division of two numbers
  - power(base, exponent): Exponentiation
  - sqrt(number): Square root
  - factorial(n): Factorial

Resources:
  - constant://pi: Pi constant (3.14159...)
  - constant://e: Euler's number (2.71828...)
  - constant://golden_ratio: Golden ratio (1.61803...)
  - greeting://{name}: Personalized greeting
  - config://app: Application configuration
  - info://capabilities: This capability list

Prompts:
  - example_prompt(question): Math question template
  - system_prompt(): System behavior template
  - calculation_guide(operation): Operation guide
"""
    return capabilities

定义工具

工具是可执行的函数,供 AI 助手调用以完成特定任务。

1. 基础运算工具
@mcp.tool()
def add(a: int, b: int) -> str:
    """
    加法运算
    计算两个整数的和

    Args:
        a: 第一个加数
        b: 第二个加数

    Returns:
        计算结果和过程的描述字符串
    """
    result = a + b
    return f"{a} + {b} = {result}"

@mcp.tool()
def subtract(a: int, b: int) -> str:
    """
    减法运算
    计算两个整数的差

    Args:
        a: 被减数
        b: 减数

    Returns:
        计算结果和过程的描述字符串
    """
    result = a - b
    return f"{a} - {b} = {result}"

@mcp.tool()
def multiply(a: int, b: int) -> str:
    """
    乘法运算
    计算两个整数的乘积

    Args:
        a: 第一个乘数
        b: 第二个乘数

    Returns:
        计算结果和过程的描述字符串
    """
    result = a * b
    return f"{a} * {b} = {result}"

@mcp.tool()
def divide(a: int, b: int) -> str:
    """
    除法运算
    计算两个整数的商(浮点数结果)

    Args:
        a: 被除数
        b: 除数(不能为零)

    Returns:
        计算结果和过程的描述字符串

    Raises:
        ValueError: 当除数为零时
    """
    if b == 0:
        raise ValueError("除数不能为零 (Divisor cannot be zero)")
    result = a / b
    return f"{a} / {b} = {result}"
2. 高级运算工具
@mcp.tool()
def power(base: int, exponent: int) -> str:
    """
    幂运算
    计算基数的整数次幂

    Args:
        base: 底数
        exponent: 指数(必须为非负整数)

    Returns:
        计算结果和过程的描述字符串

    Raises:
        ValueError: 当指数为负数时
    """
    if exponent < 0:
        raise ValueError("指数必须为非负整数 (Exponent must be non-negative)")
    result = base ** exponent
    return f"{base}^{exponent} = {result}"

@mcp.tool()
def sqrt(number: int) -> str:
    """
    平方根运算
    计算一个非负整数的平方根

    Args:
        number: 要计算平方根的非负整数

    Returns:
        计算结果和过程的描述字符串

    Raises:
        ValueError: 当数为负数时
    """
    if number < 0:
        raise ValueError("不能计算负数的平方根 (Cannot calculate square root of negative number)")
    result = math.sqrt(number)
    if result.is_integer():
        return f"√{number} = {int(result)}"
    else:
        return f"√{number}{result:.6f}"

@mcp.tool()
def factorial(n: int) -> str:
    """
    阶乘运算
    计算一个非负整数的阶乘

    Args:
        n: 非负整数

    Returns:
        计算结果和过程的描述字符串

    Raises:
        ValueError: 当n为负数时
    """
    if n < 0:
        raise ValueError("阶乘只能计算非负整数 (Factorial only defined for non-negative integers)")
    result = math.factorial(n)
    return f"{n}! = {result}"

服务器入口

if __name__ == "__main__":
    print("=" * 60)
    print("Math MCP Server (Stdio Mode)")
    print("=" * 60)
    print("")
    print("Server Information:")
    print("  - Name: Math")
    print(f"  - Version: {SERVER_VERSION}")
    print("  - Transport: Stdio")
    print("")
    print("Available Tools (7):")
    print("  1. add(a: int, b: int) -> str")
    print("     加法运算 (Addition)")
    print("  2. subtract(a: int, b: int) -> str")
    print("     减法运算 (Subtraction)")
    print("  3. multiply(a: int, b: int) -> str")
    print("     乘法运算 (Multiplication)")
    print("  4. divide(a: int, b: int) -> str")
    print("     除法运算 (Division)")
    print("  5. power(base: int, exponent: int) -> str")
    print("     幂运算 (Power)")
    print("  6. sqrt(number: int) -> str")
    print("     平方根运算 (Square Root)")
    print("  7. factorial(n: int) -> str")
    print("     阶乘运算 (Factorial)")
    print("")
    print("Available Resources (6):")
    print("  1. constant://pi")
    print("     圆周率常数 (Pi constant)")
    print("  2. constant://e")
    print("     自然常数 (Euler's number)")
    print("  3. constant://golden_ratio")
    print("     黄金比例 (Golden ratio)")
    print("  4. greeting://{name}")
    print("     个性化问候 (Personalized greeting)")
    print("  5. config://app")
    print("     应用配置 (App configuration)")
    print("  6. info://capabilities")
    print("     服务器能力 (Server capabilities)")
    print("")
    print("Available Prompts (3):")
    print("  1. example_prompt(question: str) -> str")
    print("     数学问题模板")
    print("  2. system_prompt() -> str")
    print("     系统提示模板")
    print("  3. calculation_guide(operation: str) -> str")
    print("     计算指导模板")
    print("")
    print("Server is ready and waiting for connections...")
    print("Note: This server runs in stdio mode.")
    print("Use the HTTP version (math_mcp_server_http.py) for HTTP mode.")
    print("=" * 60)
    print("")
    mcp.run()  # Run server via stdio

构建 HTTP 模式服务器

HTTP 模式服务器与 stdio 模式的主要区别在于传输方式的配置。

关键差异

if __name__ == "__main__":
    print("=" * 60)
    print("Math MCP Server (HTTP Mode)")
    print("=" * 60)
    print("")
    print("Server Information:")
    print("  - Name: Math")
    print(f"  - Version: {SERVER_VERSION}")
    print("  - Transport: HTTP (streamable-http)")
    print("  - URL: http://0.0.0.0:8000/mcp")
    print("")
    # ... 工具和资源列表 ...
    print("Starting HTTP server...")
    print("Waiting for client connections...")
    print("Note: This server runs in HTTP mode.")
    print("Use the stdio version (math_mcp_server_stdio.py) for stdio mode.")
    print("=" * 60)
    print("")
    mcp.run(transport="streamable-http")  # Run server via streamable-http

启动 HTTP 服务器

python math_mcp_server_http.py

服务器将在 http://0.0.0.0:8000/mcp 上监听连接。

构建 BMI 计算服务器

BMI 计算服务器是一个更简单的示例,展示了如何构建专注于特定功能的 MCP 服务器。

完整代码

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("BMI")

@mcp.tool()
def calculate_bmi(weight: int, height: int) -> str:
    """Calculate BMI
    
    Args:
        weight: Weight in kilograms (kg)
        height: Height in meters (m)
        
    Returns:
        BMI value and interpretation
    """
    bmi = weight / (height * height)
    
    # BMI 分类
    if bmi < 18.5:
        category = "Underweight (偏瘦)"
    elif 18.5 <= bmi < 25:
        category = "Normal weight (正常)"
    elif 25 <= bmi < 30:
        category = "Overweight (超重)"
    else:
        category = "Obese (肥胖)"
    
    return f"BMI: {bmi:.2f}\n分类: {category}"

if __name__ == "__main__":
    print("=" * 60)
    print("BMI MCP Server")
    print("=" * 60)
    print("")
    print("Server Information:")
    print("  - Name: BMI")
    print("  - Transport: HTTP (streamable-http)")
    print("  - URL: http://localhost:8000/mcp")
    print("  - Available Tools:")
    print("    + calculate_bmi(weight: int, height: int) -> str")
    print("      Calculate Body Mass Index")
    print("      weight: Weight in kilograms (kg)")
    print("      height: Height in meters (m)")
    print("")
    print("Starting HTTP server...")
    print("Waiting for client connections...")
    print("=" * 60)
    print("")
    mcp.run(transport="streamable-http")

启动 BMI 服务器

python bmi_mcp_server.py

测试服务器

使用 Stdio 客户端测试

创建测试客户端 test_math_server.py:

import asyncio
from mcp.client.stdio import stdio_client
from mcp import ClientSession, StdioServerParameters

async def main():
    server_params = StdioServerParameters(
        command="python",
        args=["math_mcp_server_stdio.py"]
    )

    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # 测试工具调用
            print("测试工具调用:")
            result = await session.call_tool("add", {"a": 10, "b": 20})
            print(f"  add(10, 20) = {result.content[0].text}")

            result = await session.call_tool("multiply", {"a": 5, "b": 6})
            print(f"  multiply(5, 6) = {result.content[0].text}")

            # 测试资源读取
            print("\n测试资源读取:")
            resource = await session.read_resource("constant://pi")
            print(f"  Pi = {resource.contents[0].text}")

            resource = await session.read_resource("greeting://Alice")
            print(f"  Greeting = {resource.contents[0].text}")

            # 测试提示模板
            print("\n测试提示模板:")
            prompt = await session.get_prompt("example_prompt", {"question": "What is 1+1?"})
            print(f"  Prompt = {prompt.messages[0].content.text}")

if __name__ == "__main__":
    asyncio.run(main())

运行测试:

python test_math_server.py

使用 HTTP 客户端测试

创建测试客户端 test_bmi_server.py:

import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def main():
    server_url = "http://localhost:8000/mcp"

    async with streamablehttp_client(server_url) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()

            # 测试 BMI 计算
            result = await session.call_tool("calculate_bmi", {
                "weight": 70,
                "height": 1.75
            })
            print(f"BMI 计算结果:\n{result.content[0].text}")

if __name__ == "__main__":
    asyncio.run(main())

运行测试:

# 终端1: 启动 BMI 服务器
python bmi_mcp_server.py

# 终端2: 运行测试
python test_bmi_server.py

最佳实践

1. 工具设计原则

  • 单一职责: 每个工具只做一件事
  • 明确的参数: 使用类型注解和文档字符串
  • 错误处理: 提供清晰的错误信息
  • 返回格式: 保持一致的返回格式

2. 资源命名规范

  • 使用 URI 格式: scheme://path/{param}
  • 保持层次清晰: config://app, data://users/{id}
  • 参数使用蛇形命名: greeting://{user_name}

3. 提示模板设计

  • 明确角色定义
  • 提供使用指导
  • 包含示例说明
  • 保持简洁明了

4. 服务器配置

# 良好的服务器配置示例
if __name__ == "__main__":
    # 显示服务器信息
    print_banner()

    # 选择传输方式
    transport = os.getenv("TRANSPORT", "stdio")

    if transport == "http":
        # HTTP 配置
        host = os.getenv("HOST", "0.0.0.0")
        port = int(os.getenv("PORT", "8000"))
        mcp.run(transport="streamable-http", host=host, port=port)
    else:
        # Stdio 配置
        mcp.run(transport="stdio")

故障排查

问题 1: 服务器启动失败

错误信息:

ModuleNotFoundError: No module named 'fastmcp'

解决方案:

uv sync
# 或
pip install fastmcp

问题 2: 工具调用参数错误

错误信息:

ValidationError: type=integer_type_constrained

解决方案:

  • 检查参数类型是否匹配
  • 验证参数值在有效范围内
  • 使用 JSON Schema 验证工具定义

问题 3: HTTP 端口被占用

错误信息:

OSError: [Errno 48] Address already in use

解决方案:

# 查找占用端口的进程
lsof -i :8000

# 终止进程
kill -9 <PID>

阅读顺序建议

  1. 01-MCP协议入门指南: 了解 MCP 基本概念和核心组件
  2. 02-快速构建MCP服务器: 使用 FastMCP 构建服务器
  3. 03-MCP客户端开发实战: 开发 stdio 和 HTTP 客户端
  4. 04-LLM与MCP集成实践: 集成到 LangGraph 构建智能代理
  5. 05-多服务器架构与最佳实践: 多服务器架构和生产部署

总结

本文详细介绍了如何使用 FastMCP 框架构建 MCP 服务器,包括:

  • 数学计算服务器的完整实现(7个工具、6个资源、3个提示模板)
  • BMI 计算服务器的简化实现
  • stdio 和 HTTP 两种传输方式的配置
  • 服务器测试和故障排查

在下一篇《MCP客户端开发实战》中,我们将学习如何开发 MCP 客户端,包括 stdio 和 HTTP 两种传输方式的客户端实现,以及如何调用工具、读取资源和获取提示模板。

参考资源

文章标签

FastMCP, MCP服务器, Python开发, 工具调用, 资源管理, 提示模板, HTTP服务器, stdio传输, 开发实战
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐