场景
SpringBoot+LangChain4j+Ollama实现Function Calling工具调用-仿智能客服示例:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/160409823
基于上述基础,学会简单的Tools的使用,下面学习MCP相关概念和入门案例。
一、MCP 核心概念
MCP(Model Context Protocol) 是由 Anthropic 推出的开放协议,旨在为大型语言模型(LLM)与外部工具、数据源之间建立统一的交互标准。
它采用 客户端-服务器 架构,核心角色如下:
| 角色 | 说明 |
|---|---|
| MCP Host | 宿主应用(如 Claude Desktop、VS Code 插件) |
| MCP Client | 在 Host 内部,与 Server 进行 1:1 协议连接,负责发现工具、发起调用、接收结果 |
| MCP Server | 轻量级服务,通过 MCP 暴露工具(Tools)、资源(Resources)、提示模板(Prompts) |
两大传输模式:
SSE(Server-Sent Events)
旧版规范,客户端通过 GET /sse 建立长连接,获取一个 sessionId,后续请求发往 POST /mcp/message?sessionId=xxx。
存在空闲超时、断连重连等问题。
Streamable HTTP
2025 新规范,统一端点 /mcp,每次请求独立,天然避免长连接超时,稳定性更好。
本文示例 基于旧版 SSE,因其学习资源更多、兼容早期 Spring AI 实现。
二、核心知识点速查
JSON-RPC 2.0:MCP 的底层消息格式,请求带 id,支持通知(无 id)。
工具动态发现:Client 初始化时发送 tools/list 请求,Server 返回工具清单及 JSON Schema。
工具调用流程:LLM 决定调用工具 → Client 发送 tools/call → Server 执行并返回结果 → Client 将结果回填给 LLM。
模型要求:必须支持 原生 Function Calling(如 gpt-4o-mini、qwen2.5:7b-instruct、llama3.1:8b-instruct-fp16)。
Ollama 兼容性:
建议通过 OpenAI 兼容端点 /v1 调用,即使用 OpenAiChatModel 并设置 baseUrl = http://localhost:11434/v1 ,
可显著提升函数调用稳定性。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
三、MCP Server搭建
环境准备
| 组件 | 版本/说明 |
|---|---|
| JDK | 17+ |
| Spring Boot | 3.4.5 |
| LangChain4j | 1.0.0-beta3 |
| MCP Server | Spring AI 1.1.4 实现的天气查询服务(暴露 /sse 和 /mcp/message) |
| Ollama | 本地运行,模型 qwen2.5:7b-instruct(需支持 Function Calling) |
MCP Server
三大核心原语:
MCP Server可以向外暴露三种不同类型的"能力":
Tools (工具):
这是最常用的功能,指AI模型可以调用的函数。模型会根据你的描述,生成结构化的参数来调用你定义的工具。
例如,一个 get_weather 工具可以接收城市名并返回天气信息。
Resources (资源):
以只读方式暴露给LLM的结构化数据,模型可以像读取文件一样访问这些数据。
Prompts (提示模板):
提供预定义的提示词模板,让客户端可以获取并使用,保证AI交互的一致性。
协议与架构:
MCP采用客户端-服务器架构,通过标准化的JSON-RPC 2.0进行通信。
协议交互流程:
一次完整的交互通常分为三个阶段:
连接建立(客户端请求并验证服务器能力)、工具发现(客户端发送 tools/list 请求获取工具列表)
和工具调用(客户端发送 tools/call 请求执行具体工具)
如何暴露工具:从注解到手动注册
在Java生态中,定义工具并将其暴露给AI有几种不同的方式,核心是告诉框架你的函数及其参数规范。
向AI应用暴露MCP工具主要有以下机制:
Spring AI @Tool 注解、Spring AI @McpTool 注解 (Spring AI 1.1.0-M1+、Spring AI FunctionCallback 接口
本文使用@Tool 注解的方式。
实战演练:搭建你的第一个MCP天气服务器
下面以"天气查询服务器"为例,基于Spring Boot生态,搭建一个MCP Server。
使用Spring Boot + Spring AI Starter (推荐)
这是最标准、最快捷的方式,Spring AI提供了专门的Starter来简化开发
第一步:引入核心依赖
在你Server项目的pom.xml中,添加以下依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>1.1.4</version>
</dependency>
(此Starter会自动引入其他必需的库,如Spring Web、Jackson等)
完整pom:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
</parent>
<groupId>com.badao.ai</groupId>
<artifactId>weather-mcp-server</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Spring AI MCP Server Starter (WebMVC 版) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
<version>1.1.4</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
第二步:配置应用基础信息
在application.yml中,设置你的服务器名称和版本:
server:
port: 8081
spring:
ai:
mcp:
server:
protocol: SSE
name: weather-mcp-server
version: 1.0.0
注意这里的protocol,指定协议方式为SSE
第三步:定义一个工具
创建一个Service类,并使用@Tool注解标记你的业务方法。
package com.badao.ai.mcpserver;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
@Service
public class WeatherService {
@Tool(name = "getWeather", description = "查询指定城市的天气信息")
public WeatherResult getWeather(
@ToolParam(description = "城市名称,例如:北京") String city) {
// 模拟天气数据,实际可接入真实API
System.out.println("调用了getWeather");
return new WeatherResult(city, "晴", "25°C", "湿度:60%");
}
public record WeatherResult(
String city,
String weather,
String temperature,
String details
) {}
}
@Tool和@ToolParam注解中的description非常重要,它们是LLM理解工具功能和参数含义的唯一途径,
描述越清晰准确,模型调用工具的成功率就越高。
第四步:注册工具
在你的Spring Boot启动类或一个配置类中,将你的WeatherService注册为工具提供者:
package com.badao.ai;
import com.badao.ai.mcpserver.WeatherService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringAiDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiDemoApplication.class, args);
}
// 方式一:启动时注册,自动扫描 @Tool 注解
@Bean
public ToolCallbackProvider weatherTools(WeatherService weatherService) {
return MethodToolCallbackProvider.builder()
.toolObjects(weatherService)
.build();
}
}
四、MCP Client搭建
环境准备
1、确保 MCP 天气服务器在 http://localhost:8081 运行。
访问:
如果返回 event:endpoint 和 sessionId,这就是旧版 (SSE)。且启动成功。
2、确保 Ollama 服务在 http://localhost:11434 运行,并且已拉取 qwen2.5:7b-instruct。
拉取并运行支持工具的模型:
ollama pull qwen2.5:7b-instruct
确认你的 Ollama 模型是否支持工具调用:
curl http://localhost:11434/api/chat -d '{
"model": "qwen2.5:7b-instruct",
"messages": [
{"role": "user", "content": "青岛天气如何?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "getWeather",
"description": "获取城市天气",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名" }
},
"required": ["city"]
}
}
}
],
"stream": false
}'
如果返回类似以下内容,说明模型支持工具调用:
{
"message": {
"role": "assistant",
"content": "",
"tool_calls": [
{
"function": {
"name": "getWeather",
"arguments": { "city": "青岛" }
}
}
]
}
}
如果返回的 content 是普通文本(没有 tool_calls),说明该模型不支持工具调用。
依赖(pom.xml)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version>
</parent>
<groupId>com.example</groupId>
<artifactId>mcp-client-standalone</artifactId>
<version>1.0</version>
<properties>
<java.version>17</java.version>
<langchain4j.version>1.0.0-beta3</langchain4j.version>
</properties>
<dependencies>
<!-- Spring Boot Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MCP 客户端依赖 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-mcp</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- Spring Boot Starter,提供 @AiService 支持 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-spring-boot-starter</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
应用配置(application.yml)
server:
port: 8082
langchain4j:
ollama:
chat-model:
base-url: http://localhost:11434
model-name: qwen2.5:7b-instruct # 需要支持 Function Calling
log-requests: true
log-responses: true
timeout: 60s # 为 Ollama 模型调用设置 60 秒的超时
注意这里的本地的ollama调用要加超时时间,否则无法看到预想的效果!
核心配置类(ManualAssistantConfig.java)
package com.badao.ai.config;
import com.badao.ai.service.WeatherAssistant;
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.HttpMcpTransport;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.tool.ToolProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
import java.util.List;
@Configuration
public class ManualMcpConfig {
@Bean
public WeatherAssistant weatherAssistant() {
// 1. 创建 Ollama 聊天模型
ChatLanguageModel model = OpenAiChatModel.builder()
.baseUrl("http://localhost:11434/v1") // Ollama 的 OpenAI 兼容端点
.apiKey("ollama") // 任意非空字符串
.modelName("qwen2.5:7b-instruct") // 你的模型名
.timeout(Duration.ofSeconds(60))
.build();
// 2. 配置 MCP 传输层(连接远程 Weather Server)
McpTransport transport = new HttpMcpTransport.Builder()
.sseUrl("http://localhost:8081/sse") // 指向 MCP Server 的 SSE 端点
.logRequests(true)
.logResponses(true)
.build();
// 3. 创建 MCP 客户端
McpClient mcpClient = new DefaultMcpClient.Builder()
.transport(transport)
.build();
// 4. 通过 McpToolProvider 将远端工具转换为本地可调用的 ToolProvider
ToolProvider toolProvider = McpToolProvider.builder()
.mcpClients(List.of(mcpClient))
.build();
// 5. 手动组装 AiService(系统提示 + 模型 + 工具)
return AiServices.builder(WeatherAssistant.class)
.chatLanguageModel(model)
.toolProvider(toolProvider)
.build();
}
}
定义 AI 服务接口
package com.badao.ai.service;
public interface WeatherAssistant {
String chat(String userMessage);
}
控制器(ChatController.java)
package com.badao.ai.controller;
import com.badao.ai.service.WeatherAssistant;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
public class ChatController {
private final WeatherAssistant weatherAssistant;
public ChatController(WeatherAssistant weatherAssistant) {
this.weatherAssistant = weatherAssistant;
}
@PostMapping("/chat")
public Map<String, String> chat(@RequestBody Map<String, String> request) {
String userMessage = request.get("message");
String response = weatherAssistant.chat(userMessage);
return Map.of("response", response);
}
}
五、 运行与验证
启动 MCP 天气服务器(端口 8081,确保 /sse 端点可访问)。
启动 Ollama,确保 qwen2.5:7b-instruct 已下载。
启动本客户端(端口 8082)。
调用接口:
curl -X POST http://localhost:8082/chat \
-H "Content-Type: application/json" \
-d '{"message": "青岛天气?"}'

此时查看mcp server的日志,确实被调用
六、常见问题排查
1、启动报 IllegalConfigurationException
未找到 ChatLanguageModel Bean 采
用手动配置类 ManualAssistantConfig,不依赖 @AiService 自动扫描
2、调用接口返回模拟调用文本
模型不支持 Function Calling 或未使用 /v1 端点
用 Postman 测试模型是否返回 tool_calls,改用 OpenAiChatModel
3、Unexpected status code: 404
SSE 会话失效,客户端向旧端点发请求 重启客户端或改用 Streamable HTTP
4、SSE 连接超时 正常现象,
SSE 空闲关闭 忽略警告或切换到 Streamable HTTP
5、依赖版本混乱导致 ClassNotFoundException 或 IllegalConfigurationException现象:
启动时提示 Please specify either chatLanguageModel or streamingChatLanguageModel,
或报 Cannot resolve symbol 'StreamableHttpMcpTransport'。
根本原因:
混用了 langchain4j 不同模块的 beta 版本(如 mcp 使用 1.1.0-beta7,而 ollama 使用 1.0.0-beta3),内部 API 不兼容。
某些版本缺少特定的类(例如 StreamableHttpMcpTransport 在 1.0.0-beta3 中不存在,需要 1.1.0-beta7 以上)。
未正确添加 langchain4j-spring-boot-starter 依赖,导致 @AiService 注解无法被识别。
解决方案:
统一所有 LangChain4j 模块的版本,最终确定使用 1.0.0-beta3,该版本各模块齐备且兼容。
直接引入 langchain4j-mcp 和 langchain4j-open-ai,避免使用多余的 Starter 造成冲突。
