Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

experiment with fake cursor pagination approach#1406

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
SamMorrowDrums wants to merge1 commit intomain
base:main
Choose a base branch
Loading
fromcursor-pagination-2
Draft
Show file tree
Hide file tree
Changes fromall 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
2 changes: 1 addition & 1 deletionpkg/github/issues.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -831,7 +831,7 @@ func SearchIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (
mcp.Description("Sort order"),
mcp.Enum("asc", "desc"),
),
WithPagination(),
WithFixedCursorPagination(),
),
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return searchHandler(ctx, getClient, request, "issue", "failed to search issues")
Expand Down
2 changes: 1 addition & 1 deletionpkg/github/pullrequests.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -968,7 +968,7 @@ func SearchPullRequests(getClient GetClientFn, t translations.TranslationHelperF
mcp.Description("Sort order"),
mcp.Enum("asc", "desc"),
),
WithPagination(),
WithFixedCursorPagination(),
),
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return searchHandler(ctx, getClient, request, "pr", "failed to search pull requests")
Expand Down
89 changes: 84 additions & 5 deletionspkg/github/search_utils.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,6 +2,7 @@ package github

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand DownExpand Up@@ -73,18 +74,27 @@ func searchHandler(
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
pagination, err :=OptionalPaginationParams(request)
pagination, err :=OptionalFixedCursorPaginationParams(request)
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

// WithFixedCursorPagination: fetch exactly pageSize items, use TotalCount to determine if there's more
pageSize := pagination.PerPage
// Determine current page from After cursor
page := 1
if pagination.After != "" {
decoded, err := decodePageCursor(pagination.After)
if err == nil && decoded > 0 {
page = decoded
}
}
opts := &github.SearchOptions{
// Default to "created" if no sort is provided, as it's a common use case.
Sort: sort,
Order: order,
ListOptions: github.ListOptions{
Page:pagination.Page,
PerPage:pagination.PerPage,
Page:page,
PerPage:pageSize,
},
}

Expand All@@ -106,10 +116,79 @@ func searchHandler(
return mcp.NewToolResultError(fmt.Sprintf("%s: %s", errorPrefix, string(body))), nil
}

r, err := json.Marshal(result)
// Prepare paginated results
items := result.Issues
totalCount := result.GetTotal()

// Calculate if there's a next page based on total count and current position
currentItemCount := len(items)
itemsSeenSoFar := (page-1)*pageSize + currentItemCount
hasNextPage := itemsSeenSoFar < totalCount

nextCursor := ""
if hasNextPage {
nextPage := page + 1
nextCursor = encodePageCursor(nextPage)
}

pageInfo := struct {
HasNextPage bool `json:"hasNextPage"`
EndCursor string `json:"endCursor,omitempty"`
}{
HasNextPage: hasNextPage,
EndCursor: nextCursor,
}

response := struct {
TotalCount int `json:"totalCount"`
IncompleteResults bool `json:"incompleteResults"`
Items []*github.Issue `json:"items"`
PageInfo interface{} `json:"pageInfo"`
}{
TotalCount: totalCount,
IncompleteResults: result.GetIncompleteResults(),
Items: items,
PageInfo: pageInfo,
}

r, err := json.Marshal(response)
if err != nil {
return nil, fmt.Errorf("%s: failed to marshal response: %w", errorPrefix, err)
}

return mcp.NewToolResultText(string(r)), nil
}

// encodePageCursor encodes the page number as a base64 string
func encodePageCursor(page int) string {
s := fmt.Sprintf("page=%d", page)
return b64Encode(s)
}

// decodePageCursor decodes a base64 cursor and extracts the page number
func decodePageCursor(cursor string) (int, error) {
data, err := b64Decode(cursor)
if err != nil {
return 1, err
}
var page int
n, err := fmt.Sscanf(data, "page=%d", &page)
if err != nil || n != 1 {
return 1, fmt.Errorf("invalid cursor format")
}
return page, nil
}

// b64Encode encodes a string to base64
func b64Encode(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
}

// b64Decode decodes a base64 string
func b64Decode(s string) (string, error) {
data, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return "", err
}
return string(data), nil
}
22 changes: 22 additions & 0 deletionspkg/github/server.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -227,6 +227,15 @@ func WithUnifiedPagination() mcp.ToolOption {
}
}

// WithFixedCursorPagination adds only cursor-based pagination parameters to a tool (no page parameter).
func WithFixedCursorPagination() mcp.ToolOption {
return func(tool *mcp.Tool) {
mcp.WithString("cursor",
mcp.Description("Cursor for pagination. Use the endCursor from the previous page's PageInfo."),
)(tool)
}
}

// WithCursorPagination adds only cursor-based pagination parameters to a tool (no page parameter).
func WithCursorPagination() mcp.ToolOption {
return func(tool *mcp.Tool) {
Expand DownExpand Up@@ -273,6 +282,19 @@ func OptionalPaginationParams(r mcp.CallToolRequest) (PaginationParams, error) {
}, nil
}

// OptionalFixedCursorPaginationParams returns the "perPage" and "after" parameters from the request,
// without the "page" parameter, suitable for cursor-based pagination only.
func OptionalFixedCursorPaginationParams(r mcp.CallToolRequest) (CursorPaginationParams, error) {
cursor, err := OptionalParam[string](r, "cursor")
if err != nil {
return CursorPaginationParams{}, err
}
return CursorPaginationParams{
PerPage: 10,
After: cursor,
}, nil
}

// OptionalCursorPaginationParams returns the "perPage" and "after" parameters from the request,
// without the "page" parameter, suitable for cursor-based pagination only.
func OptionalCursorPaginationParams(r mcp.CallToolRequest) (CursorPaginationParams, error) {
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp