LangChain + MCP 实战小项目:AI 自动写情书并发邮件
本文介绍了一个基于MCP协议的AI情书自动生成与发送项目。项目通过MCP协议连接Server端和Client端,Server端提供"写情书"和"发邮件"两个工具,Client端使用ReAct风格的Agent自主决策工具调用顺序。用户只需输入自然语言指令,Agent就能自动完成情书撰写和邮件发送的全流程。文章详细讲解了项目架构、环境配置、Server端工具实现
前言
学完 MCP 协议的基础理论后,来动手做一个有趣的实战小项目——让 AI 自动帮你写情书,还能直接通过邮件发出去。整个流程只需一句自然语言指令,Agent 会自主完成"写情书 → 发邮件"的全部步骤。
MCP协议基础理论:https://blog.csdn.net/weixin_69500620/article/details/161138379?spm=1001.2014.3001.5501
本文重点在于项目实现过程和关键代码讲解,MCP 协议的基础概念不再赘述。
目录
- 一、项目效果预览
- 二、项目架构
- 三、环境准备
- 四、MCP Server 端实现
- 4.1 LLM 初始化
- 4.2 write_love_letter 工具
- 4.3 send_message 工具
- 4.4 启动 Server
- 五、MCP Client 端实现
- 5.1 连接 MCP Server
- 5.2 创建 ReAct Agent
- 5.3 交互式主循环
- 六、运行演示
- 七、踩坑记录
- 八、总结与扩展
一、项目效果预览
用户只需输入一句话:
帮我给小美写一封情书,发送到 xiaomei@example.com
Agent 自动完成两步操作:
Step1 → 调用 write_love_letter("小美") → 生成情书
Step2 → 调用 send_message("xiaomei@example.com", "给小美的一封信", 情书内容) → 邮件发出
全程无需人工干预,Agent 自主决策调用顺序和参数。
二、项目架构
┌─────────────────────┐ HTTP ┌─────────────────────┐
│ MCP Client 端 │ ────────────────────► │ MCP Server 端 │
│ │ MCP 协议 │ │
│ LangChain Agent │ │ write_love_letter()│──► LLM 生成情书
│ (ReAct 推理) │ ◄──────────────────── │ send_message() │──► SMTP 发邮件
│ │ 工具调用结果 │ │
└─────────────────────┘ └─────────────────────┘
核心分工:
| 端 | 职责 |
|---|---|
| Server 端 | 注册两个 MCP 工具,分别负责"调 LLM 写情书"和"调 SMTP 发邮件" |
| Client 端 | 连接 Server,拿到工具列表,交给 ReAct Agent 自主推理调用 |
三、环境准备
Python 版本: >= 3.10
安装依赖:
pip install mcp langchain langchain-openai langchain-mcp-adapters python-dotenv
.env 文件配置(LLM 接口信息):
MODEL_API_KEY=sk-xxxxxxxxxxxxxxxx
MODEL_BASE_URL=https://api.deepseek.com/v1
MODEL_NAME=deepseek-chat
DeepSeek、Qwen、GLM 等国产模型都兼容 OpenAI 接口格式,改一下 URL 和模型名即可切换。
config.py(加载环境变量):
import os
from dotenv import load_dotenv
def load_project_env():
env_path = os.path.join(os.path.dirname(__file__), '.env')
load_dotenv(env_path)
四、MCP Server 端实现
4.1 LLM 初始化
from mcp.server.fastmcp import FastMCP
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
import os, smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from config import load_project_env
load_project_env()
mcp = FastMCP("Demo")
my_llm = ChatOpenAI(
api_key=os.getenv("MODEL_API_KEY"),
base_url=os.getenv("MODEL_BASE_URL"),
model=os.getenv("MODEL_NAME"),
)
这个 my_llm 在 Server 端使用,专门用于 write_love_letter 工具调用 LLM 生成情书内容。注意它不参与 Agent 推理,Agent 推理用的是 Client 端自己的 LLM 实例。
4.2 write_love_letter 工具
@mcp.tool()
def write_love_letter(name: str) -> str:
"""
写情书的工具,调用工具可以写情书
:param name: 对方的名字
:return: 情书内容
"""
messages = [
SystemMessage(content=(
"你是一位深情的中文情书作家,擅长用优美典雅的中文表达真挚的情感。"
"请遵循以下创作原则:"
"1. 使用纯中文写作,避免使用任何英文单词或短语"
"2. 情感真挚自然,避免陈词滥调"
"3. 融入诗意的比喻和生动的意象"
"4. 根据收信人名字个性化内容"
"5. 保持适当的长度(约150-200字)"
"6. 结尾要有合适的落款格式"
"7. 语气温柔深情,含蓄而动人"
"8. 结构完整:开头问候、主体情感表达、结尾承诺"
)),
HumanMessage(content=f"写一封情书给{name},内容是:我爱你,你是我心中的唯一。")
]
response = my_llm.invoke(messages).content.strip()
return response
要点:
@mcp.tool()会自动从函数签名和 docstring 中提取工具名、参数描述、返回值描述,生成标准的 MCP 工具 Schema- System Prompt 里的 8 条规则是 Prompt Engineering 的核心,约束越明确,输出越可控
- 返回纯文本字符串,MCP 协议会自动封装成标准响应
4.3 send_message 工具
@mcp.tool()
def send_message(receiver_email: str, subject: str, message: str):
"""
发送邮件的工具,接收者邮箱,邮件主题,邮件内容
:param receiver_email: 邮件接收者邮箱
:param subject: 邮件主题
:param message: 邮件内容
"""
sender_email = "" # 填你的QQ邮箱
sender_password = "" # 填QQ邮箱SMTP授权码(不是QQ密码)
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = subject
msg.attach(MIMEText(message, 'plain'))
try:
server = smtplib.SMTP_SSL("smtp.qq.com", 465)
server.login(sender_email, sender_password)
server.sendmail(sender_email, receiver_email, msg.as_string())
print("邮件发送成功!")
except Exception as e:
print(f"邮件发送失败: {e}")
要点:
- 三个参数(收件人、主题、正文)都会由 Agent 在推理过程中自动填入
- QQ 邮箱 SMTP 授权码需要在 QQ邮箱 → 设置 → 账户 → 开启SMTP服务 中获取
- 生产环境中邮箱账号建议也用环境变量管理,不要硬编码
4.4 启动 Server
if __name__ == "__main__":
mcp.run("streamable-http") # 在 http://localhost:8000/mcp 上监听
MCP 支持三种传输方式:
| 方式 | 说明 |
|---|---|
stdio |
标准输入输出,适合本地工具 |
sse |
Server-Sent Events(旧版,逐步废弃) |
streamable-http |
推荐,基于 HTTP 的流式通信 |
五、MCP Client 端实现
5.1 连接 MCP Server
from langchain_mcp_adapters.client import MultiServerMCPClient
client = MultiServerMCPClient({
"demo": {
"url": "http://localhost:8000/mcp",
"transport": "streamable_http",
}
})
tools = await client.get_tools()
# tools 中会包含 write_love_letter 和 send_message 两个工具
langchain-mcp-adapters 负责将 MCP 工具自动转换为 LangChain 可用的 StructuredTool,Agent 无需做任何适配。
5.2 创建 ReAct Agent
from langchain.agents import create_agent
agent = create_agent(
my_llm,
tools,
system_prompt="你是一个 ReAct 风格的智能助手。遇到需要外部信息的问题,请先调用工具,再根据工具结果回答用户。",
debug=True, # 开启后可看到 Agent 每步推理过程
)
ReAct 推理过程示意:
用户: "帮小美写情书并发到 xxx@qq.com"
Thought: 需要先写情书,再发邮件
Action: write_love_letter({"name": "小美"})
Obs: "亲爱的小美,见字如面..."
Thought: 情书写好了,现在发邮件
Action: send_message({"receiver_email": "xxx@qq.com", ...})
Obs: 邮件发送成功
Answer: 情书已写好并发给小美了!
5.3 交互式主循环
import asyncio
from langchain_core.messages import HumanMessage
async def run_react_demo(user_input: str) -> str:
agent = await get_agent()
state = await agent.ainvoke({"messages": [HumanMessage(content=user_input)]})
messages = state.get("messages", [])
return messages[-1].content if messages else "未获得结果"
def main():
print("请开始对话,输入 exit 退出。")
while True:
user_input = input().strip()
if user_input == "exit":
break
if user_input:
print(asyncio.run(run_react_demo(user_input)))
if __name__ == "__main__":
main()
注意必须用
ainvoke(异步调用),因为 MCP 工具本身是异步的。用同步的invoke会报错StructuredTool does not support sync invocation。
六、运行演示
第一步:启动 Server(终端 1)
python mcp_server.py
第二步:启动 Client(终端 2)
python mcp_client.py
输出:
可用工具: ['write_love_letter', 'send_message']
请开始对话,输入 exit 退出。
第三步:输入指令
> 帮我给小美写一封情书,发送到 xiaomei@example.com
控制台会打印 Agent 的推理步骤(debug 模式),最终输出:
情书已写好并通过邮件发送给小美了,请查收!
七、踩坑记录
| 坑 | 现象 | 解决 |
|---|---|---|
| 同步调用报错 | StructuredTool does not support sync invocation |
Client 端必须用 await agent.ainvoke() |
| content 格式不兼容 | 部分模型 API 要求 content 为字符串,但 LangChain 发送的是 list | 升级 langchain-openai 到最新版 |
| Server 连不上 | Client 报 Connection refused |
确保先启动 Server,且端口 8000 没被占用 |
| 邮件发不出去 | SMTPAuthenticationError |
用 QQ 邮箱的 SMTP 授权码,不是 QQ 登录密码 |
八、总结与扩展
这个小项目虽然只有两个工具,但完整走通了 MCP 的核心链路:
定义工具 → 暴露服务 → 客户端发现 → Agent 自主编排调用
基于这个骨架,你可以很轻松地扩展更多工具:
@mcp.tool()
def query_database(sql: str) -> str:
"""执行SQL查询"""
...
@mcp.tool()
def search_web(query: str) -> str:
"""搜索互联网"""
...
@mcp.tool()
def generate_image(prompt: str) -> str:
"""AI生成图片"""
...
加工具只需要写一个函数、加一个装饰器,Agent 就能自动发现并使用,这就是 MCP 协议"即插即用"的魅力。
更多推荐



所有评论(0)