The transport layer provides the abstract interface and message types for MCP communication. Transports handle the underlying message delivery mechanism between clients and servers.
import "github.com/metoro-io/mcp-golang/transport"type Transport interface {
Start(ctx context.Context) error
Send(ctx context.Context, message *BaseJsonRpcMessage) error
Close() error
SetCloseHandler(handler func())
SetErrorHandler(handler func(error))
SetMessageHandler(handler func(ctx context.Context, message *BaseJsonRpcMessage))
}The Transport interface defines the contract for all MCP communication transports.
func Start(ctx context.Context) errorStarts processing messages, including connection setup and initialization.
Parameters:
ctx: Context for controlling the transport lifecycleReturns: Error if the transport fails to start
Usage: Called by the MCP client or server to begin communication. May block depending on the transport implementation.
func Send(ctx context.Context, message *BaseJsonRpcMessage) errorSends a JSON-RPC message through the transport.
Parameters:
ctx: Context for the send operationmessage: The JSON-RPC message to sendReturns: Error if sending fails
func Close() errorCloses the transport connection and cleans up resources.
Returns: Error if cleanup fails
func SetCloseHandler(handler func())Sets a callback function that is invoked when the transport connection closes.
Parameters:
handler: Function to call when the connection closesExample:
transport.SetCloseHandler(func() {
log.Println("Transport connection closed")
})func SetErrorHandler(handler func(error))Sets a callback function that is invoked when transport errors occur.
Parameters:
handler: Function to call with error detailsExample:
transport.SetErrorHandler(func(err error) {
log.Printf("Transport error: %v", err)
})func SetMessageHandler(handler func(ctx context.Context, message *BaseJsonRpcMessage))Sets a callback function that is invoked when messages are received.
Parameters:
handler: Function to call with received messagesExample:
transport.SetMessageHandler(func(ctx context.Context, message *transport.BaseJsonRpcMessage) {
log.Printf("Received message type: %s", message.Type)
// Process the message
})type RequestId int64Unique identifier for JSON-RPC requests and their corresponding responses.
type BaseMessageType string
const (
BaseMessageTypeJSONRPCRequestType BaseMessageType = "request"
BaseMessageTypeJSONRPCNotificationType BaseMessageType = "notification"
BaseMessageTypeJSONRPCResponseType BaseMessageType = "response"
BaseMessageTypeJSONRPCErrorType BaseMessageType = "error"
)Type discriminator for JSON-RPC messages.
Constants:
BaseMessageTypeJSONRPCRequestType: Request message expecting a responseBaseMessageTypeJSONRPCNotificationType: Notification message with no responseBaseMessageTypeJSONRPCResponseType: Success response messageBaseMessageTypeJSONRPCErrorType: Error response messagetype BaseJsonRpcMessage struct {
Type BaseMessageType
JsonRpcRequest *BaseJSONRPCRequest
JsonRpcNotification *BaseJSONRPCNotification
JsonRpcResponse *BaseJSONRPCResponse
JsonRpcError *BaseJSONRPCError
}Union type representing any JSON-RPC message. Exactly one of the pointer fields will be non-nil, determined by the Type field.
Fields:
Type: Message type discriminatorJsonRpcRequest: Request message (when Type is "request")JsonRpcNotification: Notification message (when Type is "notification")JsonRpcResponse: Response message (when Type is "response")JsonRpcError: Error message (when Type is "error")func NewBaseMessageRequest(request *BaseJSONRPCRequest) *BaseJsonRpcMessageCreates a JSON-RPC request message.
Parameters:
request: The request to wrapReturns: BaseJsonRpcMessage with Type set to "request"
Example:
request := &transport.BaseJSONRPCRequest{
Id: 1,
Jsonrpc: "2.0",
Method: "tools/call",
Params: json.RawMessage(`{"name": "calculate", "arguments": {"a": 5, "b": 3}}`),
}
message := transport.NewBaseMessageRequest(request)func NewBaseMessageNotification(notification *BaseJSONRPCNotification) *BaseJsonRpcMessageCreates a JSON-RPC notification message.
Parameters:
notification: The notification to wrapReturns: BaseJsonRpcMessage with Type set to "notification"
Example:
notification := &transport.BaseJSONRPCNotification{
Jsonrpc: "2.0",
Method: "notifications/tools/list_changed",
Params: json.RawMessage(`{}`),
}
message := transport.NewBaseMessageNotification(notification)func NewBaseMessageResponse(response *BaseJSONRPCResponse) *BaseJsonRpcMessageCreates a JSON-RPC success response message.
Parameters:
response: The response to wrapReturns: BaseJsonRpcMessage with Type set to "response"
Example:
response := &transport.BaseJSONRPCResponse{
Id: 1,
Jsonrpc: "2.0",
Result: json.RawMessage(`{"content": [{"type": "text", "text": "Result: 8"}]}`),
}
message := transport.NewBaseMessageResponse(response)func NewBaseMessageError(error *BaseJSONRPCError) *BaseJsonRpcMessageCreates a JSON-RPC error response message.
Parameters:
error: The error to wrapReturns: BaseJsonRpcMessage with Type set to "error"
Example:
errorMsg := &transport.BaseJSONRPCError{
Id: 1,
Jsonrpc: "2.0",
Error: transport.BaseJSONRPCErrorInner{
Code: -32602,
Message: "Invalid parameters",
Data: "Missing required field 'name'",
},
}
message := transport.NewBaseMessageError(errorMsg)func (m *BaseJsonRpcMessage) MarshalJSON() ([]byte, error)Custom JSON marshaling for BaseJsonRpcMessage. Serializes the appropriate message type based on the Type field. This method is typically called automatically by Go's encoding/json package.
Returns:
[]byte: JSON-encoded messageerror: Error if marshaling failstype BaseJSONRPCRequest struct {
Id RequestId `json:"id"`
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params json.RawMessage `json:"params,omitempty"`
}JSON-RPC request message that expects a response.
Fields:
Id: Unique request identifier (used to match responses)Jsonrpc: JSON-RPC version (always "2.0")Method: Method name to invoke (e.g., "tools/call", "prompts/get")Params: Method parameters as raw JSON (decoded by the handler)Example:
request := &transport.BaseJSONRPCRequest{
Id: 42,
Jsonrpc: "2.0",
Method: "tools/list",
Params: json.RawMessage(`{"cursor": null}`),
}Methods:
func (m *BaseJSONRPCRequest) UnmarshalJSON(data []byte) errorCustom JSON unmarshaling for BaseJSONRPCRequest. Validates that required fields (Id, Jsonrpc, Method) are present. This method is typically called automatically by Go's encoding/json package.
Parameters:
data: JSON-encoded dataReturns: Error if unmarshaling fails or required fields are missing
type BaseJSONRPCNotification struct {
Jsonrpc string `json:"jsonrpc"`
Method string `json:"method"`
Params json.RawMessage `json:"params,omitempty"`
}JSON-RPC notification message that does not expect a response.
Fields:
Jsonrpc: JSON-RPC version (always "2.0")Method: Method name (e.g., "notifications/tools/list_changed")Params: Method parameters as raw JSONExample:
notification := &transport.BaseJSONRPCNotification{
Jsonrpc: "2.0",
Method: "notifications/prompts/list_changed",
Params: json.RawMessage(`{}`),
}Methods:
func (m *BaseJSONRPCNotification) UnmarshalJSON(data []byte) errorCustom JSON unmarshaling for BaseJSONRPCNotification. Validates that required fields (Jsonrpc, Method) are present. This method is typically called automatically by Go's encoding/json package.
Parameters:
data: JSON-encoded dataReturns: Error if unmarshaling fails or required fields are missing
type BaseJSONRPCResponse struct {
Id RequestId `json:"id"`
Jsonrpc string `json:"jsonrpc"`
Result json.RawMessage `json:"result"`
}JSON-RPC success response message.
Fields:
Id: Request ID this response corresponds toJsonrpc: JSON-RPC version (always "2.0")Result: Response result as raw JSON (decoded by the caller)Example:
response := &transport.BaseJSONRPCResponse{
Id: 42,
Jsonrpc: "2.0",
Result: json.RawMessage(`{"tools": [{"name": "calculate", "description": "Performs calculations"}]}`),
}Methods:
func (m *BaseJSONRPCResponse) UnmarshalJSON(data []byte) errorCustom JSON unmarshaling for BaseJSONRPCResponse. Validates that required fields (Id, Jsonrpc, Result) are present. This method is typically called automatically by Go's encoding/json package.
Parameters:
data: JSON-encoded dataReturns: Error if unmarshaling fails or required fields are missing
type BaseJSONRPCError struct {
Id RequestId `json:"id"`
Jsonrpc string `json:"jsonrpc"`
Error BaseJSONRPCErrorInner `json:"error"`
}JSON-RPC error response message.
Fields:
Id: Request ID this error corresponds toJsonrpc: JSON-RPC version (always "2.0")Error: Error detailsExample:
errorResp := &transport.BaseJSONRPCError{
Id: 42,
Jsonrpc: "2.0",
Error: transport.BaseJSONRPCErrorInner{
Code: -32601,
Message: "Method not found",
Data: "The method 'tools/unknown' does not exist",
},
}type BaseJSONRPCErrorInner struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}Error details in a JSON-RPC error response.
Fields:
Code: Error code (standard JSON-RPC error codes or custom codes)Message: Concise, single-sentence error messageData: Optional additional error informationStandard JSON-RPC Error Codes:
-32700: Parse error-32600: Invalid request-32601: Method not found-32602: Invalid parameters-32603: Internal error-32000 to -32099: Server error (reserved for implementation-defined errors)Example:
errorInner := transport.BaseJSONRPCErrorInner{
Code: -32602,
Message: "Invalid params",
Data: map[string]string{
"field": "operation",
"error": "must be one of: add, subtract, multiply, divide",
},
}type JSONRPCMessage interface{}Marker interface for JSON-RPC messages. Implemented by BaseJSONRPCRequest, BaseJSONRPCNotification, BaseJSONRPCResponse, and BaseJSONRPCError.
type JsonRpcBody interface{}Marker interface for JSON-RPC message bodies.
Bidirectional Transports (e.g., stdio):
Stateless Transports (e.g., HTTP):
// Client sends request
request := &transport.BaseJSONRPCRequest{
Id: 1,
Jsonrpc: "2.0",
Method: "tools/call",
Params: json.RawMessage(`{"name": "greet", "arguments": {"name": "Alice"}}`),
}
requestMsg := transport.NewBaseMessageRequest(request)
err := transport.Send(ctx, requestMsg)
// Server receives request via message handler
transport.SetMessageHandler(func(ctx context.Context, message *transport.BaseJsonRpcMessage) {
if message.Type == transport.BaseMessageTypeJSONRPCRequestType {
req := message.JsonRpcRequest
// Process request and send response
response := &transport.BaseJSONRPCResponse{
Id: req.Id,
Jsonrpc: "2.0",
Result: json.RawMessage(`{"content": [{"type": "text", "text": "Hello, Alice!"}]}`),
}
responseMsg := transport.NewBaseMessageResponse(response)
transport.Send(ctx, responseMsg)
}
})// Set up error handler
transport.SetErrorHandler(func(err error) {
log.Printf("Transport error: %v", err)
// Handle reconnection or cleanup
})
// Send error response
if err := validateRequest(request); err != nil {
errorResp := &transport.BaseJSONRPCError{
Id: request.Id,
Jsonrpc: "2.0",
Error: transport.BaseJSONRPCErrorInner{
Code: -32602,
Message: "Invalid parameters",
Data: err.Error(),
},
}
errorMsg := transport.NewBaseMessageError(errorResp)
transport.Send(ctx, errorMsg)
}package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"github.com/metoro-io/mcp-golang/transport/stdio"
)
func main() {
transport := stdio.NewStdioServerTransport()
// Set up handlers
transport.SetCloseHandler(func() {
log.Println("Connection closed")
})
transport.SetErrorHandler(func(err error) {
log.Printf("Error: %v", err)
})
transport.SetMessageHandler(func(ctx context.Context, message *transport.BaseJsonRpcMessage) {
// Handle incoming messages
log.Printf("Received %s message", message.Type)
})
// Start transport
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go func() {
if err := transport.Start(ctx); err != nil {
log.Fatalf("Transport failed: %v", err)
}
}()
// Wait for shutdown signal
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
<-sigChan
// Clean shutdown
log.Println("Shutting down...")
cancel()
if err := transport.Close(); err != nil {
log.Printf("Error closing transport: %v", err)
}
}The transport package is abstract. Concrete implementations are provided in sub-packages:
See the respective documentation for implementation-specific details: