MCP项目实践
3814 字约 13 分钟
AIAgentMCP实践
2026-07-24
本文以一个完整项目——KnowledgeOS MCP Server(为 Obsidian 知识库提供 MCP 接入能力)——为线索,串联 MCP 从设计到落地的全部关键环节。它既是一份项目文档,也是一个可复用的 MCP 项目模板。
1. 项目目标
为 KnowledgeOS 知识库提供 MCP 接入能力,使任意 MCP 兼容的 AI Host(Claude Desktop、Cursor、自建 Agent 等)能够:
- 搜索和阅读知识库中的笔记
- 创建和修改笔记内容
- 浏览目录结构和索引文件
- 执行知识库维护操作(检查断链、扫描孤立笔记、更新 MOC)
- 调用预定义 Prompt 完成知识整理、文档扩充等任务
核心衡量标准:AI Host 不需要了解文件系统细节或 Obsidian 插件机制,仅通过 MCP 协议就能完成上述所有操作。
2. 使用者与使用场景
| 使用者 | 场景 | 典型操作 |
|---|---|---|
| 知识管理者(本人) | 通过 Claude Desktop 整理知识库 | 搜索笔记、创建笔记、检查断链、更新 MOC |
| 学习 Agent | 基于知识库回答问题 | 搜索笔记、读取内容、获取目录树 |
| 自动化工作流 | 定期维护知识库健康 | 扫描孤立笔记、检查质量、生成报告 |
| 写作辅助 | 扩充或改进现有文档 | 读取笔记、更新段落、质量检查 |
关键约束:所有使用者共享同一个 Server 实例,但通过权限控制区分读写能力。
3. 为什么使用 MCP
直接让 AI 操作文件系统存在三个问题:
- 安全风险:AI 可能读写知识库之外的文件
- 语义鸿沟:AI 不理解 Obsidian wikilink、MOC、frontmatter 等概念
- 缺乏约束:没有输入校验、操作审计、权限分级
MCP Server 在这三者之上提供一层语义化、受控、可审计的访问层。相比直接暴露文件系统,MCP 的优势:
- 工具定义自带参数校验和文档说明
- Resource 可以暴露结构化索引而非原始文件树
- Prompt 模板封装了常见的知识库操作模式
- 所有操作经过 Server,天然支持审计和权限控制
相关概念详见 MCP基础 和 MCP Server设计。
4. 系统边界
做什么
- 提供笔记的 CRUD 操作(限定在知识库目录内)
- 提供全文搜索和标签搜索
- 暴露目录树、索引、标签等结构化数据
- 提供知识库维护工具(断链检查、孤立笔记扫描、MOC 更新)
- 提供预定义 Prompt 模板
- 所有操作限定在知识库根目录内
不做什么
- 不管理 Obsidian 插件:插件配置由 Obsidian 自身处理
- 不做笔记渲染:不转换 Markdown 为 HTML
- 不做跨知识库同步:只服务当前 vault
- 不做向量搜索:V1 仅支持文本搜索,向量检索留给后续迭代
- 不修改 Obsidian 配置:不触碰
.obsidian/目录
5. 架构设计
分层职责:
- 协议层:处理 JSON-RPC 消息、能力协商、生命周期管理(参考 MCP协议生命周期)
- 能力适配层:将 MCP 的 Tool/Resource/Prompt 语义映射到内部业务调用
- 业务逻辑层:实现搜索、CRUD、维护等具体逻辑
- 基础设施层:文件系统、索引、Git 的具体操作
6. 能力清单
Tools
| 能力 | 类型 | 输入 | 输出 | 副作用 | 风险等级 |
|---|---|---|---|---|---|
search_notes | Tool | query: string, tags?: string[], limit?: number | 匹配的笔记列表(路径 + 摘要) | 无 | 低 |
read_note | Tool | path: string | 笔记 Markdown 内容 | 无 | 低 |
create_note | Tool | path: string, content: string, frontmatter?: object | 创建结果 | 写入文件 | 中 |
update_section | Tool | path: string, section: string, content: string | 更新结果 | 修改文件 | 高 |
check_broken_links | Tool | scope?: string | 断链列表 | 无 | 低 |
scan_orphans | Tool | 无 | 孤立笔记列表 | 无 | 低 |
update_moc | Tool | moc_path: string, action: "add" | "remove", note_path: string | 更新结果 | 修改 MOC 文件 | 中 |
Resources
| 资源 | URI 模式 | 内容 | 用途 |
|---|---|---|---|
| 笔记内容 | knowledge://note/{path} | 笔记原始 Markdown | AI 读取单篇笔记 |
| 目录树 | knowledge://tree | 目录结构 JSON | AI 了解知识库全貌 |
| 标签索引 | knowledge://tags | 标签 → 笔记映射 | AI 按主题导航 |
| 最近修改 | knowledge://recent?days=7 | 近期修改的笔记列表 | AI 了解最新动态 |
Prompts
| Prompt | 参数 | 用途 |
|---|---|---|
| 生成知识文档 | topic, domain, depth | 按知识库模板生成新笔记 |
| 扩充目录内容 | note_path, sections | 为骨架文档填充内容 |
| 文档质量检查 | note_path | 检查结构完整性、链接有效性 |
| 主题学习计划 | topic, goals | 基于知识库内容生成学习路径 |
7. Client 设计
Client 负责与 Server 建立连接、发现能力、转发用户请求。设计要点:
连接管理:
- 支持 stdio(本地)和 SSE(远程)两种 Transport
- 自动重连机制,指数退避(初始 1s,最大 30s)
- 连接建立后立即执行
initialize握手
能力发现:
- 缓存 Server 返回的
capabilities,避免重复请求 - 工具列表变更时通过
notifications/tools/list_changed刷新
权限映射:
- Client 在配置中声明所需权限级别(read-only / read-write / admin)
- 只展示当前权限允许的 Tool
错误处理:
- Tool 调用失败时解析 JSON-RPC error code 并给出可读提示
- 区分临时错误(网络超时)和永久错误(权限不足)
更多细节参考 MCP Client设计 和 MCP错误处理。
8. Server 设计
分层架构
┌─────────────────────────────────────────┐
│ 协议层 (Protocol Layer) │
│ - JSON-RPC 2.0 消息解析与序列化 │
│ - 请求路由、错误响应 │
│ - 生命周期管理 (initialize/shutdown) │
├─────────────────────────────────────────┤
│ 能力适配层 (Capability Adapter) │
│ - Tool 注册与参数校验 │
│ - Resource URI 解析与内容提供 │
│ - Prompt 模板渲染 │
├─────────────────────────────────────────┤
│ 业务逻辑层 (Business Logic) │
│ - 搜索引擎(全文 + 标签 + 路径) │
│ - CRUD 操作(带 frontmatter 感知) │
│ - 维护工具(断链、孤立、MOC) │
├─────────────────────────────────────────┤
│ 基础设施层 (Infrastructure) │
│ - 文件系统读写(带路径沙箱) │
│ - SQLite 全文索引 │
│ - Git 操作(日志、diff) │
└─────────────────────────────────────────┘关键设计决策
路径沙箱:所有文件操作限定在 vault 根目录内,通过路径规范化 + 前缀检查防止目录穿越攻击。
Frontmatter 感知:读写笔记时正确解析和保留 YAML frontmatter,不破坏 Obsidian 元数据。
增量索引:使用文件系统 watcher 监听变更,增量更新 SQLite 全文索引,避免全量扫描。
幂等操作:create_note 在文件已存在时返回错误而非覆盖;update_section 使用 section 标题定位,内容替换是幂等的。
更多设计模式参考 MCP Server设计 和 MCP能力设计指南。
9. 认证与权限
认证方案
| Transport | 认证方式 | 说明 |
|---|---|---|
| stdio | 进程所有者身份 | 本地通信,依赖操作系统权限 |
| SSE | OAuth 2.0 Bearer Token | 远程访问,需 Server 配置 |
权限分级
read-only → search_notes, read_note, check_broken_links, scan_orphans
+ 所有 Resources
read-write → 以上 + create_note, update_section, update_moc
admin → 以上 + 索引重建、配置修改权限在 Server 启动配置中声明,Client 初始化时通过 capabilities 获取当前权限级别。详见 MCP认证与授权 和 MCP权限设计。
10. 安全威胁模型
| 资产 | 威胁主体 | 攻击路径 | 影响 | 缓解措施 |
|---|---|---|---|---|
| 笔记内容 | 恶意 AI 输出 | Tool 参数注入,尝试读取 vault 外文件 | 敏感数据泄露 | 路径沙箱 + 输入校验 |
| 笔记完整性 | 错误 AI 输出 | update_section 覆盖重要内容 | 数据丢失 | Git 自动备份 + 确认机制 |
| 索引数据 | 外部攻击者 | SSE 端点未认证访问 | 知识库结构泄露 | Token 认证 + IP 白名单 |
| Server 可用性 | 异常 Client | 高频请求导致资源耗尽 | 服务不可用 | 速率限制 + 超时控制 |
| 认证凭据 | 中间人攻击 | SSE 通信被窃听 | 凭据泄露 | TLS 加密 + 短期 Token |
安全设计遵循 MCP安全边界 中描述的最小权限原则。
11. 错误与重试
错误分类
| 类别 | JSON-RPC Code | 处理策略 | 示例 |
|---|---|---|---|
| 参数错误 | -32602 | 不重试,返回校验信息 | 路径不存在、必填参数缺失 |
| 权限不足 | -32001 | 不重试,提示升级权限 | read-only 用户调用 create_note |
| 资源冲突 | -32002 | 可选重试 | 文件被其他进程锁定 |
| 内部错误 | -32603 | 记录日志,可重试一次 | 索引服务异常 |
| 超时 | -32000 | 指数退避重试,最多 3 次 | 大文件读取超时 |
重试规则
- 只有幂等操作才自动重试(读操作、
update_section) create_note等非幂等操作不自动重试,返回错误让 Client 决策- 重试间隔:1s → 2s → 4s,带 jitter
详细策略参考 MCP错误处理。
12. 测试策略
测试分三层,覆盖协议、业务、集成三个维度(参考 MCP测试策略):
协议层测试
- JSON-RPC 消息格式合规性
- 能力协商流程正确性
- 生命周期事件顺序(initialize → 使用 → shutdown)
业务逻辑测试
| 测试目标 | 方法 | 覆盖场景 |
|---|---|---|
search_notes | 单元测试 | 空查询、无结果、中文分词、标签过滤 |
read_note | 单元测试 | 正常读取、路径穿越攻击、不存在的文件 |
create_note | 单元测试 | 正常创建、frontmatter 生成、目录不存在 |
update_section | 单元测试 | 替换段落、段落不存在、frontmatter 保留 |
| 路径沙箱 | 安全测试 | ../ 穿越、符号链接、编码绕过 |
集成测试
- 完整请求链路:Client 调用 → Server 处理 → 文件系统操作 → 返回结果
- 并发测试:多个 Client 同时操作同一文件
- 索引一致性:文件变更后索引是否及时更新
13. 日志与监控
日志分级
| 级别 | 内容 | 用途 |
|---|---|---|
| ERROR | Tool 执行失败、索引异常 | 告警 + 问题排查 |
| WARN | 权限拒绝、参数校验失败、慢查询 | 安全审计 + 性能分析 |
| INFO | Tool 调用记录(名称、耗时、结果状态) | 使用统计 |
| DEBUG | 完整 JSON-RPC 消息 | 开发调试 |
监控指标
- 请求量:按 Tool 名称统计调用次数
- 延迟分布:P50 / P95 / P99,按 Tool 分类
- 错误率:按错误类型分类
- 索引状态:文档数量、索引大小、最近更新时间
日志格式采用 JSON Lines,便于后续接入日志分析工具。详见 MCP日志与可观测性。
14. 部署方案
本地部署(默认)
{
"mcpServers": {
"knowledgeos": {
"command": "node",
"args": ["path/to/knowledgeos-mcp-server/dist/index.js"],
"env": {
"VAULT_PATH": "/Users/dimoo/KnowledgeOS",
"PERMISSION_LEVEL": "admin"
}
}
}
}- Transport:stdio
- 认证:依赖操作系统进程权限
- 适用场景:个人使用,单用户
远程部署(团队)
- Transport:SSE over HTTPS
- 认证:OAuth 2.0 Bearer Token
- 进程管理:systemd / PM2
- 反向代理:Nginx + TLS
- 适用场景:多人共享知识库
依赖
- Node.js >= 18
- SQLite 3(全文索引)
- 文件系统读取权限
15. 版本兼容
MCP 协议版本
- 当前支持:MCP 2024-11-05
- 通过
initialize响应的protocolVersion字段声明 - Client 请求的协议版本不兼容时,返回支持的版本列表
Server 版本策略
- 遵循 SemVer(语义化版本)
- Major:删除或修改 Tool 签名、修改 Resource URI 模式
- Minor:新增 Tool / Resource / Prompt
- Patch:Bug 修复、性能优化
向后兼容规则
- 新增 Tool 不影响旧 Client
- 删除 Tool 前至少保留一个 Minor 版本的 deprecated 警告
- Resource URI 模式一旦发布不变更,只新增
16. 验收标准
功能验收
安全验收
性能验收
17. 已知限制
| 限制 | 原因 | 缓解方案 |
|---|---|---|
| 不支持向量搜索 | V1 仅实现文本搜索 | V2 集成 embedding 服务 |
| 不支持 Canvas 文件 | .canvas 格式复杂 | 仅作为 Resource 暴露原始 JSON |
| 不支持 Excalidraw 文件 | 二进制格式无法文本处理 | 跳过,仅索引 .md 文件 |
| 并发写入冲突 | 文件系统无锁机制 | 依赖 Git 做最终冲突解决 |
| 大文件性能 | 单次读取无分页 | 后续支持 offset + limit 参数 |
18. 迭代计划
V0.1 — 基础能力(2 周)
- 项目脚手架搭建
search_notes+read_note实现- 路径沙箱 + 基础权限
- 单元测试覆盖核心逻辑
V0.2 — 写入能力(2 周)
create_note+update_section实现- frontmatter 感知读写
- Git 自动备份(写入前 commit)
- 集成测试
V0.3 — 维护工具(2 周)
check_broken_links+scan_orphans+update_moc- SQLite 全文索引
- 文件系统 watcher 增量索引
V0.4 — Resource & Prompt(1 周)
- 四个 Resource 实现
- 四个 Prompt 模板
- Client 能力发现测试
V1.0 — 生产就绪(1 周)
- 日志与监控完善
- 安全审计
- 部署文档
- 性能基准测试
19. 项目复盘模板
每完成一个迭代,回答以下问题:
做得好的
- 哪些设计决策被验证是正确的?
- 哪些工具的使用频率超出预期?
需要改进的
- 哪些接口设计不符合实际使用场景?
- 哪些测试没有覆盖到真实问题?
学到的
- MCP 协议的哪些特性在实际使用中特别有用?
- 哪些理论设计在实践中遇到了意外困难?
- 安全模型有哪些需要加强的地方?
下一步行动
- 哪些功能需要调整优先级?
- 哪些技术债需要在下个迭代处理?