LangChain4j MCP(模型上下文协议)—— 小白也能懂的通俗版

发布时间:2026/7/1 19:04:40

LangChain4j MCP(模型上下文协议)—— 小白也能懂的通俗版 先搞懂什么是 MCPMCPModel Context Protocol是 Anthropic 提出的一个开放标准协议目的是让 LLM 能够以一种统一的方式连接外部工具和数据源。打个比方以前每个工具都要写一套适配代码像给每种插座买不同转换器MCP 就是推出了一个万能插座——只要服务器实现了 MCP 协议任何支持 MCP 的客户端都能自动发现并使用它的工具。⚡核心结论一句话创建一个McpTransport→ 搭建McpClient→ 生成McpToolProvider→ 通过.toolProvider()挂到 AI Service 上LLM 就能调用远程服务器上暴露的所有工具和资源。 文章结构总览主题作用MCP 传输层stdio / Streamable HTTP / 旧版 HTTPSSEMCP 客户端管理与服务器的连接MCP 工具提供者将 MCP 工具桥接到 AI Service工具过滤只暴露需要的工具减少幻觉风险日志MCP 协议的日志消息处理资源 (Resources)读取外部数据源编程式 合成工具提示词 (Prompts)使用 MCP 服务器预定义的提示词模板Docker GitHub实战示例用 MCP 操作 GitHub 仓库直接调用 MCP绕过 AI Service手动执行 MCP 工具工具缓存默认缓存工具列表可手动刷新或禁用一、MCP 传输层 —— 三种连接方式MCP 规定了客户端和服务器之间的通信通道LangChain4j 全部支持方式一stdio本地子进程⭐ 最常用McpTransporttransportnewStdioMcpTransport.Builder().command(List.of(/usr/bin/npm,exec,modelcontextprotocol/server-everything0.6.2)).logEvents(true)// 在日志中打印通信流量.build();把 MCP 服务器当作本地命令启动通过 stdin/stdout 通信。适合开发调试和本地工具。方式二Streamable HTTP可流式 HTTP⭐ 生产推荐McpTransporttransportnewStreamableHttpMcpTransport.Builder().url(http://localhost:3001/mcp).logRequests(true)// 打印请求.logResponses(true)// 打印响应.build();标准的 HTTP 长连接客户端发 POST 请求服务器可以通过 SSE 流持续推送多个响应。方式三旧版 HTTPSSE已弃用⚠️McpTransporttransportnewHttpMcpTransport.Builder().sseUrl(http://localhost:3001/sse).logRequests(true).logResponses(true).build();需要两个 URLSSE 端点 POST 端点。已被官方弃用未来会移除。新项目不要用这种方式。注意Streamable HTTP 目前不会创建全局 SSE 流依赖服务器主动发起通知的功能可能无法工作。如果服务器把通知附在客户端操作的 SSE 流里返回则正常工作。二、三步创建 MCP 工具提供者第 1 步创建 MCP 客户端McpClientmcpClientnewDefaultMcpClient.Builder().key(MyMCPClient)// ← 可选但推荐多客户端时用于区分.transport(transport).build();第 2 步创建工具提供者McpToolProvidertoolProviderMcpToolProvider.builder().mcpClients(mcpClient).build();第 3 步绑定到 AI ServiceBotbotAiServices.builder(Bot.class).chatModel(model).toolProvider(toolProvider)// ← 关键把 MCP 工具注入进来.build();也可以直接提供工具映射MapToolSpecification,ToolExecutortoolsmcpClient.listTools().stream().collect(Collectors.toMap(tool-tool,tool-newMcpToolExecutor(mcpClient)));BotbotAiServices.builder(Bot.class).chatModel(model).tools(tools).build();三、工具过滤 —— 只暴露需要的工具MCP 服务器可能暴露几十个工具但你只需要其中几个。用过滤器可以减少 token 消耗和幻觉风险按名称过滤McpToolProvidertoolProviderMcpToolProvider.builder().mcpClients(mcpClient).filterToolNames(get_issue,get_issue_comments,list_issues)// 只看 issue.build();这样 AI 服务只能用这 3 个工具来读取issue 和评论不能创建新 issue。按自定义逻辑过滤McpToolProvidertoolProviderMcpToolProvider.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 日志自定义只需实现接口McpClientmcpClientnewDefaultMcpClient.Builder().transport(transport).logMessageHandler(newMyLogMessageHandler())// ← 自定义日志处理.build();五、资源 (Resources) —— 让 LLM 读取外部数据资源有两种使用方式方式一编程式访问你手动调// 列出所有资源ListMcpResourceresourcesclient.listResources();// 列出资源模板带 URI 参数的资源ListMcpResourceTemplatetemplatesclient.listResourceTemplates();// 读取指定资源内容McpReadResourceResultresultclient.readResource(file:///data/config.json);// result.getResources() 返回 McpResourceContents 列表// 可能是文本 (McpTextResourceContents) 或二进制 (McpBlobResourceContents)方式二自动暴露为合成工具让 LLM 自己调构建McpToolProvider时设置McpResourcesAsToolsPresentervarpresenternewDefaultMcpResourcesAsToolsPresenter.Builder()// 可以自定义合成工具的描述文案.build();McpToolProvidertoolProviderMcpToolProvider.builder().mcpClients(mcpClient).resourcesAsTools(presenter)// ← 开启.build();会自动增加两个合成工具list_resources— 列出所有可用资源get_resource— 读取指定资源内容需要 MCP 服务器名 URI每个资源由(mcpServer, uri)唯一标识。六、提示词 (Prompts) —— 复用服务器预定义的模板MCP 服务器可以提供预定义的提示词模板// 获取所有可用的提示词ListMcpPromptpromptsclient.listPrompts();// 渲染并执行提示词McpPromptMessagepromptclient.getPrompts(summarize_repo,Map.of(repo,langchain4j));// 转换为 ChatMessage 供 LLM 使用ChatMessagechatMessageprompt.toChatMessage();提示词内容支持多种类型McpTextContent、McpImageContent、McpEmbeddedResource。限制如果角色是assistant且内容不是文本 → 抛出异常含二进制内容 → 无法转换。七、实战通过 Docker 使用 GitHub MCP 服务器用一个完整的例子串起来让 GPT 总结 LangChain4j 仓库最近 3 次提交。前置准备# 1. 打包 GitHub MCP 服务器dockerbuild-tmcp/github-fsrc/github/Dockerfile.# 2. 确认镜像存在dockerimagels# REPOSITORY TAG IMAGE ID SIZE# mcp/github latest b141704170b1 173MB完整代码publicstaticvoidmain(String[]args)throwsException{// 1. 配置 LLMChatModelmodelOpenAiChatModel.builder().apiKey(System.getenv(OPENAI_API_KEY)).modelName(gpt-4o-mini).logRequests(true).logResponses(true).build();// 2. 通过 Docker 运行 GitHub MCP 服务器stdio 传输McpTransporttransportnewStdioMcpTransport.Builder().command(List.of(/usr/local/bin/docker,run,-e,GITHUB_PERSONAL_ACCESS_TOKEN,-i,mcp/github)).logEvents(true).build();// 3. 创建 MCP 客户端McpClientmcpClientnewDefaultMcpClient.Builder().key(github-mcp).transport(transport).build();// 4. 创建工具提供者ToolProvidertoolProviderMcpToolProvider.builder().mcpClients(List.of(mcpClient)).build();// 5. 绑定到 AI ServiceBotbotAiServices.builder(Bot.class).chatModel(model).toolProvider(toolProvider).build();try{Stringresponsebot.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-artifactv3 2. Commit 6fcd19f (2025-02-05) by Dmytro Liubarskyi - Updated to checkoutv4, deploy-pagesv4, upload-pages-artifactv4 3. Commit 2e74049 (2025-02-05) by Dmytro Liubarskyi - Updated to setup-nodev4, configure-pagesv4八、不使用 AI Service 直接调用 MCP除了高层 API也可以用底层 API 手动控制// 1. 列出 MCP 服务器上的所有工具ListToolSpecificationtoolSpecificationsmcpClient.listTools();// 2. 构造请求带上这些工具ChatRequestchatRequestChatRequest.builder().messages(UserMessage.from(What will the weather be like in London tomorrow?)).toolSpecifications(toolSpecifications).build();ChatResponseresponsechatModel.chat(chatRequest);AiMessageaiMessageresponse.aiMessage();// 3. 如果 LLM 要调用工具手动执行if(aiMessage.hasToolExecutionRequests()){for(ToolExecutionRequestreq:aiMessage.toolExecutionRequests()){StringresultStringmcpClient.executeTool(req);ToolExecutionResultMessageresultMessageToolExecutionResultMessage.from(req.id(),req.name(),resultString);// 把结果喂回给 LLM...}}直接执行某个工具ToolExecutionRequestrequestToolExecutionRequest.builder().name(tool1).arguments({\a\: \b\}).build();StringtoolResultmcpClient.executeTool(request);九、工具缓存说明DefaultMcpClient内部维护了一个工具列表缓存默认行为首次获取后不再重复请求除非服务器发来更新通知手动清除缓存mcpClient.evictToolListCache();完全禁用缓存每次都用最新列表McpClientmcpClientnewDefaultMcpClient.Builder().key(MyMCPClient).transport(transport).cacheToolList(false)// ← 禁用缓存.build(); 面试高频追问Q1MCP 和我们之前学的 Tool 有什么区别答Tool是你自己在 Java 代码里定义的工具方法由 LangChain4j 本地执行MCP 则是通过一个标准化协议连接到外部 MCP 服务器可以是 Node.js/Python/Go 写的独立服务工具由远端服务器提供和执行。MCP 本质上是工具的跨语言、跨进程分发协议。Q2为什么 MCP 被称为USB-C 之于 AI答就像 USB-C 统一了各种外设的连接标准一样MCP 试图统一 LLM 接入外部工具的协议。以前每个工具都要单独写适配器现在只要服务端实现 MCP 协议任何 MCP 客户端都能自动发现和使用它。Anthropic 提出这个协议的目的就是成为 AI 时代的通用接口标准。Q3stdio 和 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生产推荐/ 旧版 HTTPSSE已弃用三步上手Transport → Client → ToolProvider → 挂到 AI Service工具过滤按名称或自定义逻辑筛选减少幻觉和 token 浪费容错策略单服务器故障可忽略也可报错灵活可控资源系统编程式读取 合成工具自动暴露让 LLM 自主拉取数据提示词模板复用 MCP 服务器预定义的 Prompt 模板Docker 实战一行命令启动 GitHub MCP 服务器让 LLM 直接操作 Git 仓库低层 API不依赖 AI Service 也能手动列出、执行 MCP 工具工具缓存默认缓存提升性能支持手动刷新或完全禁用

相关新闻