博客 MCP Server 开发记录
为本博客构建 FastMCP 服务器,让 AI 工具能直接读写博客数据。全程记录从零开发到 15 个工具 + 4 个资源的完整过程。
博客 MCP Server 开发全记录
为什么需要 MCP Server
我的博客基于 Next.js + Prisma + SQLite,平时更新文章需要通过本地 Dev Server 的 Admin 后台手动编辑。作为一名 AI 重度使用者,我希望 AI 助手能直接读写博客数据库,实现"用自然语言管理博客"的效果。MCP(Model Context Protocol)正好提供这种标准化接口——AI 客户端通过 MCP 协议与数据库交互,而不需要理解底层 SQL。
技术选型
选择了 FastMCP 这个 Python 框架。它原生支持 stdio 传输模式,几行代码就能暴露一个工具。对比直接用 Flask/FastAPI 做 REST API 的好处:
- 标准协议:任何支持 MCP 的客户端(Cursor、Claude Desktop、自定义 CLI)都能接入
- 工具自描述:参数类型、描述自动生成,客户端不需要额外文档
- 状态管理:无需手动处理 HTTP Session,MCP 协议层面的连接生命周期管理
实现细节
数据库直连
MCP Server 直接操作博客的 SQLite 数据库文件(prisma/dev.db),不经过 Prisma ORM。原因是:
- Prisma 依赖 Node.js 运行时,Python 端调用需要额外中间层
- 对于查询密集的操作,原生 SQL 比 ORM 更可控
- SQLite 单文件特性让直连非常简单——只需读取
.env中的DATABASE_URL
需要注意的一个坑:Prisma Schema 中定义的字段是 camelCase(如 publishedAt),而 SQLite 存储时保留了这个命名。查询时直接写 SELECT publishedAt FROM Post 即可,不需要转换。
认证体系
复用博客现有的认证方案:
# 密码验证使用 bcrypt
if bcrypt.checkpw(password.encode(), user['password'].encode()):
# 生成 HMAC-SHA256 签名 token
payload = json.dumps({"userId": user['id'], "exp": time() + 7*86400})
token = base64.b64encode(payload.encode()) + b'.' + hmac.new(SECRET.encode(), payload.encode(), 'sha256').hexdigest().encode()
token 有效期 7 天,后续请求通过 blog_verify_session 验证。这个实现与博客 Next.js 端完全一致,确保 Admin 鉴权互通。
工具清单
共实现了 16 个工具,覆盖博客核心功能:
| 工具 | 功能 |
|---|---|
blog_list_posts | 分页查询已发布文章 |
blog_get_post | 按 slug 获取单篇文章 |
blog_featured_posts | 获取置顶文章 |
blog_taxonomy | 查询分类和标签统计 |
blog_search | 跨标题/内容/标签搜索 |
blog_all_slugs | 导出所有 slug(用于 sitemap) |
blog_stats | 博客统计数据 |
blog_submit_contact | 联系表单提交 |
blog_login | 管理员登录 |
blog_verify_session | 验证 session token |
blog_create_post | 创建文章(需要 token) |
blog_update_post | 更新文章(需要 token) |
blog_delete_post | 删除文章(需要 token) |
踩坑记录
1. 时区格式问题
博客前端使用 ISO 8601 格式(2026-06-26T10:00:00.000Z),但 SQLite 的 strftime 不处理时区。解决方案:统一用 strftime('%Y-%m-%d %H:%M:%S') 存储,前端读取后自己处理时区。
2. 分类/标签自动创建
创建文章时传入的分类名可能在数据库中不存在。需要在插入文章前先检查并创建分类/标签记录,再建立关联。这与博客 Admin 后端 actions.ts 的逻辑一致。
3. HAVING 语法
统计每个分类下的文章数时,最初写了 HAVING count > 0,但 SQLite 要求 HAVING 后的表达式必须出现在 SELECT 中。修正为 HAVING COUNT(p.id) > 0。
4. 种子文章自动恢复
博客的 prisma/seed.ts 使用 upsert 创建三篇默认文章("为什么我保留技术日志"等)。每次运行 npm run db:seed 都会重新创建已删除的种子文章。解决方案:在 seed 脚本开头检查 Post 表计数,已有文章则跳过 seed。
使用方式
通过 MCP 客户端配置:
{
"mcpServers": {
"blog": {
"command": "F:\GitHub_project\BLOG\blog-mcp-server\mcp\Scripts\python.exe",
"args": ["F:\GitHub_project\BLOG\blog-mcp-server\run.py"],
"cwd": "F:\GitHub_project\BLOG\blog-mcp-server"
}
}
}
启动后 AI 助手就可以通过工具调用来管理博客内容,实现"一句话发文章"的效果。目前已批量创建了 13 篇技术文章,全流程正常。