This example demonstrates an MCP (Model Context Protocol) server implementation in Go, focusing on observability and authentication token propagation. The server supports both HTTP and stdio transports, and integrates structured logging and OpenTelemetry for enhanced traceability.
- MCP Protocol Support: Implements an MCP server with tool registration and context propagation.
- Observability: Integrates OpenTelemetry and structured logging (
slog) for tracing, metrics, and error reporting. - Authentication Token Propagation: Passes authentication tokens through context, supporting both HTTP headers and environment variables.
- Middleware Architecture: Uses middleware to inject observability attributes and error details into traces or logs.
- Flexible Transport: Can run as a long-running HTTP server (with Gin) or as a stdio-based process for headless/scripted use.
04-observability/
├── server.go # Main server implementation with observability features- Go 1.24+ (recommended)
- MCP Go SDK (
github.com/mark3labs/mcp-go) - Gin, OpenTelemetry, and related dependencies (see
go.mod)
You can run the server in two modes: stdio (default) or http.
This mode is suitable for headless or local script integration.
go run server.go
# or explicitly specify stdio
go run server.go --transport stdio- The authentication token is read from the environment and injected into each request context.
This mode starts a persistent HTTP server using Gin.
go run server.go --transport http --addr :8080- The server listens on the specified address (default
:8080). - Authentication tokens are extracted from HTTP requests and injected into the context.
- Supports POST, GET, and DELETE on the
/mcpendpoint.
curl -X POST http://localhost:8080/mcp -H "Authorization: Bearer <token>" -d '{"tool": "make_authenticated_request", ...}'The MCPServer struct wraps the underlying MCP server instance and provides methods to serve via HTTP or stdio.
- Tool Registration: Registers tools such as
make_authenticated_requestandshow_auth_tokenvia theoperationpackage. - Middleware: Adds logging, recovery, and a custom tool handler middleware for observability.
The custom middleware (MCPToolHandlerMiddleware) records:
- Tool name and parameters
- Execution status (success/error)
- Error messages (if any)
- Wall-clock duration (ms)
- All attributes are injected into the current OpenTelemetry trace span, or logged if tracing is not active.
AddRequestAttributes(ctx, attrs...): Adds OpenTelemetry attributes to the current span, or logs them if no span is active.composeLogAttrs(span, attrs...): Converts attributes and span context into structured log fields.extractStatusAndError(res, err): Extracts status and error messages for observability.
- Uses Gin for HTTP routing and recovery.
- Integrates
slogfor structured logging. - Ensures context propagation from Gin to the MCP handler.
- Uses
github.com/appleboy/gracefulfor managing server lifecycle and graceful shutdowns.
- Tracing: All tool invocations are traced with OpenTelemetry attributes.
- Fallback Logging: If no trace span is active, attributes are logged with
slogfor consistent observability. - Structured Logs: All logs include trace and span IDs (or "none" if unavailable), tool names, parameters, status, errors, and durations.
func MCPToolHandlerMiddleware() server.ToolHandlerMiddleware {
return func(next server.ToolHandlerFunc) server.ToolHandlerFunc {
return func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
start := time.Now()
AddRequestAttributes(
ctx,
attribute.String("mcp.tool", req.Params.Name),
attribute.String("mcp.params", fmt.Sprintf("%+v", req.Params)),
)
res, err := next(ctx, req)
durationMs := float64(time.Since(start).Microseconds()) / 1000.0
status, errMsg := extractStatusAndError(res, err)
attrs := []attribute.KeyValue{
attribute.String("mcp.status", status),
attribute.Float64("mcp.duration_ms", durationMs),
}
if errMsg != "" {
attrs = append(attrs, attribute.String("mcp.error", errMsg))
}
AddRequestAttributes(ctx, attrs...)
return res, err
}
}
}| Flag | Description | Default |
|---|---|---|
--transport, -t |
Transport type: stdio or http |
stdio |
--addr |
Address to listen on (HTTP mode only) | :8080 |
This example is part of the go-training/mcp-workshop and is licensed under the MIT License.