在这个教程中,我将向您展示如何使用FastMCP构建一个MCP服务器,如何将您的本地AI代理连接到这个服务器上以便使用其中提供的工具,以及如何为远程MCP服务器添加支持。我们将利用LangChain v1、Ollama、Qwen和Python来完成整个系统的搭建。
模型上下文协议(MCP)是AI代理与各类工具之间的通用交流语言,它是向AI代理提供工具的标准方式。
越来越多的公司开始在现有的API基础上同时提供MCP服务器,因为MCP为大语言模型和AI代理提供了一种标准化的途径,使他们能够直接发现并使用这些功能。
目录
背景知识
许多简单的本地AI代理会将其所需工具直接定义在与其相同的Python脚本中。这类工具是专为特定代理设计的,因此每个新的代理都需要从头开始重新实现这些工具。
MCP通过为这些工具提供统一的接口来解决这个问题——任何兼容MCP的客户端都可以使用这种接口。只要将工具作为MCP服务器编写一次,任何兼容的客户端都可以重复利用它。而且由于MCP是一种网络协议,因此这些工具甚至不需要运行在用户的机器上。其他人可以托管MCP服务器,而用户的代理程序就可以像使用本地工具一样使用这些远程工具。
要学习这个教程,您需要在自己的机器上安装Ollama。本教程适用于macOS、Windows和Linux系统。我使用的是一台配备32GB内存的MacBook Pro,但如果您使用的机器内存较少,也可以选择Ollama中内存要求较低的Qwen模型来运行本教程。
什么是MCP?
MCP(模型上下文协议)是一种开放性协议,它能够向大语言模型客户端提供工具、资源以及提示信息。
就像REST协议标准化了许多Web API一样,MCP也是AI工具的标准化协议。它避免了各个开发框架各自发明不同的工具接口,而是定义了一种通用的接口标准,任何理解这种协议的系统都可以使用任何兼容MCP的服务器所提供的工具。
下面这张来自modelcontextprotocol.io的图片很好地诠释了这个概念。

