LangChain v1.3.4 笔记 - 08 MCP & 相关概念

补充

AI 应用的圈子里面整天说 Agent、FunctionCalling | ToolCalling、MCP、Herness、ReAct、Loop Engineering、Harness Engineering 都在说什么?

  • Agent:智能体,模型本身不具备记忆、工具调用、任务规划的功能,Agent 就是 循环 → 思考 → 行动 → 观察 的闭环,最终完成一个复杂的任务;

  • FunctionCalling | ToolCalling:工具调用,赋予模型调用外部工具以及格式化输出的能力,早期 OpenAI 提出的 FunctionCallingToolCalling 则是演进的产物,这也是 Agent 的一部分,

  • MCP:是 Model Context Protocol 的缩写,模型上下文协议,模型调用工具的一种标准协议,在开始的时候各种模型都有自己的工具调用方案,互不兼容,在代码体现上工具的形式也是自娱自乐;

    • MCP 分为服务端与客户端,使其变得标准化,任意模型直接连接到这个 MCP 的服务端都可以使用内部的一系列工具。连接者可以被视为客户端;
    • MCP 其实就是一系列工具的集合组合出来的工具集;
    • MCP 的服务端可以理解成一个微服务,独立于 Agent 应用,谁想使用就连接即可。
  • ReAct:是 Reasoning + Acting 的缩写,推理 + 行动,ReAct 是一种 Agent 的实现方式,模型在每一步的循环中都会进行推理和行动,推理的结果会影响行动的选择,行动的结果会影响下一步的推理。让模型不再是"盲猜"或"一次性的工具调用",而是"走一步看一步"的智能决策者。

  • Loop Engineering:ReAct 定义了 思考 → 行动 → 观察 的循环逻辑,但在真实运行中,这个循环会遇到各种工程问题,比如死循环、过早终止等,针对循环内部的逻辑和策略做出的工程设计和优化

  • Harness Engineering:围绕 Agent 的运行环境展开的一整套系统工程实践,比如沙箱隔离、可观测性、评测与基准、回放与调试,让 Agent 更稳更安全的运行。

MCP

在 LangChain 中通过 MultiServerMCPClient 作为客户端连接对象,依赖 langchain-mcp-adapters 文档

服务端则推荐通过 fastmcp 进行开发,它本身同样支持 client 文档

py 复制代码
# server.py

from fastmcp import FastMCP

mcp = FastMCP(
  name="Math", # 名字
  instructions="数学计算", # 描述
  version="1.0.0" # 版本
)

@mcp.tool
def add(a: int, b: int) -> int:
  """两数相加"""
  return a + b

@mcp.tool(name="sub_tool", description="两数相减")
def sub(a: int, b: int) -> int:
  return a - b

if __name__ == "__main__":
  mcp.run(transport="http", host="127.0.0.1", port=9000)

# [08/20/26 12:51:59] INFO     Starting MCP server 'Math' with transport 'http' on http://127.0.0.1:9000/mcp                                     
# INFO:     Started server process [9906]
# INFO:     Waiting for application startup.
# INFO:     Application startup complete.

@mcp.toollangchain 的工具使用大致差不多;支持 DocString 或者类型注解加参数的组合。

mcp.run 中的 transport 支持多种模式,除默认值外可以指定运行的地址和端口参数 文档

  • http | streamable-http 用于使用 Streamable HTTP 协议的 Web 服务
  • stdio 默认值,用于本地集成和 CLI 工具
  • sse 传统 Web 传输(已弃用)
py 复制代码
# client.py

from langchain.chat_models import init_chat_model
from langchain.agents import create_agent
from langchain.messages import HumanMessage
from langchain_mcp_adapters.client import MultiServerMCPClient
import asyncio
from dotenv import load_dotenv

load_dotenv() # 加载环境变量

async def main():
  # 连接本地的 mcp 服务端
  client = MultiServerMCPClient({
    # mcp 名称
    "Math": {
      # transport 和服务端保持一致
      "transport": "streamable_http",
      # 服务端的地址
      "url": "http://127.0.0.1:9000/mcp",
    }
  })

  # 获取 mcp 中的所有工具
  tools = await client.get_tools()

  model = init_chat_model("deepseek:deepseek-v4-flash")
  agent = create_agent(model, tools=tools) # 将工具绑定到 agent
  result = await agent.ainvoke({"messages": [HumanMessage("1 + 2 等于多少?")]})

  for message in result["messages"]:
    print(message.pretty_print())

asyncio.run(main())

客户端连接

通过代码连接的方案,可以看到实际上就是一个对象里面套着一个个的 mcp 服务,每个服务由自己对应的 key 和 value。

现在各种编辑器、包括 cursor | codex | claude code 等都支持接入 mcp

标准协议的全貌实际上是,比如 百度地图 mcp 中 env 就是请求地图 api key

py 复制代码
{
  "mcpServers": {
    # 对于已经发布的包 mcp 使用方案
    "mcp-name-1": {
      "command": "uvx | npx",
      "args": ["arg1", "arg2"...],
      "env": {  # 可选
        "key": "value"
      }
    },
    # 对于已经在线服务提供的 mcp 使用方案
    "mcp-name-2": {
      "type": "http",
      "url": "https://mcp.notion.com/mcp",
      # 对于服务还会有 headers 字段,鉴权、特殊请求头等
      "headers": {
        "key": "value"
      }
    }
  }
}

