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
Transport | Use Case | Pros | Cons |
---|---|---|---|
STDIO | CLI tools, desktop apps | Simple, secure, no network | Single client, local only |
SSE | Web apps, real-time | Multi-client, real-time, web-friendly | HTTP overhead, one-way streaming |
StreamableHTTP | Web services, APIs | Standard protocol, caching, load balancing | No real-time, more complex |
In-Process | Embedded, testing | No serialization, fastest | Same 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
- 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
- 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
- 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
- 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:
- STDIO Transport - Command-line integration
- SSE Transport - Real-time web applications
- StreamableHTTP Transport - Traditional web services
- In-Process Transport - Embedded scenarios