Skip to content

Commit 830de9f

Browse files
committed
feat: add tests and documentation for client state persistence and restoration
1 parent ab5e0e0 commit 830de9f

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

client/client_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/mark3labs/mcp-go/mcp"
8+
"github.com/mark3labs/mcp-go/server"
9+
)
10+
11+
func TestStateRestoration(t *testing.T) {
12+
mcpServer := server.NewMCPServer(
13+
"test-server",
14+
"1.0.0",
15+
server.WithToolCapabilities(true),
16+
)
17+
18+
// First client lifecycle
19+
client1, err := NewInProcessClient(mcpServer)
20+
if err != nil {
21+
t.Fatalf("Failed to create first client: %v", err)
22+
}
23+
24+
ctx := context.Background()
25+
26+
if err := client1.Start(ctx); err != nil {
27+
t.Fatalf("Failed to start first client: %v", err)
28+
}
29+
30+
initRequest := mcp.InitializeRequest{}
31+
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
32+
initRequest.Params.ClientInfo = mcp.Implementation{
33+
Name: "test-client",
34+
Version: "1.0.0",
35+
}
36+
37+
if _, err := client1.Initialize(ctx, initRequest); err != nil {
38+
t.Fatalf("Failed to initialize first client: %v", err)
39+
}
40+
41+
if err := client1.Ping(ctx); err != nil {
42+
t.Fatalf("Ping on first client failed: %v", err)
43+
}
44+
45+
exportedState := client1.ExportState()
46+
47+
if err := client1.Close(); err != nil {
48+
t.Fatalf("Failed to close first client: %v", err)
49+
}
50+
51+
// Second client lifecycle (state restoration)
52+
client2, err := NewInProcessClient(mcpServer)
53+
if err != nil {
54+
t.Fatalf("Failed to create second client: %v", err)
55+
}
56+
defer client2.Close()
57+
58+
if err := client2.Start(ctx); err != nil {
59+
t.Fatalf("Failed to start second client: %v", err)
60+
}
61+
62+
if err := client2.ImportState(ctx, exportedState); err != nil {
63+
t.Fatalf("Failed to import state into second client: %v", err)
64+
}
65+
66+
if err := client2.Ping(ctx); err != nil {
67+
t.Fatalf("Ping on restored client failed: %v", err)
68+
}
69+
70+
restoredState := client2.ExportState()
71+
72+
if !restoredState.Initialized {
73+
t.Errorf("Expected restored client to be initialized")
74+
}
75+
76+
if restoredState.RequestID <= exportedState.RequestID {
77+
t.Errorf("Expected request ID to increase after restoration; before=%d, after=%d", exportedState.RequestID, restoredState.RequestID)
78+
}
79+
}

www/docs/pages/clients/basics.mdx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,41 @@ func (mc *ManagedClient) Close() error {
297297
}
298298
```
299299

300+
### Persisting & Restoring Client State
301+
302+
MCP-Go lets you snapshot a running client and resume it later — even in a new
303+
process — with `ExportState()` and `ImportState()`.
304+
305+
```go
306+
// Save state before shutting down
307+
state := client.ExportState()
308+
if err := client.Close(); err != nil {
309+
log.Printf("close failed: %v", err)
310+
}
311+
312+
// Later, or in another process
313+
restoredClient, _ := client.NewStreamableHttpClient(baseURL)
314+
_ = restoredClient.Start(ctx) // transport must be running
315+
if err := restoredClient.ImportState(ctx, state); err != nil {
316+
log.Fatal(err)
317+
}
318+
319+
// Continue exactly where you left off
320+
if err := restoredClient.Ping(ctx); err != nil {
321+
log.Fatal(err)
322+
}
323+
```
324+
325+
`ExportState()` captures:
326+
327+
* initialization status
328+
* last JSON-RPC request ID (so IDs continue monotonically)
329+
* negotiated client / server capabilities
330+
* HTTP session ID (for StreamableHTTP transports)
331+
332+
`ImportState()` re-hydrates those values. After a successful call the
333+
restored client can immediately issue normal requests without re-initializing.
334+
300335
## Error Handling
301336

302337
Proper error handling is essential for robust client applications.

0 commit comments

Comments
 (0)