tessl install tessl/golang-mcp-golang@0.16.1An unofficial implementation of the Model Context Protocol in Go
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: