@@ -174,7 +174,7 @@ connect to the server:
174174transport := &mcp.StreamableClientTransport {
175175 Endpoint : " http://localhost:8080/mcp" ,
176176}
177- client , err := mcp.Connect (context. Background () , transport, &mcp.ClientOptions {...})
177+ client , err := mcp.Connect (ctx , transport, &mcp.ClientOptions {...})
178178```
179179
180180The ` StreamableClientTransport ` handles the HTTP requests and communicates with
@@ -199,11 +199,12 @@ or server sends a notification, the spec says nothing about when the peer
199199observes that notification relative to other request. However, the Go SDK
200200implements the following heuristics:
201201
202- - If a notifying method (such as progress notification or
202+ - If a notifying method (such as ` notifications/ progress` or
203203 ` notifications/initialized ` ) returns, then it is guaranteed that the peer
204- observes that notification before other notifications or calls.
204+ observes that notification before other notifications or calls from the same
205+ client goroutine.
205206- Calls (such as ` tools/call ` ) are handled asynchronously with respect to
206- eachother .
207+ each other .
207208
208209See
209210[ modelcontextprotocol/go-sdk #26 ] ( https://github.com/modelcontextprotocol/go-sdk/issues/26 )
@@ -225,16 +226,70 @@ Cancellation is implemented with context cancellation. Cancelling a context
225226used in a method on ` ClientSession ` or ` ServerSession ` will terminate the RPC
226227and send a "notifications/cancelled" message to the peer.
227228
228- ``` go
229- ctx , cancel := context.WithCancel (context.Background ())
230- go cs.CallTool (ctx, &CallToolParams{Name: " slow" })
231- cancel () // cancel the tool call
232- ```
233-
234229When an RPC exits due to a cancellation error, there's a guarantee that the
235230cancellation notification has been sent, but there's no guarantee that the
236231server has observed it (see [ concurrency] ( #concurrency ) ).
237232
233+ ``` go
234+ func Example_cancellation () {
235+ // For this example, we're going to be collecting observations from the
236+ // server and client.
237+ var clientResult , serverResult string
238+ var wg sync.WaitGroup
239+ wg.Add (2 )
240+
241+ // Create a server with a single slow tool.
242+ // When the client cancels its request, the server should observe
243+ // cancellation.
244+ server := mcp.NewServer (&mcp.Implementation {Name: " server" , Version: " v0.0.1" }, nil )
245+ started := make (chan struct {}, 1 ) // signals that the server started handling the tool call
246+ mcp.AddTool (server, &mcp.Tool {Name: " slow" }, func (ctx context.Context , req *mcp.CallToolRequest , _ any) (*mcp.CallToolResult , any, error ) {
247+ started <- struct {}{}
248+ defer wg.Done ()
249+ select {
250+ case <- time.After (5 * time.Second ):
251+ serverResult = " tool done"
252+ case <- ctx.Done ():
253+ serverResult = " tool canceled"
254+ }
255+ return &mcp.CallToolResult {}, nil , nil
256+ })
257+
258+ // Connect a client to the server.
259+ client := mcp.NewClient (&mcp.Implementation {Name: " client" , Version: " v0.0.1" }, nil )
260+ ctx := context.Background ()
261+ t1 , t2 := mcp.NewInMemoryTransports ()
262+ if _ , err := server.Connect (ctx, t1, nil ); err != nil {
263+ log.Fatal (err)
264+ }
265+ session , err := client.Connect (ctx, t2, nil )
266+ if err != nil {
267+ log.Fatal (err)
268+ }
269+ defer session.Close ()
270+
271+ // Make a tool call, asynchronously.
272+ ctx , cancel := context.WithCancel (context.Background ())
273+ go func () {
274+ defer wg.Done ()
275+ _, err = session.CallTool (ctx, &mcp.CallToolParams {Name: " slow" })
276+ clientResult = fmt.Sprintf (" %v " , err)
277+ }()
278+
279+ // As soon as the server has started handling the call, cancel it from the
280+ // client side.
281+ <- started
282+ cancel ()
283+ wg.Wait ()
284+
285+ fmt.Println (clientResult)
286+ fmt.Println (serverResult)
287+ // Output:
288+ // context canceled
289+ // tool canceled
290+ }
291+ ```
292+
238293### Ping
239294
240295[ Ping] ( https://modelcontextprotocol.io/specification/2025-06-18/basic/utilities/ping )
@@ -270,13 +325,13 @@ Issue #460 discusses some potential ergonomic improvements to this API.
270325func Example_progress () {
271326 server := mcp.NewServer (&mcp.Implementation {Name: " server" , Version: " v0.0.1" }, nil )
272327 mcp.AddTool (server, &mcp.Tool {Name: " makeProgress" }, func (ctx context.Context , req *mcp.CallToolRequest , _ any) (*mcp.CallToolResult , any, error ) {
273- token , ok := req.Params .GetMeta ()[" progressToken" ]
274- if ok {
328+ if token := req.Params .GetProgressToken (); token != nil {
275329 for i := range 3 {
276330 params := &mcp.ProgressNotificationParams {
277- Message: fmt. Sprintf ( " progress %d " , i) ,
331+ Message: " frobbing widgets " ,
278332 ProgressToken: token,
279333 Progress: float64 (i),
334+ Total: 2 ,
280335 }
281336 req.Session .NotifyProgress (ctx, params) // ignore error
282337 }
@@ -285,7 +340,7 @@ func Example_progress() {
285340 })
286341 client := mcp.NewClient (&mcp.Implementation {Name: " client" , Version: " v0.0.1" }, &mcp.ClientOptions {
287342 ProgressNotificationHandler: func (_ context.Context , req *mcp.ProgressNotificationClientRequest ) {
288- fmt.Println ( req.Params .Message )
343+ fmt.Printf ( " %s %.0f / %.0f \n " , req.Params .Message , req. Params . Progress , req. Params . Total )
289344 },
290345 })
291346 ctx := context.Background ()
@@ -306,8 +361,8 @@ func Example_progress() {
306361 log.Fatal (err)
307362 }
308363 // Output:
309- // progress 0
310- // progress 1
311- // progress 2
364+ // frobbing widgets 0/2
365+ // frobbing widgets 1/2
366+ // frobbing widgets 2/ 2
312367}
313368```
0 commit comments