Skip to content
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
4 changes: 3 additions & 1 deletion pkg/github/issues.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@ func SearchIssues(getClient GetClientFn, t translations.TranslationHelperFunc) (
},
}

client, err := getClient(ctx, "")
owner := extractOrgFromQuery(query)

client, err := getClient(ctx, owner)
if err != nil {
return nil, fmt.Errorf("failed to get GitHub client: %w", err)
}
Expand Down
11 changes: 11 additions & 0 deletions pkg/github/query_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,14 @@ func extractRepoFromQuery(query string) repoQueryInfo {

return repoQueryInfo{}
}

// extractOrgFromQuery attempts to extract an organization or user name from a search query.
// It looks for patterns like "org:squareup" or "user:octocat".
func extractOrgFromQuery(query string) string {
orgPattern := regexp.MustCompile(`(?:org|user):([^\s]+)`)
matches := orgPattern.FindStringSubmatch(query)
if len(matches) == 2 {
return matches[1]
}
return ""
}
99 changes: 99 additions & 0 deletions pkg/github/query_helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package github

import (
"testing"

"github.com/stretchr/testify/assert"
)

func Test_extractRepoFromQuery(t *testing.T) {
tests := []struct {
name string
query string
expected repoQueryInfo
}{
{
name: "repo:owner/repo pattern",
query: "repo:squareup/goosed-slackbot language:go",
expected: repoQueryInfo{owner: "squareup", repo: "goosed-slackbot"},
},
{
name: "bare owner/repo pattern",
query: "squareup/goosed-slackbot",
expected: repoQueryInfo{owner: "squareup", repo: "goosed-slackbot"},
},
{
name: "org qualifier only",
query: "goosed org:squareup",
expected: repoQueryInfo{},
},
{
name: "no repo pattern",
query: "golang test",
expected: repoQueryInfo{},
},
{
name: "empty query",
query: "",
expected: repoQueryInfo{},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := extractRepoFromQuery(tc.query)
assert.Equal(t, tc.expected, result)
})
}
}

func Test_extractOrgFromQuery(t *testing.T) {
tests := []struct {
name string
query string
expected string
}{
{
name: "org qualifier",
query: "goosed org:squareup",
expected: "squareup",
},
{
name: "org qualifier at start",
query: "org:tidal-engineering language:go",
expected: "tidal-engineering",
},
{
name: "user qualifier",
query: "user:octocat repos:>10",
expected: "octocat",
},
{
name: "no org qualifier",
query: "golang test",
expected: "",
},
{
name: "repo qualifier without org",
query: "repo:squareup/goosed-slackbot",
expected: "",
},
{
name: "both org and repo qualifiers",
query: "function main repo:squareup/goosed org:squareup",
expected: "squareup",
},
{
name: "empty query",
query: "",
expected: "",
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := extractOrgFromQuery(tc.query)
assert.Equal(t, tc.expected, result)
})
}
}
15 changes: 12 additions & 3 deletions pkg/github/repo_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,13 @@ func (f *MultiOrgClientFactory) GetClientFn() GetClientFn {
}

installID := f.getInstallationID(owner)
if installID == 0 && len(f.installations) > 0 {
for _, id := range f.installations {
installID = id
break
}
}
if installID == 0 {
// No installation for this org - return anonymous client
// This allows public repo operations to succeed
client := github.NewClient(nil)
client.UserAgent = fmt.Sprintf("github-mcp-server/%s", f.version)
return client, nil
Expand All @@ -253,8 +257,13 @@ func (f *MultiOrgClientFactory) GetGQLClientFn() GetGQLClientFn {
}

installID := f.getInstallationID(owner)
if installID == 0 && len(f.installations) > 0 {
for _, id := range f.installations {
installID = id
break
}
}
if installID == 0 {
// No installation for this org - return anonymous client
return githubv4.NewClient(nil), nil
}

Expand Down
12 changes: 7 additions & 5 deletions pkg/github/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,13 @@ func SearchRepositories(getClient GetClientFn, t translations.TranslationHelperF
return mcp.NewToolResultError(err.Error()), nil
}

// Extract repository info if present in the query
// This is a simple heuristic to check if the query contains a specific repository
// Format could be "repo:owner/repo" or similar
repoQuery := extractRepoFromQuery(query)
owner := ""
if repoQuery.owner != "" && repoQuery.repo != "" {
owner = repoQuery.owner
ctx = WithRepoContext(ctx, repoQuery.owner, repoQuery.repo)
} else if org := extractOrgFromQuery(query); org != "" {
owner = org
}

opts := &github.SearchOptions{
Expand Down Expand Up @@ -119,12 +118,13 @@ func SearchCode(getClient GetClientFn, t translations.TranslationHelperFunc) (to
return mcp.NewToolResultError(err.Error()), nil
}

// Extract repository info if present in the query
repoQuery := extractRepoFromQuery(query)
owner := ""
if repoQuery.owner != "" && repoQuery.repo != "" {
owner = repoQuery.owner
ctx = WithRepoContext(ctx, repoQuery.owner, repoQuery.repo)
} else if org := extractOrgFromQuery(query); org != "" {
owner = org
}

opts := &github.SearchOptions{
Expand Down Expand Up @@ -213,7 +213,9 @@ func SearchUsers(getClient GetClientFn, t translations.TranslationHelperFunc) (t
},
}

client, err := getClient(ctx, "")
owner := extractOrgFromQuery(query)

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