generate.go: remove
Remove the code generator. It has served its purpose. From now on, we will modify protocol.go directly.
This commit is contained in:
parent
ca88999e6b
commit
9305d6994f
3 changed files with 6 additions and 689 deletions
674
mcp/generate.go
674
mcp/generate.go
|
|
@ -1,674 +0,0 @@
|
||||||
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build ignore
|
|
||||||
|
|
||||||
// This script generates protocol definitions in protocol.go from the MCP spec.
|
|
||||||
//
|
|
||||||
// Only the set of declarations configured by the [declarations] value are
|
|
||||||
// generated.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"maps"
|
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/internal/util"
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/jsonschema"
|
|
||||||
)
|
|
||||||
|
|
||||||
var schemaFile = flag.String("schema_file", "", "if set, use this file as the persistent schema file")
|
|
||||||
|
|
||||||
// A typeConfig defines a rewrite to perform to a (possibly nested) struct
|
|
||||||
// field. In some cases, we may want to use an external type for the nested
|
|
||||||
// struct field. In others, we may want to extract the type definition to a
|
|
||||||
// name.
|
|
||||||
type typeConfig struct {
|
|
||||||
Name string // declaration name for the type
|
|
||||||
TypeParams [][2]string // formatted type parameter list ({name, constraint}), if any
|
|
||||||
Substitute string // type definition to substitute
|
|
||||||
Fields config // individual field configuration, or nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type config map[string]*typeConfig
|
|
||||||
|
|
||||||
// declarations configures the set of declarations to write.
|
|
||||||
//
|
|
||||||
// Top level declarations are created unless configured with Name=="-",
|
|
||||||
// in which case they are discarded, though their fields may be
|
|
||||||
// extracted to types if they have a nested field configuration.
|
|
||||||
// If Name == "", the map key is used as the type name.
|
|
||||||
var declarations = config{
|
|
||||||
"Annotations": {},
|
|
||||||
"CallToolRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{
|
|
||||||
"Params": {
|
|
||||||
Name: "CallToolParams",
|
|
||||||
Fields: config{
|
|
||||||
"Arguments": {Substitute: "any"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"CallToolResult": {},
|
|
||||||
"CancelledNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "CancelledParams"}},
|
|
||||||
},
|
|
||||||
"ClientCapabilities": {
|
|
||||||
Fields: config{"Sampling": {Name: "SamplingCapabilities"}},
|
|
||||||
},
|
|
||||||
"CreateMessageRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "CreateMessageParams"}},
|
|
||||||
},
|
|
||||||
"CreateMessageResult": {},
|
|
||||||
"GetPromptRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "GetPromptParams"}},
|
|
||||||
},
|
|
||||||
"GetPromptResult": {},
|
|
||||||
"Implementation": {Name: "implementation"},
|
|
||||||
"InitializeRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "InitializeParams"}},
|
|
||||||
},
|
|
||||||
"InitializeResult": {Name: "InitializeResult"},
|
|
||||||
"InitializedNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "InitializedParams"}},
|
|
||||||
},
|
|
||||||
"ListPromptsRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ListPromptsParams"}},
|
|
||||||
},
|
|
||||||
"ListPromptsResult": {},
|
|
||||||
"ListResourcesRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ListResourcesParams"}},
|
|
||||||
},
|
|
||||||
"ListResourcesResult": {},
|
|
||||||
"ListResourceTemplatesRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ListResourceTemplatesParams"}},
|
|
||||||
},
|
|
||||||
"ListResourceTemplatesResult": {},
|
|
||||||
"ListRootsRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ListRootsParams"}},
|
|
||||||
},
|
|
||||||
"ListRootsResult": {},
|
|
||||||
"ListToolsRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ListToolsParams"}},
|
|
||||||
},
|
|
||||||
"ListToolsResult": {},
|
|
||||||
"loggingCapabilities": {Substitute: "struct{}"},
|
|
||||||
"LoggingLevel": {},
|
|
||||||
"LoggingMessageNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{
|
|
||||||
"Params": {
|
|
||||||
Name: "LoggingMessageParams",
|
|
||||||
Fields: config{"Data": {Substitute: "any"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"ModelHint": {},
|
|
||||||
"ModelPreferences": {},
|
|
||||||
"PingRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "PingParams"}},
|
|
||||||
},
|
|
||||||
"Prompt": {},
|
|
||||||
"PromptMessage": {},
|
|
||||||
"PromptArgument": {},
|
|
||||||
"PromptListChangedNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "PromptListChangedParams"}},
|
|
||||||
},
|
|
||||||
"ProgressNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ProgressNotificationParams"}},
|
|
||||||
},
|
|
||||||
"ProgressToken": {Name: "-", Substitute: "any"}, // null|number|string
|
|
||||||
"RequestId": {Name: "-", Substitute: "any"}, // null|number|string
|
|
||||||
"ReadResourceRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ReadResourceParams"}},
|
|
||||||
},
|
|
||||||
"ReadResourceResult": {
|
|
||||||
Fields: config{"Contents": {Substitute: "[]*ResourceContents"}},
|
|
||||||
},
|
|
||||||
"Resource": {},
|
|
||||||
"ResourceListChangedNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ResourceListChangedParams"}},
|
|
||||||
},
|
|
||||||
"ResourceTemplate": {},
|
|
||||||
"ResourceTemplateListChangedNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ResourceTemplateListChangedParams"}},
|
|
||||||
},
|
|
||||||
"Role": {},
|
|
||||||
"Root": {},
|
|
||||||
"RootsListChangedNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "RootsListChangedParams"}},
|
|
||||||
},
|
|
||||||
|
|
||||||
"SamplingCapabilities": {Substitute: "struct{}"},
|
|
||||||
"SamplingMessage": {},
|
|
||||||
"ServerCapabilities": {
|
|
||||||
Name: "serverCapabilities",
|
|
||||||
Fields: config{
|
|
||||||
"Prompts": {Name: "promptCapabilities"},
|
|
||||||
"Resources": {Name: "resourceCapabilities"},
|
|
||||||
"Tools": {Name: "toolCapabilities"},
|
|
||||||
"Logging": {Name: "loggingCapabilities"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"SetLevelRequest": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "SetLevelParams"}},
|
|
||||||
},
|
|
||||||
"Tool": {
|
|
||||||
Fields: config{"InputSchema": {Substitute: "*jsonschema.Schema"}},
|
|
||||||
},
|
|
||||||
"ToolAnnotations": {},
|
|
||||||
"ToolListChangedNotification": {
|
|
||||||
Name: "-",
|
|
||||||
Fields: config{"Params": {Name: "ToolListChangedParams"}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
// Load and unmarshal the schema.
|
|
||||||
data, err := loadSchema(*schemaFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
schema := new(jsonschema.Schema)
|
|
||||||
if err := json.Unmarshal(data, &schema); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
// Resolve the schema so we have the referents of all the Refs.
|
|
||||||
if _, err := schema.Resolve(nil); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect named types. Since we may create new type definitions while
|
|
||||||
// writing types, we collect definitions and concatenate them later. This
|
|
||||||
// also allows us to sort.
|
|
||||||
named := make(map[string]*bytes.Buffer)
|
|
||||||
for name, def := range util.Sorted(schema.Definitions) {
|
|
||||||
config := declarations[name]
|
|
||||||
if config == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := writeDecl(name, *config, def, named); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
fmt.Fprintf(buf, `
|
|
||||||
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by an MIT-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Code generated by generate.go. DO NOT EDIT.
|
|
||||||
|
|
||||||
package mcp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/modelcontextprotocol/go-sdk/jsonschema"
|
|
||||||
)
|
|
||||||
`)
|
|
||||||
|
|
||||||
// Write out types.
|
|
||||||
for _, b := range util.Sorted(named) {
|
|
||||||
fmt.Fprintln(buf)
|
|
||||||
fmt.Fprint(buf, b.String())
|
|
||||||
}
|
|
||||||
// Write out method names.
|
|
||||||
fmt.Fprintln(buf, `const (`)
|
|
||||||
for _, name := range slices.Sorted(maps.Keys(schema.Definitions)) {
|
|
||||||
prefix := "method"
|
|
||||||
method, found := strings.CutSuffix(name, "Request")
|
|
||||||
if !found {
|
|
||||||
prefix = "notification"
|
|
||||||
method, found = strings.CutSuffix(name, "Notification")
|
|
||||||
}
|
|
||||||
if found {
|
|
||||||
if ms, ok := schema.Definitions[name].Properties["method"]; ok {
|
|
||||||
if c := ms.Const; c != nil {
|
|
||||||
fmt.Fprintf(buf, "%s%s = %q\n", prefix, method, *c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintln(buf, `)`)
|
|
||||||
|
|
||||||
formatted, err := format.Source(buf.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
log.Println(buf.String())
|
|
||||||
log.Fatalf("failed to format: %v", err)
|
|
||||||
}
|
|
||||||
if err := os.WriteFile("protocol.go", formatted, 0666); err != nil {
|
|
||||||
log.Fatalf("failed to write protocol.go: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadSchema(schemaFile string) (data []byte, err error) {
|
|
||||||
const schemaURL = "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/refs/heads/main/schema/2025-03-26/schema.json"
|
|
||||||
|
|
||||||
if schemaFile != "" {
|
|
||||||
data, err = os.ReadFile(schemaFile)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
data = nil
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading schema file %q: %v", schemaFile, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if data == nil {
|
|
||||||
resp, err := http.Get(schemaURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("downloading schema: %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("downloading schema: %v", resp.Status)
|
|
||||||
}
|
|
||||||
data, err = io.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading schema body: %v", err)
|
|
||||||
}
|
|
||||||
if schemaFile != "" {
|
|
||||||
if err := os.WriteFile(schemaFile, data, 0666); err != nil {
|
|
||||||
return nil, fmt.Errorf("persisting schema: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Suffixes of type names with a meta field.
|
|
||||||
var metaSuffixes = []string{"Params", "ParamsFor", "Result", "ResultFor"}
|
|
||||||
|
|
||||||
func writeDecl(configName string, config typeConfig, def *jsonschema.Schema, named map[string]*bytes.Buffer) error {
|
|
||||||
var w io.Writer = io.Discard
|
|
||||||
var typeName string
|
|
||||||
if typeName = config.Name; typeName != "-" {
|
|
||||||
if typeName == "" {
|
|
||||||
typeName = configName
|
|
||||||
}
|
|
||||||
if _, ok := named[typeName]; ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// The JSON schema does not accurately represent the source of truth, which is typescript.
|
|
||||||
// Every Params and Result type should have a _meta property.
|
|
||||||
// Also, those with a progress token will turn into a struct; we want the progress token to
|
|
||||||
// be a map item. So replace all metas.
|
|
||||||
for _, s := range metaSuffixes {
|
|
||||||
if strings.HasSuffix(typeName, s) {
|
|
||||||
def.Properties["_meta"] = metaSchema
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
w = buf
|
|
||||||
named[typeName] = buf
|
|
||||||
if def.Description != "" {
|
|
||||||
fmt.Fprintf(buf, "%s\n", toComment(def.Description))
|
|
||||||
}
|
|
||||||
typeParams := new(strings.Builder)
|
|
||||||
if len(config.TypeParams) > 0 {
|
|
||||||
typeParams.WriteByte('[')
|
|
||||||
for i, p := range config.TypeParams {
|
|
||||||
if i > 0 {
|
|
||||||
typeParams.WriteString(", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(typeParams, "%s %s", p[0], p[1])
|
|
||||||
}
|
|
||||||
typeParams.WriteByte(']')
|
|
||||||
}
|
|
||||||
fmt.Fprintf(buf, "type %s%s ", typeName, typeParams)
|
|
||||||
}
|
|
||||||
if err := writeType(w, &config, def, named); err != nil {
|
|
||||||
return err // Better error here?
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "\n")
|
|
||||||
|
|
||||||
// Any decl with a _meta field gets a GetMeta method.
|
|
||||||
if _, ok := def.Properties["_meta"]; ok {
|
|
||||||
targs := new(strings.Builder)
|
|
||||||
if len(config.TypeParams) > 0 {
|
|
||||||
targs.WriteByte('[')
|
|
||||||
for i, p := range config.TypeParams {
|
|
||||||
if i > 0 {
|
|
||||||
targs.WriteString(", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(targs, "%s", p[0])
|
|
||||||
}
|
|
||||||
targs.WriteByte(']')
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "\nfunc (x *%s%s) GetMeta() *Meta { return &x.Meta }", typeName, targs)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := def.Properties["cursor"]; ok {
|
|
||||||
fmt.Fprintf(w, "\nfunc (x *%s) cursorPtr() *string { return &x.Cursor }", typeName)
|
|
||||||
}
|
|
||||||
if _, ok := def.Properties["nextCursor"]; ok {
|
|
||||||
fmt.Fprintf(w, "\nfunc (x *%s) nextCursorPtr() *string { return &x.NextCursor }", typeName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some types have generic counterparts.
|
|
||||||
var typeArg, substField string
|
|
||||||
switch typeName {
|
|
||||||
case "CallToolParams":
|
|
||||||
typeName = "CallToolParamsFor"
|
|
||||||
typeArg = "In"
|
|
||||||
substField = "Arguments"
|
|
||||||
case "CallToolResult":
|
|
||||||
typeName = "CallToolResultFor"
|
|
||||||
typeArg = "Out"
|
|
||||||
substField = "StructuredContent"
|
|
||||||
}
|
|
||||||
if typeArg != "" {
|
|
||||||
fmt.Fprintf(w, "\n\ntype %s[%s any]", typeName, typeArg)
|
|
||||||
if config.Fields != nil {
|
|
||||||
config.Fields[substField].Substitute = typeArg
|
|
||||||
}
|
|
||||||
if err := writeType(w, &config, def, named); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "\nfunc (x *%s[%s]) GetMeta() *Meta {return &x.Meta}", typeName, typeArg)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeType writes the type definition to the given writer.
|
|
||||||
//
|
|
||||||
// If path is non-empty, it is the path to the field using this type, for the
|
|
||||||
// purpose of detecting field rewrites (see [fieldRewrite]).
|
|
||||||
//
|
|
||||||
// named is the in-progress collection of type definitions. New named types may
|
|
||||||
// be added during writeType, if they are extracted from inner fields.
|
|
||||||
func writeType(w io.Writer, config *typeConfig, def *jsonschema.Schema, named map[string]*bytes.Buffer) error {
|
|
||||||
// Use type names for Named types.
|
|
||||||
name, resolved := deref(def)
|
|
||||||
if name != "" {
|
|
||||||
// TODO: this check is not quite right: we should really panic if the
|
|
||||||
// definition is missing, *but only if w is not io.Discard*. That's not a
|
|
||||||
// great API: see if we can do something more explicit than io.Discard.
|
|
||||||
if cfg, ok := declarations[name]; ok {
|
|
||||||
if cfg.Name == "-" && cfg.Substitute == "" {
|
|
||||||
panic(fmt.Sprintf("referenced type %q cannot be referred to (no name or substitution)", name))
|
|
||||||
}
|
|
||||||
if cfg.Substitute != "" {
|
|
||||||
name = cfg.Substitute
|
|
||||||
} else if cfg.Name != "" {
|
|
||||||
name = cfg.Name
|
|
||||||
}
|
|
||||||
if isStruct(resolved) {
|
|
||||||
w.Write([]byte{'*'})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.Write([]byte(name))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// For types that explicitly allow additional properties, we can either
|
|
||||||
// unmarshal them into a map[string]any, or delay unmarshalling with
|
|
||||||
// json.RawMessage. We use any.
|
|
||||||
if def.Type == "object" && canHaveAdditionalProperties(def) && def.Properties == nil {
|
|
||||||
w.Write([]byte("map[string]"))
|
|
||||||
return writeType(w, nil, def.AdditionalProperties, named)
|
|
||||||
}
|
|
||||||
|
|
||||||
if def.Type == "" {
|
|
||||||
// special case: recognize Content
|
|
||||||
if slices.ContainsFunc(def.AnyOf, func(s *jsonschema.Schema) bool {
|
|
||||||
return s.Ref == "#/definitions/TextContent"
|
|
||||||
}) {
|
|
||||||
fmt.Fprintf(w, "*Content")
|
|
||||||
} else {
|
|
||||||
// E.g. union types.
|
|
||||||
fmt.Fprintf(w, "any")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch def.Type {
|
|
||||||
case "array":
|
|
||||||
fmt.Fprintf(w, "[]")
|
|
||||||
return writeType(w, nil, def.Items, named)
|
|
||||||
|
|
||||||
case "boolean":
|
|
||||||
fmt.Fprintf(w, "bool")
|
|
||||||
|
|
||||||
case "integer":
|
|
||||||
fmt.Fprintf(w, "int64")
|
|
||||||
|
|
||||||
// not handled: "null"
|
|
||||||
|
|
||||||
case "number":
|
|
||||||
// We could use json.Number here; use float64 for simplicity.
|
|
||||||
fmt.Fprintf(w, "float64")
|
|
||||||
|
|
||||||
case "object":
|
|
||||||
fmt.Fprintf(w, "struct {\n")
|
|
||||||
for name, fieldDef := range util.Sorted(def.Properties) {
|
|
||||||
if fieldDef.Description != "" {
|
|
||||||
fmt.Fprintf(w, "%s\n", toComment(fieldDef.Description))
|
|
||||||
}
|
|
||||||
if name == "_meta" {
|
|
||||||
fmt.Fprintln(w, "\tMeta Meta `json:\"_meta,omitempty\"`")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
export := exportName(name)
|
|
||||||
fmt.Fprintf(w, "\t%s ", export)
|
|
||||||
|
|
||||||
required := slices.Contains(def.Required, name)
|
|
||||||
|
|
||||||
// If the field is a struct type, indirect with a
|
|
||||||
// pointer so that it can be empty as defined by encoding/json.
|
|
||||||
// This also future-proofs against the struct getting large.
|
|
||||||
fieldTypeSchema := fieldDef
|
|
||||||
// If the schema is a reference, dereference it.
|
|
||||||
if _, rs := deref(fieldDef); rs != nil {
|
|
||||||
fieldTypeSchema = rs
|
|
||||||
}
|
|
||||||
needPointer := isStruct(fieldTypeSchema)
|
|
||||||
// Special case: there are no sampling or logging capabilities defined,
|
|
||||||
// but we want them to be structs for future expansion.
|
|
||||||
if !needPointer && (name == "sampling" || name == "logging") {
|
|
||||||
needPointer = true
|
|
||||||
}
|
|
||||||
if config != nil && config.Fields[export] != nil {
|
|
||||||
r := config.Fields[export]
|
|
||||||
if r.Substitute != "" {
|
|
||||||
fmt.Fprintf(w, r.Substitute)
|
|
||||||
} else {
|
|
||||||
assert(r.Name != "-", "missing ExtractTo")
|
|
||||||
typename := export
|
|
||||||
if r.Name != "" {
|
|
||||||
typename = r.Name
|
|
||||||
}
|
|
||||||
if err := writeDecl(typename, *r, fieldDef, named); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if needPointer {
|
|
||||||
fmt.Fprintf(w, "*")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, typename)
|
|
||||||
}
|
|
||||||
} else if err := writeType(w, nil, fieldDef, named); err != nil {
|
|
||||||
return fmt.Errorf("failed to write type for field %s: %v", export, err)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, " `json:\"%s", name)
|
|
||||||
if !required {
|
|
||||||
fmt.Fprint(w, ",omitempty")
|
|
||||||
}
|
|
||||||
fmt.Fprint(w, "\"`\n")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(w, "}")
|
|
||||||
|
|
||||||
case "string":
|
|
||||||
fmt.Fprintf(w, "string")
|
|
||||||
|
|
||||||
default:
|
|
||||||
fmt.Fprintf(w, "any")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// toComment converts a JSON schema description to a Go comment.
|
|
||||||
func toComment(description string) string {
|
|
||||||
var (
|
|
||||||
buf strings.Builder
|
|
||||||
lineBuf strings.Builder
|
|
||||||
)
|
|
||||||
const wrapAt = 80
|
|
||||||
for line := range strings.SplitSeq(description, "\n") {
|
|
||||||
// Start a new paragraph, if the current is nonempty.
|
|
||||||
if len(line) == 0 && lineBuf.Len() > 0 {
|
|
||||||
buf.WriteString(lineBuf.String())
|
|
||||||
lineBuf.Reset()
|
|
||||||
buf.WriteString("\n//\n")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Otherwise, fill in the current paragraph.
|
|
||||||
for field := range strings.FieldsSeq(line) {
|
|
||||||
if lineBuf.Len() > 0 && lineBuf.Len()+len(" ")+len(field) > wrapAt {
|
|
||||||
buf.WriteString(lineBuf.String())
|
|
||||||
buf.WriteRune('\n')
|
|
||||||
lineBuf.Reset()
|
|
||||||
}
|
|
||||||
if lineBuf.Len() == 0 {
|
|
||||||
lineBuf.WriteString("//")
|
|
||||||
}
|
|
||||||
lineBuf.WriteString(" ")
|
|
||||||
lineBuf.WriteString(field)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if lineBuf.Len() > 0 {
|
|
||||||
buf.WriteString(lineBuf.String())
|
|
||||||
}
|
|
||||||
return strings.TrimRight(buf.String(), "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The MCP spec improperly uses the absence of the additionalProperties keyword to
|
|
||||||
// mean that additional properties are not allowed. In fact, it means just the opposite
|
|
||||||
// (https://json-schema.org/draft-07/draft-handrews-json-schema-validation-01#rfc.section.6.5.6).
|
|
||||||
// If the MCP spec wants to allow additional properties, it will write "true" or
|
|
||||||
// an object explicitly.
|
|
||||||
func canHaveAdditionalProperties(s *jsonschema.Schema) bool {
|
|
||||||
ap := s.AdditionalProperties
|
|
||||||
return ap != nil && !reflect.DeepEqual(ap, &jsonschema.Schema{Not: &jsonschema.Schema{}})
|
|
||||||
}
|
|
||||||
|
|
||||||
// exportName returns an exported name for a Go symbol, based on the given name
|
|
||||||
// in the JSON schema, removing leading underscores and capitalizing.
|
|
||||||
// It also rewrites initialisms.
|
|
||||||
func exportName(s string) string {
|
|
||||||
if strings.HasPrefix(s, "_") {
|
|
||||||
s = s[1:]
|
|
||||||
}
|
|
||||||
s = strings.ToUpper(s[:1]) + s[1:]
|
|
||||||
// Replace an initialism if it is its own "word": see the init function below for
|
|
||||||
// a definition.
|
|
||||||
// There is probably a clever way to write this whole thing with one regexp and
|
|
||||||
// a Replace method, but it would be quite obscure.
|
|
||||||
// This doesn't have to be fast, because the first match will rarely succeed.
|
|
||||||
for ism, re := range initialisms {
|
|
||||||
replacement := strings.ToUpper(ism)
|
|
||||||
// Find the index of one match at a time, and replace. (We can't find all
|
|
||||||
// at once, because the replacement will change the indices.)
|
|
||||||
for {
|
|
||||||
if loc := re.FindStringIndex(s); loc != nil {
|
|
||||||
// Don't replace the rune after the initialism, if any.
|
|
||||||
end := loc[1]
|
|
||||||
if end < len(s) {
|
|
||||||
end--
|
|
||||||
}
|
|
||||||
s = s[:loc[0]] + replacement + s[end:]
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// deref dereferences s.Ref.
|
|
||||||
// If s.Ref refers to a schema in the Definitions section, deref
|
|
||||||
// returns the definition name and the associated schema.
|
|
||||||
// Otherwise, deref returns "", nil.
|
|
||||||
func deref(s *jsonschema.Schema) (name string, _ *jsonschema.Schema) {
|
|
||||||
name, ok := strings.CutPrefix(s.Ref, "#/definitions/")
|
|
||||||
if !ok {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
return name, s.ResolvedRef()
|
|
||||||
}
|
|
||||||
|
|
||||||
// isStruct reports whether s should be translated to a struct.
|
|
||||||
func isStruct(s *jsonschema.Schema) bool {
|
|
||||||
return s.Type == "object" && s.Properties != nil && !canHaveAdditionalProperties(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The schema for "_meta".
|
|
||||||
// We only need the description: the rest is a special case.
|
|
||||||
var metaSchema = &jsonschema.Schema{
|
|
||||||
Description: "This property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses.",
|
|
||||||
}
|
|
||||||
|
|
||||||
// schemaJSON returns the JSON for s.
|
|
||||||
// For debugging.
|
|
||||||
func schemaJSON(s *jsonschema.Schema) string {
|
|
||||||
data, err := json.Marshal(s)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf("<jsonschema.Schema:%v>", err)
|
|
||||||
}
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map from initialism to the regexp that matches it.
|
|
||||||
var initialisms = map[string]*regexp.Regexp{
|
|
||||||
"Id": nil,
|
|
||||||
"Url": nil,
|
|
||||||
"Uri": nil,
|
|
||||||
"Mime": nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for ism := range initialisms {
|
|
||||||
// Match ism if it is at the end, or followed by an uppercase letter or a number.
|
|
||||||
initialisms[ism] = regexp.MustCompile(ism + `($|[A-Z0-9])`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func assert(cond bool, msg string) {
|
|
||||||
if !cond {
|
|
||||||
panic(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Use of this source code is governed by an MIT-style
|
// Use of this source code is governed by an MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
//go:generate go run generate.go
|
|
||||||
//go:generate ../internal/readme/build.sh
|
//go:generate ../internal/readme/build.sh
|
||||||
|
|
||||||
// The mcp package provides an SDK for writing model context protocol clients
|
// The mcp package provides an SDK for writing model context protocol clients
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
// Use of this source code is governed by an MIT-style
|
// Use of this source code is governed by an MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// Code generated by generate.go. DO NOT EDIT.
|
|
||||||
|
|
||||||
package mcp
|
package mcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -103,8 +101,7 @@ func (x *CancelledParams) GetMeta() *Meta { return &x.Meta }
|
||||||
// additional capabilities.
|
// additional capabilities.
|
||||||
type ClientCapabilities struct {
|
type ClientCapabilities struct {
|
||||||
// Experimental, non-standard capabilities that the client supports.
|
// Experimental, non-standard capabilities that the client supports.
|
||||||
Experimental map[string]struct {
|
Experimental map[string]struct{} `json:"experimental,omitempty"`
|
||||||
} `json:"experimental,omitempty"`
|
|
||||||
// Present if the client supports listing roots.
|
// Present if the client supports listing roots.
|
||||||
Roots struct {
|
Roots struct {
|
||||||
// Whether the client supports notifications for changes to the roots list.
|
// Whether the client supports notifications for changes to the roots list.
|
||||||
|
|
@ -127,8 +124,7 @@ type CreateMessageParams struct {
|
||||||
Messages []*SamplingMessage `json:"messages"`
|
Messages []*SamplingMessage `json:"messages"`
|
||||||
// Optional metadata to pass through to the LLM provider. The format of this
|
// Optional metadata to pass through to the LLM provider. The format of this
|
||||||
// metadata is provider-specific.
|
// metadata is provider-specific.
|
||||||
Metadata struct {
|
Metadata struct{} `json:"metadata,omitempty"`
|
||||||
} `json:"metadata,omitempty"`
|
|
||||||
// The server's preferences for which model to select. The client MAY ignore
|
// The server's preferences for which model to select. The client MAY ignore
|
||||||
// these preferences.
|
// these preferences.
|
||||||
ModelPreferences *ModelPreferences `json:"modelPreferences,omitempty"`
|
ModelPreferences *ModelPreferences `json:"modelPreferences,omitempty"`
|
||||||
|
|
@ -585,8 +581,7 @@ type RootsListChangedParams struct {
|
||||||
func (x *RootsListChangedParams) GetMeta() *Meta { return &x.Meta }
|
func (x *RootsListChangedParams) GetMeta() *Meta { return &x.Meta }
|
||||||
|
|
||||||
// Present if the client supports sampling from an LLM.
|
// Present if the client supports sampling from an LLM.
|
||||||
type SamplingCapabilities struct {
|
type SamplingCapabilities struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// Describes a message issued to or received from an LLM API.
|
// Describes a message issued to or received from an LLM API.
|
||||||
type SamplingMessage struct {
|
type SamplingMessage struct {
|
||||||
|
|
@ -673,8 +668,7 @@ type implementation struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Present if the server supports sending log messages to the client.
|
// Present if the server supports sending log messages to the client.
|
||||||
type loggingCapabilities struct {
|
type loggingCapabilities struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// Present if the server offers any prompt templates.
|
// Present if the server offers any prompt templates.
|
||||||
type promptCapabilities struct {
|
type promptCapabilities struct {
|
||||||
|
|
@ -695,11 +689,9 @@ type resourceCapabilities struct {
|
||||||
// additional capabilities.
|
// additional capabilities.
|
||||||
type serverCapabilities struct {
|
type serverCapabilities struct {
|
||||||
// Present if the server supports argument autocompletion suggestions.
|
// Present if the server supports argument autocompletion suggestions.
|
||||||
Completions struct {
|
Completions struct{} `json:"completions,omitempty"`
|
||||||
} `json:"completions,omitempty"`
|
|
||||||
// Experimental, non-standard capabilities that the server supports.
|
// Experimental, non-standard capabilities that the server supports.
|
||||||
Experimental map[string]struct {
|
Experimental map[string]struct{} `json:"experimental,omitempty"`
|
||||||
} `json:"experimental,omitempty"`
|
|
||||||
// Present if the server supports sending log messages to the client.
|
// Present if the server supports sending log messages to the client.
|
||||||
Logging *loggingCapabilities `json:"logging,omitempty"`
|
Logging *loggingCapabilities `json:"logging,omitempty"`
|
||||||
// Present if the server offers any prompt templates.
|
// Present if the server offers any prompt templates.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue