前言

学完 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 协议"即插即用"的魅力。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