Skip to content

Commit c890482

Browse files
committed
doc: document logging
1 parent 28753c9 commit c890482

File tree

5 files changed

+172
-3
lines changed

5 files changed

+172
-3
lines changed

docs/client.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,12 @@ func Example_roots() {
5656
if _, err := s.Connect(ctx, t1, nil); err != nil {
5757
log.Fatal(err)
5858
}
59-
if _, err := c.Connect(ctx, t2, nil); err != nil {
59+
60+
clientSession, err := c.Connect(ctx, t2, nil)
61+
if err != nil {
6062
log.Fatal(err)
6163
}
64+
defer clientSession.Close()
6265

6366
// ...and add a root. The server is notified about the change.
6467
c.AddRoots(&mcp.Root{URI: "file://b"})

docs/server.md

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func Example_prompts() {
7373
if err != nil {
7474
log.Fatal(err)
7575
}
76+
defer cs.Close()
7677

7778
// List the prompts.
7879
for p, err := range cs.Prompts(ctx, nil) {
@@ -157,7 +158,86 @@ _ = mcp.NewServer(&mcp.Implementation{Name: "server"}, &mcp.ServerOptions{
157158

158159
### Logging
159160

160-
<!-- TODO -->
161+
MCP servers can send logging messages to MCP clients so their users can keep informed of progress.
162+
(This form of logging is distinct from server-side logging, where the
163+
server produces logs that remain server-side, for use by server maintainers.)
164+
165+
**Server-side**:
166+
The minimum log level is part of the server state.
167+
For stateful sessions, there is no default log level: no log messages will be sent
168+
until the client calls `SetLevel` (see below).
169+
For stateful sessions, the level defaults to "info".
170+
171+
[`ServerSession.Log`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Log) is the low-level way for servers to log to clients.
172+
It sends a logging notification to the client if the level of the message
173+
is at least the minimum log level.
174+
175+
For a simpler API, use [`NewLoggingHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#NewLoggingHandler) to obtain a [`slog.Handler`](https://pkg.go.dev/log/slog#Handler).
176+
By setting [`LoggingHandlerOptions.MinInterval`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#LoggingHandlerOptions.MinInterval), the handler can be rate-limited
177+
to avoid spamming clients with too many messages.
178+
179+
Servers always report the logging capability.
180+
181+
182+
**Client-side**:
183+
184+
Set [`ClientOptions.LoggingMessageHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.LoggingMessageHandler) to receive log messages.
185+
186+
Call [`ClientSession.SetLevel`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.SetLevel) to change the log level for a session.
187+
188+
```go
189+
func Example_logging() {
190+
ctx := context.Background()
191+
192+
// Create a server.
193+
s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
194+
195+
// Create a client that displays log messages.
196+
c := mcp.NewClient(
197+
&mcp.Implementation{Name: "client", Version: "v0.0.1"},
198+
&mcp.ClientOptions{
199+
LoggingMessageHandler: func(_ context.Context, r *mcp.LoggingMessageRequest) {
200+
m := r.Params.Data.(map[string]any)
201+
fmt.Println(m["msg"], m["value"])
202+
},
203+
})
204+
205+
// Connect the server and client.
206+
t1, t2 := mcp.NewInMemoryTransports()
207+
ss, err := s.Connect(ctx, t1, nil)
208+
if err != nil {
209+
log.Fatal(err)
210+
}
211+
defer ss.Close()
212+
cs, err := c.Connect(ctx, t2, nil)
213+
if err != nil {
214+
log.Fatal(err)
215+
}
216+
defer cs.Close()
217+
218+
// Set the minimum log level to "info".
219+
if err := cs.SetLoggingLevel(ctx, &mcp.SetLoggingLevelParams{Level: "info"}); err != nil {
220+
log.Fatal(err)
221+
}
222+
223+
// Get a slog.Logger for the server session.
224+
logger := slog.New(mcp.NewLoggingHandler(ss, nil))
225+
226+
// Log some things.
227+
logger.Info("info shows up", "value", 1)
228+
logger.Debug("debug doesn't show up", "value", 2)
229+
logger.Warn("warn shows up", "value", 3)
230+
231+
// Wait for them to arrive on the client.
232+
// In a real application, the log messages would appear asynchronously
233+
// while other work was happening.
234+
time.Sleep(500 * time.Millisecond)
235+
236+
// Output:
237+
// info shows up 1
238+
// warn shows up 3
239+
}
240+
```
161241

162242
### Pagination
163243

internal/docs/server.src.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,34 @@ requests.
5656

5757
### Logging
5858

59-
<!-- TODO -->
59+
MCP servers can send logging messages to MCP clients so their users can keep informed of progress.
60+
(This form of logging is distinct from server-side logging, where the
61+
server produces logs that remain server-side, for use by server maintainers.)
62+
63+
**Server-side**:
64+
The minimum log level is part of the server state.
65+
For stateful sessions, there is no default log level: no log messages will be sent
66+
until the client calls `SetLevel` (see below).
67+
For stateful sessions, the level defaults to "info".
68+
69+
[`ServerSession.Log`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ServerSession.Log) is the low-level way for servers to log to clients.
70+
It sends a logging notification to the client if the level of the message
71+
is at least the minimum log level.
72+
73+
For a simpler API, use [`NewLoggingHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#NewLoggingHandler) to obtain a [`slog.Handler`](https://pkg.go.dev/log/slog#Handler).
74+
By setting [`LoggingHandlerOptions.MinInterval`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#LoggingHandlerOptions.MinInterval), the handler can be rate-limited
75+
to avoid spamming clients with too many messages.
76+
77+
Servers always report the logging capability.
78+
79+
80+
**Client-side**:
81+
82+
Set [`ClientOptions.LoggingMessageHandler`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientOptions.LoggingMessageHandler) to receive log messages.
83+
84+
Call [`ClientSession.SetLevel`](https://pkg.go.dev/github.com/modelcontextprotocol/go-sdk/mcp#ClientSession.SetLevel) to change the log level for a session.
85+
86+
%include ../../mcp/server_example_test.go logging -
6087

6188
### Pagination
6289

mcp/logging.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ type LoggingHandlerOptions struct {
7070
// The value for the "logger" field of logging notifications.
7171
LoggerName string
7272
// Limits the rate at which log messages are sent.
73+
// Excess messages are dropped.
7374
// If zero, there is no rate limiting.
7475
MinInterval time.Duration
7576
}

mcp/server_example_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"context"
99
"fmt"
1010
"log"
11+
"log/slog"
12+
"time"
1113

1214
"github.com/modelcontextprotocol/go-sdk/mcp"
1315
)
@@ -82,3 +84,59 @@ func Example_prompts() {
8284
}
8385

8486
// !-prompts
87+
88+
// !+logging
89+
90+
func Example_logging() {
91+
ctx := context.Background()
92+
93+
// Create a server.
94+
s := mcp.NewServer(&mcp.Implementation{Name: "server", Version: "v0.0.1"}, nil)
95+
96+
// Create a client that displays log messages.
97+
c := mcp.NewClient(
98+
&mcp.Implementation{Name: "client", Version: "v0.0.1"},
99+
&mcp.ClientOptions{
100+
LoggingMessageHandler: func(_ context.Context, r *mcp.LoggingMessageRequest) {
101+
m := r.Params.Data.(map[string]any)
102+
fmt.Println(m["msg"], m["value"])
103+
},
104+
})
105+
106+
// Connect the server and client.
107+
t1, t2 := mcp.NewInMemoryTransports()
108+
ss, err := s.Connect(ctx, t1, nil)
109+
if err != nil {
110+
log.Fatal(err)
111+
}
112+
defer ss.Close()
113+
cs, err := c.Connect(ctx, t2, nil)
114+
if err != nil {
115+
log.Fatal(err)
116+
}
117+
defer cs.Close()
118+
119+
// Set the minimum log level to "info".
120+
if err := cs.SetLoggingLevel(ctx, &mcp.SetLoggingLevelParams{Level: "info"}); err != nil {
121+
log.Fatal(err)
122+
}
123+
124+
// Get a slog.Logger for the server session.
125+
logger := slog.New(mcp.NewLoggingHandler(ss, nil))
126+
127+
// Log some things.
128+
logger.Info("info shows up", "value", 1)
129+
logger.Debug("debug doesn't show up", "value", 2)
130+
logger.Warn("warn shows up", "value", 3)
131+
132+
// Wait for them to arrive on the client.
133+
// In a real application, the log messages would appear asynchronously
134+
// while other work was happening.
135+
time.Sleep(500 * time.Millisecond)
136+
137+
// Output:
138+
// info shows up 1
139+
// warn shows up 3
140+
}
141+
142+
// !-logging

0 commit comments

Comments
 (0)