值得一提的是 uvx | npx 用来远程执行代码的指令;

  • npx 会先查找本地 $PATH 或项目内 node_modules/.bin 有没有对应的可执行内容,没有的话就远程临时安装执行,不会污染本地环境
  • uvx 安装过直接使用,没安装过,就会在一个临时、隔离的虚拟环境中安装工具,并缓存起来以便下次快速调用

Auth0 鉴权

通过第三方的方式鉴权,Auth0 先去注册一个账号,在 Application 中创建一个 API。

APIsApplications 中拿到关键的信息

py 复制代码
# Server.py

from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier

verifier = JWTVerifier(
    # auth.yourcompany.com   Applications -> 指定应用 -> Domain
    # .well-known/jwks.json 固定拼接
    jwks_uri="https://auth.yourcompany.com/.well-known/jwks.json",
    # domain 的地址
    issuer="https://auth.yourcompany.com",
    # APIs 创建时输入的 Identifier
    audience="mcp-production-api"
)

mcp = FastMCP(name="Protected API", auth=verifier)
py 复制代码
# Client.py

client = MultiServerMCPClient({
    "Math": {
      "transport": "streamable_http",
      "url": "http://127.0.0.1:9000/mcp",
      "headers": {
        "Authorization": "Bearer {token}"
      }
    }
  })

# 通过 APIs test tab 页面提供的 curl 获取 token
# curl --request POST \
#   --url https://dev-xsq.us.auth0.com/oauth/token \
#   --header 'content-type: application/json' \
#   --data '{
#     "client_id":"{client_id}",
#     "client_secret":"{client_secret}",
#     "audience":"only-flag",
#     "grant_type":"client_credentials"
#   }'

系统结合

实际场景中,有些场景:比如将现有业务改造为 MCP 强结合,那么实际上这个 MCP 服务的鉴权和角色往往与老系统保持一致。

这种场景下,直接通过 MCP 透传,然后通过中间件鉴权的方案,显然更合理,可能还有更好的方案,不过我不知道...

在 fastmcp 中中间件的执行顺序保持先进后出的顺序

py 复制代码
# Server.py

from fastmcp import FastMCP
from fastmcp.server.auth.providers.jwt import JWTVerifier
from fastmcp.server.middleware import Middleware, MiddlewareContext
from fastmcp.server.dependencies import get_http_headers
from fastmcp.exceptions import AuthorizationError, ToolError

# 模拟用户权限
PERMISSIONS = {
  "add": ["admin", "plain"],
  "sub": ["admin"]
}

mcp = FastMCP(name="Math")

class AuthMiddleware(Middleware):
  async def on_request(self, context: MiddlewareContext, call_next):
    # 获取 headers 中所有内容
    headers = get_http_headers(include_all=True)
    if not headers or "authorization" not in headers:
      # 直接这样会导致客户端程序错误,在 on_call_tool 可以直接校验但是
      # 放行到 on_call_tool 觉得又不合理
      raise AuthorizationError(401, "鉴权错误")

    # 设置上下文信息
    context.fastmcp_context.set_state("role", headers.get('authorization'))
    return await call_next(context)

  async def on_call_tool(self, context: MiddlewareContext, call_next):
    role = await context.fastmcp_context.get_state("role")
    tool_name = context.message.name

    if role not in PERMISSIONS[tool_name]:
      raise ToolError(403, f"该角色 {role} 没有 {tool_name} 权限")   

    return await call_next(context)

# 添加中间件
mcp.add_middleware(AuthMiddleware())
py 复制代码
# Client.py

client = MultiServerMCPClient({
  "Math": {
    "transport": "streamable_http",
    "url": "http://127.0.0.1:9000/mcp",
    "headers": {
      # plain-token
      "Auhtorization": "Bearer admin-token"
    }
  }
})
相关推荐
郑州光合科技余经理1 小时前
海外版多语言团购系统架构:主数据互通与核销边界
java·开发语言·前端·后端·系统架构·php·ai编程
wdloumiga2 小时前
使用 Construct 3零基础做一些2D小游戏
python·游戏·游戏2d
vipxieliang2 小时前
ValidX 的 Date 和 DateTime vs JPA 的 Temporal 对比
java·后端
触底反弹2 小时前
🔥 从「为什么」到「怎么用」:TypeScript 类型约束与泛型完全指南
后端·面试·typescript
用户8356290780512 小时前
使用 Python 查找和替换 Word 文档中的文本
后端·python
Java编程爱好者2 小时前
Spring Boot 实现数据脱敏:自定义注解 + Jackson 序列化器
后端
神奇小汤圆2 小时前
从 ☕️Java Spring Boot → 🦀Rust Axum 体验的真实感受
后端
黑马水牛2 小时前
Carla仿真系列:9_Carla 单目障碍物测距,四种方法原理与实测
经验分享·python·深度学习·计算机视觉·ros·传感器·carla仿真
Python私教3 小时前
多个项目怎么安全合并?适配层、双轨验证与可回滚切换
python·软件架构·系统迁移