Content-Length: 9546 | pFad | http://github.com/github/github-mcp-server/pull/783.diff
thub.com diff --git a/pkg/github/repositories.go b/pkg/github/repositories.go index ecd36d7e0..6a90787a9 100644 --- a/pkg/github/repositories.go +++ b/pkg/github/repositories.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" "strings" + "time" ghErrors "github.com/github/github-mcp-server/pkg/errors" "github.com/github/github-mcp-server/pkg/raw" @@ -96,7 +97,7 @@ func GetCommit(getClient GetClientFn, t translations.TranslationHelperFunc) (too // ListCommits creates a tool to get commits of a branch in a repository. func ListCommits(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) { return mcp.NewTool("list_commits", - mcp.WithDescription(t("TOOL_LIST_COMMITS_DESCRIPTION", "Get list of commits of a branch in a GitHub repository. Returns at least 30 results per page by default, but can return more if specified using the perPage parameter (up to 100).")), + mcp.WithDescription(t("TOOL_LIST_COMMITS_DESCRIPTION", "Get list of commits of a branch in a GitHub repository. Can be filtered by author, date range (since/until), or file path. Returns at least 30 results per page by default, but can return more if specified using the perPage parameter (up to 100).")), mcp.WithToolAnnotation(mcp.ToolAnnotation{ Title: t("TOOL_LIST_COMMITS_USER_TITLE", "List commits"), ReadOnlyHint: ToBoolPtr(true), @@ -115,6 +116,15 @@ func ListCommits(getClient GetClientFn, t translations.TranslationHelperFunc) (t mcp.WithString("author", mcp.Description("Author username or email address to filter commits by"), ), + mcp.WithString("since", + mcp.Description("Only return commits after this date (ISO 8601 format, e.g., '2024-01-01T00:00:00Z')"), + ), + mcp.WithString("until", + mcp.Description("Only return commits before this date (ISO 8601 format, e.g., '2024-12-31T23:59:59Z')"), + ), + mcp.WithString("path", + mcp.Description("Only return commits that touch the specified file path"), + ), WithPagination(), ), func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { @@ -134,6 +144,18 @@ func ListCommits(getClient GetClientFn, t translations.TranslationHelperFunc) (t if err != nil { return mcp.NewToolResultError(err.Error()), nil } + since, err := OptionalParam[string](request, "since") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } + until, err := OptionalParam[string](request, "until") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } + path, err := OptionalParam[string](request, "path") + if err != nil { + return mcp.NewToolResultError(err.Error()), nil + } pagination, err := OptionalPaginationParams(request) if err != nil { return mcp.NewToolResultError(err.Error()), nil @@ -143,15 +165,35 @@ func ListCommits(getClient GetClientFn, t translations.TranslationHelperFunc) (t if perPage == 0 { perPage = 30 } + opts := &github.CommitsListOptions{ SHA: sha, Author: author, + Path: path, ListOptions: github.ListOptions{ Page: pagination.Page, PerPage: perPage, }, } + // Parse since time if provided + if since != "" { + sinceTime, err := time.Parse(time.RFC3339, since) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("invalid since date format (use ISO 8601/RFC3339): %s", err.Error())), nil + } + opts.Since = sinceTime + } + + // Parse until time if provided + if until != "" { + untilTime, err := time.Parse(time.RFC3339, until) + if err != nil { + return mcp.NewToolResultError(fmt.Sprintf("invalid until date format (use ISO 8601/RFC3339): %s", err.Error())), nil + } + opts.Until = untilTime + } + client, err := getClient(ctx) if err != nil { return nil, fmt.Errorf("failed to get GitHub client: %w", err) diff --git a/pkg/github/repositories_test.go b/pkg/github/repositories_test.go index 1572a12f4..cd7098cc2 100644 --- a/pkg/github/repositories_test.go +++ b/pkg/github/repositories_test.go @@ -719,6 +719,9 @@ func Test_ListCommits(t *testing.T) { assert.Contains(t, tool.InputSchema.Properties, "repo") assert.Contains(t, tool.InputSchema.Properties, "sha") assert.Contains(t, tool.InputSchema.Properties, "author") + assert.Contains(t, tool.InputSchema.Properties, "since") + assert.Contains(t, tool.InputSchema.Properties, "until") + assert.Contains(t, tool.InputSchema.Properties, "path") assert.Contains(t, tool.InputSchema.Properties, "page") assert.Contains(t, tool.InputSchema.Properties, "perPage") assert.ElementsMatch(t, tool.InputSchema.Required, []string{"owner", "repo"}) @@ -826,6 +829,150 @@ func Test_ListCommits(t *testing.T) { expectError: false, expectedCommits: mockCommits, }, + { + name: "successful commits fetch with time filtering", + mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposCommitsByOwnerByRepo, + expectQueryParams(t, map[string]string{ + "since": "2024-01-01T00:00:00Z", + "until": "2024-12-31T23:59:59Z", + "page": "1", + "per_page": "30", + }).andThen( + mockResponse(t, http.StatusOK, mockCommits), + ), + ), + ), + requestArgs: map[string]interface{}{ + "owner": "owner", + "repo": "repo", + "since": "2024-01-01T00:00:00Z", + "until": "2024-12-31T23:59:59Z", + }, + expectError: false, + expectedCommits: mockCommits, + }, + { + name: "successful commits fetch with only since parameter", + mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposCommitsByOwnerByRepo, + expectQueryParams(t, map[string]string{ + "since": "2024-06-01T00:00:00Z", + "page": "1", + "per_page": "30", + }).andThen( + mockResponse(t, http.StatusOK, mockCommits), + ), + ), + ), + requestArgs: map[string]interface{}{ + "owner": "owner", + "repo": "repo", + "since": "2024-06-01T00:00:00Z", + }, + expectError: false, + expectedCommits: mockCommits, + }, + { + name: "successful commits fetch with only until parameter", + mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposCommitsByOwnerByRepo, + expectQueryParams(t, map[string]string{ + "until": "2024-06-30T23:59:59Z", + "page": "1", + "per_page": "30", + }).andThen( + mockResponse(t, http.StatusOK, mockCommits), + ), + ), + ), + requestArgs: map[string]interface{}{ + "owner": "owner", + "repo": "repo", + "until": "2024-06-30T23:59:59Z", + }, + expectError: false, + expectedCommits: mockCommits, + }, + { + name: "invalid since date format", + mockedClient: mock.NewMockedHTTPClient( + // No HTTP requests expected due to early validation error + ), + requestArgs: map[string]interface{}{ + "owner": "owner", + "repo": "repo", + "since": "invalid-date-format", + }, + expectError: false, // This returns a tool error, not a Go error + expectedErrMsg: "invalid since date format", + }, + { + name: "invalid until date format", + mockedClient: mock.NewMockedHTTPClient( + // No HTTP requests expected due to early validation error + ), + requestArgs: map[string]interface{}{ + "owner": "owner", + "repo": "repo", + "until": "2024/12/31", // Wrong format + }, + expectError: false, // This returns a tool error, not a Go error + expectedErrMsg: "invalid until date format", + }, + { + name: "successful commits fetch with path filtering", + mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposCommitsByOwnerByRepo, + expectQueryParams(t, map[string]string{ + "path": "src/main.go", + "page": "1", + "per_page": "30", + }).andThen( + mockResponse(t, http.StatusOK, mockCommits), + ), + ), + ), + requestArgs: map[string]interface{}{ + "owner": "owner", + "repo": "repo", + "path": "src/main.go", + }, + expectError: false, + expectedCommits: mockCommits, + }, + { + name: "successful commits fetch with path and time filtering", + mockedClient: mock.NewMockedHTTPClient( + mock.WithRequestMatchHandler( + mock.GetReposCommitsByOwnerByRepo, + expectQueryParams(t, map[string]string{ + "path": "docs/README.md", + "since": "2024-01-01T00:00:00Z", + "until": "2024-12-31T23:59:59Z", + "author": "testuser", + "page": "1", + "per_page": "30", + }).andThen( + mockResponse(t, http.StatusOK, mockCommits), + ), + ), + ), + requestArgs: map[string]interface{}{ + "owner": "owner", + "repo": "repo", + "path": "docs/README.md", + "since": "2024-01-01T00:00:00Z", + "until": "2024-12-31T23:59:59Z", + "author": "testuser", + }, + expectError: false, + expectedCommits: mockCommits, + }, { name: "commits fetch fails", mockedClient: mock.NewMockedHTTPClient( @@ -867,6 +1014,14 @@ func Test_ListCommits(t *testing.T) { return } + if tc.expectedErrMsg != "" { + require.NoError(t, err) + require.True(t, result.IsError) + errorContent := getErrorResult(t, result) + assert.Contains(t, errorContent.Text, tc.expectedErrMsg) + return + } + require.NoError(t, err) require.False(t, result.IsError)Fetched URL: http://github.com/github/github-mcp-server/pull/783.diff
Alternative Proxies: