MCP(Model Context Protocol)

目录

1.MCP的概念

[2. MCP Java SDK框架](#2. MCP Java SDK框架)

[2.1 三层架构](#2.1 三层架构)

[2.2 传输协议(Transport Protocol)](#2.2 传输协议(Transport Protocol))

[3. MCP的使用](#3. MCP的使用)

[3.1 MCP Client 环境准备](#3.1 MCP Client 环境准备)

[3.2 安装Node.js](#3.2 安装Node.js)

[3.3 安装Python](#3.3 安装Python)

[3.4 MCP Server 的配置与使用(Cursor)](#3.4 MCP Server 的配置与使用(Cursor))

[3.5 MCP Server market](#3.5 MCP Server market)

[3.6 在IDEA 中配置MCP Server](#3.6 在IDEA 中配置MCP Server)

[4. 自定义 MCP Server](#4. 自定义 MCP Server)

[4.1 定义 Tool 为MCP Server的相关注解](#4.1 定义 Tool 为MCP Server的相关注解)

[4.1.1 @McpTool](#4.1.1 @McpTool)

[4.1.2 @McpResource](#4.1.2 @McpResource)

[4.1.3 @McpPrompt](#4.1.3 @McpPrompt)

[4.1.4 @McpComplete](#4.1.4 @McpComplete)

[4.2 用@McpTool 自定义MCP Server](#4.2 用@McpTool 自定义MCP Server)

[4.2.1 自定义 MCP Server-STDIO](#4.2.1 自定义 MCP Server-STDIO)

[4.2.2 使用自定义的MCP Server](#4.2.2 使用自定义的MCP Server)

[4.3 通过@Tool 再暴露 来自定义MCP Server](#4.3 通过@Tool 再暴露 来自定义MCP Server)


1.MCP的概念

MCP,Model Context Protocol(模型上下文协议)。

MCP只是一个协议,它的作用和诞生要从Tool Calling说起:

后端开发者要想AI模型拥有更丰富的实时知识和能力,就要使用别人对外的tool,如给模型加入天气预报,但是接入查询天气tool就需要为这个tool设计解析逻辑,要设计复杂的实体类或实现复杂的逻辑。三个API就有三套tool编写规范,麻烦的很!这时MCP就诞生了!

MCP是一个协议,它规定tool按照一定的规范对外提供服务。就像USB接口,虽然制造USB的生成商不同,但用的都是同一规范。(但我们通常说的MCP实际上指的是"MCP+Tool Calling",**2.**及后面都以这个概念讲)


**总结:**MCP是Tool Calling的 "标准化运行时",两者的关系:

  • Tool Calling是 "决策行为" :模型决定 "要不要调用工具"、"调用哪个工具" 和 "传什么参数"
  • MCP 是 "通信协议" :规定"如何描述工具"、"如何发起调用"、"如何传递结果"和"如何管理会话状态"

如何使用 和自定义 MCP?先了解MCP的框架。

2. MCP Java SDK框架

2.1 三层架构

  • Client/Server 层:MCP Client管理客户端操作和服务端链接;MCP Server 处理服务端操作和客户端请求(tool);二者均可通过MCP Session 进行通信管理。
  • Session 层(MCP Session):通过 DefaultMCPSession 实现管理通信模式及状态。
  • Transport 层(MCP Transport):处理JSON-RPC 消息的序列化与反序列化,支持多种协议模式实现。

可以把整个MCP通信想象成**"点外卖"**

|-----------------|---------------------|------------------------------------------------------------------------------|
| MCP架构层 | 功能说明 | 比喻 |
| Client / Server | 发起请求 / 处理请求 | 在外卖程序下单 / 商家制作餐食 |
| Session | 管理会话生命周期,确保请求不丢、不错乱 | 订单管理系统(订单不丢+订单状态跟踪):正在制作--->制作完成--->已出餐--->"骑手预计还有10分钟到"--->"预计还有5分钟"... |
| Transport | 消息用什么方式传送? | 传输方式有多种。可自提、可骑手送 |

2.2 传输协议(Transport Protocol)

MCP Java SDK提供多种 传输协议 来传输数据,开发者可选择合适的协议。

目前有两种传输方式:

Stdio

  • 基于stdio;
  • 通过标准输入/输出(stdin和stdout) 在两个程序之间传消息,高效直接。服务器从标准输入读取JSON-RPC消息,并将JSON-RPC消息写入标准输出。

Streamable HTTP

  • 它基于"HTTP+SSE",更准确来说它使用HTTP POST+SSE传输信息;
  • 使用该协议服务器仅暴露一个 HTTP 端点 (例如 https://example.com/mcp),该端点只接受 POST 请求。这极大简化了客户端配置和服务器路由。
  • 核心是HTTP POST,SSE作为可选;启用SSE由服务器在处理请求时动态决定,POST 与 SSE可在同时存在

Stdio 和Streamable HTTP 详细:

stdio - Model Context Protocol

Streamable HTTP - Model Context Protocol


下面是Spring AI基于以上两种传输的不同实现:

STDIO

服务类型 依赖 Property
Standard Input/Output (STDIO) spring-ai-starter-mcp-server spring.ai.mcp.server.stdio=true

WebMVC

|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------|--------------------------------------------|
| 服务类型 | 依赖 | Property |
| SSE WebMVC _(已弃用) | spring-ai-starter-mcp-server-webmvc | spring.ai.mcp.server.protocol=SSE |
| Streamable-HTTP WebMVC | spring-ai-starter-mcp-server-webmvc | spring.ai.mcp.server.protocol=STREAMABLE |
| Stateless WebMVC | spring-ai-starter-mcp-server-webmvc | spring.ai.mcp.server.protocol=STATELESS |

WebFlux

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------|--------------------------------------------|
| 服务类型 | 依赖 | Property |
| SSE WebFlux _(已弃用) | spring-ai-starter-mcp-server-webflux | spring.ai.mcp.server.protocol=SSE |
| Streamable-HTTP WebFlux | spring-ai-starter-mcp-server-webflux | spring.ai.mcp.server.protocol=STREAMABLE |
| Stateless WebFlux | spring-ai-starter-mcp-server-webflux | spring.ai.mcp.server.protocol=STATELESS |

注意:

  • Streamable-HTTP WebFlux 是基于WebFlux框架开发的,因此带有非阻塞 、响应式等特点;同理 Streamable-HTTP WebMVC 带有WebMVC 框架的特点。
  • Stateless 是Streamable HTTP 的一种能力或模式,而不是并列的第三种传输方式。

3. MCP的使用

3.1 MCP Client 环境准备

  • 在MCP 生态中,部分 MCP Server 使用TypeScript 或 JavaScript 编写,需依赖 Node.js 来运行。
  • 也许多 MCP Server 使用 Python 编写,在MCP 生态中,uv 被推荐作为首选工具来运行基于 Python 的MCP Server。
  • MCP Server也可以用Java等其他语言编写,下面的自定义MCP Server将使用Java编写展示

Node.js

  1. npm 与 npx 的区别
  • npm (Node Package Manager) 是Node.js自带的包管理工具,用于安装和管理 JavaScript包。
  • npx是npm 5.2.0及以上版本附带的工具,用于执行npm包中的可执行命令,支持临时下载并运行远程包

3.2 安装Node.js

下载地址:Node.js --- 下载 Node.js®

选择LTS(Long Term Support) 版本,按照操作系统配置选择电脑:

|---------|--------------------------|
| 操作系统 | 推荐安装方式 |
| Windows | .msi安装程序 |
| macOS | .pkg安装包 或 使用Homebrew |
| Linux | 使用包管理器,如apt、yum,或手动安装 |

注意:安装过程中务必勾选**"Add to PATH",**这是为了加入环境变量(但现在好像默认勾选了,总之注意)。


最后终端检查是否安装成功:

// 查看当前安装的Node.js版本

node --version

// 查看当前安装的npm版本

npm --version

// 查看当前安装的npx版本

npx --version

3.3 安装Python

下载地址:Download Python | Python.org

详细参考:Python安装与环境(包含Windows版和Mac版)

注意:

  1. 安装过程中,路径避免中文
  2. 务必勾选**"Add to PATH"**
  3. 务必勾选**"pip",**安装Python包管理工具

验证安装:

// 查看版本,输出版本号表示安装成功

python --version

// 安装 uv

pip install uv

//查版本

pip --version

下面将在Cursor中配置及使用MCP Server

3.4 MCP Server 的配置与使用(Cursor)

先展示如何配置和使用,下一小节介绍如何获取 MCP Server

1.配置

2.配置

  1. 使用

事实上AI在接收到提示词会融合多个MCP Server的能力,如**"帮我规划今天开车从北京到上海的路线,并告知我穿衣注意事项"** AI会调用天气服务 ,然后AI把天气信息给到地图服务进行整合,选出合适的道路,最后输出 路线规划 和 穿衣推荐。

3.5 MCP Server market

1.拥有海量MCP Server的市场:https://mcp.so/zh

选中MCP 服务,然后点击"代码仓库"进入Github:

1.1

1.2 Github中会详细介绍


  1. Cursor 官方有自己的MCP Server market,可以一键自动部署MCP Server:

3.6 在IDEA 中配置MCP Server

代码:mcp-client-demo · 巨蛇神/JavaEE进阶 - 码云 - 开源中国

首先在IDEA使用Spring AI框架下的 Client(客户端)还需要添加对应依赖。

  • 如果添加的MCP Server是常规的同步服务(不是基于WebFlux开发的服务),就添加:

<dependency>

<groupId>org.springframework.ai</groupId>

<artifactId>spring-ai-starter-mcp-client</artifactId>

</dependency>

  • 如果添加的MCP Server是基于WebFlux开发的服务,就添加:

<dependency>

<groupId>org.springframework.ai</groupId>

<artifactId>spring-ai-starter-mcp-client-webflux</artifactId>

</dependency>

官方明确指出同一个Client内不能两者同时添加使用!!!


第二步添加MCP Server服务

1.在配置文件中添加:

spring:

ai:

mcp:

client:

stdio:

servers-configuration: classpath:mcp-servers.json

具体为什么要加.cmd,在Windows 还会加另外一些后缀,可参考Spring AI官方:MCP Client Boot Starter :: Spring AI Reference

4.使用MCP 服务

4. 自定义 MCP Server

4.1 定义 Tool 为MCP Server的相关注解

四个核心注解及用法:

|--------------|--------------------------------|
| 注解 | 作用 |
| @McpTool | 将方法标记为 MCP 工具,自动生成 JSON Schema |
| @McpResource | 通过 URI 模板暴露资源 |
| @McpPrompt | 生成供 AI 交互使用的提示词消息 |
| @McpComplete | 为提示词参数或资源 URI 模板提供自动补全 |

看不懂没关系,下面使用示例详细讲解。

4.1.1 @McpTool

最核心的注解。它的基本用法是将一个方法声明为可供 AI 模型调用的工具:

java 复制代码
@Component
public class CalculatorTools { //一个加法算数工具
    @McpTool(name = "add", description = "Add two numbers together")
    public int add(
        @McpToolParam(description = "First number", required = true) int a,
        @McpToolParam(description = "Second number", required = true) int b) {
        return a + b;
    }
}

(上述MCP Server的定义很像Tool Calling中 Tool的定义过程)


由此可看出 @McpTool包含的关键属性包括:

  1. "你是谁?"(name/description/title)
  2. "我该怎么理解你的返回结果?"(generateOutputSchema)
  3. "我能不能放心地调用你?"(annotations)
  • ++name (工具的唯一技术标识符)++

name 是客户端(AI模型)在调用工具时使用的唯一技术标识符。它必须简洁、无歧义,且在同一服务器内唯一。如果省略,默认使用方法名。

java 复制代码
@McpTool(name = "add", description = "Add two numbers together")
public int add(int a, int b) { return a + b; }

在实际交互中,模型生成的 JSON-RPC 请求里 method 字段或工具调用的 name 字段,填写的就是这个 name。它面向程序层面,不面向人类阅读。

  • ++description (工具的"使用说明书")++

description 是写给AI模型看的,直接决定模型能否、以及何时正确地选择这个工具。它是一个功能性描述:说明这个工具做什么、在什么场景下使用。如果省略,默认使用方法名,建议写上!

java 复制代码
@McpTool(name = "add", description = "Add two numbers together")

上述代码中的"description"描述的使用场景是"两个数相加"。

  • ++title (给人看的标题)++

title 是面向 UI 和 终端用户 的人类可读名称。它不影响模型的工具选择逻辑,只影响展示层。Spring AI: MCP Server Annotations :: Spring AI Reference 给出了明确的显示优先级: annotations.title > title > name (如果都没有提供,则从优先级高的一直回退到name,如果那么也没设置,直接使用工具的方法名显示给客户端)

java 复制代码
@McpTool(
    name = "calculate-area",
    title = "Rectangle Area Calculator", // 给用户看的
    description = "Calculate the area of a rectangle" // 给模型看的
)

当用户在MCP 客户端(如 Claude Desktop 的工具面板)中看到这个工具时,显示的是"Rectangle Area Calculator ",而不是生硬的 calculate-area 。

  • ++genarateOutputSchema (让返回结果"结构化、可解析")++

默认值为false 。当工具返回非基本类型 (如自定义对象、List<Order>)时,如果此属性为true ,Spring AI 会自动为该返回类型生成 JSON Schema。

如果为false,返回的复杂对象可能被序列化为字符串,客户端需自行解析,可靠性下降。

java 复制代码
@McpTool(
    name = "calculate-area",
    description = "Calculate the area of a rectangle",
    generateOutputSchema = true // 为 AreaResult 生成输出 Schema
)
public AreaResult calculateRectangleArea(double width, double height) {
    return new AreaResult(width * height, "square units");
}
  • ++annotations / @McpAnnotations (给客户端的"行为风险提示")++

@McpAnnotations 下的四个 Hint 不是强制性的契约,而是"提示"而已。MCP 规范明确警告:客户端绝不应仅凭 "不可信服务器"(server) 提供的注解来做出工具使用决策。 它们四个的价值在于:帮助可信客户端(或人工审核者)快速判断一个工具的"风险画像"

这四个的提示语义:

|-----------------|-------|-------------------------------------------------------------------|
| 提示 | 默认值 | 核心语义 |
| readOnlyHint | false | 如果为true,表示工具不修改其环境(纯查询/计算) |
| destructiveHint | true | 如果为true,表示工具可能执行破坏性更新 (如删除、覆盖)。仅在readOnlyHint == false 时有意义。 |
| idempotentHint | false | 如果为true,表示用相同参数重复调用不会产生额外的副作用 。仅在readOnlyHint == false 时有意义。 |
| openWorldHint | true | 如果为true,表示工具可能与"开放世界"的外部实体交互(如网页搜索)。如果为false,交互域是封闭的(如查询内部内存) |

组合起来就是一个"风险画像",以Spring AI 官方的 calculate-area 示例为例:

java 复制代码
@McpTool(
    name = "calculate-area",
    annotations = @McpTool.McpAnnotations(
        readOnlyHint = true, //这是一个纯计算工具,不修改任何环境。
        destructiveHint = false, // 不可能造成破坏。
        idempotentHint = true //重复调用完全安全。
                              //openWorldHint 未设置,默认为 true
    )
)
public AreaResult calculateRectangleArea(double width, double height) { ... }

反过来,一个删除用户账号的mcp工具,其"风险画像"截然不同:

java 复制代码
// 概念示例,非官方文档原文
@McpTool(name = "deleteUser")
@McpAnnotations(
    readOnlyHint = false,       // 会修改环境
    destructiveHint = true,     // 可能不可逆地删除数据
    idempotentHint = true,      // 重复删除同一用户,第二次无额外效果
    openWorldHint = false       // 只操作内部数据库,不与外部世界交互
)

一个理性的客户端或人工审核者看到这组提示,就会知道这是一个高风险、可能破坏数据的工具,从而触发二次确认或限制调用。

4.1.2 @McpResource

该注解用来定义 一个通过读取URI中的数据并执行固定逻辑的资源方法 ;该注解与@McpTool同级,被@McpResource修饰的方法被注册成一个MCP Server。

@McpResource包含以下参数:

|--------------|--------------------------------------------------------------|------------------------------------------|
| 参数 | 默认值 | 核心用途 |
| uri | ""(必填) | 资源的唯一地址模板 ,支持 {varname}占位符 |
| name | "" | 资源的
程序化表示名,
也是 title 缺失时的显示回退 |
| title | "" | 面向人类用户的显示名称 |
| description | "" | 给LLM看的描述,帮助模型理解"这个资源是什么" |
| mineType | "text/plain" | 声明资源返回内容的 MIME 类型(如application/json) |
| metaProvider | DefaultMetaProvider.class | 自定义 _meta 字段的数据提供者(下面讲) |
| annotations | @McpAnnotations(audience={USER},priorty=0.5,lastModified="") | 向客户端传递资源元数据提示 |

  • ++uri++
java 复制代码
@McpResource(uri = "config://{key}", name = "Configuration")
public String getConfig(String key) {
    return configData.get(key);
}
  1. URI中的**{key}** 是一个模板变量。方法参数String key 与这个变量一一对应 。客户端会查询服务器提供的模板列表 来动态发现 uri的模板长什么样,然后它再发送符合该资源方法的URI,如客户端请求 config://spring.datasource.url 时,框架会自动将 {key} 替换为 spring.datasource.url ,并把这个值作为 key 参数传入getConfig 方法。

  2. 严格的匹配规则:

  • 数量必须相等:URI模板中的变量数,必须等于非特殊参数(后面讲)的数量。
  • 方法类型必须是String(或UUID等可转换类型),不能是 int、long等原始类型

两个变量,两个参数:

java 复制代码
@McpResource(uri = "db://{table}/{id}", name = "Database Record")
public String readRecord(String table, String id) {
    // 正确:两个变量对应两个 String 参数
    return databaseService.query(table, id);
}
  • ++name++

是逻辑标识符。如果未提供,框架会尝试回退到方法名。

  • ++title++

人类可读到的显示名称,用于UI展示。显示优先级为:annotations.title>title>name。如果title缺失,客户端最终显示的是name值。

  • ++description++

写给LLM看的方法描述。描述该方法的功能和使用场景等信息,为AI能精准选用MCP服务的关键。

  • ++mineType (声明内容类型)++

默认为**"text/plain"**。如果你的资源返回的是JSON,务必显示设置为"application/json",这能帮助客户端正确解析放回的内容。

  • ++metaProvider++(自定义 _meta 数据,面向 协议实现者 / 服务器内部程序)

MetaProvider 是一个接口 ,用于在服务器启动时为资源的 _meta字段提供元数据。默认的DefaultMetaProvider返回不含任何_meta信息的空映射。你可以实现MetaProvider接口,自定义资源版本号、团队信息等元信息,并通过这个属性按类引用(xxx.class)。

  • ++annotations/@McpResource.McpAnnotations (资源的元数据标签,面向客户端)++

这是嵌套在@McpResource内部的注解,用于向客户端传递关于资源本身(该资源方法)的元信息。它有三个核心字段:

|--------------|-----------|------------------------------------------------------------------|
| 字段 | 类型 | 语义 |
| audience | Role | 这个资源是谁给谁用的 。可以包含 USER 、ASSISTANT等多个值,表示内容对不同受众有用 |
| priority | double | 这个资源对服务器运行的重要性 。1 =最重要(几乎是必须的),0 =最不重要(可选)。默认0.5 |
| lastModified | String | 资源(方法)最后修改时间 ,必须是ISO 8601格式(如 "2026-01-25T15:00:58Z") |

示例:声明一个对用户和助手都有用、且非常重要的资源配置

java 复制代码
@McpResource(
    uri = "config://{key}",
    name = "app-config",
    title = "Application Configuration",
    description = "Spring Boot 应用的配置项,用于查询运行时参数",
    mimeType = "application/json",
    annotations = @McpResource.McpAnnotations(
        audience = { McpSchema.Role.USER, McpSchema.Role.ASSISTANT },
        priority = 1.0,
        lastModified = "2026-01-12T15:00:58Z"
    )
)
public String getConfig(String key) { ... }

  • 除了URI参数匹配外,还有一种**"特殊参数"** ------不参与URI变量匹配的**"隐形参数"**。

@McpResource 方法除了URI变量对应参数外,还可以声明一些由框架自动注入的特殊参数 。这些参数不计入URI变量数量 ,也不会出现在生成的Schema中,对MCP客户端完全透明。

常见的特殊参数:

|--------------------------------------------------------|-------------------------------------|
| 特殊参数 | 用途 |
| McpSyncRequestContext(同步) / McpAsyncRequestContext(异步) | 统一的请求上下文,可访问原始请求、发送日志/进度通知、进行采样等 |
| ReadResourceRequest | 直接获取原始的 ReadResourceRequest 对象(下面讲) |
| @McpProgressToken | 注入进度令牌,用于发送进度通知 |
| McpMeta | 访问请求中携带的元数据 |
| McpSyncServerExchange | 底层服务器交换对象(有状态模式下可用) |

示例:在资源方法中发送进度通知

java 复制代码
@McpResource(uri = "large-file://{path}", name = "Large File Reader")
public String readLargeFile(
    String path,                          // URI 变量
    McpSyncRequestContext context         // 特殊参数:自动注入
) {        //progress代表任务进行到多少;total代表任务总数
    context.progress(p -> p.progress(0.5).total(1.0).message("Reading file..."));
    return fileService.read(path);
}
  1. ReadResourceRequest VSMcpSyncRequestContext

两者都是获取MCP请求,但Read..抽象层级位于"协议层 ",接收MCP请求对象,可request.uri(),就不能额外从@McpTool uri中获得;Mcp...抽象层级位于"框架层",封装了请求+交换+便捷操作。

前者更底层!

4.1.3 @McpPrompt

@McpPrompt的职责是定义一个可以根据用户输入生成含有指定消息模板的 "消息模板方法 ",而且是面向用户的,用户自行可选择合适的模板。

被修饰的方法返回 GetPromptResult (也可返回PromptMessage或List<String>),参数用@McpArg标注。

例如,一个 code-review 提示词模板,让用户选择并填入一段代码后,服务器返回的消息可能是:"请审查以下代码,并指出潜在的安全问题: 用户代码 "。这段消息会返回给用户(客户端)作为。


参数包含:

|--------------|---------------------------|------------------------------------|
| 属性 | 默认值 | 含义 |
| name | "" | 提示词的唯一标识符 |
| title | "" | 面向UI的可选人类可读名称 |
| description | "" | 方法描述 |
| metaProvider | DefaultMetaProvider.class | 为 _mate 字段提供数据的MetaProvider实现类 |

示例:

java 复制代码
@McpPrompt(
    name = "personalized-message",
    description = "Generate a personalized message")
public GetPromptResult personalizedMessage(
    @McpArg(name = "name", required = true) String name,
    @McpArg(name = "age", required = false) Integer age,
    @McpArg(name = "interests", required = false) String interests) {
    StringBuilder message = new StringBuilder();
    message.append("Hello, ").append(name).append("!\n\n");
    if (age != null) {
        message.append("At ").append(age).append(" years old, ");
        // Add age-specific content
    }
    if (interests != null && !interests.isEmpty()) {
        message.append("Your interest in ").append(interests);
        // Add interest-specific content
    }
    return GetPromptResult.builder(List.of(
            new PromptMessage(Role.ASSISTANT, TextContent.builder(message.toString()).build())))
        .description("Personalized Message")
        .build();
}

补充:特殊参数在@McpPrompt中的应用(McpMeta)

McpMeta用于根据请求元数据生成提示词:

java 复制代码
@McpPrompt(name = "localized-prompt", description = "Localized prompt generation")
public GetPromptResult localizedPrompt(
    @McpArg(name = "topic", required = true) String topic,
    McpMeta meta) {
    String language = (String) meta.get("language");
    String region = (String) meta.get("region");
    // Generate localized content based on metadata
    String message = generateLocalizedMessage(topic, language, region);
    return GetPromptResult.builder(List.of(
            new PromptMessage(Role.ASSISTANT, TextContent.builder(message).build())))
        .description("Localized Prompt")
        .build();
}

我觉得这个 generateLocalizedMessage 有意思。

特殊参数依然对对客户端是透明的,不进入Schema。

4.1.4 @McpComplete

它是为提示词参数 或资源URI模板 提供自动补全候选值。 它的核心约束是:prompt 和 uri 两个属性二选一 ,不能同时使用。prompt 用于补全指定名称;uri 用于补全指定资源URI模板中的表达式。

示例一:提示词参数补全:

java 复制代码
@Component
public class CompletionProvider {
// 很可能就是类似这样的一个成员字段
    private final List<String> cities = List.of("Beijing", "Shanghai", "Guangzhou", "Shenzhen", ...);
    @McpComplete(prompt = "city-search")
    public List<String> completeCityName(String prefix) {
        return cities.stream()
            .filter(city -> city.toLowerCase().startsWith(prefix.toLowerCase()))
            .limit(10)
            .toList();
    }
}

客户端在输入 city-search 提示词的某个参数时,每输入一个前缀,服务器就会调用这个方法,返回以该前缀开头的城市名列表(最多10条)。方法参数 String prefix 接收的是客户端已经输入的前缀,返回值是候选列表。

注意:@McpComplete 本身不限定数据源,它定义了一个协议:根据用户输入的前缀,返回一组候选值。至于候选值是从内存LIst来,还是数据库、远程API、甚至互联网中实时查询来,王权由方法体决定。(但建议不要从互联网上查,会很慢。用@McpTool联网最好)

示例二:资源URI补全:

java 复制代码
@McpComplete(uri = "config://{key}")
public List<String> completeConfigKey(String prefix) {
    return configKeys.stream()
        .filter(key -> key.startsWith(prefix))
        .limit(10)
        .toList();
}

这里补全的是 config://{key} 模板中的 {key} ,客户端在构造资源 URI 时,服务器根据输入的前缀返回匹配的配置键候选值。同一个 @McpComplete 方法只负责补全一个目标(由uei 或 prompt指定)。

示例三:根据参数名区分补全逻辑

当一个提示词有多个参数时,可以接收 CompleteRequest.CompleteArgument 来区分当前正在补全的是哪个参数

java 复制代码
@McpComplete(prompt = "travel-planner")
public List<String> completeTravelDestination(CompleteRequest.CompleteArgument argument) {
    String prefix = argument.value().toLowerCase();
    String argumentName = argument.name();
    if ("city".equals(argumentName)) {
        return completeCities(prefix);
    } else if ("country".equals(argumentName)) {
        return completeCountries(prefix);
    }
    return List.of();
}

注意:@McpComplete 只规定根据前缀返回候选值的协议,但是用户在输入时该如何选择理想的补全方法,和选择方法后传给方法的参数内容顺序是否正确这都是前端+后端设计开发要注意的事。(顺序:如 country形参 项不能填入城市,city形参不能填入国家)

如何选择补全方法:官方规范中的请求示例明确显示:客户端要补全 code_review 这个提示词中的 language 参数时,会发送 "ref": {"type": "ref/prompt", "name": "code_review"} 和 "argument": {"name": "language"}。如果要补全 framework 参数,则把 argument.name 改为 "framework" 即可。

补全(@McpComplete)在整个交互链路中,扮演的是一个被动的、按需触发的"建议提供者" 角色。它不控制流程,不校验数据,也不决定UI。流程的控制权、UI的设计权、以及最终的校验权,始终分散在客户端 和服务器的业务逻辑之中。

4.2 用@McpTool 自定义MCP Server

使用**@McpTool** 修饰的方法会在扫描时自动注册为MCP Server。(非常方便)

下面在IDEA 中并采用Spring AI 框架进行 自定义服务。

4.2.1 自定义 MCP Server-STDIO

自做一个 查询用户信息 的MCP:

  1. 添加依赖

<dependency>

<groupId>org.springframework.ai</groupId>

<artifactId>spring-ai-starter-mcp-server</artifactId>

</dependency>

//spring ai bom(自己搭配合适的Spring boot版本)

复制代码
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>2.0.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

//打包工具
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>3.5.3</version>
        </plugin>
    </plugins>
</build>

// 除此之外,还可以添加需要的工具依赖,如 lombok

定义MCP Server: (代码:mcp-server-stdio-demo · 巨蛇神/JavaEE进阶 - 码云 - 开源中国)

  1. 打包

4.2.2 使用自定义的MCP Server

在Cursor中使用打好的工具包,当然在其他编程工具中使用也行,如IDEA。

询问AI "zhangsan的信息":(使用MCP Server)

4.3 通过@Tool 再暴露 来自定义MCP Server

第一步使用**@Tool** 修饰方法,将方法定义为工具,第二步工具暴露 出去,第三步接着在配置文件中将该暴露的方法名转换为 MCP工具。

这利用了Spring AI Tool转换。

整个父子工程代码:spring-ai-second-term-practice · 巨蛇神/JavaEE进阶 - 码云 - 开源中国

1.@Tool修饰成为工具

定义工具的内容都是一样的,只是加的注解不同,导致注服务有些区别。

2.暴露工具

3.配置文件

Cursor中使用服务:

IDEA中使用服务:

模型输出:

相关推荐
用户072367066601 小时前
Apollo 配置热更新落地:让 Spring Boot @ConfigurationProperties Bean 不重启即时生效
java
2601_970703831 小时前
中国除湿机与湿度控制行业研究报告
java·大数据·人工智能
深入云栈2 小时前
Netty 4.2.x 源码深度解析 (二十一):Pipeline 事件传播 —— Inbound 与 Outbound 的双向流转
java·后端·架构
爱敲代码的TOM2 小时前
10.27 易购软件 Java开发线下一面
java·开发语言
yuzhiboyouye2 小时前
String、StringBuilder、StringBuffer 区别(面试题)
java
斯维赤2 小时前
微信步数的10点定时推送,看似简单,实则暗藏玄机
java·后端
Linux_cy2 小时前
服务器管理方案选型:面板 / SSH 客户端 / 桌面型客户端怎么选
java·linux·运维·服务器·ssh·可视化·gmssh
可乐_不渴3 小时前
【Linux】 进程虚拟地址空间
java·linux·javascript
SimonKing3 小时前
号外号外,Stream-Nexus推送项目支持集群部署了
java·后端·程序员