Python MCP实战:构建 FastAPI 服务端与客户端示例&MCP客户端调用

引言

在现代微服务架构中,服务间的通信协议选择至关重要。除了常见的 RESTful API、gRPC 等,MCP(Message-oriented Communication Protocol)作为一种面向消息的通信协议,也逐渐在特定场景中展现出其优势。本文将通过一个具体的 Python 示例,演示如何基于 fastapi-mcpmcp 库,构建一个 MCP 服务端和客户端,并实现工具(Tool)的远程调用。

服务端将使用 FastAPI 框架,通过 fastapi-mcp 库将一个 API endpoint 暴露为 MCP 工具。客户端则会演示如何连接到 MCP 服务,列出可用的工具,并远程调用它。

核心组件:

  • 服务端 (main.py): 一个基于 FastAPI 的 Web 应用,提供 MCP 服务。
  • 客户端 (test/mcp_client.py): 一个 Python 脚本,用于连接 MCP 服务并与之交互。

服务端实现 (main.py)

我们的服务端基于 FastAPI 构建。核心是利用 fastapi-mcp 库将指定的 FastAPI 路由转换成 MCP 可以识别和调用的“工具”。

1. 定义 FastAPI 路由

首先,我们定义一个标准的 FastAPI 路由,这个路由将是我们要通过 MCP 暴露的功能。在这个例子中,我们创建了一个名为 get_date 的异步函数,它接收 questiontype 两个参数,并返回一个答案。

# main.pyfrom fastapi import FastAPI from fastapi_mcp import FastApiMCP import uvicorn # 创建FastAPI应用 app = FastAPI( title="GRAPH RAG API", description="高效的web数据管理接口", version="0.1.0",)# ... (其他FastAPI配置,如CORS、异常处理等)@app.get("/answer", operation_id="get_data", tags=["mcp"], summary="根据问题获取答案")asyncdefget_data(question:str,type:str):# 在实际应用中,这里会调用服务层处理业务逻辑# chat_service = get_chat_service()# answer = await chat_service.get_answer_mcp(question,type)print(f"收到问题: {question}, 类型: {type}")returnf"这是关于 '{question}' 的答案。"# ...

关键点:

  • operation_id="get_data": 这个 ID 将作为 MCP 工具的名称。
  • tags=["mcp"]: 我们通过标签(tag)来筛选哪些路由需要被 MCP 服务暴露。

2. 集成 FastApiMCP

接下来,我们实例化 FastApiMCP 并将其挂载到 FastAPI 应用上。

# main.py (续)# 创建 FastApiMCP 实例并挂载 mcp = FastApiMCP( app, name="问题检索mcp", description="测试描述", include_tags=["mcp"]# 只暴露包含 "mcp" 标签的路由) mcp.mount_sse(mount_path="/sse")if __name__ =="__main__": uvicorn.run(app, host="0.0.0.0", port=8000)

关键点:

  • include_tags=["mcp"]: 告诉 FastApiMCP 只扫描并注册那些 tags 列表中包含 "mcp" 的路由。
  • mcp.mount_sse(mount_path="/sse"): 在指定的路径 (/sse) 上启用基于 SSE (Server-Sent Events) 的 MCP 通信端点。客户端将通过这个端点进行连接。

客户端实现 (test/mcp_client.py)

客户端使用 mcp 库来连接服务端,并执行工具调用。

1. 连接和初始化

客户端代码的核心是使用 sse_client 创建一个到服务器 /sse 端点的连接,并初始化一个 ClientSession

# test/mcp_client.pyimport asyncio from mcp import ClientSession from mcp.client.sse import sse_client asyncdefsimple_client():"""使用 mcp 库的简化客户端""" url ="http://127.0.0.1:8000/sse"# 指向 SSE 挂载点try:asyncwith sse_client(url)as(read, write):asyncwith ClientSession(read, write)as session:# 1. 初始化会话(MCP 握手)await session.initialize()print("✅ MCP 会话初始化成功")# ... 后续操作except Exception as e:print(f"❌ 客户端连接失败: {e}")if __name__ =="__main__": asyncio.run(simple_client())

关键点:

  • sse_client(url): 创建一个异步上下文管理器,处理与 SSE 端点的连接。
  • session.initialize(): 执行 MCP 握手,确保客户端和服务端可以正常通信。

2. 列出和调用工具

初始化成功后,我们就可以与服务端交互了,例如列出所有可用的工具,或者调用某个特定的工具。

# test/mcp_client.py (续, 在 session 上下文内)# 2. 列出可用工具 tools_result =await session.list_tools()print("🛠️ 可用的工具:")for tool in tools_result.tools:print(f" - {tool.name}: {tool.description}")# 3. 调用工具 call_result =await session.call_tool("get_data", arguments={"question":"测试问题","type":"test"})if call_result.content:print(f"📝 调用结果: {call_result.content[0].text}")

关键点:

  • session.list_tools(): 从服务端获取所有已注册工具的列表。
  • session.call_tool("get_data", ...): 远程调用名为 get_data 的工具。
  • arguments: 一个字典,包含了调用工具时需要传递的参数,这些参数对应于 main.pyget_date 函数的参数。

完整的mcp_client.py客户端代码如下:

import asyncio from mcp import ClientSession from mcp.client.sse import sse_client asyncdefsimple_client():"""使用官方 mcp 库的简化客户端"""# 配置 SSE 服务器参数 url="http://127.0.0.1:8000/mcp"try:asyncwith sse_client(url)as(read, write):asyncwith ClientSession(read, write)as session:# 初始化会话(MCP 握手)await session.initialize()print("✅ MCP 会话初始化成功")# 列出可用工具 tools_result =await session.list_tools()print("🛠️ 可用的工具:")for tool in tools_result.tools:print(f" - {tool.name}: {tool.description}")# 调用 get_gStore_data 工具 call_result =await session.call_tool("get_gStore_data", arguments={"question":"测试问题","type":"test"})if call_result.content:print(f"📝 调用结果: {call_result.content[0].text}")except Exception as e:print(f"❌ 客户端连接失败: {e}")if __name__ =="__main__": asyncio.run(simple_client())

