返回博客列表
技术文章MCPAgentPythonFastMCPLangChainAI

MCP 的使用与搭建

2026年07月🇨🇳 中文

如果你已经写过一个带工具的 Agent,应该很熟悉下面这套流程:

  1. 为每个工具写一个 Python 函数。
  2. 为函数补一份模型能够理解的参数 Schema。
  3. 把 Schema 交给模型,让模型决定什么时候调用。
  4. 根据模型返回的工具名找到对应函数并执行。
  5. 把执行结果重新放回对话,再让模型继续回答。

工具只有两三个时,这样做没有问题。但当 Agent 需要连接 GitHub、PostgreSQL、Slack、本地文件和公司内部系统时,问题就出现了:每接入一个系统,都要重新编写工具定义、连接代码和结果转换逻辑;换一个 Agent 项目,同样的工作还要再做一次。

MCP 真正解决的是:如何把工具和数据从某个 Agent 项目中独立出来,让不同的 AI 应用都能用同一种方式发现和调用它们。

这篇文章会先把 MCP 的工作方式讲清楚,再介绍从哪里查找现成的 MCP Server,最后使用 FastMCP 搭建一个服务,并把它接入真正的 LangChain Agent。

什么是 MCP

MCP 的全称是 Model Context Protocol,中文通常翻译为“模型上下文协议”。它是一套连接 AI 应用与外部系统的开放协议。

这里的“外部系统”可以是本地文件、数据库、浏览器、代码仓库,也可以是一个通过网络访问的业务服务。MCP 不负责实现这些系统本身,而是约定 AI 应用如何发现它们提供的能力、如何发起调用,以及如何接收结果。

很多介绍会把 MCP 比作 AI 领域的 USB-C。这个比喻强调的是“接口统一”:电脑不需要为每一种外设设计不同形状的插口,Agent 也不应该为每一个外部服务重新设计连接方式。

但 MCP 和 USB-C 有一个很大的不同:Agent 不仅要连接服务,还要理解服务能做什么。因此,MCP Server 除了接收调用,还会向客户端描述:

  • 自己提供了哪些工具;
  • 每个工具解决什么问题;
  • 工具需要哪些参数;
  • 可以读取哪些资源;
  • 可以向用户提供哪些提示词模板。

Agent 在连接 Server 以后,可以先发现能力,再把这些能力动态交给模型,而不是把所有工具硬编码进 Agent。

MCP 的核心架构

MCP 采用 Client-Server 架构,但完整结构里不只有 Client 和 Server,还包含一个 Host。

用户
  ↓
MCP Host(Agent 应用)
  ├── MCP Client A ── MCP Server A ── 本地文件
  ├── MCP Client B ── MCP Server B ── PostgreSQL
  └── MCP Client C ── MCP Server C ── GitHub API

Host:运行 Agent 的应用

Host 是用户直接使用的 AI 应用,也是模型和 Agent 循环真正运行的地方。Claude Desktop、AI IDE、桌面助手,或者我们自己编写的 LangChain Agent,都可以扮演 Host。

Host 负责的事情通常包括:

  • 接收用户输入;
  • 调用大模型;
  • 管理对话上下文;
  • 决定连接哪些 MCP Server;
  • 控制哪些工具可以交给模型;
  • 在敏感操作前询问用户;
  • 把工具执行结果重新交给模型。

MCP Server 不负责决定整个 Agent 的行为。它只提供能力,至于模型什么时候使用、调用后是否继续推理,以及是否需要用户确认,都是 Host 的职责。

Client:Host 内部的协议连接器

MCP Client 运行在 Host 内部,负责和 MCP Server 通信。

当 Host 需要连接三个 MCP Server 时,通常会创建三条独立的 Client-Server 连接。Client 会完成初始化、能力发现、工具调用和消息收发。对 Agent 开发者来说,Client 最重要的作用是把 MCP Server 提供的能力转换成 Agent 可以使用的工具。

后面的代码中,MultiServerMCPClient 就承担这个角色:它连接 MCP Server,读取工具列表,再把这些工具转换成 LangChain Tool。

Server:能力的提供者

MCP Server 是一个独立程序。它可以和 Agent 运行在同一台机器,也可以部署在远程服务器上。

Server 负责两件事:

  1. 描述自己提供的能力。
  2. 收到调用后执行真正的业务逻辑。

例如,一个 GitHub MCP Server 可以提供“查询 Issue”“创建 Issue”“读取 Pull Request”等工具。Agent 不需要知道 GitHub API 的地址、认证头和返回格式,只需要按照 MCP 暴露的工具 Schema 发起调用。

这里要特别注意:MCP Server 不是模型,也不是 Agent。 Server 不会因为用户说了一句话就自行规划任务。它通常只是在 Client 请求某个工具后,执行一次确定的操作并返回结果。

一次连接是如何建立的

Client 连接 Server 后,会先完成初始化,而不是直接调用工具。

初始化阶段双方会交换协议版本、实现信息和支持的能力。比如 Server 会声明自己是否提供 Tools、Resources 或 Prompts,Client 也会说明自己是否支持 Roots、Sampling 等客户端能力。

初始化完成后,Client 才会调用 tools/list 等方法读取能力列表。以后 Server 的工具列表发生变化,也可以通过通知让 Client 重新获取。

从 Agent 开发者的角度,可以把一次完整连接理解为:

建立连接
  → 协商协议版本和能力
  → 获取工具列表
  → 把工具交给模型
  → 模型选择工具
  → MCP Server 执行
  → 结果返回模型
  → 关闭连接

MCP 的协议消息采用 JSON-RPC 2.0。JSON-RPC 定义了请求、响应和通知的基本格式;MCP 则在此之上定义了 initializetools/listtools/callresources/read 等具体方法。

入门阶段不需要手写 JSON-RPC 报文。官方 SDK 和 Agent 适配器会处理这些细节,但理解这层关系有助于排查“连接成功却拿不到工具”或“协议版本不兼容”之类的问题。

MCP 提供了哪些能力

MCP Server 最核心的三类能力是 Tools、Resources 和 Prompts。除此之外,MCP Client 还可以向 Server 提供 Roots、Sampling 等能力。

这些概念看起来都与“给模型更多上下文”有关,但它们的控制方和使用场景不同。

能力提供方主要控制方解决的问题
ToolsServer模型让模型执行操作
ResourcesServerHost 应用向模型提供可读取的数据
PromptsServer用户提供可复用的提示模板
RootsClientHost 应用告诉 Server 应关注哪些目录或 URI
SamplingClientHost/用户允许 Server 请求 Host 调用模型

Tools:让模型采取行动

Tool 是 MCP 中最常见的能力。Server 会为每个 Tool 提供名称、描述和输入 Schema,Client 通过 tools/list 获取它们,通过 tools/call 执行某一个工具。

Tool 被称为“模型控制”的能力,因为通常是模型根据用户问题决定是否调用。例如用户问“SKU-1001 还有多少库存”,模型可以选择 get_product_stock;用户只是说“你好”时,模型则不需要调用任何工具。

Tool 可以只是一个加法函数,也可以执行复杂操作:

  • 查询数据库;
  • 调用第三方 API;
  • 创建工单;
  • 修改文件;
  • 发送邮件;
  • 执行部署。

也因为 Tool 可能修改外部状态,Host 不能把所有决定都交给模型。发送消息、支付、删除数据等高风险操作,应该在执行前增加权限检查或人工确认。

工具描述也不是普通的代码注释。它实际上是写给模型看的使用说明。描述必须告诉模型“什么时候应该使用这个工具”“参数各自代表什么”“工具不会做什么”。描述含糊时,即使函数实现完全正确,Agent 仍然可能选错工具。

Resources:向 Host 暴露可读取的数据

Resource 表示 Server 可以提供给 Host 的内容,例如文件、数据库记录、日志、API 响应或图片。每个 Resource 由 URI 标识,例如:

