// Package mcp implements the Model Context Protocol (MCP) over JSON-RPC 2.0, // providing both a client (Nexus -> upstream MCP servers) and the primitives // used by the agent-facing MCP server (agents -> Nexus Gateway). package mcp import ( "encoding/json" "fmt" ) // JSONRPCVersion is the only JSON-RPC version MCP uses. const JSONRPCVersion = "2.0" // Standard JSON-RPC 2.0 error codes. const ( CodeParseError = -32700 CodeInvalidRequest = -32600 CodeMethodNotFound = -32601 CodeInvalidParams = -32602 CodeInternalError = -32603 ) // Message is a single JSON-RPC 2.0 frame. It is deliberately permissive so it // can represent requests, responses, and notifications on the wire; use the // helpers to interpret it. type Message struct { JSONRPC string `json:"jsonrpc"` ID json.RawMessage `json:"id,omitempty"` Method string `json:"method,omitempty"` Params json.RawMessage `json:"params,omitempty"` Result json.RawMessage `json:"result,omitempty"` Error *RPCError `json:"error,omitempty"` } // IsRequest reports whether the message is a request (has method and id). func (m *Message) IsRequest() bool { return m.Method != "" && len(m.ID) > 0 } // IsNotification reports whether the message is a notification (method, no id). func (m *Message) IsNotification() bool { return m.Method != "" && len(m.ID) == 0 } // IsResponse reports whether the message is a response (result or error, id). func (m *Message) IsResponse() bool { return m.Method == "" && len(m.ID) > 0 } // RPCError is a JSON-RPC 2.0 error object. type RPCError struct { Code int `json:"code"` Message string `json:"message"` Data json.RawMessage `json:"data,omitempty"` } func (e *RPCError) Error() string { return fmt.Sprintf("jsonrpc error %d: %s", e.Code, e.Message) } // NewRequest builds a request message with the given id, method, and params. func NewRequest(id json.RawMessage, method string, params any) (*Message, error) { m := &Message{JSONRPC: JSONRPCVersion, ID: id, Method: method} if params != nil { b, err := json.Marshal(params) if err != nil { return nil, err } m.Params = b } return m, nil } // NewNotification builds a notification message (no id). func NewNotification(method string, params any) (*Message, error) { m := &Message{JSONRPC: JSONRPCVersion, Method: method} if params != nil { b, err := json.Marshal(params) if err != nil { return nil, err } m.Params = b } return m, nil } // NewResult builds a successful response for the given request id. func NewResult(id json.RawMessage, result any) (*Message, error) { b, err := json.Marshal(result) if err != nil { return nil, err } return &Message{JSONRPC: JSONRPCVersion, ID: id, Result: b}, nil } // NewError builds an error response for the given request id. func NewError(id json.RawMessage, code int, msg string) *Message { return &Message{JSONRPC: JSONRPCVersion, ID: id, Error: &RPCError{Code: code, Message: msg}} } // UnmarshalParams decodes the params of the message into v. func (m *Message) UnmarshalParams(v any) error { if len(m.Params) == 0 { return nil } return json.Unmarshal(m.Params, v) } // UnmarshalResult decodes the result of the message into v. func (m *Message) UnmarshalResult(v any) error { if len(m.Result) == 0 { return nil } return json.Unmarshal(m.Result, v) }