AI世界的Type-C接口:MCP协议解析

一、背景

以往所有的工具调用方式中,工具描述都是由大模型应用开发者来负责的。这里引入两个问题:

  • 每个人都由自己对工具的理解,每个人的描述方式不一致,怎样保证工具调用的质量是稳定的?

  • 每次开发新应用,都要重新写一遍工具描述,引入了太多重复的工作。

核心原因在于:对工具的描述是一个繁琐而复杂的过程 ,这项工作只有工具的开发者能够清晰准确地完成,而不应该由工具调用者负责。如果工具能清晰地描述自己的功能,就能够实现"一次编写,到处调用",上述问题就迎刃而解了。

二、什么是MCP协议

MCP(Model Context Protocol,模型上下文协议)是由 Anthropic 在2024年1月提出的一套开放协议,旨在实现大型语言模型(LLM)与外部数据源和工具的无缝集成,用来在大模型和数据源之间建立安全双向的链接。

Anthropic的愿景,希望把MCP协议打造成AI世界的"Type-C"接口,可以通过MCP协议工具、数据链接起来,达类似HTTP协议的那种通用程度。

协议(Protocol)是一种约定或标准,用于定义不同系统、设备或软件之间如何通信和交换数据。它确保各方使用相同的"语言"和规则,避免混乱。例如,HTTP协议定义了浏览器与服务器的交互方式,USB协议标准化了设备连接。

(一)MCP核心架构

MCP协议有两个核心角色:客户端与服务端。

MCP服务端 (Tool Provider):

  • 角色:工具的提供者。

  • 职责 :将一个或多个本地函数(例如,Python函数)包装起来,通过一个标准的MCP接口暴露出去。它监听来自客户端的请求,执行对应的函数,并返回结果。

  • 例子:一个天气查询服务、一个数学计算服务、一个数据库访问服务。

MCP客户端 (Tool Consumer)

  • 角色:工具的调用者或消费者。

  • 职责 :连接到MCP服务端,查询可用的工具列表(自发现),并根据需要调用这些工具。

  • 例子:大模型Agent、自动化脚本、任何需要远程执行功能的应用程序。

MCP 主机(MCP Hosts)指的是发起请求的 LLM 应用程序。MCP 客户端(MCP Clients)指的是在主机程序内部的一个对象。

(二)MCP工具调用流程

步骤 1:客户端注册并连接 MCP Server

  • MCP Client 启动后,根据配置文件或命令参数连接多个 MCP Server。

  • 每个 Server 都会返回一份工具描述列表(Tool Manifest),包括:

    python 复制代码
    [
      {
        "name": "query_mysql",
        "description": "执行 SQL 查询",
        "input_schema": {...},
        "output_schema": {...}
      }
    ]
  • Client 将这些工具的元信息缓存并上报给 LLM,使大模型"知道"有哪些可用工具。

步骤 2:LLM 接收用户输入并决定调用工具

  • 用户输入请求(如:"帮我查一下 users 表中有多少行数据")。

  • LLM 分析语义后,判断需要使用 query_mysql 工具。

  • LLM 生成 function calling 格式的调用指令:

    python 复制代码
    {
      "name": "query_mysql",
      "arguments": {
        "sql": "SELECT COUNT(*) FROM users;"
      }
    }

步骤 3:MCP Client 执行工具调用

  • MCP Client 收到该调用后,匹配到对应的 Server。

  • 按协议通过 stdio 或 WebSocket 将请求发送给 MCP Server,例如:

    python 复制代码
    {
      "type": "tool_call",
      "tool": "query_mysql",
      "args": {"sql": "SELECT COUNT(*) FROM users;"}
    }

步骤 4:MCP Server 执行工具逻辑

  • MCP Server 内部执行工具逻辑(例如运行 SQL 查询)。

  • 生成结果:

    python 复制代码
    {
      "result": [{"count": 520}]
    }
  • 将结果通过相同的通信通道返回给 MCP Client。

