MCP 通讯方式与实现指南

一、三种 MCP 通讯方式对比

MCP(Model Context Protocol)目前支持三种通讯方式,各有特点:

1. stdio(标准输入输出)

工作原理

  • 通过本地进程的 stdin/stdout 进行通信
  • 客户端以子进程方式启动 MCP 服务器
  • 双方通过管道交换 JSON-RPC 格式消息(换行符分割)

适用场景

  • 本地进程间通信(如命令行工具、文件系统操作)
  • 简单的批处理任务或工具调用

|----------|--------------------------------------------------------|
| 优点 | 限制 |
| 实现简单,低延迟 | 仅限本地,不支持分布式部署 |
| 无需网络配置 | 服务端不能输出控制台日志(会污染协议流) |
| 适合本地开发 | ------------------------------------------------------ |


2. SSE(Server-Sent Events)⚠️ 已弃用

工作原理

  • 基于 HTTP 长连接实现单向消息推送
  • 客户端通过 GET /sse 建立连接
  • 服务器通过 SSE 流发送 JSON-RPC 消息
  • 客户端通过 POST /message 发送请求

适用场景

  • 远程服务调用(如云服务、多客户端监控)
  • 需要实时数据推送的场景(如流式对话)

|--------------------------|-----------------------------------|
| 优点 | 限制 |
| 支持实时单向推送 | 2025年3月后已被 Streamable HTTP 取代 |
| 适合流式交互 | 连接中断后无法恢复 |
| ------------------------ | 需维持长连接,资源消耗较高 |


3. Streamable HTTP(流式 HTTP)✅ 官方推荐

工作原理(2025年3月引入)

  • 通过统一的 /message 端点实现双向通信
  • 客户端通过 HTTP POST 发送请求
  • 服务器可将响应升级为 SSE 流式传输(按需)
  • 支持无状态模式,无需维持长连接

核心优势

  • ✅ 支持连接恢复(无需重新开始)
  • ✅ 无需维持长连接,降低资源消耗
  • ✅ 统一端点设计(/message),简化接口
  • ✅ 兼容现有基础设施(负载均衡、中间件等)

适用场景

  • 高并发远程服务调用
  • 需要灵活流式响应的场景(如 AI 助手动态输出)

二、stdio 模式实现

架构流程图: 创建 Server → 启动服务 → Client 连接 → 加载 Tools → Agent 调用

Step 1:创建 MCP Server

复制代码
from mcp.server.fastmcp import FastMCP

mcp = FastMCP('Math Tools')

@mcp.tool()
def add(a: int, b: int) -> int:
    return a + b

@mcp.tool()
def multiply(a: int, b: int) -> int:
    return a * b

if __name__ == '__main__':
    mcp.run(transport='stdio')   # ← 关键:stdio 模式

💡 说明: 使用 @mcp.tool() 注册工具方法,run(transport='stdio') 启动服务。

Step 2:启动 MCP Server

复制代码
python server.py

💡 服务启动后会持续监听 stdin/stdout 的读写事件。

Step 3:开发 MCP Client(含 Agent)

3.1 定义 Server 参数
复制代码
server_params = StdioServerParameters(
    command='python',
    args=['path/to/mcp_stdio_server.py']
)
3.2 加载 MCP Tools
复制代码
async with stdio_client(server_params) as (read, write):
    async with ClientSession(read, write) as session:
        await session.initialize()
        tools = await load_mcp_tools(session)
        print(f"成功加载 {len(tools)} 个工具")
3.3 创建 Agent 并调用
复制代码
agent = initialize_agent(
    tools=tools,
    llm=llm,
    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True,
)
resp = await agent.ainvoke("14+17*5=?")

完整示例代码

复制代码
import os
import asyncio
from dotenv import load_dotenv
from mcp import StdioServerParameters, ClientSession
from mcp.client.stdio import stdio_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain.agents import initialize_agent, AgentType
from langchain_openai import ChatOpenAI

load_dotenv()

llm = ChatOpenAI(
    model="qwen-plus",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://ws-kcaoxz5olbi6r3qa.cn-beijing.maas.aliyuncs.com/compatible-mode/v1",
    streaming=True,
    temperature=0.7,
)

