MCP专题学习笔记 · AI Agent

MCP 专题学习笔记

核心定位

MCP 是 Model Context Protocol,可以理解为:

给 Agent 使用外部能力的一套标准协议。它让 Agent 用统一方式发现工具、读取资源、复用提示模板,而不是每个应用单独硬编码一套插件接口。

它不是 REST API 的替代品。更准确的关系是:

代码块TEXT · 3 行收起展开
业务系统继续负责真实业务
MCP Server 负责把业务能力包装成 Agent 能发现、能调用、能审计的能力
MCP Client 负责在 Agent 侧连接并使用这些能力

一屏架构

MCP 三类能力 Tools 会改变世界的动作 search / write / run
代码块PLAINTEXT · 9 行收起展开
<rect x="290" y="82" width="180" height="104" rx="10" fill="#dcfce7" stroke="#4ade80"/>
<text x="344" y="112" font-weight="700">Resources</text>
<text x="316" y="140">只读上下文资料</text>
<text x="316" y="164">note / doc / record</text>

<rect x="520" y="82" width="180" height="104" rx="10" fill="#fef3c7" stroke="#f59e0b"/>
<text x="578" y="112" font-weight="700">Prompts</text>
<text x="546" y="140">可复用任务模板</text>
<text x="546" y="164">plan / review / explain</text>
判定:副作用 / 只读 / 模板
能力判断标准
Tool会执行动作或产生副作用,例如搜索、写文件、跑命令
Resource只暴露可读取的上下文资料
Prompt给模型复用的任务模板或操作流程

Client / Server 生命周期

MCP Client / Server 生命周期 Agent Client initialize capabilities list/call Backend Response

三个核心对象

对象本质例子后端类比设计重点
Tool可执行动作search_notescreate_taskController 暴露的业务操作参数 schema、权限、审计、错误
Resource可读资料notes://indexproject://history只读查询接口URI 稳定、权限、内容大小
Prompt可复用提示模板make_week_plan业务模板/工作流模板输入变量、适用场景、版本

Tool 设计原则

坏工具:

代码块TEXT · 1 行收起展开
run_command(command: string)

问题:权限过大、参数无边界、无法预测副作用、审计困难。

好工具:

代码块TEXT · 3 行收起展开
search_notes(keyword: string, limit: integer)
read_note(path: string)
create_study_task(title: string, source_note: string, due_date?: string)

好工具必须满足:

标准说明
名称具体模型看名字就知道用途
参数少参数越多,模型越容易填错
schema 严格类型、必填、枚举、长度都要限制
错误可读失败后 Agent 能恢复
权限明确只读/写入/危险操作分开
返回结构化不要只返回一大段自然语言

MCP Server 内部结构

MCP Server 内部结构 Request Router Tools Resources Prompts Guard Validate Execute Result Audit read-only skip
代码块JAVA · 13 行收起展开
final class McpToolDefinition {
    String name;                 // 工具名,必须稳定,改名等于破坏协议
    String description;          // 给模型看的用途说明,决定模型会不会选它
    JsonSchema inputSchema;      // 参数结构,服务端必须二次校验
    ToolRiskLevel riskLevel;     // READ_ONLY / WRITE / DANGEROUS
}

final class McpToolResult {
    boolean ok;                  // 是否执行成功
    String message;              // 给模型看的简短解释
    Object data;                 // 结构化结果,不要只塞自然语言
    List<String> evidencePaths;  // 如果读取资料,返回来源路径
}

协议握手与能力协商

MCP 的关键不是“能不能调用函数”,而是 client 和 server 先把能力边界说清楚。能力协商至少要回答三件事:server 提供什么、client 支持什么、后续调用按什么 schema 校验。

sequenceDiagram
    participant A as Agent
    participant C as Client
    participant S as Server
    A->>C: connect
    C->>S: initialize
    S-->>C: capabilities
    C->>S: list tools
    S-->>C: tool schemas
    A->>C: choose tool
    C->>S: call tool
    S-->>C: result
    C-->>A: observation
阶段输入输出必查点
initializeclient 信息、协议版本server 信息、能力列表版本不兼容要拒绝
list tools空或分页参数tool name、description、inputSchema名称稳定,schema 完整
list resourcesURI 范围resource uri、mime、description只暴露白名单资源
call tooltool name、argumentsstructured result 或 error服务端二次校验
read resourceresource uricontent、metadata大小限制和权限检查

