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

Feature/chewy standards tool#510

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
Show file tree
Hide file tree
Changes fromall 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
87 changes: 7 additions & 80 deletionsREADME.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -509,6 +509,12 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description
- `path`: File path (string, required)
- `ref`: Git reference (string, optional)

- **get_chewy_standards** - Get development standards and guidelines from Chewy's repository
- `owner`: Repository owner (defaults to Chewy-Inc) (string, optional)
- `repo`: Repository name containing development standards (defaults to chewy-tech-standards) (string, optional)
- `standards_path`: Path to standards directory or file (string, optional) - defaults to searching the entire repository
- `ref`: Git reference (branch, tag, or commit SHA) to get standards from (string, optional) - defaults to main/master

- **fork_repository** - Fork a repository
- `owner`: Repository owner (string, required)
- `repo`: Repository name (string, required)
Expand DownExpand Up@@ -595,83 +601,4 @@ export GITHUB_MCP_TOOL_ADD_ISSUE_COMMENT_DESCRIPTION="an alternative description


- **get_notification_details** – Get detailed information for a specific GitHub notification
- `notificationID`: The ID of the notification (string, required)

- **dismiss_notification** – Dismiss a notification by marking it as read or done
- `threadID`: The ID of the notification thread (string, required)
- `state`: The new state of the notification (`read` or `done`)

- **mark_all_notifications_read** – Mark all notifications as read
- `lastReadAt`: Describes the last point that notifications were checked (optional, RFC3339/ISO8601 string, default: now)
- `owner`: Optional repository owner (string)
- `repo`: Optional repository name (string)

- **manage_notification_subscription** – Manage a notification subscription (ignore, watch, or delete) for a notification thread
- `notificationID`: The ID of the notification thread (string, required)
- `action`: Action to perform: `ignore`, `watch`, or `delete` (string, required)

- **manage_repository_notification_subscription** – Manage a repository notification subscription (ignore, watch, or delete)
- `owner`: The account owner of the repository (string, required)
- `repo`: The name of the repository (string, required)
- `action`: Action to perform: `ignore`, `watch`, or `delete` (string, required)

## Resources

### Repository Content

- **Get Repository Content**
Retrieves the content of a repository at a specific path.

- **Template**: `repo://{owner}/{repo}/contents{/path*}`
- **Parameters**:
- `owner`: Repository owner (string, required)
- `repo`: Repository name (string, required)
- `path`: File or directory path (string, optional)

- **Get Repository Content for a Specific Branch**
Retrieves the content of a repository at a specific path for a given branch.

- **Template**: `repo://{owner}/{repo}/refs/heads/{branch}/contents{/path*}`
- **Parameters**:
- `owner`: Repository owner (string, required)
- `repo`: Repository name (string, required)
- `branch`: Branch name (string, required)
- `path`: File or directory path (string, optional)

- **Get Repository Content for a Specific Commit**
Retrieves the content of a repository at a specific path for a given commit.

- **Template**: `repo://{owner}/{repo}/sha/{sha}/contents{/path*}`
- **Parameters**:
- `owner`: Repository owner (string, required)
- `repo`: Repository name (string, required)
- `sha`: Commit SHA (string, required)
- `path`: File or directory path (string, optional)

- **Get Repository Content for a Specific Tag**
Retrieves the content of a repository at a specific path for a given tag.

- **Template**: `repo://{owner}/{repo}/refs/tags/{tag}/contents{/path*}`
- **Parameters**:
- `owner`: Repository owner (string, required)
- `repo`: Repository name (string, required)
- `tag`: Tag name (string, required)
- `path`: File or directory path (string, optional)

- **Get Repository Content for a Specific Pull Request**
Retrieves the content of a repository at a specific path for a given pull request.

- **Template**: `repo://{owner}/{repo}/refs/pull/{prNumber}/head/contents{/path*}`
- **Parameters**:
- `owner`: Repository owner (string, required)
- `repo`: Repository name (string, required)
- `prNumber`: Pull request number (string, required)
- `path`: File or directory path (string, optional)

## Library Usage

The exported Go API of this module should currently be considered unstable, and subject to breaking changes. In the future, we may offer stability; please file an issue if there is a use case where this would be valuable.

## License

This project is licensed under the terms of the MIT open source license. Please refer to [MIT](./LICENSE) for the full terms.
- `
155 changes: 155 additions & 0 deletionspkg/github/repositories.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1100,3 +1100,158 @@ func GetTag(getClient GetClientFn, t translations.TranslationHelperFunc) (tool m
return mcp.NewToolResultText(string(r)), nil
}
}

// GetChewyStandards creates a tool to get development standards from Chewy's repository.
func GetChewyStandards(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
return mcp.NewTool("get_chewy_standards",
mcp.WithDescription(t("TOOL_GET_CHEWY_STANDARDS_DESCRIPTION", "Get development standards and guidelines from Chewy's repository")),
mcp.WithToolAnnotation(mcp.ToolAnnotation{
Title: t("TOOL_GET_CHEWY_STANDARDS_USER_TITLE", "Get Chewy development standards"),
ReadOnlyHint: toBoolPtr(true),
}),
mcp.WithString("owner",
mcp.Description("Repository owner (defaults to Chewy-Inc)"),
),
mcp.WithString("repo",
mcp.Description("Repository name containing development standards (defaults to chewy-tech-standards)"),
),
mcp.WithString("standards_path",
mcp.Description("Path to standards directory or file (defaults to searching the entire repository)"),
),
mcp.WithString("ref",
mcp.Description("Git reference (branch, tag, or commit SHA) to get standards from (defaults to main/master)"),
),
),
func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
owner, err := OptionalParam[string](request, "owner")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
if owner == "" {
owner = "Chewy-Inc"
}

repo, err := OptionalParam[string](request, "repo")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
if repo == "" {
repo = "chewy-tech-standards"
}

standardsPath, err := OptionalParam[string](request, "standards_path")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}
ref, err := OptionalParam[string](request, "ref")
if err != nil {
return mcp.NewToolResultError(err.Error()), nil
}

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

// If a specific path is provided, use it; otherwise search the whole repository
var pathsToSearch []string
var searchWholeRepo bool

if standardsPath != "" {
pathsToSearch = []string{standardsPath}
searchWholeRepo = false
} else {
// Search the whole repository (starting from root)
pathsToSearch = []string{""}
searchWholeRepo = true
}

var results []map[string]interface{}
opts := &github.RepositoryContentGetOptions{Ref: ref}

if searchWholeRepo {
// Get the entire repository tree
tree, resp, err := client.Git.GetTree(ctx, owner, repo, "HEAD", true)
Copy link
Preview

CopilotAIJun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Use the user-providedref parameter when fetching the repository tree instead of hardcoding "HEAD" so that branch/tag/commit references are respected.

Suggested change
tree,resp,err:=client.Git.GetTree(ctx,owner,repo,"HEAD",true)
tree,resp,err:=client.Git.GetTree(ctx,owner,repo,ref,true)

Copilot uses AI. Check for mistakes.

if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to get repository tree: %v", err)), nil
}
if resp != nil {
defer func() { _ = resp.Body.Close() }()
}

if resp.StatusCode == 200 && tree != nil {
Copy link
Preview

CopilotAIJun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Add a check for non-200 responses fromGetTree (e.g.resp.StatusCode != http.StatusOK) and return an error, rather than continuing silently when the tree isn’t fetched.

Suggested change
ifresp.StatusCode==200&&tree!=nil {
ifresp.StatusCode!=http.StatusOK {
returnmcp.NewToolResultError(fmt.Sprintf("Failed to get repository tree: received status code %d",resp.StatusCode)),nil
}
iftree!=nil {

Copilot uses AI. Check for mistakes.

results = append(results, map[string]interface{}{
"path": "",
"type": "repository",
"content": tree,
"source_repo": fmt.Sprintf("%s/%s", owner, repo),
"reference": ref,
})
}
} else {
// Search specific paths
for _, path := range pathsToSearch {
fileContent, dirContent, resp, err := client.Repositories.GetContents(ctx, owner, repo, path, opts)
if err != nil {
// Continue to next path if this one doesn't exist
continue
}
if resp != nil {
defer func() { _ = resp.Body.Close() }()
Copy link
Preview

CopilotAIJun 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Avoid deferringresp.Body.Close() inside a loop; close the response body immediately after reading to prevent resource buildup.

Copilot uses AI. Check for mistakes.

}

if resp.StatusCode == 200 {
var content interface{}
var contentType string
var actualPath string

if fileContent != nil {
content = fileContent
contentType = "file"
actualPath = path
} else if dirContent != nil {
content = dirContent
contentType = "directory"
actualPath = path
}

if content != nil {
results = append(results, map[string]interface{}{
"path": actualPath,
"type": contentType,
"content": content,
"source_repo": fmt.Sprintf("%s/%s", owner, repo),
"reference": ref,
})
}
}
}
}

if len(results) == 0 {
return mcp.NewToolResultError("No development standards found in the specified repository. Try specifying a custom standards_path or ensure the repository contains standard documentation."), nil
}

// Create a structured response
searchNote := "This tool searched the entire repository for development standards."
if !searchWholeRepo {
searchNote = "This tool searched the specified path for development standards. Leave 'standards_path' empty to search the entire repository."
}

response := map[string]interface{}{
"chewy_org": owner,
"repository": repo,
"reference": ref,
"standards_found": len(results),
"standards": results,
"search_note": searchNote,
}

r, err := json.Marshal(response)
if err != nil {
return nil, fmt.Errorf("failed to marshal response: %w", err)
}

return mcp.NewToolResultText(string(r)), nil
}
}
126 changes: 126 additions & 0 deletionspkg/github/repositories_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -9,6 +9,7 @@ import (

"github.com/github/github-mcp-server/pkg/translations"
"github.com/google/go-github/v72/github"
"github.com/mark3labs/mcp-go/mcp"
"github.com/migueleliasweb/go-github-mock/src/mock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand DownExpand Up@@ -1963,3 +1964,128 @@ func Test_GetTag(t *testing.T) {
})
}
}

func Test_GetChewyStandards(t *testing.T) {
// Verify tool definition
mockClient := github.NewClient(nil)
tool, _ := GetChewyStandards(stubGetClientFn(mockClient), translations.NullTranslationHelper)

assert.Equal(t, "get_chewy_standards", tool.Name)
assert.NotEmpty(t, tool.Description)
assert.Contains(t, tool.InputSchema.Properties, "owner")
assert.Contains(t, tool.InputSchema.Properties, "repo")
assert.Contains(t, tool.InputSchema.Properties, "standards_path")
assert.Contains(t, tool.InputSchema.Properties, "ref")
assert.ElementsMatch(t, tool.InputSchema.Required, []string{})

// Setup mock content for success case - simulating README.md with development standards
mockStandardsContent := &github.RepositoryContent{
Type: github.Ptr("file"),
Name: github.Ptr("README.md"),
Path: github.Ptr("README.md"),
Content: github.Ptr("IyBEZXZlbG9wbWVudCBTdGFuZGFyZHMKClRoaXMgaXMgb3VyIGNvbXBhbnkncyBkZXZlbG9wbWVudCBzdGFuZGFyZHMu"), // Base64 encoded "# Development Standards\n\nThis is our company's development standards."
SHA: github.Ptr("def456"),
Size: github.Ptr(45),
HTMLURL: github.Ptr("https://github.com/chewy/standards/blob/main/README.md"),
DownloadURL: github.Ptr("https://raw.githubusercontent.com/chewy/standards/main/README.md"),
}

// Test successful retrieval of standards
t.Run("success with default paths", func(t *testing.T) {
// Mock tree structure for whole repository search
mockTree := &github.Tree{
SHA: github.Ptr("abc123"),
Entries: []*github.TreeEntry{
{
Path: github.Ptr("README.md"),
Mode: github.Ptr("100644"),
Type: github.Ptr("blob"),
SHA: github.Ptr("def456"),
Size: github.Ptr(45),
},
{
Path: github.Ptr("docs/standards.md"),
Mode: github.Ptr("100644"),
Type: github.Ptr("blob"),
SHA: github.Ptr("ghi789"),
Size: github.Ptr(123),
},
},
}

mockClient := mock.NewMockedHTTPClient(
mock.WithRequestMatch(
mock.GetReposGitTreesByOwnerByRepoByTreeSha,
mockTree,
),
)
client := github.NewClient(mockClient)
_, handler := GetChewyStandards(stubGetClientFn(client), translations.NullTranslationHelper)

// Test with default parameters (should use Chewy-Inc/chewy-tech-standards)
request := createMCPRequest(map[string]interface{}{})

result, err := handler(context.Background(), request)

assert.NoError(t, err)
assert.False(t, result.IsError)
assert.Len(t, result.Content, 1)

textContent := getTextResult(t, result)
var response map[string]interface{}
err = json.Unmarshal([]byte(textContent.Text), &response)
assert.NoError(t, err)

assert.Equal(t, "Chewy-Inc", response["chewy_org"])
assert.Equal(t, "chewy-tech-standards", response["repository"])
assert.Equal(t, float64(1), response["standards_found"]) // JSON numbers are float64
assert.Contains(t, response, "standards")
})

// Test with custom standards path
t.Run("success with custom standards path", func(t *testing.T) {
mockClient := mock.NewMockedHTTPClient(
mock.WithRequestMatch(
mock.GetReposContentsByOwnerByRepoByPath,
mockStandardsContent,
),
)
client := github.NewClient(mockClient)
_, handler := GetChewyStandards(stubGetClientFn(client), translations.NullTranslationHelper)

request := createMCPRequest(map[string]interface{}{
"owner": "chewy",
"repo": "standards",
"standards_path": "docs/development-guidelines.md",
})

result, err := handler(context.Background(), request)

assert.NoError(t, err)
assert.False(t, result.IsError)
})

// Test error case - no standards found
t.Run("no standards found", func(t *testing.T) {
// Mock client that returns 404 for Git tree requests
mockClient := mock.NewMockedHTTPClient(
mock.WithRequestMatchHandler(
mock.GetReposGitTreesByOwnerByRepoByTreeSha,
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"message": "Not Found"}`))
}),
),
)
client := github.NewClient(mockClient)
_, handler := GetChewyStandards(stubGetClientFn(client), translations.NullTranslationHelper)

request := createMCPRequest(map[string]interface{}{})

result, err := handler(context.Background(), request)

assert.NoError(t, err)
assert.True(t, result.IsError)
assert.Contains(t, result.Content[0].(mcp.TextContent).Text, "Failed to get repository tree")
})
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp