feat(mcp): 协议层换成官方 Model Context Protocol SDK,删自研实现(裁决 10.4.6 + AGENTS §9.5)
- 新增依赖:github.com/modelcontextprotocol/go-sdk v1.8.0(direct)+ github.com/google/jsonschema-go v0.4.3 (SDK 自带依赖,直接用于 schema 校验/默认值,避免再造一份);go.sum 纯新增 - 删除自研协议层:rpc.go / stdio.go / http.go / sse.go 及其测试 - 用 SDK 的成熟逻辑:命令传输(stdio 子进程)/ SSE / Streamable HTTP、协议版本协商与 -32022 回退、keepalive(含失败阈值)、tools/list_changed 订阅、能力协商(InitializeResult.Capabilities) - 保留自研的四件事(其余零自研):配置→传输、发现→registry 注册、能力族推断、schema 缓存(Prepare 命中不重复解析) - 能力闸诚实化:服务器声明 capabilities 但不含 tools 时 ListTools/DiscoverTools 返回 ErrCapabilityNotDeclared(不静默空集);完全不声明 capabilities 的老式服务器仍按 legacy 处理 - 复核:build 绿 + capability 全包测试绿(mcp 42 用例,含真子进程 stdio / httptest Streamable HTTP / 内存传输三类假 server,不依赖外网)+ `-race` 干净;变异 2 条(featuresFrom 恒 true / 删 applySchema 调用)已还原 - 无 REST/WS/env/数据目录变更;包内 API 有增删(见回报),仓外调用方签名未变(未改 cmd/cyrene 与 plugin/manager)
This commit is contained in:
1 parent
7b05b4bfa9
commit
a1083a322f
26 files changed
+1903
-3011
No files matched your search
@@ -0,0 +1,84 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/jsonschema-go/jsonschema"
|
||||
)
|
||||
|
||||
// schemaMap 把 SDK 传来的 input schema 规整成 map[string]any;nil 或无法识别时
|
||||
// 退化为「无约束的对象 schema」,保证 Parameters() 永远能返回可用形状。
|
||||
func schemaMap(v any) map[string]any {
|
||||
switch s := v.(type) {
|
||||
case nil:
|
||||
return emptyObjectSchema()
|
||||
case map[string]any:
|
||||
return s
|
||||
case json.RawMessage:
|
||||
return decodeSchema(s)
|
||||
case []byte:
|
||||
return decodeSchema(s)
|
||||
default:
|
||||
data, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return emptyObjectSchema()
|
||||
}
|
||||
return decodeSchema(data)
|
||||
}
|
||||
}
|
||||
|
||||
// emptyObjectSchema 返回无约束的空对象 schema。
|
||||
func emptyObjectSchema() map[string]any {
|
||||
return map[string]any{"type": "object", "properties": map[string]any{}}
|
||||
}
|
||||
|
||||
// decodeSchema 把 JSON 文本解成 schema map,失败时退回空对象 schema。
|
||||
func decodeSchema(data []byte) map[string]any {
|
||||
out := map[string]any{}
|
||||
if err := json.Unmarshal(data, &out); err != nil {
|
||||
return emptyObjectSchema()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// resolveSchema 把原始 schema 解析成可校验、可补默认值的已解析 schema。
|
||||
func resolveSchema(raw map[string]any) (*jsonschema.Resolved, error) {
|
||||
data, err := json.Marshal(raw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal input schema: %w", err)
|
||||
}
|
||||
var s jsonschema.Schema
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return nil, fmt.Errorf("parse input schema: %w", err)
|
||||
}
|
||||
resolved, err := s.Resolve(&jsonschema.ResolveOptions{ValidateDefaults: true})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve input schema: %w", err)
|
||||
}
|
||||
return resolved, nil
|
||||
}
|
||||
|
||||
// applySchema 按已解析的 input schema 先补默认值、再校验参数。
|
||||
// resolved 为 nil 表示 schema 不可解析,此时降级为不校验。
|
||||
// ApplyDefaults 对病态默认值会 panic,这里兜住转成错误,避免拖垮调用方。
|
||||
func applySchema(resolved *jsonschema.Resolved, args map[string]any) (err error) {
|
||||
if resolved == nil {
|
||||
return nil
|
||||
}
|
||||
if args == nil {
|
||||
args = map[string]any{}
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("input schema default application panicked: %v", r)
|
||||
}
|
||||
}()
|
||||
if err = resolved.ApplyDefaults(&args); err != nil {
|
||||
return fmt.Errorf("apply input schema defaults: %w", err)
|
||||
}
|
||||
if err = resolved.Validate(args); err != nil {
|
||||
return fmt.Errorf("arguments do not match input schema: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user