日历MCP服务
MCP(模型上下文协议)服务器,通过官方接口暴露Microsoft 365日历操作 MCP Java SDK(MCP Java软件开发工具包)。
概述
这个Spring Boot服务提供了一个标准化的MCP接口用于日历操作,使得AI助手(如Claude)能够通过专用的编排器与Microsoft 365日历进行交互。
关键技术:
- Java 21 长期支持版(LTS)+ Spring Boot 3.5.6
- 官方MCP Java SDK 0.12.1 (处理所有协议操作)
- Microsoft Graph SDK 6.15.0(日历集成)
协议MCP SDK 自动处理 JSON-RPC 2.0、传输(SSE/HTTP)和方法路由。我们只需实现业务逻辑。
______________________________________________________________________
建筑
┌─────────────────────────────────────────────────────────┐
│ MCP Client (NestJS Orchestrator) │
│ {"method": "tools/call", "params": {...}} │
└──────────────────┬──────────────────────────────────────┘
│ HTTP/SSE
▼
┌─────────────────────────────────────────────────────────┐
│ McpServer (SDK - Spring Bean) │
│ ├─ Transport Layer (mcp-spring-webmvc) │
│ ├─ Protocol Parser (JSON-RPC 2.0) │
│ └─ Method Router │
└──────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ CalendarToolsProvider │
│ ├─ get_events handler │
│ ├─ block_dates handler │
│ ├─ find_available_slots handler │
│ └─ reschedule_event handler │
└──────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ CalendarService (Business Logic) │
│ ├─ Date validation │
│ ├─ Event transformation │
│ └─ Slot detection algorithms │
└──────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ GraphAPIService (MS Graph Integration) │
│ ├─ OAuth2 authentication │
│ ├─ Token management │
│ └─ MS Graph SDK calls │
└─────────────────────────────────────────────────────────┘______________________________________________________________________
特点/功能
该服务提供了4个MCP工具:
1. get_events
按日期范围查询日历事件,并可选择应用过滤器。
输入模式(或输入架构):
{
"start_date": "2025-10-15",
"end_date": "2025-10-31",
"filter": "optional filter string"
}2. block_dates
创建“外出办公”事件以占用时间段。
输入模式(或输入架构):
{
"start_date": "2025-10-20",
"end_date": "2025-10-22",
"reason": "Vacation"
}3. find_available_slots
分析日历并检测出空闲时间段以便安排日程。
输入模式:
{
"duration": 60,
"date_range": {
"start": "2025-10-15",
"end": "2025-10-31"
},
"participants": ["optional", "list", "of", "emails"]
}4. reschedule_event
将现有会议时间调整到新的时间段。
输入模式(或输入架构):
{
"event_id": "uuid-of-event",
"new_start_date": "2025-10-16T14:00:00",
"new_end_date": "2025-10-16T15:00:00"
}______________________________________________________________________
技术栈
核心
- Java21.0.5(Eclipse Temurin 长期支持版)
- Spring Boot3.5.6
- Maven3.9.9 版本附带工具链
MCP SDK(MCP软件开发工具包)
- MCP-BOM(注:MCP通常指“最小系统电路板”或特定项目中的“主板控制平台”,BOM指“物料清单”,但具体含义需根据上下文确定,此处为直译)0.12.1(物料清单)
- MCP0.12.1(核心协议及默认传输方式)
- mcp-spring-webmvc 翻译为中文是“基于Spring的Web MVC框架(或模块)”。不过,这里的“mcp”可能是一个特定项目、公司或组织的缩写,没有具体的上下文很难给出确切的翻译,所以在实际应用中,如果“mcp”有特定含义,应该将其融入翻译中。但基于通用理解,可以翻译为上述表述0.12.1(Spring Server-Sent Events/HTTP传输)
- mcp-test(可译为“MCP测试”或根据具体上下文保留原样,若“mcp”有特定含义,则需结合上下文翻译)0.12.1(测试工具)
Microsoft 集成
- Microsoft Graph(微软图)6.15.0(MS Graph Java SDK)
- Azure 身份1.13.2(OAuth2 客户端凭证)
公用事业(或公共设施)
- Lombok1.18.34(减少样板代码)
- springdoc-openapi(可翻译为“SpringDoc OpenAPI”或保持原名,因为这是一个特定的库或框架名称,在中文语境下通常直接使用原名)2.6.0(Swagger UI)
______________________________________________________________________
先决条件
- Java 21及以上版本
- Maven 3.9及以上版本 (包含包装:
./mvnw) - Azure AD 应用程序 具有日历权限
- Microsoft 365 帐户 用于测试
______________________________________________________________________
设置
1. 克隆并构建
git clone
cd calendar-mcp-service
./mvnw clean install2. 配置Azure凭据
编辑 .env:
MS_TENANT_ID=your-azure-tenant-id
MS_CLIENT_ID=your-app-client-id
MS_CLIENT_SECRET=your-client-secret3. Azure AD 权限
您的Azure AD应用程序需要这些 应用层 权限:
Calendars.ReadWrite- 读取和写入日历事件User.Read.All- 读取用户配置文件(以支持多用户)
在 Azure 门户中授予管理员同意。
______________________________________________________________________
跑
# Development mode
./mvnw spring-boot:run
# With dev profile (DEBUG logging)
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
# Production JAR
./mvnw clean package
java -jar target/calendar-mcp-service-0.0.1-SNAPSHOT.jar服务运行在: http://localhost:8080
健康检查:
curl http://localhost:8080/actuator/health______________________________________________________________________
工作原理(SDK 集成)
1. McpServer 配置
该SDK的 McpSyncServer 被配置为具有工具功能的Spring Bean:
@Configuration
@RequiredArgsConstructor
public class McpServerConfig {
private final CalendarToolsProvider toolsProvider;
@Bean
public WebMvcSseServerTransportProvider transportProvider() {
return WebMvcSseServerTransportProvider.builder()
.messageEndpoint("/mcp")
.build();
}
@Bean
public McpSyncServer mcpServer(WebMvcSseServerTransportProvider transportProvider) {
McpSyncServer server = McpServer.sync(transportProvider)
.serverInfo("calendar-mcp-service", "1.0.0")
.capabilities(McpSchema.ServerCapabilities.builder()
.tools(true) // Enable tool capabilities
.build())
.build();
// Register tools with handlers after server creation
toolsProvider.registerTools(server);
return server;
}
}2. 工具定义与注册
工具通过处理器进行定义 CalendarToolsProvider:
@Component
@RequiredArgsConstructor
@Slf4j
public class CalendarToolsProvider {
private final CalendarService calendarService;
private final ObjectMapper objectMapper;
// Single source of truth: tool definitions with handlers
private List getToolDefinitions() {
return List.of(
new ToolDefinition(
"get_events",
"Query calendar events by date range",
GetEventsSchema::create,
this::handleGetEvents // Handler method reference
),
new ToolDefinition(
"block_dates",
"Block time slots by creating Out of Office events",
BlockDatesSchema::create,
null // To be implemented
)
// ... other tools
);
}
// For tools/list discovery
public List getTools() {
return getToolDefinitions().stream()
.map(this::buildTool)
.toList();
}
// Register handlers with MCP server
public void registerTools(McpSyncServer server) {
log.info("Registering MCP tools with handlers");
getToolDefinitions().forEach(definition -> {
if (definition.handler() != null) {
var spec = new SyncToolSpecification(
buildTool(definition), // Tool metadata
definition.handler() // Handler function
);
server.addTool(spec);
log.debug("Registered tool '{}' with handler", definition.name());
}
});
}
private Tool buildTool(ToolDefinition definition) {
return Tool.builder()
.name(definition.name())
.description(definition.description())
.inputSchema(definition.schemaSupplier().get())
.build();
}
// Handler signature: McpSyncServerExchange + arguments
public CallToolResult handleGetEvents(
McpSyncServerExchange exchange,
Map arguments
) {
String startDate = (String) arguments.get("start_date");
String endDate = (String) arguments.get("end_date");
var events = calendarService.getEvents(startDate, endDate);
String json = objectMapper.writeValueAsString(events);
var textContent = new TextContent(json);
return new CallToolResult(List.of(textContent), false, null, null);
}
// Internal record for DRY tool definitions
private record ToolDefinition(
String name,
String description,
Supplier schemaSupplier,
BiFunction, CallToolResult> handler
) {}
}3. JSON模式定义
每个工具都有一个用于输入验证的JSON Schema:
public class GetEventsSchema {
public static JsonNode create() {
return JsonNodeFactory.instance.objectNode()
.put("type", "object")
.set("properties", JsonNodeFactory.instance.objectNode()
.set("start_date", JsonNodeFactory.instance.objectNode()
.put("type", "string")
.put("description", "Start date (ISO8601: yyyy-MM-dd)"))
.set("end_date", JsonNodeFactory.instance.objectNode()
.put("type", "string")
.put("description", "End date (ISO8601: yyyy-MM-dd)"))
)
.set("required", JsonNodeFactory.instance.arrayNode()
.add("start_date")
.add("end_date"));
}
}4. SDK请求流程
当客户端发送时:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_events",
"arguments": {
"start_date": "2025-10-15",
"end_date": "2025-10-31"
}
},
"id": 1
}该SDK自动:
- 接收 HTTP请求在
/mcp终端节点 - 解析 JSON-RPC 2.0 消息
- 路线 通过注册的工具处理器
SyncToolSpecification - 电话
handleGetEvents(exchange, arguments) - 序列化
CallToolResult到 JSON-RPC 响应 - 回报 通过SSE传输
关键点: SDK处理所有协议操作。我们仅在处理程序中实现业务逻辑。
______________________________________________________________________
测试
# Run all tests
./mvnw test
# Run specific test class
./mvnw test -Dtest=CalendarToolsProviderTest
# Integration tests
./mvnw verify______________________________________________________________________
使用cURL进行手动测试
列出可用工具
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}'调用get_events工具
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "get_events",
"arguments": {
"start_date": "2025-10-15",
"end_date": "2025-10-31"
}
},
"id": 2
}'______________________________________________________________________
项目结构
calendar-mcp-service/
├── src/main/java/com/enterprise/calendar/
│ ├── CalendarMCPApplication.java
│ ├── config/
│ │ ├── McpServerConfig.java # MCP SDK configuration
│ │ └── GraphConfig.java # MS Graph OAuth2 config
│ ├── mcp/
│ │ ├── CalendarToolsProvider.java # Tool registration
│ │ └── schema/ # JSON schemas for tools
│ ├── service/
│ │ ├── CalendarService.java # Business logic
│ │ └── GraphAPIService.java # MS Graph integration
│ ├── model/
│ │ └── calendar/ # DTOs (CalendarEvent, etc.)
│ ├── exception/
│ │ └── GraphAPIException.java
│ └── util/
│ └── DateUtils.java
├── src/main/resources/
│ ├── application.yml
│ └── application-dev.yml
├── pom.xml
├── .env.example
└── README.md注: 不 controller/ 或者 model/mcp/ 软件包 - SDK 处理协议层!
______________________________________________________________________
API 文档
Swagger UI: http://localhost:8080/swagger-ui.html
______________________________________________________________________
MCP通信
协议: JSON-RPC 2.0(由SDK处理) 交通: SSE(服务器发送事件)或可流式传输的HTTP 端口: 8080
设计用于与: NestJS MCP 服务编排器,用于将 Claude 的工具调用路由到此服务。
______________________________________________________________________
多版本Java安装
这个项目使用了 Maven 工具链 用于多版本Java管理。
配置: 见 pom.xml 和 ~/.m2/toolchains.xml
______________________________________________________________________
许可证
麻省理工学院(MIT)
______________________________________________________________________
