Langchain4j - MCP 和 Tool Calling 的对比及其具体案例
MCP vs Tool Calling
各自功能定位
在大模型应用开发中,MCP(Model Context Protocol,模型上下文协议)和 Tool Calling(也称 Function Calling 工具调用) 是目前连接 LLM 与 “外部世界基础设施” 的两大核心技术。这两者并不是简单的替代关系,而是代表了两种不同的架构方式。
Tool Calling 是大模型厂商(如 OpenAI、Anthropic、阿里百炼)提供的一种闭环能力。你在代码中给大模型提供一份 Java 方法的 JSON Schema(即工具说明书),大模型根据用户的输入,决定是否要调用这个方法,并为你吐出结构化的参数。具体的代码执行、网络请求,全部需要你的微服务应用自己去跑。它属于大模型 API 交互规范的一部分(如 messages 列表中穿插的 role: tool)。
MCP 是由 Anthropic 在 2024 年底发起、现已成为行业标准的开源网络协议。就像是 USB-C 提供了一种标准化的方式将你的设备连接到各种外围设备和配件一样,MCP 提供了一种标准化的方式将AI模型连接到不同的数据源和工具。MCP 把架构解耦成了三层:大模型客户端 (App)、MCP 路由器/网关、独立运行的 MCP 服务。任何机构都可以写一个标准的 MCP Server(比如:GitHub MCP、PostgreSQL MCP)。大模型只要支持 MCP 协议,就可以即插即用、直接调取这些外部服务。MCP 是标准化的、跨语言的分布式总线架构。


实际如何选择
我们知道 MCP 不仅能实现 Tools(动作),而且还支持 Resources(让模型直接读文件/数据库)、Prompts(共享提示词模板)。并且 MCP 相比 Function Calling,维护成本也小很多,只要 App 和 Server 都符合 MCP 协议,换模型完全无感。这是否意味着我们在实际的开发中都选择 MCP 就行了?目前不建议这样做。MCP 统一天下的想法很美好,但在企业级实际落地中,它有明显的代价:
- 引入了额外的网络与体系复杂度:Function Calling 是在你的 Java 微服务进程内部反射执行的,性能极高。而 MCP 必须通过标准协议去走一次进程间通信(IPC)或网络通信(HTTP/WebSocket),这在高性能高并发场景下会带来额外的延迟和运维监控成本。
- 生态和工具链成熟度:在 Java 生态(Spring / LangChain4j)中,Function Calling 的高阶 API(如 @Tool)已经成熟到极致,开箱即用。而 MCP 现阶段在 Python 和 Node.js 生态中最为活跃,Java 对应的官方生态和 SDK 还在快速演进中。
当下我认为实际选型指南可以总结为 “内部业务用 Function,外部生态/公共设施用 MCP”。目前坚定选择 Function Calling 的场景:
- 强耦合的内部核心业务:如去本系统的订单表查数据、扣除用户钱包余额。这些逻辑紧贴你的微服务业务,不需要也不应该暴露给外部,直接用 @Tool 性能最高、开发最快。
- 高并发、低延迟场景:不需要跨进程通信,完全受控于你的微服务线程池(如 boundedElastic)。
建议引入 MCP 的场景:
- 对接成熟的公共基础设施:比如你的大模型应用需要直接读写 GitHub 仓库、查询外部 Slack 频道、查询标准的 PostgreSQL/ElasticSearch 数据库。那就别再自己手写 Function 了,直接去 GitHub 开源社区拉一个现成的 MCP Server 挂上,一分钟搞定。
- 多语言团队协作:AI 团队用 Python 写了一套非常牛的 Rag(检索增强)和工具集,而你的主站业务是 Java。可以让 Python 团队把工具打包成标准 MCP Server 暴露出来,你的 Java 端直接利用 MCP 客户端无缝调用,实现跨语言解耦。
总结来说就是:脏活、累活、通用活交给 MCP(买现成的外设);核心业务、高频调用、私有逻辑留给 Function Calling(自制集成芯片)。这里提供一个类似 maven repository 或 docker hub 的 MCP repository —— MCP.so,只要各大厂商开放了 MCP 协议,允许大模型的各种客户端来调用,基本都能在这里找到。
MCP 的架构和两种通信模式
核心架构:MCP 是一种 CS 架构,包含三个主体和两种资源。
- MCP 主机(MCP Hosts)
- 指的是发起 AI 请求并集成大模型能力的最终端侧的应用程序。
- 典型代表有 Claude Desktop、Cursor、VS Code、或者你用 LangChain4j 自研的平台系统。
- 它负责控制整个对话的生命周期,持有大模型的 API Key,知道什么时候该向用户展示什么界面。
- MCP 客户端(MCP Clients)
- 指的是内置在 MCP Host 内部的标准协议适配器。
- 它是 Host 的 “对外触角”。Host 要和外界通信时,Client 会严格按照 MCP 协议(通常是基于标准的 JSON-RPC 2.0 规范)将大模型的意图翻译成统一的底层指令,派发给各个 Server。一个 Client 可以同时挂载、调度多个不同的 MCP Server。
- MCP 服务器(MCP Servers)
- 通常指独立运行的、轻量级的外设微服务进程。
- 这是真正干脏活累活的地方。每个 Server 都是一个完全独立的进程,可以用 Python、Node.js、Go 或 Java 编写。它向 Client 暴露自己的能力说明书,并在接收到 Client 的 RPC 调用指令时,直接去操作其身后的底层资源。
MCP Server 本身不存核心业务,它是一个高效率的 “外交官”,专门连接两类资源:
本地资源(Local Resources):如 Server A 可以直接读写你电脑本地的特定文件夹(File System)、查询本地部署的 PostgreSQL 数据库,或者读取某个本地控制台的日志。远程资源(Remote Resources):如 Server C 部署在你的电脑里,但它拥有连接公网的能力,它通过标准的 Web APIs 去叩开 GitHub、Notion、Slack 等远程 SaaS 软件的大门。
MCP 通常基于两种传输模式:
- stdio(标准输入输出):如果 Server 和 Host 都在同一台电脑上(比如 Cursor 调度本地的一个 Python 脚本),它们直接通过系统的标准输入输出流进行极速进程间通信。
- SSE(Server-Sent Events / HTTP / WebSocket):如果 Server 部署在远端服务器上,它们则通过网络长连接通信。

为了说明整个数据流向是怎么跑通的,我们来举一个简单的例子。假设你对 AI(Host)说:“帮我把我电脑里的发票文件(Local Data Source A)打印出来,并把结果通知到我们公司的 Slack 群里(Remote Service B)。”

- Host 收到你的话,AI 大脑开始思考,发现自己没手没脚,得找帮手。
- MCP Client 立刻通过 MCP Protocol(数据线)同时呼叫两个扩展坞:
- 呼叫 Local MCP Server A:去把本地的数据拿给我。Server A 转身去 Data Source A 拿出文件,原路返回给 AI。
- 呼叫 Local MCP Server B:去把这个结果发到群里。Server B 转身通过 Web APIs 把消息打向互联网上的 Remote Service B。
- 两个扩展坞都干完活了,回复 AI “搞定”。AI 再转告你:“发票已处理并通知到群里了!”
MCP 的具体案例
准备工作
我们以 MCP.so 的 百度地图 为例,由于百度地图全面支持了 MCP 协议,我们把它变成我们自己构建的大模型应用的功能触角。在编码之前,个人需要 申请百度地图的开发者 AK,并配置 BAIDU_MAP_API_KEY 到环境变量,这里不再赘述。
1 | // https://github.com/baidu-maps/mcp |
依赖配置
父项目配置请参考 父项目 POM。
1 | <dependencies> |
原生方式使用MCP
1 | public static void main(String[] args) { |
高级方式使用 MCP
配置文件
application.yml
1 | server: |
声明 AiService
BaidumapAssistant
1 | public interface BaidumapAssistant { |
McpBaiduMapConfiguration:
1 | import demo02.service.BaidumapAssistant; |
业务测试类
BaidumapMcpController
1 |
|
控制台日志
1 | HTTP request: |