Tool Schema 示例

下面是 search_notes 的最小 schema。重点不是字段多,而是模型填错时服务端能明确拒绝。

代码块JSONC · 28 行收起展开
{
  "name": "search_notes",                 // 稳定工具 ID,改名会破坏调用方
  "description": "Search allowed Obsidian notes by keyword.",
  "inputSchema": {
    "type": "object",                       // arguments 必须是对象,不能直接传字符串
    "required": ["keyword"],              // 必填字段越少越好,但必须明确
    "properties": {                           // 每个字段都要限定类型和边界
      "keyword": {
        "type": "string",                  // keyword 是检索文本,不允许数组或对象
        "minLength": 2,                       // 太短会产生大量噪声召回
        "maxLength": 80,                      // 太长说明用户可能传了整段 prompt
        "description": "Search keyword or phrase."
      },
      "limit": {
        "type": "integer",                 // limit 必须是整数,便于限制返回量
        "minimum": 1,                         // 至少返回一个候选
        "maximum": 20,                        // 防止一次读取过多上下文
        "default": 5
      },
      "scope": {
        "type": "string",                  // keyword 是检索文本,不允许数组或对象
        "enum": ["ai-agent", "all-allowed"],
        "default": "ai-agent"
      }
    },
    "additionalProperties": false          // 拒绝模型幻觉出来的多余字段
  }
}
字段作用失败时怎么处理
nametool 唯一标识未注册则返回 TOOL_NOT_FOUND
description帮模型选择工具描述含糊会导致误调用
required最小必需参数缺失则拒绝执行
enum收敛模型选择非法值返回 schema error
additionalProperties禁止多余参数防止幻觉参数进入业务层

安全边界

风险错误做法正确做法
路径越权模型传什么路径就读什么resolve 后校验必须在白名单根目录内
命令执行暴露 run_command只暴露白名单脚本或固定动作
写入污染直接写正式笔记先写草稿、diff preview、人工确认
删除数据暴露 delete默认不暴露,或软删除 + confirmation
大文件读取一次返回整库限制文件大小、分页、摘要
Prompt 注入笔记内容指挥 Agent 忽略规则资源内容当数据,不当系统指令

Java 后端落地方式

推荐把 MCP Server 当适配层,而不是把业务全部塞进 MCP:

flowchart TD
    Client[MCP Client] --> Server[MCP Server]
    Server --> Guard[Permission / Schema / Audit]
    Guard --> Spring[Spring Boot API]
    Spring --> Service[Business Service]
    Service --> DB[(MySQL / Redis)]
    Service --> Notes[Obsidian Files]
职责不该做什么
MCP Server协议适配、能力声明、参数校验、审计承载复杂业务规则
Spring Boot业务逻辑、权限、事务、日志依赖某一个 Agent 客户端
Notes/RAG资料读取和索引绕过权限直接暴露全盘
Agent Runtime决定何时调用工具直接修改真实数据

适合你的 MCP 项目

Obsidian Notes MCP

Tool参数作用风险等级
list_note_roots列出允许访问的笔记根目录只读
search_noteskeyword, limit搜索标题/正文只读
read_notepath, max_chars读取指定 Markdown只读
recent_notesdays, limit最近修改笔记只读
get_ai_agent_map返回本目录学习地图只读

Project MCP

Tool参数作用
read_project_historyproject_path读取项目 history
list_tasksproject_path查看任务状态
append_learning_logcontent追加学习日志,需确认
run_safe_checkscript_name运行白名单检查脚本

MCP 与 RAG 的关系

flowchart TD
    Agent["Agent"] --> MCP["search_notes"]
    MCP --> Retriever["RAG Retriever"]
    Retriever --> Vector[("Vector DB")]
    Retriever --> Markdown["Markdown Source"]
    Markdown --> Evidence["引用片段"]
    Evidence --> Agent
问题MCP 负责RAG 负责
Agent 怎么调用能力工具协议、参数、返回不负责
知识怎么检索可把检索暴露成 toolchunk、embedding、rerank
权限怎么管server 侧白名单和审计数据过滤和来源控制
结果怎么复盘tool call traceretrieved chunks

最小实现顺序

