Skip to content

Commit e021022

Browse files
Merge branch 'main' into mcp-ui-apps-advanced
2 parents c8848f5 + dc7e789 commit e021022

File tree

12 files changed

+331
-57
lines changed

12 files changed

+331
-57
lines changed

pkg/context/token.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,37 @@ import (
66
"github.com/github/github-mcp-server/pkg/utils"
77
)
88

9-
// tokenCtxKey is a context key for authentication token information
10-
type tokenCtx string
11-
12-
var tokenCtxKey tokenCtx = "tokenctx"
9+
type tokenCtxKey struct{}
1310

1411
type TokenInfo struct {
15-
Token string
16-
TokenType utils.TokenType
17-
ScopesFetched bool
18-
Scopes []string
12+
Token string
13+
TokenType utils.TokenType
1914
}
2015

2116
// WithTokenInfo adds TokenInfo to the context
2217
func WithTokenInfo(ctx context.Context, tokenInfo *TokenInfo) context.Context {
23-
return context.WithValue(ctx, tokenCtxKey, tokenInfo)
18+
return context.WithValue(ctx, tokenCtxKey{}, tokenInfo)
2419
}
2520

2621
// GetTokenInfo retrieves the authentication token from the context
2722
func GetTokenInfo(ctx context.Context) (*TokenInfo, bool) {
28-
if tokenInfo, ok := ctx.Value(tokenCtxKey).(*TokenInfo); ok {
23+
if tokenInfo, ok := ctx.Value(tokenCtxKey{}).(*TokenInfo); ok {
2924
return tokenInfo, true
3025
}
3126
return nil, false
3227
}
28+
29+
type tokenScopesKey struct{}
30+
31+
// WithTokenScopes adds token scopes to the context
32+
func WithTokenScopes(ctx context.Context, scopes []string) context.Context {
33+
return context.WithValue(ctx, tokenScopesKey{}, scopes)
34+
}
35+
36+
// GetTokenScopes retrieves token scopes from the context
37+
func GetTokenScopes(ctx context.Context) ([]string, bool) {
38+
if scopes, ok := ctx.Value(tokenScopesKey{}).([]string); ok {
39+
return scopes, true
40+
}
41+
return nil, false
42+
}

pkg/github/issues.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -376,12 +376,9 @@ func GetIssue(ctx context.Context, client *github.Client, deps ToolDependencies,
376376
}
377377
}
378378

379-
r, err := json.Marshal(issue)
380-
if err != nil {
381-
return nil, fmt.Errorf("failed to marshal issue: %w", err)
382-
}
379+
minimalIssue := convertToMinimalIssue(issue)
383380

384-
return utils.NewToolResultText(string(r)), nil
381+
return MarshalledTextResult(minimalIssue), nil
385382
}
386383

387384
func GetIssueComments(ctx context.Context, client *github.Client, deps ToolDependencies, owner string, repo string, issueNumber int, pagination PaginationParams) (*mcp.CallToolResult, error) {

pkg/github/issues_test.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -345,15 +345,15 @@ func Test_GetIssue(t *testing.T) {
345345

346346
textContent := getTextResult(t, result)
347347

348-
var returnedIssue github.Issue
348+
var returnedIssue MinimalIssue
349349
err = json.Unmarshal([]byte(textContent.Text), &returnedIssue)
350350
require.NoError(t, err)
351-
assert.Equal(t, *tc.expectedIssue.Number, *returnedIssue.Number)
352-
assert.Equal(t, *tc.expectedIssue.Title, *returnedIssue.Title)
353-
assert.Equal(t, *tc.expectedIssue.Body, *returnedIssue.Body)
354-
assert.Equal(t, *tc.expectedIssue.State, *returnedIssue.State)
355-
assert.Equal(t, *tc.expectedIssue.HTMLURL, *returnedIssue.HTMLURL)
356-
assert.Equal(t, *tc.expectedIssue.User.Login, *returnedIssue.User.Login)
351+
assert.Equal(t, tc.expectedIssue.GetNumber(), returnedIssue.Number)
352+
assert.Equal(t, tc.expectedIssue.GetTitle(), returnedIssue.Title)
353+
assert.Equal(t, tc.expectedIssue.GetBody(), returnedIssue.Body)
354+
assert.Equal(t, tc.expectedIssue.GetState(), returnedIssue.State)
355+
assert.Equal(t, tc.expectedIssue.GetHTMLURL(), returnedIssue.HTMLURL)
356+
assert.Equal(t, tc.expectedIssue.GetUser().GetLogin(), returnedIssue.User.Login)
357357
})
358358
}
359359
}