步骤 5:结果回传给 LLM

  • MCP Client 接收结果,并包装为 ToolMessage 发送回 LLM。

  • LLM 读取结果上下文,再生成最终自然语言回答:

    "数据库中共有 520 条用户记录。"

(三)MCP的通信传输方式

MCP传输方式与MCP协议本身无关,目前主要有三种通信传输方式:stdio、基于HTTP的SSE和Streamable。

(1)stdio (标准输入/输出)

  • 类型:stdio一种非常经典和简单的进程间通信(IPC)方式。客户端启动服务端作为一个子进程。

  • 工作原理: 客户端通过写入子进程的 标准输入 (stdin) 来发送请求,并通过读取子进程的 标准输出 (stdout) 来获取响应。这种方式简单高效,无需网络开销。

  • 适用场景:非常适合在本地环境中,将一个命令行工具或脚本快速封装成一个 MCP 服务。

(2)SSE (Server-Sent Events)

  • 类型:SSE 是一种 基于 HTTP 的单向推送协议 ,它允许服务器在保持连接开放的情况下,持续向客户端发送事件流。

  • 工作原理:客户端发起一个 HTTP 请求,服务器接收请求并保持连接,然后以 text/event-stream 格式将响应数据流式传输给客户端。这在 MCP 中被用来实现请求与响应的通信。

  • 适用场景: 适用于 分布式或网络环境 ,当服务需要部署在远端,并通过网络供多个客户端访问时。

(3)Streamable

  • 类型:Streamable-HTTP 是 MCP 提供的另一种基于 HTTP 的传输方式,它同样用于网络通信。

  • 工作原理:客户端通过 HTTP 请求与服务器通信。与 SSE 的主要区别在于其传输格式和机制可能有所不同,比如Streamble的传输格式可以为任意格式,而SSE为特定格式Streamble的通信方向可以为双向,而SSE只能是单向

  • 适用场景:与 SSE 类似,适用于需要通过网络进行通信的分布式应用

对比:

传输方式 stdio SSE (Server-Sent Events) Streamable-HTTP
通信方向 双向(请求-响应) 单向(服务器推送到客户端) 双向(双向流)
通信模式 本地进程间通信(IPC) 网络通信(长连接流) 网络通信(双向流)
主要用途 封装本地命令行工具 仅需接收服务器更新的场景 复杂的、需要实时双向流的场景
是否容易丢失数据 由操作系统保证可靠性 由 TCP 协议保证可靠性 由 TCP 协议保证可靠性
关键优势 简单、高效、安全,无需网络开销 适用于简单、单向的数据推送,浏览器兼容性好 灵活性高,支持双向流式传输,提升大型任务的响应效率
状态 本地首选 已弃用 远程首选

注意:此前在早期版本中,MCP 曾使用 "HTTP + SSE" 这种组合(客户端 POST、服务器 SSE 推送)作为远程通信方式。然而,该 "HTTP + SSE" 方式在规范中已被标注为 已弃用(deprecated),从 MCP 规范 2025-03-26 起推荐转向 Streamable HTTP。

三、MCP包使用

注意:以下代码需要安装langchain_mcp_adapters包,安装方式如下。

pip install langchain-mcp-adapters --index-url https://pypi.org/simple

(一)stdio传输方式

1、服务端

python 复制代码
from mcp.server.fastmcp import FastMCP
​
mcp = FastMCP("sdg", log_level="ERROR")
​
@mcp.tool(
    name="query_high_frequency_question",
    description="从知识库中检索常见问题解答(FAQ),返回包含问题和答案的结构化JSON数据。",
)
async def query_high_frequency_question() -> str:
    """
    高频问题查询
    """
    try:
        print("调用查询高频问题的tool成功!!")
        return "高频问题是: 恐龙是怎么灭绝的?"
    except Exception as e:
        print(f"Unexpected error in question retrieval: {str(e)}")
        raise
​
​
@mcp.tool(
    name="get_weather",
    description="查询天气"
)
async def get_weather() -> str:
    """
    查询天气的tools
    """
    try:
        print("调用查询天气的tools")
        return "北京的天气是多云"
    except Exception as e:
        print(f"Unexpected error in question retrieval: {str(e)}")
        raise
​
​
if __name__ == "__main__":
    mcp.run(transport="stdio")

2、客户端(直接调用)

注意:

(1)直接启动客户端即可,不需要启动服务端,因为客户端会自动启动服务端。

(2)如果直接运行报错,可能是编码集的问题,可以尝试使用命令行的方式运行:

set PYTHONIOENCODING=utf-8

python client_raw.py

python 复制代码
import asyncio
from langchain_mcp_adapters.tools import load_mcp_tools
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
​
# 配置mcp服务器脚本路径
server_script = r".\server_stdio.py"
​
# 配置mcp服务器启动参数
server_params = StdioServerParameters(
    command="python" if server_script.endswith(".py") else "node",
    args=[server_script],
)
​
# 定义mcp客户端
mcp_client = None
​
# 主要的异步函数run_agent
async def run():
    global mcp_client
    # 启动 MCP server,并通过标准输入输出建立异步连接。
    async with stdio_client(server_params) as (read, write):
        # 使用读写通道创建 MCP 会话。
        async with ClientSession(read, write) as session:
            await session.initialize()
            # 动态创建一个临时类 MCPClientHolder,把 session 放进去。这样就可以在函数外部通过 mcp_client.session 调用 MCP 工具
            mcp_client = type("MCPClientHolder", (), {"session": session})()
​
            # 从 session 自动获取 MCP server 提供的工具列表。
            tools = await load_mcp_tools(session)
            print(f"tools-->{tools}")
​
            # 调用 MCP server 的 get_weather 工具
            response = await session.call_tool("get_weather", arguments={})
            print(f"response-->{response}")
    return
​
# 启动运行
if __name__ == "__main__":
    asyncio.run(run())

运行结果:

bash 复制代码
tools-->[StructuredTool(name='query_high_frequency_question', description='从知识库中检索常见问题解答(FAQ),返回包含问题和答案的结构化JSON数据。', atitle': 'query_high_frequency_questionArguments', 'type': 'object'}, response_format='content_and_artifact', coroutine=<function convert_mcp_tool_to_langchain_tool.<locals>.call_tool at 0x000001E176F59900>), StructuredTool(name='get_weather', description='查询天气', args_schema={'properties': {}, le': 'get_weatherArguments', 'type': 'object'}, response_format='content_and_artifact', coroutine=<function convert_mcp_tool_to_langchain_tool.<locals>.call_tool at 0x000001E176F59A20>)]
response-->meta=None content=[TextContent(type='text', text='北京的天气是多云', annotations=None, meta=None)] structuredContent={'result': '北京的天气是多云'} isError=False

3、客户端(agent调用)

注意:

(1)直接启动客户端即可,不需要启动服务端,因为客户端会自动启动服务端。

(2)如果直接运行报错,可能是编码集的问题,可以尝试使用命令行的方式运行:

set PYTHONIOENCODING=utf-8

python client_raw.py

python 复制代码
import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "../../.."))
import asyncio
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from agent_learn.config import Config
​
conf = Config()
​
# 创建模型
llm = ChatOpenAI(base_url=conf.base_url,
                 api_key=conf.api_key,
                 model=conf.model_name,
                 temperature=0.1)
​
# 配置mcp服务器脚本路径
server_script = r".\server_stdio.py"
​
# 配置mcp服务器启动参数
server_params = StdioServerParameters(
    command="python" if server_script.endswith(".py") else "node",
    args=[server_script],
)
​
# 定义mcp客户端
mcp_client = None
​
# 主要的异步函数run_agent
async def run_agent():
    global mcp_client
    # 启动 MCP server,并通过标准输入输出建立异步连接。
    async with stdio_client(server_params) as (read, write):
        # 使用读写通道创建 MCP 会话。
        async with ClientSession(read, write) as session:
            # 初始化会话
            await session.initialize()
            # 动态创建一个临时类 MCPClientHolder,把 session 放进去。这样就可以在函数外部通过 mcp_client.session 调用 MCP 工具
            mcp_client = type("MCPClientHolder", (), {"session": session})()
​
            # 从 session 自动获取 MCP server 提供的工具列表
            tools = await load_mcp_tools(session)
            # print(f"tools-->{tools}")
​
            # 创建prompt模板
            prompt_template = ChatPromptTemplate.from_messages([
                ("system", "你是一个乐于助人的助手,能够调用工具回答用户问题。"),
                ("human", "{input}"),
                ("placeholder", "{agent_scratchpad}"),
            ])
​
            # 构建工具调用代理
            agent = create_tool_calling_agent(llm, tools, prompt_template)
​
            # 创建代理执行器
            agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
​
            # 代理调用
            print("MCP客户端启动,输入'quit'退出")
            while True:
                # 接收用户查询
                query = input("\nQuery: ").strip()
                if query.lower() == "quit":
                    break
                # 发送用户查询给代理,并打印  
                try:
                    response = await agent_executor.ainvoke({"input": query})
                    print(f"response-->{response}")
                except Exception:
                    print("解析有问题")
    return
​
if __name__ == "__main__":
    asyncio.run(run_agent())

运行结果:

(二)sse传输方式

1、服务端

python 复制代码
from mcp.server.fastmcp import FastMCP
​
​
# 在创建FastMCP实例时指定host和port
mcp = FastMCP("sdg", log_level="ERROR", host="127.0.0.1", port=8001)
​
@mcp.tool(
    name="query_high_frequency_question",
    description="从知识库中检索常见问题解答(FAQ),返回包含问题和答案的结构化JSON数据。",
)
async def query_high_frequency_question() -> str:
    """
    高频问题查询
    """
    try:
        print("调用查询高频问题的tool成功!!")
        return "高频问题是: 恐龙是怎么灭绝的?"
    except Exception as e:
        print(f"Unexpected error in question retrieval: {str(e)}")
        raise
​
@mcp.tool(
    name="get_weather",
    description="查询天气"
)
async def get_weather() -> str:
    """
    查询天气的tools
    """
    try:
        print("调用查询天气的tools")
        return "北京的天气是多云"
    except Exception as e:
        print(f"Unexpected error in question retrieval: {str(e)}")
        raise
​
​
def main():
    print("正在启动MCP SSE服务器...")
    print("SSE端点: http://localhost:8001/sse")
    print("按 Ctrl+C 停止服务器")
​
    try:
        # 运行SSE服务器
        mcp.run(transport="sse")
    except KeyboardInterrupt:
        print("\n服务器已停止")
    except Exception as e:
        print(f"服务器启动失败: {e}")
​
​
if __name__ == "__main__":
    main()

2、客户端(直接调用)

python 复制代码
import asyncio
from mcp import ClientSession
from mcp.client.sse import sse_client
from langchain_mcp_adapters.tools import load_mcp_tools
​
# MCP server URL for SSE connection
server_url = "http://localhost:8001/sse"
​
# 定义mcp客户端
mcp_client = None
​
# 主要的异步函数run_agent
async def run():
    global mcp_client
    # 启动 MCP server,通过 SSE 建立异步连接。
    async with sse_client(url=server_url) as streams:
        # 使用读写通道创建 MCP 会话
        async with ClientSession(*streams) as session:
            await session.initialize()
            # 动态创建一个临时类 MCPClientHolder,把 session 放进去。这样就可以在函数外部通过 mcp_client.session 调用 MCP 工具
            mcp_client = type("MCPClientHolder", (), {"session": session})()
​
            # 从 session 自动获取 MCP server 提供的工具列表。
            tools = await load_mcp_tools(session)
            # print(f"tools-->{tools}")
​
            # 调用 MCP server 的 get_weather 工具
            response=await session.call_tool("get_weather", arguments={})
            print(f"response-->{response}")
​
​
# 启动运行agent
if __name__ == "__main__":
    asyncio.run(run())

3、客户端(agent调用)

python 复制代码
import json
import asyncio
from langchain_openai import ChatOpenAI
from mcp import ClientSession
from mcp.client.sse import sse_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from agent_learn.config import Config
​
conf = Config()
​
# 创建模型
llm = ChatOpenAI(base_url=conf.base_url,
                 api_key=conf.api_key,
                 model=conf.model_name,
                 temperature=0.1)
​
# MCP server URL for SSE connection
server_url = "http://localhost:8001/sse"
​
# 定义mcp客户端
mcp_client = None
​
# Main async function: connect, load tools, create agent, run chat loop
async def run_agent():
    global mcp_client
    # 启动 MCP server,通过 SSE 建立异步连接。
    async with sse_client(url=server_url) as streams:
        # 使用读写通道创建 MCP 会话
        async with ClientSession(*streams) as session:
            await session.initialize()
            # 动态创建一个临时类 MCPClientHolder,把 session 放进去。这样就可以在函数外部通过 mcp_client.session 调用 MCP 工具
            mcp_client = type("MCPClientHolder", (), {"session": session})()
​
            # 从 session 自动获取 MCP server 提供的工具列表。
            tools = await load_mcp_tools(session)
            # print(f"tools-->{tools}")
​
            # 创建prompt模板
            prompt_template = ChatPromptTemplate.from_messages([
                ("system", "你是一个乐于助人的助手,能够调用工具回答用户问题。"),
                ("human", "{input}"),
                ("placeholder", "{agent_scratchpad}"),
            ])
​
            # 构建工具调用代理
            agent = create_tool_calling_agent(llm, tools, prompt_template)
​
            # 创建代理执行器
            agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
​
            # 代理调用
            print("MCP客户端启动,输入'quit'退出")
            while True:
                # 接收用户查询
                query = input("\nQuery: ").strip()
                if query.lower() == "quit":
                    break
                # 发送用户查询给代理,并打印
                try:
                    response = await agent_executor.ainvoke({"input": query})
                    print(f"response-->{response}")
                except Exception:
                    print("解析有问题")
    return
​
if __name__ == "__main__":
    asyncio.run(run_agent())

运行结果:

(三)streamable方式

1、服务端

python 复制代码
from mcp.server.fastmcp import FastMCP
​
# 创建 MCP 实例,指定服务名称、日志级别、主机和端口
mcp = FastMCP("sdg", log_level="ERROR", host="127.0.0.1", port=8001)
​
@mcp.tool(
    name="query_high_frequency_question",
    description="从知识库中检索常见问题解答(FAQ),返回包含问题和答案的结构化JSON数据。",
)
async def query_high_frequency_question() -> str:
    """
    高频问题查询
    """
    try:
        print("调用查询高频问题的tool成功!!")
        return "高频问题是: 恐龙是怎么灭绝的?"
    except Exception as e:
        print(f"Unexpected error in question retrieval: {str(e)}")
        raise
​
@mcp.tool(
    name="get_weather",
    description="查询天气"
)
async def get_weather() -> str:
    """
    查询天气的tools
    """
    try:
        print("调用查询天气的tools")
        return "北京的天气是多云"
    except Exception as e:
        print(f"Unexpected error in question retrieval: {str(e)}")
        raise
​
​
def main():
    """
    启动 Streamable HTTP 服务器。
    """
    print("正在启动MCP Streamable服务器...")
    print("服务器将在 http://localhost:8001 上运行")
    print("按 Ctrl+C 停止服务器")
    try:
        mcp.run(transport="streamable-http")  # 使用 streamable-http 传输方式
    except KeyboardInterrupt:
        print("\n服务器已停止")
    except Exception as e:
        print(f"服务器启动失败: {e}")
​
​
if __name__ == "__main__":
    main()

2、客户端(直接调用)

python 复制代码
import asyncio
import logging
from langchain_mcp_adapters.tools import load_mcp_tools
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
​
# 定义服务器地址
server_url = "http://127.0.0.1:8001/mcp"
​
# 定义mcp客户端
mcp_client = None
​
# 配置日志
logging.basicConfig(
    level=logging.DEBUG,  # 提高日志级别以捕获更多信息
    format='[客户端] %(asctime)s - %(levelname)s - %(message)s'
)
​
async def main():
    global mcp_client
    logging.info(f"准备连接到 Streamable-HTTP 服务器: {server_url}")
    try:
        # 启动 MCP server,通过streamable建立连接
        async with streamablehttp_client(server_url) as (read, write, _):
            logging.info("连接已成功建立!")
            # 使用读写通道创建 MCP 会话
            async with ClientSession(read, write) as session:
                try:
                    await session.initialize()
                    logging.info("会话初始化成功,可以开始调用工具。")
                    # 动态创建一个临时类 MCPClientHolder,把 session 放进去。这样就可以在函数外部通过 mcp_client.session 调用 MCP 工具
                    mcp_client = type("MCPClientHolder", (), {"session": session})()
​
                    # 从 session 自动获取 MCP server 提供的工具列表。
                    tools = await load_mcp_tools(session)
                    # print(f"tools-->{tools}")
​
                    # 调用远程工具
                    logging.info("--> 正在调用工具: query_high_frequency_question")
                    response = await session.call_tool("query_high_frequency_question", {})
                    print(f"response-->{response}")
                    logging.info(f"<-- 收到响应: {response}")
​
                    print("-" * 30)
​
                    logging.info("--> 正在调用工具: get_weather")
                    response = await session.call_tool("get_weather", {})
                    print(f"response-->{response}")
                    logging.info(f"<-- 收到响应: {response}")
                except Exception as e:
                    logging.error(f"调用工具时发生错误: {e}", exc_info=True)
                    raise
    except Exception as e:
        logging.error(f"连接或会话初始化时发生错误: {e}", exc_info=True)
        logging.error("请确认服务端脚本已启动并运行在 http://127.0.0.1:8001/mcp")
        raise
​
​
if __name__ == "__main__":
    try:
        asyncio.run(main())
    except Exception as e:
        logging.error(f"客户端运行失败: {e}", exc_info=True)

3、客户端(agent调用)

python 复制代码
import json
import logging
import asyncio
from langchain_openai import ChatOpenAI
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from agent_learn.config import Config
​
conf = Config()
​
# 创建模型
llm = ChatOpenAI(base_url=conf.base_url,
                 api_key=conf.api_key,
                 model=conf.model_name,
                 temperature=0.1)
​
# MCP 服务器的 Streamable-HTTP 连接地址
server_url = "http://127.0.0.1:8001/mcp"
​
# 配置日志
logging.basicConfig(
    level=logging.DEBUG,  # 提高日志级别以捕获更多信息
    format='[客户端] %(asctime)s - %(levelname)s - %(message)s'
)
​
# 定义mcp客户端
mcp_client = None
​
async def run_agent():
    global mcp_client
    logging.info(f"准备连接到 Streamable-HTTP 服务器: {server_url}")
    # 启动 MCP server,通过streamable建立连接
    async with streamablehttp_client(server_url) as (read, write, _):
        logging.info("连接已成功建立!")
        # 使用读写通道创建 MCP 会话
        async with ClientSession(read, write) as session:
            try:
                await session.initialize()
                logging.info("会话初始化成功,可以开始加载工具。")
                # 动态创建一个临时类 MCPClientHolder,把 session 放进去。这样就可以在函数外部通过 mcp_client.session 调用 MCP 工具
                mcp_client = type("MCPClientHolder", (), {"session": session})()
​
                # 从 session 自动获取 MCP server 提供的工具列表。
                tools = await load_mcp_tools(session)
                # print(f"tools-->{tools}")
​
                # 创建 agent 的提示模板
                prompt = ChatPromptTemplate.from_messages([
                    ("system", "你是一个乐于助人的助手,能够调用工具回答用户问题。"),
                    ("human", "{input}"),
                    ("placeholder", "{agent_scratchpad}"),
                ])
​
                # 构建工具调用代理
                agent = create_tool_calling_agent(llm, tools, prompt)
​
                # 创建代理执行器
                agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
