@@ -23,20 +23,19 @@ import (
23
23
// while sending requests over regular HTTP POST calls. The client handles
24
24
// automatic reconnection and message routing between requests and responses.
25
25
type SSEMCPClient struct {
26
- baseURL * url.URL
27
- endpoint * url.URL
28
- httpClient * http.Client
29
- requestID atomic.Int64
30
- responses map [int64 ]chan RPCResponse
31
- mu sync.RWMutex
32
- done chan struct {}
33
- initialized bool
34
- notifications []func (mcp.JSONRPCNotification )
35
- notifyMu sync.RWMutex
36
- endpointChan chan struct {}
37
- capabilities mcp.ServerCapabilities
38
- headers map [string ]string
39
- sseReadTimeout time.Duration
26
+ baseURL * url.URL
27
+ endpoint * url.URL
28
+ httpClient * http.Client
29
+ requestID atomic.Int64
30
+ responses map [int64 ]chan RPCResponse
31
+ mu sync.RWMutex
32
+ done chan struct {}
33
+ initialized bool
34
+ notifications []func (mcp.JSONRPCNotification )
35
+ notifyMu sync.RWMutex
36
+ endpointChan chan struct {}
37
+ capabilities mcp.ServerCapabilities
38
+ headers map [string ]string
40
39
}
41
40
42
41
type ClientOption func (* SSEMCPClient )
@@ -47,12 +46,6 @@ func WithHeaders(headers map[string]string) ClientOption {
47
46
}
48
47
}
49
48
50
- func WithSSEReadTimeout (timeout time.Duration ) ClientOption {
51
- return func (sc * SSEMCPClient ) {
52
- sc .sseReadTimeout = timeout
53
- }
54
- }
55
-
56
49
// NewSSEMCPClient creates a new SSE-based MCP client with the given base URL.
57
50
// Returns an error if the URL is invalid.
58
51
func NewSSEMCPClient (baseURL string , options ... ClientOption ) (* SSEMCPClient , error ) {
@@ -62,13 +55,12 @@ func NewSSEMCPClient(baseURL string, options ...ClientOption) (*SSEMCPClient, er
62
55
}
63
56
64
57
smc := & SSEMCPClient {
65
- baseURL : parsedURL ,
66
- httpClient : & http.Client {},
67
- responses : make (map [int64 ]chan RPCResponse ),
68
- done : make (chan struct {}),
69
- endpointChan : make (chan struct {}),
70
- sseReadTimeout : 30 * time .Second ,
71
- headers : make (map [string ]string ),
58
+ baseURL : parsedURL ,
59
+ httpClient : & http.Client {},
60
+ responses : make (map [int64 ]chan RPCResponse ),
61
+ done : make (chan struct {}),
62
+ endpointChan : make (chan struct {}),
63
+ headers : make (map [string ]string ),
72
64
}
73
65
74
66
for _ , opt := range options {
@@ -93,6 +85,9 @@ func (c *SSEMCPClient) Start(ctx context.Context) error {
93
85
req .Header .Set ("Accept" , "text/event-stream" )
94
86
req .Header .Set ("Cache-Control" , "no-cache" )
95
87
req .Header .Set ("Connection" , "keep-alive" )
88
+ for k , v := range c .headers {
89
+ req .Header .Set (k , v )
90
+ }
96
91
97
92
resp , err := c .httpClient .Do (req )
98
93
if err != nil {
@@ -128,12 +123,9 @@ func (c *SSEMCPClient) readSSE(reader io.ReadCloser) {
128
123
br := bufio .NewReader (reader )
129
124
var event , data string
130
125
131
- ctx , cancel := context .WithTimeout (context .Background (), c .sseReadTimeout )
132
- defer cancel ()
133
-
134
126
for {
135
127
select {
136
- case <- ctx . Done () :
128
+ case <- c . done :
137
129
return
138
130
default :
139
131
line , err := br .ReadString ('\n' )
@@ -399,7 +391,7 @@ func (c *SSEMCPClient) Initialize(
399
391
err ,
400
392
)
401
393
}
402
- resp .Body .Close ()
394
+ defer resp .Body .Close ()
403
395
404
396
c .initialized = true
405
397
return & result , nil
@@ -410,42 +402,77 @@ func (c *SSEMCPClient) Ping(ctx context.Context) error {
410
402
return err
411
403
}
412
404
413
- func (c * SSEMCPClient ) ListResources (
405
+ // ListResourcesByPage manually list resources by page.
406
+ func (c * SSEMCPClient ) ListResourcesByPage (
414
407
ctx context.Context ,
415
408
request mcp.ListResourcesRequest ,
416
409
) (* mcp.ListResourcesResult , error ) {
417
- response , err := c . sendRequest (ctx , "resources/list" , request . Params )
410
+ result , err := listByPage [mcp. ListResourcesResult ] (ctx , c , request . PaginatedRequest , "resources/list" )
418
411
if err != nil {
419
412
return nil , err
420
413
}
414
+ return result , nil
415
+ }
421
416
422
- var result mcp.ListResourcesResult
423
- if err := json .Unmarshal (* response , & result ); err != nil {
424
- return nil , fmt .Errorf ("failed to unmarshal response: %w" , err )
417
+ func (c * SSEMCPClient ) ListResources (
418
+ ctx context.Context ,
419
+ request mcp.ListResourcesRequest ,
420
+ ) (* mcp.ListResourcesResult , error ) {
421
+ result , err := c .ListResourcesByPage (ctx , request )
422
+ if err != nil {
423
+ return nil , err
425
424
}
425
+ for result .NextCursor != "" {
426
+ select {
427
+ case <- ctx .Done ():
428
+ return nil , ctx .Err ()
429
+ default :
430
+ request .Params .Cursor = result .NextCursor
431
+ newPageRes , err := c .ListResourcesByPage (ctx , request )
432
+ if err != nil {
433
+ return nil , err
434
+ }
435
+ result .Resources = append (result .Resources , newPageRes .Resources ... )
436
+ result .NextCursor = newPageRes .NextCursor
437
+ }
438
+ }
439
+ return result , nil
440
+ }
426
441
427
- return & result , nil
442
+ func (c * SSEMCPClient ) ListResourceTemplatesByPage (
443
+ ctx context.Context ,
444
+ request mcp.ListResourceTemplatesRequest ,
445
+ ) (* mcp.ListResourceTemplatesResult , error ) {
446
+ result , err := listByPage [mcp.ListResourceTemplatesResult ](ctx , c , request .PaginatedRequest , "resources/templates/list" )
447
+ if err != nil {
448
+ return nil , err
449
+ }
450
+ return result , nil
428
451
}
429
452
430
453
func (c * SSEMCPClient ) ListResourceTemplates (
431
454
ctx context.Context ,
432
455
request mcp.ListResourceTemplatesRequest ,
433
456
) (* mcp.ListResourceTemplatesResult , error ) {
434
- response , err := c .sendRequest (
435
- ctx ,
436
- "resources/templates/list" ,
437
- request .Params ,
438
- )
457
+ result , err := c .ListResourceTemplatesByPage (ctx , request )
439
458
if err != nil {
440
459
return nil , err
441
460
}
442
-
443
- var result mcp.ListResourceTemplatesResult
444
- if err := json .Unmarshal (* response , & result ); err != nil {
445
- return nil , fmt .Errorf ("failed to unmarshal response: %w" , err )
461
+ for result .NextCursor != "" {
462
+ select {
463
+ case <- ctx .Done ():
464
+ return nil , ctx .Err ()
465
+ default :
466
+ request .Params .Cursor = result .NextCursor
467
+ newPageRes , err := c .ListResourceTemplatesByPage (ctx , request )
468
+ if err != nil {
469
+ return nil , err
470
+ }
471
+ result .ResourceTemplates = append (result .ResourceTemplates , newPageRes .ResourceTemplates ... )
472
+ result .NextCursor = newPageRes .NextCursor
473
+ }
446
474
}
447
-
448
- return & result , nil
475
+ return result , nil
449
476
}
450
477
451
478
func (c * SSEMCPClient ) ReadResource (
@@ -476,21 +503,40 @@ func (c *SSEMCPClient) Unsubscribe(
476
503
return err
477
504
}
478
505
479
- func (c * SSEMCPClient ) ListPrompts (
506
+ func (c * SSEMCPClient ) ListPromptsByPage (
480
507
ctx context.Context ,
481
508
request mcp.ListPromptsRequest ,
482
509
) (* mcp.ListPromptsResult , error ) {
483
- response , err := c . sendRequest (ctx , "prompts/list" , request . Params )
510
+ result , err := listByPage [mcp. ListPromptsResult ] (ctx , c , request . PaginatedRequest , "prompts/list" )
484
511
if err != nil {
485
512
return nil , err
486
513
}
514
+ return result , nil
515
+ }
487
516
488
- var result mcp.ListPromptsResult
489
- if err := json .Unmarshal (* response , & result ); err != nil {
490
- return nil , fmt .Errorf ("failed to unmarshal response: %w" , err )
517
+ func (c * SSEMCPClient ) ListPrompts (
518
+ ctx context.Context ,
519
+ request mcp.ListPromptsRequest ,
520
+ ) (* mcp.ListPromptsResult , error ) {
521
+ result , err := c .ListPromptsByPage (ctx , request )
522
+ if err != nil {
523
+ return nil , err
491
524
}
492
-
493
- return & result , nil
525
+ for result .NextCursor != "" {
526
+ select {
527
+ case <- ctx .Done ():
528
+ return nil , ctx .Err ()
529
+ default :
530
+ request .Params .Cursor = result .NextCursor
531
+ newPageRes , err := c .ListPromptsByPage (ctx , request )
532
+ if err != nil {
533
+ return nil , err
534
+ }
535
+ result .Prompts = append (result .Prompts , newPageRes .Prompts ... )
536
+ result .NextCursor = newPageRes .NextCursor
537
+ }
538
+ }
539
+ return result , nil
494
540
}
495
541
496
542
func (c * SSEMCPClient ) GetPrompt (
@@ -505,21 +551,40 @@ func (c *SSEMCPClient) GetPrompt(
505
551
return mcp .ParseGetPromptResult (response )
506
552
}
507
553
508
- func (c * SSEMCPClient ) ListTools (
554
+ func (c * SSEMCPClient ) ListToolsByPage (
509
555
ctx context.Context ,
510
556
request mcp.ListToolsRequest ,
511
557
) (* mcp.ListToolsResult , error ) {
512
- response , err := c . sendRequest (ctx , "tools/list" , request . Params )
558
+ result , err := listByPage [mcp. ListToolsResult ] (ctx , c , request . PaginatedRequest , "tools/list" )
513
559
if err != nil {
514
560
return nil , err
515
561
}
562
+ return result , nil
563
+ }
516
564
517
- var result mcp.ListToolsResult
518
- if err := json .Unmarshal (* response , & result ); err != nil {
519
- return nil , fmt .Errorf ("failed to unmarshal response: %w" , err )
565
+ func (c * SSEMCPClient ) ListTools (
566
+ ctx context.Context ,
567
+ request mcp.ListToolsRequest ,
568
+ ) (* mcp.ListToolsResult , error ) {
569
+ result , err := c .ListToolsByPage (ctx , request )
570
+ if err != nil {
571
+ return nil , err
520
572
}
521
-
522
- return & result , nil
573
+ for result .NextCursor != "" {
574
+ select {
575
+ case <- ctx .Done ():
576
+ return nil , ctx .Err ()
577
+ default :
578
+ request .Params .Cursor = result .NextCursor
579
+ newPageRes , err := c .ListToolsByPage (ctx , request )
580
+ if err != nil {
581
+ return nil , err
582
+ }
583
+ result .Tools = append (result .Tools , newPageRes .Tools ... )
584
+ result .NextCursor = newPageRes .NextCursor
585
+ }
586
+ }
587
+ return result , nil
523
588
}
524
589
525
590
func (c * SSEMCPClient ) CallTool (
0 commit comments