如何运行

1. 启动服务端

在项目根目录下,运行 main.py

python main.py 

你应该会看到 Uvicorn 启动服务的日志,表明服务正在 http://0.0.0.0:8000 上运行。

2. 运行客户端

打开另一个终端,运行 test/mcp_client.py

python test/mcp_client.py 

预期输出

如果一切顺利,你将在客户端的终端看到类似以下的输出:

✅ MCP 会话初始化成功 🛠️ 可用的工具: - get_data: 根据问题获取回复 📝 调用结果: 这是关于 '测试问题' 的答案。 

同时,在服务端的终端,你会看到 get_date 函数被触发时打印的信息:

收到问题: 测试问题, 类型: test 

结论

本文通过一个简单的示例,展示了如何使用 fastapi-mcpmcp 库在 Python 中构建 MCP 服务端和客户端。这种模式非常适合于需要将现有 Web API(特别是 FastAPI)快速封装成可远程调用的“工具集”的场景,为服务间通信提供了一种灵活且强大的替代方案。通过 operation_idtags 的智能映射,集成过程变得非常直观和高效。

Read more

Flutter 三方库 matrix 鸿蒙终端底层复杂超维数学算力适配突破:无缝植入极限级张量系统与密集线性代数矩阵运算推演算法,解锁端侧图形处理边界-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 matrix 鸿蒙终端底层复杂超维数学算力适配突破:无缝植入极限级张量系统与密集线性代数矩阵运算推演算法,解锁端侧图形处理边界-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 matrix 鸿蒙终端底层复杂超维数学算力适配突破:无缝植入极限级张量系统与密集线性代数矩阵运算推演算法,全面解锁端侧图形视觉处理边界并拔高数据分析算力上限 在图形学渲染、物理引擎模拟、复杂地理坐标转换以及端侧小型机器学习框架中,底层的矩阵运算(Matrix Operations)是决速步骤。matrix 库是一个专注于高性能线性代数计算的 Dart 库。本文将详解该库在 OpenHarmony 环境下的适配与实战应用。 封面 前言 什么是 matrix?它为 Dart 提供了一套类似于 NumPy 的多维数组运算接口。在鸿蒙操作系统这种强调极致流畅度和复杂视觉动效的系统中,利用高效的矩阵算法可以显著提升自定义 Canvas 绘图或实时传器数据处理的性能,避免因 Dart 层的低效循环导致的 UI 掉帧。 一、原理解析 1.1 基础概念 matrix 库核心基于

By Ne0inhk
Python连接和操作Elasticsearch详细指南

Python连接和操作Elasticsearch详细指南

Python连接和操作Elasticsearch详细指南 * 一、服务器端配置 * 1. 修改 Elasticsearch 配置文件 * 2. 开放防火墙端口 * 二、本地 Python 连接 Elasticsearch * 1. 连接 Elasticsearch * 2. 索引操作 * 3. 文档操作 * 4. 搜索内容 * 5. 聚合查询 * 6. 批量操作 * 三、注意事项 * 四、故障排除 * 结论 Elasticsearch 是一个强大的搜索引擎,广泛应用于数据存储和搜索场景。通过 Python,我们可以方便地与 Elasticsearch 进行交互。本文将详细介绍如何在本地使用 Python 连接到服务器上的 Elasticsearch,并进行基本的操作。 一、服务器端配置 在开始之前,确保你的 Elasticsearch

By Ne0inhk

如何用Qwen2.5做代码生成?0.5B模型Python调用详细步骤

如何用Qwen2.5做代码生成?0.5B模型Python调用详细步骤 1. 为什么选择Qwen2.5-0.5B做代码生成? 你是不是也遇到过这样的场景:写个脚本卡在某个函数上,查文档太慢,搜答案又一堆不相关的?或者只是想快速生成一段处理CSV的代码,但懒得从头敲?这时候,一个轻量、快速、能理解中文指令的AI助手就特别实用。 Qwen2.5-0.5B-Instruct 正是为此而生。它是通义千问Qwen2.5系列中最小的成员,只有约5亿参数,模型文件不到1GB,但它可不是“缩水版”。经过专门的指令微调,它对中文语境下的任务理解非常到位,尤其是像“写个Python函数来读取Excel并去重”这种具体需求,回答得很接地气。 最关键的是——它不需要GPU。你在一台普通的云服务器、甚至本地笔记本的CPU上就能跑起来,响应速度还很快。不像动辄几十GB显存的大模型,这个小家伙特别适合集成到工具链里,做自动化代码辅助。 我最近就在一个数据清洗项目里用它当“编程搭子”,每次要写重复逻辑时就丢一句“帮我写个函数,输入是字典列表,按某个字段去重”,几秒钟就出结果,改改变量名就能用,

By Ne0inhk
python核心语法(四)- 函数

python核心语法(四)- 函数

一.介绍 1.1 定义 函数是可重复调用的代码块,用来实现特定功能。 1.2 语法 def 函数名(参数1, 参数2, ...):        """函数文档字符串(可选,说明函数功能)"""        # 函数体:实现功能的代码        执行语句        return 返回值 # 可选,没有则默认返回None 1.3 注意事项 1.参数相关 默认参数:禁用列表 / 字典(可变对象),改用 None(内部初始化:lst = [] if lst is None else lst) 参数顺序:

By Ne0inhk