MCP服务器是一个小型程序,它负责提供一系列工具供使用。而MCP客户端则是任何能够连接到该服务器的程序(例如AI代理),并且能让大语言模型调用这些工具。
常见的MCP服务器连接方式包括:
-
stdio:服务器作为客户端的子进程运行,通过标准输入/输出进行通信。这种方式最适合仅你的代理程序需要的本地工具。
-
http:服务器以HTTP服务的形式运行,客户端通过网络与之连接。这种方式非常适合共享或远程使用的工具。
该协议标准化了工具的暴露方式,这样不同的AI代理程序和客户端就能一致地使用这些工具。
什么是FastMCP?
FastMCP是一个Python库,它让编写MCP服务器变得就像编写FastAPI应用程序一样简单。你只需用@mcp.tool装饰函数,FastMCP就会处理所有的协议细节:JSON-RPC消息的生成、根据类型提示和文档字符串生成工具结构,以及负责传输层的实现。
在LangChain方面,langchain-mcp-adapters这个库可以连接到一个或多个MCP服务器,并将这些服务器提供的工具加载成LangChain v1版本能够直接使用的格式。代理程序根本不需要知道某个工具是运行在你的本地机器上还是远程服务器上,它看到的只是一份包含工具名称和描述的列表而已。
开发动机与架构设计
这个项目背后的动机是为了创建可共享的工具,并复用其他人已经开发的工具。我想要创建像current_time和word_count这样的工具,并让我在构建的每一个代理程序中都能使用它们;同时,我也希望可以利用公共MCP服务器上的工具来完成那些我自己不想编写的功能,比如浏览GitHub仓库。
如果使用本地的LLM模型,那么我的所有对话过程都只会发生在我的本地机器上。唯一会涉及网络操作的部分,就是当模型决定向远程工具发送数据时,或者决定调用这些远程工具的时候。
对于这个项目来说,我会使用FastMCP来构建一个包含两个工具的本地MCP服务器,然后连接到DeepWiki提供的免费公共MCP服务器以查询GitHub仓库信息;接着利用langchain-mcp-adapters将这些工具加载到LangChain v1版本的代理程序中,最后用Ollama模型来运行本地的Qwen模型。
整个流程由三个部分组成:
-
本地MCP服务器是一个独立的Python脚本,它提供了current_time和word_count这两个工具。这个服务器作为代理程序的子进程运行,通过标准输入/输出进行通信。
-
远程MCP服务器是DeepWiki提供的公共服务,它提供了read_wiki_structure、read_wiki_contents和ask_question这三个工具,可以通过HTTP协议来查询任何GitHub仓库的相关信息。
-
代理程序则是负责协调这两个系统的脚本,它会将它们提供的工具合并成一份列表,然后启动交互式流程。
当用户提出问题时,模型会将两台服务器上的所有工具视为一份列表,然后从中选择所需的工具来使用。
步骤 1:安装 Ollama 并下载模型
首先,请为你的平台安装 Ollama 应用程序。
我们将使用 Qwen 作为聊天模型。Qwen 具有原生的工具调用功能,因此它能很好地与 MCP 工具配合使用。我使用的版本是 qwen3.5:4b;如果你的机器内存较少,也可以使用 qwen3.5:0.8b。
ollama pull qwen3.5:4b
步骤 2:安装 Python 相关依赖库
python3 -m venv venv
source venv/bin/activate
pip install fastmcp langchain langchain-core langchain-ollama langchain-mcp-adapters
本教程要求使用 langchain>=1.0.0。
步骤 3:使用 FastMCP 构建本地 MCP 服务器
本地 MCP 服务器提供了两个实用工具:`current_time` 用于获取当前日期和时间,`word_count` 用于统计文本中的单词数量。任何 MCP 客户端都可以使用这些工具,而不仅仅是我们创建的代理程序。
FastMCP 会根据类型提示和文档字符串自动生成每个工具的接口结构,因此文档字符串的编写非常重要——模型在决定是否调用某个工具时,就会参考这些文档信息。
请将上述代码保存到 mcp_server.py 文件中。
from datetime import datetime
from fastmcp import FastMCP
mcp = FastMCP("local-tools")
@mcp.tool
def current_time() -> str:
"""返回当前的本地日期和时间。
当用户询问当前时间或日期时,可以使用这个函数。"""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@mcp的工具
def word_count(text: str) -> int:
"""统计文本中的单词数量。
当用户询问某段文字的长度,或者要求你计算他们分享的内容中的单词数时,可以使用这个函数。
返回的数值为单词总数。"""
return len(text.split()))
if __name__ == "__main__":
# 通过标准输入方式运行 MCP 服务器。
mcp.run()
由于 tools_server.py 会作为子进程在标准输入模式下运行,所以我们不需要单独启动它——代理程序会自动执行这个文件。
步骤 4:代理程序的 Python 代码
代理程序的主要功能有三点:首先,文件开头的配置信息用于指定模型、系统提示语以及远程 MCP 服务器的地址;build_agent() 函数会连接两台 MCP 服务器,将它们的工具收集到同一个列表中,然后创建一个 LangChain v1 代理对象;main() 函数则负责运行交互式循环。
通过 [工具调用] 日志记录,我们可以清楚地看到代理程序在每一步中选择了哪个工具(无论是本地的还是远程的)来执行操作。
最后,由于 build_agent(client) 是异步操作的,因此需要使用 await 来等待像 client.get_tools() 这样的异步请求完成之后,才能返回最终的代理对象。如果没有 await,我们得到的只会是一个协程对象,而不是真正的代理程序。
将这段代码保存到你的`agent_with_mcp.py`文件中:
import asyncio
from langchain.agents import create_agent
from langchain_ollama import ChatOllama
from langchain_mcp_adapters.client import MultiServerMCPClient
# 用于聊天代理的本地Ollama模型。
CHAT_MODEL = "qwen3.5:4b"
# 我们将通过HTTP连接到该远程MCP服务器。
DEEPWIKI_MCP_URL = "https://mcp.deepwiki.com/mcp"
# 这条系统提示语用于告知模型它可以使用哪些工具,以及应该如何响应用户的请求。
SYSTEM_PROMPT = (
"你是一个功能强大的助手,可以使用各种工具来查看当前时间、统计单词数量,或查询关于GitHub仓库的信息。"
"当用户的需求需要你目前还不具备的信息时,就可以使用这些工具。”
"如果某个工具出现了错误,请直接告诉用户,不要用编造的理由重新尝试。”
"如果某个问题并不需要使用任何工具,那就直接给出答案吧。”
)
async def build_agent(client: MultiServerMCPClient):
# 从所有连接的MCP服务器中加载工具。
# 这个过程是异步的,因为MCP通信是通过I/O操作来完成的。
tools = await client.get_tools()
print(f"已加载{len(tools)}个工具:[{t.name for t in tools]}")
# 创建本地Ollama聊天模型。
model = ChatOllama(model=CHAT_MODEL, temperature=0)
# 使用本地模型和所有MCP工具来构建LangChain代理。
return create_agent(
model=model,
tools=tools,
systemprompt=SYSTEM_PROMPT,
)
async def main():
# 创建一个能够连接两个服务器的MCP客户端:
#
# 1. “tools”是一个本地MCP服务器,它是作为子进程通过标准输入/输出方式运行的。LangChain会自动执行`python mcp_server.py`来启动这个服务器。
# 2. “deepwiki”是一个远程MCP服务器,我们是通过HTTP连接到它的。
client = MultiServerMCPClient({
"tools": {
"command": "python",
"args": ["mcp_server.py"],
"transport": "stdio",
},
"deepwiki": {
"url": DEEPWIKI_MCP_URL,
"transport": "streamable_http",
},
})
# 在MCP客户端准备好并且工具加载完成之后,构建代理对象。
agent = await build_agent(client)
print("\n准备就绪!可以向代理提问了。\n")
print("输入‘exit’即可退出。\n")
while True:
question = input("你:").strip()
if not question or question.lower() in {"exit", "quit"}:
break
# 将用户的请求发送给代理。
# 我们使用`ainvoke()`方法,因为代理可能会调用一些异步的MCP工具。
result = await agent.ainvoke({
"messages": [{"role": "user", "content": question}],
})
# 遍历返回的结果中的信息,打印出代理在这一轮中使用的所有工具。
for msg in result["messages"]:
tool_calls = getattr(msg, "tool_calls", None)
if tool_calls:
for call in tool_calls:
print(f"[工具调用] {call['name']}({call['args']})")
# 结果列表中的最后一条信息就是代理的最终回答。
print(f"\n答案:{result['messages'][-1].content}\n")
if __name__ == "__main__":
# 运行这个异步程序。
asyncio.run(main())
步骤 5:运行代理程序
python agent_with_mcp.py
你无需自行启动本地的MCP服务器。MultiServerMCPClient会通过stdio以子进程的形式运行mcp_server.py,同时还会建立与DeepWiki的HTTP连接。如果任意一台服务器无法访问,在程序启动时你会看到错误提示,而不会出现无声的失败处理情况。
一旦代理程序开始运行,你就可以用普通的英语向它提问。在相信它的回答之前,请先观察其调用的工具,确保它确实选择了正确的工具并使用了正确的参数。本地模型通常比托管在远程服务器上的模型规模更小,因此也更容易出现错误答案;定期进行抽查检查会很有帮助。
作为测试,我向代理程序提出了一些问题:
$ python agent_with_tools.py
正在使用“stdio”传输方式启动名为“local-tools”的MCP服务器…… transport.py:210
已加载5个工具:['current_time', 'word_count', 'read_wiki_structure', 'read_wiki_contents', 'ask_question']
准备就绪!可以开始向代理程序提问了。
输入“exit”即可退出。
你:当前时间是多少?
[tool call] current_time({})
答案:当前时间是2026-07-01 16:41:42
你:请简述一下karpathy/nanochat这个仓库的作用。
[tool call] ask_question({'repoName': 'karpathy/nanochat', 'question': 'Please describe this repository.'})
答案:这个名为“karpathy/nanochat”的仓库是一个用于从零开始训练大型语言模型的小型全栈实验系统。它的设计目的是让开发过程更加便捷且成本更低,目前的主要研发方向是优化“Time-to-GPT-2”基准测试指标。
你:法国的首都是什么?
Answer: Paris
对于一个规模仅为40亿参数的本地模型来说,这个代理程序的表现相当不错。对于时间相关的问题,它调用了current_time工具来获取答案;而对于关于nanochat仓库的问题,它则联系了DeepWiki的远程ask_question工具来获得信息。至于法国的首都这个问题,它干脆没有使用任何工具来进行查询。
你可以在MCP服务器注册表中查看更多可用的MCP服务器:https://github.com/modelcontextprotocol/servers
总结
通过本教程,我们使用FastMCP构建了一个MCP服务器,并将其与一个免费的公共远程MCP服务器连接起来。然后,我们利用LangChain v1的create_agent和langchain-mcp-adapters功能,将这两个服务器分别与本地AI代理程序进行了集成。
接下来,你可以尝试在自己的本地服务器上添加更多的工具,比如笔记阅读器或针对其他本地功能的封装模块。也可以让代理程序连接到其他的远程MCP服务器。或者,通过将服务器的传输方式改为HTTP,并在小型服务器上运行它,你就可以在任何设备上使用这个本地服务器了;甚至还可以将其分享给其他人使用。祝你在探索过程中取得成功!
如果你喜欢本教程,可以在我的博客中找到我的更多文章(最近的文章包括一系列系统设计相关的内容),也可以在我的个人网站上了解我的工作进展,或者通过LinkedIn关注我的最新动态。