A simple, reusable Model Context Protocol (MCP) server framework written in Go. This framework supports multiple transport mechanisms including HTTP Streams (MCP-compliant), SSE (Server-Sent Events), and STDIO transports and can be used as both a library and a standalone executable.
- 🚀 Multiple Transports: Support for HTTP Streams (MCP-compliant), SSE (Server-Sent Events), and STDIO
- 📦 Library & Standalone: Use as a Go library or run as a standalone server
- 🧪 Well Tested: Comprehensive test coverage
- 🔄 JSON-RPC 2.0: Full JSON-RPC 2.0 protocol support
- 🌐 CORS Enabled: Built-in CORS support for web clients
- 🐳 Containerized: Docker support with automated builds
- ⚡ Easy to Use: Simple API for registering handlers
- 🔧 MCP Client: Full-featured client implementation for testing and development
# HTTP Streams transport (default)
go run cmd/mcp-server/main.go -addr=8080
# SSE transport
go run cmd/mcp-server/main.go -transport=sse -addr=8080
# STDIO transport
go run cmd/mcp-server/main.go -transport=stdio
package main
import (
"context"
"encoding/json"
"log"
"github.com/protobomb/mcp-server-framework/pkg/transport"
"github.com/protobomb/mcp-server-framework/pkg/mcp"
)
func main() {
// Create transport (HTTP Streams, SSE, or STDIO)
transport := transport.NewHTTPStreamsTransport(mcp.NewServerWithoutTransport(), transport.HTTPStreamsTransportOptions{})
// transport := transport.NewSSETransport(":8080")
// transport := transport.NewSTDIOTransport()
// Create server
server := mcp.NewServer(transport)
// Register a custom handler
server.RegisterHandler("greet", func(ctx context.Context, params json.RawMessage) (interface{}, error) {
var greetParams struct {
Name string `json:"name"`
}
if len(params) > 0 {
json.Unmarshal(params, &greetParams)
}
return map[string]string{
"message": "Hello, " + greetParams.Name + "!",
}, nil
})
// Start server
ctx := context.Background()
if err := server.Start(ctx); err != nil {
log.Fatal(err)
}
// Keep running...
select {}
}
package main
import (
"context"
"log"
"github.com/openhands/mcp-server-framework/internal/transport"
"github.com/openhands/mcp-server-framework/pkg/client"
"github.com/openhands/mcp-server-framework/pkg/mcp"
)
func main() {
// Create transport (STDIO or HTTP)
transport := transport.NewSTDIOTransport("./mcp-server")
// transport := transport.NewHTTPTransport("http://localhost:8080")
// Create client
client := client.NewClient(transport)
defer client.Close()
ctx := context.Background()
// Initialize connection
clientInfo := mcp.ServerInfo{
Name: "my-client",
Version: "1.0.0",
}
result, err := client.Initialize(ctx, clientInfo)
if err != nil {
log.Fatal(err)
}
log.Printf("Server: %s v%s", result.ServerInfo.Name, result.ServerInfo.Version)
// List available tools
tools, err := client.ListTools(ctx)
if err != nil {
log.Fatal(err)
}
log.Printf("Available tools: %d", len(tools.Tools))
// Call a tool
params := map[string]interface{}{
"message": "Hello from client!",
}
response, err := client.CallTool(ctx, "echo", params)
if err != nil {
log.Fatal(err)
}
log.Printf("Tool response: %v", response)
}
# Build everything
make build-both
# Test the client
make run-client
# Try the interactive demo
make demo
# Run all tests
make test
go get github.com/openhands/mcp-server-framework
docker pull ghcr.io/openhands/mcp-server-framework:latest
# Run with HTTP Streams transport (default)
docker run -p 8080:8080 ghcr.io/openhands/mcp-server-framework:latest
# Run with SSE transport
docker run -p 8080:8080 ghcr.io/openhands/mcp-server-framework:latest -transport=sse
# Run with STDIO transport
docker run -i ghcr.io/openhands/mcp-server-framework:latest -transport=stdio
The framework supports multiple transport mechanisms to suit different use cases:
- MCP Streamable HTTP (2024-11-05) compliant
- Modern HTTP + SSE hybrid approach
- Built-in session management with secure session IDs
- Batch request support for improved efficiency
- Excellent proxy and firewall compatibility
- Full MCP specification compliance
- Endpoints:
/mcp
(POST/GET),/health
(GET) - 📖 Detailed Documentation
# Start HTTP Streams server
./mcp-server -addr=8080
# Test with curl
curl http://localhost:8080/health
- Custom SSE implementation for real-time communication
- Bidirectional communication via SSE + HTTP POST
- Built-in session management
- CORS support for web browsers
- Endpoints:
/sse
(GET),/message
(POST),/health
(GET) - 📖 Detailed Documentation
# Start SSE server
./mcp-server -transport=sse -addr=8080
# Test SSE connection
curl -N -H "Accept: text/event-stream" http://localhost:8080/sse
- Standard input/output communication
- Perfect for command-line tools and scripts
- Lightweight and efficient
- No network dependencies
# Start STDIO server
./mcp-server -transport=stdio
# Communicate via stdin/stdout
echo '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{}}' | ./mcp-server -transport=stdio
Feature | HTTP Streams | SSE Transport | STDIO |
---|---|---|---|
MCP Compliance | ✅ Full (2024-11-05) | ✅ Standard | |
Session Management | ✅ Built-in | ✅ Built-in | ❌ N/A |
Batch Requests | ✅ Supported | ❌ Not supported | ✅ Supported |
Web Browser Support | ✅ Excellent | ✅ Good | ❌ N/A |
Proxy Compatibility | ✅ Excellent | ❌ N/A | |
Network Required | ✅ Yes | ✅ Yes | ❌ No |
Use Case | Web apps, APIs | Real-time apps | CLI tools |
server := mcp.NewServer(transport)
// Request handler (expects response)
server.RegisterHandler("methodName", func(ctx context.Context, params json.RawMessage) (interface{}, error) {
// Handle request and return result
return result, nil
})
// Notification handler (no response expected)
server.RegisterNotificationHandler("notificationName", func(ctx context.Context, params json.RawMessage) error {
// Handle notification
return nil
})
// Start the server
err := server.Start(ctx)
// Send notifications to clients
err := server.SendNotification("eventName", eventData)
// Stop the server
err := server.Stop()
// Close the server
err := server.Close()
// With STDIO transport
transport := transport.NewSTDIOTransport("./mcp-server")
client := client.NewClient(transport)
// With HTTP transport
transport := transport.NewHTTPTransport("http://localhost:8080")
client := client.NewClient(transport)
// Initialize connection
clientInfo := mcp.ServerInfo{
Name: "my-client",
Version: "1.0.0",
}
result, err := client.Initialize(ctx, clientInfo)
// List available tools
tools, err := client.ListTools(ctx)
// Call a tool
params := map[string]interface{}{
"param1": "value1",
"param2": 42,
}
response, err := client.CallTool(ctx, "toolName", params)
// Send notifications
err := client.Notify(ctx, "notificationName", params)
// Close the client
err := client.Close()
transport := transport.NewSTDIOTransport()
// or with custom IO
transport := transport.NewSTDIOTransportWithIO(reader, writer)
transport := transport.NewSSETransport(":8080")
SSE endpoints:
GET /sse
- SSE event stream (with optional ?sessionId parameter)POST /message
- Send messages to server (requires ?sessionId parameter)GET /health
- Health check
The framework includes these built-in MCP handlers:
initialize
- MCP initialization handshakeinitialized
- MCP initialization complete notificationtools/list
- List available toolstools/call
- Call a specific tool
The framework includes these example tools:
echo
- Echo back the provided message- Parameters:
message
(string) - The message to echo back - Returns: Text content with "Echo: {message}"
- Parameters:
The framework includes comprehensive testing with both unit and integration tests.
# Run all tests (unit + integration)
make test-all
# Run only unit tests
make test
# Run only integration tests
make test-integration
# Run tests with coverage
make test-coverage
# Run all checks (format, lint, test)
make check
The framework has 42 comprehensive test cases covering:
- SSE Transport: 14 tests covering session management, message handling, CORS, error handling
- STDIO Transport: 9 tests covering transport lifecycle and message handling
- MCP Server: 13 tests covering handlers, tools, capabilities, and protocol compliance
- MCP Client: 14 tests covering client operations, timeouts, and error handling
Integration tests automatically:
- Start the MCP server with SSE transport
- Test complete MCP protocol flow (initialize → initialized → tools/list → tools/call)
- Verify request/response matching and notification handling
- Clean up server process
# Manual unit testing
go test ./pkg/...
go test -v -race ./pkg/...
go test -cover ./pkg/...
# Test with mcp-cli (requires Node.js)
./mcp-server -transport=sse -addr=8080 &
npx @modelcontextprotocol/cli connect sse http://localhost:8080/sse
# Test client with STDIO server
make test-client-stdio
# Test client with HTTP server
make test-client-http
scripts/test_sse_integration.py
- Python-based SSE integration testscripts/test-examples.sh
- Bash-based endpoint testing script
# Build everything (server + client)
make build-both
# Build just the server
make build
# Build just the client
make build-client
# Run the client with help
make run-client
# Try the interactive demo
make demo
# Manual building
go build -o mcp-server cmd/mcp-server/main.go
go build -o mcp-client cmd/mcp-client/main.go
# Build for different platforms
GOOS=linux GOARCH=amd64 go build -o mcp-server-linux cmd/mcp-server/main.go
GOOS=windows GOARCH=amd64 go build -o mcp-server.exe cmd/mcp-server/main.go
GOOS=darwin GOARCH=amd64 go build -o mcp-server-darwin cmd/mcp-server/main.go
# Build Docker image
docker build -t mcp-server-framework .
# Run with SSE transport (default)
docker run -p 8080:8080 mcp-server-framework
# Run with STDIO transport
docker run -i mcp-server-framework -transport=stdio
# Run with custom SSE address
docker run -p 9090:9090 mcp-server-framework -addr=9090
# Test with STDIO server
./mcp-client -transport=stdio -command='./mcp-server'
# Test with HTTP server (start server first)
./mcp-server -transport=sse -addr=8080 &
./mcp-client -transport=http -addr=http://localhost:8080
# Interactive demo
make demo
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | ./mcp-server
// Generate or get session ID
const sessionId = 'your-session-id'; // or generate one
// Connect to SSE endpoint
const eventSource = new EventSource(`http://localhost:8080/sse?sessionId=${sessionId}`);
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
// Send a message
fetch(`http://localhost:8080/message?sessionId=${sessionId}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'echo',
params: { message: 'Hello, World!' }
})
});
Comprehensive documentation is available for all transport mechanisms and features:
- HTTP Streams Transport - Complete guide to the MCP-compliant HTTP Streams transport
- SSE Transport - Server-Sent Events transport documentation
- API Reference - Complete API documentation with examples
- Testing Guide - Testing strategies and coverage information
- Fork the repository
- Create a feature branch
- Add tests for your changes
- Ensure all tests pass
- Submit a pull request
MIT License - see LICENSE file for details.
This framework implements the Model Context Protocol (MCP) specification:
- JSON-RPC 2.0 messaging
- Initialization handshake
- Request/response patterns
- Notification support
- Error handling
For more information about MCP, visit the official specification.