LangChain4j(17)——MCP客户端

该文章已生成可运行项目,

MCP介绍

MCP (Model Context Protocol) 是一个开放协议,用于标准化应用程序如何向 LLM 提供上下文。可以将 MCP 想象成 AI 应用程序的 USB 接口。就像 USB 为设备连接各种外设和配件提供标准化方式一样,MCP 为 AI 模型连接不同的数据源和工具提供了标准化的方式。

sever-everyting服务介绍

本例使用的mcp服务是server-everything。它是一个 MCP 客户端构建者的测试服务器,没有具体的用途。其支持的工具方法:

echo

回显输入消息的简单工具
输入:
        message(string):要回显的消息
返回: 带有回声消息的文本内容
add

将两个数字相加
输入:
        a(number):第一个数字
        b(number):第二个数字
返回: 添加的文本结果
longRunningOperation

演示长时间作的进度通知
输入:
        duration(number,默认值:10):持续时间(以秒为单位)
        steps(number,默认值:5):进度步骤数
返回: 包含持续时间和步骤的完成消息
        在执行过程中发送进度通知
sampleLLM

演示使用 MCP 采样功能的 LLM 采样功能
输入:
        prompt(string):要发送到 LLM 的提示
        maxTokens(number,默认值:100):要生成的最大令牌数
返回: 生成的 LLM 响应
getTinyImage

返回一个小的测试图像
无需输入
返回:Base64 编码的 PNG 图像数据
printEnv

打印所有环境变量,用于调试 MCP 服务器配置
无需输入
返回: 所有环境变量的 JSON 字符串
annotatedMessage

演示如何使用批注提供有关内容的元数据
输入:
        messageType(enum: “error” |“成功” |“debug”):用于演示不同注释模式的消息类型
        includeImage(boolean, default: false):是否包含示例图片
返回: 具有不同注释的内容:
        错误消息:高优先级 (1.0),对用户和助手都可见
        成功消息:中等优先级 (0.7),以用户为中心
        调试消息:低优先级 (0.3),以助手为中心
        可选图片:中等优先级 (0.5),以用户为中心

getResourceReference

返回 MCP 客户端可以使用的资源引用
输入:
        resourceId(number, 1-100):要引用的资源的 ID
返回: 一个资源引用,其中包含:
        文本介绍
        嵌入资源与type: "resource"
        使用资源 URI 的文本说明

使用LangChain4j调用MCP服务

LangChain4j 支持模型上下文协议 (MCP),用于与符合 MCP 的服务器通信。

该协议指定了两种传输类型:

Http:客户端请求一个 SSE 通道来接收来自服务器的事件,然后通过 HTTP POST 请求发送命令。
Stdio:客户端可以将 MCP 服务器作为本地子进程运行,并通过标准输入/输出直接与其通信。

注意:LangChain4j官网给出的案例中,sse方式无法连接server-everything服务(不确定是不是langchain4j版本的原因),本例使用stdio协议连接

package com.renr.langchain4jnew.app2;

import com.renr.langchain4jnew.constant.CommonConstants;
import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel;
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.stdio.StdioMcpTransport;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.tool.ToolProvider;

import java.time.Duration;
import java.util.List;

/**
 * @Title: AI Service基本使用
 * @Author 老任与码
 * @Date 2025-04-12 9:25
 */
public class AppMPC {
    public static void main(String[] args) {
        // 创建智谱的模型对象
        ZhipuAiChatModel zhipuAiChatModel = ZhipuAiChatModel.builder()
                // 模型key
                .apiKey(CommonConstants.API_KEY)
                // 精确度
                .temperature(0.9)
                .model("GLM-4-Flash")
                .maxRetries(3)
                .logRequests(true)
                .logResponses(true)
                .callTimeout(Duration.ofSeconds(60))
                .connectTimeout(Duration.ofSeconds(60))
                .writeTimeout(Duration.ofSeconds(60))
                .readTimeout(Duration.ofSeconds(60))
                .build();

        // 使用stdio协议创建传输对象,由于在window环境测试,这里需要指定windows下npm命令的路径
        McpTransport transport = new StdioMcpTransport.Builder()
                .command(List.of("C:/Program Files/nodejs/npm.cmd", "exec", "@modelcontextprotocol/server-everything@2025.4.28"))
                .logEvents(true) // 仅当你想在日志中查看流量时
                .build();

          // 实际测试,无法连接
//        McpTransport transport = new HttpMcpTransport.Builder()
//                .sseUrl("http://127.0.0.1:3001/sse") // SSE 事件channel地址
//                .logRequests(true) // 开启请求日志
//                .logResponses(true) // 开启响应日志
//                .build();

        // 创建mcp客户端对象
        McpClient mcpClient = new DefaultMcpClient.Builder()
                .transport(transport)
                .build();

        // 创建工具对象
        ToolProvider toolProvider = McpToolProvider.builder()
                .mcpClients(List.of(mcpClient))
                .build();

        // 模型中,指定使用的工具
        Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(zhipuAiChatModel)
                .toolProvider(toolProvider)
                .build();

        // 发送消息,本例测试是否会调用mcp服务的add函数
        String info = assistant.chat("100加100等于多少");
        System.out.println(info);

    }
}

执行结果:

从执行结果看,大模型选择让用户调用add函数执行加法运算。

第一次发送的请求数据:

tools属性中给出了server-everything中定义的函数,并将这些信息一起发送给大模型。

Request:
- method: POST
- url: https://open.bigmodel.cn/api/paas/v4/chat/completions
- headers: [Authorization: Bearer ey...fQ..EsWKuDgfSyfeaAg10__UaDzVuty1QOkzosp-g0-bVdo]
- body: {
  "model" : "GLM-4-Flash",
  "messages" : [ {
    "role" : "user",
    "content" : "100加100等于多少"
  } ],
  "stream" : false,
  "temperature" : 0.9,
  "max_tokens" : 512,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "echo",
      "description" : "Echoes back the input",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "message" : {
            "type" : "string",
            "description" : "Message to echo"
          }
        },
        "required" : [ "message" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "add",
      "description" : "Adds two numbers",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "a" : {
            "type" : "number",
            "description" : "First number"
          },
          "b" : {
            "type" : "number",
            "description" : "Second number"
          }
        },
        "required" : [ "a", "b" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "printEnv",
      "description" : "Prints all environment variables, helpful for debugging MCP server configuration",
      "parameters" : {
        "type" : "object",
        "properties" : { },
        "required" : [ ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "longRunningOperation",
      "description" : "Demonstrates a long running operation with progress updates",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "duration" : {
            "type" : "number",
            "description" : "Duration of the operation in seconds"
          },
          "steps" : {
            "type" : "number",
            "description" : "Number of steps in the operation"
          }
        },
        "required" : [ ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "sampleLLM",
      "description" : "Samples from an LLM using MCP's sampling feature",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "prompt" : {
            "type" : "string",
            "description" : "The prompt to send to the LLM"
          },
          "maxTokens" : {
            "type" : "number",
            "description" : "Maximum number of tokens to generate"
          }
        },
        "required" : [ "prompt" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "getTinyImage",
      "description" : "Returns the MCP_TINY_IMAGE",
      "parameters" : {
        "type" : "object",
        "properties" : { },
        "required" : [ ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "annotatedMessage",
      "description" : "Demonstrates how annotations can be used to provide metadata about content",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "messageType" : {
            "type" : "string",
            "description" : "Type of message to demonstrate different annotation patterns",
            "enum" : [ "error", "success", "debug" ]
          },
          "includeImage" : {
            "type" : "boolean",
            "description" : "Whether to include an example image"
          }
        },
        "required" : [ "messageType" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "getResourceReference",
      "description" : "Returns a resource reference that can be used by MCP clients",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "resourceId" : {
            "type" : "number",
            "description" : "ID of the resource to reference (1-100)"
          }
        },
        "required" : [ "resourceId" ]
      }
    }
  } ],
  "tool_choice" : "auto"
}

大模型收到第一次请求,根据用户提问和tools中支持的函数,返回的第一次响应中要求用户执行add函数,然后发送第二次请求:

 Request:
- method: POST
- url: https://open.bigmodel.cn/api/paas/v4/chat/completions
- headers: [Authorization: Bearer ey...fQ..EsWKuDgfSyfeaAg10__UaDzVuty1QOkzosp-g0-bVdo]
- body: {
  "model" : "GLM-4-Flash",
  "messages" : [ {
    "role" : "user",
    "content" : "100加100等于多少"
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "id" : "call_-8666101128331871791",
      "type" : "function",
      "function" : {
        "name" : "add",
        "arguments" : "{\"a\": 100, \"b\": 100}"
      }
    } ]
  }, {
    "role" : "tool",
    "content" : "The sum of 100 and 100 is 200."
  } ],
  "stream" : false,
  "temperature" : 0.9,
  "max_tokens" : 512,
  "tools" :
    ......
}

根据第二次请求的数据,tool_calls属性表示执行了add函数。

本文章已经生成可运行项目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值