FastMCP:构建 MCP 服务器和客户端的高效 Python 框架
在人工智能领域,模型上下文协议(Model Context Protocol,简称 MCP)作为一种标准化的协议,为大型语言模型(LLM)提供了丰富的上下文和工具支持。而 FastMCP 作为构建 MCP 服务器和客户端的 Python 框架,以其简洁的 API 设计、高效的开发体验以及强大的扩展能力,正逐渐成为开发者们的首选工具。
一、FastMCP 简介
FastMCP 是一个用于构建 MCP 服务器和客户端的 Python 框架,其目标是让开发者能够以更少的代码、更高的效率构建出功能强大的 MCP 应用。相比官方的 MCP Python SDK,FastMCP 在多个方面进行了优化和改进,提供了更简洁的 API 设计、更高效的开发体验以及更强大的扩展能力。
FastMCP 的核心优势主要体现在以下几个方面:
(一)简洁的 API 设计
FastMCP 采用了 Pythonic 的设计风格,通过装饰器(Decorator)即可轻松定义工具(Tools)、资源(Resources)和提示(Prompts)。这种设计方式极大地减少了开发过程中的样板代码,让开发者能够更加专注于业务逻辑的实现。
(二)高效的开发体验
FastMCP 提供了丰富的内置功能,如服务器组合、远程服务器代理、OpenAPI/FastAPI 集成等,这些功能不仅提高了开发效率,还为开发者提供了更多的灵活性。此外,FastMCP 还支持多种客户端/服务端传输模式,包括 Stdio、SSE 和内存传输,能够满足不同场景下的开发需求。
(三)强大的扩展能力
FastMCP 的设计具有高度的可扩展性,开发者可以根据自己的需求轻松添加新的功能和模块。无论是构建复杂的 AI 应用,还是进行简单的数据处理,FastMCP 都能够提供强大的支持。
二、FastMCP 与官方 SDK 的关系
FastMCP 1.0 的核心概念已经被纳入官方的 MCP Python SDK,而当前的 FastMCP 2.0 则是在此基础上进行的进一步扩展和优化。FastMCP 2.0 不仅继承了 1.0 版本的基础功能,还引入了完整的客户端支持、服务器组合、OpenAPI/FastAPI 集成、远程服务器代理以及内置测试工具等新功能,显著提升了开发效率和应用的灵活性。
三、开发示例
为了更好地理解 FastMCP 的强大功能和简洁的开发方式,我们以一个基于 FastMCP 的数学运算智能问答应用为例,详细介绍其开发过程。
(一)安装 FastMCP
首先,需要安装 FastMCP 框架。通过以下命令即可完成安装:
uv pip install fastmcp
(二)服务端实现
服务端的实现非常简单,只需要定义几个基本的数学运算工具即可。以下是服务端的代码示例:
from fastmcp import FastMCPmcp = FastMCP(name="MyAssistantServer")@mcp.tool()
def add(a: float, b: float) -> float:"""加法运算参数:a: 第一个数字b: 第二个数字返回:两数之和"""return a + b@mcp.tool()
def subtract(a: float, b: float) -> float:"""减法运算参数:a: 第一个数字b: 第二个数字返回:两数之差 (a - b)"""return a - b@mcp.tool()
def multiply(a: float, b: float) -> float:"""乘法运算参数:a: 第一个数字b: 第二个数字返回:两数之积"""return a * b@mcp.tool()
def divide(a: float, b: float) -> float:"""除法运算参数:a: 被除数b: 除数返回:两数之商 (a / b)异常:ValueError: 当除数为零时"""if b == 0:raise ValueError("除数不能为零")return a / bif __name__ == "__main__":mcp.run(transport='sse', host="127.0.0.1", port=8001)
在上述代码中,我们定义了四个基本的数学运算工具:加法、减法、乘法和除法。通过装饰器 @mcp.tool()
,这些函数被注册为 MCP 服务器的工具,可供客户端调用。最后,通过调用 mcp.run()
方法启动服务器,并指定使用 SSE 传输模式。
(三)客户端实现
客户端的实现同样简单。通过一行代码即可创建一个 MCP 客户端,并连接到服务端。以下是客户端的代码示例:
from fastmcp import Client
import asyncioasync def main():# 测试 mcp 客户端的功能async with Client("http://127.0.0.1:8001/sse") as mcp_client:tools = await mcp_client.list_tools()print(f"Available tools: {tools}")result = await mcp_client.call_tool("add", {"a": 5, "b": 3})print(f"Result: {result[0].text}")if __name__ == "__main__":asyncio.run(main())
在上述代码中,我们通过 Client
类创建了一个 MCP 客户端,并连接到服务端。通过调用 list_tools()
方法,我们可以获取服务端提供的所有工具列表。然后,通过调用 call_tool()
方法,我们可以调用服务端的工具并获取结果。
(四)数学运算智能问答应用
基于 FastMCP,我们还可以构建一个数学运算智能问答应用。该应用通过与大语言模型(LLM)进行交互,根据用户的输入调用相应的工具进行计算,并返回结果。以下是该应用的代码示例:
import asyncio
import json
import logging
import os
from typing import List, Dictfrom fastmcp import Client
from openai import OpenAIclass LLMClient:"""LLM客户端,负责与大语言模型API通信"""def __init__(self, model_name: str, url: str, api_key: str) -> None:self.model_name: str = model_nameself.url: str = urlself.client = OpenAI(api_key=api_key, base_url=url)def get_response(self, messages: List[Dict[str, str]]) -> str:"""发送消息给LLM并获取响应"""response = self.client.chat.completions.create(model=self.model_name,messages=messages,stream=False)return response.choices[0].message.contentclass ChatSession:"""聊天会话,处理用户输入和LLM响应,并与MCP工具交互"""def __init__(self, llm_client: LLMClient, mcp_client: Client) -> None:self.mcp_client: Client = mcp_clientself.llm_client: LLMClient = llm_clientasync def process_llm_response(self, llm_response: str) -> str:"""处理LLM响应,解析工具调用并执行"""try:# 尝试移除可能的markdown格式if llm_response.startswith('```json'):llm_response = llm_response.strip('```json').strip('```').strip()tool_call = json.loads(llm_response)if "tool" in tool_call and "arguments" in tool_call:# 检查工具是否可用tools = await self.mcp_client.list_tools()if any(tool.name == tool_call["tool"] for tool in tools):try:# 执行工具调用result = await self.mcp_client.call_tool(tool_call["tool"], tool_call["arguments"])return f"Tool execution result: {result}"except Exception as e:error_msg = f"Error executing tool: {str(e)}"logging.error(error_msg)return error_msgreturn f"No server found with tool: {tool_call['tool']}"return llm_responseexcept json.JSONDecodeError:# 如果不是JSON格式,直接返回原始响应return llm_responseasync def start(self, system_message: str) -> None:"""启动聊天会话的主循环"""messages = [{"role": "system", "content": system_message}]while True:try:# 获取用户输入user_input = input("用户: ").strip().lower()if user_input in ["quit", "exit", "退出"]:print('AI助手退出')breakmessages.append({"role": "user", "content": user_input})# 获取LLM的初始响应llm_response = self.llm_client.get_response(messages)print("助手: ", llm_response)# 处理可能的工具调用result = await self.process_llm_response(llm_response)# 如果处理结果与原始响应不同,说明执行了工具调用,需要进一步处理while result != llm_response:messages.append({"role": "assistant", "content": llm_response})messages.append({"role": "system", "content": result})# 将工具执行结果发送回LLM获取新响应llm_response = self.llm_client.get_response(messages)result = await self.process_llm_response(llm_response)print("助手: ", llm_response)messages.append({"role": "assistant", "content": llm_response})except KeyboardInterrupt:print('AI助手退出')breakasync def main():async with Client("http://127.0.0.1:8001/sse") as mcp_client:# 初始化LLM客户端,使用通义千问模型llm_client = LLMClient(model_name='qwen-plus-latest', api_key=os.getenv('DASHSCOPE_API_KEY'),url='https://dashscope.aliyuncs.com/compatible-mode/v1')# 获取可用工具列表并格式化为系统提示的一部分tools = await mcp_client.list_tools()dict_list = [tool.__dict__ for tool in tools]tools_description = json.dumps(dict_list, ensure_ascii=False)# 系统提示,指导LLM如何使用工具和返回响应system_message = f'''你是一个智能助手,严格遵循以下协议返回响应:可用工具:{tools_description}响应规则:1、当需要计算时,返回严格符合以下格式的纯净JSON:{{"tool": "tool-name","arguments": {{"argument-name": "value"}}}}2、禁止包含以下内容:- Markdown标记(如```json)- 自然语言解释(如"结果:")- 格式化数值(必须保持原始精度)- 单位符号(如元、kg)校验流程:✓ 参数数量与工具定义一致✓ 数值类型为number✓ JSON格式有效性检查正确示例:用户:单价88.5买235个多少钱?响应:{{"tool":"multiply","arguments":{{"a":88.5,"b":235}}}}错误示例:用户:总金额是多少?错误响应:总价500元 → 含自然语言错误响应:```json{{...}}```→ 含Markdown3、在收到工具的响应后:- 将原始数据转化为自然、对话式的回应- 保持回复简洁但信息丰富- 聚焦于最相关的信息- 使用用户问题中的适当上下文- 避免简单重复使用原始数据'''# 启动聊天会话chat_session = ChatSession(llm_client=llm_client, mcp_client=mcp_client)await chat_session.start(system_message=system_message)if __name__ == "__
__main__":asyncio.run(main())
(五)运行验证
运行服务端代码:
python fast_mcp_server.py
运行客户端代码:
python fast_mcp_client.py
在客户端中输入数学问题,例如:
用户: 现在要购买一批货,单价是 1034.32423,数量是 235326。商家后来又说,可以在这个基础上,打95折,折后总价是多少?
客户端会调用服务端的 multiply
工具进行计算,并返回结果:
助手: {"tool": "multiply","arguments": {"a": 1034.32423,"b": 235326}
}
助手: {"tool": "multiply","arguments": {"a": 243403383.74898,"b": 0.95}
}
助手: 折后总价是231233214.56。
四、FastMCP 的更多功能
除了上述提到的功能外,FastMCP 还提供了许多其他强大的功能,例如:
(一)服务器组合
通过 mcp.mount()
或 mcp.import_server()
方法,可以将多个 FastMCP
实例组合到一个父服务器中,从而构建出模块化的应用程序。这种方式不仅提高了代码的可维护性,还方便了功能的扩展。
(二)OpenAPI/FastAPI 集成
FastMCP 提供了从现有的 OpenAPI 规范或 FastAPI 应用程序生成 FastMCP
服务器的功能。通过这种方式,开发者可以轻松地将现有的 Web API 集成到 MCP 生态系统中,进一步扩展了 MCP 的应用场景。
(三)代理服务器
通过 FastMCP.as_proxy()
方法,可以创建一个代理服务器,该服务器可以作为本地或远程 MCP 服务器的中间层。这种方式特别适用于桥接不同的传输协议(例如,将远程 SSE 服务器代理到本地 Stdio 客户端)或为不受控制的服务器添加逻辑层。
(四)内置测试工具
FastMCP 提供了强大的内置测试工具,支持通过内存传输直接连接到 FastMCP
服务器实例,从而在测试过程中无需进行进程管理和网络调用。这种方式极大地提高了测试效率,降低了测试成本。
五、总结
FastMCP 作为一个高效、简洁且功能强大的 Python 框架,为构建 MCP 服务器和客户端提供了极大的便利。通过其简洁的 API 设计、高效的开发体验和强大的扩展能力,开发者可以快速构建出功能丰富的 MCP 应用。无论是简单的工具开发,还是复杂的 AI 应用构建,FastMCP 都能够满足开发者的需求。
如果你对 MCP 或 FastMCP 感兴趣,不妨尝试使用它来构建自己的应用。相信你一定会被其强大的功能和简洁的设计所吸引。同时,也欢迎关注我的后续文章,我将继续介绍更多关于 MCP 的内容,以及 FastMCP 的高级用法和实战案例。