本文约 6000 字, 读完你将拥有一个可以被 Claude、IDEA、Cursor 等任意 AI 工具调用的业务接口。


一、先搞清楚:MCP 到底解决了什么问题

在 MCP 出现之前,AI 调用外部工具的方式是这样的:

每个 AI 工具(Claude、GPT、Cursor)有自己的插件系统,每个业务系统想接入,就要针对每个 AI 单独写一套适配器。M 个 AI × N 个业务系统 = M×N 个适配器,维护成本指数级爆炸。

MCP(Model Context Protocol)做的事情,和当年的 LSP(Language Server Protocol)一模一样——定义一套标准协议,把 M×N 变成 M+N。

MCP 协议规定了三类能力:

  • Tools:AI 可以主动调用的函数,有入参有出参,最常用

  • Resources:AI 可以读取的数据源,类似文件系统

  • Prompts:预定义的提示词模板

本文重点讲 Tools,这是 90% 的业务场景需要的。


二、MCP 的底层通信机制

很多人以为 MCP 是 HTTP,其实不是。

MCP 底层走的是 JSON-RPC 2.0,通信方式有两种:

stdio 模式:AI 客户端把 MCP Server 作为子进程启动,通过标准输入输出通信。本地开发用这个,零网络配置。

SSE 模式:基于 HTTP 的服务器推送,适合部署到远程服务器,多个客户端共享一个 MCP Server。

一次完整的工具调用流程是这样的:

  1. AI 客户端启动时,向 MCP Server 发送 initialize 请求

  2. Server 返回自己支持的所有 Tools 列表(名称、描述、参数 Schema)

  3. 用户发起对话,AI 判断需要调用某个 Tool

  4. AI 发送 tools/call 请求,带上工具名和参数

  5. Server 执行业务逻辑,返回结果

  6. AI 把结果整合进回复

关键点:AI 是通过 Tool 的描述文字来决定要不要调用它的。描述写得好不好,直接决定 AI 能不能正确理解和使用你的接口。

三、Java 实现 MCP Server 的两条路

目前 Java 生态有两个主流选择:

Spring AI MCP

MCP4J

维护方

Spring 官方

社区

成熟度

1.0 正式版

活跃开发中

集成难度

低,原生 Spring Boot

文档质量

一般

适合场景

已有 Spring 项目

独立 MCP 服务

本文用 Spring AI MCP,理由很简单:你大概率已经在用 Spring Boot,零额外学习成本。


四、从零搭一个 MCP Server

4.1 项目初始化

在 start.spring.io 创建项目,依赖选:

<dependencies>
    <!-- Spring AI MCP Server -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
    </dependency>

    <!-- Web(SSE 模式需要) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<!-- Spring AI BOM -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

4.2 application.yml 配置

spring:
  ai:
    mcp:
      server:
        name: my-business-mcp-server
        version: 1.0.0
        # stdio 模式用这个
        transport: stdio
        # SSE 模式改成:
        # transport: sse

4.3 写第一个 Tool

假设我们有一个订单查询业务接口,让 Claude 能直接查:

@Service
publicclass OrderService {

    // 模拟数据库
    privatestaticfinal Map<String, Order> orders = Map.of(
        "ORD001", new Order("ORD001", "张三", 299.0, "PENDING"),
        "ORD002", new Order("ORD002", "李四", 599.0, "SHIPPED"),
        "ORD003", new Order("ORD003", "王五", 199.0, "DELIVERED")
    );

    /**
     * 这个注解让 Spring AI 自动把这个方法注册为 MCP Tool
     * description 是关键——AI 靠这段文字决定什么时候调用它
     */
    @Tool(description = "根据订单ID查询订单信息,包括客户名称、金额和配送状态")
    public Order getOrderById(
            @ToolParam(description = "订单ID,格式为 ORD 开头加三位数字,例如 ORD001") 
            String orderId) {
        
        Order order = orders.get(orderId);
        if (order == null) {
            thrownew RuntimeException("订单不存在: " + orderId);
        }
        return order;
    }

    @Tool(description = "查询指定客户的所有订单列表")
    public List<Order> getOrdersByCustomer(
            @ToolParam(description = "客户姓名") 
            String customerName) {
        
        return orders.values().stream()
            .filter(o -> o.getCustomerName().equals(customerName))
            .collect(Collectors.toList());
    }

    @Tool(description = "统计各状态订单数量,返回 PENDING/SHIPPED/DELIVERED 各自的数量")
    public Map<String, Long> getOrderStatistics() {
        return orders.values().stream()
            .collect(Collectors.groupingBy(Order::getStatus, Collectors.counting()));
    }
}
// Order 实体
public record Order(
    String orderId,
    String customerName,
    Double amount,
    String status
) {}

4.4 注册 Tool 到 MCP Server

@Configuration
public class McpConfig {

    @Bean
    public ToolCallbackProvider orderTools(OrderService orderService) {
        // Spring AI 会自动扫描 @Tool 注解的方法
        return MethodToolCallbackProvider.builder()
            .toolObjects(orderService)
            .build();
    }
}

4.5 启动类

@SpringBootApplication
public class McpServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(McpServerApplication.class, args);
    }
}

就这些。运行 mvn spring-boot:run,一个 MCP Server 就跑起来了。

五、接入 Claude Desktop 验证

5.1 找到 Claude Desktop 配置文件

macOS~/Library/Application Support/Claude/claude_desktop_config.json

Windows%APPDATA%\Claude\claude_desktop_config.json

5.2 添加 MCP Server 配置

{
  "mcpServers": {
    "my-order-service": {
      "command": "java",
      "args": [
        "-jar",
        "/你的项目路径/target/mcp-server-1.0.0.jar"
      ]
    }
  }
}

5.3 重启 Claude Desktop,验证接入

重启后打开 Claude Desktop,在输入框左下角应该能看到一个工具图标,点击可以看到你注册的三个 Tool。

5.4 实际对话测试

现在直接用自然语言问 Claude:

:帮我查一下 ORD002 这个订单的情况

Claude:我来帮你查询一下。[调用 getOrderById,参数:orderId="ORD002"]

查询结果:

  • 订单号:ORD002

  • 客户:李四

  • 金额:¥599.0

  • 状态:已发货(SHIPPED)


六、接入 IDEA Claude Code 插件

如果你上一篇配好了 IDEA + Claude Code,可以直接把这个 MCP Server 接进去。

在 ~/.claude.json 的 mcpServers 里加一条:

{
  "mcpServers": {
    "my-order-service": {
      "command": "java",
      "args": [
        "-jar",
        "/你的项目路径/target/mcp-server-1.0.0.jar"
      ]
    }
  }
}

然后在 IDEA 终端里重新启动 Claude Code,它会自动加载这个 MCP Server。

七、Tool 描述怎么写才能让 AI 准确调用

这是整篇文章最容易被忽略、但最重要的一节。

AI 决定调不调用某个 Tool,完全依赖 description 字段。描述写得差,AI 要么不知道该用,要么用错场景。

反面案例

// 坏的描述 ❌
@Tool(description = "查询订单")
public Order getOrderById(String orderId) { ... }

// 问题:
// 1. "查询订单"太模糊,AI 不知道需要什么参数
// 2. 没说返回什么信息
// 3. 没说适用场景

正面案例

// 好的描述 ✅
@Tool(description = """
    根据订单ID查询单个订单的详细信息。
    返回内容包括:客户姓名、订单金额、当前配送状态(PENDING待发货/SHIPPED已发货/DELIVERED已签收)。
    当用户询问某个具体订单的状态、金额或客户信息时使用此工具。
    """)
public Order getOrderById(
    @ToolParam(description = "订单唯一标识符,格式为 ORD 开头加三位数字,例如 ORD001、ORD002")
    String orderId) { ... }

描述写作原则

要素

说明

示例

功能说明

这个工具做什么

"根据订单ID查询订单详情"

返回内容

返回哪些字段

"返回客户名、金额、状态"

触发场景

什么情况下用

