Skip to content

Transport Options

MCP-Go supports multiple transport methods to fit different deployment scenarios and integration patterns. Choose the right transport based on your use case, performance requirements, and client capabilities.

Overview

Transport layers handle the communication between MCP clients and servers. Each transport has different characteristics and is optimized for specific scenarios:

  • STDIO - Standard input/output for command-line tools
  • SSE - Server-Sent Events for web applications
  • StreamableHTTP - Traditional HTTP for REST-like interactions
  • In-Process - Direct integration for embedded scenarios

Transport Comparison

TransportUse CaseProsCons
STDIOCLI tools, desktop appsSimple, secure, no networkSingle client, local only
SSEWeb apps, real-timeMulti-client, real-time, web-friendlyHTTP overhead, one-way streaming
StreamableHTTPWeb services, APIsStandard protocol, caching, load balancingNo real-time, more complex
In-ProcessEmbedded, testingNo serialization, fastestSame process only

Quick Example

The same server code works with any transport:

package main
 
import (
    "context"
    "fmt"
    "log"
    "os"
 
    "github.com/mark3labs/mcp-go/mcp"
    "github.com/mark3labs/mcp-go/server"
    "github.com/mark3labs/mcp-go/client"
)
 
func main() {
    // Create server (transport-agnostic)
    s := server.NewMCPServer("Multi-Transport Server", "1.0.0",
        server.WithToolCapabilities(true),
    )
 
    // Add a simple tool
    s.AddTool(
        mcp.NewTool("echo",
            mcp.WithDescription("Echo back the input"),
            mcp.WithString("message", mcp.Required()),
        ),
        handleEcho,
    )
 
    // Choose transport based on environment
    transport := os.Getenv("MCP_TRANSPORT")
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
 
    switch transport {
    case "sse":
        fmt.Printf("Starting SSE server on port %s\n", port)
        sseServer := server.NewSSEServer(s)
        sseServer.Start(":" + port)
    case "streamablehttp":
        fmt.Printf("Starting StreamableHTTP server on port %s\n", port)
        httpServer := server.NewStreamableHTTPServer(s)
        httpServer.Start(":" + port)
    default:
        fmt.Println("Starting STDIO server")
        server.ServeStdio(s)
    }
}
 
func handleEcho(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    message, err := req.RequireString("message")
    if err != nil {
        return nil, err
    }
    return mcp.NewToolResultText(fmt.Sprintf("Echo: %s", message)), nil
}

Choosing the Right Transport

STDIO Transport

Best for:
  • Command-line tools and utilities
  • Desktop application integrations
  • Local development and testing
  • Single-user scenarios
Example use cases:
  • File system tools for IDEs
  • Local database utilities
  • Development workflow automation
  • System administration tools

SSE Transport

Best for:
  • Web applications requiring real-time updates
  • Browser-based LLM interfaces
  • Multi-user collaborative tools
  • Dashboard and monitoring applications
Example use cases:
  • Web-based chat interfaces
  • Real-time data visualization
  • Collaborative document editing
  • Live system monitoring

StreamableHTTP Transport

Best for:
  • Traditional web services
  • REST API integrations
  • Load-balanced deployments
  • Stateless interactions
Example use cases:
  • Microservice architectures
  • Public API endpoints
  • Integration with existing HTTP infrastructure
  • Cached or rate-limited services

In-Process Transport

Best for:
  • Embedded MCP servers
  • Testing and development
  • High-performance scenarios
  • Library integrations
Example use cases:
  • Testing MCP implementations
  • Embedded analytics engines
  • High-frequency trading systems
  • Real-time game servers

Transport Configuration

Environment-Based Selection

func startServer(s *server.MCPServer) error {
    switch os.Getenv("MCP_TRANSPORT") {
    case "sse":
        sseServer := server.NewSSEServer(s)
        return sseServer.Start(getPort())
    case "streamablehttp":
        httpServer := server.NewStreamableHTTPServer(s)
        return httpServer.Start(getPort())
    case "inprocess":
        // Note: In-process transport doesn't use network ports
        // This would typically be used differently in practice
        client := client.NewInProcessClient(s)
        defer client.Close()
        // Keep the process running
        select {}
    default:
        return server.ServeStdio(s)
    }
}
 
func getPort() string {
    if port := os.Getenv("PORT"); port != "" {
        return ":" + port
    }
    return ":8080"
}

Multi-Transport Server

func main() {
    s := server.NewMCPServer("Multi-Transport", "1.0.0")
    
    // Add your tools, resources, prompts...
    setupServer(s)
    
    // Start multiple transports concurrently with proper error handling
    errChan := make(chan error, 3)
    
    go func() {
        log.Println("Starting STDIO server...")
        if err := server.ServeStdio(s); err != nil {
            log.Printf("STDIO server error: %v", err)
            errChan <- fmt.Errorf("STDIO server failed: %w", err)
        }
    }()
    
    go func() {
        log.Println("Starting SSE server on :8080...")
        sseServer := server.NewSSEServer(s)
        if err := sseServer.Start(":8080"); err != nil {
            log.Printf("SSE server error: %v", err)
            errChan <- fmt.Errorf("SSE server failed: %w", err)
        }
    }()
    
    log.Println("Starting StreamableHTTP server on :8081...")
    httpServer := server.NewStreamableHTTPServer(s)
    if err := httpServer.Start(":8081"); err != nil {
        log.Printf("StreamableHTTP server error: %v", err)
        errChan <- fmt.Errorf("StreamableHTTP server failed: %w", err)
    }
    
    // Wait for any server to fail
    select {
    case err := <-errChan:
        log.Printf("Server failed: %v", err)
        return
    }
}
 
// Helper function for the multi-transport example
func setupServer(s *server.MCPServer) {
    // Placeholder implementation - would add tools, resources, etc.
}

Performance Considerations

Latency Comparison

  • In-Process: ~1μs (no serialization)
  • STDIO: ~100μs (local pipes)
  • HTTP/SSE: ~1-10ms (network + HTTP overhead)

Throughput Comparison

  • In-Process: Limited by CPU/memory
  • STDIO: Limited by pipe buffers (~64KB)
  • HTTP/SSE: Limited by network bandwidth

Memory Usage

  • In-Process: Shared memory space
  • STDIO: Minimal overhead
  • HTTP/SSE: Connection pooling, request buffering

Security Considerations

STDIO Transport

  • Pros: No network exposure, process isolation
  • Cons: Inherits parent process permissions
  • Best practices: Validate all inputs, use least privilege

Network Transports (SSE/HTTP)

  • Authentication: Implement proper auth middleware
  • Authorization: Validate permissions per request
  • Rate limiting: Prevent abuse and DoS
  • HTTPS: Always use TLS in production
// Example with security middleware
s := server.NewMCPServer("Secure Server", "1.0.0",
    server.WithToolMiddleware(authMiddleware),
    server.WithToolMiddleware(rateLimitMiddleware),
    server.WithRecovery(),
)

Next Steps

Explore each transport in detail: