1. 项目概述
在人工智能和深度学习领域,大模型的应用越来越广泛,但随之而来的是一个棘手的工程问题:如何高效地连接大模型与各种外部工具和数据源?这就像在USB接口出现之前,每个设备都需要自己的专用连接器一样麻烦。
MCP(Model Context Protocol)协议应运而生,它就像是为大模型世界打造的"标准USB接口"。这个协议的核心价值在于解决了N个大模型对接M个数据源时产生的N×M复杂性问题。通过统一接口标准,开发者只需编写一次工具服务,就能让所有支持MCP的客户端调用。
1.1 核心架构解析
MCP协议采用三层架构设计:
-
客户端(MCP Host):内置大模型的调用方,如OpenCode、ClaudeCode等开发工具。它们负责发起调用请求并展示结果。
-
服务端(MCP Server):工具和数据的提供方,通常是我们需要开发的部分。它将业务功能封装成标准接口供客户端调用。
-
传输层(Transport):负责客户端和服务端之间的通信。本地开发常用STDIO(标准输入输出),云端服务则使用Streamable HTTP。
这种架构的最大优势是解耦。开发者不再需要为每个大模型平台单独适配工具接口,只需按照MCP标准实现一次,就能在所有兼容平台上使用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 实战准备
2.1 环境配置
在开始开发MCP服务前,我们需要准备好开发环境。推荐使用uv作为Python包管理工具,它比传统pip更高效:
bash复制pip install uv
uv init
uv add mcp requests feedparser python-dotenv
这里安装的关键依赖包括:
mcp:MCP协议的核心库requests:用于HTTP请求feedparser:解析RSS订阅python-dotenv:管理环境变量
2.2 安全配置
敏感信息如API密钥应该通过环境变量管理,避免硬编码在代码中。创建一个.env文件存储这些信息:
env复制TARGET_EMAIL="your_email@example.com"
EMAIL_API_KEY="your_resend_api_key"
WECHAT_API_KEY="your_wechat_api_key"
重要提示:.env文件必须添加到.gitignore中,避免将敏感信息提交到版本控制系统。
3. MCP服务实现
3.1 基础服务框架
我们使用FastMCP来快速构建服务,它简化了底层通信细节。基本框架如下:
python复制from mcp.server.fastmcp import FastMCP
from dotenv import load_dotenv
import os
# 初始化MCP服务器
load_dotenv()
mcp = FastMCP("Blog_Monitor_Notifier")
关键点说明:
load_dotenv()加载.env文件中的环境变量FastMCP初始化时传入服务名称,用于客户端识别- 服务名称应该简明扼要地描述功能
3.2 工具函数实现
我们将实现三个核心功能:获取最新博客、发送邮件通知和发送微信通知。
3.2.1 获取最新博客
python复制import feedparser
@mcp.tool()
def get_latest_blog_post(rss_url: str) -> str:
"""
获取指定RSS源的最新博客文章
参数:
rss_url: RSS订阅地址
返回:
最新文章的标题和链接,格式为"Title: xxx\nLink: xxx"
"""
try:
feed = feedparser.parse(rss_url)
if feed.entries:
latest_entry = feed.entries[0]
return f"Title: {latest_entry.title}\nLink: {latest_entry.link}"
return f"在RSS源{rss_url}中未找到任何文章。"
except Exception as e:
return f"获取博客失败: {str(e)}"
注意事项:
- 使用
@mcp.tool()装饰器将函数注册为MCP工具 - 类型注解必须明确(参数和返回值)
- docstring要详细,它会被发送给大模型用于理解工具用途
3.2.2 发送邮件通知
python复制import requests
@mcp.tool()
def send_email_notification(post_title: str, post_link: str) -> str:
"""
发送邮件通知
参数:
post_title: 博客标题
post_link: 博客链接
返回:
发送结果描述
"""
target_email = os.environ.get("TARGET_EMAIL")
email_api_key = os.environ.get("EMAIL_API_KEY")
if not target_email or not email_api_key:
return "邮件发送失败:未配置TARGET_EMAIL或EMAIL_API_KEY环境变量。"
headers = {
"Authorization": f"Bearer {email_api_key}",
"Content-Type": "application/json"
}
payload = {
"from": "onboarding@resend.dev",
"to": target_email,
"subject": f"博客更新通知:{post_title}",
"text": f"新博客发布\n标题:{post_title}\n链接: {post_link}"
}
try:
response = requests.post(
"https://api.resend.com/emails",
headers=headers,
json=payload
)
if response.status_code == 200:
return "邮件通知发送成功"
return f"邮件发送失败: {response.text}"
except Exception as e:
return f"发送邮件时发生异常: {str(e)}"
关键点:
- API密钥从环境变量获取,避免硬编码
- 使用requests库发送HTTP请求
- 完善的错误处理,返回有意义的错误信息
3.2.3 发送微信通知
python复制@mcp.tool()
def send_wechat_notification(post_title: str, post_link: str) -> str:
"""
发送微信通知
参数:
post_title: 博客标题
post_link: 博客链接
返回:
发送结果描述
"""
wechat_api_key = os.environ.get("WECHAT_API_KEY")
if not wechat_api_key:
return "微信通知发送失败:未配置WECHAT_API_KEY环境变量。"
url = f"https://sctapi.ftqq.com/{wechat_api_key}.send"
data = {
"title": f"博客更新:{post_title}",
"desp": f"新博客发布\n标题:{post_title}\n链接: {post_link}"
}
try:
response = requests.post(url, data=data)
if response.status_code != 200:
return f"微信消息发送失败: {response.text}"
result = response.json()
if result.get("code") != 0:
return f"API拒绝请求: {result.get('message')}"
return "微信通知发送成功"
except Exception as e:
return f"发送微信通知时发生异常: {str(e)}"
3.3 服务启动
最后添加启动代码:
python复制if __name__ == "__main__":
mcp.run()
运行服务:
bash复制uv run mcp_server.py
4. 客户端集成
4.1 OpenCode配置
在项目目录下创建opencode.json配置文件:
json复制{
"mcp": {
"blog_monitor": {
"type": "local",
"command": ["uv", "run", "mcp_server.py"],
"enabled": true
}
}
}
配置说明:
type: "local"表示本地服务command指定启动命令enabled: true启用该服务
4.2 服务测试
启动OpenCode后,按Shift+P输入"mcp",选择"Toggle MCPs"即可看到我们的服务。连接成功后,就可以通过自然语言指令让大模型调用我们的工具了。
例如输入:"检查我的博客是否有更新,如果有就通知我",大模型会自动调用get_latest_blog_post获取最新文章,然后调用通知工具发送提醒。
5. 高级技巧与问题排查
5.1 日志记录
在MCP服务中添加日志记录可以帮助调试:
python复制import logging
import sys
logger = logging.getLogger("mcp_server")
logger.setLevel(logging.INFO)
# 清除可能存在的默认handler
if logger.hasHandlers():
logger.handlers.clear()
# 文件日志
file_handler = logging.FileHandler('mcp_server.log')
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(file_handler)
# 标准错误输出
stderr_handler = logging.StreamHandler(sys.stderr)
stderr_handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s'))
logger.addHandler(stderr_handler)
# 禁止传播到根logger
logger.propagate = False
5.2 常见问题解决
-
环境变量加载失败
- 确保
.env文件路径正确 - 使用绝对路径:
load_dotenv(os.path.join(os.path.dirname(__file__), '.env')) - 添加
override=True参数覆盖系统环境变量
- 确保
-
服务启动失败
- 检查uv是否安装正确
- 确保所有依赖已安装
- 查看日志文件获取详细错误信息
-
客户端连接问题
- 确认服务已启动
- 检查OpenCode配置中的命令路径
- 尝试使用绝对路径
6. 协议优势与应用场景
6.1 技术优势
- 标准化接口:统一了不同大模型平台的工具调用方式
- 开发效率:一次开发,多平台使用
- 安全性:本地服务使用STDIO通信,避免网络暴露
- 灵活性:支持动态工具注册和资源管理
6.2 典型应用场景
- 数据查询:将数据库、API等数据源封装为MCP服务
- 自动化任务:如文件处理、备份等重复性工作
- 通知提醒:集成多种通知渠道
- 知识管理:连接本地文档和笔记系统
7. 扩展与进阶
7.1 动态工具注册
对于需要根据运行时状态动态提供工具的场景,可以使用底层Server API:
python复制from mcp.server import Server
server = Server("Dynamic_Tools")
async def handle_list_tools(request):
# 根据当前状态动态生成工具列表
tools = generate_tools_based_on_state()
return {"tools": tools}
server.on_list_tools(handle_list_tools)
7.2 资源模板
对于大量相似资源,可以使用模板避免重复注册:
python复制@mcp.resource("note://{note_id}")
def get_note(note_id: str):
# 根据note_id获取对应笔记内容
return fetch_note_content(note_id)
客户端可以通过"note://daily-report"这样的URI访问特定资源。
7.3 性能优化技巧
- 异步处理:对于耗时操作,使用async/await避免阻塞
- 缓存机制:对频繁访问的数据添加缓存
- 连接池:数据库等资源使用连接池管理
- 批量处理:支持批量操作减少调用次数
8. 安全最佳实践
- 输入验证:对所有输入参数进行严格验证
- 权限控制:实现细粒度的访问控制
- 敏感数据处理:日志中过滤敏感信息
- 错误处理:避免泄露系统内部细节
- 定期更新:保持依赖库版本最新
9. 项目结构建议
规范的目录结构有助于长期维护:
code复制project/
├── src/
│ ├── services/
│ │ └── blog_monitor.py
│ └── utils/
├── tests/
├── .env
├── opencode.json
├── requirements.txt
└── README.md
10. 监控与维护
- 健康检查:实现/_health端点供监控系统调用
- 性能指标:收集响应时间、错误率等指标
- 日志分析:设置日志轮转和监控告警
- 文档更新:维护最新的API文档和使用说明
通过MCP协议,我们成功将博客监控工具标准化,使其能够被各种大模型平台调用。这种解耦架构不仅提高了开发效率,也为构建更复杂的大模型应用生态系统奠定了基础。随着MCP生态的成熟,开发者可以专注于业务逻辑实现,而不必担心平台适配问题。
