Skip to content

Commit dd49b70

Browse files
mcp: Add example of using Middleware for logging purposes (#58)
I needed to implement some logging for our experimental server that we are building out using the SDK, and figured I could propose an example that might make it easier for folks in the future. For #33.
1 parent de4b788 commit dd49b70

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed

mcp/example_middleware_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
2+
// Use of this source code is governed by an MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package mcp_test
6+
7+
import (
8+
"context"
9+
"fmt"
10+
"log/slog"
11+
"os"
12+
"time"
13+
14+
"github.com/modelcontextprotocol/go-sdk/jsonschema"
15+
"github.com/modelcontextprotocol/go-sdk/mcp"
16+
)
17+
18+
// This example demonstrates server side logging using the mcp.Middleware system.
19+
func Example_loggingMiddleware() {
20+
// Create a logger for demonstration purposes.
21+
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
22+
Level: slog.LevelInfo,
23+
ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr {
24+
// Simplify timestamp format for consistent output.
25+
if a.Key == slog.TimeKey {
26+
return slog.String("time", "2025-01-01T00:00:00Z")
27+
}
28+
return a
29+
},
30+
}))
31+
32+
loggingMiddleware := func(next mcp.MethodHandler[*mcp.ServerSession]) mcp.MethodHandler[*mcp.ServerSession] {
33+
return func(
34+
ctx context.Context,
35+
session *mcp.ServerSession,
36+
method string,
37+
params mcp.Params,
38+
) (mcp.Result, error) {
39+
logger.Info("MCP method started",
40+
"method", method,
41+
"session_id", session.ID(),
42+
"has_params", params != nil,
43+
)
44+
45+
start := time.Now()
46+
47+
result, err := next(ctx, session, method, params)
48+
49+
duration := time.Since(start)
50+
51+
if err != nil {
52+
logger.Error("MCP method failed",
53+
"method", method,
54+
"session_id", session.ID(),
55+
"duration_ms", duration.Milliseconds(),
56+
"err", err,
57+
)
58+
} else {
59+
logger.Info("MCP method completed",
60+
"method", method,
61+
"session_id", session.ID(),
62+
"duration_ms", duration.Milliseconds(),
63+
"has_result", result != nil,
64+
)
65+
}
66+
67+
return result, err
68+
}
69+
}
70+
71+
// Create server with middleware
72+
server := mcp.NewServer(&mcp.Implementation{Name: "logging-example"}, nil)
73+
server.AddReceivingMiddleware(loggingMiddleware)
74+
75+
// Add a simple tool
76+
server.AddTool(
77+
&mcp.Tool{
78+
Name: "greet",
79+
Description: "Greet someone with logging.",
80+
InputSchema: &jsonschema.Schema{
81+
Type: "object",
82+
Properties: map[string]*jsonschema.Schema{
83+
"name": {
84+
Type: "string",
85+
Description: "Name to greet",
86+
},
87+
},
88+
Required: []string{"name"},
89+
},
90+
},
91+
func(
92+
ctx context.Context,
93+
ss *mcp.ServerSession,
94+
params *mcp.CallToolParamsFor[map[string]any],
95+
) (*mcp.CallToolResultFor[any], error) {
96+
name, ok := params.Arguments["name"].(string)
97+
if !ok {
98+
return nil, fmt.Errorf("name parameter is required and must be a string")
99+
}
100+
101+
message := fmt.Sprintf("Hello, %s!", name)
102+
return &mcp.CallToolResultFor[any]{
103+
Content: []mcp.Content{
104+
&mcp.TextContent{Text: message},
105+
},
106+
}, nil
107+
},
108+
)
109+
110+
// Create client-server connection for demonstration
111+
client := mcp.NewClient(&mcp.Implementation{Name: "test-client"}, nil)
112+
clientTransport, serverTransport := mcp.NewInMemoryTransports()
113+
114+
ctx := context.Background()
115+
116+
// Connect server and client
117+
serverSession, _ := server.Connect(ctx, serverTransport)
118+
defer serverSession.Close()
119+
120+
clientSession, _ := client.Connect(ctx, clientTransport)
121+
defer clientSession.Close()
122+
123+
// Call the tool to demonstrate logging
124+
result, _ := clientSession.CallTool(ctx, &mcp.CallToolParams{
125+
Name: "greet",
126+
Arguments: map[string]any{
127+
"name": "World",
128+
},
129+
})
130+
131+
fmt.Printf("Tool result: %s\n", result.Content[0].(*mcp.TextContent).Text)
132+
133+
// Output:
134+
// time=2025-01-01T00:00:00Z level=INFO msg="MCP method started" method=initialize session_id="" has_params=true
135+
// time=2025-01-01T00:00:00Z level=INFO msg="MCP method completed" method=initialize session_id="" duration_ms=0 has_result=true
136+
// time=2025-01-01T00:00:00Z level=INFO msg="MCP method started" method=notifications/initialized session_id="" has_params=true
137+
// time=2025-01-01T00:00:00Z level=INFO msg="MCP method completed" method=notifications/initialized session_id="" duration_ms=0 has_result=false
138+
// time=2025-01-01T00:00:00Z level=INFO msg="MCP method started" method=tools/call session_id="" has_params=true
139+
// time=2025-01-01T00:00:00Z level=INFO msg="MCP method completed" method=tools/call session_id="" duration_ms=0 has_result=true
140+
// Tool result: Hello, World!
141+
}

0 commit comments

Comments
 (0)