Transports

Transports

Transports

GoMCP separates the core protocol logic from how clients and servers communicate. Supported transports are found in the transport/ directory. The gomcp/client package provides constructors for the supported transports, abstracting away the low-level communication details.

Choosing a Transport

Select the transport that corresponds to the server you are connecting to:

  • Stdio: For communicating with a server running as a local child process, piping standard input/output.
  • SSE + HTTP: For connecting to servers implementing the 2024-11-05 protocol’s HTTP+SSE transport model (HTTP POST for requests, SSE for server-to-client messages).
  • WebSocket: For connecting to servers implementing the 2025-03-26 protocol’s Streamable HTTP transport model (single WebSocket connection for all messages).
  • TCP: A lower-level option for raw TCP socket communication.

Stdio Transport

The Stdio transport facilitates communication over standard input and output streams. This is particularly useful for:

  • Local Inter-Process Communication: When your client application launches and manages the server as a child process.
  • Simple Examples and Testing: Provides an easy way to demonstrate basic MCP communication without network setup.

To create a client using the Stdio transport, use the client.NewStdioClient constructor:

package main

import (
	"context"
	"log"
	"os"
	"time"

	"github.com/localrivet/gomcp/client"
)

func main() {
	log.SetOutput(os.Stderr)
	log.SetFlags(log.Ltime | log.Lshortfile)
	log.Println("Starting Stdio Client...")

	// Create a client instance for the stdio transport
	// Provide a client name and optional ClientOptions
	clt, err := client.NewStdioClient("MyStdioClient", client.ClientOptions{
		// Optional configurations can go here
		// Logger: myCustomLogger,
	})
	if err != nil {
		log.Fatalf("Failed to create stdio client: %v", err)
	}
	// Ensure the client connection is closed when main exits
	defer clt.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Connect to the server and perform the initialization handshake
	log.Println("Connecting to server via stdio...")
	err = clt.Connect(ctx)
	if err != nil {
		log.Fatalf("Failed to connect and initialize: %v", err)
	}
	log.Printf("Connected to server: %s", clt.ServerInfo().Name)

	// Client is now ready to make requests using the 'clt' instance
	// ... example requests like ListTools, CallTool, etc. ...

	log.Println("Client operations finished.")
}

When using Stdio, the client typically writes JSON-RPC messages to standard output, which are read by the server from its standard input, and vice-versa.

SSE + HTTP Hybrid Transport

This transport is designed for network communication and is compatible with servers implementing the transport model introduced in the 2024-11-05 protocol specification. It utilizes:

  • HTTP POST: For the client to send requests (like initialize, tools/call).
  • Server-Sent Events (SSE): For the server to send asynchronous messages (like initialize responses, notifications, server-initiated requests) to the client over a persistent connection.

To create a client for this transport, use the client.NewSSEClient constructor:

package main

import (
	"context"
	"log"
	"os"
	"time"

	"github.com/localrivet/gomcp/client"
)

func main() {
	log.SetOutput(os.Stderr)
	log.SetFlags(log.Ltime | log.Lshortfile)
	log.Println("Starting SSE Client...")

	// Server's base URL (e.g., http://localhost:8080)
	baseURL := "http://localhost:8080"
	// The base path for MCP endpoints on the server (e.g., /mcp)
	basePath := "/mcp"

	// Create a client instance for the SSE transport
	clt, err := client.NewSSEClient("MySSEClient", baseURL, basePath, client.ClientOptions{
		// Optional configurations
	})
	if err != nil {
		log.Fatalf("Failed to create SSE client: %v", err)
	}
	defer clt.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Connect to the server and perform the initialization handshake
	log.Println("Connecting to server via SSE+HTTP...")
	err = clt.Connect(ctx)
	if err != nil {
		log.Fatalf("Failed to connect and initialize: %v", err)
	}
	log.Printf("Connected to server: %s", clt.ServerInfo().Name)

	// Client is now ready to make requests using the 'clt' instance
	// ... example requests ...

	log.Println("Client operations finished.")
}

This transport is suitable for web-based clients or environments where SSE is a preferred mechanism for server-push.

WebSocket Transport

The WebSocket transport is the recommended network transport for servers implementing the 2025-03-26 protocol specification’s “Streamable HTTP” model. It uses a single, full-duplex WebSocket connection for all communication, allowing both client and server to send messages asynchronously over the same connection.

To create a client for this transport, use the client.NewWebSocketClient constructor:

package main

import (
	"context"
	"log"
	"os"
	"time"

	"github.com/localrivet/gomcp/client"
)

func main() {
	log.SetOutput(os.Stderr)
	log.SetFlags(log.Ltime | log.Lshortfile)
	log.Println("Starting WebSocket Client...")

	// Server's WebSocket URL (e.g., ws://localhost:8080/mcp)
	wsURL := "ws://localhost:8080/mcp"

	// Create a client instance for the WebSocket transport
	clt, err := client.NewWebSocketClient("MyWebSocketClient", wsURL, client.ClientOptions{
		// Optional configurations
	})
	if err != nil {
		log.Fatalf("Failed to create WebSocket client: %v", err)
	}
	defer clt.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Connect to the server and perform the initialization handshake
	log.Println("Connecting to server via WebSocket...")
	err = clt.Connect(ctx)
	if err != nil {
		log.Fatalf("Failed to connect and initialize: %v", err)
	}
	log.Printf("Connected to server: %s", clt.ServerInfo().Name)

	// Client is now ready to make requests using the 'clt' instance
	// ... example requests ...

	log.Println("Client operations finished.")
}

WebSocket is generally preferred for new implementations supporting the latest protocol version due to its simplicity and efficiency for bidirectional communication.

TCP Transport

The TCP transport provides a lower-level option for raw TCP socket connections. This might be used in specific scenarios where a custom layer is built on top of TCP or for direct socket-based communication.

To create a client for this transport, use the client.NewTCPClient constructor:

package main

import (
	"context"
	"log"
	"os"
	"time"

	"github.com/localrivet/gomcp/client"
)

func main() {
	log.SetOutput(os.Stderr)
	log.SetFlags(log.Ltime | log.Lshortfile)
	log.Println("Starting TCP Client...")

	// Server's TCP address (e.g., localhost:6000)
	tcpAddr := "localhost:6000"

	// Create a client instance for the TCP transport
	clt, err := client.NewTCPClient("MyTCPClient", tcpAddr, client.ClientOptions{
		// Optional configurations
	})
	if err != nil {
		log.Fatalf("Failed to create TCP client: %v", err)
	}
	defer clt.Close()

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Connect to the server and perform the initialization handshake
	log.Println("Connecting to server via TCP...")
	err = clt.Connect(ctx)
	if err != nil {
		log.Fatalf("Failed to connect and initialize: %v", err)
	}
	log.Printf("Connected to server: %s", clt.ServerInfo().Name)

	// Client is now ready to make requests using the 'clt' instance
	// ... example requests ...

	log.Println("Client operations finished.")
}

When using the TCP transport, you are responsible for ensuring that the data sent and received over the socket adheres to the JSON-RPC 2.0 and MCP specifications.

Client Options (client.ClientOptions)

All client transport constructors accept a client.ClientOptions struct for configuration:

type ClientOptions struct {
	// ClientCapabilities allows specifying the capabilities the client supports.
	// This is sent to the server during the initialization handshake.
	ClientCapabilities protocol.ClientCapabilities

	Logger types.Logger

	// PreferredProtocolVersion allows the client to request a specific protocol version.
	// If nil, the client will attempt to negotiate the latest supported version.
	PreferredProtocolVersion *string

	// Custom options can be provided as key-value pairs for transport-specific settings.
	Custom map[string]interface{}
}
  • ClientCapabilities: Define the features your client supports.
  • Logger: Provide a custom logger if you don’t want to use the default.
  • PreferredProtocolVersion: Specify a desired protocol version.
  • Custom: Pass transport-specific options.

By choosing the appropriate transport and configuring the client options, you can build GoMCP clients that effectively communicate with various MCP servers.