Ohhnews

分类导航

$ cd ..
DZone Java原文

第一部分:使用Quarkus LangChain4j和Goose构建受治理的MCP工具服务

#quarkus#langchain4j#goose#mcp协议#java微服务

Goose —— 由 Block 开发的开源、基于 Rust 的 AI 开发智能体(已捐赠给 Linux 基金会 Agentic AI 基金会)—— 通过模型上下文协议(Model Context Protocol,MCP)与本地开发环境原生交互。在本教程中,你将学习如何使用 Quarkus LangChain4j 构建无状态、云原生的 Java 微服务,并将其作为受治理的 MCP 扩展公开,供 Goose 无缝发现和运行。

自主式 AI 编码智能体(如 Goose)远不止于简单的代码自动补全。Goose 使用 Rust 构建,注重速度和可移植性,它运行在本地机器上,可以检查文件、执行终端命令,并通过 MCP 使用工具来自动化复杂的工程任务。然而,当开发者希望让 AI 智能体 查询企业微服务、触发数据库迁移或获取内部 API 指标时,编写自定义本地脚本或临时包装器既脆弱又危险。解决方案是使用 Quarkus LangChain4j 在 Java 中构建一个无状态 MCP 工具服务器。Quarkus 提供接近零的启动时间和较低的内存占用,而 LangChain4j 让通过标准 MCP HTTP/JSON-RPC 暴露 @Tool 方法变得非常简单。

架构:Goose 如何与 Quarkus MCP 集成

┌────────────────────────────────────────────────────────┐
│ Goose AI Agent (Rust Runtime)                          │
│ (Local CLI / Desktop App / ACP Server)                 │
└───────────────────────────┬────────────────────────────┘
Model Context Protocol (MCP)
JSON-RPC over Stateless HTTP
┌────────────────────────────────────────────────────────┐
│ Quarkus LangChain4j MCP Server                         │
│ - @Tool Annotations & Bean Validation                  │
│ - Reactive SmallRye Mutiny Execution                   │
│ - GraalVM Native Image Ready                           │
└───────────────────────────┬────────────────────────────┘
                      Reactive Clients
          Enterprise APIs / Databases / Dev UI
  1. Goose 智能体(客户端):在开发者机器上执行,通过 MCP 编排 LLM 工具循环。
  2. MCP HTTP 传输层:Goose 使用标准 MCP 方法(tools/listtools/call),以无状态 HTTP POST 请求的形式向 Quarkus 后端发送结构化工具调用。
  3. Quarkus 微服务:使用 Jakarta Bean Validation 校验参数,执行响应式业务逻辑,并将结构化数据返回给 Goose。

第 1 步:在 Quarkus 中配置依赖

创建一个新的 Quarkus 项目,或更新你的 pom.xml,加入 quarkus-langchain4j-mcp 和响应式(Reactive)依赖:

注意:完整演示应用见 https://github.com/danieloh30/governed-mcp-tools.git

${quarkus.platform.group-id} ${quarkus.platform.artifact-id} ${quarkus.platform.version}
pom
import io.quarkus
quarkus-arc
io.quarkus
quarkus-rest-jackson
io.quarkiverse.mcp
quarkus-mcp-server-http 2.0.0.CR2
io.quarkus
quarkus-hibernate-validator
io.quarkus
quarkus-junit
test

第 2 步:实现加固的 MCP 工具

我们将创建一个 Customer Services MCP 工具,当工程师提出:“Goose,检查客户 CUST-4091 的数据库状态并获取其最近的遥测数据”时,Goose 可以调用该工具。

通过将 @Tool 注解放在 CDI Bean 上,Quarkus LangChain4j 会自动将该类注册为 MCP 服务端点:

$ java
@ApplicationScoped
public class CustomerServiceTools {

    @Tool(description = "Retrieve the current account status, service tier, and primary deployment region for a given customer.")
    public Uni<CustomerStatusResponse> getCustomerStatus(
            @ToolArg(description = "Customer ID formatted as CUST-XXXX")
            @NotNull
            @Pattern(regexp = "^CUST-[0-9]{4,8}$")
            String customerId) {

        CustomerStatusResponse response = switch (customerId) {
            case "CUST-4091" -> new CustomerStatusResponse("CUST-4091", "ACTIVE", "ENTERPRISE_TIER", "US-EAST-1");
            case "CUST-2187" -> new CustomerStatusResponse("CUST-2187", "ACTIVE", "BUSINESS_TIER", "EU-WEST-1");
            case "CUST-7734" -> new CustomerStatusResponse("CUST-7734", "SUSPENDED", "STARTER_TIER", "AP-SOUTH-1");
            default -> new CustomerStatusResponse(customerId, "NOT_FOUND", "UNKNOWN", "UNKNOWN");
        };

        return Uni.createFrom().item(response);
    }

    @Tool(description = "Retrieve recent health-check logs and diagnostic metrics for a specified availability zone.")
    public Uni<List<String>> getZoneHealthLogs(
            @ToolArg(description = "Zone identifier, e.g., US-EAST-1")
            @Size(max = 20)
            String zoneId) {

        return Uni.createFrom().item(List.of(
                "[" + zoneId + "] CPU utilization: 42% (healthy)",
                "[" + zoneId + "] Memory pressure: 31% (normal)",
                "[" + zoneId + "] Network I/O: 1.2 Gbps ingress / 0.8 Gbps egress",
                "[" + zoneId + "] Disk IOPS: 12,400 read / 8,300 write (within SLA)",
                "[" + zoneId + "] Active connections: 18,230 (capacity: 50,000)",
                "[" + zoneId + "] Last incident: none in past 72 hours"
        ));
    }

    @Tool(description = "Track the current status, item count, and estimated delivery for an enterprise order.")
    public Uni<OrderStatusResponse> getOrderStatus(
            @ToolArg(description = "Order ID formatted as ORD-XXXXXXXX")
            @NotNull
            @Pattern(regexp = "^ORD-[0-9]{8}$")
            String orderId) {

        OrderStatusResponse response = switch (orderId) {
            case "ORD-20240815" -> new OrderStatusResponse("ORD-20240815", "SHIPPED", 12, "$48,750.00", "2024-08-22", "US-EAST-1");
            case "ORD-20240901" -> new OrderStatusResponse("ORD-20240901", "PROCESSING", 5, "$12,300.00", "2024-09-10", "EU-WEST-1");
            case "ORD-20241003" -> new OrderStatusResponse("ORD-20241003", "DELIVERED", 28, "$134,500.00", "2024-10-08", "AP-SOUTH-1");
            default -> new OrderStatusResponse(orderId, "NOT_FOUND", 0, "$0.00", "N/A", "UNKNOWN");
        };

        return Uni.createFrom().item(response);
    }

    @Tool(description = "Retrieve SLA compliance metrics including uptime, latency, and violation count for a service.")
    public Uni<SLAComplianceResponse> getSLACompliance(
            @ToolArg(description = "Service identifier, e.g., api-gateway, auth-service")
            @NotNull
            @Size(max = 40)
            String serviceId) {

        SLAComplianceResponse response = switch (serviceId) {
            case "api-gateway" -> new SLAComplianceResponse("api-gateway", 99.97, "45ms", 99.99, 0, "2024-Q3");
            case "auth-service" -> new SLAComplianceResponse("auth-service", 99.82, "120ms", 99.95, 3, "2024-Q3");
            case "data-pipeline" -> new SLAComplianceResponse("data-pipeline", 98.50, "340ms", 99.80, 12, "2024-Q3");
            case "notification-hub" -> new SLAComplianceResponse("notification-hub", 99.91, "78ms", 99.97, 1, "2024-Q3");
            default -> new SLAComplianceResponse(serviceId, 0.0, "N/A", 0.0, -1, "N/A");
        };

        return Uni.createFrom().item(response);
    }
    ...
}

第 3 步:在 application.properties 中启用 MCP 扩展

配置 Quarkus MCP 服务器设置:

$ properties
quarkus.mcp-server.server-info.name=customer-tools
quarkus.mcp-server.server-info.version=1.0.0
quarkus.mcp-server.http.root-path=/mcp
quarkus.log.category."io.quarkiverse.mcp".level=DEBUG

以开发模式启动 Quarkus:./mvnw quarkus:dev

第 4 步:将 Goose 连接到 Quarkus MCP 服务器

Goose 可以通过 stdio 或 HTTP 扩展任何 MCP 服务器。配置方式可以是编辑 Goose 的 YAML 配置文件,或使用 Goose CLI。

选项 A:使用 Goose CLI

直接在终端中注册 Quarkus MCP 服务器:

$ bash
goose extension add customer-tools \
  --type http \
  --uri http://localhost:8080/mcp

选项 B:编辑 ~/.config/goose/config.yaml

将 Quarkus 后端添加到你的 Goose 扩展配置中:

$ config
extensions:
  customer-tools:
    enabled: true
    type: http
    uri: http://localhost:8080/mcp
    headers:
      Content-Type: "application/json"

第 5 步:测试开发者工作流

通过 CLI 或桌面应用启动 Goose:

goose session

向 Goose 发送提示:

开发者:“我正在调试客户 CUST-4091。请使用 customer-tools 获取其账户层级,然后检查其主区域的健康日志。”

前端界面:

开发者:选择某个 Tool explorer。点击右侧面板中的“Run tool”按钮。验证审计事件。

[LOADING...]

底层发生的过程

  1. 发现:Goose 发送 HTTP POST /mcp JSON-RPC tools/list 请求。Quarkus 返回基于 getCustomerStatusgetZoneHealthLogs 生成的 JSON Schema 定义。
  2. 工具调用 1:Goose 解析提示词,构造包含 {"customerId": "CUST-4091"}tools/call JSON 负载,并 POST 到 Quarkus。
  3. 执行与校验:Quarkus 执行 Hibernate Bean Validation。由于 CUST-4091 符合 ^CUST-[0-9]{4,8}$,它运行 getCustomerStatus 并返回 primaryRegion: US-EAST-1
  4. 工具调用 2:Goose 看到 US-EAST-1 后,触发 getZoneHealthLogs("US-EAST-1"),收到绿色健康指标,并在 CLI 中向你汇总完整的诊断报告。

总结与后续步骤

通过将 Java 业务逻辑封装在 Quarkus LangChain4j 的 @Tool Bean 中,你可以让 Goose 这类本地 AI 开发智能体安全、经过校验地访问企业后端系统。然而,当数百名开发者在生产环境中针对共享后端微服务运行本地 Goose 智能体时,直接连接会带来安全和治理风险。

第 2 部分预告:我们将介绍 agentgateway —— Linux 基金会的数据平面代理 —— 将其置于 Goose 与 Quarkus 之间。我们将配置 OAuth2/OIDC 认证、细粒度的工具级 RBAC 和限流,以加固企业 AI 基础设施。

本文表达的观点仅代表 DZone 贡献者个人意见。