​
                # 代理调用
                print("MCP客户端启动,输入'quit'退出")
                while True:
                    query = input("\nQuery: ").strip()
                    if query.lower() == "quit":
                        break
                    # 发送用户查询到 agent 并打印格式化响应
                    logging.info(f"处理用户查询: {query}")
                    try:
                        response = await agent_executor.ainvoke({"input": query})
                        print(f"response-->{response}")
                    except Exception:
                        print("解析有问题")
            except Exception as e:
                logging.error(f"会话初始化或工具调用时发生错误: {e}", exc_info=True)
                raise
​
if __name__ == "__main__":
    try:
        asyncio.run(run_agent())
    except Exception as e:
        logging.error(f"客户端运行失败: {e}", exc_info=True)

四、python_a2a包使用

(一)服务端

python 复制代码
import logging
import uvicorn
from python_a2a.mcp import FastMCP
from python_a2a.mcp import create_fastapi_app
​
# 配置日志,方便调试
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
​
# 创建 MCP 服务器实例
mcp = FastMCP(
    name="MyMCPTools",
    description="提供高频问题和天气查询工具",
    version="1.0.0"
)
​
# 定义工具 1:查询高频问题
@mcp.tool(
    name="query_high_frequency_question",
    description="获取知识库中的高频问答,返回 JSON 数据",
)
async def query_high_frequency_question(**kwargs) -> str:
    """
    query_high_frequency_question 不需要任何传参
    查询高频问题并返回答案
    返回示例:[{"question_id": 1, "question_text": "恐龙怎么灭绝", "answer_text": "小行星撞击", ...}]
    """
    try:
        logger.info(f"调用查询高频问题的工具,参数为:{kwargs}")
        return '{"status": "success", "data": [{"question_id": 1, "question_text": "恐龙是怎么灭绝的?", "answer_text": "可能是小行星撞击", "category": "历史", "frequency_score": 0.9}]}'
    except Exception as e:
        logger.error(f"查询高频问题出错: {str(e)}")
        raise
​
# 定义工具 2:查询天气
@mcp.tool(
    name="get_weather",
    description="查询天气",
)
async def get_weather(**kwargs) -> str:
    """
    get_weather 不需要任何传参
    查询天气并返回结果
    返回示例:{"status": "success", "data": "北京的天气是多云"}
    """
    try:
        logger.info(f"调用查询天气的工具,参数为{kwargs}")
        return '{"status": "success", "data": "北京的天气是多云"}'
    except Exception as e:
        logger.error(f"查询天气出错: {str(e)}")
        raise
​
# 启动服务器
def start_server():
    logger.info("=== MCP 服务器信息 ===")
    logger.info(f"名称: {mcp.name}")
    logger.info(f"描述: {mcp.description}")
​
    port = 8010
    app = create_fastapi_app(mcp)
    logger.info(f"启动 MCP 服务器于 http://localhost:{port}")
    uvicorn.run(app, host="0.0.0.0", port=port)
​
​
if __name__ == "__main__":
    start_server()

(二)客户端(直接调用)

python 复制代码
import asyncio
import logging
from python_a2a.mcp import MCPClient
​
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
​
async def test_mcp_tools():
    # 连接到服务端,端口 8000
    client = MCPClient("http://localhost:8010")
    try:
        # 步骤 1:获取可用工具列表
        tools = await client.get_tools()
        logger.info("可用工具列表:")
        for tool in tools:
            print(tool)
            logger.info(f"- {tool.get('name', '未知')}: {tool.get('description', '无描述')}")
​
        # 步骤 2:调用查询高频问题工具
        result_qhf = await client.call_tool("query_high_frequency_question")
        logger.info(f"高频问题查询结果:{result_qhf}")
​
        # 步骤 3:调用查询天气工具
        result_weather = await client.call_tool("get_weather")
        logger.info(f"天气查询结果:{result_weather}")
​
    except Exception as e:
        logger.error(f"MCP 客户端出错:{str(e)}", exc_info=True)
    finally:
        await client.close()
​
async def main():
    await test_mcp_tools()