pkg/github/minimal_types.go

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package github
22

33
import (
4+
"time"
5+
46
"github.com/google/go-github/v82/github"
57
)
68

@@ -134,8 +136,241 @@ type MinimalProject struct {
134136
OwnerType string `json:"owner_type,omitempty"`
135137
}
136138

139+
// MinimalReactions is the trimmed output type for reaction summaries, dropping the API URL.
140+
type MinimalReactions struct {
141+
TotalCount int `json:"total_count"`
142+
PlusOne int `json:"+1"`
143+
MinusOne int `json:"-1"`
144+
Laugh int `json:"laugh"`
145+
Confused int `json:"confused"`
146+
Heart int `json:"heart"`
147+
Hooray int `json:"hooray"`
148+
Rocket int `json:"rocket"`
149+
Eyes int `json:"eyes"`
150+
}
151+
152+
// MinimalIssue is the trimmed output type for issue objects to reduce verbosity.
153+
type MinimalIssue struct {
154+
Number int `json:"number"`
155+
Title string `json:"title"`
156+
Body string `json:"body,omitempty"`
157+
State string `json:"state"`
158+
StateReason string `json:"state_reason,omitempty"`
159+
Draft bool `json:"draft,omitempty"`
160+
Locked bool `json:"locked,omitempty"`
161+
HTMLURL string `json:"html_url"`
162+
User *MinimalUser `json:"user,omitempty"`
163+
AuthorAssociation string `json:"author_association,omitempty"`
164+
Labels []string `json:"labels,omitempty"`
165+
Assignees []string `json:"assignees,omitempty"`
166+
Milestone string `json:"milestone,omitempty"`
167+
Comments int `json:"comments,omitempty"`
168+
Reactions *MinimalReactions `json:"reactions,omitempty"`
169+
CreatedAt string `json:"created_at,omitempty"`
170+
UpdatedAt string `json:"updated_at,omitempty"`
171+
ClosedAt string `json:"closed_at,omitempty"`
172+
ClosedBy string `json:"closed_by,omitempty"`
173+
IssueType string `json:"issue_type,omitempty"`
174+
}
175+
176+
// MinimalPullRequest is the trimmed output type for pull request objects to reduce verbosity.
177+
type MinimalPullRequest struct {
178+
Number int `json:"number"`
179+
Title string `json:"title"`
180+
Body string `json:"body,omitempty"`
181+
State string `json:"state"`
182+
Draft bool `json:"draft"`
183+
Merged bool `json:"merged"`
184+
MergeableState string `json:"mergeable_state,omitempty"`
185+
HTMLURL string `json:"html_url"`
186+
User *MinimalUser `json:"user,omitempty"`
187+
Labels []string `json:"labels,omitempty"`
188+
Assignees []string `json:"assignees,omitempty"`
189+
RequestedReviewers []string `json:"requested_reviewers,omitempty"`
190+
MergedBy string `json:"merged_by,omitempty"`
191+
Head *MinimalPRBranch `json:"head,omitempty"`
192+
Base *MinimalPRBranch `json:"base,omitempty"`
193+
Additions int `json:"additions,omitempty"`
194+
Deletions int `json:"deletions,omitempty"`
195+
ChangedFiles int `json:"changed_files,omitempty"`
196+
Commits int `json:"commits,omitempty"`
197+
Comments int `json:"comments,omitempty"`
198+
CreatedAt string `json:"created_at,omitempty"`
199+
UpdatedAt string `json:"updated_at,omitempty"`
200+
ClosedAt string `json:"closed_at,omitempty"`
201+
MergedAt string `json:"merged_at,omitempty"`
202+
Milestone string `json:"milestone,omitempty"`
203+
}
204+
205+
// MinimalPRBranch is the trimmed output type for pull request branch references.
206+
type MinimalPRBranch struct {
207+
Ref string `json:"ref"`
208+
SHA string `json:"sha"`
209+
Repo *MinimalPRBranchRepo `json:"repo,omitempty"`
210+
}
211+
212+
// MinimalPRBranchRepo is the trimmed repo info nested inside a PR branch.
213+
type MinimalPRBranchRepo struct {
214+
FullName string `json:"full_name"`
215+
Description string `json:"description,omitempty"`
216+
}
217+
137218
// Helper functions
138219