async def create_mcp_stdio_client():
    server_params = StdioServerParameters(
        command='python',
        args=['D:/sd14/ai-agent/app/mcp_/stdio/mcp_stdio_server.py']
    )

    try:
        async with stdio_client(server_params) as (read, write):
            async with ClientSession(read, write) as session:
                await session.initialize()
                tools = await load_mcp_tools(session)
                print(f"成功加载 {len(tools)} 个工具")

                agent = initialize_agent(
                    tools=tools,
                    llm=llm,
                    agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
                    verbose=True,
                )

                resp = await agent.ainvoke("14+17*5=?")
                print(f"\n回答: {resp}")
                return resp
    except Exception as e:
        print(f"连接失败: {e}")
        traceback.print_exc()

if __name__ == '__main__':
    asyncio.run(create_mcp_stdio_client())

三、SSE 模式实现(已弃用,仅供参考)

⚠️ 官方已废弃,建议直接使用 Streamable HTTP

Server 端差异

复制代码
if __name__ == '__main__':
    mcp.run(transport='sse')  # ← 仅 transport 参数不同

Client 端差异

复制代码
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient({
    "math": {
        "url": "http://127.0.0.1:8000/sse",
        "transport": "sse",      # ← 指定传输方式
    }
})
tools = await client.get_tools()

SSE 模式完整代码

SSE Server 端 (mcp_sse_server.py)
复制代码
from mcp.server.fastmcp import FastMCP

# 创建 MCP 服务器实例
mcp = FastMCP('Math Tools - SSE')

# ============ 注册工具方法 ============

@mcp.tool()
def add(a: int, b: int) -> int:
    """
    计算两个整数的和
    """
    return a + b

@mcp.tool()
def multiply(a: int, b: int) -> int:
    """
    计算两个整数的乘积
    """
    return a * b

@mcp.tool()
def subtract(a: int, b: int) -> int:
    """
    计算两个整数的差 (a - b)
    """
    return a - b

@mcp.tool()
def divide(a: int, b: int) -> float:
    """
    计算两个整数的商 (a / b)
    """
    if b == 0:
        raise ValueError("除数不能为0")
    return a / b

@mcp.tool()
def power(base: int, exponent: int) -> int:
    """
    计算幂运算 (base ^ exponent)
    """
    return base ** exponent

# ============ 启动服务 ============
if __name__ == '__main__':
    mcp.run(transport='sse')
SSE Client 端 (mcp_sse_client.py)
复制代码
import os
import asyncio
from dotenv import load_dotenv
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import initialize_agent, AgentType
from langchain_openai import ChatOpenAI

# ============ 加载环境变量 ============
load_dotenv()

# ============ 初始化 LLM ============
llm = ChatOpenAI(
    model="qwen-plus",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://ws-kcaoxz5olbi6r3qa.cn-beijing.maas.aliyuncs.com/compatible-mode/v1",
    streaming=True,
    temperature=0.7,
)

# ============ 创建 SSE 客户端 ============
async def create_mcp_sse_client():
    try:
        # 创建多服务器客户端
        client = MultiServerMCPClient(
            {
                "math": {
                    "url": "http://127.0.0.1:8000/sse",  # SSE 端点
                    "transport": "sse",                   # 指定传输方式
                }
            }
        )

        # 获取所有工具
        tools = await client.get_tools()
        print(f"✅ 成功加载 {len(tools)} 个工具:")
        for tool in tools:
            print(f"  - {tool.name}: {tool.description}")

        # ============ 创建 Agent ============
        agent = initialize_agent(
            tools=tools,
            llm=llm,
            agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
            verbose=True,
            handle_parsing_errors=True,
        )

        # ============ 测试调用 ============
        print("\n" + "="*50)
        print("🧪 测试 1: 基础运算")
        print("="*50)
        resp1 = await agent.ainvoke("请计算 14 + 17 * 5 = ?")
        print(f"\n📝 回答: {resp1['output']}\n")

        print("="*50)
        print("🧪 测试 2: 复杂表达式")
        print("="*50)
        resp2 = await agent.ainvoke("计算 (100 - 25) * 4 / 3 = ?")
        print(f"\n📝 回答: {resp2['output']}\n")

        print("="*50)
        print("🧪 测试 3: 幂运算")
        print("="*50)
        resp3 = await agent.ainvoke("计算 2 的 10 次方等于多少?")
        print(f"\n📝 回答: {resp3['output']}\n")

        return resp1

    except Exception as e:
        print(f"❌ 连接失败: {e}")
        import traceback
        traceback.print_exc()
        return None

# ============ 入口 ============
if __name__ == '__main__':
    print("🚀 启动 SSE MCP 客户端...")
    print("📌 请确保 SSE 服务器已启动: python mcp_sse_server.py")
    print()
    asyncio.run(create_mcp_sse_client())
