Skip to content

feat: add WithHTTPClient option for SSEMCPClient configuration #133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions client/sse.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ func WithHeaders(headers map[string]string) ClientOption {
}
}

func WithSSEReadTimeout(timeout time.Duration) ClientOption {
return func(sc *SSEMCPClient) {
sc.sseReadTimeout = timeout
}
}
Comment on lines +49 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Missing sseReadTimeout field in SSEMCPClient struct

The WithSSEReadTimeout function attempts to set sc.sseReadTimeout, but there's no corresponding field in the SSEMCPClient struct (lines 25-39). This will cause a compilation error.

You need to add the field to the struct definition:

type SSEMCPClient struct {
	baseURL       *url.URL
	endpoint      *url.URL
	httpClient    *http.Client
	requestID     atomic.Int64
	responses     map[int64]chan RPCResponse
	mu            sync.RWMutex
	done          chan struct{}
	initialized   bool
	notifications []func(mcp.JSONRPCNotification)
	notifyMu      sync.RWMutex
	endpointChan  chan struct{}
	capabilities  mcp.ServerCapabilities
	headers       map[string]string
+	sseReadTimeout time.Duration
}

Additionally, the sseReadTimeout field is never used in the implementation. You should add timeout handling in the readSSE function to actually use this configuration option.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func WithSSEReadTimeout(timeout time.Duration) ClientOption {
return func(sc *SSEMCPClient) {
sc.sseReadTimeout = timeout
}
}
type SSEMCPClient struct {
baseURL *url.URL
endpoint *url.URL
httpClient *http.Client
requestID atomic.Int64
responses map[int64]chan RPCResponse
mu sync.RWMutex
done chan struct{}
initialized bool
notifications []func(mcp.JSONRPCNotification)
notifyMu sync.RWMutex
endpointChan chan struct{}
capabilities mcp.ServerCapabilities
headers map[string]string
sseReadTimeout time.Duration
}


func WithHTTPClient(client *http.Client) ClientOption {
return func(sc *SSEMCPClient) {
sc.httpClient = client
}
}
Comment on lines +49 to +59
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider implementing the read timeout functionality

After adding the sseReadTimeout field to the struct, you should modify the readSSE function to actually use this timeout. The current implementation does not utilize the timeout value.

Here's a suggested implementation for using the timeout in the readSSE function:

func (c *SSEMCPClient) readSSE(reader io.ReadCloser) {
	defer reader.Close()

	br := bufio.NewReader(reader)
	var event, data string

	for {
		select {
		case <-c.done:
			return
		default:
+			// Set read deadline if timeout is specified
+			if c.sseReadTimeout > 0 {
+				if deadline, ok := reader.(interface{ SetReadDeadline(time.Time) error }); ok {
+					_ = deadline.SetReadDeadline(time.Now().Add(c.sseReadTimeout))
+				}
+			}
			line, err := br.ReadString('\n')
			if err != nil {
				// ...existing error handling...
			}
			// ...rest of the function...
		}
	}
}

Note that this implementation handles the case where the reader supports setting deadlines via the SetReadDeadline method. Not all readers support this, so it's implemented as a type assertion.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func WithSSEReadTimeout(timeout time.Duration) ClientOption {
return func(sc *SSEMCPClient) {
sc.sseReadTimeout = timeout
}
}
func WithHTTPClient(client *http.Client) ClientOption {
return func(sc *SSEMCPClient) {
sc.httpClient = client
}
}
func (c *SSEMCPClient) readSSE(reader io.ReadCloser) {
defer reader.Close()
br := bufio.NewReader(reader)
var event, data string
for {
select {
case <-c.done:
return
default:
// Set read deadline if timeout is specified
if c.sseReadTimeout > 0 {
if deadline, ok := reader.(interface{ SetReadDeadline(time.Time) error }); ok {
_ = deadline.SetReadDeadline(time.Now().Add(c.sseReadTimeout))
}
}
line, err := br.ReadString('\n')
if err != nil {
// ...existing error handling...
}
// ...rest of the function...
}
}
}


// NewSSEMCPClient creates a new SSE-based MCP client with the given base URL.
// Returns an error if the URL is invalid.
func NewSSEMCPClient(baseURL string, options ...ClientOption) (*SSEMCPClient, error) {
Expand Down