file:///project/README.md
inventory://products/SKU-1001
logs://production/2026-07-30

Resource 与 Tool 的区别不只是“一个读数据,一个执行动作”。更关键的区别是控制权:Resource 通常由 Host 应用决定什么时候读取、如何展示,以及是否加入模型上下文。

例如,一个 IDE 可以把当前文件作为 Resource 提供给模型;一个知识库客户端可以允许用户手动选择某篇文档。Server 只负责声明和返回资源,最终怎样使用由 Host 决定。

Resources 既可以是固定 URI,也可以使用 URI Template 表达一组动态资源。Server 还可以通知 Client 资源列表或内容发生了变化。

如果你希望模型自己根据问题自动查询某份数据,Tool 往往更合适;如果你希望 Host 或用户明确选择一份上下文,Resource 更符合语义。

Prompts:由用户选择的提示模板

Prompt 是 Server 提供的可复用提示词或工作流模板。它可以接受参数、引用 Resource,也可以生成多轮消息。

例如,代码仓库 MCP Server 可以提供:

/review-pull-request
/explain-module
/generate-release-notes

Prompt 被称为“用户控制”的能力,因为它通常会显示成菜单项或斜杠命令,由用户主动选择。Tool 的目标是让模型决定“调用什么”,Prompt 的目标则是让用户快速启动一套预先设计好的工作流。

并不是所有 MCP Host 都完整支持 Prompts 和 Resources。接入某个客户端前,需要查看它的 MCP 能力支持范围,不能因为 Server 实现了某项能力,就默认所有客户端都能展示。

Roots:告诉 Server 操作边界

Roots 由 Client 提供,用来告诉 Server 当前任务关注哪些 URI。最常见的是项目目录:

file:///Users/example/projects/my-app

代码类 MCP Server 可以据此知道当前工作区在哪里,而不是扫描整个文件系统。一个 Client 也可以提供多个 Root,例如前端仓库和后端仓库。

Roots 主要表达“建议关注的边界”,不应该被当成唯一的安全机制。Server 仍然需要自行验证路径和权限,不能只因为某个 URI 被声明为 Root,就默认其中所有操作都是安全的。

Sampling:让 Server 反过来请求模型

通常的调用方向是 Host 调用 Server。Sampling 则允许 Server 向 Client 请求一次模型生成。

例如,一个数据分析 Server 在完成初步计算后,可能请求 Host 中的模型总结结果,而不需要 Server 自己持有模型 API Key。模型选择、权限控制和用户确认仍然掌握在 Host 手中。

Sampling 适合更复杂的 Agent 工作流,但客户端支持并不统一。对于第一版 MCP Server,先把 Tools 设计好通常更重要。

MCP 的传输方式

协议规定“消息表达什么”,传输层决定“消息怎样从 Client 到达 Server”。

MCP 的消息使用 JSON-RPC 2.0,但 JSON-RPC 可以承载在不同传输方式之上。当前最常见的是 stdio 和 Streamable HTTP,旧项目中还会见到 SSE。

stdio:本地 Server 最常用的方式

stdio 是 standard input/output 的缩写,即标准输入和标准输出。

使用 stdio 时,Host 会按照配置启动一个本地子进程,然后通过 stdin 向进程发送 MCP 消息,通过 stdout 接收响应:

Agent Host
  ├── 启动:uv run server.py
  ├── stdin  ──> MCP Server
  └── stdout <── MCP Server

stdio 不需要监听端口,也不需要额外实现身份认证,非常适合本地文件、Git、SQLite、命令行工具等场景。

它还有一个非常重要的约束:stdout 是协议通道,不能随意 print() 调试信息。 一段普通文本混入 JSON-RPC 消息后,Client 可能无法解析响应。调试日志应该写到 stderr,或者使用日志框架输出到文件。

stdio Server 通常跟随 Host 生命周期:Host 启动它,Host 退出时连接和子进程也随之关闭。它适合用户机器上的个人 Agent,不适合让多台机器共享同一个服务。

