MCP(模型上下文协议)服务器,具有高级功能,包括MCP提示、采样和支持多种协议。
一个生产就绪的Spring Boot应用程序,演示了支持多种传输协议、MCP Prompts和AI采样功能的模型上下文协议(MCP)。此应用程序展示了如何构建一个灵活的MCP服务器,该服务器可以通过STDIO、SSE、流式HTTP和无状态HTTP协议进行操作。
🚀 特性
- 多种传输协议:STDIO、SSE、流式HTTP和无状态HTTP
- MCP工具:完成CRUD操作,作为可发现的MCP工具公开
- MCP提示:常见todo操作的预配置提示
- AI采样:与客户端人工智能功能集成,以增强响应能力
- 弹簧靴3.5.7 Java 21
- 春季AI 1.1.0-M4 支持MCP
- H2内存数据库 为了坚持
- REST API 测试和健康检查的端点
- Gradle 使用不同配置文件的自定义任务进行构建
📋 先决条件
- Java 21或更高版本
- Gradle 8.14.3+(包装内含)
- 可选: 任务 便于任务执行
🛠️ 技术栈
- 弹簧靴3.5.7:核心框架
- 春季AI 1.1.0-M4:MCP服务器实现
- Spring数据JPA:数据持久层
- H2数据库:内存数据库
- 龙目:锅炉板减少
- 雅加达验证:输入验证
📦 项目设置
该项目使用以下关键依赖项 build.gradle:
dependencies {
implementation platform("org.springframework.ai:spring-ai-bom:1.1.0-M4")
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'com.h2database:h2'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation 'org.springframework.ai:spring-ai-starter-mcp-server-webmvc'
implementation 'org.springframework.ai:spring-ai-starter-mcp-server'
implementation 'org.springframework.ai:spring-ai-mcp-annotations'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}存储库:
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
maven { url 'https://repo.spring.io/snapshot' }
maven {
name = 'Central Portal Snapshots'
url = 'https://central.sonatype.com/repository/maven-snapshots/'
}
}⚙️ 配置文件
主要配置(application.properties)
spring.application.name=todoapp
spring.profiles.active=stdio
# H2 Database Configuration
spring.datasource.url=jdbc:h2:mem:todo-db
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
# Enable H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-consoleSTDIO配置文件(application-stdio.properties)
# STDIO Profile - Standard Input/Output communication
# Uses spring-ai-starter-mcp-server dependency
# Disable web server for STDIO (no HTTP server needed)
spring.main.web-application-type=none
# Spring AI MCP Server STDIO configuration
spring.ai.mcp.server.stdio=true
# Disable Spring Boot banner
spring.main.banner-mode=off
###################################################################################
# Logging Configuration
###################################################################################
# Disable console logging completely for STDIO communication
logging.threshold.console=OFF
# Only use file logging
logging.file.name=${user.home}/mcp-server-stdio.log
logging.logback.rollingpolicy.max-file-size=10MB
logging.logback.rollingpolicy.max-history=10
# Set logging levels
logging.level.root=INFO
logging.level.org.springframework=WARN
logging.level.com.bothub.movie_mcp_server=DEBUG
# File log pattern
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%nSSE简介(application-sse.properties)
# SSE WebMVC Profile - Server-Sent Events
# Uses spring-ai-starter-mcp-server-webmvc dependency
spring.ai.mcp.server.stdio=false
# Spring AI MCP Server SSE configuration
spring.ai.mcp.server.protocol=SSE
# Server configuration for WebMVC
server.port=8080
# Note: MCP server metadata (name, version, description) are defined in main application.properties可流式配置文件(application-streamable.properties)
# Streamable WebMVC Profile - Streamable HTTP
# Uses spring-ai-starter-mcp-server-webmvc dependency
# Spring AI MCP Server Streamable configuration
spring.ai.mcp.server.protocol=STREAMABLE
# Server configuration for WebMVC
server.port=8080
logging.level.root=INFO
logging.level.org.apache.tomcat.util.compat=ERROR
# Note: MCP server metadata (name, version, description) are defined in main application.properties可流式配置文件(application-stateless.properties)
# Streamable WebMVC Profile - Streamable HTTP
# Uses spring-ai-starter-mcp-server-webmvc dependency
# Spring AI MCP Server Streamable configuration
spring.ai.mcp.server.protocol=STATELESS
# Server configuration for WebMVC
server.port=8080
logging.level.root=INFO
logging.level.org.apache.tomcat.util.compat=ERROR
# Note: MCP server metadata (name, version, description) are defined in main application.properties🔌 MCP传输协议
该应用程序支持四种不同的MCP传输协议:
| 协议 | 类型 | 用例 | 端口 |
|---|---|---|---|
| 工作室 | 标准I/O | CLI工具,直接进程通信 | N/A |
| 上海证券交易所 | 服务器发送事件 | Web客户端,实时更新 | 8080 |
| 流式HTTP | 带流媒体的HTTP | 有状态的HTTP连接 | 8080 |
| 无状态HTTP | HTTP无状态 | RESTful交互 | 8080 |
通过更改活动配置文件在协议之间切换:
# STDIO (no web server)
./gradlew bootRun --args='--spring.profiles.active=stdio'
# SSE
./gradlew bootRun --args='--spring.profiles.active=sse'
# Streamable HTTP
./gradlew bootRun --args='--spring.profiles.active=streamable'
# Stateless HTTP
./gradlew bootRun --args='--spring.profiles.active=stateless'📊 架构和组件
领域模型
所有实体(Todo.java)
package tools.muthuishere.todo.todo.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Title is required")
private String title;
private String description;
private boolean completed;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}要点:
@Entity:将其标记为用于数据库持久性的JPA实体@Data:生成getter、setter、toString、equals和hashCode的Lombok注释@Builder:Lombok注释,为对象创建提供构建器模式@NotBlank:验证注释确保标题不为空- 自动生成的ID
@GeneratedValue
Todo响应模型(TodoToolResponse.java)
package tools.muthuishere.todo.todo.model;
import lombok.*;
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TodoToolResponse {
private Todo todo;
private String fact;
}目的: MCP工具使用此包装器类使用采样功能返回Todo对象和AI生成的事实。
库层
所有仓库(TodoRepository.java)
package tools.muthuishere.todo.todo;
import tools.muthuishere.todo.todo.model.Todo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface TodoRepository extends JpaRepository {
}要点:
- 扩展
JpaRepository自动提供CRUD操作 - Spring Data JPA在运行时生成实现
- 基本操作不需要自定义查询
Long表示Todo实体的ID类型
服务层
Todo服务(TodoService.java)
package tools.muthuishere.todo.todo;
import tools.muthuishere.todo.todo.model.Todo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class TodoService {
private final TodoRepository todoRepository;
public List getAllTodos() {
return todoRepository.findAll();
}
public Optional getTodoById(Long id) {
return todoRepository.findById(id);
}
public Todo createTodo(Todo todo) {
todo.setCreatedAt(LocalDateTime.now());
todo.setUpdatedAt(LocalDateTime.now());
return todoRepository.save(todo);
}
public Optional updateTodo(Long id, Todo todoDetails) {
return todoRepository.findById(id).map(todo -> {
todo.setTitle(todoDetails.getTitle());
todo.setDescription(todoDetails.getDescription());
todo.setCompleted(todoDetails.isCompleted());
todo.setUpdatedAt(LocalDateTime.now());
return todoRepository.save(todo);
});
}
public boolean deleteTodo(Long id) {
return todoRepository.findById(id).map(todo -> {
todoRepository.delete(todo);
return true;
}).orElse(false);
}
}要点:
@Service:将其标记为Spring服务组件@RequiredArgsConstructor:Lombok为最终字段生成构造函数(依赖注入)- GetalTodos():从数据库中检索所有待办事项
- getTodoById():返回可选 -当todo不存在时处理案例
- Createtodour):设置时间戳并保存新待办事项
- 下载中:使用Optionalmap()进行安全更新,如果未找到todo,则返回空
- 删除幼儿/婴儿):返回表示成功/失败的布尔值
🔧 MCP工具
MCP工具通过以下方式向AI客户端公开应用程序功能 @McpTool 注释。该应用程序提供了五个可发现的工具:
Todo工具实施(TodoTools.java)
位于 src/main/java/tools/muthuishere/todo/todo/tools/TodoTools.java,该组件公开了五个集成了AI采样的MCP工具:
@Component
@RequiredArgsConstructor
public class TodoTools {
private final TodoService todoService;
@McpTool(name = "fetch-all-todos", description = "Gets all Todo items")
public List fetchAllTodos() {
return todoService.getAllTodos();
}
@McpTool(name = "fetch-todo-by-id", description = "Gets a Todo item by ID")
public Optional fetchTodoById(
@McpToolParam(description = "id for the Item")
Long id
) {
return todoService.getTodoById(id);
}
@McpTool(name = "make-todo", description = "Creates a new Todo item")
public TodoToolResponse makeTodo(
@McpToolParam(description = "Title for the Todo")
String title,
@McpToolParam(description = "Description for the Todo")
String description,
@McpToolParam(description = "Is the Todo completed?")
boolean completed,
McpSyncServerExchange serverExchange
) {
Todo todo = Todo.builder()
.title(title)
.description(description)
.completed(completed)
.createdAt(LocalDateTime.now())
.updatedAt(LocalDateTime.now())
.build();
Todo savedTodo = todoService.createTodo(todo);
// AI Sampling - get interesting fact about the todo
String fact = Sampling.createSamplingRequest(
serverExchange,
"You are an expert todo list assistant. Provide an interesting fact related to todo lists or productivity.",
"Share an interesting fact about this todo item: " + title
);
return TodoToolResponse.builder()
.todo(savedTodo)
.fact(fact)
.build();
}
@McpTool(name = "change-todo", description = "Updates an existing Todo item")
public Optional changeTodo(
@McpToolParam(description = "id for the Item") Long id,
@McpToolParam(description = "Title for the Todo") String title,
@McpToolParam(description = "Description for the Todo") String description,
@McpToolParam(description = "Is the Todo completed?") boolean completed
) {
return todoService.getTodoById(id).map(todo -> {
todo.setTitle(title);
todo.setDescription(description);
todo.setCompleted(completed);
todo.setUpdatedAt(LocalDateTime.now());
return todoService.createTodo(todo);
});
}
@McpTool(name = "remove-todo", description = "Deletes a Todo item by ID")
public boolean removeTodo(
@McpToolParam(description = "id for the Item") Long id
) {
return todoService.getTodoById(id).map(todo -> {
todoService.deleteTodo(id);
return true;
}).orElse(false);
}
}主要特点:
@McpTool:将方法公开为可发现的MCP工具@McpToolParam:为AI客户端提供参数描述- AI采样:The
make-todo工具用途McpSyncServerExchange请求人工智能生成的事实 - 可选处理:使用Java进行优雅的错误处理可选
- 建造者模式:使用龙目岛清洁物体构造
可用的MCP工具
| 工具名称 | 描述 | 参数 |
|---|---|---|
fetch-all-todos | 检索所有待办事项 | 无 |
fetch-todo-by-id | 按ID获取特定待办事项 | id: Long |
make-todo | 使用AI事实创建新待办事项 | title: String, description: String, completed: boolean |
change-todo | 更新现有待办事项 | id: Long, title: String, description: String, completed: boolean |
remove-todo | 按ID删除待办事项 | id: Long |
💬 MCP提示
该应用程序包括中常见操作的预配置提示 src/main/java/tools/muthuishere/todo/todo/prompts/TodoPrompts.java:
@Service
public class TodoPrompts {
@McpPrompt(
name = "create-todo-prompt",
description = "Prompt to create a new Todo item"
)
public McpSchema.GetPromptResult createTodoPrompt(
@McpArg(name = "title", description = "Title of the Todo item", required = true)
String title
) {
String message = "Add this " + title + " as a new todo item.";
return new McpSchema.GetPromptResult(
"Create a new Todo Item",
List.of(new McpSchema.PromptMessage(McpSchema.Role.USER, new McpSchema.TextContent(message)))
);
}
@McpPrompt(
name = "list-todos-prompt",
description = "Prompt to list all Todo items"
)
public McpSchema.GetPromptResult listTodosPrompt() {
String message = "List all the todo items.";
return new McpSchema.GetPromptResult(
"List all Todo Items",
List.of(new McpSchema.PromptMessage(McpSchema.Role.USER, new McpSchema.TextContent(message)))
);
}
}可用提示
| 提示名称 | 描述 | 参数 |
|---|---|---|
create-todo-prompt | 生成创建新待办事项的提示 | title: String (必填) |
list-todos-prompt | 生成提示以列出所有待办事项 | 无 |
🤖 AI采样集成
该应用程序包括 Sampling 公用事业类(src/main/java/tools/muthuishere/todo/utils/Sampling.java)这使得人工智能增强成为可能:
public static String createSamplingRequest(
McpSyncServerExchange exchange,
String systemPrompt,
String userPrompt
) {
// Creates a sampling request to the MCP client
// Returns AI-generated content based on prompts
}特征:
- 在发出请求之前检查客户端采样能力
- 向客户端发送日志记录通知
- 返回AI生成的文本内容
- 用于
make-todo生成有趣事实的工具
🏃 运行应用程序
使用Gradle
# Build the project
./gradlew clean build
# Run with STDIO profile (no web server)
./gradlew bootRun --args='--spring.profiles.active=stdio'
# Run with SSE profile
./gradlew bootRun --args='--spring.profiles.active=sse'
# Run with Streamable HTTP profile
./gradlew bootRun --args='--spring.profiles.active=streamable'
# Run with Stateless HTTP profile
./gradlew bootRun --args='--spring.profiles.active=stateless'使用Gradle自定义任务
# Development tasks defined in build.gradle
./gradlew devSse # Run with SSE profile
./gradlew devStreamable # Run with Streamable profile
./gradlew devStateless # Run with Stateless profile使用任务(任务文件)
如果你有 任务 安装:
# Build for STDIO
task build:stdio
# Run development servers
task dev:sse
task dev:streamable
task dev:stateless🔧 生产大楼
构建JAR文件
./gradlew clean bootJarJAR文件将在以下位置创建: build/libs/todo-0.0.1-SNAPSHOT.jar
运行JAR
# STDIO mode
java -Dspring.profiles.active=stdio -jar build/libs/todo-0.0.1-SNAPSHOT.jar
# SSE mode
java -Dspring.profiles.active=sse -jar build/libs/todo-0.0.1-SNAPSHOT.jar
# Streamable mode
java -Dspring.profiles.active=streamable -jar build/libs/todo-0.0.1-SNAPSHOT.jar
# Stateless mode
java -Dspring.profiles.active=stateless -jar build/libs/todo-0.0.1-SNAPSHOT.jar🔌 MCP客户端配置
克劳德桌面/MCP客户端
添加到MCP客户端配置中:
STDIO服务器配置
#### STDIO Server Configuration{ "mcpServers": { "todo-mcp-server-stdio": { "type": "stdio", "command": "java", "args": [ "-Dspring.profiles.active=stdio", "-jar", "/path/to/build/libs/todo-0.0.1-SNAPSHOT.jar" ] } } }
#### SSE服务器配置
{ "mcpServers": { "todo-mcp-server-sse": { "url": "http://localhost:8080/sse", "type": "sse" } } }
#### 流式HTTP服务器配置
{ "mcpServers": { "todo-mcp-server-streamable": { "url": "http://localhost:8080/mcp", "type": "http" } } }
#### 无状态HTTP服务器配置
{ "mcpServers": { "todo-mcp-server-stateless": { "url": "http://localhost:8080/mcp", "type": "http" } } }
## 🧪 测试服务器
### REST API端点
该应用程序包括用于测试的REST端点(运行web配置文件时):
Health check
curl http://localhost:8080/api/health
Test endpoint
curl http://localhost:8080/api/test
Root endpoint
curl http://localhost:8080/api/
### H2控制台
访问H2数据库控制台: `http://localhost:8080/h2-console`
**连接详细信息:**
- JDBC网址: `jdbc:h2:mem:todo-db`
- 用户名: `sa`
- 密码: `password`
### 使用MCP客户端进行测试
在MCP客户端(例如Claude Desktop)中配置后:
1. **全部列出**:“显示所有待办事项”(使用 `fetch-all-todos` 工具)
1. **创建待办事项**:“创建购买杂货的待办事项”(使用 `make-todo` 带有AI采样的工具)
1. **更新待办事项**:“将todo#1标记为已完成”(使用 `change-todo` 工具)
1. **删除待办事项**:“删除todo#2”(使用 `remove-todo` 工具)
1. **使用提示**:提示在客户端中自动可用
## 📁 项目结构
mcp-internals/ ├── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── tools/muthuishere/todo/ │ │ │ ├── TodoApplication.java # Main application │ │ │ ├── todo/ │ │ │ │ ├── ApiTestController.java # REST endpoints │ │ │ │ ├── TodoRepository.java # JPA repository │ │ │ │ ├── TodoService.java # Business logic │ │ │ │ ├── model/ │ │ │ │ │ ├── Todo.java # Entity │ │ │ │ │ └── TodoToolResponse.java # Response DTO │ │ │ │ ├── prompts/ │ │ │ │ │ └── TodoPrompts.java # MCP prompts │ │ │ │ └── tools/ │ │ │ │ └── TodoTools.java # MCP tools │ │ │ └── utils/ │ │ │ └── Sampling.java # AI sampling utility │ │ └── resources/ │ │ ├── application.properties # Main config │ │ ├── application-stdio.properties # STDIO config │ │ ├── application-sse.properties # SSE config │ │ ├── application-streamable.properties # Streamable config │ │ └── application-stateless.properties # Stateless config │ └── test/ │ └── java/ │ └── tools/muthuishere/todo/ │ └── TodoApplicationTests.java ├── build.gradle # Gradle build file ├── Taskfile.yaml # Task runner config └── README.md # This file
## 🎯 关键概念
### 模型上下文协议(MCP)
MCP是一种协议,使AI助手能够与外部工具和数据源进行交互。此应用程序演示:
- **工具**:AI可以调用的暴露函数(`@McpTool`)
- **提示词**:预先配置的对话启动器(`@McpPrompt`)
- **采样**:AI生成的内容集成(`McpSyncServerExchange`)
- **传输协议**:多种通信方式(STDIO、SSE、HTTP)
### Spring AI集成
该应用程序使用Spring AI的MCP实现:
- `spring-ai-starter-mcp-server`:STDIO服务器支持
- `spring-ai-starter-mcp-server-webmvc`:支持WebVC(SSE、HTTP)
- `spring-ai-mcp-annotations`:基于注释的配置
### 多协议支持
通过更改活动的Spring配置文件,相同的应用程序代码可以使用不同的传输协议。这展示了MCP规范的灵活性。
## 🚀 高级功能
### 1.人工智能增强响应
这 `make-todo` 该工具使用AI采样生成有关待办事项的有趣事实,演示如何将AI功能集成到MCP工具中。
### 2.日志记录配置
STDIO模式包括复杂的日志配置,以防止对协议通信的干扰:
- STDIO的控制台日志记录已禁用
- 基于文件的日志记录 `~/mcp-server-stdio.log`
- 可配置的日志轮换
### 3.多种传输协议
演示单个代码库如何通过Spring配置文件支持多个MCP传输协议。
### 4.健康与监测
包括用于健康检查和测试的REST端点,可用于监控基于HTTP的服务器。
## 📚 资源
- [模型上下文协议规范](https://modelcontextprotocol.io/)
- [Spring AI文档](https://docs.spring.io/spring-ai/reference/)
- [Spring Boot文档](https://docs.spring.io/spring-boot/docs/current/reference/html/)
- [MCP弹簧靴示例](https://github.com/spring-projects-experimental/spring-ai-mcp)