LangChain4j集成MCP

本文最后更新于 2026年8月14日

概述

本文基于LangChain4j 1.10.0,还是以12306-mcp为例,演示加载调用MCP,关于12306-mcp安装运行的前置准备,已经在以下文章提到:

使用LangChain4j调用MCP需要引入MCP的依赖langchain4j-mcp

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.4</version>
    <relativePath/>
</parent>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-bom</artifactId>
            <version>1.10.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai</artifactId>
    </dependency>

    <!-- MCP -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-mcp</artifactId>
    </dependency>
</dependencies>

同其他Agent框架一样,LangChain4j也是通过注入为工具来调用MCP。

1.加载

LangChain4j加载MCP,并不像Spring AI或LangChain那么简单,如加载外部mcp.json文件,需要自己完成加载MCP为工具的逻辑。

例:自定义加载逻辑,Stdio/HTTP通用

classpath:mcp-server.json

{
  "mcpServers": {
    "12306-mcp": {
      "transport": "stdio",
      "command": "cmd",
      "args": [
        "/c",
        "npx",
        "-y",
        "12306-mcp"
      ]
    }

  }
}
{
  "mcpServers": {
    "12306-mcp-http": {
      "transport": "http",
      "url": "https://mcp.api-inference.modelscope.net/************/mcp"
    }

  }
}

org.example.config.McpConfig

所有MCP工具加载进McpToolProvider对象

package org.example.config;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.mcp.client.transport.http.StreamableHttpMcpTransport;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import java.util.*;

@Configuration
@Slf4j
public class McpConfig {

    @Value("classpath:mcp-server.json")
    private Resource configFile;

    @Data
    static class McpServerConfig {
        private String name;
        private String transport;
        private String command;
        private List<String> args; // for stdio
        private String url; // for http
    }
    
    @Bean
    public McpToolProvider mcpToolProvider() {
        // 3. 用所有 MCP Client 创建 ToolProvider
        return McpToolProvider.builder()
                .mcpClients(this.loadConfig())
                // 设置为 false(默认)允许部分服务器启动失败,继续使用其他服务器
                .failIfOneServerFails(false)
                .build();
    }
    
    private List<McpClient> loadConfig() {
        // 1. 解析 JSON 配置文件
        ObjectMapper mapper = new ObjectMapper();

        List<McpServerConfig> configs = new ArrayList<>();

        try {

            JsonNode root  = mapper.readTree(configFile.getFile() );
            JsonNode serversNode = root.get("mcpServers");

            if (serversNode != null) {
                Iterator<Map.Entry<String, JsonNode>> fields = serversNode.fields();
                while (fields.hasNext()) {
                    Map.Entry<String, JsonNode> entry = fields.next();
                    McpServerConfig config = parseServerConfig(entry.getKey(), entry.getValue());
                    configs.add(config);
                }
            }

        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }


        // 2. 为每个服务器配置创建 MCP Client
        List<McpClient> mcpClients = new ArrayList<>();

        for (McpServerConfig serverConfig : configs ) {
            McpTransport transport = this.createTransport(serverConfig);
            if (transport != null) {
                DefaultMcpClient client = new DefaultMcpClient.Builder()
                        .transport(transport)
                        .build();

                mcpClients.add(client);
            }
        }

        return mcpClients;
    }
    
    private McpServerConfig parseServerConfig(String name, JsonNode node) {
        McpServerConfig config = new McpServerConfig();
        config.setName(name);
        config.setTransport(node.get("transport") != null ? node.get("transport").asText() : null);
        config.setCommand(node.get("command") != null ? node.get("command").asText() : null);
        config.setUrl(node.get("url") != null ? node.get("url").asText() : null);

        // 解析 args
        JsonNode argsNode = node.get("args");
        if (argsNode != null && argsNode.isArray()) {
            List<String> args = new ArrayList<>();
            argsNode.forEach(arg -> args.add(arg.asText()));
            config.setArgs(args);
        }

        return config;
    }


    /*
     *  根据配置创建对应的 Transport
     */
    private McpTransport createTransport(McpServerConfig config) {
        if ("stdio".equalsIgnoreCase(config.getTransport()) && StringUtils.hasText(config.getCommand()) ) {

            List<String> commandAndArgs = new ArrayList<>();
            commandAndArgs.add(config.getCommand());
            commandAndArgs.addAll(config.getArgs());

            StdioMcpTransport transport = new StdioMcpTransport.Builder()
                    .command(commandAndArgs)
                    .build();
            log.info("Using stdio transport {}", transport);
            return transport;
        }
        else if ("http".equalsIgnoreCase(config.getTransport()) && StringUtils.hasText(config.getUrl()) ) {
            StreamableHttpMcpTransport transport = new StreamableHttpMcpTransport.Builder()
                    .url(config.getUrl())
                    .build();
            log.info("Using http transport {}", transport);
            return transport;
        }
        else {
            log.info("未知的传输类型或配置错误");
            return null;
        }

    }

}

2.调用

ModelConfig配置类,将加载好的MCP作为工具给大模型使用,Stdio/HTTP通用。

org.example.config.ModelConfig

注入McpToolProvider,将加载的所有MCP工具注入,供大模型调用

package org.example.config;

import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.example.TestAssistant;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@Slf4j
public class ModelConfig {

    @Resource
    private McpToolProvider mcpToolProvider;

    @Bean
    public ChatModel chatModel() {
        //暂时关闭日志/思考
        return OpenAiChatModel.builder()
                .baseUrl("https://api.deepseek.com/v1")
                .apiKey(System.getenv("DEEPSEEK_API_KEY"))
                .modelName("deepseek-v4-pro")
                .maxRetries(3)
                .logRequests(false) 
                .logResponses(false)
                .returnThinking(false)
                .build();
    }

    @Bean
    public TestAssistant testAssistant () {
        return AiServices.builder(TestAssistant.class)
                .chatModel(chatModel())
                .toolProvider(mcpToolProvider) // 关键:注入包含所有 MCP 工具的提供者
                .build();
    }

}

org.example.TestAssistant

package org.example;

import dev.langchain4j.service.SystemMessage;

public interface TestAssistant {

    @SystemMessage("你是一个AI助手,可以使用所有可用的工具来帮助用户。")
    String chat(String userMessage);

}

org.example.test.McpTest

package org.example.test;

import jakarta.annotation.Resource;
import org.example.Main;
import org.example.TestAssistant;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = Main.class)
public class McpTest {

    @Resource
    private TestAssistant testAssistant;

    @Test
    public void test() {
        String res = testAssistant.chat("帮我查询明天北京到保定的高铁");
        System.out.println(res);
    }

}
明天是 **2026-08-15**,北京到保定的高铁(G字头)车次很多,全天从凌晨到深夜都有,主要从 **北京西、北京丰台** 发车,终点多为 **保定东**。以下选取部分代表车次供参考:

| 车次 | 出发站 → 到达站 | 出发 → 到达 | 历时 | 二等座 |
|------|----------------|------------|------|--------|
| G651 | 北京丰台 → 保定东 | 06:38 → 07:15 | 37分 | 61元(有票) |
| G8859 | 北京西 → 保定东 | 06:48 → 07:23 | 35分 | 65元(有票) |
| G601 | 北京丰台 → 保定东 | 07:26 → 08:03 | 37分 | 73元(有票) |
| G1025 | 北京西 → 保定东 | 07:26 → 08:07 | 41分 | 72元(有票) |
| G969 | 北京西 → 保定东 | 09:10 → 09:51 | 41分 | 68元(有票) |
| G1043 | 北京西 → 保定东 | 10:15 → 10:56 | 41分 | 72元(有票) |
| G521 | 北京西 → 保定东 | 13:10 → 13:51 | 41分 | 69元(有票) |
| G611 | 北京丰台 → 保定东 | 15:18 → 15:55 | 37分 | 73元(有票) |
| G345 | 北京西 → 保定东 | 16:00 → 16:35 | 35分 | 76元(有票) |
| G527 | 北京西 → 保定东 | 17:20 → 18:01 | 41分 | 69元(有票) |
| G6733 | 北京丰台 → 保定东 | 21:14 → 21:51 | 37分 | 62元(有票) |

另外,还有部分 **C字头城际**(如 C4093 北京西→保定,二等座33元)也可到达保定站,速度稍慢但更便宜。

如果你有偏好的出发时间(比如上午/下午/晚上)或预算,我可以帮你进一步筛选。

LangChain4j除了支持Stdio调用本地MCP软件包,还支持调用Docker容器中的MCP软件包,实现进程隔离,防止模型失控调用高权限MCP为宿主机系统带来风险。


"如果文章对您有帮助,可以请作者喝杯咖啡吗?"

微信二维码

微信支付

支付宝二维码

支付宝


LangChain4j集成MCP
https://blog.liuzijian.com/post/langchain4j/2025/12/30/langchain4j-mcp/
作者
Liu Zijian
发布于
2025年12月30日
更新于
2026年8月14日
许可协议