LangChain4j实战-模型上下文协议MCP(Model Context Protocol)

LangChain4j实战-模型上下文协议MCP(Model Context Protocol)

模型上下文协议(MCP)是什么

MCP(Model Context Protocol)模型上下文协议是一种用于将人工智能应用程序连接到外部系统的开源标准。

借助MCP,像Claude或ChatGPT这样的人工智能应用程序能够连接到数据源(例如本地文件、数据库)、工具(例如搜索引擎、计算器)以及工作流。

可以把MCP想象成AI应用的USB-C接口。就像USB-C为电子设备的连接提供了一种标准化方式一样,MCP也为AI应用与外部系统的连接提供了一种标准化方式。

1

MCP可以实现哪些功能

  • 智能体可以访问您的Google Calendar和Notion,充当一个更个性化的AI助手。
  • Claude Code 可以基于Figma设计生成一个完整的网页应用。
  • 企业聊天机器人可以连接组
  • 织内的多个数据库,赋能用户通过聊天来分析数据。
  • AI模型可以在Blender上创建3D设计,并使用3D打印机将其打印出来。

为什么MCP很重要

根据你在生态系统中所处的角色,MCP能带来多方面的益处。

  • 开发者:在构建或集成AI应用或智能体时,MCP能够减少开发时间和复杂性。
  • AI应用或智能体:MCP提供了对数据源、工具和应用构成生态系统的访问,从而增强其自身能力并改善最终用户体验。
  • 最终用户:MCP带来了能力更强的AI应用或智能体,它们可以在必要时访问你的数据,并代表你执行操作。

模型上下文协议(MCP)的概念

参与者(Participants)

MCP遵循客户端-服务器(Client-Server)架构,其中一个MCP主机(一个像Claude Code或Claude Desktop这样的AI应用)会与一个或多个MCP服务器建立连接。MCP主机通过为每个MCP服务器创建一个MCP客户端来实现这一点。每个MCP客户端与其对应的MCP服务器维护一个专用的一对一连接。

MCP架构中的关键参与者是:

  • MCP主机:负责协调和管理一个或多个MCP客户端的AI应用程序。
  • MCP客户端:一种组件,用于维护与MCP服务器的连接,并从该服务器获取上下文供MCP主机使用。
  • MCP服务器:向MCP客户端提供上下文的程序。
例如:Visual Studio Code就扮演着MCP主机的角色。当Visual Studio Code与一个MCP服务器(例如 Sentry MCP 服务器)建立连接时,Visual Studio Code运行时会实例化一个MCP客户端对象,该对象负责维护与Sentry MCP服务器的连接。当Visual Studio Code 随后连接到另一个MCP服务器(例如本地文件系统服务器)时,Visual Studio Code运行时会实例化另一个MCP客户端对象来维护此连接,从而维持了MCP客户端与MCP服务之间的一对一关系。

请注意,MCP服务器指的是提供上下文数据的程序,与其运行位置无关。MCP服务器可以在本地或者远程运行。

例如,当Claude Desktop启动文件系统服务器时,该服务器会在同一台机器上本地运行,因为它使用的是STDIO传输。这通常被称为"本地"MCP服务器。而官方的Sentry MCP服务器则运行在Sentry平台上,并使用Streamable HTTP传输。这通常被称为"远程"MCP服务器。

2

数据层与传输层(Data layer and Transport layer)

MCP由两层组成

  • 数据层:定义了基于JSON-RPC的客户端与服务器之间的通信协议,包括生命周期管理,以及如工具、资源、提示和通知等核心元素。
  • 传输层:定义了客户端和服务器之间交换数据的通信方式和通道,包括建立特定传输的连接、消息的封装格式,以及授权过程。

从概念上讲,数据层是内部的层次,而传输层则是外部层次。

数据层(Data layer)

数据层实现了一个基于JSON-RPC 2.0的交换协议,该协议定义了消息的结构和语义。这一层包含:

  • 生命周期管理:处理客户端与服务器之间的连接初始化、能力协商和连接终止。
  • 服务器功能:使服务器能够提供核心功能,包括用于AI操作的工具、用于上下文数据的资源,以及用于交互模版的提示。
  • 客户端功能:使服务器能够请求客户端对主机LLM进行采样、获取用户输入,以及将日志消息记录到客户端。
  • 实用功能:支持额外的能力,例如用于实时更新的通知,以及用于长时间运行操作的进度跟踪。

传输层(Transport layer)

传输层管理客户端与服务器之间的通信通道和认证。它处理MCP参与者之间的连接建立、消息的封装格式和安全通信。

MCP支持两种传输机制:

  • STDIO传输:使用标准输入/输出流,实现同一台机器上本地进程间的直接进程通信,从而提供最佳性能且无网络开销。
  • 流式HTTP传输:使用HTTP POST来发送客户端到服务器的消息,并可选地通过服务器发送事件提供流式能力。该传输允许进行远程服务器通信,并支持标准的HTTP认证方法,包括Bearer令牌、API密钥和自定义头部。MCP建议使用OAuth来获取认证令牌。
传输层将通信细节从协议层中抽象出来,使得所有传输机制都可以使用相同的JSON-RPC 2.0消息格式。

LangChain4j-支持模型上下文协议(MCP)

LangChain4j支持模型上下文协议(MCP),以便与能够提供和执行工具的MCP兼容服务器进行通信。该协议规定了两种传输类型,LangChain4j对这两种类型都提供了支持:

  • 流式HTTP:客户端发送HTTP请求,服务器会以常规响应作为回复,或者在需要随时间发送多个响应时,打开一个SSE(Server-Sent-Events)流。
  • STDIO:客户端可以将MCP服务器作为一个本地子进程来运行,并通过标准输入/输出与它直接通信。

要让你的聊天模型或AI服务能够执行由MCP服务器提供的工具,你需要创建一个MCP工具提供者的实例。

LangChain4j创建MCP工具提供者

MCP Transport

首先需要创建一个MCP Transport的实例,对于STDIO和流式HTTP两种传输形式由两种方式来创建

STDIO:需要从NPM包中以子进程启动服务器

McpTransport transport = StdioMcpTransport.builder()
    .command(List.of("/usr/bin/npm", "exec", "@modelcontextprotocol/server-everything@0.6.2"))
    .logEvents(true) // only if you want to see the traffic in the log
    .build();

流式HTTP:需要提供服务器POST端点的URL

McpTransport transport = StreamableHttpMcpTransport.builder()
        .url("http://localhost:3001/mcp")
        .logRequests(true) // if you want to see the traffic in the log
        .logResponses(true)
        .build();

MCP Client

从transport创建MCP客户端

McpClient mcpClient = DefaultMcpClient.builder()
    .key("MyMCPClient")
    .transport(transport)
    .build();

//注意:客户端密钥是可选项,但建议设置,当有多个MCP客户端时,有必要通过key来区分它们

MCP Tool Provider

从client创建一个MCP工具提供者

McpToolProvider toolProvider = McpToolProvider.builder()
    .mcpClients(mcpClient)
    .build();

//注意:一个MCPToolProvider可以同时使用多个客户端。如果你使用此功能,还可以指定在从特定服务器检索工具失败时ToolProvider的行为。它通过builder.failIfOneServerFails(boolean)方法实现的。默认值为false,这意味着ToolProvider会忽略来自一个服务器的错误,并继续处理其他服务器。如果你将其设置为true,任何服务器的失败都会导致ToolProvider抛出异常。

一个MCP服务器可能提供数十种工具,而某个AI服务可能只需要其中几种,既时为了防止使用不需要的工具,也是为了减少幻觉的可能性。McpToolProvider运行通过名称过滤这些工具。

McpToolProvider toolProvider = McpToolProvider.builder()
    .mcpClients(mcpClient)
    .filterToolNames("get_issue", "get_issue_comments", "list_issues")
    .build();

通过这种方式,配置了ToolProvider的AI服务只能使用所提到的3个工具,允许其读取现有问题,但阻止其创建新问题。

将工具提供者(Tool Provider)绑定到AI服务

只需使用AI服务构建器的toolProvider方法

Bot bot = AiServices.builder(Bot.class)
    .chatModel(model)
    .toolProvider(toolProvider)
    .build();

使用LangChain4j本地调用MCP服务-Baidu Map 示例

百度地图的MCP服务

百度地图核心API已全面兼容MCP协议,可以mcp.so(MCP服务器与客户端聚合平台)看到相关服务

百度地图MCP服务网址:https://mcp.so/server/baidu-map/baidu-maps

3

使用百度地图MCP服务的前置条件

前置条件需要通过百度地图开发平台注册账户申请并API-key,还需要安装Node.js

百度地图开发平台网址:https://lbsyun.baidu.com/products/map

Node.js的安装可以参考之前写的随笔:https://www.cnblogs.com/shenStudy/p/19151601

引入依赖

bom物料清单

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>21</java.version>
        <!--Spring Boot-->
        <spring-boot.version>3.5.3</spring-boot.version>
        <!--LangChain4J-->
        <langchain4j.version>1.7.1</langchain4j.version>
        <!--LangChain4J community-->
        <langchain4j-community.version>1.7.1-beta14</langchain4j-community.version>
    </properties>
    <!--    <dependencyManagement> 是一个声明和集中管理依赖版本和配置的机制(物料清单)。它本身并不引入实际的依赖,而是为依赖提供一个“模板”或“蓝图”-->
    <dependencyManagement>
        <dependencies>
            <!--Spring Boot-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--LangChain4J-->
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-bom</artifactId>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--langchain4j-community-->
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-community-bom</artifactId>
                <version>${langchain4j-community.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

pom依赖

    <dependencies>
        <!--快速构建一个基于 Spring MVC 的 Web 应用程序而预置的一组依赖项的集合-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--一个通过注解在编译时自动生成 Java 样板代码的库-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!--LangChain4J openAI集成依赖(低级API依赖)-->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai</artifactId>
        </dependency>
        <!--LangChain4J 高级AI服务API依赖-->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
        </dependency>
        <!--LangChain4J 响应式编程依赖(AI服务使用Flux)-->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
        </dependency>
        <!--hutool Java工具库-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.40</version>
        </dependency>
        <!-- MCP Client 依赖 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-mcp</artifactId>
        </dependency>
    </dependencies>

AI服务接口

/**
 * AI服务接口
 */
public interface McpChatAssistant {
    Flux<String> chat(String question);
}

大模型配置文件

/**
 * 大模型配置类
 */
@Configuration
public class LLMConfig {
    @Bean(value = "qwen")
    public ChatModel chatLanguageModelQwen() {
        return OpenAiChatModel.builder()
                .apiKey(System.getenv("aliyunQwen-apiKey"))
                .modelName("qwen3-max")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .build();
    }

    @Bean(value = "streamQwen")
    public StreamingChatModel streamingChatLanguageModelQwen() {
        return OpenAiStreamingChatModel.builder()
                .apiKey(System.getenv("aliyunQwen-apiKey"))
                .modelName("qwen3-max")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .build();
    }
}

AI服务控制层接口

由于使用的STDIO的传输方式,使用CMD命令执行,需要保证以管理员身份运行程序

@Slf4j
@RestController
@RequestMapping("/mcp")
public class McpCallController {
    @Autowired
    private StreamingChatModel streamingChatModel;

    /**
     * 基于MCP协议调用大模型
     *
     * @param question 问题
     * @return 流式输出
     */
    @GetMapping("/chatOne")
    public Flux<String> chatOne(@RequestParam(value = "question"
            , defaultValue = "今天天气怎么样") String question) {
        //1.构建MCP协议
        /**
         * 1.1 cmd:启动 Windows 命令行解释器。
         * 1.2 /c:告诉 cmd 执行完后面的命令后关闭自身。
         * 1.3 npx:npx = npm execute package,Node.js 的一个工具,用于执行 npm 包中的可执行文件。
         * 1.4 -y 或 --yes:自动确认操作(类似于默认接受所有提示)。
         * 1.5 @baidumap/mcp-server-baidu-map:要通过 npx 执行的 npm 包名
         * 1.6 BAIDU_MAP_API_KEY 是访问百度地图开放平台API的AK
         */
        McpTransport transport = new StdioMcpTransport.Builder()
                .command(List.of("cmd", "/c", "npx", "-y", "@baidumap/mcp-server-baidu-map"))
                .environment(Map.of("BAIDU_MAP_API_KEY", System.getenv("BAIDU_MAP_API_KEY")))
                .build();

        // 2.构建McpClient客户端
        McpClient mcpClient = new DefaultMcpClient.Builder()
                .transport(transport)
                .build();

        //3.创建工具集和原生的FunctionCalling类型
        ToolProvider toolProvider = McpToolProvider.builder()
                .mcpClients(mcpClient)
                .build();

        //4.通过AIServcies给我们接口McpService构建实现类并将工具集和大模型赋值给AIServices
        McpChatAssistant mcpChatAssistant = AiServices.builder(McpChatAssistant.class)
                .streamingChatModel(streamingChatModel)
                .toolProvider(toolProvider).build();
        return mcpChatAssistant.chat(question);
    }

    /**
     * 常规流式调用大模型
     *
     * @param question 问题
     * @return 流式输出
     */
    @GetMapping("/chatTwo")
    public Flux<String> chatTwo(@RequestParam(value = "question"
            , defaultValue = "今天天气怎么样") String question) {
        McpChatAssistant normalAssistant = AiServices.builder(McpChatAssistant.class)
                .streamingChatModel(streamingChatModel).build();
        return normalAssistant.chat(question);
    }
}

参考资料

https://docs.langchain4j.dev/tutorials/mcp

https://modelcontextprotocol.io/docs/getting-started/intro

https://mcp.so/server/baidu-map/baidu-maps

https://lbsyun.baidu.com/products/map

posted @ 2025-12-16 23:51  柯南。道尔  阅读(3)  评论(0)    收藏  举报