SSE 模式启动步骤
复制代码
# 终端1: 启动 SSE Server
python mcp_sse_server.py

# 终端2: 启动 SSE Client
python mcp_sse_client.py

四、Streamable HTTP 模式实现 ✅ 推荐

Server 端

复制代码
if __name__ == '__main__':
    mcp.run(transport="streamable-http")

Client 端

复制代码
client = MultiServerMCPClient({
    "math": {
        "url": "http://127.0.0.1:8000/mcp",
        "transport": "streamable_http",   # ← 注意:下划线
    }
})
tools = await client.get_tools()

Streamable HTTP 完整代码

Streamable HTTP Server 端 (mcp_streamable_server.py)
复制代码
from mcp.server.fastmcp import FastMCP

# 创建 MCP 服务器实例
mcp = FastMCP('Math Tools - Streamable HTTP')

# ============ 注册工具方法 ============

@mcp.tool()
def add(a: int, b: int) -> int:
    """
    计算两个整数的和
    """
    return a + b

@mcp.tool()
def multiply(a: int, b: int) -> int:
    """
    计算两个整数的乘积
    """
    return a * b

@mcp.tool()
def subtract(a: int, b: int) -> int:
    """
    计算两个整数的差 (a - b)
    """
    return a - b

@mcp.tool()
def divide(a: int, b: int) -> float:
    """
    计算两个整数的商 (a / b)
    """
    if b == 0:
        raise ValueError("除数不能为0")
    return a / b

@mcp.tool()
def power(base: int, exponent: int) -> int:
    """
    计算幂运算 (base ^ exponent)
    """
    return base ** exponent

@mcp.tool()
def factorial(n: int) -> int:
    """
    计算阶乘 (n!)
    """
    if n < 0:
        raise ValueError("阶乘只支持非负整数")
    if n == 0 or n == 1:
        return 1
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result

@mcp.tool()
def fibonacci(n: int) -> list:
    """
    生成前 n 个斐波那契数列
    """
    if n <= 0:
        return []
    if n == 1:
        return [0]
    fib = [0, 1]
    for i in range(2, n):
        fib.append(fib[i-1] + fib[i-2])
    return fib

# ============ 启动服务 ============
if __name__ == '__main__':
    mcp.run(transport='streamable-http')
Streamable HTTP Client 端 (mcp_streamable_client.py)
复制代码
import os
import asyncio
from dotenv import load_dotenv
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain.agents import initialize_agent, AgentType
from langchain_openai import ChatOpenAI
from langchain.callbacks import StreamingStdOutCallbackHandler

# ============ 加载环境变量 ============
load_dotenv()

# ============ 初始化 LLM ============
llm = ChatOpenAI(
    model="qwen-plus",
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://ws-kcaoxz5olbi6r3qa.cn-beijing.maas.aliyuncs.com/compatible-mode/v1",
    streaming=True,
    temperature=0.7,
    callbacks=[StreamingStdOutCallbackHandler()],
)


# ============ 创建 Streamable HTTP 客户端 ============
async def create_mcp_streamable_client():
    try:
        # 创建多服务器客户端
        client = MultiServerMCPClient(
            {
                "math": {
                    "url": "http://127.0.0.1:8000/mcp",  # Streamable HTTP 端点
                    "transport": "streamable_http",  # 注意:下划线
                }
            }
        )

        # 获取所有工具
        tools = await client.get_tools()
        print(f"\n✅ 成功加载 {len(tools)} 个工具:")
        for tool in tools:
            print(f"  - {tool.name}: {tool.description}")

        # ============ 创建 Agent ============
        agent = initialize_agent(
            tools=tools,
            llm=llm,
            agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
            verbose=True,
            handle_parsing_errors=True,
            max_iterations=5,
        )

        # ============ 测试调用 ============
        test_cases = [
            "请计算 14 + 17 * 5 = ?",
            "计算 (100 - 25) * 4 / 3 = ?",
            "2 的 10 次方等于多少?",
            "计算 5!(5的阶乘)等于多少?",
            "生成前 10 个斐波那契数列",
        ]

        for i, query in enumerate(test_cases, 1):
            print("\n" + "=" * 60)
            print(f"🧪 测试 {i}: {query}")
            print("=" * 60)

            try:
                resp = await agent.ainvoke(query)
                print(f"\n📝 最终回答:\n{resp['output']}\n")
            except Exception as e:
                print(f"❌ 调用失败: {e}")

        return True

    except Exception as e:
        print(f"❌ 连接失败: {e}")
        import traceback
        traceback.print_exc()
        return False