"当用户询问具体订单时"

参数格式

入参的格式约束

"格式为 ORD 开头加三位数字"

边界条件

什么情况不适用

"不支持模糊查询,需要精确订单号"


八、生产环境注意事项

8.1 权限控制

MCP Server 暴露的是真实业务接口,必须做权限验证:

@Tool(description = "查询订单信息")
public Order getOrderById(String orderId) {
    // 从 MCP 上下文获取调用方信息(Spring AI 1.0 支持)
    // 根据业务规则做权限校验
    validatePermission(orderId);
    return orderRepository.findById(orderId);
}

8.2 参数校验

AI 传来的参数不一定合法,必须做校验:

@Tool(description = "查询订单")
public Order getOrderById(
    @ToolParam(description = "订单ID") String orderId) {
    
    // 格式校验
    if (!orderId.matches("ORD\\d{3}")) {
        throw new IllegalArgumentException("订单ID格式错误: " + orderId);
    }
    return orderService.getById(orderId);
}

8.3 返回值设计

返回 JSON 友好的对象,避免循环引用、懒加载等 JPA 常见坑:

// 不要直接返回 JPA Entity ❌
public OrderEntity getOrder(String id) { ... }

// 返回专门的 DTO ✅
public OrderDTO getOrder(String id) {
    OrderEntity entity = repository.findById(id);
    return OrderDTO.from(entity); // 只包含 AI 需要的字段
}

8.4 错误处理

AI 需要能读懂你的错误信息,才能告知用户:

@Tool(description = "查询订单")
public Order getOrderById(String orderId) {
    return orderRepository.findById(orderId)
        .orElseThrow(() -> new RuntimeException(
            // 错误信息要对人类友好,AI 会原样转述给用户
            String.format("未找到订单 %s,请确认订单号是否正确", orderId)
        ));
}

九、进阶:接入真实数据库

把 OrderService 换成真实的数据库查询:

@Service
publicclass OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Tool(description = """
        查询订单详情。支持按订单ID精确查询。
        返回订单的完整信息包括商品列表、价格明细、配送地址和当前状态。
        """)
    public OrderDetailDTO getOrderById(
        @ToolParam(description = "订单ID") String orderId) {
        
        return orderRepository.findById(orderId)
            .map(OrderDetailDTO::from)
            .orElseThrow(() -> new RuntimeException("订单不存在: " + orderId));
    }

    @Tool(description = """
        统计订单数据。可以按时间范围、状态、客户维度聚合。
        当用户询问"最近多少天有多少订单"、"各状态订单分布"等统计问题时使用。
        """)
    public OrderStatDTO getStatistics(
        @ToolParam(description = "统计开始日期,格式 yyyy-MM-dd") String startDate,
        @ToolParam(description = "统计结束日期,格式 yyyy-MM-dd") String endDate) {
        
        LocalDate start = LocalDate.parse(startDate);
        LocalDate end = LocalDate.parse(endDate);
        return orderRepository.getStatistics(start, end);
    }
}

这样,Claude 就能直接查你的生产数据库了。


十、完整架构回顾

你的业务系统
├── Spring Boot MCP Server
│   ├── OrderService (@Tool)
│   ├── UserService (@Tool)  
│   └── ReportService (@Tool)
│
└── 可被以下 AI 工具直接调用:
    ├── Claude Desktop
    ├── IDEA + Claude Code
    ├── Cursor
    └── 任何支持 MCP 的客户端

你写了一次,所有 AI 工具都能用。这就是 MCP 的价值。

总结

步骤

做了什么

1

理解 MCP 协议:JSON-RPC 2.0,stdio/SSE 两种模式

2

用 Spring AI MCP Starter 搭建 Server

3

用 @Tool 注解把业务方法暴露给 AI

4

写好 description,让 AI 准确理解和调用

5

接入 Claude Desktop 和 IDEA Claude Code 验证

6

生产环境加权限校验、参数验证、错误处理

环境:Java 21 · Spring Boot 3.3 · Spring AI 1.0.0 · Claude Code v2.1.114

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