220+
func convertToMinimalIssue(issue *github.Issue) MinimalIssue {
221+
m := MinimalIssue{
222+
Number: issue.GetNumber(),
223+
Title: issue.GetTitle(),
224+
Body: issue.GetBody(),
225+
State: issue.GetState(),
226+
StateReason: issue.GetStateReason(),
227+
Draft: issue.GetDraft(),
228+
Locked: issue.GetLocked(),
229+
HTMLURL: issue.GetHTMLURL(),
230+
User: convertToMinimalUser(issue.GetUser()),
231+
AuthorAssociation: issue.GetAuthorAssociation(),
232+
Comments: issue.GetComments(),
233+
}
234+
235+
if issue.CreatedAt != nil {
236+
m.CreatedAt = issue.CreatedAt.Format(time.RFC3339)
237+
}
238+
if issue.UpdatedAt != nil {
239+
m.UpdatedAt = issue.UpdatedAt.Format(time.RFC3339)
240+
}
241+
if issue.ClosedAt != nil {
242+
m.ClosedAt = issue.ClosedAt.Format(time.RFC3339)
243+
}
244+
245+
for _, label := range issue.Labels {
246+
if label != nil {
247+
m.Labels = append(m.Labels, label.GetName())
248+
}
249+
}
250+
251+
for _, assignee := range issue.Assignees {
252+
if assignee != nil {
253+
m.Assignees = append(m.Assignees, assignee.GetLogin())
254+
}
255+
}
256+
257+
if closedBy := issue.GetClosedBy(); closedBy != nil {
258+
m.ClosedBy = closedBy.GetLogin()
259+
}
260+
261+
if milestone := issue.GetMilestone(); milestone != nil {
262+
m.Milestone = milestone.GetTitle()
263+
}
264+
265+
if issueType := issue.GetType(); issueType != nil {
266+
m.IssueType = issueType.GetName()
267+
}
268+
269+
if r := issue.Reactions; r != nil {
270+
m.Reactions = &MinimalReactions{
271+
TotalCount: r.GetTotalCount(),
272+
PlusOne: r.GetPlusOne(),
273+
MinusOne: r.GetMinusOne(),
274+
Laugh: r.GetLaugh(),
275+
Confused: r.GetConfused(),
276+
Heart: r.GetHeart(),
277+
Hooray: r.GetHooray(),
278+
Rocket: r.GetRocket(),
279+
Eyes: r.GetEyes(),
280+
}
281+
}
282+
283+
return m
284+
}
285+
286+
func convertToMinimalPullRequest(pr *github.PullRequest) MinimalPullRequest {
287+
m := MinimalPullRequest{
288+
Number: pr.GetNumber(),
289+
Title: pr.GetTitle(),
290+
Body: pr.GetBody(),
291+
State: pr.GetState(),
292+
Draft: pr.GetDraft(),
293+
Merged: pr.GetMerged(),
294+
MergeableState: pr.GetMergeableState(),
295+
HTMLURL: pr.GetHTMLURL(),
296+
User: convertToMinimalUser(pr.GetUser()),
297+
Additions: pr.GetAdditions(),
298+
Deletions: pr.GetDeletions(),
299+
ChangedFiles: pr.GetChangedFiles(),
300+
Commits: pr.GetCommits(),
301+
Comments: pr.GetComments(),
302+
}
303+
304+
if pr.CreatedAt != nil {
305+
m.CreatedAt = pr.CreatedAt.Format(time.RFC3339)
306+
}
307+
if pr.UpdatedAt != nil {
308+
m.UpdatedAt = pr.UpdatedAt.Format(time.RFC3339)
309+
}
310+
if pr.ClosedAt != nil {
311+
m.ClosedAt = pr.ClosedAt.Format(time.RFC3339)
312+
}
313+
if pr.MergedAt != nil {
314+
m.MergedAt = pr.MergedAt.Format(time.RFC3339)
315+
}
316+
317+
for _, label := range pr.Labels {
318+
if label != nil {
319+
m.Labels = append(m.Labels, label.GetName())
320+
}
321+
}
322+
323+
for _, assignee := range pr.Assignees {
324+
if assignee != nil {
325+
m.Assignees = append(m.Assignees, assignee.GetLogin())
326+
}
327+
}
328+
329+
for _, reviewer := range pr.RequestedReviewers {
330+
if reviewer != nil {
331+
m.RequestedReviewers = append(m.RequestedReviewers, reviewer.GetLogin())
332+
}
333+
}
334+
335+
if mergedBy := pr.GetMergedBy(); mergedBy != nil {
336+
m.MergedBy = mergedBy.GetLogin()
337+
}
338+
339+
if head := pr.Head; head != nil {
340+
m.Head = convertToMinimalPRBranch(head)
341+
}
342+
343+
if base := pr.Base; base != nil {
344+
m.Base = convertToMinimalPRBranch(base)
345+
}
346+
347+
if milestone := pr.GetMilestone(); milestone != nil {
348+
m.Milestone = milestone.GetTitle()
349+
}
350+
351+
return m
352+
}
353+
354+
func convertToMinimalPRBranch(branch *github.PullRequestBranch) *MinimalPRBranch {
355+
if branch == nil {
356+
return nil
357+
}
358+
359+
b := &MinimalPRBranch{
360+
Ref: branch.GetRef(),
361+
SHA: branch.GetSHA(),
362+
}
363+
364+
if repo := branch.GetRepo(); repo != nil {
365+
b.Repo = &MinimalPRBranchRepo{
366+
FullName: repo.GetFullName(),
367+
Description: repo.GetDescription(),
368+
}
369+
}
370+
371+
return b
372+
}
373+
139374
func convertToMinimalProject(fullProject *github.ProjectV2) *MinimalProject {
140375
if fullProject == nil {
141376
return nil

pkg/github/pullrequests.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,12 +186,9 @@ func GetPullRequest(ctx context.Context, client *github.Client, deps ToolDepende
186186
}
187187
}
188188

189-
r, err := json.Marshal(pr)
190-
if err != nil {
191-
return nil, fmt.Errorf("failed to marshal response: %w", err)
192-
}
189+
minimalPR := convertToMinimalPullRequest(pr)
193190

194-
return utils.NewToolResultText(string(r)), nil
191+
return MarshalledTextResult(minimalPR), nil
195192
}
196193

