跳到主要内容
Python MCP 工具开发入门:Server、Client 与 LLM 集成 | 极客日志
Python AI 算法
Python MCP 工具开发入门:Server、Client 与 LLM 集成 介绍如何使用 Python 构建 MCP(Model Context Protocol)服务器与客户端,实现与大语言模型(LLM)的集成。内容包括 MCP 核心概念、环境配置、代码示例(Server/Client)、工具与资源定义、以及多轮对话流程。通过 FastMCP 框架简化开发,支持自定义工具调用及资源读取,帮助开发者快速搭建 AI 应用。
漫步 发布于 2026/3/29 更新于 2026/6/3 33 浏览1. 从零开始:如何用 Python 创建你的第一个 MCP(Model Context Protocol)
1.1 什么是 MCP?
Model Context Protocol (MCP) 是一个标准化协议,允许应用程序与大语言模型(LLM)进行安全、结构化的交互。通过 MCP,你可以:
为 LLM 提供自定义工具和资源
实现 LLM 和外部系统的无缝集成
构建可复用的、模块化的 AI 应用
1.2 核心概念
1.2.1 MCP Server(服务器)
定义工具、资源和提示词,通过 stdio 或其他传输方式提供给客户端。
1.2.2 MCP Client(客户端)
连接到 MCP 服务器,获取工具列表,调用工具,并与 LLM 集成。
1.2.3 Tools(工具)
服务器暴露给 LLM 的可调用函数,LLM 可以根据用户需求调用这些工具。
1.3 项目结构
hello-world/
├── server.py # MCP 服务器定义
├── client-qwen.py # 使用 Qwen LLM 的客户端
├── client.py # 基础客户端
├── pyproject.toml # 项目配置
└── uv.lock # 依赖锁定文件
2. 第一步:创建 MCP 服务器
2.1 安装依赖
cd hello-world
uv sync
主要依赖:
mcp[cli]>=1.6.0 - MCP 框架
openai>=1.75.0 - OpenAI 兼容的 LLM 客户端
python-dotenv>=1.1.0 - 环境变量管理
2.2 编写 Server 端代码
创建 server.py:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Demo" )
@mcp.tool()
def add (a: int , b: int ) -> int :
"""Add two numbers"""
a + b
( ) -> :
__name__ == :
mcp.run( )
return
@mcp.resource("greeting://{name}" )
def
get_greeting
name: str
str
"""Get a personalized greeting"""
return
f"Hello, {name} !"
if
"__main__"
"stdio"
FastMCP - 简化的 MCP 服务器框架
@mcp.tool() - 装饰器定义工具函数
@mcp.resource() - 装饰器定义资源(带 URI 模式)
mcp.run('stdio') - 通过标准输入输出运行服务器
3. 第二步:创建 MCP 客户端
3.1 基础客户端(无 LLM) import sys
import asyncio
from mcp import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters
async def main ():
server_script = "server.py"
params = StdioServerParameters(
command=sys.executable,
args=[server_script],
)
transport = stdio_client(params)
stdio, write = await transport.__aenter__()
session = await ClientSession(stdio, write).__aenter__()
await session.initialize()
result = await session.call_tool("add" , {"a" : 3 , "b" : 5 })
print (f"3 + 5 = {result} " )
await session.__aexit__(None , None , None )
await transport.__aexit__(None , None , None )
if __name__ == "__main__" :
asyncio.run(main())
3.2 与 LLM 集成的客户端 client-qwen.py 展示了如何让 LLM 自动调用工具:
import sys
import asyncio
import os
import json
from mcp import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters
from openai import OpenAI
from dotenv import load_dotenv
load_dotenv()
async def main ():
print (">>> 初始化加法 LLM 工具客户端" )
server_script = "server.py"
params = StdioServerParameters(
command=sys.executable,
args=[server_script],
)
transport = stdio_client(params)
stdio, write = await transport.__aenter__()
session = await ClientSession(stdio, write).__aenter__()
await session.initialize()
print (">>> 连接到 MCP 服务器成功" )
client = OpenAI(
api_key=os.getenv("QWEN_API_KEY" ),
base_url=os.getenv("QWEN_BASE_URL" )
)
resp = await session.list_tools()
tools = [
{
"type" : "function" ,
"function" : {
"name" : tool.name,
"description" : tool.description,
"parameters" : tool.inputSchema
}
}
for tool in resp.tools
]
print ("可用工具:" , [t["function" ]["name" ] for t in tools])
while True :
print ("\n请输入你的加法问题(如:5 加 7 是多少?或'退出'):" )
user_input = input ("> " )
if user_input.strip().lower() == '退出' :
break
print (f"\n📝 用户问题:{user_input} " )
messages = [
{"role" : "system" , "content" : "你是一个加法助手,遇到加法问题请调用工具 add,最后用自然语言回答用户。" },
{"role" : "user" , "content" : user_input}
]
iteration = 0
while True :
iteration += 1
print (f"\n🔄 第 {iteration} 次 LLM 调用..." )
response = client.chat.completions.create(
model="qwen-plus" ,
messages=messages,
tools=tools,
tool_choice="auto"
)
message = response.choices[0 ].message
messages.append(message)
if not message.tool_calls:
print (f"\n✅ LLM 最终回答:" )
print (f"\nAI 回答:\n {message.content} " )
break
for tool_call in message.tool_calls:
args = json.loads(tool_call.function.arguments)
print (f"\n🔧 调用工具:{tool_call.function.name} " )
print (f"📥 工具参数:{args} " )
result = await session.call_tool(tool_call.function.name, args)
print (f"📤 工具返回结果:{result} " )
messages.append({"role" : "tool" , "content" : str (result), "tool_call_id" : tool_call.id })
await session.__aexit__(None , None , None )
await transport.__aexit__(None , None , None )
print (">>> 客户端已关闭" )
if __name__ == "__main__" :
asyncio.run(main())
4. 第三步:配置环境变量
QWEN_API_KEY =your-api-key-here
QWEN_BASE_URL =https://dashscope.aliyuncs.com/compatible-mode/v1
5. 第四步:运行程序
5.1 方式一:两个终端分别运行 cd hello-world
uv run python server.py
cd hello-world
QWEN_API_KEY="your-api-key"
QWEN_BASE_URL="https://dashscope.aliyuncs.com/compatible-mode/v1"
uv run python client-qwen.py
5.2 方式二:非交互式测试 cd hello-world
timeout 60 bash -c 'echo -e "15 加 8 等于多少?\n退出" | uv run python client-qwen.py'
6. 执行流程示例 📝 用户问题:15 加 8 等于多少?
🔄 第 1 次 LLM 调用...
🔧 调用工具:add
📥 工具参数:{'a ': 15 , 'b' : 8 }
📤 工具返回结果:meta=None content =[TextContent(type='text' , text='23' , annotations=None)] isError=False
🔄 第 2 次 LLM 调用...
✅ LLM 最终回答:
AI 回答:15 加 8 等于 23 。
7. 关键要点
7.1 异步编程 MCP 使用 asyncio,所有网络操作都是异步的:
async def main ():
await session.initialize()
result = await session.call_tool(...)
7.2 工具定义 @mcp.tool()
def my_tool (param1: int , param2: str ) -> str :
"""Tool description"""
return f"Result: {param1} {param2} "
7.3 工具调用链 用户输入 → LLM 分析 → 调用工具 → 获得结果 → LLM 再次分析 → 最终回答
7.4 消息历史 messages = [
{"role" : "system" , "content" : "..." },
{"role" : "user" , "content" : "..." },
{"role" : "assistant" , "content" : "..." },
{"role" : "tool" , "content" : "..." },
]
8. 扩展应用
8.1 添加更多工具 @mcp.tool()
def multiply (a: int , b: int ) -> int :
"""Multiply two numbers"""
return a * b
@mcp.tool()
def divide (a: float , b: float ) -> float :
"""Divide two numbers"""
if b == 0 :
raise ValueError("Cannot divide by zero" )
return a / b
8.2 添加资源 @mcp.resource("user://{user_id}" )
def get_user_info (user_id: str ) -> str :
"""Get user information"""
return f"User {user_id} information"
8.2.1 什么是资源(Resource)? @mcp.resource() 装饰器用于定义一个可以根据参数返回不同数据的接口 。资源是不同于工具的数据取…
特性 Resource(资源) Tool(工具) 用途 提供只读或结构化的数据 执行操作或计算 调用方式 read_resource("uri://path")call_tool("name", args)参数传递 URI 路径参数 函数参数 使用场景 获取文件、查询数据库 计算、修改数据
8.2.2 URI 模式详解 @mcp.resource("greeting://{name}" )
def get_greeting (name: str ) -> str :
"""Get a personalized greeting"""
return f"Hello, {name} !"
部分 含义 说明 @mcp.resource()资源装饰器 定义资源的标记 "greeting://"资源协议(Scheme) user://、file://、api://{name}动态参数占位符 类似路由参数,接收不同值 get_greeting(name: str)处理函数 参数名必须 与 URI 占位符一致
客户端请求 greeting:
↓ MCP 框架识别 URI 模式
↓ 提取参数 name = "Alice"
↓ 调用函数 get_greeting ("Alice" )
↓ 返回 "Hello, Alice!" 给客户端
8.2.3 常见资源示例 @mcp.resource("user://{user_id}" )
def get_user_info (user_id: str ):
users = {"1" : "Alice" , "2" : "Bob" }
return users.get(user_id, "Not found" )
@mcp.resource("file://{folder}/{filename}" )
def read_file (folder: str , filename: str ) -> str :
path = f"{folder} /{filename} "
try :
with open (path, 'r' ) as f:
return f.read()
except FileNotFoundError:
return "File not found"
@mcp.resource("docs://api/{version}" )
def get_api_docs (version: str ) -> str :
docs = {"v1" : "API v1: 支持基础功能" , "v2" : "API v2: 新增高级功能" }
return docs.get(version, "Version not found" )
@mcp.resource("product://{category}/{product_id}" )
def get_product (category: str , product_id: str ) -> str :
return f"Product {product_id} from {category} "
8.2.4 客户端中使用资源
result = await session.read_resource("greeting://Alice" )
print (result)
resources = await session.list_resources()
for resource in resources.resources:
print (f"Resource: {resource.uri} - {resource.description} " )
8.2.5 资源 URI 命名最佳实践 @mcp.resource("user://{user_id}" )
@mcp.resource("file://{path}/{filename}" )
@mcp.resource("docs://api/{version}" )
@mcp.resource("res://{id}" )
@mcp.resource("user://{user_id}" )
def get_user (uid: str ):
pass
8.3 自定义 System Prompt system_prompt = """
你是一个数学助手。
当用户问到加法、减法、乘法、除法时,请调用相应的工具。
最后用清晰的中文回答用户的问题。
"""
messages = [
{"role" : "system" , "content" : system_prompt},
{"role" : "user" , "content" : user_input}
]
9. 常见问题
9.1 Q1: 如何调试 MCP 服务器? import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
@mcp.tool()
def my_tool (x: int ):
logger.info(f"Tool called with x={x} " )
return x * 2
9.2 Q2: 如何处理工具调用错误? try :
result = await session.call_tool(tool_name, args)
except Exception as e:
print (f"Tool call failed: {e} " )
9.3 Q3: 如何支持多轮对话? 保持 messages 列表,每次交互都添加新消息:
while True :
user_input = input ("> " )
messages.append({"role" : "user" , "content" : user_input})
10. 性能优化建议
连接复用 - 不要频繁创建/销毁连接
超时设置 - 为 LLM 调用设置合理超时
错误重试 - 实现指数退避重试机制
日志级别 - 生产环境减少日志输出
11. 总结 通过 MCP,你可以轻松构建强大的 AI 应用,让 LLM 能够访问和使用自定义工具。核心步骤是:
定义工具(Server)
连接到服务器(Client)
让 LLM 自动调用工具
处理工具结果并返回给用户
12. 参考资源 相关免费在线工具 加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
RSA密钥对生成器 生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
Mermaid 预览与可视化编辑 基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
随机西班牙地址生成器 随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online
Gemini 图片去水印 基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online