Skip to content

Commit c576843

Browse files
committed
feat: integrate jsonschema output
1 parent 41d1ff1 commit c576843

File tree

7 files changed

+391
-38
lines changed

7 files changed

+391
-38
lines changed

README.md

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,42 @@ MCP Go SDK 是模型上下文协议(Model Context Protocol)的 Go 语言实
3636
- **高性能** - 并发安全,优化的消息处理
3737
- **安全防护** - 内置输入验证、路径遍历保护、资源限制
3838

39+
## MCP协议版本支持
40+
41+
本SDK跟踪并支持MCP协议的最新发展,确保与生态系统的兼容性:
42+
43+
### 支持的版本
44+
45+
| 版本 | 发布时间 | 主要特性 | 支持状态 |
46+
|------|----------|----------|----------|
47+
| **2025-06-18** | 2025年6月 | 结构化工具输出、工具注解、用户交互请求 | **完全支持** |
48+
| **2025-03-26** | 2025年3月 | OAuth 2.1授权、Streamable HTTP、JSON-RPC批处理 | **完全支持** |
49+
| **2024-11-05** | 2024年11月 | HTTP+SSE传输、基础工具和资源 | **完全支持** |
50+
51+
### 最新特性 (2025-06-18)
52+
53+
- **结构化工具输出**:工具可返回类型化JSON数据,便于程序化处理
54+
- **工具注解**:描述工具行为特征(只读、破坏性、缓存策略等)
55+
- **用户交互请求**:工具可主动请求用户输入或确认
56+
- **资源链接**:支持资源间的关联和引用
57+
- **协议版本头**:HTTP传输需要`MCP-Protocol-Version`
58+
59+
### 主要变更历史
60+
61+
**2025-03-26 → 2025-06-18**
62+
63+
- 新增结构化工具输出支持
64+
- 增强工具注解系统
65+
- 添加用户交互请求机制
66+
- 支持资源链接功能
67+
68+
**2024-11-05 → 2025-03-26**
69+
70+
- 引入OAuth 2.1授权框架
71+
- 用Streamable HTTP替代HTTP+SSE
72+
- 添加JSON-RPC批处理支持
73+
- 增加音频内容类型支持
74+
3975
## 安装
4076

4177
```bash
@@ -253,16 +289,16 @@ result, err := client.CallTool(ctx, "tool_name", map[string]interface{}{"param":
253289
## 协议支持
254290

255291
### MCP 标准合规性
256-
**完全符合 MCP 2025-06-18 规范**,向后兼容 MCP 2025-03-26, 2024-11-05
292+
**完全符合 MCP 2025-06-18 规范**,向后兼容 MCP 2025-03-26, 2024-11-05
257293

258294
### 传输协议
259295
| 协议 | 使用场景 | 官方支持 | 协议版本 |
260-
|------|----------|----------|----------|
261-
| **STDIO** | 子进程通信 | 官方标准 | 2024-11-05+ |
262-
| **SSE** | Web 应用 | 官方标准 | 2024-11-05+ |
263-
| **Streamable HTTP** | 现代 Web 应用 | 官方标准 | 2025-06-18 |
264-
| ~~**WebSocket**~~ | ~~实时应用~~ | 非官方标准 | - |
265-
| ~~**gRPC**~~ | ~~微服务~~ | 非官方标准 | - |
296+
|------|----------|------|----------|
297+
| **STDIO** | 子进程通信 | 官方标准 | 2024-11-05+ |
298+
| **SSE** | Web 应用 | 官方标准 | 2024-11-05+ |
299+
| **Streamable HTTP** | 现代 Web 应用 | 官方标准 | 2025-06-18 |
300+
| ~~**WebSocket**~~ | ~~实时应用~~ | 非官方标准 | - |
301+
| ~~**gRPC**~~ | ~~微服务~~ | 非官方标准 | - |
266302

267303
**支持的协议版本**: 2025-06-18, 2025-03-26, 2024-11-05
268304

@@ -301,6 +337,15 @@ if result.IsError {
301337

302338
MIT License - 详见 [LICENSE](LICENSE) 文件
303339

340+
## Roadmap
341+
342+
- [x] **结构化工具输出** - 支持类型化、验证的工具结果 (MCP 2025-06-18)
343+
- [ ] **基础会话管理** - 支持每客户端独立状态管理
344+
- [ ] **简单中间件系统** - 提供基本的请求/响应拦截能力
345+
- [ ] **用户交互请求 (Elicitation)** - 服务器可在交互过程中请求用户输入 (MCP 2025-06-18)
346+
- [ ] **OAuth 2.1授权支持** - 企业级安全认证机制
347+
- [ ] **高级工具过滤** - 基于用户角色的工具访问控制
348+
304349
## 相关项目
305350

306351
- [MCP 官方规范](https://github.com/anthropics/model-context-protocol) - 协议规范定义

examples/streamable-demo/server/main.go

Lines changed: 170 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,47 @@ import (
77
"os"
88
"os/signal"
99
"syscall"
10+
"time"
1011

1112
"github.com/voocel/mcp-sdk-go/protocol"
1213
"github.com/voocel/mcp-sdk-go/server"
1314
"github.com/voocel/mcp-sdk-go/transport/streamable"
1415
)
1516

17+
// CalculationResult 计算结果
18+
type CalculationResult struct {
19+
Expression string `json:"expression" description:"原始表达式"`
20+
Result float64 `json:"result" description:"计算结果"`
21+
Operation string `json:"operation" description:"执行的运算"`
22+
Timestamp string `json:"timestamp" description:"计算时间"`
23+
}
24+
25+
// WeatherInfo 天气信息
26+
type WeatherInfo struct {
27+
Location string `json:"location" description:"地点"`
28+
Temperature float64 `json:"temperature" description:"温度(摄氏度)"`
29+
Humidity int `json:"humidity" description:"湿度百分比"`
30+
Condition string `json:"condition" description:"天气状况"`
31+
UpdateTime string `json:"update_time" description:"更新时间"`
32+
}
33+
34+
// ServerStats 服务器统计信息
35+
type ServerStats struct {
36+
Uptime string `json:"uptime" description:"运行时间"`
37+
RequestCount int `json:"request_count" description:"请求总数"`
38+
ActiveTools int `json:"active_tools" description:"活跃工具数"`
39+
Protocol string `json:"protocol" description:"协议版本"`
40+
}
41+
42+
var (
43+
serverStartTime = time.Now()
44+
requestCounter = 0
45+
)
46+
1647
func main() {
1748
ctx, cancel := context.WithCancel(context.Background())
1849
defer cancel()
1950

20-
// 处理优雅关闭
2151
signalCh := make(chan os.Signal, 1)
2252
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
2353
go func() {
@@ -26,10 +56,7 @@ func main() {
2656
cancel()
2757
}()
2858

29-
// 创建FastMCP服务器
3059
mcp := server.NewFastMCP("Streamable HTTP 演示服务", "1.0.0")
31-
32-
// 注册一个简单的问候工具
3360
mcp.Tool("greet", "问候用户").
3461
WithStringParam("name", "用户名称", true).
3562
WithStringParam("language", "语言(可选)", false).
@@ -54,26 +81,125 @@ func main() {
5481
return protocol.NewToolResultText(greeting), nil
5582
})
5683

57-
// 注册一个数学计算工具
58-
mcp.Tool("calculate", "执行数学计算").
59-
WithStringParam("expression", "数学表达式", true).
60-
Handle(func(ctx context.Context, args map[string]any) (*protocol.CallToolResult, error) {
61-
expr, ok := args["expression"].(string)
84+
// 注册一个数学计算工具 - 结构化输出 (MCP 2025-06-18)
85+
err := mcp.Tool("calculate", "执行数学计算,返回结构化结果").
86+
WithStringParam("operation", "运算类型 (add, subtract, multiply, divide)", true).
87+
WithNumberParam("a", "第一个数字", true).
88+
WithNumberParam("b", "第二个数字", true).
89+
WithStructOutputSchema(CalculationResult{}). // 使用结构体自动生成输出模式
90+
HandleWithValidation(func(ctx context.Context, args map[string]any) (*protocol.CallToolResult, error) {
91+
requestCounter++
92+
93+
operation, _ := args["operation"].(string)
94+
a, _ := args["a"].(float64)
95+
b, _ := args["b"].(float64)
96+
97+
var result float64
98+
var opSymbol string
99+
100+
switch operation {
101+
case "add":
102+
result = a + b
103+
opSymbol = "+"
104+
case "subtract":
105+
result = a - b
106+
opSymbol = "-"
107+
case "multiply":
108+
result = a * b
109+
opSymbol = "×"
110+
case "divide":
111+
if b == 0 {
112+
return protocol.NewToolResultError("除数不能为零"), nil
113+
}
114+
result = a / b
115+
opSymbol = "÷"
116+
default:
117+
return protocol.NewToolResultError("不支持的运算类型"), nil
118+
}
119+
120+
calcResult := CalculationResult{
121+
Expression: fmt.Sprintf("%.2f %s %.2f", a, opSymbol, b),
122+
Result: result,
123+
Operation: operation,
124+
Timestamp: time.Now().Format(time.RFC3339),
125+
}
126+
127+
return protocol.NewToolResultTextWithStructured(
128+
fmt.Sprintf("计算完成:%.2f %s %.2f = %.2f", a, opSymbol, b, result),
129+
calcResult,
130+
), nil
131+
})
132+
if err != nil {
133+
log.Fatal(err)
134+
}
135+
136+
err = mcp.SimpleStructuredTool(
137+
"get_weather",
138+
"获取指定城市的天气信息",
139+
protocol.JSONSchema{
140+
"type": "object",
141+
"properties": map[string]any{
142+
"location": map[string]any{"type": "string", "description": "地点"},
143+
"temperature": map[string]any{"type": "number", "description": "温度(摄氏度)"},
144+
"humidity": map[string]any{"type": "integer", "description": "湿度百分比"},
145+
"condition": map[string]any{"type": "string", "description": "天气状况"},
146+
"update_time": map[string]any{"type": "string", "description": "更新时间"},
147+
},
148+
"required": []string{"location", "temperature", "humidity", "condition", "update_time"},
149+
},
150+
func(ctx context.Context, args map[string]any) (any, error) {
151+
requestCounter++
152+
153+
location, ok := args["location"].(string)
62154
if !ok {
63-
return protocol.NewToolResultError("参数 'expression' 必须是字符串"), nil
155+
return nil, fmt.Errorf("location参数是必需的")
64156
}
65157

66-
// 简单的计算示例(实际应用中应该使用安全的表达式解析器)
67-
result := fmt.Sprintf("计算结果:%s = [此处应该是计算结果]", expr)
68-
return protocol.NewToolResultText(result), nil
158+
conditions := []string{"晴天", "多云", "小雨", "阴天"}
159+
weather := WeatherInfo{
160+
Location: location,
161+
Temperature: 15 + float64(len(location)%20),
162+
Humidity: 50 + len(location)%40,
163+
Condition: conditions[len(location)%len(conditions)],
164+
UpdateTime: time.Now().Format("2006-01-02 15:04:05"),
165+
}
166+
167+
return weather, nil
168+
},
169+
)
170+
if err != nil {
171+
log.Fatal(err)
172+
}
173+
174+
// 注册服务器状态工具 - 演示动态结构化数据
175+
err = mcp.Tool("server_stats", "获取服务器运行状态").
176+
WithStructOutputSchema(ServerStats{}).
177+
HandleWithValidation(func(ctx context.Context, args map[string]any) (*protocol.CallToolResult, error) {
178+
requestCounter++
179+
180+
uptime := time.Since(serverStartTime)
181+
stats := ServerStats{
182+
Uptime: uptime.Round(time.Second).String(),
183+
RequestCount: requestCounter,
184+
ActiveTools: 4, // greet, calculate, get_weather, server_stats
185+
Protocol: "Streamable HTTP (MCP 2025-06-18)",
186+
}
187+
188+
return protocol.NewToolResultTextWithStructured(
189+
fmt.Sprintf("服务器已运行 %s,处理了 %d 个请求", stats.Uptime, stats.RequestCount),
190+
stats,
191+
), nil
69192
})
193+
if err != nil {
194+
log.Fatal(err)
195+
}
70196

71197
// 注册一个资源
72198
mcp.Resource("info://server", "服务器信息", "获取服务器基本信息").
73199
Handle(func(ctx context.Context) (*protocol.ReadResourceResult, error) {
74200
info := `# Streamable HTTP MCP 服务器
75201
76-
这是一个演示 Streamable HTTP 传输协议的 MCP 服务器。
202+
这是一个演示 Streamable HTTP 传输协议和结构化工具输出的 MCP 服务器。
77203
78204
## 特性
79205
@@ -82,16 +208,35 @@ func main() {
82208
- **会话管理**:支持有状态的会话
83209
- **可恢复连接**:支持连接中断后的恢复
84210
- **安全防护**:内置 DNS rebinding 攻击防护
211+
- **结构化输出**:支持类型化的工具结果 (MCP 2025-06-18)
85212
86213
## 协议版本
87214
88-
- MCP 版本:2025-03-26
215+
- MCP 版本:2025-06-18
89216
- 传输协议:Streamable HTTP
217+
- 新特性:结构化工具输出
90218
91219
## 可用工具
92220
93-
1. **greet** - 多语言问候工具
94-
2. **calculate** - 数学计算工具(演示用)
221+
1. **greet** - 多语言问候工具(传统文本输出)
222+
2. **calculate** - 数学计算工具(结构化输出演示)
223+
3. **get_weather** - 天气查询工具(简化API演示)
224+
4. **server_stats** - 服务器状态工具(动态数据演示)
225+
226+
## 结构化输出示例
227+
228+
工具现在可以返回类型化的JSON数据:
229+
230+
` + "```" + `json
231+
{
232+
"content": [{"type": "text", "text": "人类可读的描述"}],
233+
"structuredContent": {
234+
"result": 42,
235+
"timestamp": "2025-01-15T10:30:00Z",
236+
"operation": "multiply"
237+
}
238+
}
239+
` + "```" + `
95240
96241
## 可用资源
97242
@@ -113,13 +258,17 @@ func main() {
113258
var content string
114259
switch topic {
115260
case "transport":
116-
content = "Streamable HTTP 是 MCP 2025-03-26 规范中的新传输协议,它统一了 HTTP 和 SSE 的优势。"
261+
content = "Streamable HTTP 是 MCP 2025-06-18 规范中的传输协议,它统一了 HTTP 和 SSE 的优势。"
117262
case "session":
118263
content = "会话管理允许服务器在多个请求之间保持状态,提供更好的用户体验。"
119264
case "security":
120265
content = "Streamable HTTP 包含多种安全机制,包括 Origin 验证和会话管理。"
266+
case "structured":
267+
content = "结构化工具输出是 MCP 2025-06-18 的新特性,允许工具返回类型化的JSON数据,同时保持文本描述的可读性。"
268+
case "tools":
269+
content = "本服务器演示了多种工具类型:传统文本输出(greet)、结构化输出(calculate)、简化API(get_weather)和动态数据(server_stats)。"
121270
default:
122-
content = "Streamable HTTP 是一个现代化的 MCP 传输协议,提供了灵活的通信方式。"
271+
content = "Streamable HTTP 是一个现代化的 MCP 传输协议,现在支持结构化工具输出功能。"
123272
}
124273

125274
messages := []protocol.PromptMessage{
@@ -138,6 +287,8 @@ func main() {
138287
log.Println("启动 Streamable HTTP MCP 服务器")
139288
log.Println("监听地址: http://localhost:8081")
140289
log.Println("传输协议: Streamable HTTP")
290+
log.Println("MCP版本: 2025-06-18 (支持结构化工具输出)")
291+
log.Println("可用工具: greet, calculate, get_weather, server_stats")
141292

142293
if err := streamableServer.Serve(ctx); err != nil && err != context.Canceled {
143294
log.Fatalf("服务器错误: %v", err)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010
)
1111

1212
require (
13+
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
1314
golang.org/x/net v0.34.0 // indirect
1415
golang.org/x/sys v0.29.0 // indirect
1516
golang.org/x/text v0.21.0 // indirect

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
1010
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
1111
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
1212
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
13+
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
14+
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
1315
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
1416
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
1517
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=

0 commit comments

Comments
 (0)