From b95458b055ac886860b77f96c4cb2ba86edaa72f Mon Sep 17 00:00:00 2001 From: Jinlkj Date: Thu, 3 Apr 2025 21:26:44 +0800 Subject: [PATCH 1/4] add pagination functionality --- client/client.go | 46 ++++++++++++++ client/sse.go | 135 ++++++++++++++++++++++++++++++--------- client/stdio.go | 145 ++++++++++++++++++++++++++++++------------ server/server.go | 128 ++++++++++++++++++++++++++++++------- server/server_test.go | 2 +- 5 files changed, 362 insertions(+), 94 deletions(-) diff --git a/client/client.go b/client/client.go index 1d3cb1051..cdea0201d 100644 --- a/client/client.go +++ b/client/client.go @@ -3,6 +3,8 @@ package client import ( "context" + "encoding/json" + "fmt" "github.com/mark3labs/mcp-go/mcp" ) @@ -18,12 +20,25 @@ type MCPClient interface { // Ping checks if the server is alive Ping(ctx context.Context) error + // ListResourcesByPage manually list resources by page. + ListResourcesByPage( + ctx context.Context, + request mcp.ListResourcesRequest, + ) (*mcp.ListResourcesResult, error) + // ListResources requests a list of available resources from the server ListResources( ctx context.Context, request mcp.ListResourcesRequest, ) (*mcp.ListResourcesResult, error) + // ListResourceTemplatesByPage manually list resource templates by page. + ListResourceTemplatesByPage( + ctx context.Context, + request mcp.ListResourceTemplatesRequest, + ) (*mcp.ListResourceTemplatesResult, + error) + // ListResourceTemplates requests a list of available resource templates from the server ListResourceTemplates( ctx context.Context, @@ -43,6 +58,12 @@ type MCPClient interface { // Unsubscribe cancels notifications for a specific resource Unsubscribe(ctx context.Context, request mcp.UnsubscribeRequest) error + // ListPromptsByPage manually list prompts by page. + ListPromptsByPage( + ctx context.Context, + request mcp.ListPromptsRequest, + ) (*mcp.ListPromptsResult, error) + // ListPrompts requests a list of available prompts from the server ListPrompts( ctx context.Context, @@ -55,6 +76,12 @@ type MCPClient interface { request mcp.GetPromptRequest, ) (*mcp.GetPromptResult, error) + // ListToolsByPage manually list tools by page. + ListToolsByPage( + ctx context.Context, + request mcp.ListToolsRequest, + ) (*mcp.ListToolsResult, error) + // ListTools requests a list of available tools from the server ListTools( ctx context.Context, @@ -81,4 +108,23 @@ type MCPClient interface { // OnNotification registers a handler for notifications OnNotification(handler func(notification mcp.JSONRPCNotification)) + + sendRequest(ctx context.Context, method string, params interface{}) (*json.RawMessage, error) +} + +func listByPage[T any]( + ctx context.Context, + client MCPClient, + request mcp.PaginatedRequest, + method string, +) (*T, error) { + response, err := client.sendRequest(ctx, method, request.Params) + if err != nil { + return nil, err + } + var result T + if err := json.Unmarshal(*response, &result); err != nil { + return nil, fmt.Errorf("failed to unmarshal response: %w", err) + } + return &result, nil } diff --git a/client/sse.go b/client/sse.go index cf4a1028e..d5b85539c 100644 --- a/client/sse.go +++ b/client/sse.go @@ -410,42 +410,77 @@ func (c *SSEMCPClient) Ping(ctx context.Context) error { return err } -func (c *SSEMCPClient) ListResources( +// ListResourcesByPage manually list resources by page. +func (c *SSEMCPClient) ListResourcesByPage( ctx context.Context, request mcp.ListResourcesRequest, ) (*mcp.ListResourcesResult, error) { - response, err := c.sendRequest(ctx, "resources/list", request.Params) + result, err := listByPage[mcp.ListResourcesResult](ctx, c, request.PaginatedRequest, "resources/list") if err != nil { return nil, err } + return result, nil +} - var result mcp.ListResourcesResult - if err := json.Unmarshal(*response, &result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) +func (c *SSEMCPClient) ListResources( + ctx context.Context, + request mcp.ListResourcesRequest, +) (*mcp.ListResourcesResult, error) { + result, err := c.ListResourcesByPage(ctx, request) + if err != nil { + return nil, err } + for result.NextCursor != "" { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + request.Params.Cursor = result.NextCursor + newPageRes, err := c.ListResourcesByPage(ctx, request) + if err != nil { + return nil, err + } + result.Resources = append(result.Resources, newPageRes.Resources...) + result.NextCursor = newPageRes.NextCursor + } + } + return result, nil +} - return &result, nil +func (c *SSEMCPClient) ListResourceTemplatesByPage( + ctx context.Context, + request mcp.ListResourceTemplatesRequest, +) (*mcp.ListResourceTemplatesResult, error) { + result, err := listByPage[mcp.ListResourceTemplatesResult](ctx, c, request.PaginatedRequest, "resources/templates/list") + if err != nil { + return nil, err + } + return result, nil } func (c *SSEMCPClient) ListResourceTemplates( ctx context.Context, request mcp.ListResourceTemplatesRequest, ) (*mcp.ListResourceTemplatesResult, error) { - response, err := c.sendRequest( - ctx, - "resources/templates/list", - request.Params, - ) + result, err := c.ListResourceTemplatesByPage(ctx, request) if err != nil { return nil, err } - - var result mcp.ListResourceTemplatesResult - if err := json.Unmarshal(*response, &result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) + for result.NextCursor != "" { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + request.Params.Cursor = result.NextCursor + newPageRes, err := c.ListResourceTemplatesByPage(ctx, request) + if err != nil { + return nil, err + } + result.ResourceTemplates = append(result.ResourceTemplates, newPageRes.ResourceTemplates...) + result.NextCursor = newPageRes.NextCursor + } } - - return &result, nil + return result, nil } func (c *SSEMCPClient) ReadResource( @@ -476,21 +511,40 @@ func (c *SSEMCPClient) Unsubscribe( return err } -func (c *SSEMCPClient) ListPrompts( +func (c *SSEMCPClient) ListPromptsByPage( ctx context.Context, request mcp.ListPromptsRequest, ) (*mcp.ListPromptsResult, error) { - response, err := c.sendRequest(ctx, "prompts/list", request.Params) + result, err := listByPage[mcp.ListPromptsResult](ctx, c, request.PaginatedRequest, "prompts/list") if err != nil { return nil, err } + return result, nil +} - var result mcp.ListPromptsResult - if err := json.Unmarshal(*response, &result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) +func (c *SSEMCPClient) ListPrompts( + ctx context.Context, + request mcp.ListPromptsRequest, +) (*mcp.ListPromptsResult, error) { + result, err := c.ListPromptsByPage(ctx, request) + if err != nil { + return nil, err } - - return &result, nil + for result.NextCursor != "" { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + request.Params.Cursor = result.NextCursor + newPageRes, err := c.ListPromptsByPage(ctx, request) + if err != nil { + return nil, err + } + result.Prompts = append(result.Prompts, newPageRes.Prompts...) + result.NextCursor = newPageRes.NextCursor + } + } + return result, nil } func (c *SSEMCPClient) GetPrompt( @@ -505,21 +559,40 @@ func (c *SSEMCPClient) GetPrompt( return mcp.ParseGetPromptResult(response) } -func (c *SSEMCPClient) ListTools( +func (c *SSEMCPClient) ListToolsByPage( ctx context.Context, request mcp.ListToolsRequest, ) (*mcp.ListToolsResult, error) { - response, err := c.sendRequest(ctx, "tools/list", request.Params) + result, err := listByPage[mcp.ListToolsResult](ctx, c, request.PaginatedRequest, "tools/list") if err != nil { return nil, err } + return result, nil +} - var result mcp.ListToolsResult - if err := json.Unmarshal(*response, &result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) +func (c *SSEMCPClient) ListTools( + ctx context.Context, + request mcp.ListToolsRequest, +) (*mcp.ListToolsResult, error) { + result, err := c.ListToolsByPage(ctx, request) + if err != nil { + return nil, err } - - return &result, nil + for result.NextCursor != "" { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + request.Params.Cursor = result.NextCursor + newPageRes, err := c.ListToolsByPage(ctx, request) + if err != nil { + return nil, err + } + result.Tools = append(result.Tools, newPageRes.Tools...) + result.NextCursor = newPageRes.NextCursor + } + } + return result, nil } func (c *SSEMCPClient) CallTool( diff --git a/client/stdio.go b/client/stdio.go index 8e0845dca..c92334928 100644 --- a/client/stdio.go +++ b/client/stdio.go @@ -300,48 +300,77 @@ func (c *StdioMCPClient) Initialize( return &result, nil } -func (c *StdioMCPClient) ListResources( +// ListResourcesByPage manually list resources by page. +func (c *StdioMCPClient) ListResourcesByPage( ctx context.Context, request mcp.ListResourcesRequest, -) (*mcp. - ListResourcesResult, error) { - response, err := c.sendRequest( - ctx, - "resources/list", - request.Params, - ) +) (*mcp.ListResourcesResult, error) { + result, err := listByPage[mcp.ListResourcesResult](ctx, c, request.PaginatedRequest, "resources/list") if err != nil { return nil, err } + return result, nil +} - var result mcp.ListResourcesResult - if err := json.Unmarshal(*response, &result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) +func (c *StdioMCPClient) ListResources( + ctx context.Context, + request mcp.ListResourcesRequest, +) (*mcp.ListResourcesResult, error) { + result, err := c.ListResourcesByPage(ctx, request) + if err != nil { + return nil, err } + for result.NextCursor != "" { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + request.Params.Cursor = result.NextCursor + newPageRes, err := c.ListResourcesByPage(ctx, request) + if err != nil { + return nil, err + } + result.Resources = append(result.Resources, newPageRes.Resources...) + result.NextCursor = newPageRes.NextCursor + } + } + return result, nil +} - return &result, nil +func (c *StdioMCPClient) ListResourceTemplatesByPage( + ctx context.Context, + request mcp.ListResourceTemplatesRequest, +) (*mcp.ListResourceTemplatesResult, error) { + result, err := listByPage[mcp.ListResourceTemplatesResult](ctx, c, request.PaginatedRequest, "resources/templates/list") + if err != nil { + return nil, err + } + return result, nil } func (c *StdioMCPClient) ListResourceTemplates( ctx context.Context, request mcp.ListResourceTemplatesRequest, -) (*mcp. - ListResourceTemplatesResult, error) { - response, err := c.sendRequest( - ctx, - "resources/templates/list", - request.Params, - ) +) (*mcp.ListResourceTemplatesResult, error) { + result, err := c.ListResourceTemplatesByPage(ctx, request) if err != nil { return nil, err } - - var result mcp.ListResourceTemplatesResult - if err := json.Unmarshal(*response, &result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) + for result.NextCursor != "" { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + request.Params.Cursor = result.NextCursor + newPageRes, err := c.ListResourceTemplatesByPage(ctx, request) + if err != nil { + return nil, err + } + result.ResourceTemplates = append(result.ResourceTemplates, newPageRes.ResourceTemplates...) + result.NextCursor = newPageRes.NextCursor + } } - - return &result, nil + return result, nil } func (c *StdioMCPClient) ReadResource( @@ -373,21 +402,40 @@ func (c *StdioMCPClient) Unsubscribe( return err } -func (c *StdioMCPClient) ListPrompts( +func (c *StdioMCPClient) ListPromptsByPage( ctx context.Context, request mcp.ListPromptsRequest, ) (*mcp.ListPromptsResult, error) { - response, err := c.sendRequest(ctx, "prompts/list", request.Params) + result, err := listByPage[mcp.ListPromptsResult](ctx, c, request.PaginatedRequest, "prompts/list") if err != nil { return nil, err } + return result, nil +} - var result mcp.ListPromptsResult - if err := json.Unmarshal(*response, &result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) +func (c *StdioMCPClient) ListPrompts( + ctx context.Context, + request mcp.ListPromptsRequest, +) (*mcp.ListPromptsResult, error) { + result, err := c.ListPromptsByPage(ctx, request) + if err != nil { + return nil, err } - - return &result, nil + for result.NextCursor != "" { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + request.Params.Cursor = result.NextCursor + newPageRes, err := c.ListPromptsByPage(ctx, request) + if err != nil { + return nil, err + } + result.Prompts = append(result.Prompts, newPageRes.Prompts...) + result.NextCursor = newPageRes.NextCursor + } + } + return result, nil } func (c *StdioMCPClient) GetPrompt( @@ -402,21 +450,40 @@ func (c *StdioMCPClient) GetPrompt( return mcp.ParseGetPromptResult(response) } -func (c *StdioMCPClient) ListTools( +func (c *StdioMCPClient) ListToolsByPage( ctx context.Context, request mcp.ListToolsRequest, ) (*mcp.ListToolsResult, error) { - response, err := c.sendRequest(ctx, "tools/list", request.Params) + result, err := listByPage[mcp.ListToolsResult](ctx, c, request.PaginatedRequest, "tools/list") if err != nil { return nil, err } + return result, nil +} - var result mcp.ListToolsResult - if err := json.Unmarshal(*response, &result); err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) +func (c *StdioMCPClient) ListTools( + ctx context.Context, + request mcp.ListToolsRequest, +) (*mcp.ListToolsResult, error) { + result, err := c.ListToolsByPage(ctx, request) + if err != nil { + return nil, err } - - return &result, nil + for result.NextCursor != "" { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + request.Params.Cursor = result.NextCursor + newPageRes, err := c.ListToolsByPage(ctx, request) + if err != nil { + return nil, err + } + result.Tools = append(result.Tools, newPageRes.Tools...) + result.NextCursor = newPageRes.NextCursor + } + } + return result, nil } func (c *StdioMCPClient) CallTool( diff --git a/server/server.go b/server/server.go index ec4fcef00..14ecc8787 100644 --- a/server/server.go +++ b/server/server.go @@ -3,9 +3,11 @@ package server import ( "context" + "encoding/base64" "encoding/json" "errors" "fmt" + "reflect" "sort" "sync" @@ -147,6 +149,7 @@ type MCPServer struct { tools map[string]ServerTool notificationHandlers map[string]NotificationHandlerFunc capabilities serverCapabilities + paginationLimit *int sessions sync.Map hooks *Hooks } @@ -162,6 +165,13 @@ func ServerFromContext(ctx context.Context) *MCPServer { return nil } +// WithPaginationLimit sets the pagination limit for the server. +func WithPaginationLimit(limit int) ServerOption { + return func(s *MCPServer) { + s.paginationLimit = &limit + } +} + // WithContext sets the current client session and returns the provided context func (s *MCPServer) WithContext( ctx context.Context, @@ -510,6 +520,42 @@ func (s *MCPServer) handlePing( return &mcp.EmptyResult{}, nil } +func listByPagination[T any]( + ctx context.Context, + s *MCPServer, + cursor mcp.Cursor, + allElements []T, +) ([]T, mcp.Cursor, error) { + startPos := 0 + if cursor != "" { + c, err := base64.StdEncoding.DecodeString(string(cursor)) + if err != nil { + return nil, "", err + } + cString := string(c) + startPos = sort.Search(len(allElements), func(i int) bool { + return reflect.ValueOf(allElements[i]).FieldByName("Name").String() > cString + }) + } + endPos := len(allElements) + if s.paginationLimit != nil { + if len(allElements) > startPos+*s.paginationLimit { + endPos = startPos + *s.paginationLimit + } + } + elementsToReturn := allElements[startPos:endPos] + // set the next cursor + nextCursor := func() mcp.Cursor { + if s.paginationLimit != nil && len(elementsToReturn) >= *s.paginationLimit { + nc := reflect.ValueOf(elementsToReturn[len(elementsToReturn)-1]).FieldByName("Name").String() + toString := base64.StdEncoding.EncodeToString([]byte(nc)) + return mcp.Cursor(toString) + } + return "" + }() + return elementsToReturn, nextCursor, nil +} + func (s *MCPServer) handleListResources( ctx context.Context, id interface{}, @@ -522,11 +568,23 @@ func (s *MCPServer) handleListResources( } s.mu.RUnlock() - result := mcp.ListResourcesResult{ - Resources: resources, + // Sort the resources by name + sort.Slice(resources, func(i, j int) bool { + return resources[i].Name < resources[j].Name + }) + resourcesToReturn, nextCursor, err := listByPagination[mcp.Resource](ctx, s, request.Params.Cursor, resources) + if err != nil { + return nil, &requestError{ + id: id, + code: mcp.INVALID_PARAMS, + err: err, + } } - if request.Params.Cursor != "" { - result.NextCursor = "" // Handle pagination if needed + result := mcp.ListResourcesResult{ + Resources: resourcesToReturn, + PaginatedResult: mcp.PaginatedResult{ + NextCursor: nextCursor, + }, } return &result, nil } @@ -542,12 +600,22 @@ func (s *MCPServer) handleListResourceTemplates( templates = append(templates, entry.template) } s.mu.RUnlock() - - result := mcp.ListResourceTemplatesResult{ - ResourceTemplates: templates, + sort.Slice(templates, func(i, j int) bool { + return templates[i].Name < templates[j].Name + }) + templatesToReturn, nextCursor, err := listByPagination[mcp.ResourceTemplate](ctx, s, request.Params.Cursor, templates) + if err != nil { + return nil, &requestError{ + id: id, + code: mcp.INVALID_PARAMS, + err: err, + } } - if request.Params.Cursor != "" { - result.NextCursor = "" // Handle pagination if needed + result := mcp.ListResourceTemplatesResult{ + ResourceTemplates: templatesToReturn, + PaginatedResult: mcp.PaginatedResult{ + NextCursor: nextCursor, + }, } return &result, nil } @@ -628,11 +696,23 @@ func (s *MCPServer) handleListPrompts( } s.mu.RUnlock() - result := mcp.ListPromptsResult{ - Prompts: prompts, + // sort prompts by name + sort.Slice(prompts, func(i, j int) bool { + return prompts[i].Name < prompts[j].Name + }) + promptsToReturn, nextCursor, err := listByPagination[mcp.Prompt](ctx, s, request.Params.Cursor, prompts) + if err != nil { + return nil, &requestError{ + id: id, + code: mcp.INVALID_PARAMS, + err: err, + } } - if request.Params.Cursor != "" { - result.NextCursor = "" // Handle pagination if needed + result := mcp.ListPromptsResult{ + Prompts: promptsToReturn, + PaginatedResult: mcp.PaginatedResult{ + NextCursor: nextCursor, + }, } return &result, nil } @@ -682,21 +762,23 @@ func (s *MCPServer) handleListTools( // Sort the tool names for consistent ordering sort.Strings(toolNames) - - // Add tools in sorted order - for _, name := range toolNames { - tools = append(tools, s.tools[name].Tool) + toolsToReturn, nextCursor, err := listByPagination[mcp.Tool](ctx, s, request.Params.Cursor, tools) + if err != nil { + return nil, &requestError{ + id: id, + code: mcp.INVALID_PARAMS, + err: err, + } } - s.mu.RUnlock() - result := mcp.ListToolsResult{ - Tools: tools, - } - if request.Params.Cursor != "" { - result.NextCursor = "" // Handle pagination if needed + Tools: toolsToReturn, + PaginatedResult: mcp.PaginatedResult{ + NextCursor: nextCursor, + }, } return &result, nil } + func (s *MCPServer) handleToolCall( ctx context.Context, id interface{}, diff --git a/server/server_test.go b/server/server_test.go index 3dde9a460..96574c326 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -335,7 +335,6 @@ func TestMCPServer_Tools(t *testing.T) { toolsList := server.HandleMessage(ctx, []byte(`{ "jsonrpc": "2.0", "id": 1, - "method": "tools/list" }`)) tt.validate(t, notifications, toolsList.(mcp.JSONRPCMessage)) }) @@ -1125,6 +1124,7 @@ func createTestServer() *MCPServer { server := NewMCPServer("test-server", "1.0.0", WithResourceCapabilities(true, true), WithPromptCapabilities(true), + WithPaginationLimit(2), ) server.AddResource( From 61040b769825b65a224f29469f1e9ec6374aa18d Mon Sep 17 00:00:00 2001 From: Jinlkj Date: Thu, 3 Apr 2025 21:56:33 +0800 Subject: [PATCH 2/4] update test --- server/server.go | 5 +++++ server/server_test.go | 10 ++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/server/server.go b/server/server.go index 14ecc8787..001654f66 100644 --- a/server/server.go +++ b/server/server.go @@ -762,6 +762,11 @@ func (s *MCPServer) handleListTools( // Sort the tool names for consistent ordering sort.Strings(toolNames) + + // Add tools in sorted order + for _, name := range toolNames { + tools = append(tools, s.tools[name].Tool) + } toolsToReturn, nextCursor, err := listByPagination[mcp.Tool](ctx, s, request.Params.Cursor, tools) if err != nil { return nil, &requestError{ diff --git a/server/server_test.go b/server/server_test.go index 96574c326..2c7a108d8 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -2,6 +2,7 @@ package server import ( "context" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -335,6 +336,7 @@ func TestMCPServer_Tools(t *testing.T) { toolsList := server.HandleMessage(ctx, []byte(`{ "jsonrpc": "2.0", "id": 1, + "method": "tools/list" }`)) tt.validate(t, notifications, toolsList.(mcp.JSONRPCMessage)) }) @@ -429,7 +431,7 @@ func TestMCPServer_HandleValidMessages(t *testing.T) { func TestMCPServer_HandlePagination(t *testing.T) { server := createTestServer() - + cursor := base64.StdEncoding.EncodeToString([]byte("My Resource")) tests := []struct { name string message string @@ -437,14 +439,14 @@ func TestMCPServer_HandlePagination(t *testing.T) { }{ { name: "List resources with cursor", - message: `{ + message: fmt.Sprintf(`{ "jsonrpc": "2.0", "id": 1, "method": "resources/list", "params": { - "cursor": "test-cursor" + "cursor": "%s" } - }`, + }`, cursor), validate: func(t *testing.T, response mcp.JSONRPCMessage) { resp, ok := response.(mcp.JSONRPCResponse) assert.True(t, ok) From 60ef6084c2707b9be4e81dbcb1ec1fdb28cd3c1c Mon Sep 17 00:00:00 2001 From: Jinlkj Date: Mon, 7 Apr 2025 13:28:20 +0800 Subject: [PATCH 3/4] solve race problem --- server/stdio.go | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/server/stdio.go b/server/stdio.go index 14c1e76e9..41a677767 100644 --- a/server/stdio.go +++ b/server/stdio.go @@ -158,18 +158,32 @@ func (s *StdioServer) processInputStream(ctx context.Context, reader *bufio.Read func (s *StdioServer) readNextLine(ctx context.Context, reader *bufio.Reader) (string, error) { readChan := make(chan string, 1) errChan := make(chan error, 1) - defer func() { - close(readChan) - close(errChan) - }() + done := make(chan struct{}) + defer close(done) + //defer func() { + // close(readChan) + // close(errChan) + //}() go func() { - line, err := reader.ReadString('\n') - if err != nil { - errChan <- err + select { + case <-done: return + default: + line, err := reader.ReadString('\n') + if err != nil { + select { + case errChan <- err: + case <-done: + + } + return + } + select { + case readChan <- line: + case <-done: + } } - readChan <- line }() select { From 7488f535d72a864b500298959592c64e9ecaa016 Mon Sep 17 00:00:00 2001 From: Jinlkj Date: Mon, 7 Apr 2025 14:38:14 +0800 Subject: [PATCH 4/4] solve race pro --- server/stdio.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/stdio.go b/server/stdio.go index 41a677767..43d9570c6 100644 --- a/server/stdio.go +++ b/server/stdio.go @@ -160,10 +160,6 @@ func (s *StdioServer) readNextLine(ctx context.Context, reader *bufio.Reader) (s errChan := make(chan error, 1) done := make(chan struct{}) defer close(done) - //defer func() { - // close(readChan) - // close(errChan) - //}() go func() { select {