197194
func GetPullRequestDiff(ctx context.Context, client *github.Client, owner, repo string, pullNumber int) (*mcp.CallToolResult, error) {

pkg/github/pullrequests_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,14 @@ func Test_GetPullRequest(t *testing.T) {
127127
// Parse the result and get the text content if no error
128128
textContent := getTextResult(t, result)
129129

130-
// Unmarshal and verify the result
131-
var returnedPR github.PullRequest
130+
// Unmarshal and verify the minimal result
131+
var returnedPR MinimalPullRequest
132132
err = json.Unmarshal([]byte(textContent.Text), &returnedPR)
133133
require.NoError(t, err)
134-
assert.Equal(t, *tc.expectedPR.Number, *returnedPR.Number)
135-
assert.Equal(t, *tc.expectedPR.Title, *returnedPR.Title)
136-
assert.Equal(t, *tc.expectedPR.State, *returnedPR.State)
137-
assert.Equal(t, *tc.expectedPR.HTMLURL, *returnedPR.HTMLURL)
134+
assert.Equal(t, tc.expectedPR.GetNumber(), returnedPR.Number)
135+
assert.Equal(t, tc.expectedPR.GetTitle(), returnedPR.Title)
136+
assert.Equal(t, tc.expectedPR.GetState(), returnedPR.State)
137+
assert.Equal(t, tc.expectedPR.GetHTMLURL(), returnedPR.HTMLURL)
138138
})
139139
}
140140
}

0 commit comments

Comments
 (0)