内容
# MCP 天气服务器
POC 使用 Spring Boot 4.1.0 和 WebMVC(HTTP + SSE)传输协议开发的 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器。服务器通过公共 API Open-Meteo 提供天气查询工具。
## 规格驱动开发与 Kiro
该项目使用 **规格驱动开发** 与 [Kiro](https://kiro.dev) 开发,Kiro 是一款具有 AI 代理的 IDE,可以将模糊的需求转化为结构化的规格说明,然后生成代码。
遵循的流程是:
1. **需求** → Kiro 根据初始想法生成功能性和非功能性需求(`.kiro/specs/mcp-server-weather-tool/requirements.md`)
2. **设计** → 生成带有图表和架构决策的技术设计(`.kiro/specs/mcp-server-weather-tool/design.md`)
3. **任务** → 创建增量实施的任务列表(`.kiro/specs/mcp-server-weather-tool/tasks.md`)
4. **实施** → 根据规格生成代码,并在每一步进行验证
这种方法确保需求和代码之间的可追溯性,并通过模糊性减少返工。
## 前置条件
- Java 21
- Maven 3.9+
## 构建
```bash
./mvnw clean package
```
## 执行
```bash
./mvnw spring-boot:run
```
服务器将在 8080 端口启动,并使用 SSE 传输协议。
## MCP 功能
| 类型 | 名称 | 描述 |
|------|------|-----------|
| **工具** | `get-current-weather` | 获取给定地理坐标的当前温度 |
| **工具** | `get-weather-forecast` | 获取给定地理坐标的每日天气预报(最高/最低温度) |
| **资源** | `weather://cities/{cityName}` | 已注册的巴西城市的坐标 |
| **提示** | `weather-query` | 生成结构化的气象查询 |
## 已注册城市
São Paulo, Rio de Janeiro, Brasília, Curitiba, Belo Horizonte
---
## MCP 集成测试(使用 curl)
MCP 协议通过 SSE 的工作方式如下:
1. 客户端打开 `GET /sse`(长连接)
2. 服务器用包含 sessionId 的 `event:endpoint` 响应
3. 客户端通过 `POST /mcp/messages?sessionId=...` 发送 JSON-RPC
4. 服务器通过 SSE 流以 `event:message` 的形式发送响应
### 测试脚本
```bash
#!/bin/bash
# 在后台打开 SSE 连接
SSE_LOG=/tmp/mcp-sse-events.log
curl -s -N http://localhost:8080/sse > $SSE_LOG 2>&1 &
SSE_PID=$!
sleep 2
# 提取带有 sessionId 的端点
SESSION_ENDPOINT=$(grep "^data:" $SSE_LOG | head -1 | sed 's/^data://')
BASE="http://localhost:8080${SESSION_ENDPOINT}"
# 1. 初始化
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2024-11-05","capabilities":{},
"clientInfo":{"name":"test-client","version":"1.0.0"}}
}'
sleep 1
# 2. 初始化通知
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","method":"notifications/initialized"
}'
sleep 1
# 3. 列出工具
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","id":2,"method":"tools/list"
}'
sleep 1
# 4. 调用 get-current-weather(São Paulo)
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"get-current-weather","arguments":{"latitude":-23.55,"longitude":-46.63}}
}'
sleep 2
# 5. 调用 get-weather-forecast(Curitiba,3 天)
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","id":4,"method":"tools/call",
"params":{"name":"get-weather-forecast","arguments":{"latitude":-25.43,"longitude":-49.27,"days":3}}
}'
sleep 2
# 6. 列出提示
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","id":5,"method":"prompts/list"
}'
sleep 1
# 7. 获取提示
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","id":6,"method":"prompts/get",
"params":{"name":"weather-query","arguments":{"city":"Curitiba","language":"pt-BR"}}
}'
sleep 1
# 8. 列出资源模板
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","id":7,"method":"resources/templates/list"
}'
sleep 1
# 9. 读取资源
curl -s -X POST "$BASE" -H "Content-Type: application/json" -d '{
"jsonrpc":"2.0","id":8,"method":"resources/read",
"params":{"uri":"weather://cities/São Paulo"}
}'
sleep 1
# 显示通过 SSE 接收的响应
echo "=== SSE 响应 ==="
cat $SSE_LOG
kill $SSE_PID 2>/dev/null
```
### 获取结果(25/06/2026)
所有端点都成功响应:
| # | JSON-RPC 方法 | 结果 |
|---|----------------|-----------|
| 1 | `initialize` | ✅ 服务器:`mcp-weather-server v1.0.0`,协议 `2024-11-05` |
| 2 | `notifications/initialized` | ✅ 确认 |
| 3 | `tools/list` | ✅ 2 个工具:`get-current-weather`,`get-weather-forecast` |
| 4 | `tools/call` get-current-weather | ✅ **São Paulo: 11.5°C** |
| 5 | `tools/call` get-weather-forecast | ✅ **Curitiba 3 天:13.5/2.3, 17.7/5.6, 20.1/10.6 °C** |
| 6 | `prompts/list` | ✅ 1 个提示:`weather-query` |
| 7 | `prompts/get` | ✅ 为 Curitiba 生成的天气查询消息 |
| 8 | `resources/templates/list` | ✅ 1 个模板:`weather://cities/{cityName}` |
| 9 | `resources/read` | ✅ São Paulo: 纬度 -23.5505, 经度 -46.6333 |
### SSE 响应(原始)
```
event:endpoint
data:/mcp/messages?sessionId=91b0c7e6-f404-4e44-9850-cc819246a7b4
event:message
data:{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2024-11-05","capabilities":{"completions":{},"logging":{},"prompts":{"listChanged":true},"resources":{"subscribe":false,"listChanged":true},"tools":{"listChanged":true}},"serverInfo":{"name":"mcp-weather-server","version":"1.0.0"}}}
event:message
data:{"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"get-current-weather","description":"使用纬度和经度坐标获取给定位置的当前温度","inputSchema":{"type":"object","properties":{"latitude":{"type":"number","description":"纬度(-90.0 至 90.0)"},"longitude":{"type":"number","description":"经度(-180.0 至 180.0)"}},"required":["latitude","longitude"]}},{"name":"get-weather-forecast","description":"获取给定位置在未来几天的每日天气预报(最高/最低温度)","inputSchema":{"type":"object","properties":{"latitude":{"type":"number","description":"纬度(-90.0 至 90.0)"},"longitude":{"type":"number","description":"经度(-180.0 至 180.0)"},"days":{"type":"integer","description":"预报天数(1-7,默认为 3)"}},"required":["latitude","longitude"]}}]}}
event:message
data:{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"São Paulo 的当前温度:11.5°C"}],"isError":false}}
event:message
data:{"jsonrpc":"2.0","id":4,"result":{"content":[{"type":"text","text":"Curitiba 3 天天气预报:\n2026-06-25:最高 13.5°C,最低 2.3°C\n2026-06-26:最高 17.7°C,最低 5.6°C\n2026-06-27:最高 20.1°C,最低 10.6°C"}],"isError":false}}
event:message
data:{"jsonrpc":"2.0","id":5,"result":{"prompts":[{"name":"weather-query","description":"为给定城市生成结构化的天气查询提示","arguments":[{"name":"city","description":"城市名称(必填)","required":true},{"name":"language","description":"响应语言(默认为 pt-BR)","required":false}]}]}}
event:message
data:{"jsonrpc":"2.0","id":6,"result":{"description":"Curitiba 的天气查询提示","messages":[{"role":"user","content":{"type":"text","text":"查找 Curitiba 当前的气象信息,并以 pt-BR 显示结果。"}}]}}
event:message
data:{"jsonrpc":"2.0","id":7,"result":{"resourceTemplates":[{"uriTemplate":"weather://cities/{cityName}","name":"城市坐标","description":"获取已注册的巴西城市的地理坐标","mimeType":"text/plain"}]}}
event:message
data:{"jsonrpc":"2.0","id":8,"result":{"contents":[{"uri":"weather://cities/São Paulo","mimeType":"text/plain","text":"{\"name\": \"São Paulo\", \"latitude\": -23.5505, \"longitude\": -46.6333}"}]}}
```