MCP 系列 3:使用 LangChain Agent 调用 MCP 服务


上一篇已经使用 FastMCP 实现了一个城市旅行 MCP Server,其中包含 Tool、Resource 和 Prompt,并分别验证了 stdio、Streamable HTTP 和旧 HTTP+SSE。

这一篇不再通过 python xxx_agent.py 执行一次性 Agent,而是创建两个可以部署到 LangGraph 的 Agent:

  1. http_mcp_agent:通过 Streamable HTTP 调用 MCP。
  2. sse_mcp_agent:通过旧 HTTP+SSE 调用 MCP。

启动 langgraph dev 后,可以在 LangGraph Studio 浏览器页面中选择 Agent、输入问题、查看 MCP Tool 调用过程和最终回答。

1. 完整调用链

浏览器中的一次请求会经过下面几个组件:

  1. 用户在 LangGraph Studio 中输入消息。
  2. Studio 把消息发送给本地 LangGraph Agent Server。
  3. Agent Server 运行 http_mcp_agentsse_mcp_agent
  4. Qwen3 根据问题和 Tool Schema 决定调用 calculate_trip_budget
  5. LangChain MCP Adapter 将 LangChain Tool 调用转换为 MCP tools/call
  6. FastMCP Server 计算预算并返回结果。
  7. Qwen3 根据 MCP Resource、MCP Prompt 和 Tool 结果生成最终行程。
  8. Studio 展示模型消息、Tool 节点、Tool 结果和最终回答。

这里有两个不同的网络服务:

  • FastMCP Server 运行在 18100 或 18101 端口,负责提供 MCP 能力。
  • LangGraph Agent Server 默认运行在 2024 端口,负责接收浏览器请求并执行 Agent 图。

2. 项目结构

mcp/p03_langchain_mcp_agent/
├── server.py
├── 02_agent_with_http_mcp.py
├── 03_agent_with_sse_mcp.py
├── langgraph.json
├── requirements.txt
└── README.md

本例只保留 HTTP 和 SSE 两个 Agent。server.py 从 p02 复制而来,因此 p03 可以独立运行。

3. MCP Server

server.py 同时提供 Tool、Resource 和 Prompt:

"""定义一个简单的 FastMCP Server。

MCP 服务端对外暴露三类能力:
  - Tool:可被 LLM / Client 调用的函数(如计算预算)
  - Resource:可被读取的静态或动态资料(如城市介绍)
  - Prompt:预置的提示词模板(如行程规划指令)

本示例用 FastMCP 装饰器注册能力,再分别通过 stdio / HTTP / SSE 传输。
"""

from fastmcp import FastMCP


# 第 1 步:创建 FastMCP 实例。
# name 和 instructions 会出现在 MCP 协议握手信息中,供 Client 识别服务用途。
mcp = FastMCP(
    name="city-trip-mcp-server",
    instructions="提供城市资料、旅行预算计算和行程规划提示词。",
)


# 演示用内存数据;真实项目可替换为数据库或外部 API。
CITY_DATA = {
    "杭州": {
        "attractions": ["西湖", "灵隐寺"],
        "daily_budget": 500,
    },
    "北京": {
        "attractions": ["故宫", "天坛"],
        "daily_budget": 600,
    },
}


# 第 2 步:注册 Tool —— Client 通过 call_tool 调用。
@mcp.tool
def calculate_trip_budget(city: str, days: int) -> str:
    """计算一个人的旅行预算。"""

    city_info = CITY_DATA.get(city)
    if city_info is None:
        return f"暂时没有 {city} 的预算资料。"

    total_budget = city_info["daily_budget"] * days
    return f"{city}旅行 {days} 天,预计需要 {total_budget} 元。"


# 第 3 步:注册 Resource —— Client 通过 read_resource 读取。
# 固定 URI,返回服务说明文档。
@mcp.resource("guide://city-trip")
def service_guide() -> str:
    """返回服务使用说明。"""

    return "支持杭州和北京,可以查询城市资料、计算预算和生成行程提示词。"


