返回顶部
热门问答 更多热门问答
技术文章 更多技术文章

代码级拆解:如何用Java从0开始构建MCP Client(附完整Demo)

[复制链接]
链载Ai 显示全部楼层 发表于 5 小时前 |阅读模式 打印 上一主题 下一主题

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">MCP协议最近风头正盛,之前MCP整个社区的例子都是Python和TypeScript居多,而最近MCP官方发布了一条消息:

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;font-style: normal;padding: 1em;border-radius: 6px;color: rgba(0, 0, 0, 0.5);background: rgb(247, 247, 247);">

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 1em;display: block;letter-spacing: 0.1em;color: rgb(63, 63, 63);">我们很高兴地宣布,由 Spring AI 在 VMware Tanzu 开发的 Java SDK 现在成为 MCP 的官方 Java SDK。这使我们支持的语言列表不断增长,其中包括我们现有的 Kotlin SDK。Spring AI 团队将作为模型上下文协议组织的一个组成部分来维护 SDK。我们非常高兴欢迎他们加入 MCP 社区!

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">MCP协议已经提供了Java语言的SDK,并且是Java生态位中著名的Spring团队提供的支持。

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">我通过官方提供的SDK实现了一个简易的Java MCP Client,跟大家分享下如何在Java中对接各种MCP服务。

如果还不了解MCP协议,前面《Claude、Cline都在用的MCP协议,正在掀起AI工具新革命 》的文章中做了相应的介绍。

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;padding-left: 8px;color: rgb(63, 63, 63);">工具调用 Tools Calling

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">在使用MCP之前,先了解一下工具调用这个机制,OpenAI的请求API中存在一个参数tools,用于让LLM进行工具调用。

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">工具调用(也称为函数调用)提供对外部工具让LLM进行调用。LLM不直接调用工具,但是它会建议调用哪个工具,怎么入参。然后用户单独调用工具,并将结果反馈给LLM。最后LLM将结合工具调用的结果再回答用户问题。

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">具体的解释可以参见OpenAI的解释: https://platform.openai.com/docs/guides/function-calling

ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 14px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">这张图很好地解释了tools calling的机制,MCP的tools其实就是利用了这个tools calling的机制。

Java MCP Client Demo

这个部分我分享一下用Java实现MCP Client的思路。

  1. 1. 首先在本地安装一个MCP Server,示例中我使用的是Fetch(https://github.com/zcaceres/fetch-mcp) 这个MCP Server。

如何安装可以参考之前的文章《Claude、Cline都在用的MCP协议,正在掀起AI工具新革命》

  1. 2. 通过Java MCP SDK,在自己的项目中创建一个对应的Fetch MCP Client.
/**
* 创建一个Fetch MCP Client
*
*@return
*/
publicstaticMcpSyncClientcreateFetchMcpClient(){

// 声明MCP Server的参数
ServerParametersparams=ServerParameters
.builder("node")
.args("/Users/whthomas/Documents/Cline/MCP/fetch-mcp/dist/index.js")
.build();

// 声明MCP Server的传输方式
ClientMcpTransporttransport=newStdioClientTransport(params);

// 创建一个MCP Client
McpSyncClientclient=McpClient
.sync(transport)
.requestTimeout(Duration.ofSeconds(10))
// 设置MCP Client的基本信息
.clientInfo(newMcpSchema.Implementation("Fetch","1.0.0"))
.capabilities(McpSchema.ClientCapabilities.builder().roots(true).sampling().build())
.build();

// 初始化MCP Server
client.initialize();

returnclient;
}
  1. 3. 通过上一步创建的Fetch MCP Client获取这个MCP Server中全部的tools,并转换成LLM服务使用的Tools。
publicstaticList<ChatCompletionTool>prepareChatCompletionTools(McpSyncClient mcpClient){

// 列出对应的MCP Server上全部的tools
McpSchema.ListToolsResulttools=mcpClient.listTools();

// 将其转换成OpenAI Client上的Tools参数
returntools
.tools()
.stream()
.map(tool -> {

returnChatCompletionTool
.builder()
.function(FunctionDefinition.builder()
.name(tool.name())
.description(tool.description())
.parameters(prepareFunctionParameters(tool))
.build()
)
.build();

})
.toList();

}

/**
* 将工具的入参定义进行转换
*/
privatestaticFunctionParametersprepareFunctionParameters(Tool tool){

try{

ObjectMapperobjectMapper=newObjectMapper();

StringjsonString=objectMapper.writeValueAsString(tool.inputSchema());

JsonNodejsonNode=objectMapper.readTree(jsonString);

FunctionParameters.BuilderparamsBuilder=FunctionParameters.builder();

jsonNode.fields().forEachRemaining(entry -> {

JsonValuevalue=JsonValue.fromJsonNode(entry.getValue());

paramsBuilder.putAdditionalProperty(entry.getKey(), value);

});

returnparamsBuilder.build();

}catch(JsonProcessingException e) {
thrownewRuntimeException(e);
}

}
  1. 4. 将MCP Client得到的Tools信息转换成LLM服务使用的Tools,并提供给大模型,大模型就会根据tools calling的机制进行思考和输出,最终再通过MCP Client去调用MCP Server上的服务,得到MCP Server的执行结果并提供给后续的调用。
 publicvoidchatWithMcpServer(String userMessage, String chatModel){

List<ChatCompletionTool> chatCompletionTools = McpToolExample.prepareChatCompletionTools(mcpClient);

// 构建ChatCompletionCreateParams对象,设置用户消息、模型、工具等参数
ChatCompletionCreateParamsparams=ChatCompletionCreateParams.builder()
.addUserMessage(userMessage)
.model(chatModel)
.tools(chatCompletionTools)
.build();

// 调用OpenAI的Chat API
openAIClient
.chat()
.completions()
.create(params)
.thenAccept(completion -> completion
.choices()
.stream()
.map(ChatCompletion.Choice::message)
.flatMap(message -> {
message.content().ifPresent(System.out::println);
returnmessage.toolCalls().stream().flatMap(Collection::stream);
})
.forEach(toolCall -> System.out.println(callFunction(toolCall.function())))
)
.join();

}

/**
* 通过MCP Client 调用MCP Server上的方法
*
*@paramfunction LLM思考得到的期望被调用的函数信息(包括函数对应的参数)
*@return
*/
privateStringcallFunction(ChatCompletionMessageToolCall.Function function){

try{

Map<String, Object> toolParams = objectMapper.readValue(function.arguments(), HashMap.class);

McpSchema.CallToolResultresult=mcpClient.callTool(newMcpSchema.CallToolRequest(function.name(), toolParams));

// 将执行的结果返回
returnresult.content().stream().map(Object::toString).collect(Collectors.joining());

}catch(Exception ex) {
returnex.getMessage();
}

}

注意:

  1. 1. 完整的Demo代码已经上传到Github上,未来我做的其他MCP相关的一些实验也会放到这个仓库里面: https://github.com/whthomas/mcp-example
  2. 2. MCP官方的SDK最低支持的JDK版本是Java 17.

实践中的几个观察

这几天尝试用Java来编写一个MCP的Client有几个观察:

  1. 1. 这个Demo实现本来我是希望通过Spring AI来构建的,Spring一如既往地进行了大量的封装(甚至是过度的封装),做了几次不太成功的尝试之后,我最后还是决定使用官方的SDK手搓一个Demo。
  2. 2. 官方的Java文档其实写的暂时不太完善,按照这个文档的步骤,调试出来其实有点困难,我是看着官方Python的MCP Client文档把这个过程弄明白的。
  3. 3. MCP中最重要的Tools(至少我目前认为Tools是MCP协议中提供的最重要功能),输出的结构与OpenAI Chat API中的tools参数是兼容的,也就是MCP的tools给出描述的json结构直接赋值到OpenAI Chat API是可以直接用的。我觉得这种生态发展还是比较良性的,虽然Anthropic是OpenAI的竞争对手,但在构建生态的一些设计上,没有选择重新设计,对整个生态来说是一个很好的事情。
  4. 4. OpenAI官方的Java SDK是通过Kotlin实现的,我观察到不止是OpenAI其他一些厂商在Java生态中的SDK实现也逐渐采用Kotlin作为实现(比如OkHttp),不知道是基于移动端的复用考虑还是其他?OpenAI Java SDK 设计的还是比较优雅的,等待它正式release。

总结

MCP这个生态目前主流还是以Python和TS为主,但随着企业端的需求逐渐增长(Java应用在企业侧的占比还是非常高的),我认为Java应用在MCP的生态位还是会逐渐变得重要,在生态构建的初期,市场上还是充满了各种竞争的机会。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

链载AI是专业的生成式人工智能教程平台。提供Stable Diffusion、Midjourney AI绘画教程,Suno AI音乐生成指南,以及Runway、Pika等AI视频制作与动画生成实战案例。从提示词编写到参数调整,手把手助您从入门到精通。
  • 官方手机版

  • 微信公众号

  • 商务合作

  • Powered by Discuz! X3.5 | Copyright © 2025-2025. | 链载Ai
  • 桂ICP备2024021734号 | 营业执照 | |广西笔趣文化传媒有限公司|| QQ