MCP 是什么:Agent 如何通过标准协议连接工具和外部服务
前言
前面我们已经学习了 Tool Calling。
Tool Calling 的基本思路是:模型决定要调用什么工具,后端负责校验参数、执行真实业务,然后把结果返回给模型。
但是当 Agent 项目逐渐变多时,会出现一个新问题:
每个 Agent 都要重复接数据库工具吗? 每个 Agent 都要重复对接 Git、文件系统、接口文档吗? 不同模型、不同框架之间,工具定义能不能复用?这时,MCP 就出现了。
MCP 全称是 Model Context Protocol,可以理解为一种让 AI 应用连接外部能力的标准协议。
它希望解决的问题不是“让模型变聪明”,而是:
让 Agent 能以更统一的方式发现、调用和使用外部工具、资源与提示词模板。
对于 Java 后端开发者来说,MCP 可以先理解成:
Agent 侧的标准化工具接入协议它和我们平时做 REST API、RPC、消息队列一样,核心价值是降低系统之间的耦合。
一、没有 MCP 时,工具接入会遇到什么问题
假设我们开发了三个 Agent:
项目知识库助手 代码审查助手 运维排障助手它们都可能需要访问一些公共能力:
查询 Git 提交记录 搜索项目文件 读取接口文档 查询发布记录 查询服务日志如果每个 Agent 都自己封装一套工具,会变成下面这样:
知识库助手 -> 自己实现 Git 查询工具 代码审查助手 -> 再实现一套 Git 查询工具 运维助手 -> 再实现一套日志查询工具久而久之就会出现:
- 工具定义不统一;
- 参数格式不统一;
- 权限校验逻辑分散;
- 错误处理方式不一致;
- 工具升级时需要改多个项目;
- 很难让新的 Agent 快速复用已有能力。
MCP 的思路是把这些能力标准化。
多个 Agent Client | | MCP 协议 | 多个 MCP Server | Git、数据库、文件、内部接口、日志系统这样,Agent 不需要关心每个外部系统具体怎么实现,而是通过统一的工具描述和调用方式使用能力。
二、MCP 的核心角色
理解 MCP 时,可以先记住三个角色。
1. MCP Host
Host 是承载 AI 交互的应用。
例如:
- 桌面 AI 客户端;
- IDE 插件;
- Agent 平台;
- 企业内部智能助手;
- Java 编写的聊天应用。
它负责管理用户交互、模型调用以及 MCP Client。
2. MCP Client
Client 负责连接具体的 MCP Server。
它通常会做这些事情:
- 建立连接;
- 获取工具列表;
- 调用工具;
- 获取资源;
- 接收结果;
- 处理异常和超时。
一个 Host 可以有多个 MCP Client,因为它可能要连接多个 MCP Server。
3. MCP Server
Server 是能力提供方。
它可以对外暴露:
- Tools:可执行工具;
- Resources:可读取资源;
- Prompts:可复用提示词模板。
例如一个项目管理 MCP Server 可以提供:
get_project_info list_project_documents search_issue get_release_history一个日志 MCP Server 可以提供:
search_error_logs get_service_health get_trace_detail三、MCP 中的 Tools、Resources、Prompts
1. Tools:可执行能力
Tool 是模型可以请求调用的动作。
例如:
查询订单状态 搜索项目文件 获取服务健康状态 创建测试任务 查询发布记录工具通常带有:
- 工具名称;
- 工具描述;
- 参数定义;
- 参数类型;
- 返回结果;
- 权限要求。
一个工具可以抽象成下面这个结构:
{"name":"get_order_status","description":"根据订单号查询订单当前状态,只能查询当前用户有权限访问的订单。","inputSchema":{"type":"object","properties":{"orderNo":{"type":"string","description":"订单编号"}},"required":["orderNo"]}}模型会根据工具描述判断是否需要调用它。
但请注意:
工具描述是给模型理解能力边界用的,不是权限控制。
真正的权限校验必须由 MCP Server 或业务后端完成。
2. Resources:可读取资料
Resource 更像“外部可读取内容”。
例如:
项目 README 接口规范 数据库设计文档 部署手册 系统配置说明资源可以理解为给 Agent 提供上下文的内容来源。
例如:
resource://project/readme resource://project/api-doc resource://ops/deploy-guide与 Tool 的区别在于:
- Tool 偏向执行动作;
- Resource 偏向读取内容;
- Tool 可能产生副作用;
- Resource 一般用于提供信息。
例如“查询实时订单状态”应该是 Tool,“读取订单状态枚举说明”更适合作为 Resource。
3. Prompts:可复用提示词模板
Prompts 用于提供标准化的提示词模板。
例如:
代码审查模板 线上故障排查模板 接口设计模板 SQL 优化分析模板一个“代码审查” Prompt 可能需要参数:
language diff focus调用时可以传入:
{"language":"Java","focus":"安全性和事务边界","diff":"..."}对于团队来说,这种方式可以把高质量 Prompt 沉淀下来,而不是让每个用户都从零写一遍。
四、MCP 和 Tool Calling 有什么关系
很多同学第一次看到 MCP 时,会觉得它和 Tool Calling 很像。
确实,它们有关联,但不是同一个概念。
| 对比项 | Tool Calling | MCP |
|---|---|---|
| 本质 | 模型调用工具的机制 | 工具、资源、提示词的标准协议 |
| 关注点 | 模型如何选择并发起调用 | Agent 如何统一连接外部能力 |
| 工具定义 | 常由应用自行维护 | 可由 MCP Server 对外暴露 |
| 复用范围 | 单个应用内也可使用 | 更适合跨 Agent、跨客户端复用 |
| 权限控制 | 由业务后端实现 | 仍然由 MCP Server 和业务系统实现 |
可以这样理解:
Tool Calling 解决“模型怎么发起工具调用” MCP 解决“外部工具怎么以标准方式提供给 Agent”它们完全可以配合使用。
用户提问 -> Agent 判断需要工具 -> 通过 MCP Client 发现并调用 MCP Tool -> MCP Server 校验权限并执行 -> 返回结果给 Agent -> Agent 组织最终回答五、一个项目查询 MCP Server 的设计示例
假设我们要做一个“项目研发助手”,它需要查询项目文档、发布记录和接口信息。
可以先定义三个工具:
search_project_document get_release_history get_api_detail1. 工具定义
@DatapublicclassMcpToolDefinition{privateStringname;privateStringdescription;privateMap<String,Object>inputSchema;}构造一个查询发布记录工具:
publicMcpToolDefinitionbuildReleaseHistoryTool(){McpToolDefinitiontool=newMcpToolDefinition();tool.setName("get_release_history");tool.setDescription(""" 查询指定项目最近的发布记录。 只能查询当前用户有权限访问的项目。 返回发布时间、版本号、发布环境和发布状态。 """);Map<String,Object>properties=Map.of("projectId",Map.of("type","string","description","项目ID"),"limit",Map.of("type","integer","description","返回数量,范围1到20"));tool.setInputSchema(Map.of("type","object","properties",properties,"required",List.of("projectId")));returntool;}这段代码只是表达工具描述的思路。实际接入 MCP SDK 时,会按照 SDK 提供的方式注册工具。
六、工具执行的正确边界
假设模型请求调用:
{"name":"get_release_history","arguments":{"projectId":"project-a","limit":10}}后端不能直接拿参数执行查询,而应该完成以下步骤:
1. 校验工具是否允许调用 2. 校验参数格式 3. 校验当前用户是否登录 4. 校验用户是否有项目权限 5. 执行受控业务查询 6. 过滤敏感字段 7. 记录审计日志 8. 返回结构化结果1. 参数对象
@DatapublicclassReleaseHistoryQuery{@NotBlank(message="项目ID不能为空")privateStringprojectId;@Min(value=1,message="limit不能小于1")@Max(value=20,message="limit不能大于20")privateIntegerlimit=10;}2. 工具执行 Service
@Service@RequiredArgsConstructorpublicclassReleaseHistoryToolService{privatefinalProjectPermissionServiceprojectPermissionService;privatefinalReleaseRecordMapperreleaseRecordMapper;privatefinalAgentAuditLogServiceagentAuditLogService;publicToolExecuteResultexecute(LonguserId,ReleaseHistoryQueryquery){projectPermissionService.checkReadPermission(userId,query.getProjectId());List<ReleaseRecord>records=releaseRecordMapper.selectRecent(query.getProjectId(),query.getLimit());List<ReleaseRecordVO>result=records.stream().map(this::convertToSafeView).toList();agentAuditLogService.record(userId,"get_release_history",query.getProjectId(),"SUCCESS");returnToolExecuteResult.success(result);}privateReleaseRecordVOconvertToSafeView(ReleaseRecordrecord){ReleaseRecordVOvo=newReleaseRecordVO();vo.setVersion(record.getVersion());vo.setEnvironment(record.getEnvironment());vo.setStatus(record.getStatus());vo.setReleaseTime(record.getReleaseTime());returnvo;}}文字说明:
- 工具调用之前必须做项目权限校验;
- 不要把数据库实体原样返回给模型;
- 应该通过 VO 过滤内部字段;
- 每次工具执行都应记录审计日志;
- 工具失败时应该返回可控错误信息,而不是数据库异常堆栈。
七、为什么不要让 MCP Server 直接暴露数据库能力
有些人会想:
既然 Agent 要查数据,那我干脆提供一个 execute_sql 工具。例如:
{"name":"execute_sql","arguments":{"sql":"SELECT * FROM user"}}这在生产环境中风险非常高。
模型可能因为理解偏差、提示词注入或参数构造错误,生成危险 SQL:
DELETEFROMuser;或者越权查询:
SELECT*FROMemployee_salary;更合理的方式是提供业务语义明确的工具:
get_order_status list_user_orders get_project_release_history search_error_logs get_api_detail这样可以让后端控制:
- 查询范围;
- 可返回字段;
- 分页上限;
- 权限逻辑;
- 敏感字段脱敏;
- 操作审计。
核心原则是:
模型负责表达意图,后端负责执行受控业务能力。
八、MCP Server 中的安全设计
1. 工具白名单
不要让客户端随意调用任意内部能力。
privatestaticfinalSet<String>ALLOWED_TOOLS=Set.of("search_project_document","get_release_history","get_api_detail");当收到工具调用时:
publicvoidcheckToolAllowed(StringtoolName){if(!ALLOWED_TOOLS.contains(toolName)){thrownewBusinessException("不允许调用该工具");}}2. 参数校验
模型生成的参数并不可靠。
例如它可能传:
{"limit":999999}所以必须做:
- 类型校验;
- 必填校验;
- 长度校验;
- 枚举校验;
- 数值范围校验;
- 业务规则校验。
3. 高风险操作必须确认
以下操作不能因为模型调用了工具就立即执行:
- 删除数据;
- 修改权限;
- 发布生产环境;
- 发起支付;
- 发送批量通知;
- 调整库存;
- 执行退款。
推荐的流程:
Agent 生成操作计划 -> 后端返回待确认信息 -> 用户确认 -> 后端执行 -> 返回执行结果例如:
即将发布项目 project-a 到测试环境,版本为 v1.2.0。 本次操作会重启 2 个服务实例,是否确认?4. 审计日志
建议至少记录:
用户ID 会话ID 工具名称 请求参数摘要 执行结果 耗时 时间 失败原因但日志中不要记录完整 Token、密码、密钥和敏感业务字段。
九、MCP 与 RAG 如何配合
MCP 不等于 RAG。
RAG 主要解决“从文档中检索知识”,MCP 主要解决“以标准协议连接工具与资源”。
但它们可以结合。
例如用户问:
测试环境最近一次发布是什么时候?发布后错误率有没有升高?Agent 可以拆成两个步骤:
步骤1:通过 MCP Tool 查询最近发布记录。 步骤2:通过 MCP Tool 查询发布前后的错误日志或监控指标。 步骤3:结合结果生成分析结论。如果用户问:
项目的灰度发布流程是什么?这更适合使用 RAG,从发布规范文档中检索答案。
简单记忆:
稳定说明文档 -> RAG 实时系统数据 -> MCP Tool 用户历史偏好 -> Memory 固定高风险流程 -> Workflow十、实际开发建议
1. 先从只读工具开始
第一次做 MCP Server,建议先提供只读能力:
查询项目文档 查询接口说明 查询发布记录 查询日志摘要 查询服务健康状态只读工具的风险更低,也更容易调试。
2. 工具粒度不要太粗
不推荐:
manage_project operate_system execute_database_command推荐:
get_project_info get_release_history search_project_document get_service_health search_error_logs工具名称越清晰,模型选择工具越稳定,后端也越容易控制权限。
3. 工具返回要简洁、结构化
不要把几百 KB 原始日志直接返回给模型。
推荐返回:
{"serviceName":"order-service","timeRange":"2026-07-18 10:00:00 ~ 2026-07-18 10:10:00","errorCount":12,"topErrors":[{"type":"DatabaseTimeoutException","count":8}]}模型需要的是能用于推理和回答的信息,不是无限量原始数据。
十一、总结
这一篇我们认识了 MCP 的基本作用和开发边界。
重点可以记住:
- MCP 是 Agent 连接外部工具、资源和提示词模板的标准协议。
- MCP 包含 Host、Client、Server 三类角色。
- Tool 用于执行受控能力,Resource 用于读取资料,Prompt 用于复用提示词模板。
- MCP 与 Tool Calling 可以配合,但两者不是同一个概念。
- MCP Server 不应该直接暴露任意 SQL、Shell 或内部高危操作。
- 权限校验、参数校验、脱敏、审计日志必须由后端负责。
- 高风险操作需要显式确认,不能只靠模型判断。
- 初学阶段建议先实现只读、业务语义明确的工具。
下一篇我们继续学习多智能体协作:一个 Agent 不够用时,如何把规划、执行、审核等职责拆开。