# 带路径参数的 Resource 模板:city://{city}
# FastMCP 会把 URI 中的 {city} 映射为函数参数。
@mcp.resource("city://{city}")
def city_profile(city: str) -> str:
    """根据 URI 中的城市名称返回城市资料。"""

    city_info = CITY_DATA.get(city)
    if city_info is None:
        return f"暂时没有 {city} 的资料。"

    attractions = "、".join(city_info["attractions"])
    return f"{city}的推荐景点有:{attractions}。"


# 第 4 步:注册 Prompt —— Client 通过 get_prompt 获取消息模板。
# 返回的字符串会被包装成 MCP PromptMessage,供 Agent 直接使用。
@mcp.prompt
def plan_city_trip(city: str, days: int) -> str:
    """生成旅行规划提示词。"""

    return (
        f"请为我规划一次 {days} 天的{city}旅行。"
        f"先读取 city://{city} 资源了解景点,"
        "再调用 calculate_trip_budget 工具计算预算。"
    )


# 直接运行 server.py 时,默认以 stdio 传输启动(供 Cursor / Claude Desktop 等本地集成)。
if __name__ == "__main__":
    mcp.run(transport="stdio", show_banner=False)

4. 创建 Streamable HTTP Agent

LangGraph 可以加载普通的已编译图,也可以加载返回图的工厂函数。因为 MCP Client 的加载方法是异步的,所以这里定义异步 make_graph()

02_agent_with_http_mcp.py

"""创建通过 Streamable HTTP 调用 MCP 的 LangGraph Agent。"""

from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI


async def make_graph():
    """加载 HTTP MCP 能力并返回可部署的 Agent 图。"""

    client = MultiServerMCPClient(
        {
            "travel": {
                "transport": "http",
                "url": "http://127.0.0.1:18100/mcp",
            }
        }
    )

    tools = await client.get_tools()
    resources = await client.get_resources("travel", uris=["city://杭州"])
    prompt_messages = await client.get_prompt(
        "travel",
        "plan_city_trip",
        arguments={"city": "杭州", "days": "3"},
    )

    resource_text = resources[0].as_string()
    prompt_text = prompt_messages[0].content

    model = ChatOpenAI(
        model="Qwen3-14B-AWQ-4bit-MLX",
        base_url="http://127.0.0.1:18080/v1",
        api_key="not-needed",
        temperature=0,
        max_tokens=512,
    )

    return create_agent(
        model=model,
        tools=tools,
        system_prompt=(
            "你是旅行助手。回答旅行规划问题前必须调用旅行预算工具。\n"
            f"MCP Prompt:{prompt_text}\n"
            f"MCP Resource:{resource_text}"
        ),
    )

图加载时会完成三件事:

  1. get_tools() 将 MCP Tool 转换为 LangChain Tool。
  2. get_resources() 读取杭州的城市 Resource。
  3. get_prompt() 获取三日游 Prompt。

Resource 和 Prompt 被放进系统提示词,Tool 则交给 Agent。用户在浏览器中输入问题后,Agent 才会真正调用预算 Tool。

5. 创建旧 HTTP+SSE Agent

SSE Agent 的业务逻辑完全相同,只有 MCP 连接配置不同。

03_agent_with_sse_mcp.py

"""创建通过旧 HTTP+SSE 调用 MCP 的 LangGraph Agent。"""

from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI


async def make_graph():
    """加载 SSE MCP 能力并返回可部署的 Agent 图。"""

    client = MultiServerMCPClient(
        {
            "travel": {
                "transport": "sse",
                "url": "http://127.0.0.1:18101/sse",
            }
        }
    )

    tools = await client.get_tools()
    resources = await client.get_resources("travel", uris=["city://杭州"])
    prompt_messages = await client.get_prompt(
        "travel",
        "plan_city_trip",
        arguments={"city": "杭州", "days": "3"},
    )

    resource_text = resources[0].as_string()
    prompt_text = prompt_messages[0].content

    model = ChatOpenAI(
        model="Qwen3-14B-AWQ-4bit-MLX",
        base_url="http://127.0.0.1:18080/v1",
        api_key="not-needed",
        temperature=0,
        max_tokens=512,
    )

    return create_agent(
        model=model,
        tools=tools,
        system_prompt=(
            "你是旅行助手。回答旅行规划问题前必须调用旅行预算工具。\n"
            f"MCP Prompt:{prompt_text}\n"
            f"MCP Resource:{resource_text}"
        ),
    )

