社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

FastMCP:用Pythonic方式构建MCP服务最佳选择!

新一代智能化应用 • 3 天前 • 44 次点击  

在前面的文章里,笔者也介绍过 MCP 是什么,以及 MCP 相较于传统 Function Call 方式的优势。当下,更多开发者关注的是如何在自己的代码中实际集成和使用 MCP,这也是过去几期文章讨论的核心,感兴趣的朋友可以翻阅查看。

虽然 MCP 协议官方定义了 LLM 应用访问外部“工具”(执行动作)和“资源”(获取数据)的标准方式,并提供了不少客户端和服务器的构建方法与示例,但从协议层直接构建 MCP 服务,仍然不可避免地涉及大量繁琐的底层工作。对于普通开发者而言,一个封装完善、简单易用的开发框架就显得尤为重要。

今天,笔者就向大家介绍一个非常好用的 MCP 库——FastMCP。可以说,FastMCP 是当前最受欢迎的用于构建 MCP Server 的库之一。

基本介绍

FastMCP 核心目标就是成为构建 MCP Server 和客户端的“快速、Pythonic 的方式”。它远不止是一个简单的封装,而是一套完整的解决方案。它旨在屏蔽 MCP 协议的底层复杂性,让开发者能够运用熟悉且喜爱的 Python 语法和模式,专注于创造真正有价值的功能,而无需在协议处理、Server 管理、内容类型协商等事务上耗费心神。

快速上手:构建计算器服务

使用 FastMCP 构建一个简单的加法工具,只需几行代码:

# server.py
from fastmcp import FastMCP

# 1. 创建一个 FastMCP 服务器实例
mcp = FastMCP("计算器 Demo")

# 2. 使用 @mcp.tool() 装饰器暴露一个函数作为工具
@mcp.tool()
def add(a: int, b: int) -> int:
    """将两个数字相加"""
    return a + b

# 3. (非必要) 使脚本可以直接运行
if __name__ == "__main__":
    mcp.run() # 启动服务器

运行服务

在终端中,你可以这样启动服务:

  • 开发模式 (推荐用于调试):

    fastmcp dev server.py

    这将启动一个本地开发服务器,并提供一个交互式的 MCP Inspector 界面,方便调试(如下图所示)。

  • 生产模式 (直接运行):

    python server.py

启动后,一个本地 MCP Server 就构建完成了,任何兼容 MCP 的客户端都可以发现并使用这个 add 工具。

提示:FastMCP 项目的 examples/ 目录下有更丰富的代码示例,涵盖基础到高级用法,是学习和测试的好资源。

核心概念与特性

FastMCP 使用直观的装饰器来定义 Server 管理的各个组件:

1. 构建 FastMCP 服务器实例:

  • 它是 MCP 应用的核心对象,负责处理连接、协议和路由。
  • 创建:mcp = FastMCP("My Awesome App")
  • 指定依赖 (可选):mcp = FastMCP("Data App", dependencies=["pandas", "numpy"])

2. 工具 (Tools): @mcp.tool()

  • 用途: 让 LLM 执行你的 Python 函数(同步/异步),适用于计算、API 调用、产生副作用等场景。
  • 实现: 使用 @mcp.tool() 装饰函数。FastMCP 会根据函数的类型提示(支持 Pydantic 模型)和文档字符串自动生成 MCP 协议所需的模式 (Schema)。
@mcp.tool()
async def send_notification(user_id: int, message: str) -> dict:
    """向用户发送通知。"""
    print(f"Notifying user {user_id}{message}")
    return {"status""sent"}

3. 资源 (Resources): @mcp.resource("your://uri/template/{param}")

  • 用途: 向 LLM 暴露数据,主要用于信息获取(类似 HTTP GET),应避免复杂计算和副作用。
  • 实现: 使用 @mcp.resource("uri") 装饰函数。URI 可以是静态的,也可以使用 {} 定义动态模板,URI 中的参数会自动传递给函数。
# 静态资源示例
@mcp.resource("config://app-version")
def get_app_version() -> str:
    """返回应用版本号。"""
    return  "v2.1.0"

# 动态资源示例
@mcp.resource("db://users/{user_id}/email")
async def get_user_email(user_id: str) -> str:
    """根据用户ID检索邮箱地址。"""
    # ... 此处执行数据库查询逻辑 ...
    return f"{user_id}@example.com"

4. 提示 (Prompts): @mcp.prompt()

  • 用途: 定义可复用的文本模板或交互模式,用于指导 LLM 如何更有效地使用你的工具和资源。
  • 实现: 使用 @mcp.prompt() 装饰函数。函数可以返回简单的字符串,或者 UserMessageAssistantMessage 对象(或它们的列表)。
from fastmcp.prompts.base import UserMessage

@mcp.prompt()
def ask_review(code_snippet: str) -> UserMessage:
    """生成标准的 Code Review 请求。"""
    return UserMessage(f"请帮忙审查以下代码:\n```python\n{code_snippet}\n```")

5. 上下文 (Context):

  • 用途: 在工具或资源函数的内部访问 MCP 服务器提供的能力。
  • 实现: 在函数参数中添加类型提示 ctx: Context
  • 核心能力:
    • 日志记录: ctx.debug()ctx.info()ctx.warning()ctx.error()
    • 进度报告: ctx.report_progress(current, total)
    • 读取其他资源: await ctx.read_resource(uri)
    • 获取请求信息: ctx.request_idctx.client_id
    • LLM 采样 (高级): await ctx.sample(...) 请求客户端 LLM 生成内容。
from fastmcp import Context

@mcp.tool()
async def process_data(data_uri: str, ctx: Context) -> str:
    """处理大型文件,并报告进度。"""
    await ctx.info(f"开始处理文件:{data_uri}")
    content_resource = await ctx.read_resource(data_uri)
    content = content_resource[0].content # 假设是文本内容
    # ... 执行处理逻辑 ...
    await ctx.report_progress(11# 报告处理完成
    await ctx.info(f"文件处理完成:{data_uri}")
    return "处理成功。"

6. 图像处理 (Image):

  • 用途: 简化图像数据的输入和输出。
  • 实现: 使用 fastmcp.Image 类。FastMCP 会自动处理图像数据与 MCP 协议要求的 Base64 编码之间的转换。
from fastmcp import Image
from PIL import Image as PILImage
import io

@mcp.tool()
def create_thumbnail(img_data: Image) -> Image:
    """根据提供的图像创建缩略图。"""
    img = PILImage.open(io.BytesIO(img_data.data))
    img.thumbnail((100100))
    buffer = io.BytesIO()
    img.save(buffer, format="PNG")
    # 返回包含缩略图数据的新 Image 对象
    return Image(data=buffer.getvalue(), format="png")

@mcp.tool()
def load_image(path: str) -> Image:
     """从指定路径加载图像。"""
     # 自动处理文件读取和基于扩展名的格式检测
     return Image(path=path)

客户端交互

FastMCP 不仅能构建服务器,还提供了强大的 Client 类。你可以用它通过 Python 代码轻松地与 任何 兼容 MCP 的服务器进行交互(调用工具、读取资源),并支持多种传输协议和 LLM 采样等高级功能。

  • 客户端交互示例:
from fastmcp import Client

# 连接目标可以是脚本路径、服务器URL或FastMCP对象
async with Client("path/to/server_script.py"as client:
    # 调用工具
    result = await client.call_tool("add", {"a"5"b"3})
    print(f"调用 add(5, 3) 结果: {result.content[0].text}"# Output: 8

    # 读取资源
    version = await client.read_resource("config://app-version")
    print(f"读取 config://app-version 结果: {version.content[0].text}")

FastMCP v2 高级特性:无缝集成现有 API

FastMCP v2 引入了多项高级特性,其中最引人瞩目的显著优势是能够从你现有的 FastAPI 应用或 OpenAPI 规范自动生成 MCP 服务器。这意味着你可以轻松复用已有的 Web API 代码,将其快速接入 LLM 生态。

只需使用 FastMCP.from_fastapi(your_fastapi_app) 或 FastMCP.from_openapi(spec, client=http_client),FastMCP 就能智能地将 HTTP 端点映射为 MCP 的工具和资源。

  • 示例:一行代码桥接 FastAPI 应用
from fastapi import FastAPI
from fastmcp import FastMCP

# 假设你有一个现成的 FastAPI 应用
fastapi_app = FastAPI()

@fastapi_app.get("/status", summary="获取服务状态")
def get_status():
return {"status""ok"}

@fastapi_app.post("/users/{user_id}/notify", summary="向用户发送通知")
def  notify_user(user_id: int, message: str):
  print(f"正在通知用户 {user_id}{message}")
return {"delivered"True}

# 只需这一行代码,即可从 FastAPI 应用创建 MCP 服务器!
mcp_server = FastMCP.from_fastapi(fastapi_app, name="API Bridge")

# 现在 mcp_server 就可以运行并被 LLM 使用了
# if __name__ == "__main__":
#     mcp_server.run()

此外,FastMCP v2 还支持代理现有 MCP 服务器 (FastMCP.from_client) 和组合多个 MCP 应用 (mcp.mount),这为构建更复杂、模块化的系统提供了便利。

小结

FastMCP 凭借其 Pythonic 的设计哲学、简洁易用的 API 以及强大的功能(尤其是 FastAPI/OpenAPI 集成),极大地降低了构建 MCP Server 或将现有服务暴露为 MCP Server 的门槛。它为开发者提供了一条优雅的适配MCP协议的路径,轻松扩展 LLM 的能力边界,同时自己也成为当前构建MCP Server最佳方案之一。

  • 官方文档:gofastmcp.com
  • GitHub 仓库:github.com/jlowin/fastmcp

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/181324
 
44 次点击