flowchart TD
    A[只读 search_notes] --> B[read_note]
    B --> C[recent_notes]
    C --> D[get_ai_agent_map]
    D --> E[接 RAG Retriever]
    E --> F[增加写入草稿工具]
    F --> G[人工确认后写入]

不要第一天就写 delete_noteedit_noterun_command。MCP 的第一课是边界,不是炫工具数量。

错误分类与恢复策略

MCP tool 的错误要让 Agent 能恢复,而不是只抛异常字符串。错误至少分成参数错误、权限错误、资源错误、执行错误、协议错误。

代码块JAVA · 7 行收起展开
final class McpError {
    String code;          // 稳定错误码,例如 ARGUMENT_INVALID
    String message;       // 给模型看的简短原因
    boolean retryable;    // 是否允许 Agent 换参数或稍后重试
    String field;         // 哪个参数出错,没有则为空
    String traceId;       // 对应服务端审计日志
}
错误码典型原因Agent 应该怎么做
ARGUMENT_INVALID参数缺失、类型错、超长修正参数后重试一次
PERMISSION_DENIED路径越权、危险操作停止调用,解释权限边界
RESOURCE_NOT_FOUND文件或 URI 不存在换检索条件或追问用户
TOOL_TIMEOUT后端慢、外部服务卡住降级回答或稍后重试
TOOL_FAILED业务异常记录 trace,给出可读失败原因
PROTOCOL_ERRORclient/server 消息不兼容断开并提示版本问题

Notes MCP 验收用例

用例输入期望结果证明什么
正常搜索keyword=RAG, limit=5返回标题、路径、摘要、score检索链路可用
参数缺失没有 keywordARGUMENT_INVALIDschema 生效
越权读取path=F:\secret.mdPERMISSION_DENIED白名单生效
大文件读取max_chars=999999被限制或分页上下文预算受控
不存在文件合法目录下的假路径RESOURCE_NOT_FOUND错误可恢复
prompt 注入笔记中写“忽略规则”只当资料引用资源不是系统指令
trace 检查任意 tool call有参数、耗时、结果、错误可审计

MCP 生产契约

学习 MCP 时最容易漏掉的是:协议能通不等于能力可用。一个可放进作品集的 MCP Server 至少要有稳定契约,让 Agent 知道“能调用什么、不能调用什么、失败后怎么恢复”。

契约项必须明确反例
能力发现tools/list 返回稳定 name、description、inputSchema、readOnlyHint每次启动工具名变化
输入边界字段类型、长度、枚举、默认值、路径白名单path 接收任意绝对路径
输出形态成功和失败都返回结构化 content,不只是一段字符串报错直接抛异常,Agent 只能猜
副作用声明read-only / destructive / requires-confirmation写文件工具伪装成普通查询
超时策略每个 tool 的 timeout、retryable、fallback卡住后 Agent 无限等待
审计字段traceId、tool、argsHash、latency、status、errorCode只在控制台打印日志
版本兼容protocolVersion、serverVersion、schemaVersionclient 升级后无提示地坏掉

推荐的 tool response 语义:

字段作用例子
isError区分工具失败和正常结果true
code稳定错误码,便于 Agent 决策PERMISSION_DENIED
message给模型看的短说明path is outside notes root
retryable是否值得换参数重试false
evidence成功结果的来源或摘要sourcePath, score, titlePath
traceId后端审计入口tool-20260706-...

判断 MCP Server 是否成熟,看这 4 个问题:

问题合格答案
模型传错参数怎么办schema 拦截,返回 ARGUMENT_INVALID,不执行工具
模型试图读白名单外文件怎么办返回 PERMISSION_DENIED,trace 记录被拒绝路径
工具执行一半超时怎么办返回 TOOL_TIMEOUT,标明是否可重试
返回内容太大怎么办分页、截断或摘要,并在结果里写明 truncated=true

验收清单

  • 能解释 MCP client / server / tool / resource / prompt。
  • 能写出一个只读 MCP tool 的 schema。
  • 能限制工具只能访问 F:\2. ObsidianNotes 白名单目录。
  • 能处理参数错误、文件不存在、权限拒绝。
  • 能记录每次 tool call 的参数、结果、耗时、错误。
  • 能区分 MCP 和 RAG:MCP 是调用协议,RAG 是知识检索能力。
  • 能把已有 Spring Boot API 包装成 MCP tool,而不是重写业务系统。