or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

client.mdcontent-types.mdindex.mdserver.mdtransport-http.mdtransport-stdio.mdtransport.md
tile.json

transport.mddocs/

Transport Layer

The transport layer provides the abstract interface and message types for MCP communication. Transports handle the underlying message delivery mechanism between clients and servers.

Package

import "github.com/metoro-io/mcp-golang/transport"

Transport Interface

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.

Start

func Start(ctx context.Context) error

Starts processing messages, including connection setup and initialization.

Parameters:

  • ctx: Context for controlling the transport lifecycle

Returns: 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.

Send

func Send(ctx context.Context, message *BaseJsonRpcMessage) error

Sends a JSON-RPC message through the transport.

Parameters:

  • ctx: Context for the send operation
  • message: The JSON-RPC message to send

Returns: Error if sending fails

Close

func Close() error

Closes the transport connection and cleans up resources.

Returns: Error if cleanup fails

SetCloseHandler

func SetCloseHandler(handler func())

Sets a callback function that is invoked when the transport connection closes.

Parameters:

  • handler: Function to call when the connection closes

Example:

transport.SetCloseHandler(func() {
    log.Println("Transport connection closed")
})

SetErrorHandler

func SetErrorHandler(handler func(error))

Sets a callback function that is invoked when transport errors occur.

Parameters:

  • handler: Function to call with error details

Example:

transport.SetErrorHandler(func(err error) {
    log.Printf("Transport error: %v", err)
})

SetMessageHandler

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 messages

Example:

transport.SetMessageHandler(func(ctx context.Context, message *transport.BaseJsonRpcMessage) {
    log.Printf("Received message type: %s", message.Type)
    // Process the message
})

JSON-RPC Message Types

RequestId

type RequestId int64

Unique identifier for JSON-RPC requests and their corresponding responses.

BaseMessageType

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 response
  • BaseMessageTypeJSONRPCNotificationType: Notification message with no response
  • BaseMessageTypeJSONRPCResponseType: Success response message
  • BaseMessageTypeJSONRPCErrorType: Error response message

BaseJsonRpcMessage

type 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 discriminator
  • JsonRpcRequest: 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")

BaseJsonRpcMessage Constructors

NewBaseMessageRequest

func NewBaseMessageRequest(request *BaseJSONRPCRequest) *BaseJsonRpcMessage

Creates a JSON-RPC request message.

Parameters:

  • request: The request to wrap

Returns: 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)

NewBaseMessageNotification

func NewBaseMessageNotification(notification *BaseJSONRPCNotification) *BaseJsonRpcMessage

Creates a JSON-RPC notification message.

Parameters:

  • notification: The notification to wrap

Returns: 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)

NewBaseMessageResponse

func NewBaseMessageResponse(response *BaseJSONRPCResponse) *BaseJsonRpcMessage

Creates a JSON-RPC success response message.

Parameters:

  • response: The response to wrap

Returns: 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)

NewBaseMessageError

func NewBaseMessageError(error *BaseJSONRPCError) *BaseJsonRpcMessage

Creates a JSON-RPC error response message.

Parameters:

  • error: The error to wrap

Returns: 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)

MarshalJSON

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 message
  • error: Error if marshaling fails

JSON-RPC Message Body Types

BaseJSONRPCRequest

type 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) error

Custom 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 data

Returns: Error if unmarshaling fails or required fields are missing

BaseJSONRPCNotification

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 JSON

Example:

notification := &transport.BaseJSONRPCNotification{
    Jsonrpc: "2.0",
    Method:  "notifications/prompts/list_changed",
    Params:  json.RawMessage(`{}`),
}

Methods:

func (m *BaseJSONRPCNotification) UnmarshalJSON(data []byte) error

Custom 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 data

Returns: Error if unmarshaling fails or required fields are missing

BaseJSONRPCResponse

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 to
  • Jsonrpc: 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) error

Custom 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 data

Returns: Error if unmarshaling fails or required fields are missing

BaseJSONRPCError

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 to
  • Jsonrpc: JSON-RPC version (always "2.0")
  • Error: Error details

Example:

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",
    },
}

BaseJSONRPCErrorInner

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 message
  • Data: Optional additional error information

Standard 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",
    },
}

Marker Interfaces

JSONRPCMessage

type JSONRPCMessage interface{}

Marker interface for JSON-RPC messages. Implemented by BaseJSONRPCRequest, BaseJSONRPCNotification, BaseJSONRPCResponse, and BaseJSONRPCError.

JsonRpcBody

type JsonRpcBody interface{}

Marker interface for JSON-RPC message bodies.

Transport Implementation Notes

Bidirectional vs Stateless Transports

Bidirectional Transports (e.g., stdio):

  • Support full MCP features including notifications
  • Maintain persistent connections
  • Enable server-to-client notifications (e.g., tool list changes)
  • Suitable for long-running sessions

Stateless Transports (e.g., HTTP):

  • Request-response only, no notifications
  • No persistent connection state
  • Simpler to deploy and scale
  • Limited to client-initiated requests

Message Flow Example

// 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)
    }
})

Error Handling

// 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)
}

Lifecycle Management

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)
    }
}

Transport Implementations

The transport package is abstract. Concrete implementations are provided in sub-packages:

  • stdio: Standard input/output transport for full bidirectional communication
  • http: HTTP-based transports for stateless request-response communication
    • HTTPTransport: Server-side HTTP transport
    • HTTPClientTransport: Client-side HTTP transport
    • GinTransport: Server-side transport using Gin framework

See the respective documentation for implementation-specific details:

  • Stdio Transport Documentation
  • HTTP Transports Documentation