FastMCP:构建MCP服务器与客户端的完整指南

FastMCP:构建MCP服务器与客户端的完整指南

FastMCP 2.0 全方位教程:用 Python 的高效方式构建模型上下文协议 (MCP) 服务器与客户端。学习如何为大型语言模型 (LLM) 应用创建工具、资源和提示。

课 4 2025-07-03 06:59

将 FastMCP 与 Claude 代码集成

将 FastMCP 与 Claude Code 集成

Claude Code 是 Anthropic 官方支持 MCP 服务器的命令行工具 (CLI),它允许用户通过自定义 FastMCP 服务器扩展 Claude 的功能。这种集成实现了强大的开发工作流,让 Claude 能够访问你的工具、资源和特定领域的功能。

为什么要与 Claude Code 集成?

Claude Code 的集成提供了以下几个引人注目的优势:

  • 增强开发工作流:Claude 可以直接访问你的自定义工具。
  • 上下文感知协助:Claude 能够读取你的项目资源和配置。
  • 自动化任务:通过你的 MCP 工具执行复杂操作。
  • 本地和远程支持:可连接本地开发服务器和已部署的服务。
  • 实时交互:动态工具发现和执行。

设置 FastMCP 服务器

创建开发服务器

让我们创建一个专为开发工作流设计的综合性 FastMCP 服务器:

# dev_server.py
import os
import subprocess
import json
from pathlib import Path
from datetime import datetime
from fastmcp import FastMCP, Context

mcp = FastMCP(
    name="Development Assistant",
    instructions="""
    此服务器提供开发工具,包括:
    - 项目分析和文件操作
    - Git 操作和仓库管理
    - 代码质量检查和测试
    - 环境和依赖管理
    """
)

@mcp.tool
def run_command(command: str, cwd: str = ".") -> dict:
    """执行一个 shell 命令并返回结果。"""
    try:
        result = subprocess.run(
            command.split(),
            cwd=cwd,
            capture_output=True,
            text=True,
            timeout=30
        )
        return {
            "command": command,
            "returncode": result.returncode,
            "stdout": result.stdout,
            "stderr": result.stderr,
            "success": result.returncode == 0
        }
    except subprocess.TimeoutExpired:
        return {"error": "命令执行超时(30 秒)"}
    except Exception as e:
        return {"error": str(e)}

@mcp.tool
def analyze_project_structure(path: str = ".") -> dict:
    """分析项目目录结构。"""
    project_path = Path(path)

    if not project_path.exists():
        return {"error": f"路径 {path} 不存在"}

    structure = {
        "root": str(project_path.absolute()),
        "files": [],
        "directories": [],
        "python_files": [],
        "config_files": [],
        "total_size": 0
    }

    config_extensions = {".json", ".yaml", ".yml", ".toml", ".ini", ".cfg"}

    for item in project_path.rglob("*"):
        if item.is_file():
            size = item.stat().st_size
            structure["total_size"] += size

            file_info = {
                "path": str(item.relative_to(project_path)),
                "size": size,
                "modified": datetime.fromtimestamp(item.stat().st_mtime).isoformat()
            }

            structure["files"].append(file_info)

            if item.suffix == ".py":
                structure["python_files"].append(file_info)
            elif item.suffix in config_extensions:
                structure["config_files"].append(file_info)

        elif item.is_dir():
            structure["directories"].append(str(item.relative_to(project_path)))

    return structure

@mcp.tool
async def check_code_quality(file_path: str, ctx: Context) -> dict:
    """使用各种 linting 工具检查代码质量。"""
    await ctx.info(f"正在检查 {file_path} 的代码质量...")

    if not os.path.exists(file_path):
        return {"error": f"文件 {file_path} 未找到"}

    results = {"file": file_path, "checks": {}}

    # 使用不同的工具检查
    tools = {
        "flake8": ["flake8", file_path],
        "pylint": ["pylint", file_path, "--output-format=json"],
        "mypy": ["mypy", file_path]
    }

    for tool, command in tools.items():
        try:
            result = subprocess.run(
                command,
                capture_output=True,
                text=True,
                timeout=15
            )
            results["checks"][tool] = {
                "returncode": result.returncode,
                "output": result.stdout,
                "errors": result.stderr
            }
        except (subprocess.TimeoutExpired, FileNotFoundError):
            results["checks"][tool] = {"error": f"{tool} 不可用或超时"}

    return results

@mcp.resource("project://config")
def get_project_config() -> dict:
    """从各种配置文件中获取项目配置。"""
    config = {"found_files": [], "configurations": {}}

    config_files = [
        "pyproject.toml", "setup.py", "requirements.txt", 
        "package.json", "Dockerfile", ".env", "config.yaml"
    ]

    for config_file in config_files:
        if os.path.exists(config_file):
            config["found_files"].append(config_file)
            try:
                with open(config_file, 'r') as f:
                    content = f.read()
                config["configurations"][config_file] = {
                    "content": content[:1000] + ("..." if len(content) > 1000 else ""),
                    "size": len(content)
                }
            except Exception as e:
                config["configurations"][config_file] = {"error": str(e)}

    return config

@mcp.resource("git://status")
def get_git_status() -> dict:
    """获取当前 Git 仓库状态。"""
    try:
        # 检查是否在 git 仓库中
        subprocess.run(["git", "rev-parse", "--git-dir"], 
                      check=True, capture_output=True)

        # 获取各种 git 信息
        commands = {
            "branch": ["git", "branch", "--show-current"],
            "status": ["git", "status", "--porcelain"],
            "last_commit": ["git", "log", "-1", "--pretty=format:%H|%an|%ad|%s"],
            "remote": ["git", "remote", "-v"]
        }

        result = {}
        for key, command in commands.items():
            try:
                output = subprocess.run(
                    command, capture_output=True, text=True, check=True
                ).stdout.strip()
                result[key] = output
            except subprocess.CalledProcessError:
                result[key] = "不可用"

        return result

    except subprocess.CalledProcessError:
        return {"error": "当前目录不是一个 Git 仓库"}

if __name__ == "__main__":
    mcp.run(transport="http", host="127.0.0.1", port=8000)

连接到 Claude Code

运行服务器

首先,启动 FastMCP 服务器:

# 以 HTTP 模式启动服务器
python dev_server.py

你的服务器将在 http://127.0.0.1:8000/mcp/ 运行。

将服务器添加到 Claude Code

使用 Claude Code 的 MCP 管理命令添加你的服务器:

# 添加你的 FastMCP 服务器
claude mcp add dev-assistant --transport http http://localhost:8000/mcp/

# 验证服务器是否已连接
claude mcp list

备选方案:STDIO 传输

对于本地开发,你可能更喜欢 STDIO 传输:

# dev_server_stdio.py
# ... (与上面的服务器代码相同)

if __name__ == "__main__":
    mcp.run()  # 默认使用 STDIO

将其添加到 Claude Code:

claude mcp add dev-assistant --transport stdio "python dev_server_stdio.py"

使用 Claude Code 访问你的服务器

连接后,你可以通过 Claude Code 与你的 MCP 服务器交互:

基本工具使用

# Claude 会自动发现并使用你的工具
claude "分析我当前的项目结构"

# Claude 将调用你的 analyze_project_structure 工具
# 响应将包含文件数量、大小和组织结构

资源访问

Claude 可以通过 @ 提及来访问服务器的资源:

# 访问项目配置
claude "这个项目有什么配置文件?使用 @dev-assistant:project://config"

# 检查 git 状态
claude "当前的 git 状态是什么?使用 @dev-assistant:git://status"

复杂工作流

结合多个工具进行复杂操作:

claude "请检查 src/ 目录下所有 Python 文件的代码质量并总结问题"

# Claude 将:
# 1. 使用 analyze_project_structure 找到 Python 文件
# 2. 对每个文件使用 check_code_quality
# 3. 总结结果

高级配置示例

带有数据库工具的开发服务器

# db_server.py
import sqlite3
from fastmcp import FastMCP

mcp = FastMCP("Database Tools")

@mcp.tool
def query_database(query: str, db_path: str = "app.db") -> dict:
    """对数据库执行 SQL 查询。"""
    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()
        cursor.execute(query)

        if query.strip().upper().startswith("SELECT"):
            results = cursor.fetchall()
            columns = [description[0] for description in cursor.description]
            return {
                "columns": columns,
                "rows": results,
                "count": len(results)
            }
        else:
            conn.commit()
            return {"affected_rows": cursor.rowcount}

    except Exception as e:
        return {"error": str(e)}
    finally:
        conn.close()

@mcp.resource("db://schema")
def get_database_schema() -> dict:
    """获取数据库模式信息。"""
    # 获取数据库模式的实现
    pass

if __name__ == "__main_":
    mcp.run(transport="http", port=8001)

API 测试服务器

# api_server.py
import requests
from fastmcp import FastMCP, Context

mcp = FastMCP("API Testing Tools")

@mcp.tool
async def test_api_endpoint(url: str, method: str = "GET", 
                          headers: dict = None, data: dict = None,
                          ctx: Context = None) -> dict:
    """测试 API 端点并返回详细结果。"""
    await ctx.info(f"正在测试 {method} {url}...")

    try:
        response = requests.request(
            method=method.upper(),
            url=url,
            headers=headers or {},
            json=data,
            timeout=10
        )

        return {
            "url": url,
            "method": method.upper(),
            "status_code": response.status_code,
            "headers": dict(response.headers),
            "response_time": response.elapsed.total_seconds(),
            "body": response.text[:1000] if response.text else None,
            "success": 200 <= response.status_code < 300
        }

    except Exception as e:
        return {
            "url": url,
            "method": method.upper(),
            "error": str(e),
            "success": False
        }

if __name__ == "__main__":
    mcp.run(transport="http", port=8002)

多服务器设置

你可以将多个 FastMCP 服务器连接到 Claude Code:

# 添加多个服务器
claude mcp add dev-tools --transport http http://localhost:8000/mcp/
claude mcp add db-tools --transport http http://localhost:8001/mcp/
claude mcp add api-tools --transport http http://localhost:8002/mcp/

# Claude 现在可以访问所有服务器
claude "检查项目结构,然后测试配置中定义的 API 端点"

常见问题故障排除

服务器无响应

# 检查服务器是否正在运行
curl http://localhost:8000/mcp/

# 移除并重新添加服务器
claude mcp remove dev-assistant
claude mcp add dev-assistant --transport http http://localhost:8000/mcp/

未找到工具

确保你的服务器已正确注册并且工具已用装饰器标记:

# 确保工具已正确装饰
@mcp.tool  # 此装饰器是必需的
def my_tool():
    pass

资源访问问题

检查资源 URI 格式:

# 正确的格式
claude "使用 @server-name:resource://uri"

# 错误的格式: @server-name/resource://uri

Claude Code 集成最佳实践

  1. 清晰的工具名称:使用描述性强、不言自明的工具名称。
  2. 良好的文档:包含全面的文档字符串。
  3. 错误处理:提供有意义的错误消息。
  4. 资源组织:使用逻辑 URI 方案来组织资源。
  5. 性能:保持工具执行时间合理。
  6. 安全性:验证输入并限制危险操作。

后续步骤

将 FastMCP 服务器集成到 Claude Code 后,你可以:

  • 构建特定领域的开发助手。
  • 自动化复杂的项目管理任务。
  • 创建自定义代码分析和质量工具。
  • 开发专门的测试和部署工作流。

在最后一部分,我们将探讨 FastMCP 服务器的高级模式、生产部署和扩展策略。