Skip to content

chore: groundwork for multi-user to server #195

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 9, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion cmd/github-mcp-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,11 @@ func runStdioServer(cfg runConfig) error {

t, dumpTranslations := translations.TranslationHelper()

getClient := func(_ context.Context) (*gogithub.Client, error) {
return ghClient, nil // closing over client
}
// Create
ghServer := github.NewServer(ghClient, version, cfg.readOnly, t)
ghServer := github.NewServer(getClient, version, cfg.readOnly, t)
stdioServer := server.NewStdioServer(ghServer)

stdLogger := stdlog.New(cfg.logger.Writer(), "stdioserver", 0)
Expand Down
13 changes: 11 additions & 2 deletions pkg/github/code_scanning.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/mark3labs/mcp-go/server"
)

func GetCodeScanningAlert(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func GetCodeScanningAlert(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_code_scanning_alert",
mcp.WithDescription(t("TOOL_GET_CODE_SCANNING_ALERT_DESCRIPTION", "Get details of a specific code scanning alert in a GitHub repository.")),
mcp.WithString("owner",
Expand Down Expand Up @@ -43,6 +43,11 @@ func GetCodeScanningAlert(client *github.Client, t translations.TranslationHelpe
return mcp.NewToolResultError(err.Error()), nil
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}

alert, resp, err := client.CodeScanning.GetAlert(ctx, owner, repo, int64(alertNumber))
if err != nil {
return nil, fmt.Errorf("failed to get alert: %w", err)
Expand All @@ -66,7 +71,7 @@ func GetCodeScanningAlert(client *github.Client, t translations.TranslationHelpe
}
}

func ListCodeScanningAlerts(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func ListCodeScanningAlerts(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("list_code_scanning_alerts",
mcp.WithDescription(t("TOOL_LIST_CODE_SCANNING_ALERTS_DESCRIPTION", "List code scanning alerts in a GitHub repository.")),
mcp.WithString("owner",
Expand Down Expand Up @@ -110,6 +115,10 @@ func ListCodeScanningAlerts(client *github.Client, t translations.TranslationHel
return mcp.NewToolResultError(err.Error()), nil
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
alerts, resp, err := client.CodeScanning.ListAlertsForRepo(ctx, owner, repo, &github.AlertListOptions{Ref: ref, State: state, Severity: severity})
if err != nil {
return nil, fmt.Errorf("failed to list alerts: %w", err)
Expand Down
8 changes: 4 additions & 4 deletions pkg/github/code_scanning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
func Test_GetCodeScanningAlert(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
tool, _ := GetCodeScanningAlert(mockClient, translations.NullTranslationHelper)
tool, _ := GetCodeScanningAlert(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "get_code_scanning_alert", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -82,7 +82,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := GetCodeScanningAlert(client, translations.NullTranslationHelper)
_, handler := GetCodeScanningAlert(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := createMCPRequest(tc.requestArgs)
Expand Down Expand Up @@ -118,7 +118,7 @@ func Test_GetCodeScanningAlert(t *testing.T) {
func Test_ListCodeScanningAlerts(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
tool, _ := ListCodeScanningAlerts(mockClient, translations.NullTranslationHelper)
tool, _ := ListCodeScanningAlerts(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "list_code_scanning_alerts", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -201,7 +201,7 @@ func Test_ListCodeScanningAlerts(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := ListCodeScanningAlerts(client, translations.NullTranslationHelper)
_, handler := ListCodeScanningAlerts(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := createMCPRequest(tc.requestArgs)
Expand Down
42 changes: 35 additions & 7 deletions pkg/github/issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
)

// GetIssue creates a tool to get details of a specific issue in a GitHub repository.
func GetIssue(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func GetIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_issue",
mcp.WithDescription(t("TOOL_GET_ISSUE_DESCRIPTION", "Get details of a specific issue in a GitHub repository")),
mcp.WithString("owner",
Expand Down Expand Up @@ -45,6 +45,10 @@ func GetIssue(client *github.Client, t translations.TranslationHelperFunc) (tool
return mcp.NewToolResultError(err.Error()), nil
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
issue, resp, err := client.Issues.Get(ctx, owner, repo, issueNumber)
if err != nil {
return nil, fmt.Errorf("failed to get issue: %w", err)
Expand All @@ -69,7 +73,7 @@ func GetIssue(client *github.Client, t translations.TranslationHelperFunc) (tool
}

// AddIssueComment creates a tool to add a comment to an issue.
func AddIssueComment(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func AddIssueComment(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("add_issue_comment",
mcp.WithDescription(t("TOOL_ADD_ISSUE_COMMENT_DESCRIPTION", "Add a comment to an existing issue")),
mcp.WithString("owner",
Expand Down Expand Up @@ -111,6 +115,10 @@ func AddIssueComment(client *github.Client, t translations.TranslationHelperFunc
Body: github.Ptr(body),
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
createdComment, resp, err := client.Issues.CreateComment(ctx, owner, repo, issueNumber, comment)
if err != nil {
return nil, fmt.Errorf("failed to create comment: %w", err)
Expand All @@ -135,7 +143,7 @@ func AddIssueComment(client *github.Client, t translations.TranslationHelperFunc
}

// SearchIssues creates a tool to search for issues and pull requests.
func SearchIssues(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func SearchIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("search_issues",
mcp.WithDescription(t("TOOL_SEARCH_ISSUES_DESCRIPTION", "Search for issues and pull requests across GitHub repositories")),
mcp.WithString("q",
Expand Down Expand Up @@ -191,6 +199,10 @@ func SearchIssues(client *github.Client, t translations.TranslationHelperFunc) (
},
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
result, resp, err := client.Search.Issues(ctx, query, opts)
if err != nil {
return nil, fmt.Errorf("failed to search issues: %w", err)
Expand All @@ -215,7 +227,7 @@ func SearchIssues(client *github.Client, t translations.TranslationHelperFunc) (
}

// CreateIssue creates a tool to create a new issue in a GitHub repository.
func CreateIssue(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func CreateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("create_issue",
mcp.WithDescription(t("TOOL_CREATE_ISSUE_DESCRIPTION", "Create a new issue in a GitHub repository")),
mcp.WithString("owner",
Expand Down Expand Up @@ -305,6 +317,10 @@ func CreateIssue(client *github.Client, t translations.TranslationHelperFunc) (t
Milestone: milestoneNum,
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
issue, resp, err := client.Issues.Create(ctx, owner, repo, issueRequest)
if err != nil {
return nil, fmt.Errorf("failed to create issue: %w", err)
Expand All @@ -329,7 +345,7 @@ func CreateIssue(client *github.Client, t translations.TranslationHelperFunc) (t
}

// ListIssues creates a tool to list and filter repository issues
func ListIssues(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func ListIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("list_issues",
mcp.WithDescription(t("TOOL_LIST_ISSUES_DESCRIPTION", "List issues in a GitHub repository with filtering options")),
mcp.WithString("owner",
Expand Down Expand Up @@ -419,6 +435,10 @@ func ListIssues(client *github.Client, t translations.TranslationHelperFunc) (to
opts.PerPage = int(perPage)
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
issues, resp, err := client.Issues.ListByRepo(ctx, owner, repo, opts)
if err != nil {
return nil, fmt.Errorf("failed to list issues: %w", err)
Expand All @@ -443,7 +463,7 @@ func ListIssues(client *github.Client, t translations.TranslationHelperFunc) (to
}

// UpdateIssue creates a tool to update an existing issue in a GitHub repository.
func UpdateIssue(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func UpdateIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("update_issue",
mcp.WithDescription(t("TOOL_UPDATE_ISSUE_DESCRIPTION", "Update an existing issue in a GitHub repository")),
mcp.WithString("owner",
Expand Down Expand Up @@ -557,6 +577,10 @@ func UpdateIssue(client *github.Client, t translations.TranslationHelperFunc) (t
issueRequest.Milestone = &milestoneNum
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
updatedIssue, resp, err := client.Issues.Edit(ctx, owner, repo, issueNumber, issueRequest)
if err != nil {
return nil, fmt.Errorf("failed to update issue: %w", err)
Expand All @@ -581,7 +605,7 @@ func UpdateIssue(client *github.Client, t translations.TranslationHelperFunc) (t
}

// GetIssueComments creates a tool to get comments for a GitHub issue.
func GetIssueComments(client *github.Client, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
func GetIssueComments(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_issue_comments",
mcp.WithDescription(t("TOOL_GET_ISSUE_COMMENTS_DESCRIPTION", "Get comments for a GitHub issue")),
mcp.WithString("owner",
Expand Down Expand Up @@ -632,6 +656,10 @@ func GetIssueComments(client *github.Client, t translations.TranslationHelperFun
},
}

client, err := getClient(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
comments, resp, err := client.Issues.ListComments(ctx, owner, repo, issueNumber, opts)
if err != nil {
return nil, fmt.Errorf("failed to get issue comments: %w", err)
Expand Down
28 changes: 14 additions & 14 deletions pkg/github/issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
func Test_GetIssue(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
tool, _ := GetIssue(mockClient, translations.NullTranslationHelper)
tool, _ := GetIssue(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "get_issue", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -82,7 +82,7 @@ func Test_GetIssue(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := GetIssue(client, translations.NullTranslationHelper)
_, handler := GetIssue(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := createMCPRequest(tc.requestArgs)
Expand Down Expand Up @@ -114,7 +114,7 @@ func Test_GetIssue(t *testing.T) {
func Test_AddIssueComment(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
tool, _ := AddIssueComment(mockClient, translations.NullTranslationHelper)
tool, _ := AddIssueComment(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "add_issue_comment", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -185,7 +185,7 @@ func Test_AddIssueComment(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := AddIssueComment(client, translations.NullTranslationHelper)
_, handler := AddIssueComment(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := mcp.CallToolRequest{
Expand Down Expand Up @@ -237,7 +237,7 @@ func Test_AddIssueComment(t *testing.T) {
func Test_SearchIssues(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
tool, _ := SearchIssues(mockClient, translations.NullTranslationHelper)
tool, _ := SearchIssues(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "search_issues", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -352,7 +352,7 @@ func Test_SearchIssues(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := SearchIssues(client, translations.NullTranslationHelper)
_, handler := SearchIssues(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := createMCPRequest(tc.requestArgs)
Expand Down Expand Up @@ -393,7 +393,7 @@ func Test_SearchIssues(t *testing.T) {
func Test_CreateIssue(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
tool, _ := CreateIssue(mockClient, translations.NullTranslationHelper)
tool, _ := CreateIssue(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "create_issue", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -506,7 +506,7 @@ func Test_CreateIssue(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := CreateIssue(client, translations.NullTranslationHelper)
_, handler := CreateIssue(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := createMCPRequest(tc.requestArgs)
Expand Down Expand Up @@ -567,7 +567,7 @@ func Test_CreateIssue(t *testing.T) {
func Test_ListIssues(t *testing.T) {
// Verify tool definition
mockClient := github.NewClient(nil)
tool, _ := ListIssues(mockClient, translations.NullTranslationHelper)
tool, _ := ListIssues(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "list_issues", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -698,7 +698,7 @@ func Test_ListIssues(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := ListIssues(client, translations.NullTranslationHelper)
_, handler := ListIssues(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := createMCPRequest(tc.requestArgs)
Expand Down Expand Up @@ -743,7 +743,7 @@ func Test_ListIssues(t *testing.T) {
func Test_UpdateIssue(t *testing.T) {
// Verify tool definition
mockClient := github.NewClient(nil)
tool, _ := UpdateIssue(mockClient, translations.NullTranslationHelper)
tool, _ := UpdateIssue(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "update_issue", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -882,7 +882,7 @@ func Test_UpdateIssue(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := UpdateIssue(client, translations.NullTranslationHelper)
_, handler := UpdateIssue(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := createMCPRequest(tc.requestArgs)
Expand Down Expand Up @@ -1000,7 +1000,7 @@ func Test_ParseISOTimestamp(t *testing.T) {
func Test_GetIssueComments(t *testing.T) {
// Verify tool definition once
mockClient := github.NewClient(nil)
tool, _ := GetIssueComments(mockClient, translations.NullTranslationHelper)
tool, _ := GetIssueComments(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "get_issue_comments", tool.Name)
assert.NotEmpty(t, tool.Description)
Expand Down Expand Up @@ -1100,7 +1100,7 @@ func Test_GetIssueComments(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
// Setup client with mock
client := github.NewClient(tc.mockedClient)
_, handler := GetIssueComments(client, translations.NullTranslationHelper)
_, handler := GetIssueComments(stubGetClientFn(client), translations.NullTranslationHelper)

// Create call request
request := createMCPRequest(tc.requestArgs)
Expand Down
Loading