资讯动态

从零开发MCP Server:用Python实现AI Agent工具调用与知识库接入

发布时间:2026/9/17 4:10:25 来源:尧图企业网站定制
大概半年前我还在为每一个 AI Agent 项目手写 Function Calling 的壳子模型每说要调一个工具我就得补一段解析逻辑每换一个供应商之前的适配代码基本作废。后来我把一个内部知识库工具改成了 MCP Server接入 Claude、接入自研 Agent几乎是一套代码到处用。今天这篇就算是一条龙记录从 MCP 和 AI Agent 的基本理论到怎么用 Python 从零开发一个可运行的 MCP Server再接到真实客户端里跑通“让大模型替你调用工具”的完整链路。这篇文章做的事很具体讲清楚 MCP 协议到底解决了什么、为什么 AI Agent 开发离不开它然后一步步实现一个带搜索、新增、阅读功能的本地知识库 MCP Server最后教你调试、排错以及往生产环境演进时要注意什么。适合正在做 AI Agent无论你用 LangChain、LangGraph、Spring AI 还是自研框架的开发者也适合想把业务系统能力安全开放给大模型的团队。1. MCP 到底是什么AI Agent 开发者需要先想清楚的事1.1 补课AI Agent 的工具调用困局想象一下只靠大模型本身它是没法去查你的数据库、发工单、改文件权限的。模型是“读上下文、预测输出”的机器没有手也没有眼睛。为了让模型完成真实任务OpenAI 最早设计了 Function Calling让模型输出一个结构化的工具调用请求再由程序执行。这确实解决了一部分问题但很快大家发现工具接入这件事被做成了“熔炉测试”每个模型厂商一套工具声明格式每个 Agent 框架又有一层自己的抽象每接一个内部系统就得写一次适配层。更麻烦的是同一个工具在新模型上可能需要重新定义提示词和参数描述否则模型“不会用”。我见过不少团队Agent 代码里一半是工具适配器另一半是解析不同模型返回结果的补丁。内部的搜索 API、工单系统、审批流每个都要一份单独的 schema 解析测试成本成倍上涨。这就是 MCP 出现之前最常见的工具调用困局不是模型不够强而是工具接入的“物理链路”太乱。1.2 MCP 的核心架构与工作原理MCP 的全称是 Model Context Protocol翻译过来就是“模型上下文协议”。它的思路很简单把工具、数据、提示词都变成可以动态发现和调用的资源用一份统一的协议把它们暴露给大模型应用。类比一下最直观传统做法是给每个外设都焊一根专用线鼠标口、键盘口、打印机口各不兼容MCP 想做的是 USB-C所有设备都用同一个接口标准插上就能认。角色上分三层。Host 是大模型应用比如 Claude Desktop、IDE 插件、自研 AgentClient 是 Host 内部负责跟单个 Server 通信的模块Server 暴露具体能力。协议底层是 JSON-RPC 2.0消息有明确的生命周期。连接建立后先initialize双方交换能力信息接着客户端会主动list_tools、list_resources、list_prompts把能力列表拉到本地真正要用时再发一个call_tool请求。整个过程对模型透明模型只需要按照工具名称和参数 JSON 去“请求”具体怎么执行是 Server 的事。传输上最常见的是 stdio也就是 Host 直接拉起一个子进程跑 Server简单、安全、适合本机跨机器部署则用 Streamable HTTP 或 SSE本质上就是把 MCP 消息包装成 HTTP 请求。无论哪种传输MCP 的核心能力域是一致的Tools 是可执行操作Resources 是只读上下文Prompts 是提示词模板Sampling 和 Roots 则是更进阶的能力日常开发前三个就够用了。1.3 MCP 能给 AI Agent 带来什么MCP 最实际的价值是解耦。拿同一个 MCP Server 来说接入 Claude Desktop 和接入自研的 Agent 框架配置完全不同但 Server 代码不用改。只要对方实现了 MCP Client工具描述、参数类型、输出格式都是“自带说明书”的。这等于把工具接入从“定制开发”降级成了即插即用。第二是动态能力发现。MCP Server 在启动时告诉客户端自己有哪些工具、资源、提示词模板客户端基于这份清单决定让模型去调用什么。你新加一个工具旧客户端只要重新加载就能看到不需要升级部署。第三是上下文控制。工具返回的内容由 Server 决定可以只回摘要、状态码避免把整个数据库倒进 Prompt 里。最后是权限边界Server 可以严格限制只暴露白名单操作大模型再聪明也没法调用没暴露的方法。但它也不是万能的。MCP 适合有一定工具复杂度、需要跨客户端复用的场景如果只是给 Prompt 塞一两段静态知识完全用不到 MCP。这个判断标准后面实操部分还会再强调。2. 动手前先把方案定下来设计一个知识库 MCP Server2.1 选语言和 SDKPython 还是 TypeScriptMCP 官方提供 Python 和 TypeScript SDK体验已经很成熟。选哪个主要看团队技术栈。如果是 Java 后端团队也有 Spring AI 等封装方式但需要留意版本更新节奏我更推荐先从官方 SDK 跑通逻辑再考虑框架集成。维度Python SDKTypeScript SDK上手成本低FastMCP 封装非常友好中等类型定义更严格适合场景数据类工具、机器学习服务、内部脚本前端生态、Node 服务、CLI 工具部署方式pip 包要求 Python 环境npm 包Node 环境周边生态文档全面Inspector 支持好文档全面Playground 常用对于本文示例我选 Python因为它代码量最少几乎不需要样板代码适合把注意力放在理解协议本身。你不需要担心 Python 版本3.9 以上都行。2.2 拆解业务需求这个 Server 到底提供哪些工具先把需求讲清楚。我们要做的“知识库 MCP Server”解决的是两种高频场景一是让 Agent 在回答之前先检索团队积累的文档、笔记把检索结果作为上下文补充进回复二是让 Agent 能把新想法“存”回知识库形成闭环。围绕这两个场景我设计了三个工具和一个资源、一个提示词模板。名称类型说明输入参数输出search_docstool检索与关键词匹配的文档片段query: string, top_k?: intMarkdown 文本add_notetool新增一条知识库笔记title: string, content: string保存成功提示recent_notesresource返回最近保存的笔记标题列表无Markdown 列表summarize_noteprompt生成“总结笔记”的提示词模板title: string文本 Prompt这里资源是只读的工具是可执行的。Agent 在调用前会先通过能力列表发现它们不需要我们额外写说明文件。之所以把资源单独拆出来是为了让模型在不需要执行副作用的情况下也能拿到“最近有哪些笔记”这类轻量信息。2.3 接口设计里容易踩的坑设计工具接口时最容易犯的错误是“只写工具名不写工具描述”。模型靠描述判断什么时候该调用含糊的描述会让它在最该调的时候不调在最不该调的时候乱调。我一般把描述写成“返回给谁看、输入是什么、输出是什么”三句话比如 search_docs 的描述是“在本地知识库中检索与 query 相关的文档片段返回 Markdown 格式的结果用于回答问题时补充事实依据。”第二个坑是参数类型和必填约束不明确。MCP 走 JSON-RPC参数本质上是一个 JSON 对象Agent 会根据 schema 构造参数。如果可空字段没标好调用时就会缺参或者多参。尽量给每个参数写一个 exampleAgent 见过示例后成功率会高很多。第三个坑是错误信息不能太“人类化”。内部抛出的异常要转成可以被 Agent 理解的错误描述比如“数据库连接失败请稍后重试”而不是一长串 traceback否则模型会把这堆日志当成正常回复的一部分很影响后续判断。3. 实战用 FastMCP 半小时做一个可用的 Server3.1 环境准备与项目初始化先准备虚拟环境我习惯用 venvpython -m venv .venv source .venv/bin/activate # Windows 下是 .venv\Scripts\activate pip install mcp[cli]装好之后你会在 bin 里拿到 mcp 命令。这个命令除了启动服务还能调用官方 Inspector 打开调试面板非常有用。项目结构不用复杂knowledge-base-server/ ├── .venv/ ├── server.py └── data/ └── notes.json我给示例项目准备了一个 notes.json存两条种子数据方便测试时能搜到结果。3.2 实现工具、资源和提示词在 server.py 里写核心逻辑# server.py import json from pathlib import Path from mcp.server.fastmcp import FastMCP DATA_FILE Path(__file__).parent / data / notes.json mcp FastMCP(knowledge-base) def _load_notes() - dict: if DATA_FILE.exists(): return json.loads(DATA_FILE.read_text(encodingutf-8)) return {} def _save_notes(notes: dict) - None: DATA_FILE.write_text(json.dumps(notes, ensure_asciiFalse, indent2), encodingutf-8) mcp.tool() def search_docs(query: str, top_k: int 3) - str: 在本地知识库中检索与query相关的文档片段返回Markdown格式的结果用于回答问题时补充事实依据。 notes _load_notes() query_lower query.lower() hits [title for title, content in notes.items() if query_lower in title.lower() or query_lower in content.lower()] hits hits[:top_k] if not hits: return 没有找到相关内容请尝试更换关键词。 return \n\n.join([f### {title}\n\n{notes[title]} for title in hits]) mcp.tool() def add_note(title: str, content: str) - str: 保存一条新的知识库笔记title为完整标题content为笔记正文返回保存状态。 notes _load_notes() notes[title] content _save_notes(notes) return f已保存笔记{title} mcp.resource(notes://recent) def recent_notes() - str: 返回最近保存的笔记标题列表按保存顺序倒序展示。 notes _load_notes() if not notes: return 当前知识库为空。 return \n.join([f- {title} for title in list(notes)[-10:]]) mcp.prompt() def summarize_note(title: str) - str: 生成一个用于总结指定笔记的提示词模板。 notes _load_notes() content notes.get(title, 未找到该笔记) return f请阅读以下笔记并给出三句话总结和三件事项建议\n\n标题{title}\n\n内容{content} if __name__ __main__: mcp.run()这段代码有几个细节值得展开。第一所有读写都走 JSON 文件避免引入外部依赖真实场景可以替换成 SQLite 或向量数据库。第二FastMCP 会根据函数签名自动生成工具 schema包括参数类型、描述和必填项所以代码里一定要写类型标注和 docstring。第三mcp.resource(notes://recent)这个 URI 是给客户端读取用的Agent 可以通过资源接口直接拿到“最近笔记”列表而 prompt 模板则是给模型预置提示词让模型知道该怎么答、怎么总结。工具只暴露了两个但已经覆盖“查”和“存”两个核心动作。你可以照着这个模式继续加工具比如删除记录、更新标签、导入文件等。MCP 本身不限制工具数量但建议一个 Server 保持内聚一个领域一个 Server别把所有功能堆在一起。3.3 本地启动与命令行自测写完先别急着接前端直接在终端启动python server.py正常会看到一段日志提示 MCP server running。到这一步已经算把最小的 MCP Server 跑起来了。但怎么验证它能不能被 Agent 正确调用我推荐用官方 Inspectormcp dev server.py这个命令会拉起一个本地调试面板你可以在里面看到所有已注册的 tools/resources/prompts还能直接模拟调用 search_docs。我每次写完新工具都会先用它做一次冒烟测试确认 schema 描述足够清晰再交给客户端。如果不想开浏览器也可以写一段 Python 脚本直接发起调用下一节会给出最小 Client 的写法。这里补充一个容易忽略的点MCP Server 在 stdio 模式下启动后只从标准输入读、往标准输出写所以不要在代码里随便 print 日志这会污染协议数据。想要更详细的运行日志请使用 logging 模块输出到 stderr很多客户端也默认看 stderr 的日志来定位问题。4. 把 MCP Server 接进 Claude Desktop 和其他 Agent 客户端4.1 配置 Claude Desktop三分钟跑通Claude Desktop 当前支持在配置文件里声明 MCP Server。以 macOS 为例配置文件一般在~/Library/Application Support/Claude/claude_desktop_config.jsonWindows 则在%APPDATA%\Claude\claude_desktop_config.json。打开后加入{ mcpServers: { knowledge-base: { command: python, args: [/absolute/path/to/server.py] } } }这里必须填绝对路径尤其是python命令如果系统里有多个 Python 环境建议直接写虚拟环境里的解释器路径例如/path/to/.venv/bin/python否则 Claude 可能找不到依赖包。配置保存后完全退出 Claude 再重新打开左下角工具图标里就能看到 knowledge-base 下面的工具列表了。一个小技巧配置成功后先在对话框里直接问一句“帮我搜索最近的知识库笔记”它会自动调用 recent_notes 资源或 search_docs 工具。如果报错打开 Claude 的日志文件它会同时记录启动 MCP Server 时 stderr 的输出很多问题都在这里直接体现。4.2 用 MCP Client SDK 写一个最小调用端不只有商业客户端可以用自己写 Agent 也可以直接消费 MCP Server。官方 Python SDK 里提供了 ClientSession下面是一个最小示例# client.py import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): server_params StdioServerParameters(commandpython, args[server.py]) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools await session.list_tools() print(发现工具, [t.name for t in tools]) if any(t.name search_docs for t in tools): result await session.call_tool(search_docs, {query: MCP}) print(result) if __name__ __main__: asyncio.run(main())这段代码里stdio_client 负责把 MCP Server 作为子进程拉起并建立标准输入输出管道ClientSession 封装了 JSON-RPC 消息的发送和接收。跑通这个脚本说明你的 MCP Server 对任何 MCP Client 都可用而不是只能被 Claude 识别。真正做 Agent 时你只需要把 list_tools 的结果转成大模型能读的工具列表把 call_tool 的返回作为工具结果回传给模型就能实现“Agent 自主调用工具”的闭环。4.3 远程部署与 HTTP 模式要点如果知识库要放在服务器上让公司多台电脑的 Agent 都能访问就得切换到 HTTP 模式。FastMCP 也直接支持if __name__ __main__: mcp.run(transportstreamable-http, host0.0.0.0, port8000)这样会暴露一个 HTTP 端点客户端在配置里填上 URL 即可。但远程暴露要注意两点一是必须加认证最简单的方案是在服务层校验 Bearer Token或者让 MCP Server 处于内网只允许公司网络访问二是要处理超时配置Agent 调用工具可能要持续几秒不要把超时设得太短。如果走的是外网强烈建议用 HTTPS 加密传输避免敏感数据明文散落。5. 常见问题与排查技巧实录5.1 连接类问题速查表我整理了实际调试 MCP Server 过程中最容易遇到的几个问题按“现象-原因-解法”列在下面现象可能原因处理建议客户端看不到任何工具服务启动失败或 schema 生成异常先用mcp dev server.py看错误日志工具列表存在但调用报错参数 schema 与实际函数签名不匹配检查工具定义是否缺少类型标注或 docstring调用一直卡住不返回工具内部同步阻塞或超时时间太短耗时的 IO 改成异步或调大客户端超时时间返回内容被模型理解错返回格式太复杂尽量返回 Markdown 或纯文本限制长度端口启动失败端口被占用换端口用lsof -i:端口查占用进程配置 Claude 后不生效未完全退出重启或配置路径不对强制退出 Claude 重开检查日志文件表格之外还要说一个重要原则MCP Client 不会帮模型做“拼装”模型看到的工具描述和返回内容完全来自 Server所以 Server 的返回要尽量“自解释”。比如返回“没有找到相关内容”时最好顺带补一句“请尝试更换关键词或查看最近笔记”模型就会接着做下一步。5.2 我在调试中踩过的土坑第一个坑是 Python 环境的绝对路径。我有一次配置 Claude Desktop 时 command 写成了python但那个终端会话里激活的是某虚拟环境Claude 子进程却用的是系统 Python结果找不到所有依赖。后来统一写成.venv/bin/python的绝对路径问题立刻消失。第二个坑是工具描述太简短。早期我只写“搜索笔记”模型在回答简单问题时偶尔会跳过工具、直接编答案把描述改完整后调用准确率高了很多。第三个坑是误用 print 调试。前面提过stdio 模式下所有 stdout 都算作协议数据一次调试时我在工具里 print 了一行日志导致客户端无法解析后续 JSON-RPC 消息表现就是工具调用返回空。排查半天才想到是 print 的锅改用 logging 到 stderr 后就没再犯过。这些“土办法”看着不起眼但能帮你省一晚上。另外一个建议在正式交付前一定要用两个不同客户端各测一遍很多“只能在我的 Agent 里用”的 Server换到其他客户端就暴露了过度依赖某个 Host 内部行为的隐藏问题。6. 进阶从单 Server 到多 Agent 生产级实践6.1 多 Server 和多 Agent 如何共存实践中一个 AI Agent 经常要同时使用多个 MCP Server比如一个是知识库一个是设计稿读取一个负责统计数据。MCP Host 在配置层面天然支持多 Server每个 Server 有独立的命名空间Client 会分别连接工具列表也按来源分开。这个设计很关键不同 Server 之间不会互相污染命名同一个工具名在不同 Server 里也可以同时存在。自研 Agent 里我推荐在发起模型请求前把多个 Server 的 tools 合并成一个列表但给每个工具名前加上 Server 前缀或者显式记录工具来源提示词里说明“哪个工具属于哪个服务”避免模型选错。多 Agent 场景也类似。你完全可以让“数据分析 Agent”和“文档整理 Agent”共享同一个 MCP Server也可以给每个 Agent 配各自的 Server。MCP 不是 Multi-Agent 框架但它是 Agent 之间共享能力的底层协议配合 LangGraph 或自研编排时MCP Server 就是每个 Agent 的“手和脚”。垂直领域里Figma MCP 把设计稿数据结构化暴露给 AgentBlender MCP 让模型能操作 3D 建模这些案例的思路都一样把专业工具封装成标准能力再交给大模型调度。你的业务系统只要想清楚要暴露哪些能力完全可以照着这个模式接入。6.2 生产环境还需要的三件事认证、观测、版本管理本地调试可以不管安全但生产环境把 MCP Server 开放给多个 Agent 时至少要补三块。第一是认证与授权。HTTP 传输模式下必须校验调用方身份工具粒度上做白名单在很多公司里“允许 Agent 读知识库”和“允许 Agent 改知识库”是两码事最好是工具级权限。第二是可观测性。每一次工具调用都要有日志记录调用方、时间、参数、返回码最好加上耗时否则出了错根本没法复盘。第三是版本管理。MCP Server 本身也是一个软件工具签名和行为变更应该走版本发布流程客户端初始化时会做能力协商但为了兼容老客户端建议工具接口保持向后兼容非必要不删除已有工具。我见过不少团队直接把本地 demo 推到服务器上结果没有认证日志一个幻觉生成的 Agent 请求把测试数据全改掉了。所以生产化不是功能做完再说的事而是从设计接口那天就要考虑进去。6.3 什么时候不要用 MCP最后说点冷静话。MCP 不是所有工具接线的银弹简单的场景滥用反而增加复杂度。比如你只是给某个固定 Prompt 塞一段当前时间、一条静态说明用常规的上下文模板注入就够了不用跑一个独立进程如果只有一个客户端使用、并且未来也不会复用写一个普通函数调用可能是更省事的选择。MCP 的收益在“一次实现、多处复用”只有当这个条件成立时才值得为每个能力单独建 Server。我的习惯是先看这个工具是否需要被不同 Host 复用是否含有比较复杂的输入输出结构是否希望由 Server 控制返回给模型的上下文。只要命中两条再动手写 MCP Server否则继续走轻量封装没必要为了追概念而加一层协议。等你真的做过一两个 Server再回头看 MCP 协议里的那些设计细节会发现每一个约定都在为“稳定、可复用、可运维”服务。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价