Skip to content

Commit 7939297

Browse files
committed
Add pagination helper
1 parent c174179 commit 7939297

File tree

6 files changed

+150
-93
lines changed

6 files changed

+150
-93
lines changed

pkg/github/issues.go

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -162,15 +162,7 @@ func searchIssues(client *github.Client, t translations.TranslationHelperFunc) (
162162
mcp.Description("Sort order ('asc' or 'desc')"),
163163
mcp.Enum("asc", "desc"),
164164
),
165-
mcp.WithNumber("perPage",
166-
mcp.Description("Results per page (max 100)"),
167-
mcp.Min(1),
168-
mcp.Max(100),
169-
),
170-
mcp.WithNumber("page",
171-
mcp.Description("Page number"),
172-
mcp.Min(1),
173-
),
165+
withPagination(),
174166
),
175167
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
176168
query, err := requiredParam[string](request, "q")
@@ -185,11 +177,7 @@ func searchIssues(client *github.Client, t translations.TranslationHelperFunc) (
185177
if err != nil {
186178
return mcp.NewToolResultError(err.Error()), nil
187179
}
188-
perPage, err := optionalIntParamWithDefault(request, "perPage", 30)
189-
if err != nil {
190-
return mcp.NewToolResultError(err.Error()), nil
191-
}
192-
page, err := optionalIntParamWithDefault(request, "page", 1)
180+
pagination, err := optionalPaginationParams(request)
193181
if err != nil {
194182
return mcp.NewToolResultError(err.Error()), nil
195183
}
@@ -198,8 +186,8 @@ func searchIssues(client *github.Client, t translations.TranslationHelperFunc) (
198186
Sort: sort,
199187
Order: order,
200188
ListOptions: github.ListOptions{
201-
PerPage: perPage,
202-
Page: page,
189+
PerPage: pagination.perPage,
190+
Page: pagination.page,
203191
},
204192
}
205193

@@ -375,12 +363,7 @@ func listIssues(client *github.Client, t translations.TranslationHelperFunc) (to
375363
mcp.WithString("since",
376364
mcp.Description("Filter by date (ISO 8601 timestamp)"),
377365
),
378-
mcp.WithNumber("page",
379-
mcp.Description("Page number"),
380-
),
381-
mcp.WithNumber("perPage",
382-
mcp.Description("Results per page"),
383-
),
366+
withPagination(),
384367
),
385368
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
386369
owner, err := requiredParam[string](request, "owner")

pkg/github/pullrequests.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,7 @@ func listPullRequests(client *github.Client, t translations.TranslationHelperFun
9494
mcp.WithString("direction",
9595
mcp.Description("Sort direction ('asc', 'desc')"),
9696
),
97-
mcp.WithNumber("perPage",
98-
mcp.Description("Results per page (max 100)"),
99-
),
100-
mcp.WithNumber("page",
101-
mcp.Description("Page number"),
102-
),
97+
withPagination(),
10398
),
10499
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
105100
owner, err := requiredParam[string](request, "owner")
@@ -130,11 +125,7 @@ func listPullRequests(client *github.Client, t translations.TranslationHelperFun
130125
if err != nil {
131126
return mcp.NewToolResultError(err.Error()), nil
132127
}
133-
perPage, err := optionalIntParamWithDefault(request, "perPage", 30)
134-
if err != nil {
135-
return mcp.NewToolResultError(err.Error()), nil
136-
}
137-
page, err := optionalIntParamWithDefault(request, "page", 1)
128+
pagination, err := optionalPaginationParams(request)
138129
if err != nil {
139130
return mcp.NewToolResultError(err.Error()), nil
140131
}
@@ -146,8 +137,8 @@ func listPullRequests(client *github.Client, t translations.TranslationHelperFun
146137
Sort: sort,
147138
Direction: direction,
148139
ListOptions: github.ListOptions{
149-
PerPage: perPage,
150-
Page: page,
140+
PerPage: pagination.perPage,
141+
Page: pagination.page,
151142
},
152143
}
153144

pkg/github/repositories.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,7 @@ func listCommits(client *github.Client, t translations.TranslationHelperFunc) (t
2929
mcp.WithString("sha",
3030
mcp.Description("Branch name"),
3131
),
32-
mcp.WithNumber("page",
33-
mcp.Description("Page number"),
34-
),
35-
mcp.WithNumber("perPage",
36-
mcp.Description("Number of records per page"),
37-
),
32+
withPagination(),
3833
),
3934
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
4035
owner, err := requiredParam[string](request, "owner")
@@ -49,20 +44,16 @@ func listCommits(client *github.Client, t translations.TranslationHelperFunc) (t
4944
if err != nil {
5045
return mcp.NewToolResultError(err.Error()), nil
5146
}
52-
page, err := optionalIntParamWithDefault(request, "page", 1)
53-
if err != nil {
54-
return mcp.NewToolResultError(err.Error()), nil
55-
}
56-
perPage, err := optionalIntParamWithDefault(request, "perPage", 30)
47+
pagination, err := optionalPaginationParams(request)
5748
if err != nil {
5849
return mcp.NewToolResultError(err.Error()), nil
5950
}
6051

6152
opts := &github.CommitsListOptions{
6253
SHA: sha,
6354
ListOptions: github.ListOptions{
64-
Page: page,
65-
PerPage: perPage,
55+
Page: pagination.page,
56+
PerPage: pagination.perPage,
6657
},
6758
}
6859

pkg/github/search.go

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,22 @@ func searchRepositories(client *github.Client, t translations.TranslationHelperF
2020
mcp.Required(),
2121
mcp.Description("Search query"),
2222
),
23-
mcp.WithNumber("page",
24-
mcp.Description("Page number for pagination"),
25-
),
26-
mcp.WithNumber("perPage",
27-
mcp.Description("Results per page (max 100)"),
28-
),
23+
withPagination(),
2924
),
3025
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
3126
query, err := requiredParam[string](request, "query")
3227
if err != nil {
3328
return mcp.NewToolResultError(err.Error()), nil
3429
}
35-
page, err := optionalIntParamWithDefault(request, "page", 1)
36-
if err != nil {
37-
return mcp.NewToolResultError(err.Error()), nil
38-
}
39-
perPage, err := optionalIntParamWithDefault(request, "perPage", 30)
30+
pagination, err := optionalPaginationParams(request)
4031
if err != nil {
4132
return mcp.NewToolResultError(err.Error()), nil
4233
}
4334

4435
opts := &github.SearchOptions{
4536
ListOptions: github.ListOptions{
46-
Page: page,
47-
PerPage: perPage,
37+
Page: pagination.page,
38+
PerPage: pagination.perPage,
4839
},
4940
}
5041

@@ -86,15 +77,7 @@ func searchCode(client *github.Client, t translations.TranslationHelperFunc) (to
8677
mcp.Description("Sort order ('asc' or 'desc')"),
8778
mcp.Enum("asc", "desc"),
8879
),
89-
mcp.WithNumber("perPage",
90-
mcp.Description("Results per page (max 100)"),
91-
mcp.Min(1),
92-
mcp.Max(100),
93-
),
94-
mcp.WithNumber("page",
95-
mcp.Description("Page number"),
96-
mcp.Min(1),
97-
),
80+
withPagination(),
9881
),
9982
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
10083
query, err := requiredParam[string](request, "q")
@@ -109,11 +92,7 @@ func searchCode(client *github.Client, t translations.TranslationHelperFunc) (to
10992
if err != nil {
11093
return mcp.NewToolResultError(err.Error()), nil
11194
}
112-
perPage, err := optionalIntParamWithDefault(request, "perPage", 30)
113-
if err != nil {
114-
return mcp.NewToolResultError(err.Error()), nil
115-
}
116-
page, err := optionalIntParamWithDefault(request, "page", 1)
95+
pagination, err := optionalPaginationParams(request)
11796
if err != nil {
11897
return mcp.NewToolResultError(err.Error()), nil
11998
}
@@ -122,8 +101,8 @@ func searchCode(client *github.Client, t translations.TranslationHelperFunc) (to
122101
Sort: sort,
123102
Order: order,
124103
ListOptions: github.ListOptions{
125-
PerPage: perPage,
126-
Page: page,
104+
PerPage: pagination.perPage,
105+
Page: pagination.page,
127106
},
128107
}
129108

@@ -166,15 +145,7 @@ func searchUsers(client *github.Client, t translations.TranslationHelperFunc) (t
166145
mcp.Description("Sort order ('asc' or 'desc')"),
167146
mcp.Enum("asc", "desc"),
168147
),
169-
mcp.WithNumber("perPage",
170-
mcp.Description("Results per page (max 100)"),
171-
mcp.Min(1),
172-
mcp.Max(100),
173-
),
174-
mcp.WithNumber("page",
175-
mcp.Description("Page number"),
176-
mcp.Min(1),
177-
),
148+
withPagination(),
178149
),
179150
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
180151
query, err := requiredParam[string](request, "q")
@@ -189,11 +160,7 @@ func searchUsers(client *github.Client, t translations.TranslationHelperFunc) (t
189160
if err != nil {
190161
return mcp.NewToolResultError(err.Error()), nil
191162
}
192-
perPage, err := optionalIntParamWithDefault(request, "perPage", 30)
193-
if err != nil {
194-
return mcp.NewToolResultError(err.Error()), nil
195-
}
196-
page, err := optionalIntParamWithDefault(request, "page", 1)
163+
pagination, err := optionalPaginationParams(request)
197164
if err != nil {
198165
return mcp.NewToolResultError(err.Error()), nil
199166
}
@@ -202,8 +169,8 @@ func searchUsers(client *github.Client, t translations.TranslationHelperFunc) (t
202169
Sort: sort,
203170
Order: order,
204171
ListOptions: github.ListOptions{
205-
PerPage: perPage,
206-
Page: page,
172+
PerPage: pagination.perPage,
173+
Page: pagination.page,
207174
},
208175
}
209176

pkg/github/server.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,45 @@ func optionalStringArrayParam(r mcp.CallToolRequest, p string) ([]string, error)
229229
return []string{}, fmt.Errorf("parameter %s could not be coerced to []string, is %T", p, r.Params.Arguments[p])
230230
}
231231
}
232+
233+
// withPagination returns a ToolOption that adds "page" and "perPage" parameters to the tool.
234+
// The "page" parameter is optional, min 1. The "perPage" parameter is optional, min 1, max 100.
235+
func withPagination() mcp.ToolOption {
236+
return func(tool *mcp.Tool) {
237+
mcp.WithNumber("page",
238+
mcp.Description("Page number for pagination (min 1)"),
239+
mcp.Min(1),
240+
)(tool)
241+
242+
mcp.WithNumber("perPage",
243+
mcp.Description("Results per page for pagination (min 1, max 100)"),
244+
mcp.Min(1),
245+
mcp.Max(100),
246+
)(tool)
247+
}
248+
}
249+
250+
type paginationParams struct {
251+
page int
252+
perPage int
253+
}
254+
255+
// optionalPaginationParams returns the "page" and "perPage" parameters from the request,
256+
// or their default values if not present, "page" default is 1, "perPage" default is 30.
257+
// In future, we may want to make the default values configurable, or even have this
258+
// function returned from `withPagination`, where the defaults are provided alongside
259+
// the min/max values.
260+
func optionalPaginationParams(r mcp.CallToolRequest) (paginationParams, error) {
261+
page, err := optionalIntParamWithDefault(r, "page", 1)
262+
if err != nil {
263+
return paginationParams{}, err
264+
}
265+
perPage, err := optionalIntParamWithDefault(r, "perPage", 30)
266+
if err != nil {
267+
return paginationParams{}, err
268+
}
269+
return paginationParams{
270+
page: page,
271+
perPage: perPage,
272+
}, nil
273+
}

pkg/github/server_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,3 +551,86 @@ func TestOptionalStringArrayParam(t *testing.T) {
551551
})
552552
}
553553
}
554+
555+
func TestOptionalPaginationParams(t *testing.T) {
556+
tests := []struct {
557+
name string
558+
params map[string]any
559+
expected paginationParams
560+
expectError bool
561+
}{
562+
{
563+
name: "no pagination parameters, default values",
564+
params: map[string]any{},
565+
expected: paginationParams{
566+
page: 1,
567+
perPage: 30,
568+
},
569+
expectError: false,
570+
},
571+
{
572+
name: "page parameter, default perPage",
573+
params: map[string]any{
574+
"page": float64(2),
575+
},
576+
expected: paginationParams{
577+
page: 2,
578+
perPage: 30,
579+
},
580+
expectError: false,
581+
},
582+
{
583+
name: "perPage parameter, default page",
584+
params: map[string]any{
585+
"perPage": float64(50),
586+
},
587+
expected: paginationParams{
588+
page: 1,
589+
perPage: 50,
590+
},
591+
expectError: false,
592+
},
593+
{
594+
name: "page and perPage parameters",
595+
params: map[string]any{
596+
"page": float64(2),
597+
"perPage": float64(50),
598+
},
599+
expected: paginationParams{
600+
page: 2,
601+
perPage: 50,
602+
},
603+
expectError: false,
604+
},
605+
{
606+
name: "invalid page parameter",
607+
params: map[string]any{
608+
"page": "not-a-number",
609+
},
610+
expected: paginationParams{},
611+
expectError: true,
612+
},
613+
{
614+
name: "invalid perPage parameter",
615+
params: map[string]any{
616+
"perPage": "not-a-number",
617+
},
618+
expected: paginationParams{},
619+
expectError: true,
620+
},
621+
}
622+
623+
for _, tc := range tests {
624+
t.Run(tc.name, func(t *testing.T) {
625+
request := createMCPRequest(tc.params)
626+
result, err := optionalPaginationParams(request)
627+
628+
if tc.expectError {
629+
assert.Error(t, err)
630+
} else {
631+
assert.NoError(t, err)
632+
assert.Equal(t, tc.expected, result)
633+
}
634+
})
635+
}
636+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy