使用Spring AI MCP构建MCP服务器与客户端的完整指南

本文详细介绍了如何使用Spring AI MCP构建完整的MCP服务器和客户端系统,包括数据库连接、工具暴露、HTTP+SSE通信集成,以及如何通过MCP增强AI应用的数据访问能力。

如何用Spring AI MCP构建MCP服务器和客户端

用例场景

在电信费用管理领域,本文的实验使用模拟电信发票的数据。假设用户可以访问包含这些相关数据的简单数据库模式。目标是使用AI聊天客户端,让LLM编译关于特定发票的几个关键洞察,这些洞察在制定业务决策时可能很有用。

数据库设置

考虑到PostgreSQL数据库服务器已启动并运行,可以创建这个简单的模式:

1
create schema mcptelecom;

只有一个实体——Invoice,其属性具有描述性且简单明了。可以使用以下脚本进行数据库初始化:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
drop table if exists invoices cascade;
create table if not exists invoices (
    id serial primary key,
    number varchar not null unique,
    date date not null,
    vendor varchar not null,
    service varchar not null,
    status varchar not null,
    amount numeric(18, 2) default 0.0 not null
);

开发MCP服务器

项目设置

服务器项目设置如下:

  • Java 21
  • Maven 3.9.9
  • Spring Boot 3.5.3
  • Spring AI 1.0.0
  • PostgreSQL Driver 42.7.7
  • Asentinel ORM 1.71.0

MCP服务器配置

在application.properties文件中添加以spring.ai.mcp.server为前缀的几个属性:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
spring.ai.mcp.server.name=mcp-invoice-server
spring.ai.mcp.server.type=sync
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.instructions=Instructions - SSE endpoint: /mcp/invoices/sse, SSE message endpoint: /mcp/invoices/messages

spring.ai.mcp.server.sse-message-endpoint=/mcp/invoices/messages
spring.ai.mcp.server.sse-endpoint=/mcp/invoices/sse

spring.ai.mcp.server.capabilities.tool=true
spring.ai.mcp.server.capabilities.completion=false
spring.ai.mcp.server.capabilities.prompt=false
spring.ai.mcp.server.capabilities.resource=false

工具配置

注册一个ToolCallbackProvider,向Spring AI通信暴露为MCP服务的bean:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@Configuration
public class McpConfig {

    @Bean
    public ToolCallbackProvider toolCallbackProvider(InvoiceTools invoiceTools) {
        return MethodToolCallbackProvider.builder()
                .toolObjects(invoiceTools)
                .build();
    }
}

工具配置在以下组件中实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Component
public class InvoiceTools {

    private final InvoiceService invoiceService;

    public InvoiceTools(InvoiceService invoiceService) {
        this.invoiceService = invoiceService;
    }

    @Tool(name = "get-invoices-by-pattern",
            description = "Filters invoices by the provided pattern")
    public List<Invoice> invoicesBy(@ToolParam(description = "The pattern looked up when filtering") String pattern) {
        return invoiceService.findByPattern(pattern);
    }
}

数据源配置

数据源属性在application.properties文件中设置:

1
2
3
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres?currentSchema=mcptelecom
spring.datasource.username=${POSTGRES_USER}
spring.datasource.password=${POSTGRES_PASSWORD}

开发AI聊天客户端

项目设置

客户端项目设置如下:

  • Java 21
  • Maven 3.9.9
  • Spring Boot 3.5.3
  • Spring AI 1.0.0

MCP客户端配置

在application.properties文件中添加以spring.ai.mcp.client为前缀的几个属性。此外,以下属性指示客户端连接到的MCP服务器的基础URL:

1
mcp.invoices.server.base-url = http://localhost:8081

OpenAI配置

为了能够连接到AI模型,应在application.properties文件中设置代表其进行通信的用户的有效API密钥:

1
2
spring.ai.openai.api-key = ${OPEN_AI_API_KEY}
spring.ai.openai.chat.options.temperature = 0.3

MCP客户端配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
@Configuration
public class McpConfig {

    @Bean
    public McpSyncClient mcpSyncClient(@Value("${mcp.invoices.server.base-url}") String baseUrl) {
        var transport = HttpClientSseClientTransport.builder(baseUrl)
                .sseEndpoint("mcp/invoices/sse")
                .build();

        McpSyncClient client = McpClient.sync(transport)
                .requestTimeout(Duration.ofSeconds(10))
                .clientInfo(new McpSchema.Implementation("MCP Invoices Client", "1.0.0"))
                .build();

        client.initialize();
        return client;
    }
}

助手服务

用户和LLM之间的交互可通过注入以下AssistantService的ChatClient实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Service
public class AssistantService {

    private final ChatClient client;

    public AssistantService(ChatClient.Builder clientBuilder,
                            McpSyncClient mcpSyncClient) {
        client = clientBuilder
                .defaultSystem("You are a helpful assistant with Telecom knowledge")
                .defaultToolCallbacks(new SyncMcpToolCallbackProvider(mcpSyncClient))
                .build();
    }

    public String invoiceInsights(String month, String year, String pattern) {
        final String text = """
                Give me some key insights about the invoices that contain '{pattern}' in their number?
                If available, use year {year} and month {month} when analyzing.
                """;
    
        return client.prompt()
            .user(userSpec -> userSpec.text(text)
                    .param("month", month)
                    .param("year", year)
                    .param("pattern", pattern))
            .call()
            .content();
    }
}

结果

当用户向客户端应用程序发送请求时,对2025年发票的洞察感兴趣,但只关注编号中包含vdf模式的发票。响应相当有趣:

LLM能够提供关于包含’vdf’的发票的关键洞察,包括:

  • 总发票数量
  • 详细的发票信息
  • 总金额
  • 状态概览
  • 服务细分

最终考虑

本文中展示的应用程序说明了用户如何从各个组件无缝集成以提供有意义结果的凝聚系统中受益。通过使用MCP,更准确地说,通过在MCP服务器和MCP客户端之间创建基于HTTP的有状态1:1连接,将私有数据库中的数据置于上下文中,以帮助OpenAI LLM向用户提供与电信发票相关的业务洞察。

这确实是一个简单的用例,专注于单个特定工具,旨在说明其目的。然而,可以将其作为起点来探索MCP的工作原理,理解其架构,以及MCP如何通过利用其功能来增强AI应用程序。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计