@@ -2,6 +2,7 @@ package github
22
33import (
44 "context"
5+ "encoding/base64"
56 "encoding/json"
67 "fmt"
78 "io"
@@ -73,18 +74,27 @@ func searchHandler(
7374 if err != nil {
7475 return mcp .NewToolResultError (err .Error ()), nil
7576 }
76- pagination , err := OptionalPaginationParams (request )
77+ pagination , err := OptionalFixedCursorPaginationParams (request )
7778 if err != nil {
7879 return mcp .NewToolResultError (err .Error ()), nil
7980 }
8081
82+ // WithFixedCursorPagination: fetch exactly pageSize items, use TotalCount to determine if there's more
83+ pageSize := pagination .PerPage
84+ // Determine current page from After cursor
85+ page := 1
86+ if pagination .After != "" {
87+ decoded , err := decodePageCursor (pagination .After )
88+ if err == nil && decoded > 0 {
89+ page = decoded
90+ }
91+ }
8192 opts := & github.SearchOptions {
82- // Default to "created" if no sort is provided, as it's a common use case.
8393 Sort : sort ,
8494 Order : order ,
8595 ListOptions : github.ListOptions {
86- Page : pagination . Page ,
87- PerPage : pagination . PerPage ,
96+ Page : page ,
97+ PerPage : pageSize ,
8898 },
8999 }
90100
@@ -106,10 +116,79 @@ func searchHandler(
106116 return mcp .NewToolResultError (fmt .Sprintf ("%s: %s" , errorPrefix , string (body ))), nil
107117 }
108118
109- r , err := json .Marshal (result )
119+ // Prepare paginated results
120+ items := result .Issues
121+ totalCount := result .GetTotal ()
122+
123+ // Calculate if there's a next page based on total count and current position
124+ currentItemCount := len (items )
125+ itemsSeenSoFar := (page - 1 )* pageSize + currentItemCount
126+ hasNextPage := itemsSeenSoFar < totalCount
127+
128+ nextCursor := ""
129+ if hasNextPage {
130+ nextPage := page + 1
131+ nextCursor = encodePageCursor (nextPage )
132+ }
133+
134+ pageInfo := struct {
135+ HasNextPage bool `json:"hasNextPage"`
136+ EndCursor string `json:"endCursor,omitempty"`
137+ }{
138+ HasNextPage : hasNextPage ,
139+ EndCursor : nextCursor ,
140+ }
141+
142+ response := struct {
143+ TotalCount int `json:"totalCount"`
144+ IncompleteResults bool `json:"incompleteResults"`
145+ Items []* github.Issue `json:"items"`
146+ PageInfo interface {} `json:"pageInfo"`
147+ }{
148+ TotalCount : totalCount ,
149+ IncompleteResults : result .GetIncompleteResults (),
150+ Items : items ,
151+ PageInfo : pageInfo ,
152+ }
153+
154+ r , err := json .Marshal (response )
110155 if err != nil {
111156 return nil , fmt .Errorf ("%s: failed to marshal response: %w" , errorPrefix , err )
112157 }
113158
114159 return mcp .NewToolResultText (string (r )), nil
115160}
161+
162+ // encodePageCursor encodes the page number as a base64 string
163+ func encodePageCursor (page int ) string {
164+ s := fmt .Sprintf ("page=%d" , page )
165+ return b64Encode (s )
166+ }
167+
168+ // decodePageCursor decodes a base64 cursor and extracts the page number
169+ func decodePageCursor (cursor string ) (int , error ) {
170+ data , err := b64Decode (cursor )
171+ if err != nil {
172+ return 1 , err
173+ }
174+ var page int
175+ n , err := fmt .Sscanf (data , "page=%d" , & page )
176+ if err != nil || n != 1 {
177+ return 1 , fmt .Errorf ("invalid cursor format" )
178+ }
179+ return page , nil
180+ }
181+
182+ // b64Encode encodes a string to base64
183+ func b64Encode (s string ) string {
184+ return base64 .StdEncoding .EncodeToString ([]byte (s ))
185+ }
186+
187+ // b64Decode decodes a base64 string
188+ func b64Decode (s string ) (string , error ) {
189+ data , err := base64 .StdEncoding .DecodeString (s )
190+ if err != nil {
191+ return "" , err
192+ }
193+ return string (data ), nil
194+ }
0 commit comments