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

Commit01aefd3

Browse files
authored
Add ability to view branches for a repo#141 (#205)
* Add ability to view branches for a repo#141* fix: update ListBranches test to use InputSchema and correct translation helper* fix: update ListBranches test to use InputSchema and correct translation helper* fix: update ListBranches test to handle errors in tool result* fix: replace deprecated github.String with github.Ptr* docs: add list_branches tool documentation to README
1 parent3c18a34 commit01aefd3

File tree

4 files changed

+181
-0
lines changed

4 files changed

+181
-0
lines changed

‎README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,13 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
311311
-`branch`: Branch name (string, optional)
312312
-`sha`: File SHA if updating (string, optional)
313313

314+
-**list_branches** - List branches in a GitHub repository
315+
316+
-`owner`: Repository owner (string, required)
317+
-`repo`: Repository name (string, required)
318+
-`page`: Page number (number, optional)
319+
-`perPage`: Results per page (number, optional)
320+
314321
-**push_files** - Push multiple files in a single commit
315322

316323
-`owner`: Repository owner (string, required)

‎pkg/github/repositories.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,69 @@ func ListCommits(getClient GetClientFn, t translations.TranslationHelperFunc) (t
150150
}
151151
}
152152

153+
// ListBranches creates a tool to list branches in a GitHub repository.
154+
funcListBranches(getClientGetClientFn,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
155+
returnmcp.NewTool("list_branches",
156+
mcp.WithDescription(t("TOOL_LIST_BRANCHES_DESCRIPTION","List branches in a GitHub repository")),
157+
mcp.WithString("owner",
158+
mcp.Required(),
159+
mcp.Description("Repository owner"),
160+
),
161+
mcp.WithString("repo",
162+
mcp.Required(),
163+
mcp.Description("Repository name"),
164+
),
165+
WithPagination(),
166+
),
167+
func(ctx context.Context,request mcp.CallToolRequest) (*mcp.CallToolResult,error) {
168+
owner,err:=requiredParam[string](request,"owner")
169+
iferr!=nil {
170+
returnmcp.NewToolResultError(err.Error()),nil
171+
}
172+
repo,err:=requiredParam[string](request,"repo")
173+
iferr!=nil {
174+
returnmcp.NewToolResultError(err.Error()),nil
175+
}
176+
pagination,err:=OptionalPaginationParams(request)
177+
iferr!=nil {
178+
returnmcp.NewToolResultError(err.Error()),nil
179+
}
180+
181+
opts:=&github.BranchListOptions{
182+
ListOptions: github.ListOptions{
183+
Page:pagination.page,
184+
PerPage:pagination.perPage,
185+
},
186+
}
187+
188+
client,err:=getClient(ctx)
189+
iferr!=nil {
190+
returnnil,fmt.Errorf("failed to get GitHub client: %w",err)
191+
}
192+
193+
branches,resp,err:=client.Repositories.ListBranches(ctx,owner,repo,opts)
194+
iferr!=nil {
195+
returnnil,fmt.Errorf("failed to list branches: %w",err)
196+
}
197+
deferfunc() {_=resp.Body.Close() }()
198+
199+
ifresp.StatusCode!=http.StatusOK {
200+
body,err:=io.ReadAll(resp.Body)
201+
iferr!=nil {
202+
returnnil,fmt.Errorf("failed to read response body: %w",err)
203+
}
204+
returnmcp.NewToolResultError(fmt.Sprintf("failed to list branches: %s",string(body))),nil
205+
}
206+
207+
r,err:=json.Marshal(branches)
208+
iferr!=nil {
209+
returnnil,fmt.Errorf("failed to marshal response: %w",err)
210+
}
211+
212+
returnmcp.NewToolResultText(string(r)),nil
213+
}
214+
}
215+
153216
// CreateOrUpdateFile creates a tool to create or update a file in a GitHub repository.
154217
funcCreateOrUpdateFile(getClientGetClientFn,t translations.TranslationHelperFunc) (tool mcp.Tool,handler server.ToolHandlerFunc) {
155218
returnmcp.NewTool("create_or_update_file",

‎pkg/github/repositories_test.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1423,3 +1423,113 @@ func Test_PushFiles(t *testing.T) {
14231423
})
14241424
}
14251425
}
1426+
1427+
funcTest_ListBranches(t*testing.T) {
1428+
// Verify tool definition once
1429+
mockClient:=github.NewClient(nil)
1430+
tool,_:=ListBranches(stubGetClientFn(mockClient),translations.NullTranslationHelper)
1431+
1432+
assert.Equal(t,"list_branches",tool.Name)
1433+
assert.NotEmpty(t,tool.Description)
1434+
assert.Contains(t,tool.InputSchema.Properties,"owner")
1435+
assert.Contains(t,tool.InputSchema.Properties,"repo")
1436+
assert.Contains(t,tool.InputSchema.Properties,"page")
1437+
assert.Contains(t,tool.InputSchema.Properties,"perPage")
1438+
assert.ElementsMatch(t,tool.InputSchema.Required, []string{"owner","repo"})
1439+
1440+
// Setup mock branches for success case
1441+
mockBranches:= []*github.Branch{
1442+
{
1443+
Name:github.Ptr("main"),
1444+
Commit:&github.RepositoryCommit{SHA:github.Ptr("abc123")},
1445+
},
1446+
{
1447+
Name:github.Ptr("develop"),
1448+
Commit:&github.RepositoryCommit{SHA:github.Ptr("def456")},
1449+
},
1450+
}
1451+
1452+
// Test cases
1453+
tests:= []struct {
1454+
namestring
1455+
argsmap[string]interface{}
1456+
mockResponses []mock.MockBackendOption
1457+
wantErrbool
1458+
errContainsstring
1459+
}{
1460+
{
1461+
name:"success",
1462+
args:map[string]interface{}{
1463+
"owner":"owner",
1464+
"repo":"repo",
1465+
"page":float64(2),
1466+
},
1467+
mockResponses: []mock.MockBackendOption{
1468+
mock.WithRequestMatch(
1469+
mock.GetReposBranchesByOwnerByRepo,
1470+
mockBranches,
1471+
),
1472+
},
1473+
wantErr:false,
1474+
},
1475+
{
1476+
name:"missing owner",
1477+
args:map[string]interface{}{
1478+
"repo":"repo",
1479+
},
1480+
mockResponses: []mock.MockBackendOption{},
1481+
wantErr:false,
1482+
errContains:"missing required parameter: owner",
1483+
},
1484+
{
1485+
name:"missing repo",
1486+
args:map[string]interface{}{
1487+
"owner":"owner",
1488+
},
1489+
mockResponses: []mock.MockBackendOption{},
1490+
wantErr:false,
1491+
errContains:"missing required parameter: repo",
1492+
},
1493+
}
1494+
1495+
for_,tt:=rangetests {
1496+
t.Run(tt.name,func(t*testing.T) {
1497+
// Create mock client
1498+
mockClient:=github.NewClient(mock.NewMockedHTTPClient(tt.mockResponses...))
1499+
_,handler:=ListBranches(stubGetClientFn(mockClient),translations.NullTranslationHelper)
1500+
1501+
// Create request
1502+
request:=createMCPRequest(tt.args)
1503+
1504+
// Call handler
1505+
result,err:=handler(context.Background(),request)
1506+
iftt.wantErr {
1507+
require.Error(t,err)
1508+
iftt.errContains!="" {
1509+
assert.Contains(t,err.Error(),tt.errContains)
1510+
}
1511+
return
1512+
}
1513+
1514+
require.NoError(t,err)
1515+
require.NotNil(t,result)
1516+
1517+
iftt.errContains!="" {
1518+
textContent:=getTextResult(t,result)
1519+
assert.Contains(t,textContent.Text,tt.errContains)
1520+
return
1521+
}
1522+
1523+
textContent:=getTextResult(t,result)
1524+
require.NotEmpty(t,textContent.Text)
1525+
1526+
// Verify response
1527+
varbranches []*github.Branch
1528+
err=json.Unmarshal([]byte(textContent.Text),&branches)
1529+
require.NoError(t,err)
1530+
assert.Len(t,branches,2)
1531+
assert.Equal(t,"main",*branches[0].Name)
1532+
assert.Equal(t,"develop",*branches[1].Name)
1533+
})
1534+
}
1535+
}

‎pkg/github/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func NewServer(getClient GetClientFn, version string, readOnly bool, t translati
7070
s.AddTool(GetFileContents(getClient,t))
7171
s.AddTool(GetCommit(getClient,t))
7272
s.AddTool(ListCommits(getClient,t))
73+
s.AddTool(ListBranches(getClient,t))
7374
if!readOnly {
7475
s.AddTool(CreateOrUpdateFile(getClient,t))
7576
s.AddTool(CreateRepository(getClient,t))

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp