@@ -6,14 +6,11 @@ import (
66 "fmt"
77 "io"
88 "net/http"
9- "net/url"
10- "reflect"
119 "strings"
1210
1311 ghErrors "github.com/github/github-mcp-server/pkg/errors"
1412 "github.com/github/github-mcp-server/pkg/translations"
1513 "github.com/google/go-github/v77/github"
16- "github.com/google/go-querystring/query"
1714 "github.com/mark3labs/mcp-go/mcp"
1815 "github.com/mark3labs/mcp-go/server"
1916)
@@ -237,27 +234,19 @@ func ListProjectFields(getClient GetClientFn, t translations.TranslationHelperFu
237234 return mcp .NewToolResultError (err .Error ()), nil
238235 }
239236
240- var url string
241- if ownerType == "org" {
242- url = fmt .Sprintf ("orgs/%s/projectsV2/%d/fields" , owner , projectNumber )
243- } else {
244- url = fmt .Sprintf ("users/%s/projectsV2/%d/fields" , owner , projectNumber )
245- }
246- projectFields := []projectV2Field {}
247-
248- opts := paginationOptions {PerPage : perPage }
237+ var resp * github.Response
238+ var projectFields []* github.ProjectV2Field
249239
250- url , err = addOptions (url , opts )
251- if err != nil {
252- return nil , fmt .Errorf ("failed to add options to request: %w" , err )
240+ opts := & github.ListProjectsOptions {
241+ ListProjectsPaginationOptions : github.ListProjectsPaginationOptions {PerPage : & perPage },
253242 }
254243
255- httpRequest , err := client .NewRequest ("GET" , url , nil )
256- if err != nil {
257- return nil , fmt .Errorf ("failed to create request: %w" , err )
244+ if ownerType == "org" {
245+ projectFields , resp , err = client .Projects .ListOrganizationProjectFields (ctx , owner , projectNumber , opts )
246+ } else {
247+ projectFields , resp , err = client .Projects .ListUserProjectFields (ctx , owner , projectNumber , opts )
258248 }
259249
260- resp , err := client .Do (ctx , httpRequest , & projectFields )
261250 if err != nil {
262251 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
263252 "failed to list project fields" ,
@@ -317,7 +306,7 @@ func GetProjectField(getClient GetClientFn, t translations.TranslationHelperFunc
317306 if err != nil {
318307 return mcp .NewToolResultError (err .Error ()), nil
319308 }
320- fieldID , err := RequiredInt (req , "field_id" )
309+ fieldID , err := RequiredBigInt (req , "field_id" )
321310 if err != nil {
322311 return mcp .NewToolResultError (err .Error ()), nil
323312 }
@@ -326,21 +315,15 @@ func GetProjectField(getClient GetClientFn, t translations.TranslationHelperFunc
326315 return mcp .NewToolResultError (err .Error ()), nil
327316 }
328317
329- var url string
318+ var resp * github.Response
319+ var projectField * github.ProjectV2Field
320+
330321 if ownerType == "org" {
331- url = fmt . Sprintf ( "orgs/%s/projectsV2/%d/fields/%d" , owner , projectNumber , fieldID )
322+ projectField , resp , err = client . Projects . GetOrganizationProjectField ( ctx , owner , projectNumber , fieldID )
332323 } else {
333- url = fmt .Sprintf ("users/%s/projectsV2/%d/fields/%d" , owner , projectNumber , fieldID )
334- }
335-
336- projectField := projectV2Field {}
337-
338- httpRequest , err := client .NewRequest ("GET" , url , nil )
339- if err != nil {
340- return nil , fmt .Errorf ("failed to create request: %w" , err )
324+ projectField , resp , err = client .Projects .GetUserProjectField (ctx , owner , projectNumber , fieldID )
341325 }
342326
343- resp , err := client .Do (ctx , httpRequest , & projectField )
344327 if err != nil {
345328 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
346329 "failed to get project field" ,
@@ -416,41 +399,32 @@ func ListProjectItems(getClient GetClientFn, t translations.TranslationHelperFun
416399 if err != nil {
417400 return mcp .NewToolResultError (err .Error ()), nil
418401 }
419- fields , err := OptionalStringArrayParam (req , "fields" )
402+ fields , err := OptionalBigIntArrayParam (req , "fields" )
420403 if err != nil {
421404 return mcp .NewToolResultError (err .Error ()), nil
422405 }
423-
424406 client , err := getClient (ctx )
425407 if err != nil {
426408 return mcp .NewToolResultError (err .Error ()), nil
427409 }
428410
429- var url string
430- if ownerType == "org" {
431- url = fmt .Sprintf ("orgs/%s/projectsV2/%d/items" , owner , projectNumber )
432- } else {
433- url = fmt .Sprintf ("users/%s/projectsV2/%d/items" , owner , projectNumber )
434- }
435- projectItems := []projectV2Item {}
436-
437- opts := listProjectItemsOptions {
438- paginationOptions : paginationOptions {PerPage : perPage },
439- filterQueryOptions : filterQueryOptions {Query : queryStr },
440- fieldSelectionOptions : fieldSelectionOptions {Fields : fields },
441- }
411+ var resp * github.Response
412+ var projectItems []* github.ProjectV2Item
442413
443- url , err = addOptions (url , opts )
444- if err != nil {
445- return nil , fmt .Errorf ("failed to add options to request: %w" , err )
414+ opts := & github.ListProjectItemsOptions {
415+ Fields : fields ,
416+ ListProjectsOptions : github.ListProjectsOptions {
417+ ListProjectsPaginationOptions : github.ListProjectsPaginationOptions {PerPage : & perPage },
418+ Query : & queryStr ,
419+ },
446420 }
447421
448- httpRequest , err := client .NewRequest ("GET" , url , nil )
449- if err != nil {
450- return nil , fmt .Errorf ("failed to create request: %w" , err )
422+ if ownerType == "org" {
423+ projectItems , resp , err = client .Projects .ListOrganizationProjectItems (ctx , owner , projectNumber , opts )
424+ } else {
425+ projectItems , resp , err = client .Projects .ListUserProjectItems (ctx , owner , projectNumber , opts )
451426 }
452427
453- resp , err := client .Do (ctx , httpRequest , & projectItems )
454428 if err != nil {
455429 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
456430 ProjectListFailedError ,
@@ -518,11 +492,11 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
518492 if err != nil {
519493 return mcp .NewToolResultError (err .Error ()), nil
520494 }
521- itemID , err := RequiredInt (req , "item_id" )
495+ itemID , err := RequiredBigInt (req , "item_id" )
522496 if err != nil {
523497 return mcp .NewToolResultError (err .Error ()), nil
524498 }
525- fields , err := OptionalStringArrayParam (req , "fields" )
499+ fields , err := OptionalBigIntArrayParam (req , "fields" )
526500 if err != nil {
527501 return mcp .NewToolResultError (err .Error ()), nil
528502 }
@@ -532,32 +506,21 @@ func GetProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
532506 return mcp .NewToolResultError (err .Error ()), nil
533507 }
534508
535- var url string
536- if ownerType == "org" {
537- url = fmt .Sprintf ("orgs/%s/projectsV2/%d/items/%d" , owner , projectNumber , itemID )
538- } else {
539- url = fmt .Sprintf ("users/%s/projectsV2/%d/items/%d" , owner , projectNumber , itemID )
540- }
541-
542- opts := fieldSelectionOptions {}
509+ opts := & github.GetProjectItemOptions {}
543510
544511 if len (fields ) > 0 {
545512 opts .Fields = fields
546513 }
547514
548- url , err = addOptions (url , opts )
549- if err != nil {
550- return mcp .NewToolResultError (err .Error ()), nil
551- }
552-
553- projectItem := projectV2Item {}
515+ var resp * github.Response
516+ var projectItem * github.ProjectV2Item
554517
555- httpRequest , err := client .NewRequest ("GET" , url , nil )
556- if err != nil {
557- return nil , fmt .Errorf ("failed to create request: %w" , err )
518+ if ownerType == "org" {
519+ projectItem , resp , err = client .Projects .GetOrganizationProjectItem (ctx , owner , projectNumber , itemID , opts )
520+ } else {
521+ projectItem , resp , err = client .Projects .GetUserProjectItem (ctx , owner , projectNumber , itemID , opts )
558522 }
559523
560- resp , err := client .Do (ctx , httpRequest , & projectItem )
561524 if err != nil {
562525 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
563526 "failed to get project item" ,
@@ -624,7 +587,7 @@ func AddProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
624587 if err != nil {
625588 return mcp .NewToolResultError (err .Error ()), nil
626589 }
627- itemID , err := RequiredInt (req , "item_id" )
590+ itemID , err := RequiredBigInt (req , "item_id" )
628591 if err != nil {
629592 return mcp .NewToolResultError (err .Error ()), nil
630593 }
@@ -642,24 +605,20 @@ func AddProjectItem(getClient GetClientFn, t translations.TranslationHelperFunc)
642605 return mcp .NewToolResultError (err .Error ()), nil
643606 }
644607
645- var projectsURL string
646- if ownerType == "org" {
647- projectsURL = fmt .Sprintf ("orgs/%s/projectsV2/%d/items" , owner , projectNumber )
648- } else {
649- projectsURL = fmt .Sprintf ("users/%s/projectsV2/%d/items" , owner , projectNumber )
650- }
651-
652- newItem := & newProjectItem {
653- ID : int64 (itemID ),
608+ newItem := & github.AddProjectItemOptions {
609+ ID : itemID ,
654610 Type : toNewProjectType (itemType ),
655611 }
656- httpRequest , err := client .NewRequest ("POST" , projectsURL , newItem )
657- if err != nil {
658- return nil , fmt .Errorf ("failed to create request: %w" , err )
612+
613+ var resp * github.Response
614+ var addedItem * github.ProjectV2Item
615+
616+ if ownerType == "org" {
617+ addedItem , resp , err = client .Projects .AddOrganizationProjectItem (ctx , owner , projectNumber , newItem )
618+ } else {
619+ addedItem , resp , err = client .Projects .AddUserProjectItem (ctx , owner , projectNumber , newItem )
659620 }
660- addedItem := projectV2Item {}
661621
662- resp , err := client .Do (ctx , httpRequest , & addedItem )
663622 if err != nil {
664623 return ghErrors .NewGitHubAPIErrorResponse (ctx ,
665624 ProjectAddFailedError ,
@@ -869,11 +828,6 @@ func DeleteProjectItem(getClient GetClientFn, t translations.TranslationHelperFu
869828 }
870829}
871830
872- type newProjectItem struct {
873- ID int64 `json:"id,omitempty"`
874- Type string `json:"type,omitempty"`
875- }
876-
877831type updateProjectItemPayload struct {
878832 Fields []updateProjectItem `json:"fields"`
879833}
@@ -883,17 +837,6 @@ type updateProjectItem struct {
883837 Value any `json:"value"`
884838}
885839
886- type projectV2Field struct {
887- ID * int64 `json:"id,omitempty"` // The unique identifier for this field.
888- NodeID string `json:"node_id,omitempty"` // The GraphQL node ID for this field.
889- Name string `json:"name,omitempty"` // The display name of the field.
890- DataType string `json:"data_type,omitempty"` // The data type of the field (e.g., "text", "number", "date", "single_select", "multi_select").
891- URL string `json:"url,omitempty"` // The API URL for this field.
892- Options []* any `json:"options,omitempty"` // Available options for single_select and multi_select fields.
893- CreatedAt * github.Timestamp `json:"created_at,omitempty"` // The time when this field was created.
894- UpdatedAt * github.Timestamp `json:"updated_at,omitempty"` // The time when this field was last updated.
895- }
896-
897840type projectV2ItemFieldValue struct {
898841 ID * int64 `json:"id,omitempty"` // The unique identifier for this field.
899842 Name string `json:"name,omitempty"` // The display name of the field.
@@ -931,26 +874,6 @@ type projectV2ItemContent struct {
931874 URL * string `json:"url,omitempty"`
932875}
933876
934- type paginationOptions struct {
935- PerPage int `url:"per_page,omitempty"`
936- }
937-
938- type filterQueryOptions struct {
939- Query string `url:"q,omitempty"`
940- }
941-
942- type fieldSelectionOptions struct {
943- // Specific list of field IDs to include in the response. If not provided, only the title field is included.
944- // Example: fields=102589,985201,169875 or fields[]=102589&fields[]=985201&fields[]=169875
945- Fields []string `url:"fields,omitempty"`
946- }
947-
948- type listProjectItemsOptions struct {
949- paginationOptions
950- filterQueryOptions
951- fieldSelectionOptions
952- }
953-
954877func toNewProjectType (projType string ) string {
955878 switch strings .ToLower (projType ) {
956879 case "issue" :
@@ -986,28 +909,6 @@ func buildUpdateProjectItem(input map[string]any) (*updateProjectItem, error) {
986909 return payload , nil
987910}
988911
989- // addOptions adds the parameters in opts as URL query parameters to s. opts
990- // must be a struct whose fields may contain "url" tags.
991- func addOptions (s string , opts any ) (string , error ) {
992- v := reflect .ValueOf (opts )
993- if v .Kind () == reflect .Ptr && v .IsNil () {
994- return s , nil
995- }
996-
997- u , err := url .Parse (s )
998- if err != nil {
999- return s , err
1000- }
1001-
1002- qs , err := query .Values (opts )
1003- if err != nil {
1004- return s , err
1005- }
1006-
1007- u .RawQuery = qs .Encode ()
1008- return u .String (), nil
1009- }
1010-
1011912func ManageProjectItemsPrompt (t translations.TranslationHelperFunc ) (tool mcp.Prompt , handler server.PromptHandlerFunc ) {
1012913 return mcp .NewPrompt ("ManageProjectItems" ,
1013914 mcp .WithPromptDescription (t ("PROMPT_MANAGE_PROJECT_ITEMS_DESCRIPTION" , "Interactive guide for managing GitHub Projects V2, including discovery, field management, querying, and updates." )),
0 commit comments