MCP Go SDK v0.3.0
Open in GitHub Codespaces (在 GitHub Codespaces 中打开)
BREAKING CHANGES (重大变更)
This version contains breaking changes. See the release notes for details
PkgGoDev (Go 官方包文档入口)
This repository contains an unreleased implementation of the official Go software development kit (SDK) for the Model Context Protocol
本仓库包含 模型上下文协议(Model Context Protocol,简称 MCP) 官方 Go 软件开发工具包(SDK)的未发布实现版本
[!WARNING]
The SDK is not yet at v1.0.0 and may still be subject to incompatible API changes
We aim to tag v1.0.0 in September, 2025
See https://github.com/modelcontextprotocol/go-sdk/issues/328 for details
该 SDK 尚未达到 v1.0.0 正式版,后续仍可能出现不兼容的 API 变更
我们计划于 2025 年 9 月发布 v1.0.0 正式版,详情可查看:https://github.com/modelcontextprotocol/go-sdk/issues/328
Design
The design doc for this SDK is at design.md, which was initially reviewed at modelcontextprotocol/discussions/364.
本 SDK 的设计文档位于 design.md,该文档最初在 modelcontextprotocol/discussions/364(MCP 社区讨论区)中经过评审
Further design discussion should occur in issues (for concrete proposals) or discussions for open-ended discussion.
See CONTRIBUTING.md for details.
后续设计讨论请通过以下渠道进行:
- 具体提案:通过 GitHub Issues 提交
- 开放性讨论:通过 GitHub Discussions 板块参与
详情请参阅 CONTRIBUTING.md
Package documentation 包文档
The SDK consists of three importable packages:
该 SDK 包含三个可导入的包,功能如下:
The github.com/modelcontextprotocol/go-sdk/mcp package defines the primary APIs for constructing and using MCP clients and servers.
定义构建和使用 MCP 客户端(Client)与服务端(Server)的核心 API
The github.com/modelcontextprotocol/go-sdk/jsonschema package provides an implementation of JSON Schema, used for MCP tool input and output schema.
提供 JSON Schema(JSON 模式)的实现,用于 MCP 工具的输入和输出Schema
The github.com/modelcontextprotocol/go-sdk/jsonrpc package is for users implementing their own transports.
供需要自定义传输层(Transport)的开发者使用(如自定义 JSON-RPC 通信逻辑)。
sidecar 进程(sidecar process):一种架构模式,指与主应用进程独立运行、为其提供辅助功能(如通信、监控、协议转换)的进程,常见于微服务或分布式系统中,此处用于承载 MCP 服务端逻辑,与客户端进程解耦
JSON Schema:一种基于 JSON 的规范,用于定义 JSON 数据的结构、类型和约束,MCP 用它确保工具输入输出的数据格式符合预期
JSON-RPC:一种基于 JSON 的远程过程调用(RPC)协议,用于不同进程 / 服务间的通信,jsonrpc 包为开发者提供了自定义该协议传输逻辑的能力(如添加加密、重试机制)
Example
an MCP client communicates with an MCP server running in a sidecar process:
以下示例展示了 MCP 客户端如何与运行在 sidecar 进程中的 MCP 服务端进行通信:
客户端调用服务端提供的 “greet” 工具,服务端返回问候信息,整体逻辑清晰地体现了 MCP 协议的基本使用方式
客户端调用名为 “greet” 的工具并传递参数(如name: “you”),最后接收并打印服务端返回的问候信息
package main
import (
"context"
"log"
"os/exec"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func main() {
ctx := context.Background()
// Create a new client, with no features.
client := mcp.NewClient(&mcp.Implementation{Name: "mcp-client", Version: "v1.0.0"}, nil)
// Connect to a server over stdin/stdout
// MCP 提供的传输层实现,通过执行外部命令(exec.Command("myserver"))启动服务端进程,并通过该进程的标准输入输出与服务端通信
// 这里的 myserver 是服务端可执行程序的名称(即第二段代码编译后的程序)
transport := &mcp.CommandTransport{Command: exec.Command("myserver")}
session, err := client.Connect(ctx, transport, nil)
if err != nil {
log.Fatal(err)
}
defer session.Close()
// Call a tool on the server.
// 调用 mcp 服务端的工具 greet
params := &mcp.CallToolParams{
Name: "greet",
Arguments: map[string]any{"name": "you"},
}
res, err := session.CallTool(ctx, params)
if err != nil {
log.Fatalf("CallTool failed: %v", err)
}
if res.IsError {
log.Fatal("tool failed")
}
for _, c := range res.Content {
log.Print(c.(*mcp.TextContent).Text)
}
}
the corresponding server component, which communicates with its client over stdin/stdout:
package main
import (
"context"
"log"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// 用于接收客户端传递的参数,字段 Name 对应客户端发送的 arguments.name
// json:"name" 指定 JSON 反序列化时的字段名(与客户端参数键名对应)
// jsonschema:"...":通过 JSON Schema 描述该字段的含义,用于 MCP 协议的元数据校验(确保客户端传递的参数符合预期)
type HiParams struct {
Name string `json:"name" jsonschema:"the name of the person to greet"`
}
func SayHi(ctx context.Context, req *mcp.CallToolRequest, args HiParams) (*mcp.CallToolResult, any, error) {
// 功能是根据输入的姓名返回问候语
// 服务端将响应通过 stdin/stdout 发送回客户端
return &mcp.CallToolResult{
Content: []mcp.Content{&mcp.TextContent{Text: "Hi " + args.Name}},
}, nil, nil
}
// 注册一个 “greet” 工具(功能是根据输入的姓名返回问候语),通过标准输入输出与客户端通信,接收工具调用请求并返回结果。
func main() {
// Create a server with a single tool.
server := mcp.NewServer(&mcp.Implementation{Name: "greeter", Version: "v1.0.0"}, nil)
// 当有请求时,服务端解析请求,找到注册的 “greet” 工具
mcp.AddTool(server, &mcp.Tool{Name: "greet", Description: "say hi"}, SayHi)
// Run the server over stdin/stdout, until the client disconnects
// 服务端将响应通过 stdin/stdout 发送回客户端
// 启动服务端并监听请求
if err := server.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
log.Fatal(err)
}
}
Acknowledgements 致谢
Several existing Go MCP SDKs inspired the development and design of this official SDK, notably mcp-go, authored by Ed Zynda. We are grateful to Ed as well as the other contributors to mcp-go, and to authors and contributors of other SDKs such as mcp-golang and go-mcp. Thanks to their work, there is a thriving ecosystem of Go MCP clients and servers.
本官方软件开发工具包(SDK)的开发与设计,受到了多个现有 Go 语言版 MCP(模型上下文协议)SDK 的启发,其中值得特别提及的是由 Ed Zynda 开发的 mcp-go。我们向 Ed 以及 mcp-go 的其他贡献者,同时也向 mcp-golang、go-mcp 等其他 SDK 的开发者与贡献者表示感谢。正是得益于他们的工作,Go 语言生态下的 MCP 客户端与服务端生态系统才得以蓬勃发展。
JsonSchema
什么是 JSON Schema
JSON Schema 是一种基于 JSON 格式的规范,用于定义和验证 JSON 数据的结构、类型、约束条件
它相当于 JSON 数据的 “蓝图”:
- 规定 JSON 数据必须包含哪些字段(如 name、age)
- 限制字段的类型(如 age 必须是整数)
- 设定数值范围(如 age 必须 ≥ 0)
- 定义数组元素的类型(如数组元素必须是字符串)
- 支持复杂结构(如嵌套对象、枚举值、条件约束等)
例如,一个简单的 JSON Schema 可以定义 “用户信息” 的数据格式:
{
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1 },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name"]
}
这个 Schema 规定:JSON 必须是对象,包含 name(非空字符串)和可选的 age(非负整数)
元信息
// A Schema is a JSON schema object
type Schema struct {
// core
ID string `json:"$id,omitempty"`
Schema string `json:"$schema,omitempty"`
Ref string `json:"$ref,omitempty"`
Comment string `json:"$comment,omitempty"`
Defs map[string]*Schema `json:"$defs,omitempty"`
// definitions is deprecated but still allowed. It is a synonym for $defs.
Definitions map[string]*Schema `json:"definitions,omitempty"`
Anchor string `json:"$anchor,omitempty"`
DynamicAnchor string `json:"$dynamicAnchor,omitempty"`
DynamicRef string `json:"$dynamicRef,omitempty"`
Vocabulary map[string]bool `json:"$vocabulary,omitempty"`
// metadata
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
Default json.RawMessage `json:"default,omitempty"`
Deprecated bool `json:"deprecated,omitempty"`
ReadOnly bool `json:"readOnly,omitempty"`
WriteOnly bool `json:"writeOnly,omitempty"`
Examples []any `json:"examples,omitempty"`
// validation
// Use Type for a single type, or Types for multiple types; never both.
Type string `json:"-"`
Types []string `json:"-"`
Enum []any `json:"enum,omitempty"`
// Const is *any because a JSON null (Go nil) is a valid value.
Const *any `json:"const,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty"`
Minimum *float64 `json:"minimum,omitempty"`
Maximum *float64 `json:"maximum,omitempty"`
ExclusiveMinimum *float64 `json:"exclusiveMinimum,omitempty"`
ExclusiveMaximum *float64 `json:"exclusiveMaximum,omitempty"`
MinLength *int `json:"minLength,omitempty"`
MaxLength *int `json:"maxLength,omitempty"`
Pattern string `json:"pattern,omitempty"`
// arrays
PrefixItems []*Schema `json:"prefixItems,omitempty"`
Items *Schema `json:"items,omitempty"`
MinItems *int `json:"minItems,omitempty"`
MaxItems *int `json:"maxItems,omitempty"`
AdditionalItems *Schema `json:"additionalItems,omitempty"`
UniqueItems bool `json:"uniqueItems,omitempty"`
Contains *Schema `json:"contains,omitempty"`
MinContains *int `json:"minContains,omitempty"` // *int, not int: default is 1, not 0
MaxContains *int `json:"maxContains,omitempty"`
UnevaluatedItems *Schema `json:"unevaluatedItems,omitempty"`
// objects
MinProperties *int `json:"minProperties,omitempty"`
MaxProperties *int `json:"maxProperties,omitempty"`
Required []string `json:"required,omitempty"`
DependentRequired map[string][]string `json:"dependentRequired,omitempty"`
Properties map[string]*Schema `json:"properties,omitempty"`
PatternProperties map[string]*Schema `json:"patternProperties,omitempty"`
AdditionalProperties *Schema `json:"additionalProperties,omitempty"`
PropertyNames *Schema `json:"propertyNames,omitempty"`
UnevaluatedProperties *Schema `json:"unevaluatedProperties,omitempty"`
// logic
AllOf []*Schema `json:"allOf,omitempty"`
AnyOf []*Schema `json:"anyOf,omitempty"`
OneOf []*Schema `json:"oneOf,omitempty"`
Not *Schema `json:"not,omitempty"`
// conditional
If *Schema `json:"if,omitempty"`
Then *Schema `json:"then,omitempty"`
Else *Schema `json:"else,omitempty"`
DependentSchemas map[string]*Schema `json:"dependentSchemas,omitempty"`
// other
// https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.8
ContentEncoding string `json:"contentEncoding,omitempty"`
ContentMediaType string `json:"contentMediaType,omitempty"`
ContentSchema *Schema `json:"contentSchema,omitempty"`
// https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-00#rfc.section.7
Format string `json:"format,omitempty"`
// Extra allows for additional keywords beyond those specified.
Extra map[string]any `json:"-"`
}
这段代码定义了 Go 语言中 JSON Schema 2020-12 版本的核心结构体 Schema,是 JSON Schema 规范在代码层面的直接映射
https://json-schema.org/draft/2020-12
https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01
https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-validation-01
它完整覆盖了 JSON Schema 的核心功能(如元数据描述、数据验证、逻辑组合、条件约束等)
同时通过 Go 语言特性(如指针、切片、映射)解决了 JSON Schema 中的 “可选字段”“空值区分” 等问题
A Schema value may have non-zero values for more than one field, all relevant non-zero fields are used for validation
一个 Schema 值可以有多个字段为非零值(即包含多个约束规则),所有相关的非零字段都会被用于数据验证
即多个约束需同时满足,例如同时设置 MinLength 和 Pattern 时,数据需同时符合长度和正则要求
There is one exception to provide more Go type-safety: the Type and Types fields are mutually exclusive.
但存在一个例外,目的是提供更强的 Go 类型安全性:Type 字段(单类型)和 Types 字段(多类型)是互斥的
即不能同时设置,避免出现类型定义冲突
Since this struct is a Go representation of a JSON value, it inherits JSON’s distinction between nil and empty.
Nil slices and maps are considered absent, but empty ones are present and affect validation.
由于该结构体是 JSON 值在 Go 语言中的表示形式,因此它继承了 JSON 中 “nil(空值)” 与 “empty(空结构)” 的区别。
nil 切片和 nil 映射会被视为 “字段不存在 absent”,但空切片(如 [] any {})和空映射(如 map [string]*Schema {})会被视为 “字段存在”,且会影响(affect) 验证逻辑
Schema{Enum: nil}
is equivalent to an empty schema, so it validates every instance
Schema{Enum: []any{}}
requires equality to some slice element, so it vacuously rejects every instance.
要求与某些切片元素相等,因此它会空洞地拒绝每个实例
因此结构体大量使用 指针类型(如 *float64、*int) 和 nil 切片 / 映射 来区分这两种场景
类型安全:通过 Type(单类型)和 Types(多类型)互斥设计,避免 JSON Schema 中类型字段格式错误(如同时指定单类型和多类型)
结构体字段按 JSON Schema 的功能模块 可分为 8 大类,每类对应 JSON Schema 的一个核心能力:
核心元数据(Core Metadata)- Schema 自身的标识与引用
这类字段用于定义 Schema 的 “身份信息”“引用关系”“子 Schema 定义”,是构建复杂 Schema(如嵌套、复用)的基础
字段名 类型 JSON 键名 核心含义
ID string $id Schema 的唯一标识符(URI 格式,如 https://example.com/user.schema),用于跨 Schema 引用;
Schema string $schema 声明该 Schema 遵循的 JSON Schema 版本(如 http://json-schema.org/draft-07/schema#);
Ref string $ref 引用其他 Schema(本地 #/$defs/user 或远程 URI),实现 Schema 复用;
Comment string $comment 开发者备注(不影响验证逻辑,仅用于文档说明);
Defs map[string]*Schema $defs 定义可复用的子 Schema(键为子 Schema 名称,值为子 Schema 实例),如 $defs: { "age": { "type": "integer" } };
Definitions map[string]*Schema definitions 兼容旧版本的 “子 Schema 定义”(JSON Schema 2019-09 前用该字段,2020-12 推荐用 $defs);
Anchor string $anchor 本地锚点(如 #user-anchor),用于同一 Schema 内的局部引用;
DynamicAnchor string $dynamicAnchor 动态锚点(高级特性),用于 “扩展 Schema 时动态替换引用目标”;
DynamicRef string $dynamicRef 动态引用(配合 DynamicAnchor 使用),支持更灵活的 Schema 扩展;
Vocabulary map[string]bool $vocabulary 声明该 Schema 依赖的 “关键词集合”(键为关键词 URI,值为 “是否必须支持”),如 { "https://json-schema.org/draft/2020-12/vocab/validation": true };
描述性元数据(Descriptive Metadata)- Schema 的文档说明
这类字段仅用于 “描述 Schema 的含义”,不影响数据验证逻辑,目的是提升 Schema 的可读性和可维护性
字段名 类型 JSON 键名 核心含义
Title string title Schema 的简短标题(如 “用户信息 Schema”)
Description string description Schema 的详细描述(如 “用于验证用户注册接口的请求参数”)
Default json.RawMessage default 数据的默认值(需与 Schema 类型匹配,如 default: 18 对应整数类型)
Deprecated bool deprecated 标记该字段已废弃(如 API 版本迭代中不再推荐使用)
ReadOnly bool readOnly 标记该字段仅用于 “读取数据”(如接口响应中的 id,不允许客户端提交)
WriteOnly bool writeOnly 标记该字段仅用于 “写入数据”(如密码字段,不允许在响应中返回)
Examples []any examples 示例值(用于文档,不影响验证,如 examples: ["张三", "李四"])
基础数据验证(Basic Validation)- 数据类型与基础约束
这类字段是 JSON Schema 的核心,用于定义 “数据必须满足的基础条件”,包括类型、枚举、数值范围、字符串长度等。
字段名 类型 JSON 键名 核心含义
Type string 无(特殊处理) 单数据类型(如 string、integer、object),与 Types 互斥;
Types []string 无(特殊处理) 多数据类型(如 ["string", "null"] 表示 “字符串或 null”),与 Type 互斥;
Enum []any enum 枚举约束(数据必须是数组中的某一个值,如 enum: ["男", "女"]);
Const *any const 常量约束(数据必须严格等于该值,如 const: "admin" 表示 “只能是 admin”);
MultipleOf *float64 multipleOf 数值必须是该值的整数倍(如 multipleOf: 2 表示 “偶数”);
Minimum *float64 minimum 数值最小值(包含等于,如 minimum: 0 表示 “≥0”);
Maximum *float64 maximum 数值最大值(包含等于,如 maximum: 100 表示 “≤100”);
ExclusiveMinimum *float64 exclusiveMinimum 数值最小值(不包含等于,如 exclusiveMinimum: 0 表示 “>0”);
ExclusiveMaximum *float64 exclusiveMaximum 数值最大值(不包含等于,如 exclusiveMaximum: 100 表示 “<100”);
MinLength *int minLength 字符串最小长度(如 minLength: 2 表示 “至少 2 个字符”);
MaxLength *int maxLength 字符串最大长度(如 maxLength: 10 表示 “最多 10 个字符”);
Pattern string pattern 字符串正则表达式约束(如 pattern: "^[a-zA-Z0-9]+$" 表示 “仅字母数字”);
数组验证(Array Validation)- 数组结构与元素约束
专门用于验证 “数组类型” 数据,定义数组的元素规则、长度、唯一性等。
字段名 类型 JSON 键名 核心含义
PrefixItems []*Schema prefixItems 数组 “前 N 个元素” 的单独约束(如 prefixItems: [{"type":"string"}, {"type":"integer"}] 表示 “第 1 个元素是字符串,第 2 个是整数”)
Items *Schema items 数组 “所有元素” 的统一约束(如 items: {"type":"string"} 表示 “所有元素都是字符串”)
MinItems *int minItems 数组最小长度(如 minItems: 1 表示 “至少 1 个元素”)
MaxItems *int maxItems 数组最大长度(如 maxItems: 10 表示 “最多 10 个元素”)
AdditionalItems *Schema additionalItems 数组 “超出 PrefixItems 部分元素” 的约束(如 PrefixItems 定义前 2 个元素,AdditionalItems 定义第 3 个及以后元素)
UniqueItems bool uniqueItems 数组元素是否唯一(true 表示 “不允许重复”,如 uniqueItems: true 表示 “数组内无重复值”)
Contains *Schema contains 数组 “至少包含一个” 符合该 Schema 的元素(如 contains: {"type":"integer"} 表示 “数组中至少有一个整数”)
MinContains *int minContains 数组 “至少包含 N 个” 符合 Contains 约束的元素(默认 1,如 minContains: 2 表示 “至少 2 个整数”)
MaxContains *int maxContains 数组 “最多包含 N 个” 符合 Contains 约束的元素(如 maxContains: 3 表示 “最多 3 个整数”)
UnevaluatedItems *Schema unevaluatedItems 数组 “未被其他规则(如 Items、PrefixItems)覆盖的元素” 的约束(高级特性,用于复杂嵌套 Schema)
对象验证(Object Validation)- 对象结构与字段约束
专门用于验证 “对象类型” 数据(JSON 中的 {}),定义对象的字段规则、字段数量、字段名约束等
字段名 类型 JSON 键名 核心含义
MinProperties *int minProperties 对象最小字段数量(如 minProperties: 2 表示 “至少 2 个字段”)
MaxProperties *int maxProperties 对象最大字段数量(如 maxProperties: 5 表示 “最多 5 个字段”)
Required []string required 对象 “必须包含” 的字段(如 required: ["name", "age"] 表示 “必须有 name 和 age 字段”)
DependentRequired map[string][]string dependentRequired 字段依赖(键为 “触发字段”,值为 “触发后必须包含的字段”,如 dependentRequired: {"role": ["permissions"]} 表示 “若有 role 字段,则必须有 permissions 字段”)
Properties map[string]*Schema properties 对象 “指定字段” 的约束(键为字段名,值为该字段的 Schema,如 properties: {"name": {"type":"string"}} 表示 “name 字段是字符串”)
PatternProperties map[string]*Schema patternProperties 对象 “字段名匹配正则” 的约束(键为正则表达式,值为字段 Schema,如 patternProperties: {"^age_": {"type":"integer"}} 表示 “字段名以 age_ 开头的字段必须是整数”)
AdditionalProperties *Schema additionalProperties 对象 “未在 Properties/PatternProperties 中定义的字段” 的约束(如 additionalProperties: false 表示 “不允许额外字段”)
PropertyNames *Schema propertyNames 对象 “字段名” 的约束(如 propertyNames: {"pattern": "^[a-z_]+$"} 表示 “字段名只能是小写字母和下划线”)
UnevaluatedProperties *Schema unevaluatedProperties 对象 “未被其他规则覆盖的字段” 的约束(类似 UnevaluatedItems,用于复杂嵌套)
逻辑组合(Logical Combination)- 多 Schema 的逻辑关系
通过 “与、或、异或、非” 等逻辑,将多个 Schema 组合成更复杂的约束
字段名 类型 JSON 键名 核心含义
AllOf []*Schema allOf 数据必须 “同时满足所有” 子 Schema(逻辑 “与”,如 allOf: [{"type":"integer"}, {"minimum":0}] 表示 “整数且≥0”)
AnyOf []*Schema anyOf 数据必须 “满足至少一个” 子 Schema(逻辑 “或”,如 anyOf: [{"type":"string"}, {"type":"integer"}] 表示 “字符串或整数”)
OneOf []*Schema oneOf 数据必须 “恰好满足一个” 子 Schema(逻辑 “异或”,如 oneOf: [{"minimum":0}, {"maximum":-1}] 表示 “非负或负,二选一”)
Not *Schema not 数据必须 “不满足” 该子 Schema(逻辑 “非”,如 not: {"type":"string"} 表示 “不是字符串”)
条件约束(Conditional Validation)- 基于条件的动态约束
根据 “数据是否满足某个 Schema”,动态应用不同的约束规则
字段名 类型 JSON 键名 核心含义
If *Schema if 条件 Schema(若数据满足 If,则应用 Then;否则应用 Else)
Then *Schema then 满足 If 时应用的约束(如 If: {"type":"integer"}, Then: {"minimum":0} 表示 “若为整数,则≥0”)
Else *Schema else 不满足 If 时应用的约束(如 Else: {"type":"string"} 表示 “若不是整数,则必须是字符串”)
DependentSchemas map[string]*Schema dependentSchemas 字段依赖的 Schema 约束(键为 “触发字段”,值为 “触发后必须满足的 Schema”,如 dependentSchemas: {"role": {"properties": {"permissions": {"type":"array"}}}} 表示 “若有 role 字段,则 permissions 字段必须是数组”)
内容与格式(Content & Format)- 特殊数据类型的约束
针对 “编码内容”(如 Base64)和 “特定格式”(如日期、邮箱)的专项约束
字段名 类型 JSON 键名 核心含义
ContentEncoding string contentEncoding 内容编码格式(如 base64 表示 “数据是 Base64 编码的二进制”)
ContentMediaType string contentMediaType 内容媒体类型(MIME 类型,如 image/png 表示 “Base64 编码的 PNG 图片”)
ContentSchema *Schema
示例
验证 Go 结构体与 JSON 的双向转换
验证 JSON Schema 结构体在 Go 语言中的如下特性:
- 序列化 / 反序列化的一致性(JSON 与 Go 结构体之间的转换是否准确)
- 错误处理(对不符合 Schema 规范的 JSON 输入能否正确报错)
- 克隆功能(复制 Schema 结构体时是否深拷贝,避免原对象被修改)
确保Schema 在 Go 结构体与 JSON 之间的转换准确无误
目的:确保 Schema 结构体序列化为 JSON 后,再反序列化为 Go 结构体,结果与原始结构体完全一致(“往返” 无损失)
TestGoRoundTrip
func TestGoRoundTrip(t *testing.T) {
// Verify that Go representations round-trip.
for _, s := range []*Schema{
{Type: "null"}, // 基础类型(type: "null")
{Types: []string{"null", "number"}}, // 多类型(types: ["null", "number"])
{Type: "string", MinLength: Ptr(20)}, // 字符串约束(minLength: 20)
{Minimum: Ptr(20.0)}, // 数值约束(minimum: 20.0)
{Items: &Schema{Type: "integer"}}, // 数组元素约束(items: {type: "integer"})
{Const: Ptr(any(0))}, // 常量值(const: 0、const: null 等)
{Const: Ptr(any(nil))}, // 常量值(const: 0、const: null 等)
{Const: Ptr(any([]int{}))},
{Const: Ptr(any(map[string]any{}))},
{Default: mustMarshal(1)},
{Default: mustMarshal(nil)}, // 默认值(default: 1)
{Extra: map[string]any{"test": "value"}}, // 额外字段(extra: {"test": "value"})
} {
data, err := json.Marshal(s) // 序列化为 JSON 字节流
if err != nil {
t.Fatal(err)
}
var got *Schema
mustUnmarshal(t, data, &got) // 反序列化为新的 Schema 实例 got
if !Equal(got, s) {
t.Errorf("got %s, want %s", got.json(), s.json())
if got.Const != nil && s.Const != nil {
t.Logf("Consts: got %#v (%[1]T), want %#v (%[2]T)", *got.Const, *s.Const)
}
}
}
}
TestJSONRoundTrip - 验证 JSON 文本的序列化一致性
确保 JSON 格式的 Schema 文本,经反序列化(到 Go Schema 结构体)再序列化(回 JSON)后,结果与原始文本等效
测试用例解析:
简单布尔值:true → 序列化后仍为 true(JSON Schema 中 true 表示任何数据都符合要求)
空字段省略:{“type”:“”, “enum”:null} → 序列化后为 true(空字段无意义,被省略)
数值格式优化:{“minimum”:1.0} → 序列化后为 {“minimum”:1}(整数的小数部分被省略)
字段排序:{“$vocabulary”:{“b”:true, “a”:false}} → 序列化后按键名排序为 {“a”:false,“b”:true}
未知字段保留:{“unk”:0} → 序列化后仍包含 unk 字段(JSON Schema 允许扩展字段)
意义:验证 JSON Schema 文本在转换过程中,核心语义不变(即使格式有微小调整,如排序、整数格式)
func TestJSONRoundTrip(t *testing.T) {
// Verify that JSON texts for schemas marshal into equivalent forms.
// 验证 schemas 的JSON文本是否被封送为等效的形式
// We don't expect everything to round-trip perfectly. For example, "true" and "false"
// will turn into their object equivalents.
// But most things should.
// Some of these cases test Schema.{UnM,M}arshalJSON.
// Most of others follow from the behavior of encoding/json, but they are still
// valuable as regression tests of this package's behavior.
for _, tt := range []struct {
in, want string
}{
{`true`, `true`},
{`false`, `false`},
{`{"type":"", "enum":null}`, `true`}, // empty fields are omitted
{`{"minimum":1}`, `{"minimum":1}`},
{`{"minimum":1.0}`, `{"minimum":1}`}, // floating-point integers lose their fractional part
{`{"minLength":1.0}`, `{"minLength":1}`}, // some floats are unmarshaled into ints, but you can't tell
{
// map keys are sorted
`{"$vocabulary":{"b":true, "a":false}}`,
`{"$vocabulary":{"a":false,"b":true}}`,
},
{`{"unk":0}`, `{"unk":0}`}, // unknown fields are not dropped
{
// known and unknown fields are not dropped
// note that the order will be by the declaration order in the anonymous struct inside MarshalJSON
`{"comment":"test","type":"example","unk":0}`,
`{"type":"example","comment":"test","unk":0}`,
},
{`{"extra":0}`, `{"extra":0}`}, // extra is not a special keyword and should not be dropped
{`{"Extra":0}`, `{"Extra":0}`}, // Extra is not a special keyword and should not be dropped
} {
var s Schema
mustUnmarshal(t, []byte(tt.in), &s)
data, err := json.Marshal(&s)
if err != nil {
t.Fatal(err)
}
if got := string(data); got != tt.want {
t.Errorf("%s:\ngot %s\nwant %s", tt.in, got, tt.want)
}
}
}
TestUnmarshalErrors 验证错误处理逻辑
确保对不符合 JSON Schema 规范的输入,能正确返回错误信息
测试用例解析:
非对象 / 布尔值的根节点:1 → 报错 “cannot unmarshal number”(JSON Schema 根节点必须是对象或布尔值)
类型错误:{“type”:1} → 报错 “invalid value for “type””(type 字段必须是字符串或字符串数组)
整数约束错误:{“minLength”:1.5} → 报错 “not an integer value”(minLength 必须是整数)
数值范围错误:{“maxContains”:9223372036854775808}(超过 int32 最大值)→ 报错 “out of range”
意义:确保 Schema 解析过程中能识别无效输入,避免错误的 Schema 被使用
func TestUnmarshalErrors(t *testing.T) {
for _, tt := range []struct {
in string
want string // error must match this regexp
}{
{`1`, "cannot unmarshal number"},
{`{"type":1}`, `invalid value for "type"`},
{`{"minLength":1.5}`, `not an integer value`},
{`{"maxLength":1.5}`, `not an integer value`},
{`{"minItems":1.5}`, `not an integer value`},
{`{"maxItems":1.5}`, `not an integer value`},
{`{"minProperties":1.5}`, `not an integer value`},
{`{"maxProperties":1.5}`, `not an integer value`},
{`{"minContains":1.5}`, `not an integer value`},
{`{"maxContains":1.5}`, `not an integer value`},
{fmt.Sprintf(`{"maxContains":%d}`, int64(math.MaxInt32+1)), `out of range`},
{`{"minLength":9e99}`, `cannot be unmarshaled`},
{`{"minLength":"1.5"}`, `not a number`},
} {
var s Schema
err := json.Unmarshal([]byte(tt.in), &s)
if err == nil {
t.Fatalf("%s: no error but expected one", tt.in)
}
if !regexp.MustCompile(tt.want).MatchString(err.Error()) {
t.Errorf("%s: error %q does not match %q", tt.in, err, tt.want)
}
}
}
func mustUnmarshal(t *testing.T, data []byte, ptr any) {
t.Helper()
if err := json.Unmarshal(data, ptr); err != nil {
t.Fatal(err)
}
}
// json returns the schema in json format.
func (s *Schema) json() string {
data, err := json.Marshal(s)
if err != nil {
return fmt.Sprintf("<jsonschema.Schema:%v>", err)
}
return string(data)
}
// json returns the schema in json format, indented.
func (s *Schema) jsonIndent() string {
data, err := json.MarshalIndent(s, "", " ")
if err != nil {
return fmt.Sprintf("<jsonschema.Schema:%v>", err)
}
return string(data)
}
TestCloneSchemas - 验证 Schema 克隆功能
测试 Schema.CloneSchemas 方法是否能深拷贝 Schema 结构体(包括嵌套的子 Schema),避免克隆后修改新对象影响原对象
测试逻辑:
定义一个包含嵌套结构的 Schema(Contains、PrefixItems、Properties 等字段引用其他 Schema 实例)
调用 CloneSchemas 生成克隆对象
验证:
克隆对象与原对象的 JSON 序列化结果一致(结构相同)
克隆对象的嵌套子 Schema 与原对象的子 Schema 不是同一个内存地址(深拷贝,而非浅拷贝)
修改克隆对象后,原对象不受影响
意义:
在需要修改 Schema 但又要保留原始版本的场景(如动态生成 Schema 时),确保克隆操作安全可靠
func TestCloneSchemas(t *testing.T) {
ss1 := &Schema{Type: "string"}
ss2 := &Schema{Type: "integer"}
ss3 := &Schema{Type: "boolean"}
ss4 := &Schema{Type: "number"}
ss5 := &Schema{Contains: ss4}
s1 := Schema{
Contains: ss1,
PrefixItems: []*Schema{ss2, ss3},
Properties: map[string]*Schema{"a": ss5},
}
s2 := s1.CloneSchemas()
// The clones should appear identical.
if g, w := s1.json(), s2.json(); g != w {
t.Errorf("\ngot %s\nwant %s", g, w)
}
// None of the schemas should overlap.
schemas1 := map[*Schema]bool{ss1: true, ss2: true, ss3: true, ss4: true, ss5: true}
for ss := range s2.all() {
if schemas1[ss] {
t.Errorf("uncloned schema %s", ss.json())
}
}
// s1's original schemas should be intact.
if s1.Contains != ss1 || s1.PrefixItems[0] != ss2 || s1.PrefixItems[1] != ss3 || ss5.Contains != ss4 || s1.Properties["a"] != ss5 {
t.Errorf("s1 modified")
}
}
s1.json打印出来后是
{
"prefixItems": [
{
"type": "integer"
},
{
"type": "boolean"
}
],
"contains": {
"type": "string"
},
"properties": {
"a": {
"contains": {
"type": "number"
}
}
}
}
附录
“seamless integration” 直译为 “无缝集成”,是技术、产品或系统协作中的核心概念,指不同组件、工具、平台之间通过设计或技术手段,实现 “无感知、无摩擦、高效协同” 的连接状态 —— 用户或开发者无需额外手动干预,即可让多个独立部分像 “原生一体” 一样工作,核心是消除 “集成断点”(如数据割裂、操作繁琐、兼容性问题)。
be subject to 可能遭受(不好的事情)
thriving ecosystem:thriving 意为 “蓬勃发展的、繁荣的”,此处用于描述 MCP 相关客户端与服务端工具在 Go 生态中的活跃状态,体现开源社区协作的积极成果。
ˈθraɪvɪŋ
ˈθraɪvɪŋ
欣欣向荣的,繁华的,兴旺的
notably ˈnəʊtəbli
ˈnoʊtəbli
adv. 明显地,显著地;尤其,特别
vocabulary vəˈkæbjələri
; vəʊˈkæbjələri
;vəˈkæbjəleri
; voʊˈkæbjəleri
(某人掌握或使用的)全部词汇;(某一语言的)词汇;(某个学科的)专业词汇,术语;(尤指外语教科书中的)词汇表;(美术、音乐等领域的)表现形式,表达手段
equality iˈkwɒləti
iˈkwɑːləti
均等,平等;等式
vacuously ˈvækjuəsli
ˈvækjuəsli
直愣愣地,没有表情地;空虚地,空地
mutually ˈmjuːtʃuəli
ˈmjuːtʃuəli
相互地,共同地
inherits ɪnˈherɪts
继承
Unevaluated 未鉴定的 ʌni'væljueitid
未评价的;未经估值的;未鉴定的