旧 SSE 端点是 /sse。新项目应该使用 Streamable HTTP,这里保留 SSE Agent 只是为了验证兼容链路。

6. 注册两个 LangGraph

在项目目录中创建 langgraph.json

{
  "dependencies": ["."],
  "graphs": {
    "http_mcp_agent": "./02_agent_with_http_mcp.py:make_graph",
    "sse_mcp_agent": "./03_agent_with_sse_mcp.py:make_graph"
  },
  "python_version": "3.12"
}

graphs 中的 Key 是 Studio 中显示的图名称,Value 由“Python 文件路径 + 导出变量或函数名称”组成。

这里注册的是两个 make_graph 工厂函数。LangGraph 创建 Assistant 时会执行对应函数,并取得函数返回的 Agent 图。

7. 安装 LangGraph CLI

p03 的 requirements.txt 内容如下:

fastmcp==3.2.4
httpx==0.28.1
langchain==1.2.13
langchain-mcp-adapters==0.2.2
langchain-openai==1.1.12
langgraph==1.1.3
langgraph-cli[inmem]==0.4.19

安装:

cd /Users/bianhn/Documents/git/llm-learning
uv pip install \
  --python .venv_mcp/bin/python \
  -r mcp/p03_langchain_mcp_agent/requirements.txt
uv pip check --python .venv_mcp/bin/python
source .venv_mcp/bin/activate

langgraph-cli[inmem] 提供本地 Agent Server。它使用内存存储,适合开发和浏览器测试,不是生产部署方案。

8. 启动所有服务

8.1 启动 Qwen3

首先启动本地模型服务:

source .venv_tool_server/bin/activate

"$VIRTUAL_ENV/bin/python" -m mlx_lm server \
  --model Qwen3-14B-AWQ-4bit-MLX \
  --host 127.0.0.1 \
  --port 18080 \
  --prompt-cache-size 0 \
  --chat-template-args '{"enable_thinking": false}'

8.2 启动 Streamable HTTP MCP

新终端执行:

cd /Users/bianhn/Documents/git/llm-learning
source .venv_mcp/bin/activate

fastmcp run mcp/p03_langchain_mcp_agent/server.py \
  --transport http \
  --host 127.0.0.1 \
  --port 18100

如果 p02 的同一个 HTTP Server 已经运行在 18100 端口,可以直接复用。

8.3 启动 HTTP+SSE MCP

再打开一个终端:

cd /Users/bianhn/Documents/git/llm-learning
source .venv_mcp/bin/activate

fastmcp run mcp/p03_langchain_mcp_agent/server.py \
  --transport sse \
  --host 127.0.0.1 \
  --port 18101

8.4 启动 LangGraph Agent Server

必须进入包含 langgraph.json 的目录:

cd /Users/bianhn/Documents/git/llm-learning/mcp/p03_langchain_mcp_agent
source ../../.venv_mcp/bin/activate

langgraph dev \
  --host 127.0.0.1 \
  --port 2024

启动成功后,终端会显示:

API: http://127.0.0.1:2024
Studio UI: https://smith.langchain.com/studio/?baseUrl=http://127.0.0.1:2024
API Docs: http://127.0.0.1:2024/docs

如果 2024 已经被其他 LangGraph 项目占用,可以换一个端口:

langgraph dev --host 127.0.0.1 --port 2025

Studio URL 中的 baseUrl 也要相应改成 http://127.0.0.1:2025

9. 在浏览器中测试

sse_mcp_agent

sse_mcp_agent

http_mcp_agent

10. 总结

本篇完成了两个可在浏览器中测试的 LangGraph Agent:

  • http_mcp_agent 使用 Streamable HTTP MCP。
  • sse_mcp_agent 使用旧 HTTP+SSE MCP。
  • 两个 Agent 在加载时读取相同的 MCP Resource 和 Prompt。
  • 浏览器提交消息后,Qwen3 通过 LangChain MCP Tool 调用 FastMCP Server。
  • LangGraph Studio 可以查看模型节点、Tool 节点、预算结果和最终回答。

部署结构中要同时区分三层:FastMCP 提供能力,LangGraph 部署 Agent,Studio 提供浏览器交互和调试界面。


文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
  目录