在 Quarkus 中实现 MCP 服务器
模型上下文协议 (MCP) 是一项新兴标准,可让 AI 模型安全地与外部工具和资源进行交互。在本教程中,我将向您展示如何使用 Quarkus 实现 MCP 服务器,从而允许您使用由 Java 生态系统驱动的自定义工具来扩展 AI 应用程序。
我们将要构建的内容
我们将实现一个简单的 MCP 服务器,该服务器提供用于获取美国地区天气预报和警报的工具。我们选择此示例是因为它与官方 MCP 快速入门指南 modelcontextprotocol.io/quickstart/server 一致,从而更容易比较不同语言的实现。
我们的服务器将公开两个工具:getAlerts
和 getForecast
。构建完成后,我们将将其连接到 MCP 主机,该主机将服务器作为子进程运行。集成 Claude 后的效果如下:

核心 MCP 概念
MCP 服务器可以提供三种主要功能:
- 资源
-
客户端可以读取的类文件数据(如 API 响应或文件内容)
- 工具
-
可由 LLM 调用(经用户批准)的函数
- 提示
-
预先编写的模板,帮助用户完成特定任务
本教程重点介绍工具的实现。
构建服务器
创建一个新文件 src/main/java/org/acme/Weather.java
。此示例的完整代码可在 此处 找到。
天气 API 集成
首先,我们为天气 API 设置 REST 客户端。
@RegisterRestClient(baseUri = "https://api.weather.gov")
public interface WeatherClient {
// Get active alerts for a specific state
@GET
@Path("/alerts/active/area/{state}")
Alerts getAlerts(@RestPath String state);
// Get point metadata for coordinates
@GET
@Path("/points/{latitude},{longitude}")
JsonObject getPoints(@RestPath double latitude, @RestPath double longitude);
// Get detailed forecast using dynamically provided URL
@GET
@Path("/")
Forecast getForecast(@Url String url);
}
为了处理 API 响应,我们将定义一些数据类。请注意,我们仅包含所需的字段,因为完整的 API 响应包含更多数据。
static record Period(
String name,
int temperature,
String temperatureUnit,
String windSpeed,
String windDirection,
String detailedForecast) {
}
static record ForecastProperties(
List<Period> periods) {
}
static record Forecast(
ForecastProperties properties) {
}
由于天气 API 使用重定向,请将此添加到您的 application.properties
文件中。
quarkus.rest-client.follow-redirects=true
格式化助手
我们将使用 Qute 模板来格式化天气数据。
String formatForecast(Forecast forecast) {
return forecast.properties().periods().stream().map(period -> {
// Template for each forecast period
return Qute.fmt(
"""
Temperature: {p.temperature}°{p.temperatureUnit}
Wind: {p.windSpeed} {p.windDirection}
Forecast: {p.detailedForecast}
""",
Map.of("p", period)).toString();
}).collect(Collectors.joining("\n---\n"));
}
实现 MCP 工具
现在,让我们实现实际的 MCP 工具。来自 io.quarkiverse.mcp.server
的 @Tool
注解将方法标记为可用工具,而 @ToolArg
则描述了参数。
@Tool(description = "Get weather alerts for a US state.")
String getAlerts(@ToolArg(description = "Two-letter US state code (e.g. CA, NY)") String state) {
return formatAlerts(weatherClient.getAlerts(state));
}
@Tool(description = "Get weather forecast for a location.")
String getForecast(
@ToolArg(description = "Latitude of the location") double latitude,
@ToolArg(description = "Longitude of the location") double longitude) {
// First get the point metadata which contains the forecast URL
var points = weatherClient.getPoints(latitude, longitude);
// Extract the forecast URL using Qute template
var url = Qute.fmt("{p.properties.forecast}", Map.of("p", points));
// Get and format the forecast
return formatForecast(weatherClient.getForecast(url));
}
预报 API 需要一个两步过程,我们首先获取点元数据,然后使用该响应中的 URL 来获取实际预报。 |
运行服务器
为了简化部署和开发,我们将服务器打包为 uber-jar。这使得可以 mvn install
并将其作为 jar 发布到 Maven 存储库,从而更轻松地与他人共享和运行。
quarkus.package.uber-jar=true
最后,我们可以选择启用文件日志记录,因为如果没有文件日志记录,我们将无法看到服务器的任何日志,因为标准输入/输出是为 MCP 协议保留的。
quarkus.log.file.enable=true
quarkus.log.file.path=weather-quarkus.log
运行 mvn install
后,您可以使用 JBang 使用其 Maven 坐标运行服务器:org.acme:weather:1.0.0-SNAPSHOT:runner
,或手动使用 java -jar target/weather-1.0.0-SNAPSHOT-runner.jar
运行。
与 Claude Desktop 集成
将此添加到您的 claude_desktop_config.json
文件中。
{
"mcpServers": {
"weather": {
"command": "jbang",
"args": ["--quiet",
"org.acme:weather:1.0.0-SNAPSHOT:runner"]
}
}
}
--quiet
标志可防止 JBang 的输出干扰 MCP 协议。

您也可以直接运行服务器而不使用 java,那么它将是 |
开发工具
MCP Inspector
为了进行开发和测试,您可以使用 MCP Inspector 工具。
npx @modelcontextprotocol/inspector
这会启动一个本地 Web 服务器,您可以在其中测试您的 MCP 服务器。

与 LangChain4j 集成
从 0.23.0 版本开始,Quarkus LangChain4j 支持 MCP,这意味着它充当 MCP 客户端。有关详细信息,请参阅 将模型上下文协议与 Quarkus+LangChain4j 结合使用。
要将我们的天气服务器与 LangChain4j 结合使用,请添加此配置。
quarkus.langchain4j.mcp.weather.transport-type=stdio
quarkus.langchain4j.mcp.weather.command=jbang,--quiet,org.acme:weather:1.0.0-SNAPSHOT:runner
其他客户端/MCP 主机
模型上下文协议有一个列出已知客户端的页面。
虽然我没有测试所有各种客户端和 MCP 主机,但使用 jbang --quiet <GAV>
的类似方法应该适用于大多数(如果不是全部)客户端。
测试服务器
您可以通过 Claude 或其他 MCP 主机测试服务器,查询方式如下:
-
“索尔万格的天气预报是什么?”
-
“纽约市的天气警报是什么?”
幕后发生的事情如下:
-
您的问题以及可用的工具信息会发送给 LLM。
-
LLM 分析问题并确定要使用哪些工具。
-
客户端通过 MCP 服务器执行选定的工具。
-
结果返回给 LLM。
-
LLM 使用工具结果构建答案。
-
您看到最终的响应!