// Package mcptypes defines the typed MCP 2026-07-28 wire model. // // Core protocol values use Go structs. json.RawMessage is used only where the // specification deliberately permits arbitrary JSON (extensions, JSON Schema, // and structured tool content). package mcptypes import ( "bytes" "encoding/json" "errors" "fmt" ) const ( JSONRPCVersion = "2.0" ProtocolVersion = "2026-07-28" ) type RequestID struct{ value json.RawMessage } func NewStringID(value string) RequestID { return RequestID{value: json.RawMessage(fmt.Sprintf("%q", value))} } func NewNumberID(value json.Number) RequestID { return RequestID{value: json.RawMessage(value.String())} } func (id RequestID) IsNull() bool { return len(id.value) == 0 || bytes.Equal(id.value, []byte("null")) } func (id RequestID) MarshalJSON() ([]byte, error) { if id.IsNull() { return []byte("null"), nil } return id.value, nil } func (id *RequestID) UnmarshalJSON(data []byte) error { decoder := json.NewDecoder(bytes.NewReader(data)) var value json.RawMessage if err := decoder.Decode(&value); err != nil { return fmt.Errorf("invalid JSON-RPC id: %w", err) } if len(value) == 0 { return fmt.Errorf("invalid JSON-RPC id") } switch value[0] { case '"', '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': id.value = append(id.value[:0], value...) return nil case 'n': if bytes.Equal(value, []byte("null")) { id.value = []byte("null") return nil } } return fmt.Errorf("JSON-RPC id must be a string, number, or null") } type Request struct { JSONRPC string `json:"jsonrpc"` ID *RequestID `json:"id,omitempty"` Method string `json:"method"` Params json.RawMessage `json:"params,omitempty"` Meta RequestMeta `json:"_meta"` IDPresent bool `json:"-"` } func (r *Request) UnmarshalJSON(data []byte) error { type alias Request var decoded alias if err := json.Unmarshal(data, &decoded); err != nil { return err } var fields map[string]json.RawMessage if err := json.Unmarshal(data, &fields); err != nil { return err } *r = Request(decoded) _, r.IDPresent = fields["id"] return nil } func (r Request) IsNotification() bool { return !r.IDPresent } type RequestMeta struct { ProtocolVersion string `json:"io.modelcontextprotocol/protocolVersion,omitempty"` ClientCapabilities ClientCapabilities `json:"io.modelcontextprotocol/clientCapabilities"` ClientInfo *ClientInfo `json:"io.modelcontextprotocol/clientInfo,omitempty"` LogLevel *LogLevel `json:"io.modelcontextprotocol/logLevel,omitempty"` Extensions map[string]json.RawMessage `json:"-"` } type ResultMeta struct { ServerInfo *ServerInfo `json:"io.modelcontextprotocol/serverInfo,omitempty"` SubscriptionID string `json:"io.modelcontextprotocol/subscriptionId,omitempty"` Extensions map[string]json.RawMessage `json:"-"` } type Response struct { JSONRPC string `json:"jsonrpc"` ID *RequestID `json:"id"` Result json.RawMessage `json:"result"` } type ErrorResponse struct { JSONRPC string `json:"jsonrpc"` ID *RequestID `json:"id"` Error ErrorObject `json:"error"` } type ErrorObject struct { Code int `json:"code"` Message string `json:"message"` Data json.RawMessage `json:"data,omitempty"` } var ErrInvalidArguments = errors.New("invalid tool arguments") const ( ErrParse = -32700 ErrInvalidRequest = -32600 ErrMethodNotFound = -32601 ErrInvalidParams = -32602 ErrInternal = -32603 ErrHeaderMismatch = -32020 ErrMissingRequiredClientCapability = -32021 ErrUnsupportedProtocolVersion = -32022 ) type ClientInfo struct { Name string `json:"name"` Version string `json:"version"` } type ServerInfo struct { Name string `json:"name"` Title string `json:"title,omitempty"` Version string `json:"version"` } type ClientCapabilities struct { Extensions map[string]json.RawMessage `json:"extensions,omitempty"` } type ServerCapabilities struct { Extensions map[string]json.RawMessage `json:"extensions,omitempty"` Prompts *PromptsCapability `json:"prompts,omitempty"` Resources *ResourcesCapability `json:"resources,omitempty"` Tools *ToolsCapability `json:"tools,omitempty"` } type PromptsCapability struct { ListChanged bool `json:"listChanged,omitempty"` } type ResourcesCapability struct { ListChanged bool `json:"listChanged,omitempty"` } type ToolsCapability struct { ListChanged bool `json:"listChanged,omitempty"` } type LogLevel string // Method parameter and result types. type DiscoverParams struct{} type DiscoverResult struct { ResultType string `json:"resultType"` ProtocolVersions []string `json:"protocolVersions"` Capabilities ServerCapabilities `json:"capabilities"` ServerInfo ServerInfo `json:"serverInfo"` Instructions string `json:"instructions,omitempty"` Meta ResultMeta `json:"_meta"` } type PaginatedParams struct { Cursor string `json:"cursor,omitempty"` } type ToolsListResult struct { ResultType string `json:"resultType"` Tools []Tool `json:"tools"` NextCursor string `json:"nextCursor,omitempty"` TTLMS int64 `json:"ttlMs"` CacheScope CacheScope `json:"cacheScope"` Meta ResultMeta `json:"_meta"` } type ResourcesListResult struct { ResultType string `json:"resultType"` Resources []Resource `json:"resources"` NextCursor string `json:"nextCursor,omitempty"` TTLMS int64 `json:"ttlMs"` CacheScope CacheScope `json:"cacheScope"` Meta ResultMeta `json:"_meta"` } type ReadResourceParams struct { URI string `json:"uri"` } type ReadResourceResult struct { ResultType string `json:"resultType"` Contents []ResourceContents `json:"contents"` TTLMS int64 `json:"ttlMs"` CacheScope CacheScope `json:"cacheScope"` Meta ResultMeta `json:"_meta"` } type CallToolParams struct { Name string `json:"name"` Arguments json.RawMessage `json:"arguments,omitempty"` } type CallToolResult struct { ResultType string `json:"resultType"` Content []Content `json:"content"` StructuredContent json.RawMessage `json:"structuredContent,omitempty"` IsError bool `json:"isError,omitempty"` Meta ResultMeta `json:"_meta"` } type CacheScope string const ( CachePublic CacheScope = "public" CachePrivate CacheScope = "private" ) type Tool struct { Meta json.RawMessage `json:"_meta,omitempty"` Name string `json:"name"` Title string `json:"title,omitempty"` Description string `json:"description,omitempty"` InputSchema json.RawMessage `json:"inputSchema"` OutputSchema json.RawMessage `json:"outputSchema,omitempty"` Annotations *ToolAnnotations `json:"annotations,omitempty"` } type ToolAnnotations struct { Title string `json:"title,omitempty"` ReadOnlyHint *bool `json:"readOnlyHint,omitempty"` DestructiveHint *bool `json:"destructiveHint,omitempty"` IdempotentHint *bool `json:"idempotentHint,omitempty"` OpenWorldHint *bool `json:"openWorldHint,omitempty"` } type Resource struct { Meta json.RawMessage `json:"_meta,omitempty"` URI string `json:"uri"` Name string `json:"name"` Title string `json:"title,omitempty"` Description string `json:"description,omitempty"` MIMEType string `json:"mimeType,omitempty"` Size *int64 `json:"size,omitempty"` } type ResourceContents struct { Meta json.RawMessage `json:"_meta,omitempty"` URI string `json:"uri"` MIMEType string `json:"mimeType,omitempty"` Text string `json:"text,omitempty"` Blob string `json:"blob,omitempty"` } type Content struct { Type string `json:"type"` Text string `json:"text,omitempty"` Data string `json:"data,omitempty"` MIMEType string `json:"mimeType,omitempty"` } func Decode[T any](data json.RawMessage) (T, error) { var value T if len(bytes.TrimSpace(data)) == 0 { data = []byte("{}") } err := json.Unmarshal(data, &value) return value, err } // Validatable is implemented by typed tool argument structs that enforce // required fields and application-level constraints. type Validatable interface { Validate() error } // DecodeValidated decodes typed tool arguments and runs their validation. func DecodeValidated[T Validatable](data json.RawMessage) (T, error) { value, err := Decode[T](data) if err != nil { return value, fmt.Errorf("%w: %v", ErrInvalidArguments, err) } if err := value.Validate(); err != nil { return value, fmt.Errorf("%w: %v", ErrInvalidArguments, err) } return value, nil } // NewToolResultText returns a successful text-only tool result. func NewToolResultText(text string) CallToolResult { return CallToolResult{ResultType: "complete", Content: []Content{{Type: "text", Text: text}}} } // NewToolResultError returns an in-band tool execution error. func NewToolResultError(message string) CallToolResult { result := NewToolResultText(message) result.IsError = true return result } // NewToolResultStructured encodes a typed Go value as structuredContent. func NewToolResultStructured[T any](value T, text string) (CallToolResult, error) { raw, err := json.Marshal(value) if err != nil { return CallToolResult{}, fmt.Errorf("encode structured tool result: %w", err) } return CallToolResult{ResultType: "complete", Content: []Content{{Type: "text", Text: text}}, StructuredContent: raw}, nil } // NewToolResultStructuredOnly encodes a typed Go value and uses its JSON form // as the text fallback for clients that do not support structuredContent. func NewToolResultStructuredOnly[T any](value T) (CallToolResult, error) { raw, err := json.Marshal(value) if err != nil { return CallToolResult{}, fmt.Errorf("encode structured tool result: %w", err) } return CallToolResult{ResultType: "complete", Content: []Content{{Type: "text", Text: string(raw)}}, StructuredContent: raw}, nil }