​
​
if __name__ == "__main__":
    asyncio.run(main())

运行结果:

bash 复制代码
{'name': 'query_high_frequency_question', 'description': '获取知识库中的高频问答,返回 JSON 数据', 'parameters': {'type': 'object', 'properties': {}, 'required': []}}
{'name': 'get_weather', 'description': '查询天气', 'parameters': {'type': 'object', 'properties': {}, 'required': []}}

(三)客户端(agent调用)

python 复制代码
import asyncio
import json
import logging
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from python_a2a.mcp import MCPClient
from python_a2a.langchain import to_langchain_tool
from agent_learn.config import Config
​
conf = Config()
​
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
​
# 创建模型
llm = ChatOpenAI(base_url=conf.base_url,
                 api_key=conf.api_key,
                 model=conf.model_name,
                 temperature=0.1)
​
async def test_mcp_tools():
    # 连接到服务端,端口 8000
    client = MCPClient("http://localhost:8010")
    try:
        # 步骤 1:获取可用工具列表
        tools = await client.get_tools()
        logger.info("可用工具列表:")
        for tool in tools:
            print(tool)
            logger.info(f"- {tool.get('name', '未知')}: {tool.get('description', '无描述')}")
​
        # 将 MCP tool 转成 LangChain 的工具
        get_weather_tool = to_langchain_tool("http://localhost:8010", "get_weather")
        query_high_frequency_question = to_langchain_tool("http://localhost:8010", "query_high_frequency_question")
        tools=[get_weather_tool, query_high_frequency_question]
​
        # 创建prompt模板
        prompt_template = ChatPromptTemplate.from_messages([
            ("system", "你是一个乐于助人的助手,能够调用工具回答用户问题。工具不需要传参。"),
            ("human", "{input}"),
            ("placeholder", "{agent_scratchpad}"),
        ])
​
        # 构建工具调用代理
        agent = create_tool_calling_agent(llm, tools, prompt_template)
​
        # 创建代理执行器
        agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
​
        # 代理调用
        print("MCP客户端启动,输入'quit'退出")
        while True:
            # 接收用户查询
            query = input("\nQuery: ").strip()
            if query.lower() == "quit":
                break
            # 发送用户查询给代理,并打印
            try:
                response = await agent_executor.ainvoke({"input": query})
                print(f"response-->{response}")
            except Exception:
                print("解析有问题")
    except Exception as e:
        logger.error(f"MCP 客户端出错:{str(e)}", exc_info=True)
    finally:
        await client.close()
​
async def main():
    await test_mcp_tools()
​
if __name__ == "__main__":
    asyncio.run(main())

运行结果:

相关推荐
骚戴3 小时前
AI架构指南:大型语言模型 (LLM) API 的通用集成与企业级配置(2025年)
人工智能·大模型·llm·gateway·api
技术小甜甜3 小时前
[AI] openwebui内网部署网页加载缓慢?一个设置绕过openai连接问题!
人工智能·llm·ollama·openwebui
天行无忌13 小时前
2025:AI炒作巅峰,2026:价值回归与实用落地之年
agent
装不满的克莱因瓶14 小时前
【Coze智能体实战二】一键生成儿歌背单词视频
人工智能·ai·实战·agent·工作流·智能体·coze
RockHopper202516 小时前
一种认知孪生xLLM架构的原理说明
人工智能·llm·数字孪生·认知孪生
沛沛老爹16 小时前
Web开发者实战RAG评估:从指标到工程化验证体系
前端·人工智能·llm·agent·rag·评估
荣江17 小时前
从无状态到有记忆:AI Agent 记忆系统的演进与 Cortex Memory 的实践
agent
玄同76517 小时前
Python 数据类型:LLM 语料与 API 参数的底层处理逻辑
开发语言·人工智能·python·自然语言处理·llm·nlp·知识图谱
玄同76519 小时前
Python 真零基础入门:从 “什么是编程” 到 LLM Prompt 模板生成
人工智能·python·语言模型·自然语言处理·llm·nlp·prompt