# ============ 高级用法:上下文管理 ============
async def advanced_usage():
    """
    展示如何优雅地管理客户端生命周期
    """
    async with MultiServerMCPClient(
            {
                "math": {
                    "url": "http://127.0.0.1:8000/mcp",
                    "transport": "streamable_http",
                }
            }
    ) as client:
        tools = await client.get_tools()
        print(f"✅ 加载了 {len(tools)} 个工具")

        # 创建 Agent
        agent = initialize_agent(
            tools=tools,
            llm=llm,
            agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
            verbose=True,
        )

        # 多轮对话
        queries = [
            "计算 3 + 4 * 5",
            "计算 10!",
            "生成前 5 个斐波那契数列",
        ]

        for query in queries:
            print(f"\n💬 用户: {query}")
            resp = await agent.ainvoke(query)
            print(f"🤖 AI: {resp['output']}")


# ============ 入口 ============
if __name__ == '__main__':
    print("🚀 启动 Streamable HTTP MCP 客户端...")
    print("📌 请确保 Streamable HTTP 服务器已启动: python mcp_streamable_server.py")
    print("📌 访问地址: http://127.0.0.1:8000/mcp")
    print()

    # 基础用法
    asyncio.run(create_mcp_streamable_client())

    # 高级用法(取消注释使用)
    # asyncio.run(advanced_usage())
Streamable HTTP 模式启动步骤
复制代码
# 终端1: 启动 Streamable HTTP Server
python mcp_streamable_server.py

# 终端2: 启动 Streamable HTTP Client
python mcp_streamable_client.py

快速对比总览

|----------|-------|----------|-------------------|
| 特性 | stdio | SSE(已弃用) | Streamable HTTP ✅ |
| 适用场景 | 本地开发 | 远程调用 | 生产环境/云服务 |
| 网络需求 | 无需网络 | 需要 HTTP | 需要 HTTP |
| 连接状态 | 进程级 | 需维持长连接 | 支持无状态 |
| 连接恢复 | N/A | ❌ 不支持 | ✅ 支持 |
| 资源消耗 | 低 | 高 | 低 |
| 官方推荐 | 本地开发 | ❌ 已淘汰 | ✅ 强烈推荐 |


五、补充:如何选择合适的传输方式?

复制代码
┌─────────────────────────────────────────────────────┐
│  你的需求是什么?                                   │
├─────────────────────────────────────────────────────┤
│  • 本地开发/测试              → 使用 stdio          │
│  • 远程服务/生产部署          → 使用 Streamable HTTP│
│  • 需要流式响应               → 使用 Streamable HTTP│
│  • 高并发场景                 → 使用 Streamable HTTP│
│  • 旧项目维护(不推荐新项目)→ SSE(但建议迁移)   │
└─────────────────────────────────────────────────────┘

六、踩坑提醒 💡

|------------------------------|---------------------------------------|
| 问题 | 解决方案 |
| stdio 模式 服务端不能 print() | 使用 logging 输出到文件 |
| SSE 连接中断 | 迁移到 Streamable HTTP |
| Streamable HTTP 端口被占用 | 更换端口或检查服务是否已启动 |
| 工具加载失败 | 检查 server 端的 transport 参数是否匹配 |
| 环境变量未生效 | 确保 .env 文件在项目根目录,使用 load_dotenv() |

相关推荐
淼澄研学1 小时前
Kimi API黑产倒卖技术解析与Python合规接入指南
开发语言·网络·python
何以解忧,唯有..1 小时前
Python 元组(tuple)详解:使用、遍历与排序
开发语言·python
KANGBboy1 小时前
Python eval安全隐患
开发语言·python
evans在进步2 小时前
LeetCode 394:字符串解码——Java 单栈模拟与嵌套解析详解
java·python·leetcode
淼澄研学2 小时前
Python结合大模型挖掘搜题长尾关键词实操指南
开发语言·python
Eloudy3 小时前
预词力:LLM 的唯一形式化能力
人工智能·算法·agent
苏灿烤鱼4 小时前
Cursor 官方插件仓,许可证未声明
typescript·agent·cursor
JaydenAI4 小时前
[基于AgentEvals的自动化评估-04]全面优化面向LangGraph的轨迹评估[下篇]
ai·langchain·agent·evaluation·openevals
中科高级技工学校4 小时前
乌鲁木齐美术艺考技校实践分享
人工智能·python