【AI Agent基础 | 第五篇】简析MCP(模型上下文协议)

前言

本文是我近期学习MCP相关的知识并结合AI整理总结而来,一是为了记录,二是用于日后的回顾,三也是希望能其给他初学者带来一点点帮助。


目录

什么是MCP

MCP的核心架构

[MCP Host](#MCP Host)

[MCP Client](#MCP Client)

[MCP Server](#MCP Server)

MCP的工作原理

[MCP与Function Calling的区别](#MCP与Function Calling的区别)

代码示例


在大模型应用越来越普及之后,一个问题变得越来越明显:模型本身很强,但它和外部世界之间的连接方式并不统一。

MCP,正是在这样的背景下出现的。

什么是MCP

MCP,中文一般翻译为模型上下文协议。它是一个开放协议,目的是为大模型应用与外部数据源、工具、工作流之间的连接提供统一标准。只要双方都遵循 MCP,就能以更标准化的方式进行连接。

简单来说,MCP 解决的是这样一个问题:

如何让 AI 不仅会说,还能够标准化地获取上下文和调用外部能力。

它并不是某一个具体的模型,也不是某一家公司的专属框架,而更像一套协议规范。只要一个 AI 客户端支持 MCP,它就可以接入不同的 MCP Server;只要一个外部系统实现成 MCP Server,它就能被不同的 AI 应用复用。

MCP的核心架构

MCP Host

MCP Host 就是承载大模型能力的宿主程序,也可以理解为用户真正接触到的 AI 应用。

比如 Claude Desktop、Cursor、一些带 AI 能力的 IDE,或者其他智能助手类产品,都可以看作 MCP Host。

它主要负责几件事:

  • 接收用户输入
  • 触发模型处理任务
  • 在内部调用 MCP Client
  • 把最终结果返回给用户

所以 Host 更像是整个 MCP 体系的入口,用户并不会直接接触到底层 Server,而是先通过 Host 发起请求。

MCP Client

MCP Client 位于 Host 内部,主要作用是负责和 MCP Server 建立连接,并完成双方之间的通信。

它相当于大模型和外部能力之间的桥梁。

通常情况下,一个 MCP Client 会和一个 MCP Server 保持 1:1 的连接关系。Client 不负责业务逻辑本身,它更像一个协调者,负责把用户请求、模型决策和服务调用串起来。

它的大致工作流程可以概括为下面几步:

  1. 先从 MCP Server 获取当前可用的工具、资源和提示信息
  2. 再把用户的问题和这些能力描述一起交给大模型
  3. 由大模型判断当前是否需要调用工具
  4. 如果需要,Client 就向 MCP Server 发起对应调用
  5. Server 返回结果后,Client 再把结果交回给模型
  6. 最后由模型生成自然语言结果,展示给用户

也就是说,MCP Client 本身并不提供能力,它主要负责连接、转发、执行、回传这一整套过程。

从实际落地来看,很多支持 MCP 的产品,本质上都内置了 MCP Client。比如 Claude Desktop 和 Cursor,它们能够接入各种 MCP Server,本质上就是因为内部具备这层能力。

MCP Server

MCP Server 是整个架构里真正提供能力的一端。

它负责向 Client 暴露可用的资源、工具以及提示模板,让大模型在需要的时候可以调用。

从功能上看,MCP Server 主要可以提供三类内容:

  • 资源:类似文件的数据,可以被客户端读取,如 API 响应或文件内容。
  • 工具:可以被 LLM 调用的函数。
  • 提示:预先编写的模板,帮助用户完成特定任务。

MCP的工作原理

1. 初始化连接

客户端首先会向服务器发起连接请求,用来建立双方之间的通信通道。

只有连接建立完成之后,后续的请求和响应才能正常进行。

2. 发送请求

当用户发出指令,或者模型判断需要调用某种外部能力时,客户端就会按照协议格式构建请求消息,并将其发送给服务器。

这个请求里通常会包含操作类型、参数信息以及目标资源等内容。

3. 处理请求

服务器接收到请求之后,会先解析请求内容,再根据具体需求执行对应操作。比如有的请求是读取本地文件,有的是查询数据库,还有的可能是调用某个远程 API。也就是说,Server 本身更像一个统一调度层,负责把请求分发到真正的资源侧去执行。

4. 返回结果

当请求处理完成后,服务器会把结果按照统一格式封装成响应消息,再返回给客户端。客户端拿到这些结果之后,可以继续交给大模型进行理解、总结和生成,最后输出成自然语言内容。

5. 断开连接

等当前任务执行完成之后,客户端可以选择主动断开连接;如果没有继续通信,服务器也可以在超时后自动关闭连接。这样做有助于释放资源,也方便管理整个通信生命周期。

MCP与Function Calling的区别

Function Calling 和 MCP 都是在解决大模型调用外部工具的问题,但两者的定位并不一样。Function Calling 更像是一种具体的工具调用机制,重点在于让大模型判断当前是否需要调用某个函数,并返回对应参数来完成单次任务。它实现起来相对直接,但通常更依赖模型本身具备稳定的工具调用能力,而且很多时候缺少统一标准,往往需要跟着厂商接口或者自己做适配,扩展到多个工具、多个系统时,定制化成本会比较高。相比之下,MCP 更像是一套统一的连接协议,不只是管工具调用,还把资源访问、提示模板、上下文传递这些能力一起纳入进来,让大模型和外部服务之间的交互方式更标准化。

从使用场景来看,Function Calling 更适合简单、明确的单步调用场景,比如调用某个函数查一次天气、查一次订单,重点是把这一次任务执行完;而 MCP 更适合更复杂的系统接入场景,它可以统一连接不同的工具和数据源,也更方便支撑多轮对话、组合调用和后续扩展。你也可以把两者理解成:Function Calling 解决的是"模型怎么调用某个函数",而 MCP 解决的是"模型怎么用统一方式接入外部世界"。所以前者更偏具体能力,后者更偏底层协议和生态组织能力,在项目规模变大、工具变多之后,MCP 的优势通常会更明显。

代码示例

java 复制代码
@Slf4j
public abstract class BaseMcpService {

    protected final WebClient webClient;
    protected final ObjectMapper objectMapper;
    protected final int timeout;

    protected BaseMcpService(String serverUrl, int timeout) {
        this.timeout = timeout;
        this.objectMapper = new ObjectMapper();

        if (serverUrl != null && !serverUrl.isBlank()) {
            this.webClient = WebClient.builder()
                    .baseUrl(serverUrl)
                    .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(10 * 1024 * 1024))
                    .build();
        } else {
            this.webClient = null;
        }
    }

    protected boolean isServerAvailable() {
        return webClient != null;
    }

    protected Mono<Map> callTool(String toolName, Map<String, Object> arguments) {
        if (!isServerAvailable()) {
            log.warn("MCP server not available for tool: {}", toolName);
            return Mono.empty();
        }

        Map<String, Object> request = Map.of(
                "jsonrpc", "2.0",
                "method", "tools/call",
                "params", Map.of(
                        "name", toolName,
                        "arguments", arguments
                )
        );

        return webClient.post()
                .uri("/mcp")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(Map.class)
                .timeout(Duration.ofSeconds(timeout))
                .doOnError(e -> log.error("Error calling MCP tool {}: {}", toolName, e.getMessage()));
    }

    protected Mono<Map> listTools() {
        if (!isServerAvailable()) {
            return Mono.empty();
        }

        Map<String, Object> request = Map.of(
                "jsonrpc", "2.0",
                "method", "tools/list"
        );

        return webClient.post()
                .uri("/mcp")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(Map.class)
                .timeout(Duration.ofSeconds(timeout));
    }

    public abstract String getServiceName();
    public abstract boolean isEnabled();
}
java 复制代码
public SearchResult search(String query, int maxResults) {
    log.info("Tavily MCP search for: {}", query);

    if (!isEnabled()) {
        log.warn("Tavily MCP is disabled");
        return SearchResult.empty("Tavily MCP is disabled");
    }

    if (!isServerAvailable()) {
        log.info("Tavily MCP server not available, using direct API");
        return searchDirect(query, maxResults);
    }

    try {
        Map<String, Object> arguments = new HashMap<>();
        arguments.put("query", query);
        arguments.put("max_results", maxResults);
        if (apiKey != null && !apiKey.isBlank()) {
            arguments.put("api_key", apiKey);
        }

        Map result = callTool("tavily-search", arguments)
                .block(Duration.ofSeconds(timeout));

        return parseSearchResult(result);
    } catch (Exception e) {
        log.error("Tavily MCP search failed: {}", e.getMessage());
        return searchDirect(query, maxResults);
    }
}

上述是我的深度搜索个人项目里面使用到的MCP部分代码。

具体流程是,我先抽了一个 BaseMcpService,把所有 MCP 相关的公共逻辑都放进去,比如 WebClient 初始化、超时控制,还有 JSON-RPC 请求的封装。这样后面不管接 Tavily、Jina 还是 Obsidian,都走同一套调用方式,代码会比较整齐。

使用的时候,我是按工具类型分别封装服务的。比如要做网页搜索,就走 TavilyMcpService;要读取网页正文,就走 JinaMcpService;研究完成后要把内容存进知识库,就走 ObsidianMcpService。本质上都是后端主动发 tools/call 请求,只是调用的 tool name 不一样,比如 tavily-searchjina-readobsidian-create-note


上述内容也同步在我的飞书,欢迎访问

https://my.feishu.cn/wiki/QLauws6lWif1pnkhB8IcAvkhncc?from=from_copylink

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,你们的支持就是我坚持下去的动力!

相关推荐
Learn Beyond Limits2 小时前
双向循环神经网络|Bi-RNN(Bidirectional Recurrent Neural Networks)
人工智能·rnn·深度学习·神经网络·语言模型·自然语言处理·nlp
咚咚王者2 小时前
人工智能之语音领域 语音处理 第四章 语音与文本、图像的多模态融合应用
人工智能
arvin_xiaoting2 小时前
使用 exo 技术构建 Mac mini AI 推理集群:从架构到实战
人工智能·macos·架构·mac mini·exo
Figo_Cheung2 小时前
Figo人机交互中“疯态”边界的引导与驯化————“可控赛博疯态”动态机制与实现路径研究
人工智能·人机交互
Cvmax2 小时前
LiblibAI 到底强在哪
人工智能
Agent产品评测局2 小时前
保险行业自动化工具选型,核保理赔全流程优化:2026年大模型Agent重塑数智金融新基座
大数据·人工智能·ai·金融·自动化
steem_ding2 小时前
AI核心概念解析:Agent、Prompt、Skill 及生态关系
人工智能·prompt
景联文科技2 小时前
高质量数据集驱动工业智能跃迁——景联文科技亮相浙江数商发展推进会
大数据·人工智能