
关键词MCPModel Context ProtocolPythonFastMCPStreamableHTTPAI AgentHermesSQLite文章目录从零开发一个 MCP Server架构、实践与踩坑实录一、MCP 简介为什么 Agent 需要一套“外设接口”二、MCP Server 应用介绍2.1 需求拆解2.2 技术选型2.3 架构定位三、MCP Server 架构说明3.1 总体架构3.2 项目结构3.3 分层职责四、MCP Server 开发实践4.1 环境准备4.2 依赖声明4.3 数据层 db.py节选4.4 Server 入口 server.py节选4.5 接入 Hermes4.6 切换到 HTTP 模式远程部署预览五、测试验证过程5.1 数据层单元测试5.2 stdio 模式端到端冒烟5.3 HTTP 模式端到端验证5.4 在 Hermes 会话里真实调用5.5 验证工具链5.6 踩坑记录六、总结说明从零开发一个 MCP Server架构、实践与踩坑实录本文基于 2026-08-15 本机真实开发会话。文中所有命令输出均为实际运行结果不是示例数据。适合读者已有 Python 基础、想给 Agent/LLM 接入外部数据能力的开发者。通读约 15 分钟完整跟练约 1 小时。一、MCP 简介为什么 Agent 需要一套“外设接口”大模型 Agent 的瓶颈从来不是“会想”而是“够不到”。模型只能看到对话文本拿不到数据库、文件系统、内部 API 里的真实数据。早期方案是给每个 Agent 硬编码工具函数但工具一多维护就失控每个 Agent 框架一套工具协议换个框架全部重写。MCPModel Context Protocol解决的就是这个问题。它由 Anthropic 于 2024 年底提出是一个开放协议定义了MCP Server能力提供方和MCP Client能力消费方之间的标准通信方式Server 把能力包装成“工具”tool暴露给客户端Client 启动时自动发现工具列表运行时可调用传输层支持 stdio本地子进程和 StreamableHTTP远程传输2025-03 版协议规范引入取代旧的 HTTPSSE 方案类比一句话MCP 之于 Agent就像 USB 之于电脑——外设只要符合协议就能即插即用不用管里面是什么芯片。对比项传统硬编码工具MCP Server工具协议每框架一套各自为政统一开放协议复用性换框架重写一套 Server 到处接部署与 Agent 同进程本地子进程 / 远程独立部署工具发现手工注册启动自动发现二、MCP Server 应用介绍本文的实战场景开发一个“学生成绩查询”MCP Server让 Agent 能直接回答“张伟的均分是多少”这类问题而不是让人先查好再喂给模型。2.1 需求拆解能力说明查学生按学号精确 / 按姓名模糊查成绩按学生查全部成绩、按课程查成绩排名统计分析平均分、加权均分、最高最低分2.2 技术选型组件选型理由语言Python 3.12MCP 官方 SDK 生态成熟SDKmcp Python SDKFastMCP一份代码支持 stdio HTTP 双传输存储SQLite单文件、零运维演示/内网够用可替换 MySQL/API接入端Hermes Agent原生 MCP 客户端配置即用2.3 架构定位这个 Server 属于“数据访问型 MCP”后端接 SQLite前端通过 MCP 协议向 Agent 暴露只读查询工具。后续接真实教务系统时只改数据层工具层不动。三、MCP Server 架构说明3.1 总体架构Server 侧: grade-mcp-server客户端侧JSON-RPC 2.0stdio / StreamableHTTPHermes AgentMCP Client 模块FastMCP Server工具层list_students / query_student_gradesquery_course_grades / query_student_stats ...数据层 db.pySQLite 读写封装SQLitedata/grades.db通信细节协议MCP 基于 JSON-RPC 2.0核心方法initialize握手、tools/list发现工具、tools/call调用工具传输本地用 stdio子进程 stdin/stdout远程用 StreamableHTTPPOST /mcp工具命名接入 Hermes 后自动加前缀如mcp_grades_query_student_grades3.2 项目结构grade-mcp-server/ ├── pyproject.toml # 项目元数据 依赖声明 ├── grade_mcp/ │ ├── __init__.py │ ├── server.py # FastMCP Server 入口,工具定义 │ └── db.py # SQLite 数据层:建表/种子数据/查询 ├── tests/ │ └── test_db.py # 数据层单元测试(9 个用例) ├── scripts/ │ ├── test_stdio.py # stdio 模式客户端冒烟测试 │ └── test_http.py # HTTP 模式客户端冒烟测试 ├── deploy/ │ ├── deploy.sh # rsync systemd 一键部署脚本 │ ├── grade-mcp.service # systemd 单元文件 │ └── notes.md # 防火墙/反代/认证建议 ├── Dockerfile # 容器化部署 └── README.md3.3 分层职责server.py工具层只声明工具签名和 docstring不碰 SQL。docstring 就是给 LLM 看的工具说明写清楚参数格式如S001、2024-秋能显著提高模型调用准确率。db.py数据层所有 SQL 在这里通过GRADES_DB_PATH环境变量控制库文件位置。换 MySQL 只改这一个文件。传输层FastMCP 封装server.run(transportstdio)与streamable_http_app()两行切换。四、MCP Server 开发实践4.1 环境准备系统 Python 是 3.9.6MCP SDK 需要 3.10用 uv 建独立环境$cd~/workspace/hermesmkdirgrade-mcp-servercdgrade-mcp-server $ uv venv--python3.12.venv Using CPython3.12.13 Creating virtual environment at: .venv4.2 依赖声明[project] name grade-mcp-server version 0.1.0 requires-python 3.10 dependencies [ mcp1.28,2, uvicorn0.30.0, ]这里锁mcp2是本会话踩的第一个大坑见“测试验证”章节的踩坑 1。安装依赖以可编辑模式装进虚拟环境方便后续改代码即时生效$ uv pipinstall-e.若不想打可编辑安装也可以直接uv pip install mcp1.28,2 uvicorn再从项目根目录运行python -m grade_mcp.server利用当前目录的包路径。4.3 数据层 db.py节选SCHEMA CREATE TABLE IF NOT EXISTS students ( id TEXT PRIMARY KEY, name TEXT NOT NULL, class_name TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS courses ( id TEXT PRIMARY KEY, name TEXT NOT NULL, credits INTEGER NOT NULL DEFAULT 3 ); CREATE TABLE IF NOT EXISTS grades ( id INTEGER PRIMARY KEY AUTOINCREMENT, student_id TEXT NOT NULL REFERENCES students(id), course_id TEXT NOT NULL REFERENCES courses(id), semester TEXT NOT NULL, score REAL NOT NULL CHECK (score 0 AND score 100), UNIQUE (student_id, course_id, semester) ); 种子数据5 名学生、5 门课程、16 条成绩记录。首次启动自动建库写入。4.4 Server 入口 server.py节选frommcp.server.fastmcpimportFastMCPfromgrade_mcpimportdb serverFastMCP(grade-mcp-server,instructions(成绩查询 MCP server。提供学生成绩查询、课程成绩查询、学生统计等工具。学生 ID 形如 S001,课程 ID 形如 C001。),)server.tool()defquery_student_grades(student_id:str,semester:str|NoneNone)-dict:查询某学生的全部成绩。student_id 为学号(如 S001);semester 可选,如 2024-秋。studentdb.get_student_by_id(student_id.strip().upper())ifnotstudent:return{error:f学生{student_id}不存在}gradesdb.get_grades_by_student(student[id],semester)return{student:student,grades:grades}入口同时支持两种传输ifargs.transporthttp:appserver.streamable_http_app()# StreamableHTTP,端点 /mcpuvicorn.run(app,hostargs.host,portargs.port)else:server.run(transportstdio)# 本地子进程模式4.5 接入 Hermes本地开发用 stdio一条命令接入$ hermes mcpaddgrades--command/Users/workspace/hermes/grade-mcp-server/.venv/bin/python\--connect-timeout30--args-mgrade_mcp.server ✓ Savedgradesto ~/.hermes/config.yaml(6/6 tools enabled)生成配置mcp_servers:grades:command:/Users/workspace/hermes/grade-mcp-server/.venv/bin/pythonargs:--m-grade_mcp.serverconnect_timeout:30.0enabled:true4.6 切换到 HTTP 模式远程部署预览$ .venv/bin/python-mgrade_mcp.server--transporthttp--host127.0.0.1--port8000Grade MCP Server listening on http://127.0.0.1:8000/mcp INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8000(Press CTRLC to quit)$ hermes mcp remove grades ✓ Removedgradesfrom config $ hermes mcpaddgrades--urlhttp://127.0.0.1:8000/mcp --connect-timeout30✓ Savedgradesto ~/.hermes/config.yaml(6/6 tools enabled)HTTP 模式配置mcp_servers:grades:url:http://127.0.0.1:8000/mcpconnect_timeout:30.0headers:{}enabled:true⚠️ 注意HTTP 模式默认没有任何认证。上面的示例刻意只绑定127.0.0.1一旦要暴露到内网/公网务必先加反代 鉴权如 Nginx Bearer Token或 OAuth方案见仓库deploy/notes.md。五、测试验证过程5.1 数据层单元测试先给数据层写 9 个 pytest 用例覆盖种子数据、精确/模糊查询、学期过滤、成绩降序、统计聚合、异常学生$ .venv/bin/python-mpytest tests/-q.........[100%]9passedin0.03s5.2 stdio 模式端到端冒烟写一个最小 MCP 客户端真实走一遍 initialize → list_tools → call_tool$ .venv/bin/python scripts/test_stdio.py[tools]6registered: - list_students: 列出所有学生(学号、姓名、班级)。 - list_courses: 列出所有课程(课程号、课程名、学分)。 - find_student: 按学号或姓名关键字查找学生。... - query_student_grades: 查询某学生的全部成绩。... - query_course_grades: 查询某课程的所有学生成绩。... - query_student_stats: 查询学生的成绩统计:...[query_student_grades S001]{student:{id:S001,name:张伟,class_name:软件工程 2023级1班},grades:[{semester:2024-秋,score:88.0,course_id:C001,course_name:高等数学,credits:5},{semester:2024-秋,score:92.0,course_id:C002,course_name:数据结构,credits:4},...]}5.3 HTTP 模式端到端验证$curl-shttp://127.0.0.1:8000/health{status:ok,service:grade-mcp-server}$ hermes mcptestgrades Transport: HTTP → http://127.0.0.1:8000/mcp Auth: none ✓ Connected(47ms)✓ Tools discovered:65.4 在 Hermes 会话里真实调用切换成 HTTP 后直接在对话中让 Agent 查所有学生成绩统计5 个并行查询全部返回学号 姓名 班级 课程数 平均分 加权均分 最低 最高 S005 陈静 人工智能 2023级3班 3 92.67 92.67 89.0 96.0 S003 王强 计算机科学 2023级2班 3 91.33 91.67 88.0 95.0 S001 张伟 软件工程 2023级1班 4 88.75 88.63 85.0 92.0 S002 李娜 软件工程 2023级1班 3 78.67 78.46 76.0 81.0 S004 刘洋 计算机科学 2023级2班 3 68.33 68.31 65.0 72.05.5 验证工具链用hermes verify跑完整验证链bootstrap → test → 启动服务 → 健康检查{recipe:grade-mcp-server (uv pytest),ok:true,phases:[{phase:bootstrap,ok:true},{phase:test,ok:true,outputTail:9 passed in 0.03s}],readiness:{ready:true,statusCode:200}}5.6 踩坑记录坑 1mcp 2.0 API 大改导入直接失败现象装最新版mcp2.0.0后from mcp.server.fastmcp import FastMCP报 ModuleNotFoundError。原因mcp 2.0 移除了mcp.server.fastmcp模块改用新的MCPServerAPImcp.server.mcpserver且 stdio 握手协议与 1.x 不兼容——Hermes 内置客户端是 1.28.1两端协议对不上连接直接Connection closed。修复锁定mcp1.28,2代码改回 FastMCP API。手动 spawn 正常、但 Hermes 连不上是排查这个问题的关键信号——两端版本不一致。坑 2hermes mcp add的--args会吞掉后面的选项现象执行hermes mcp add grades --command python --args -m grade_mcp.server --connect-timeout 30server 报unrecognized arguments: --connect-timeout 30。原因--args是贪婪参数会吃掉它后面所有 token--connect-timeout被当成 server 的启动参数传进去了。修复--connect-timeout必须放在--args之前hermes mcpaddgrades--commandpython--connect-timeout30--args-mgrade_mcp.server坑 3交互式确认被管道输入误答生成脏配置现象用printf y | hermes mcp add ...喂交互提示配置里多出headers: Authorization: Bearer y真实值就是 “y”.env 里多了MCP_GRADES_API_KEYy。原因add 流程里还有认证相关提示一个y被误认为“使用 header 认证”。修复清掉假 tokenheaders设为{}。注意hermes config set mcp_servers.grades.headers {}会存成字符串导致 test 崩str object has no attribute items要用 YAML 结构写入真正的空对象。共性教训给 CLI 的交互式流程喂管道输入先确认提示顺序和数量工具链版本尤其协议类 SDK先对齐再开发省掉一整轮排查。六、总结说明这次实践把“开发一个 MCP Server 并接入 Agent”的完整链路走通了FastMCP 一份代码支持 stdio 和 HTTP 双传输SQLite 做数据层接入 Hermes 后对话里直接查成绩。核心收获MCP 的价值在于协议标准化——工具定义、发现、调用全部标准化Server 与 Client 解耦换 Agent 框架不用重写工具。传输模式要按场景选——本地开发用 stdio免运维、免端口远程部署用 StreamableHTTP独立进程、可扩展一份代码两行切换。工具 docstring 就是 AI 的 API 文档——写清楚参数格式和返回结构模型调用准确率明显提升这是 MCP 开发区别于传统 API 开发的地方。版本对齐是第一优先级——协议类 SDK 大版本之间不兼容开发前先确认 Client 端版本能省一整轮排障。边界当前 Server 是只读查询没有写权限和鉴权SQLite 只适合单机/内网生产接真实教务系统时建议换 MySQL/PostgreSQL 并加 OAuth 认证。后续方向接真实数据源、加写入工具、部署到云服务器Docker/systemd 方案已就绪。