Streamable HTTP:远程 MCP 服务

当 MCP Server 需要独立部署、供多个 Agent 使用时,可以通过 Streamable HTTP 提供服务。

Agent A ──HTTP──┐
Agent B ──HTTP──┼──> 远程 MCP Server ──> 企业数据库/API
Agent C ──HTTP──┘

远程 Server 不再由 Host 作为子进程启动,而是长期运行在服务器上。Client 只需要知道 MCP Endpoint,例如 https://example.com/mcp

但 HTTP 并不自动意味着“可以安全部署到公网”。远程 MCP 还需要处理:

  • HTTPS;
  • 身份认证;
  • 用户和租户级授权;
  • 凭据隔离;
  • 超时、重试和限流;
  • 日志、监控与审计;
  • Tool 调用的幂等性。

本地 stdio 示例改成 HTTP 传输,只解决了“能够通过网络连接”,并没有自动补齐这些生产能力。

如何找到现成的 MCP

如果只是想给 Agent 增加 GitHub、浏览器、数据库或文件系统能力,通常不需要从头开发 MCP Server。可以先从下面这些地址查找。

官方 MCP Registry

地址:https://registry.modelcontextprotocol.io/

这是 MCP 项目的官方 Registry,用来收录 Server 的名称、版本、包信息、远程地址和源码仓库。它更接近一个标准化元数据注册中心,适合确认某个 Server 是否已经按官方格式发布。

Registry 的 API 文档位于:

https://registry.modelcontextprotocol.io/docs

如果你需要在自己的 Agent 平台中实现自动搜索或安装 MCP,可以直接消费 Registry API。

MCP 官方参考 Server 仓库

地址:https://github.com/modelcontextprotocol/servers

这个仓库包含 MCP 的参考实现,并链接到一部分已经迁移到各自官方仓库的 Server。它适合学习标准写法,也适合确认某类基础能力是否已经有参考实现。

需要注意,“出现在列表中”不等于“适合直接用于生产”。安装前仍然要进入具体仓库检查维护状态、权限范围和部署说明。

Smithery

地址:https://smithery.ai/

Smithery 提供 MCP Server 搜索、分类、安装说明和托管能力,适合按 GitHub、数据库、浏览器、搜索等使用场景查找 Server。

PulseMCP

地址:https://www.pulsemcp.com/servers

PulseMCP 是一个规模较大的 MCP Server 目录,可以按类别和关键词查找。它适合做广泛发现,但目录收录量大,质量与维护状态需要逐项判断。

Glama MCP Servers

地址:https://glama.ai/mcp/servers

Glama 提供 MCP Server 目录、分类与源码信息,适合搜索社区实现并比较相似 Server。

MCP.so

地址:https://mcp.so/

MCP.so 是社区 MCP 目录,可以按名称和功能搜索 Server,也提供部分安装配置示例。

无论从哪个目录找到 Server,最后都应该回到它的源码仓库或服务商官方文档,确认以下信息:

  • 发布者是不是目标服务的官方团队;
  • 最近一次提交和发布是什么时候;
  • Server 会读取哪些文件、环境变量和凭据;
  • 有哪些 Tool 会修改或删除外部数据;
  • 使用 stdio 还是 Streamable HTTP;
  • 是否需要把 API Key 交给第三方托管服务。

Registry 解决的是“发现”,不是“安全背书”。MCP Server 本质上仍然是一段会在本机运行或接触业务数据的代码。

使用 FastMCP 搭建一个能被 Agent 调用的 MCP Server

这一节不再把“Client 能调用 Tool”当成终点。我们的目标是完成下面这条真实链路:

用户询问库存
  → Agent 获取 MCP Tools
  → 模型选择 get_product_stock
  → LangChain MCP Adapter 调用 Server
  → Server 查询库存并返回
  → Agent 根据结果回答用户

我们会创建两个文件:

mcp-inventory/
├── server.py   # FastMCP Server,提供库存工具
└── agent.py    # LangChain Agent,加载并使用 MCP 工具

