LangChain4j MCP(模型上下文协议)—— 小白也能懂的通俗版
🧩 先搞懂:什么是 MCP? MCP(Model Context Protocol)是 Anthropic 提出的一个开放标准协议,目的是让 LLM 能够以一种统一的方式连接外部工具和数据源。打个比方:以前每个工具都要写一套适配代码(像给每种插座买不同转换器),MCP 就是推出了一个"万能插座"——只要服务器实现了 MCP 协议,任何支持 MCP 的客户端都能自动发现并使用它的工具。
⚡ 核心结论一句话 创建一个 McpTransport → 搭建 McpClient → 生成 McpToolProvider → 通过 .toolProvider() 挂到 AI Service 上,LLM 就能调用远程服务器上暴露的所有工具和资源。
📋 文章结构总览
| 主题 | 作用 |
|---|---|
| MCP 传输层 | stdio / Streamable HTTP / 旧版 HTTP+SSE |
| MCP 客户端 | 管理与服务器的连接 |
| MCP 工具提供者 | 将 MCP 工具桥接到 AI Service |
| 工具过滤 | 只暴露需要的工具,减少幻觉风险 |
| 日志 | MCP 协议的日志消息处理 |
| 资源 (Resources) | 读取外部数据源(编程式 + 合成工具) |
| 提示词 (Prompts) | 使用 MCP 服务器预定义的提示词模板 |
| Docker + GitHub | 实战示例:用 MCP 操作 GitHub 仓库 |
| 直接调用 MCP | 绕过 AI Service,手动执行 MCP 工具 |
| 工具缓存 | 默认缓存工具列表,可手动刷新或禁用 |
一、MCP 传输层 —— 三种连接方式
MCP 规定了客户端和服务器之间的通信通道,LangChain4j 全部支持:
方式一:stdio(本地子进程) ⭐ 最常用
McpTransport transport = new StdioMcpTransport.Builder()
.command(List.of("/usr/bin/npm", "exec", "@modelcontextprotocol/server-everything@0.6.2"))
.logEvents(true) // 在日志中打印通信流量
.build();
把 MCP 服务器当作本地命令启动,通过 stdin/stdout 通信。适合开发调试和本地工具。
方式二:Streamable HTTP(可流式 HTTP) ⭐ 生产推荐
McpTransport transport = new StreamableHttpMcpTransport.Builder()
.url("http://localhost:3001/mcp")
.logRequests(true) // 打印请求
.logResponses(true) // 打印响应
.build();
标准的 HTTP 长连接,客户端发 POST 请求,服务器可以通过 SSE 流持续推送多个响应。
方式三:旧版 HTTP+SSE(已弃用) ⚠️
McpTransport transport = new HttpMcpTransport.Builder()
.sseUrl("http://localhost:3001/sse")
.logRequests(true)
.logResponses(true)
.build();
需要两个 URL(SSE 端点 + POST 端点)。已被官方弃用,未来会移除。新项目不要用这种方式。
注意:Streamable HTTP 目前不会创建全局 SSE 流,依赖"服务器主动发起通知"的功能可能无法工作。如果服务器把通知附在客户端操作的 SSE 流里返回,则正常工作。
二、三步创建 MCP 工具提供者
第 1 步:创建 MCP 客户端
McpClient mcpClient = new DefaultMcpClient.Builder()
.key("MyMCPClient") // ← 可选但推荐,多客户端时用于区分
.transport(transport)
.build();
第 2 步:创建工具提供者
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.build();
第 3 步:绑定到 AI Service
Bot bot = AiServices.builder(Bot.class)
.chatModel(model)
.toolProvider(toolProvider) // ← 关键:把 MCP 工具注入进来
.build();
也可以直接提供工具映射:
Map<ToolSpecification, ToolExecutor> tools = mcpClient.listTools().stream().collect(Collectors.toMap(
tool -> tool,
tool -> new McpToolExecutor(mcpClient)
));
Bot bot = AiServices.builder(Bot.class)
.chatModel(model)
.tools(tools)
.build();
三、工具过滤 —— 只暴露需要的工具
MCP 服务器可能暴露几十个工具,但你只需要其中几个。用过滤器可以减少 token 消耗和幻觉风险:
按名称过滤:
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.filterToolNames("get_issue", "get_issue_comments", "list_issues") // 只看 issue
.build();
这样 AI 服务只能用这 3 个工具来读取 issue 和评论,不能创建新 issue。
按自定义逻辑过滤:
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient1, mcpClient2)
.filter((mcpClient, tool) ->
!tool.name().startsWith("echoInteger") ||
mcpClient.key().equals("numeric-mcp")
)
.build();
同一个 builder 上多次调用
filter会形成逻辑 AND 关系。运行时还可以动态增删客户端和过滤器。
容错策略:
.failIfOneServerFails(false) // 默认:忽略某个服务器的错误,继续其他
.failIfOneServerFails(true) // 任意服务器出错就抛异常
四、日志
MCP 协议定义了服务器向客户端发送日志消息的机制。默认转为 SLF4J 日志,自定义只需实现接口:
McpClient mcpClient = new DefaultMcpClient.Builder()
.transport(transport)
.logMessageHandler(new MyLogMessageHandler()) // ← 自定义日志处理
.build();
五、资源 (Resources) —— 让 LLM 读取外部数据
资源有两种使用方式:
方式一:编程式访问(你手动调)
// 列出所有资源
List<McpResource> resources = client.listResources();
// 列出资源模板(带 URI 参数的资源)
List<McpResourceTemplate> templates = client.listResourceTemplates();
// 读取指定资源内容
McpReadResourceResult result = client.readResource("file:///data/config.json");
// result.getResources() 返回 McpResourceContents 列表
// 可能是文本 (McpTextResourceContents) 或二进制 (McpBlobResourceContents)
方式二:自动暴露为合成工具(让 LLM 自己调)
构建 McpToolProvider 时设置 McpResourcesAsToolsPresenter:
var presenter = new DefaultMcpResourcesAsToolsPresenter.Builder()
// 可以自定义合成工具的描述文案
.build();
McpToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.resourcesAsTools(presenter) // ← 开启
.build();
会自动增加两个合成工具:
list_resources— 列出所有可用资源get_resource— 读取指定资源内容(需要 MCP 服务器名 + URI)
每个资源由 (mcpServer, uri) 唯一标识。
六、提示词 (Prompts) —— 复用服务器预定义的模板
MCP 服务器可以提供预定义的提示词模板:
// 获取所有可用的提示词
List<McpPrompt> prompts = client.listPrompts();
// 渲染并执行提示词
McpPromptMessage prompt = client.getPrompts("summarize_repo", Map.of("repo", "langchain4j"));
// 转换为 ChatMessage 供 LLM 使用
ChatMessage chatMessage = prompt.toChatMessage();
提示词内容支持多种类型:McpTextContent、McpImageContent、McpEmbeddedResource。
限制:如果角色是
assistant且内容不是文本 → 抛出异常;含二进制内容 → 无法转换。
七、实战:通过 Docker 使用 GitHub MCP 服务器
用一个完整的例子串起来:让 GPT 总结 LangChain4j 仓库最近 3 次提交。
前置准备:
# 1. 打包 GitHub MCP 服务器
docker build -t mcp/github -f src/github/Dockerfile .
# 2. 确认镜像存在
docker image ls
# REPOSITORY TAG IMAGE ID SIZE
# mcp/github latest b141704170b1 173MB
完整代码:
public static void main(String[] args) throws Exception {
// 1. 配置 LLM
ChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.logRequests(true)
.logResponses(true)
.build();
// 2. 通过 Docker 运行 GitHub MCP 服务器(stdio 传输)
McpTransport transport = new StdioMcpTransport.Builder()
.command(List.of("/usr/local/bin/docker", "run", "-e",
"GITHUB_PERSONAL_ACCESS_TOKEN", "-i", "mcp/github"))
.logEvents(true)
.build();
// 3. 创建 MCP 客户端
McpClient mcpClient = new DefaultMcpClient.Builder()
.key("github-mcp")
.transport(transport)
.build();
// 4. 创建工具提供者
ToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();
// 5. 绑定到 AI Service
Bot bot = AiServices.builder(Bot.class)
.chatModel(model)
.toolProvider(toolProvider)
.build();
try {
String response = bot.chat(
"Summarize the last 3 commits of the LangChain4j GitHub repository"
);
System.out.println("RESPONSE: " + response);
} finally {
mcpClient.close(); // 记得关闭!
}
}
输出效果:
RESPONSE: Here are the summaries of the last three commits in the LangChain4j GitHub repository:
1. Commit 36951f9 (2025-02-05) by Dmytro Liubarskyi
- Updated to `upload-pages-artifact@v3`
2. Commit 6fcd19f (2025-02-05) by Dmytro Liubarskyi
- Updated to `checkout@v4`, `deploy-pages@v4`, `upload-pages-artifact@v4`
3. Commit 2e74049 (2025-02-05) by Dmytro Liubarskyi
- Updated to `setup-node@v4`, `configure-pages@v4`
八、不使用 AI Service 直接调用 MCP
除了高层 API,也可以用底层 API 手动控制:
// 1. 列出 MCP 服务器上的所有工具
List<ToolSpecification> toolSpecifications = mcpClient.listTools();
// 2. 构造请求,带上这些工具
ChatRequest chatRequest = ChatRequest.builder()
.messages(UserMessage.from("What will the weather be like in London tomorrow?"))
.toolSpecifications(toolSpecifications)
.build();
ChatResponse response = chatModel.chat(chatRequest);
AiMessage aiMessage = response.aiMessage();
// 3. 如果 LLM 要调用工具,手动执行
if (aiMessage.hasToolExecutionRequests()) {
for (ToolExecutionRequest req : aiMessage.toolExecutionRequests()) {
String resultString = mcpClient.executeTool(req);
ToolExecutionResultMessage resultMessage =
ToolExecutionResultMessage.from(req.id(), req.name(), resultString);
// 把结果喂回给 LLM...
}
}
直接执行某个工具:
ToolExecutionRequest request = ToolExecutionRequest.builder()
.name("tool1")
.arguments("{\"a\": \"b\"}")
.build();
String toolResult = mcpClient.executeTool(request);
九、工具缓存说明
DefaultMcpClient 内部维护了一个工具列表缓存:
- 默认行为:首次获取后不再重复请求,除非服务器发来更新通知
- 手动清除缓存:
mcpClient.evictToolListCache(); - 完全禁用缓存(每次都用最新列表):
McpClient mcpClient = new DefaultMcpClient.Builder()
.key("MyMCPClient")
.transport(transport)
.cacheToolList(false) // ← 禁用缓存
.build();
🎯 面试高频追问
Q1:MCP 和我们之前学的 @Tool 有什么区别?
答:
@Tool是你自己在 Java 代码里定义的工具方法,由 LangChain4j 本地执行;MCP 则是通过一个标准化协议连接到外部 MCP 服务器(可以是 Node.js/Python/Go 写的独立服务),工具由远端服务器提供和执行。MCP 本质上是工具的"跨语言、跨进程分发协议"。
Q2:为什么 MCP 被称为"USB-C 之于 AI"?
答:就像 USB-C 统一了各种外设的连接标准一样,MCP 试图统一 LLM 接入外部工具的协议。以前每个工具都要单独写适配器,现在只要服务端实现 MCP 协议,任何 MCP 客户端都能自动发现和使用它。Anthropic 提出这个协议的目的就是成为 AI 时代的通用接口标准。
Q3:stdio 和 Streamable HTTP 两种传输方式怎么选?
答:开发调试阶段用 stdio 最简单——直接把 MCP 服务器当本地命令启动就行。生产环境推荐 Streamable HTTP——更稳定、更适合分布式部署,客户端和服务端通过网络通信而非父子进程。
Q4:多个 MCP 服务器同时使用时要注意什么?
答:① 给每个客户端设唯一的
key;② 用filterToolNames或自定义BiPredicate过滤掉冲突或不需要的工具;③ 设置failIfOneServerFails决定容错策略;④ 注意工具总数不能超过 LLM 的 context window。
Q5:资源和工具有什么区别?
答:工具是可以执行操作的(比如创建 issue、发送邮件),资源是被动读取的数据(比如文件内容、数据库记录)。MCP 把它们分开设计,资源通过
list_resources/get_resource两个合成工具暴露给 LLM,而工具则正常注册到工具列表中。
✅ 总结
MCP 是 LangChain4j 连接外部世界的桥梁:
- 传输层三路并行:stdio(开发首选)/ Streamable HTTP(生产推荐)/ 旧版 HTTP+SSE(已弃用)
- 三步上手:Transport → Client → ToolProvider → 挂到 AI Service
- 工具过滤:按名称或自定义逻辑筛选,减少幻觉和 token 浪费
- 容错策略:单服务器故障可忽略也可报错,灵活可控
- 资源系统:编程式读取 + 合成工具自动暴露,让 LLM 自主拉取数据
- 提示词模板:复用 MCP 服务器预定义的 Prompt 模板
- Docker 实战:一行命令启动 GitHub MCP 服务器,让 LLM 直接操作 Git 仓库
- 低层 API:不依赖 AI Service 也能手动列出、执行 MCP 工具
- 工具缓存:默认缓存提升性能,支持手动刷新或完全禁用
更多推荐


所有评论(0)