MCP transports enable communication between clients and servers over various channels. The SDK provides multiple built-in transport implementations.
All transports implement the Transport interface.
import "github.com/modelcontextprotocol/go-sdk/mcp"
type Transport interface {
Connect(ctx context.Context) (Connection, error)
}The low-level connection interface returned by transports.
type Connection interface {
Read(context.Context) (jsonrpc.Message, error)
Write(context.Context, jsonrpc.Message) error
Close() error
SessionID() string
}Methods:
Read: Reads the next message from the connectionWrite: Writes a message to the connection (may be called concurrently)Close: Closes the connection (may be called multiple times concurrently)SessionID: Returns the session ID for this connectionError returned when sending to a closed connection.
var ErrConnectionClosed = errors.New("connection closed")Communicates over stdin/stdout using newline-delimited JSON.
type StdioTransport struct{}Usage:
transport := &mcp.StdioTransport{}
err := server.Run(ctx, transport)Use cases:
Communicates over custom readers and writers using newline-delimited JSON.
type IOTransport struct {
Reader io.ReadCloser
Writer io.WriteCloser
}Example:
transport := &mcp.IOTransport{
Reader: customReader,
Writer: customWriter,
}
conn, err := transport.Connect(ctx)Use cases:
Runs a command as a subprocess and communicates with it over stdin/stdout.
type CommandTransport struct {
Command *exec.Cmd
TerminateDuration time.Duration
}Fields:
Command: The command to executeTerminateDuration: Time to wait after closing stdin before sending SIGTERM (default: 5s)Example:
import "os/exec"
transport := &mcp.CommandTransport{
Command: exec.Command("path/to/mcp-server"),
}
session, err := client.Connect(ctx, transport, nil)Shutdown behavior:
TerminateDuration for graceful exitTerminateDurationUse cases:
Provides in-memory connections for testing and same-process communication.
type InMemoryTransport struct {
// contains filtered or unexported fields
}Creates a pair of connected in-memory transports.
func NewInMemoryTransports() (*InMemoryTransport, *InMemoryTransport)Returns: Two symmetrical transports connected to each other
Example:
serverTransport, clientTransport := mcp.NewInMemoryTransports()
// Connect server first
serverSession, err := server.Connect(ctx, serverTransport, nil)
if err != nil {
log.Fatal(err)
}
// Then connect client
clientSession, err := client.Connect(ctx, clientTransport, nil)
if err != nil {
log.Fatal(err)
}Use cases:
Server-side handler for SSE-based MCP sessions (2024-11-05 spec).
func NewSSEHandler(
getServer func(request *http.Request) *Server,
opts *SSEOptions,
) *SSEHandlerParameters:
getServer: Function that returns a Server for each new request (or reuses existing)opts: Optional configurationExample:
server := mcp.NewServer(&mcp.Implementation{Name: "my-server"}, nil)
handler := mcp.NewSSEHandler(func(req *http.Request) *mcp.Server {
return server
}, nil)
http.Handle("/sse", handler)
log.Fatal(http.ListenAndServe(":8080", nil))Configuration for SSEHandler.
type SSEOptions struct{}Currently empty, reserved for future options.
Low-level SSE server transport (typically managed by SSEHandler).
type SSEServerTransport struct {
Endpoint string
Response http.ResponseWriter
}Fields:
Endpoint: Session endpoint where client can POST messagesResponse: Hanging response body for the GET requestMethods:
func (t *SSEServerTransport) Connect(context.Context) (Connection, error)
func (t *SSEServerTransport) ServeHTTP(w http.ResponseWriter, req *http.Request)Client-side SSE transport.
type SSEClientTransport struct {
Endpoint string
HTTPClient *http.Client
}Fields:
Endpoint: The SSE endpoint to connect toHTTPClient: HTTP client for making requests (optional)Example:
transport := &mcp.SSEClientTransport{
Endpoint: "http://localhost:8080/sse",
}
session, err := client.Connect(ctx, transport, nil)How SSE transport works:
Use cases:
Server-side handler for streamable MCP sessions (2025-03-26 spec).
func NewStreamableHTTPHandler(
getServer func(*http.Request) *Server,
opts *StreamableHTTPOptions,
) *StreamableHTTPHandlerExample:
handler := mcp.NewStreamableHTTPHandler(
func(req *http.Request) *mcp.Server {
return server
},
&mcp.StreamableHTTPOptions{
EventStore: eventStore,
SessionTimeout: 5 * time.Minute,
},
)
http.Handle("/mcp", handler)Configuration for StreamableHTTPHandler.
type StreamableHTTPOptions struct {
Stateless bool
JSONResponse bool
Logger *slog.Logger
EventStore EventStore
SessionTimeout time.Duration
}Fields:
Stateless: If true, server doesn't validate session IDs and uses temporary sessionsJSONResponse: Return application/json instead of text/event-streamLogger: Logger for server activity (nil disables logging)EventStore: Enables stream resumption by persisting eventsSessionTimeout: Automatically close idle sessions after this duration (0 disables)Low-level streamable server transport.
type StreamableServerTransport struct {
SessionID string
Stateless bool
EventStore EventStore
}Fields:
SessionID: The ID of this sessionStateless: Whether the session is statelessEventStore: Optional event store for stream resumptionMethods:
func (t *StreamableServerTransport) Connect(ctx context.Context) (Connection, error)
func (t *StreamableServerTransport) ServeHTTP(w http.ResponseWriter, req *http.Request)Client-side streamable HTTP transport.
type StreamableClientTransport struct {
Endpoint string
HTTPClient *http.Client
MaxRetries int
}Fields:
Endpoint: The HTTP endpoint to connect toHTTPClient: HTTP client for requests (optional)MaxRetries: Maximum reconnection attempts before giving up (default: 5)Example:
transport := &mcp.StreamableClientTransport{
Endpoint: "http://localhost:8080/mcp",
MaxRetries: 10,
}
session, err := client.Connect(ctx, transport, nil)Features:
Use cases:
Interface for persisting and replaying SSE stream data.
type EventStore interface {
Open(_ context.Context, sessionID, streamID string) error
Append(_ context.Context, sessionID, streamID string, data []byte) error
After(_ context.Context, sessionID, streamID string, index int) iter.Seq2[[]byte, error]
SessionClosed(_ context.Context, sessionID string) error
}Methods:
Open: Called when a new stream is createdAppend: Appends data for an outgoing event to the streamAfter: Returns an iterator over data after a given indexSessionClosed: Called when a session is finishedAll methods must be safe for concurrent use.
In-memory EventStore implementation.
func NewMemoryEventStore(opts *MemoryEventStoreOptions) *MemoryEventStoreMethods:
func (m *MemoryEventStore) MaxBytes() int
func (m *MemoryEventStore) SetMaxBytes(n int)
func (m *MemoryEventStore) After(
_ context.Context,
sessionID, streamID string,
index int,
) iter.Seq2[[]byte, error]
func (m *MemoryEventStore) Append(
_ context.Context,
sessionID, streamID string,
data []byte,
) error
func (m *MemoryEventStore) Open(
_ context.Context,
sessionID, streamID string,
) error
func (m *MemoryEventStore) SessionClosed(
_ context.Context,
sessionID string,
) errorConfiguration for MemoryEventStore.
type MemoryEventStoreOptions struct{}Currently empty, reserved for future options.
Error returned when requested events are no longer available.
var ErrEventsPurged = errors.New("events purged")Example:
store := mcp.NewMemoryEventStore(nil)
handler := mcp.NewStreamableHTTPHandler(
getServer,
&mcp.StreamableHTTPOptions{
EventStore: store,
},
)Represents a server-sent event.
type Event struct {
Name string
ID string
Data []byte
}Fields:
Name: The "event" fieldID: The "id" fieldData: The "data" fieldMethods:
func (e Event) Empty() boolReturns whether the event is empty.
Wraps another transport and logs all JSON-RPC messages.
type LoggingTransport struct {
Transport Transport
Writer io.Writer
}Example:
import "os"
transport := &mcp.LoggingTransport{
Transport: &mcp.StdioTransport{},
Writer: os.Stderr,
}Use cases:
You can implement custom transports by satisfying the Transport interface. The transport must return a Connection that handles reading and writing JSON-RPC messages.
Example custom transport:
type MyTransport struct {
// custom fields
}
func (t *MyTransport) Connect(ctx context.Context) (mcp.Connection, error) {
// Create your connection implementation
return &myConnection{}, nil
}
type myConnection struct {
// connection state
}
func (c *myConnection) Read(ctx context.Context) (jsonrpc.Message, error) {
// Read next message
}
func (c *myConnection) Write(ctx context.Context, msg jsonrpc.Message) error {
// Write message
}
func (c *myConnection) Close() error {
// Clean up resources
}
func (c *myConnection) SessionID() string {
// Return session identifier
}| Transport | Use Case | Pros | Cons |
|---|---|---|---|
| StdioTransport | CLI tools, simple servers | Simple, language-agnostic | Single connection only |
| CommandTransport | External servers | Process isolation, clean shutdown | Subprocess overhead |
| InMemoryTransport | Testing | Fast, no I/O overhead | Same-process only |
| SSEClientTransport/SSEHandler | Web applications (legacy) | HTTP-based, firewall-friendly | Older spec |
| StreamableClientTransport/StreamableHTTPHandler | Modern web apps | Resumable, fault-tolerant | More complex |
| IOTransport | Custom I/O | Flexible | Requires custom setup |