恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

MCP快速入门实战:统一Agent工具调用,从零搭建Client与Server

  • 首页
  • 资讯中心
  • /
  • MCP快速入门实战:统一Agent工具调用,从零搭建Client与Server

相关资讯

Gartner预测:70%的AI攻击来自供应链,如何用AI TRiSM左移防御 2026/10/15 15:26:20
Qt QImage 加载裸 RGB 数据:构造参数、内存管理与显示优化 2026/10/15 15:26:20
天然气阀门选型与进口阀门核心技术解析 2026/10/15 15:21:20

最新资讯

爆火的 OpenClaw 到底怎么用?完整安装与使用教程(TaoToken 统一 Key 接入版)
layui输入框验证规则全解析:从lay-verify到form.verify的实践指南
AI科研助手赋能论文数据分析:从清洗到三线表的全流程实践
周立功CAN用户手册实战:从零跑通USBCAN收发链路与避坑指南
结婚证双认证公证需要什么材料?证天下一站式申办教程,附避坑指南
单通道盲源分离实战:SSA-ICA原理、Matlab代码与避坑指南

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

MCP快速入门实战:统一Agent工具调用,从零搭建Client与Server

发布时间:2026/10/15 15:26:20
MCP快速入门实战:统一Agent工具调用,从零搭建Client与Server 简介从零到一MCP快速入门实战是一份面向AI应用开发者的MCP入门PDF教程讲解模型上下文协议的技术体系、Function calling演进及大模型Agent开发基础适合想快速掌握MCP客户端与服务器搭建、没有系统接触过工具调用协议的开发者。资料共1个PDF文件压缩包约47.66MB内容按实战路径组织涵盖uv工具使用、MCP极简客户端搭建、接入OpenAI/DeepSeek在线模型以及本地ollama/vLLM模型接入流程并演示天气查询MCP服务器与客户端的完整创建、测试与代码解析。教程还引入MCP Inspector调试工具和Server/Client进阶功能帮助读者打通从协议认知到项目落地的全过程降低后续构建智能体应用时的外部工具集成门槛。目前已有1125人学习/下载适合作为零基础Agent开发、MCP方向系统入门与动手实操参考。1. MCP快速入门实战为什么说它是Agent开发的“书同文”MCPModel Context Protocol模型上下文协议正在成为Agent开发与外部工具之间最值得投入的标准化通道。过去接一个工具要把一个上百行的外部函数、一段JSON Schema格式说明和精心设计的提示词模板绑在一起换个项目又得重写一遍MCP把这套过程压缩成几行客户端代码加一个服务器进程一次封装、到处复用。这篇实战笔记从零开始带你搭一个能跑的MCP客户端再接一个天气查询服务器把协议层、SDK层、联调层都过一遍。适合刚接触Agent开发、想用手搓的方式搞懂MCP到底怎么工作的开发者不需要先读过任何协议源码。2. MCP技术体系Client与Server的职责边界和通信规范2.1 从Function calling到MCP统一协议解决了哪三个成本MCP并不是凭空冒出来的概念它要解决的问题恰恰是Function calling时代留下的三个老大难。大模型本身不能直接操作外部工具所以早期思路是写一个外部函数当中间人大模型负责表达意图这个中间函数负责真正执行。这个设计本身没问题问题出在重复劳动太多。第一个成本是样板代码量。一个简单的天气查询函数从参数校验到错误处理动辄上百行而且每个项目都要重新写一遍类似逻辑。第二个成本是JSON Schema说明。为了让大模型“看懂”每个函数该传什么参数开发者得额外为每个函数写一套结构化的功能说明字段错一个模型就可能在调用时给错参数。第三个成本是提示词模板。Function calling的准确率不只看函数定义还依赖你怎么在系统提示词里描述“什么情况下该调哪个函数”。这三样加在一起接一个工具就要折腾大半天接十个工具工作量直接失控。MCP的核心思路就是“书同文、车同轨”把外部函数的运行环境统一命名为MCP Server把大模型的运行环境统一命名为MCP Client再规定两边之间跑同一种通信格式。客户端发一个list_tools服务器就返回当前全部工具清单客户端发一个call_tool服务器就执行对应工具并返回结构化结果。这套规范一旦统一服务器就能跨项目复用——你今天写好一个天气服务器明天可以直接接到另一个客户端里不用改任何业务代码。2.2 MCP Client与Server的角色分工谁调用谁、参数怎么传搞清楚MCP的架构关键是记住一个方向客户端发起请求服务器响应请求。MCP Client是主动方通常是Agent应用运行时里面跑着大模型和对话逻辑MCP Server是被动方里面注册了外部工具的具体实现。两边不是直接函数调用而是通过传输层交换JSON-RPC风格的请求和响应。常见的传输方式是stdio也就是客户端启动一个服务器子进程通过标准输入输出通信。这种方式最适合本地调试进程生命周期跟着客户端走关掉客户端进程也会被清理。另一种是Streamable HTTP适合把服务器部署到远端让多个客户端共享一个工具服务。在SDK里这两者的连接代码差别主要在一行本地用stdio_client远程用streamable_http_client。参数传递上MCP规定每个工具都要有名称、描述和inputSchema。客户端拿到工具列表后把这段信息拼成大模型能理解的tools参数大模型根据用户提问决定用哪个工具再把这个决定回传给客户端最后由客户端向服务器发起真实调用。这个链路里大模型并不直接碰服务器所有协作都发生在客户端这一层这也是为什么MCP能同时适配在线模型和本地模型。2.3 MCP的生态现状可复用Server与本地模型的边界MCP从发布到现在社区里已经有上千个现成Server覆盖SQL数据库检索、网页爬取、命令行操作、数据分析建模等常见需求。这意味着只要本地运行的大模型支持MCP协议几行代码就能接入这些能力省掉大量造轮子时间。实际使用中我一般会先去社区的Server列表里搜一圈确认有没有能直接用的再决定要不要自己写。不过生态繁荣不代表没有边界。第一你的模型得具备tool calling能力否则工具列表传过去也是白搭。第二MCP Server能访问什么资源取决于它跑在什么环境里——如果直接从网上下载一个来路不明的Server又给它配了数据库地址那等于把敏感资源暴露给未知代码。我建议生产环境只接入自己审查过代码或者可信维护者发布的Server本地测试再随意折腾。第三Server数量多了之后客户端要把工具无限塞进上下文不可行实际项目里通常只挂两三个高频率Server配合动态加载来平衡上下文长度。3. 从零搭建MCP客户端uv环境、极简Client与在线模型接入3.1 先装好uv为什么MCP开发推荐用它管理依赖MCP官方的开发样例普遍基于Python而Python环境管理最大的痛点是依赖冲突MCP相关库又往往依赖最新版本特性用传统pip经常踩到版本对不上的坑。uv是这几年社区里用得越来越多的替代方案它用Rust实现核心目标就是替代pip、venv和pip-tools这三个工具的组合创建虚拟环境、装依赖、跑项目一条龙完成。安装很简单如果你的机器已经有pip直接执行pip install uv如果系统里连pip都没有可以用官方安装脚本curl -LsSf https://astral.sh/uv/install.sh | sh脚本会把uv安装到用户bin目录安装完成后建议先执行uv --version确认版本号。参数上要注意uv默认从系统Python解释器创建虚拟环境想指定版本可以用uv venv --python 3.11 .venvMCP官方SDK目前对3.10到3.13的支持都比较稳定。装好之后常用指令只有几条uv venv创建虚拟环境、uv pip install安装依赖、uv add往项目里加依赖、uv run直接跑脚本。和pip相比uv会读取项目目录下的pyproject.toml做依赖锁定如果你在一个项目目录里反复增删依赖用uv管理要省心很多。这套笔记里的所有MCP项目都推荐先用uv初始化。3.2 极简客户端骨架AsyncExitStack与交互式CLIMCP的客户端设计采用的是异步模型因为客户端同时要跟大模型、服务器两端通信异步能让事件循环统一调度避免阻塞。下面是一个最低可运行版本的MCP客户端它不连接任何真实服务器先跑通代码结构和聊天循环。import asyncio from contextlib import AsyncExitStack from mcp import ClientSession class MCPClient: def __init__(self): 初始化MCP客户端暂不连接具体服务器 self.session None self.exit_stack AsyncExitStack() async def connect_to_mock_server(self): 模拟连接服务器打印一条状态即可 print(MCP客户端已初始化当前未连接真实服务器) async def chat_loop(self): 交互式命令行输入内容后返回模拟回复 print(MCP客户端已启动输入 quit 退出) while True: try: query input(\nQuery: ).strip() if query.lower() quit: break print(f[Mock Response] 你说的是{query}) except Exception as exc: print(f发生错误: {exc}) async def cleanup(self): 退出前释放资源 await self.exit_stack.aclose() async def main(): client MCPClient() try: await client.connect_to_mock_server() await client.chat_loop() finally: await client.cleanup() if __name__ __main__: asyncio.run(main())这段代码里有三个关键点。AsyncExitStack是资源管理器作用是保证客户端在退出时能把session、子进程、网络连接全部关闭不会留下僵尸进程。connect_to_mock_server目前只是占位后面接真实服务器时会在这里建立连接。chat_loop里的无限循环就是Agent的对话主循环未来大模型的推理和工具调用都会挂在这里。整体结构定好后后面所有功能都是用真实逻辑替换这个骨架里的几个函数。3.3 接入在线大模型API把Mock回复替换成真实Completionmock版本跑通之后接下来把在线大模型接进来让客户端不再复读用户输入而是真正理解问题并返回回答。先在项目里补齐依赖在虚拟环境里执行uv add httpx python-dotenv在线请求用httpx这个异步HTTP客户端来发。然后创建.env文件填入服务商相关的配置LLM_BASE_URLhttps://api.example.com/v1 LLM_API_KEYsk-xxxxx LLM_MODELexample-model这里需要说明现在各家在线模型服务商普遍提供OpenAI兼容的接口形态你在哪个平台买的模型就把对应的base_url、api_key、model填进去代码无需改动。接着把client.py里的mock回复部分替换成下面这个请求函数import os import httpx async def ask_llm(messages): 调用在线大模型接口返回模型生成的文本回复 async with httpx.AsyncClient(timeout60) as client: resp await client.post( os.environ[LLM_BASE_URL] /chat/completions, headers{Authorization: fBearer {os.environ[LLM_API_KEY]}}, json{model: os.environ[LLM_MODEL], messages: messages}, ) resp.raise_for_status() data resp.json() return data[choices][0][message][content]这段代码的逻辑是把用户输入的对话历史发给模型模型返回完整响应取choices数组里第一条消息的content字段。参数上timeout60是给长推理留余量防止模型思考时间稍长就被误判为超时messages是标准的历史消息列表每条包括role和content两个字段。接入之后chat_loop里不再直接打印用户输入而是调用ask_llm拿到模型回答再输出。这一步跑通了就说明你的MCP客户端已经具备真实的对话能力后面唯一缺的就是外部工具。3.4 接入本地推理引擎Ollama与vLLM的参数差异本地模型和在线模型的接入方式本质一样都是调用对话补全接口区别只在几个配置项。说去读取.env里的base_url但本地引擎你得改写成进程启动参数和环境变量。下表是我实际联调时的经验值配置项Ollama默认参数vLLM推荐参数base_urlhttp://127.0.0.1:11434/v1http://127.0.0.1:8000/v1模型名自己拉取的tag如qwen2.5:7b启动时指定的model路径或服务名工具调用支持取决于模型是否带tool calling启动时需加--enable-auto-tool-choice关键注意无需额外配置默认开/v1接口必须配--tool-call-parser才能返回结构化工具参数用Ollama接MCP客户端只需在.env里把base_url改成http://127.0.0.1:11434/v1模型名填你本地pull的tag。如果你按上一节的方式用httpx直接发请求Ollama的OpenAI兼容层会把请求转发给本地模型调用方式和在线模型完全一致。vLLM则多一步启动参数在启动vLLM服务时需要加上--enable-auto-tool-choice --tool-call-parser否则即使接口通了模型也不会输出工具调用指令。这一步是最容易忽略的坑很多人连上了vLLM却调不到工具十有八九是启动参数少写了这个开关。4. 天气查询Server实战服务器代码、客户端联调与Inspector4.1 定义天气Server工具声明与返回结构MCP Server的角色是提供工具为了让链路尽量短下面这个server用模拟数据返回天气不需要申请任何API Key专注理解协议本身。创建文件weather_server.py写入from mcp.server.fastmcp import FastMCP mcp FastMCP(weather-server) mcp.tool() def get_weather(city: str) - str: 查询指定城市当前天气这里返回演示用的模拟数据 return f{city} 当前天气多云气温 22℃湿度 55% if __name__ __main__: mcp.run(transportstdio)整个server只有12行靠的是FastMCP这个SDK的封装。mcp.tool()装饰器把普通函数注册成MCP工具函数名、参数名、类型注解和docstring都会被自动解析成工具定义最终转换成上一章提到的JSON Schema格式返回给客户端。mcp.run(transportstdio)表示这个server通过标准输入输出和客户端通信后续客户端会以子进程方式启动它。返回值的类型可以直接写str也可以返回结构化字典客户端那边拿到的是序列化后的JSON对象。这里有个值得强调的设计docstring里那句“查询指定城市当前天气”不是给人看的注释而是会被当作工具描述喂给大模型的关键信息。大模型在决定“该不该调用、调谁”的时候主要就是看这个描述。建议把描述写得跟自然语言一样明确不要只写“天气查询”四个字越具体越能减少误判。4.2 客户端侧如何拿到工具列表并调用server就绪后把之前mock的那版client升级成连接真实server。新建client.py核心连接逻辑变成import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): server_params StdioServerParameters( commandpython, args[weather_server.py], ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: tools await session.list_tools() print(当前可用工具:, [t.name for t in tools.tools]) result await session.call_tool( get_weather, {city: 示例城市}, ) print(调用结果:, result.content) if __name__ __main__: asyncio.run(main())StdioServerParameters定义了如何拉起server子进程command是解释器路径args是入口脚本路径。stdio_client负责建立双向管道返回的read、write两端被注入ClientSession。连上之后第一步调用list_tools()确认工具列表被正常解析出来这一步相当于握手测试第二步调用call_tool执行真实查询传入工具名和参数字典。运行这个客户端看到“调用结果”里出现天气文本就说明MCP通讯链路完全打通。如果你在同一台机器上既开server又开client注意不要让两个测试同时抢stdin最好服务端只用子进程方式启动避免在终端前台手动运行server时阻塞了后续命令。4.3 用MCP Inspector验证Server端问题当客户端和server联调不顺利时与其反复打印日志不如直接用MCP Inspector这个可视化调试工具。它会把server包一层交互面板让你在浏览器里看到工具清单、参数Schema甚至直接点按钮发起调用。安装和启动命令是pip install mcp-inspector mcp dev weather_server.py启动后终端会输出一个本地地址浏览器打开就是调试界面。Inspector最常用的动作有两个。第一个是刷新工具列表确认server注册的get_weather是否出现在面板里第二个是手动填参数调用一次直接看到原始返回结果。如果不带Inspector遇到问题你得在client代码里不断加打印改一轮重启一轮效率低很多。我习惯在写任何新server之后先过一遍Inspector再写客户端。原因很简单先把server本身验证成“黑匣子可用”再排查客户端就只需要关心连接和协议问题面缩小了一半。尤其是当你从社区下载现成server时Inspector是判断这个server能不能用的最快途径基本五分钟内就能确定它是装的还是真能用。5. MCP避坑与常见问题从装依赖到联调的高频翻车点5.1 依赖与网络类uv安装与SDK拉取现象一执行uv add mcp时卡住进度条长时间不动最后报网络超时。原因uv默认使用PyPI官方源网络不稳定时拉包很容易中断尤其在拉取带编译依赖的包时会更明显。解决给uv配置国内镜像源在项目目录创建uv.toml或设置环境变量UV_DEFAULT_INDEX换成可用的PyPI镜像地址然后重跑uv add mcp。如果uv本身没装成功先确认uv --version能输出版本号再继续后续操作。现象二uv add mcp成功但运行时import mcp仍然报ModuleNotFoundError。原因这不是包没装上而是解释器环境乱套了。最常见的情况是你在终端里直接用python client.py跑而我安装的包在.venv虚拟环境里系统的python找不到。解决先跑source .venv/bin/activate激活虚拟环境再用uv run python client.py执行。项目里凡是涉及python命令的地方都统一走uv run基本能避免这类环境错配问题。5.2 联调与调用类模型接入与工具执行现象三Ollama能连上但客户端拿到工具列表后发给大模型模型就是不输出工具调用指令。原因底层的本地模型本身不支持tool calling或者支持的格式和MCP不兼容。Ollama会把工具列表传给模型但模型没有接受过相应训练时根本不会输出结构化工具指令。解决换一个带工具调用能力的模型比如qwen2.5系列带tool的版本或者在Ollama里检测模型是否返回tool_calls字段不返回就说明模型能力不足。现象四调用session.call_tool时报错说找不到对应工具但list_tools明明能看到。原因工具名对不上通常是server和client两边代码不同步。FastMCP默认用函数名当工具名如果你在server里给函数加了name参数自定义了注册名而client还是用原函数名调用就会落空。解决在Inspector里确认实际注册的工具名对比client里的字符串完全一致再试。另外还有一个隐蔽坑server热更新后旧进程没退出新请求被旧进程的旧工具列表接住表现也是找不到工具。解决方法是改完server后彻底停掉旧进程再重新拉client。现象五在Jupyter Notebook里运行asyncio.run()会报“event loop is already running”。原因Jupyter内核自身已经把事件循环跑起来了你的代码不能再主动调用asyncio.run开新循环这是Python异步编程的规范限制。解决在Jupyter里改用await main()直接调度或者先安装nest_asyncio并调用nest_asyncio.apply()把已有循环临时允许嵌套。这个方法只推荐在交互环境里用正式脚本里还是要保持单事件循环的干净结构。6. 把MCP Server做得更实用一个带状态的备忘工具到最后一步给你一个能直接扩到实际项目里的套路在Server里维护内存状态。很多教程写的工具都是无状态的查完即丢但真实Agent需要记住用户偏好、中间结果之类的东西。下面的notes-server用两个工具完成“存备忘”和“查备忘”的最小闭环from mcp.server.fastmcp import FastMCP mcp FastMCP(notes-server) notes [] mcp.tool() def add_note(content: str) - str: 把一条备忘内容存到内存列表里 notes.append(content) return f已保存当前共 {len(notes)} 条 mcp.tool() def list_notes() - str: 列出全部备忘内容 return \n.join(notes) if notes else 还没有备忘记录 if __name__ __main__: mcp.run(transportstdio)这个server里的notes是一个Python内存列表server进程活着数据就活着进程重启数据清零。参数上content是用户想记录的任何文本返回串里带当前条数方便大模型判断是否成功。后面如果想升级只改存储实现就行客户端代码完全不用动。配合一个客户端同时加载多个server时StdioServerParameters可以定义多组用多个stdio_client上下文分别连接然后在同一个ClientSession级别把它们收集起来。当MCP工具多到一定数量建议给每个server加一个命名前缀比如weather:get_weather、notes:add_note这样大模型和人都能一眼看出工具归属。从那以后我每次写完一个MCP Server都会强制走一遍三连验证Inspector里看工具清单、手动call一次、再检查模型是否能根据描述主动选中正确工具。这套流程跑完再接入新客户端基本不会再翻车。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号