示例使用 OpenAI 模型。换成其他支持 Tool Calling 的 LangChain Chat Model 时,MCP Server 不需要修改,这正是解耦带来的价值。

1. 创建项目并安装依赖

uv init mcp-inventory
cd mcp-inventory

uv add "mcp[cli]" langchain langchain-openai langchain-mcp-adapters

设置模型密钥:

export OPENAI_API_KEY="你的 API Key"

实际项目应把密钥放在安全的环境变量或密钥管理服务中,不要写进代码或提交到 Git。

2. 使用 FastMCP 实现库存工具

创建 server.py

from mcp.server.fastmcp import FastMCP


mcp = FastMCP("inventory")

PRODUCTS = {
    "SKU-1001": {
        "name": "机械键盘",
        "stock": 8,
        "warehouse": "上海仓",
    },
    "SKU-1002": {
        "name": "无线鼠标",
        "stock": 0,
        "warehouse": "北京仓",
    },
}


@mcp.tool()
def get_product_stock(sku: str) -> dict:
    """查询指定 SKU 的当前库存。

    当用户询问某个商品是否有货、剩余数量或所在仓库时使用。
    sku 必须是类似 SKU-1001 的完整商品编号。
    此工具只查询库存,不会创建、修改或扣减库存。
    """
    normalized_sku = sku.strip().upper()
    product = PRODUCTS.get(normalized_sku)

    if product is None:
        return {
            "found": False,
            "sku": normalized_sku,
            "message": "没有找到该 SKU",
        }

    return {
        "found": True,
        "sku": normalized_sku,
        **product,
    }


@mcp.tool()
def list_out_of_stock_products() -> list[dict]:
    """列出当前所有库存为 0 的商品。

    当用户询问哪些商品缺货、需要补货或库存为零时使用。
    此工具只返回缺货商品,不会自动创建补货单。
    """
    return [
        {"sku": sku, **product}
        for sku, product in PRODUCTS.items()
        if product["stock"] == 0
    ]


if __name__ == "__main__":
    mcp.run(transport="stdio")

@mcp.tool() 会根据函数签名生成输入 Schema。sku: str 会变成字符串参数,函数返回值则作为 Tool Result 返回给 Client。

这个示例的 docstring 比普通函数注释更详细,因为它的读者不是另一个 Python 开发者,而是模型。模型需要从描述中判断:

  • 用户表达什么意图时应该调用;
  • 参数应该使用什么格式;
  • 工具能做什么;
  • 工具不会产生哪些副作用。

get_product_stocklist_out_of_stock_products 看起来都与库存有关,但触发条件不同。把边界写清楚,可以减少模型在两个工具之间选错的概率。

返回值也使用了结构化字典,而不是一句模糊文本。这样 Agent 能明确区分 foundstockwarehouse,后续还可以根据 stock == 0 决定是否建议补货。

3. 在 Agent 中加载 MCP Tools

创建 agent.py

import asyncio
from pathlib import Path

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


async def main() -> None:
    server_path = Path(__file__).with_name("server.py").resolve()

    mcp_client = MultiServerMCPClient(
        {
            "inventory": {
                "command": "uv",
                "args": ["run", str(server_path)],
                "transport": "stdio",
            }
        }
    )

    # 连接 MCP Server,读取 tools/list,
    # 并转换成 LangChain Agent 可以使用的 Tools。
    tools = await mcp_client.get_tools()

    agent = create_agent(
        model="openai:gpt-4.1",
        tools=tools,
        system_prompt=(
            "你是库存助手。回答库存问题时必须使用库存工具,"
            "不要根据商品名称或常识猜测库存。"
        ),
    )

    result = await agent.ainvoke(
        {
            "messages": [
                {
                    "role": "user",
                    "content": "SKU-1002 还有货吗?如果没有,请告诉我它在哪个仓库。",
                }
            ]
        }
    )

    print(result["messages"][-1].content)


if __name__ == "__main__":
    asyncio.run(main())

运行 Agent:

uv run agent.py

一次实际运行中,Agent 的内部过程是:

1. MultiServerMCPClient 启动 server.py
2. Client 与 FastMCP Server 完成初始化
3. Client 通过 tools/list 获取两个库存工具
4. Adapter 把 MCP Tools 转换成 LangChain Tools
5. create_agent 把这些工具交给模型
6. 模型根据用户问题选择 get_product_stock
7. Agent 通过 MCP 调用工具,参数为 {"sku": "SKU-1002"}
8. Server 返回 stock=0、warehouse=北京仓
9. 工具结果进入 Agent 消息历史
10. 模型生成最终回答

最终回答会类似:

SKU-1002(无线鼠标)目前库存为 0,已经缺货。该商品归属北京仓。

这里和上一版“手动调用 session.call_tool()”最大的区别是:我们没有在 Agent 代码中指定要调用哪个工具。

session.call_tool("get_product_stock", ...) 只能证明 Client 和 Server 能通信;现在的代码则是由模型理解用户问题、选择 Tool、生成参数,再由 Agent 执行并继续推理。这才是 MCP Tool 在 Agent 中的完整使用方式。

4. MCP 在 Agent 代码中替代了什么

如果不使用 MCP,我们需要在 agent.py 中导入库存函数,再用 LangChain 的 @tool 包装它。换到另一个 Agent 项目时,还要复制同一份业务代码。

使用 MCP 后,Agent 只关心:

tools = await mcp_client.get_tools()
agent = create_agent(model="openai:gpt-4.1", tools=tools)

库存查询的实现、输入校验和返回格式都留在 Server 中。如果以后把内存字典换成 PostgreSQL,只要工具名称和 Schema 保持兼容,Agent 侧不需要修改。

另一个 Agent 也可以连接同一个 Server。它可以使用不同模型、不同 System Prompt,甚至使用 LangGraph 编排更复杂的工作流,而库存能力仍然只维护一份。

5. 同时连接多个 MCP Server

MultiServerMCPClient 不只可以连接一个 Server。下面的配置同时连接本地库存 Server 和远程物流 Server:

mcp_client = MultiServerMCPClient(
    {
        "inventory": {
            "command": "uv",
            "args": ["run", "/absolute/path/server.py"],
            "transport": "stdio",
        },
        "shipping": {
            "url": "https://shipping.example.com/mcp",
            "transport": "http",
        },
    }
)

tools = await mcp_client.get_tools()

加载完成后,Agent 可以先查库存,再查询物流时效。Agent 不需要知道两个工具来自不同进程、使用不同传输方式,适配器会把它们统一转换为 LangChain Tools。

但工具并不是越多越好。一次给模型几十甚至上百个名称相似的 Tool,会增加上下文消耗和选择错误。生产系统通常需要按用户权限、当前任务和业务域筛选工具,而不是把所有 Server 的所有能力一次性交给模型。

如何设计一个 Agent 真正好用的 MCP Tool

FastMCP 可以让一个普通 Python 函数很快变成 Tool,但“能够调用”和“Agent 能够正确使用”是两件事。

工具应该围绕 Agent 的任务来设计

传统 API 往往按照后端资源组织,例如:

GET /products
GET /warehouses
GET /inventory-records

Agent Tool 更适合围绕用户任务组织。例如用户想知道商品是否有货,一个 get_product_stock Tool 直接返回商品、数量和仓库,通常比让 Agent 依次调用三个底层接口更可靠。

工具过细会让 Agent 承担大量编排工作;工具过大又会让行为不透明、难以控制。一个实用的判断标准是:一次 Tool 调用应该完成一个清晰、可描述、可验证的用户意图。

名称和描述要能区分相似工具

query_datarun_task 这类名称对模型几乎没有帮助。更好的名称应该直接表达动作和对象:

get_product_stock
list_out_of_stock_products
create_inventory_restock_request

描述中还要明确使用条件和限制。如果两个 Tool 的描述大量重叠,模型就更容易选错。

返回模型完成任务所需的上下文

Tool Result 不能只考虑程序是否能解析,还要考虑模型下一步需要什么信息。

如果创建补货单后只返回 success,模型无法告诉用户创建了哪一张单、补了什么商品,也无法在下一步查询状态。更合理的结果至少应包含:

{
  "success": true,
  "request_id": "RESTOCK-2048",
  "sku": "SKU-1002",
  "quantity": 50,
  "status": "pending"
}

但返回内容也不是越多越好。把完整数据库记录或几十 KB 日志都塞进 Tool Result,会迅速占满模型上下文。应该优先返回与任务有关的字段,对长内容进行分页、截断或提供 Resource URI。

错误也要对模型有意义

“Internal Server Error”只能告诉 Agent 调用失败,却不能帮助它决定下一步。

更好的错误应说明:

  • 什么地方出错;
  • 是否可以重试;
  • 参数应该如何修改;
  • 是否需要用户补充信息;
  • 是否因为权限不足而不能继续。

例如:

{
  "error": "invalid_sku",
  "message": "SKU 必须使用 SKU-数字 的格式,例如 SKU-1001",
  "retryable": true
}

模型看到这种结果后,可以修正参数或向用户询问,而不是重复提交同一个错误请求。

对有副作用的工具保留人工确认

查询库存是只读操作,创建采购单、扣减库存和取消订单则会改变外部状态。

Server 应该验证输入和权限,Host 也应该在高风险工具执行前展示工具名和参数,让用户确认。不能仅凭“模型应该不会乱调用”来保证安全。

常见问题

Agent 找不到 MCP Tool

先确认 server.py 能通过配置中的同一条命令启动。stdio 场景下最常见的问题不是 Tool 代码,而是 Python 环境、相对路径和工作目录不一致。

在桌面应用或 Web Server 中,进程继承到的 PATH 可能与终端不同。必要时使用 uv、Python 和 Server 文件的绝对路径。

Server 启动后立即退出

通常是缺少依赖、脚本路径错误或初始化期间抛出异常。stdio Server 的错误日志应查看 stderr,不要为了调试在 stdout 中加入 print()

Client 能列出 Tool,但模型不调用

这说明 MCP 连接大概率没有问题,问题在模型决策层。检查:

  • Tool 名称是否表达了明确动作;
  • docstring 是否说明了使用时机;
  • 参数说明是否足够清楚;
  • System Prompt 是否与工具行为冲突;
  • 是否一次给模型提供了太多相似工具。

Tool 被调用了,但参数经常错误

尽量使用明确的类型、枚举和格式说明,并在 Server 中做输入验证。对于模型无法从用户问题中推断的必填信息,不要期待模型凭空生成,应返回可理解的错误或让 Agent 向用户追问。

应该使用 Tool 还是 Resource

如果模型需要根据任务自动发起查询或执行动作,使用 Tool。

如果内容更像一份可以浏览、选择和加入上下文的资料,使用 Resource。

如果希望用户从菜单或斜杠命令启动一套固定工作流,使用 Prompt。

总结

MCP 的核心不是 @mcp.tool() 这个装饰器,而是把 Agent 与工具实现之间的连接方式标准化。

MCP Server 负责描述和执行能力,MCP Client 负责连接与调用,Host 则运行模型和 Agent 循环。Tools、Resources、Prompts 分别服务于模型控制、应用控制和用户控制;Roots 与 Sampling 则让 Client 能够向 Server 提供边界和模型能力。

在真正的 Agent 中,完整过程不是“手动调用一次 MCP Tool”,而是:

发现 Tools
  → 交给模型
  → 模型选择 Tool
  → MCP 执行
  → 结果返回模型
  → Agent 继续推理

当你理解了这条链路,就能清楚地判断 MCP 在系统中的位置:它负责让能力可以被发现、连接和复用;至于 Agent 如何规划、何时调用、是否需要确认,以及怎样组合多个工具,仍然由 Host 中的 Agent 逻辑决定。